All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.elemento.BodyObserver Maven / Gradle / Ivy

There is a newer version: 1.0.7
Show newest version
/*
 * Copyright 2015-2016 Red Hat, Inc, and individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.elemento;

import java.util.ArrayList;
import java.util.List;

import elemental2.dom.HTMLElement;
import elemental2.dom.MutationObserver;
import elemental2.dom.MutationObserverInit;
import elemental2.dom.MutationRecord;
import jsinterop.base.Js;

import static elemental2.dom.DomGlobal.document;
import static java.util.stream.Collectors.toList;
import static org.jboss.elemento.Elements.asHtmlElement;
import static org.jboss.elemento.Elements.htmlElements;
import static org.jboss.elemento.Elements.stream;

final class BodyObserver {

    private static final String ATTACH_UID_KEY = "on-attach-uid";
    private static final String DETACH_UID_KEY = "on-detach-uid";

    private static final List detachObservers = new ArrayList<>();
    private static final List attachObservers = new ArrayList<>();
    private static boolean ready = false;

    private static void startObserving() {
        MutationObserver mutationObserver = new MutationObserver((mutationRecords, observer) -> {
            MutationRecord[] records = Js.uncheckedCast(mutationRecords);
            for (int i = 0; i < records.length; i++) {
                onElementsRemoved(records[i]);
                onElementsAppended(records[i]);
            }
            return null;
        });

        MutationObserverInit mutationObserverInit = MutationObserverInit.create();
        mutationObserverInit.setChildList(true);
        mutationObserverInit.setSubtree(true);
        mutationObserver.observe(document.body, mutationObserverInit);
        ready = true;
    }

    @SuppressWarnings("DuplicatedCode")
    private static void onElementsAppended(MutationRecord record) {
        List observed = new ArrayList<>();
        List addedElements = stream(record.addedNodes)
                .filter(htmlElements())
                .map(asHtmlElement())
                .collect(toList());

        for (ElementObserver elementObserver : attachObservers) {
            if (elementObserver.observedElement() == null) {
                observed.add(elementObserver);
            } else {
                if (addedElements.contains(elementObserver.observedElement()) ||
                        isChildOfObservedElement(addedElements, ATTACH_UID_KEY, elementObserver.attachId())) {
                    elementObserver.callback().onObserved(record);
                    elementObserver.observedElement().removeAttribute(ATTACH_UID_KEY);
                    observed.add(elementObserver);
                }
            }
        }
        attachObservers.removeAll(observed);
    }

    @SuppressWarnings("DuplicatedCode")
    private static void onElementsRemoved(MutationRecord record) {
        List observed = new ArrayList<>();
        List removedElements = stream(record.removedNodes)
                .filter(htmlElements())
                .map(asHtmlElement())
                .collect(toList());

        for (ElementObserver elementObserver : detachObservers) {
            if (elementObserver.observedElement() == null) {
                observed.add(elementObserver);
            } else {
                if (removedElements.contains(elementObserver.observedElement()) ||
                        isChildOfObservedElement(removedElements, DETACH_UID_KEY, elementObserver.attachId())) {
                    elementObserver.callback().onObserved(record);
                    elementObserver.observedElement().removeAttribute(DETACH_UID_KEY);
                    observed.add(elementObserver);
                }
            }
        }
        detachObservers.removeAll(observed);
    }

    private static boolean isChildOfObservedElement(List elements, String attribute, String attachId) {
        for (HTMLElement element : elements) {
            if (element.querySelector("[" + attribute + "='" + attachId + "']") != null) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks if the observer has already been started, if not it will start it. Then register and callback when the
     * element is attached to the DOM.
     */
    static void addAttachObserver(HTMLElement element, ObserverCallback callback) {
        if (!ready) {
            startObserving();
        }
        attachObservers.add(createObserver(element, callback, ATTACH_UID_KEY));
    }

    /**
     * Checks if the observer has already been started, if not it will start it. Then register and callback when the
     * element is removed from the DOM.
     */
    static void addDetachObserver(HTMLElement element, ObserverCallback callback) {
        if (!ready) {
            startObserving();
        }
        detachObservers.add(createObserver(element, callback, DETACH_UID_KEY));
    }

    private static ElementObserver createObserver(HTMLElement element, ObserverCallback callback,
            String attribute) {
        String elementId = element.getAttribute(attribute);
        if (elementId == null) {
            element.setAttribute(attribute, Id.unique());
        }
        return new ElementObserver() {
            @Override
            public String attachId() {
                return element.getAttribute(attribute);
            }

            @Override
            public HTMLElement observedElement() {
                return element;
            }

            @Override
            public ObserverCallback callback() {
                return callback;
            }
        };
    }

    private BodyObserver() {
    }

    private interface ElementObserver {

        String attachId();

        HTMLElement observedElement();

        ObserverCallback callback();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy