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

org.dominokit.domino.ui.utils.BodyObserver Maven / Gradle / Ivy

/*
 * Copyright © 2019 Dominokit
 *
 * 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.dominokit.domino.ui.utils;

import static elemental2.dom.DomGlobal.document;
import static org.dominokit.domino.ui.utils.BaseDominoElement.ATTACH_UID_KEY;
import static org.dominokit.domino.ui.utils.BaseDominoElement.DETACH_UID_KEY;
import static org.dominokit.domino.ui.utils.Domino.*;
import static org.dominokit.domino.ui.utils.ElementsFactory.elements;

import elemental2.core.JsArray;
import elemental2.dom.CustomEvent;
import elemental2.dom.CustomEventInit;
import elemental2.dom.Event;
import elemental2.dom.HTMLElement;
import elemental2.dom.MutationObserver;
import elemental2.dom.MutationObserverInit;
import elemental2.dom.MutationRecord;
import elemental2.dom.Node;
import java.util.List;
import jsinterop.base.Js;

/**
 * The {@code BodyObserver} class is responsible for observing mutations in the document's body. It
 * tracks the addition and removal of elements with specific attributes and dispatches events
 * accordingly.
 */
final class BodyObserver {

  private static boolean ready = false;
  private static boolean paused = false;
  private static MutationObserver mutationObserver;

  private BodyObserver() {}

  /**
   * Pauses the observer for a specified action and resumes it afterward.
   *
   * @param handler The action to perform while the observer is paused.
   */
  static void pauseFor(Runnable handler) {
    mutationObserver.disconnect();
    try {
      handler.run();
    } finally {
      observe();
    }
  }

  /** Starts observing mutations in the document's body. */
  static void startObserving() {
    if (!ready) {
      mutationObserver =
          new MutationObserver(
              (JsArray records, MutationObserver observer) -> {
                if (!paused) {
                  MutationRecord[] recordsArray =
                      Js.uncheckedCast(records.asArray(new MutationRecord[records.length]));
                  for (MutationRecord record : recordsArray) {
                    if (!record.removedNodes.asList().isEmpty()) {
                      onElementsRemoved(record);
                    }

                    if (!record.addedNodes.asList().isEmpty()) {
                      onElementsAppended(record);
                    }
                  }
                }
                return null;
              });

      observe();
      ready = true;
    }
  }

  private static void observe() {
    MutationObserverInit mutationObserverInit = MutationObserverInit.create();
    mutationObserverInit.setChildList(true);
    mutationObserverInit.setSubtree(true);
    mutationObserver.observe(document.body, mutationObserverInit);
  }

  private static void onElementsAppended(MutationRecord record) {
    List nodes = record.addedNodes.asList();
    for (int i = 0; i < nodes.size(); i++) {
      Node elementNode = Js.uncheckedCast(nodes.get(i));
      if (Node.ELEMENT_NODE == elementNode.nodeType) {
        HTMLElement element = Js.uncheckedCast(elementNode);
        if (element.hasAttribute(ATTACH_UID_KEY)) {
          element.dispatchEvent(
              new CustomEvent<>(AttachDetachEventType.attachedType(elements.elementOf(element))));
        }
        elements
            .elementOf(element)
            .querySelectorAll("[" + ATTACH_UID_KEY + "]")
            .forEach(
                child -> {
                  CustomEventInit ceinit = CustomEventInit.create();
                  ceinit.setDetail(record);
                  CustomEvent event =
                      new CustomEvent<>(
                          AttachDetachEventType.attachedType(elements.elementOf(child)), ceinit);
                  child.element().dispatchEvent(event);
                });
      }
    }
  }

  private static void onElementsRemoved(MutationRecord record) {
    List nodes = record.removedNodes.asList();
    for (int i = 0; i < nodes.size(); i++) {
      Node elementNode = Js.uncheckedCast(nodes.get(i));
      if (Node.ELEMENT_NODE == elementNode.nodeType) {
        HTMLElement element = Js.uncheckedCast(elementNode);
        if (element.hasAttribute(DETACH_UID_KEY)) {
          element.dispatchEvent(
              new Event(AttachDetachEventType.detachedType(elements.elementOf(element))));
        }
        elements
            .elementOf(element)
            .querySelectorAll("[" + DETACH_UID_KEY + "]")
            .forEach(
                child -> {
                  CustomEventInit ceinit = CustomEventInit.create();
                  ceinit.setDetail(record);
                  CustomEvent event =
                      new CustomEvent<>(
                          AttachDetachEventType.detachedType(elements.elementOf(child)), ceinit);
                  child.element().dispatchEvent(event);
                });
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy