'use strict';
/**
* @author schukai GmbH
*/
import {Monster, Base} from '../types/base.js';
import {Stack} from "../types/stack.js";
import {validateInstance} from "../types/validate.js";
import {ProxyObserver} from "../types/proxyobserver.js";
import {Observer} from "../types/observer.js";
import {getGlobalFunction, getGlobalObject} from "../types/global.js";
import {isInstance} from "../types/is.js";
import {ATTRIBUTEPREFIX} from "./assembler.js"
import {ID} from "../types/id.js";
/**
* @private
* @type {Symbol}
*/
const MONSTERDOMHANDLE = Symbol('MonsterHandle');
/**
* you can call the method via the monster namespace `new Monster.DOM.Handle()`.
*
* ```
* <script type="module">
* import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.6.0/dist/modules/dom/handle.js';
* console.log(new Monster.DOM.Handle())
* </script>
* ```
*
* Alternatively, you can also integrate this function individually.
*
* ```
* <script type="module">
* import {Handle} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.6.0/dist/modules/dom/handle.js';
* console.log(new Handle())
* </script>
* ```
*
* @since 1.6.0
* @copyright schukai GmbH
* @memberOf Monster.DOM
*/
class Handle extends Base {
/**
*
* @param {ProxyObserver} data
*/
constructor(data) {
super();
let self = this;
self.id = new ID();
validateInstance(data, ProxyObserver);
this.data = data
this.mutationObserver = createMutationObserver.call(this);
this.dataObserver = createDataObserver.call(this);
this.data.attachObserver(this.dataObserver);
this.nodes = new WeakSet
this.updates = new Stack();
}
/**
* @return {void}
*/
update() {
return;
}
/**
*
* @param {HTMLElement|Document} node
* @return {Handle}
*/
remove(node) {
if (isInstance(node, getGlobalFunction('Document'))) {
node = node.firstElementChild
}
validateInstance(node, getGlobalFunction('HTMLElement'))
if (!this.nodes.has(node)) {
return this;
}
this.mutationObserver.disconnect(node);
delete node.dataset[MONSTERDOMHANDLE]
node.removeAttribute(ATTRIBUTEPREFIX + "handler");
return this;
}
/**
*
* @param {HTMLElement|Document} node
* @return {Handle}
*/
append(node) {
if (isInstance(node, getGlobalFunction('Document'))) {
node = node.firstElementChild
}
validateInstance(node, getGlobalFunction('HTMLElement'))
if (this.nodes.has(node)) {
return this;
}
node.dataset[MONSTERDOMHANDLE] = this;
node.setAttribute(ATTRIBUTEPREFIX + "handler", true);
this.mutationObserver.observe(node, {
attributes: true,
childList: true,
subtree: true,
characterData: true,
characterDataOldValue: true,
attributeOldValue: true
});
this.nodes.add(node);
return this;
}
}
/**
*
* @private
* @return {Observer}
*/
function createDataObserver() {
const self = this;
return new Observer(() => {
self.update();
});
}
/**
*
* @private
* @return {MutationObserver}
*/
function createMutationObserver() {
const self = this;
/**
* @private
* @type {MutationObserver}
*/
const MutationObserver = getGlobalFunction('MutationObserver');
// @link https://developer.mozilla.org/en/docs/Web/API/MutationObserver
return new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
self.updates.push(mutation);
}
self.update();
}
)
}
/**
* get the handle of a node
*
* if a node is specified without a handler, a recursive search upwards is performed until the corresponding
* handle is found, or undefined is returned.
*
* you can call the method via the monster namespace `Monster.DOM.getHandleFromNode()`.
*
* ```
* <script type="module">
* import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.6.0/dist/modules/dom/handle.js';
* console.log(Monster.DOM.getHandleFromNode())
* </script>
* ```
*
* Alternatively, you can also integrate this function individually.
*
* ```
* <script type="module">
* import {getHandleFromNode} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.6.0/dist/modules/dom/handle.js';
* console.log(getHandleFromNode())
* </script>
* ```
*
* @param {Node} node
* @return {Handle|undefined}
* @since 1.6.0
* @copyright schukai GmbH
* @memberOf Monster/DOM
* @throws {TypeError} value is not an instance of Node
*/
function getHandleFromNode(node) {
validateInstance(node, getGlobalFunction('Node'));
let handle = node.dataset?.[MONSTERDOMHANDLE];
if (handle === undefined) {
let parentNode = node?.['parentNode'];
if (isInstance(parentNode, getGlobalFunction('Node'))) {
return getHandleFromNode(parentNode)
}
}
return handle;
}
Monster.assignToNamespace('Monster.DOM', getHandleFromNode, Handle);
export {Monster, getHandleFromNode, Handle}