UNPKG

@evolv/mutate

Version:

A library of standard DOM mutations by Evolv AI.

1,456 lines (1,418 loc) 193 kB
/*! * Evolv Mutate v2.7.1 <https://github.com/evolv-ai/mutate> * * Copyright 2021-2025 Evolv Technology Solutions * * All rights reserved. */ 'use strict'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; const KEY_SEPARATOR = '\x90\x90'; /*** * Generates a scoped name by combining a scope string and a name. * * @param name - The name to scope. * @param scope - The scope to prepend to the name. Can be `undefined`. * @return A scoped name */ const SCOPE = (name, scope) => { return scope ? `${scope}:${name}` : name; }; /*** * Default interval time in ms */ const INTERVAL = 50; /*** * Generates a unique id */ const UID = () => (Math.random().toString(36).slice(2, 7) + Date.now().toString(36).slice(3, 8)).toUpperCase(); var EventType; (function (EventType) { EventType[EventType["ADDED"] = 0] = "ADDED"; EventType[EventType["REMOVED"] = 1] = "REMOVED"; EventType[EventType["MODIFIED"] = 2] = "MODIFIED"; })(EventType || (EventType = {})); var CollectorState; (function (CollectorState) { CollectorState[CollectorState["INITIALIZED"] = 0] = "INITIALIZED"; CollectorState[CollectorState["VALIDATED"] = 1] = "VALIDATED"; CollectorState[CollectorState["FAILED"] = 2] = "FAILED"; CollectorState[CollectorState["PAUSED"] = 3] = "PAUSED"; CollectorState[CollectorState["UNPAUSED"] = 4] = "UNPAUSED"; CollectorState[CollectorState["DESTROYED"] = 5] = "DESTROYED"; })(CollectorState || (CollectorState = {})); class EvolvError extends Error { constructor(message) { super('Evolv: ' + message); } } class CollectorNotFoundError extends EvolvError { constructor(collectorName) { super(`Collector '${collectorName}' not found.`); } } class CollectorDestroyedError extends EvolvError { constructor() { super('This collector has been destroyed'); } } var LogLevel; (function (LogLevel) { LogLevel[LogLevel["DEBUG"] = 0] = "DEBUG"; LogLevel[LogLevel["INFO"] = 1] = "INFO"; LogLevel[LogLevel["WARNING"] = 2] = "WARNING"; LogLevel[LogLevel["ERROR"] = 3] = "ERROR"; })(LogLevel || (LogLevel = {})); const LOGGERS = { [LogLevel.DEBUG]: console.debug, [LogLevel.INFO]: console.info, [LogLevel.WARNING]: console.warn, [LogLevel.ERROR]: console.error, }; /*** * Outputs messages to the console. */ class MessageLog { get logLevel() { return this._logLevel; } constructor(scope = null, logLevel = LogLevel.WARNING) { var _a, _b; this._scope = null; this._errorLimit = 50; this._errorCount = 0; this._scope = scope; this._logLevel = ((_b = (_a = window.evolv) === null || _a === void 0 ? void 0 : _a.config) === null || _b === void 0 ? void 0 : _b.logLevel) || logLevel; } qualifyMessage(message) { return `[Evolv AI > ${this._scope}] ${message}`; } debug(message) { this.log(LogLevel.DEBUG, message); } info(message) { this.log(LogLevel.INFO, message); } warn(message) { this.log(LogLevel.WARNING, message); } error(message) { if (this._errorCount < this._errorLimit) { this._errorCount++; this.log(LogLevel.ERROR, message); } } log(logLevel, message) { if (logLevel < this._logLevel) { return; } const qualifiedMessage = this.qualifyMessage(message); LOGGERS[logLevel](qualifiedMessage); } } const MUTATE_SOURCE_ATTR = 'mutatorSrc'; class AbstractAction { pauseEvaluation() { this._evaluationPaused = true; } unpauseEvaluation() { this._evaluationPaused = false; } get subject() { return this._subject; } get state() { return this._state; } get branches() { return this._branches; } get factory() { return this._factory; } constructor(state, subject, factory) { this._branches = []; this._applying = false; this._evaluationPaused = false; this._state = state; this._subject = subject; this._factory = factory; } _evaluate(event) { if (!this._applying) { this._applying = true; if (!this._evaluationPaused) { // @ts-expect-error We are indexing an object with a passed in string to retrieve a function. this._branches.forEach((a) => a[event]()); } this._applying = false; } } removed() { var _a; (_a = this._factory) === null || _a === void 0 ? void 0 : _a.detach(this._subject); this._evaluate('removed'); } modified() { this._evaluate('modified'); } revert() { this._evaluate('revert'); } connect(action) { this._branches.push(action); } disconnect(action) { this._branches = this._branches.filter((a) => a !== action); } addMutatorIdsToTree(element, includeSelf = false) { if (includeSelf) { this.addToMutateSrcArray(element, this.state.mutator.id); } for (const child of element.children) { this.addToMutateSrcArray(child, this.state.mutator.id); this.addMutatorIdsToTree(child); } } removeMutatorIdsFromTree(element, includeSelf = false) { if (includeSelf) { this.elementRemoveMutateSrc(element, this.state.mutator.id); } for (const child of element.children) { this.removeMutatorIdsFromTree(child); this.elementRemoveMutateSrc(child, this.state.mutator.id); } } addToMutateSrcArray(element, mutateSrc) { const mutateSrcs = getMutateSrcArray(element); if (elementHasMutateSrc(element, mutateSrc)) { return; } mutateSrcs.push(mutateSrc); element.dataset[MUTATE_SOURCE_ATTR] = mutateSrcs.join(','); } elementRemoveMutateSrc(element, mutateSrc) { const mutateSrcs = getMutateSrcArray(element); const index = mutateSrcs.indexOf(mutateSrc); if (index > -1) { mutateSrcs.splice(index, 1); } if (mutateSrcs.length) { element.dataset[MUTATE_SOURCE_ATTR] = mutateSrcs.join(','); } else { delete element.dataset[MUTATE_SOURCE_ATTR]; } } } const getMutateSrcArray = (element) => { var _a; const strMutateSrc = (_a = element.dataset[MUTATE_SOURCE_ATTR]) !== null && _a !== void 0 ? _a : ''; if (strMutateSrc) { return strMutateSrc.split(','); } return []; }; const elementHasMutateSrc = (element, mutateSrc) => { const mutateSrcs = getMutateSrcArray(element); return mutateSrcs.includes(mutateSrc); }; class AbstractActionFactory { constructor() { this._instances = new Map(); this._once = false; } get instances() { return this._instances; } get once() { return this._once; } set once(value) { this._once = value; } get next() { return this._next; } set next(value) { if (!value) { return; } this._instances.forEach((branch) => { value.attach(branch.state, branch.subject, branch); }); this._next = value; } attach(state, subject, branch, attachNext = true) { let inst = this.instances.get(subject); if (inst === undefined) { inst = this.createInstance(state, subject, branch); branch.connect(inst); this.instances.set(subject, inst); } else { inst.modified(); } if (!(attachNext && inst && this.next)) { return inst; } this.next.attach(inst.state, inst.subject, inst); return inst; } detach(subject) { this.instances.delete(subject); } } /*** * A type represents the state within an {@link Effect} tree. */ class State { get properties() { return this._properties; } get mutator() { return this._mutator; } get collector() { return this._collector; } constructor(collector, mutator, properties) { this._collector = collector; this._mutator = mutator; this._properties = properties || new Map(); } } class AbstractBinding extends AbstractAction { get stateProperty() { return this._stateProperty; } get state() { return this._localState; } get localProperties() { return this._localProperties; } get transform() { return this._transform || ((v) => v); } constructor(state, stateProperty, subject, transform, factory) { super(state, subject, factory); this._stateProperty = stateProperty; this._transform = transform; this._localProperties = new Map(state.properties); this._localState = new State(state.collector, state.mutator, this._localProperties); } mutated() { const transformedValue = this.transform ? this.transform(this.value) : this.value; if (transformedValue === this._previousValue) { return; } this._previousValue = transformedValue; this.localProperties.set(this.stateProperty, transformedValue); } } class AbstractBindingFactory extends AbstractActionFactory { get stateProperty() { return this._stateProperty; } get transform() { return this._transform; } constructor(stateProperty, transform) { super(); this._stateProperty = stateProperty; this._transform = transform; } } class AttributeBinding extends AbstractBinding { constructor(state, stateProperty, subject, attrName, transform, factory) { super(state, stateProperty, subject, transform, factory); this._attrName = attrName; this.mutated(); } get value() { return this.subject.getAttribute(this._attrName); } modified() { var _a; if (!((_a = this.factory) === null || _a === void 0 ? void 0 : _a.once)) { this.mutated(); } super.modified(); } } class AttributeBindingFactory extends AbstractBindingFactory { constructor(stateProperty, attrName, transform) { super(stateProperty, transform); this._attrName = attrName; } createInstance(state, subject) { return new AttributeBinding(state, this.stateProperty, subject, this._attrName, this.transform, this); } } class ContentBinding extends AbstractBinding { constructor(state, stateProperty, subject, transform, factory) { super(state, stateProperty, subject, transform, factory); this.mutated(); } get value() { return this.subject.textContent; } modified() { var _a; if (!((_a = this.factory) === null || _a === void 0 ? void 0 : _a.once)) { this.mutated(); } super.modified(); } } class ContentBindingFactory extends AbstractBindingFactory { createInstance(state, subject) { return new ContentBinding(state, this.stateProperty, subject, this.transform, this); } } const CONTEXT_INITIALIZED = 'context.initialized'; const CONTEXT_VALUE_REMOVED = 'context.value.removed'; const CONTEXT_VALUE_ADDED = 'context.value.added'; const CONTEXT_VALUE_CHANGED = 'context.value.changed'; const CONTEXT_DESTROYED = 'context.destroyed'; class ContextBinding extends AbstractBinding { get value() { return this._currentValue; } constructor(state, stateProperty, propertyName, subject, transform, factory) { var _a; super(state, stateProperty, subject, transform, factory); this._propertyName = propertyName; if (!((_a = window.evolv) === null || _a === void 0 ? void 0 : _a.context)) { throw new EvolvError('Mutate: Evolv Context not found.'); } const handler = () => { const before = this._currentValue; this._currentValue = window.evolv.context.get(this._propertyName); if (this._currentValue !== before) { this.mutated(); } }; window.evolv.client.on(CONTEXT_INITIALIZED, handler); window.evolv.client.on(CONTEXT_VALUE_REMOVED, handler); window.evolv.client.on(CONTEXT_VALUE_ADDED, handler); window.evolv.client.on(CONTEXT_VALUE_CHANGED, handler); window.evolv.client.on(CONTEXT_DESTROYED, handler); } } class ContextBindingFactory extends AbstractBindingFactory { constructor(stateProperty, propertyName, transform) { super(stateProperty, transform); this._propertyName = propertyName; } createInstance(state, subject) { return new ContextBinding(state, this.stateProperty, this._propertyName, subject, this.transform, this); } } class ComputedBinding extends AbstractBinding { get value() { return this._currentValue; } constructor(computable, state, stateProperty, subject, factory) { super(state, stateProperty, subject, undefined, factory); this.computable = computable; this._currentValue = computable(state, subject, undefined); this.mutated(); } modified() { var _a; if (!((_a = this.factory) === null || _a === void 0 ? void 0 : _a.once)) { this._currentValue = this.computable(this.state, this.subject, this._currentValue); this.mutated(); } super.modified(); } } class ComputedBindingFactory extends AbstractBindingFactory { constructor(stateProperty, computable) { super(stateProperty); this.computable = computable; } createInstance(state, subject) { return new ComputedBinding(this.computable, state, this.stateProperty, subject, this); } } class AbstractEffect extends AbstractAction { computeValue(subject, value, name) { return typeof value === 'function' ? value(this.state, this.subject, subject.getAttribute(name)) : value; } } class AbstractEffectFactory extends AbstractActionFactory { } class AttributeModifiedEffect extends AbstractEffect { constructor(state, subject, name, value, factory) { super(state, subject, factory); this._name = name; this._value = value; // @ts-ignore if (subject instanceof Element && subject.getAttribute(this._name) !== value) { // @ts-ignore this._previousValue = subject.getAttribute(this._name); } this._set(this.subject, this._value); } _set(subject, value) { const actualValue = this.computeValue(subject, value, this._name); if (actualValue === undefined || actualValue === null) { subject.removeAttribute(this._name); return; } // @ts-ignore if (subject instanceof Element && subject.getAttribute(this._name) !== value) { // @ts-ignore subject.setAttribute(this._name, actualValue); } } modified() { var _a; if (!((_a = this.factory) === null || _a === void 0 ? void 0 : _a.once)) { this._set(this.subject, this._value); } super.modified(); } revert() { var _a; // Revert the children before the parent in case some downstream changes are dependent on this change. super.revert(); this._set(this.subject, (_a = this._previousValue) !== null && _a !== void 0 ? _a : ''); } } class AttributeModifiedEffectFactory extends AbstractEffectFactory { constructor(name, value) { super(); this._name = name; this._value = value; } createInstance(state, subject) { return new AttributeModifiedEffect(state, subject, this._name, this._value, this); } } class ClassModifiedEffect extends AbstractEffect { constructor(state, subject, name, value, factory) { super(state, subject, factory); this._previousValue = null; this._name = name; this._value = value; this._previousValue = subject.classList.contains(this._name); this._set(this.subject, this._value); } _set(subject, value) { const actualValue = this.computeValue(subject, value, this._name); if (actualValue && !subject.classList.contains(this._name)) { subject.classList.add(this._name); } else if (!actualValue && subject.classList.contains(this._name)) { subject.classList.remove(this._name); } } modified() { var _a; if (!((_a = this.factory) === null || _a === void 0 ? void 0 : _a.once)) { this._set(this.subject, this._value); } super.modified(); } revert() { // Revert the children before the parent in case some downstream changes are dependent on this change. super.revert(); this._set(this.subject, this._previousValue); } } class ClassModifiedEffectFactory extends AbstractEffectFactory { constructor(name, value) { super(); this._name = name; this._value = value; } createInstance(state, subject) { return new ClassModifiedEffect(state, subject, this._name, this._value, this); } } class CustomMutation extends AbstractEffect { constructor(state, subject, initialize, modify, revert, remove, factory) { super(state, subject, factory); this._initialize = initialize; this._modify = modify; this._revert = revert; this._remove = remove; if (this._initialize) { this._initialize(state, subject); } } modified() { var _a; if (this._modify && !((_a = this.factory) === null || _a === void 0 ? void 0 : _a.once)) { this._modify(this.state, this.subject); } super.modified(); } revert() { // Revert the children before the parent in case some downstream changes are dependent on this change. super.revert(); if (this._revert) { this._revert(this.state, this.subject); } } removed() { if (this._remove) { this._remove(this.state, this.subject); } super.removed(); } } class CustomMutationFactory extends AbstractEffectFactory { constructor(initialize, modify, revert, remove) { super(); this._initialize = initialize; this._modify = modify; this._revert = revert; this._remove = remove; } createInstance(state, subject) { return new CustomMutation(state, subject, this._initialize, this._modify, this._revert, this._remove, this); } } const create = (html) => { const template = document.createElement('template'); template.innerHTML = html; return Array.from(template.content.childNodes); }; /*** * Compares two {@link Array}s of primitives to determine if they are equal * @param array1 The first {@link Array} to compare * @param array1 The second {@link Array} to compare */ const areEqual$1 = (array1, array2) => { if (array1.length !== array2.length) { return false; } for (let i = 0; i < array1.length; i++) { if (array1[i] !== array2[i]) { return false; } } return true; }; /*** * Compare two {@link Map}s containing primitives or {@link Array}s * * @param map1 The first {@link Map} to compare * @param map2 The second {@link Map} to compare */ const areEqual = (map1, map2) => { if (map1.size !== map2.size) { return false; } for (const [key, value1] of map1) { const value2 = map2.get(key); if (Array.isArray(value1) && Array.isArray(value2)) { if (!areEqual$1(value1, value2)) { return false; } } else if (value2 !== value1 || (value2 === undefined && !map2.has(key))) { return false; } } return true; }; class InjectNodesEffect extends AbstractEffect { constructor(state, subject, injectable, inserter, context, factory) { super(state, subject, factory); this._injectable = injectable; this._reverse = context.get('reverse'); this._inserter = inserter; this._nodesInjected = []; this._previousProperties = new Map(state.properties); this.injectNodes(); } injectNodes(nodes = this.nodes) { nodes.forEach((node) => { this._nodesInjected = [ ...this._nodesInjected, ...this._inserter(this.subject, node), ]; }); } removeInjectedNodes() { this._nodesInjected.forEach((node) => { if (node instanceof Text || node instanceof Element) { node.remove(); } }); this._nodesInjected = []; } get nodes() { let nodes; function handleInjectableType(injectable) { if (typeof injectable === 'string') { return create(injectable); } else if (injectable instanceof Node) { return [injectable]; } else if (Array.isArray(injectable) || injectable instanceof NodeList || injectable instanceof HTMLCollection) { return [...injectable]; } else if (injectable === null || injectable === undefined) { return []; } try { return create(String(injectable)); } catch (_a) { return []; } } if (typeof this._injectable === 'function') { nodes = handleInjectableType(this._injectable(this.state, this.subject)); } else { nodes = handleInjectableType(this._injectable); } return this._reverse ? nodes.reverse() : nodes; } modified() { var _a; if (!((_a = this.factory) === null || _a === void 0 ? void 0 : _a.once) && (this._nodesInjected.length === 0 || this._nodesInjected.some((injectedNode) => !injectedNode.isConnected) || (this._injectable instanceof Function && !areEqual(this._previousProperties, this.state.properties)))) { this.removeInjectedNodes(); this.injectNodes(); } this._previousProperties = new Map(this.state.properties); super.modified(); } revert() { // Revert the children before the parent in case some downstream changes are dependent on this change. super.revert(); this.removeInjectedNodes(); } } class InjectNodesEffectFactory extends AbstractEffectFactory { constructor(injectable, inserter, context) { super(); this._injectable = injectable; this._inserter = inserter; this._context = context; } createInstance(state, subject) { return new InjectNodesEffect(state, subject, this._injectable, this._inserter, this._context, this); } } class AbstractNodeEffect extends AbstractEffect { get node() { return this._node; } constructor(state, subject, node) { super(state, subject); this._node = node; } append() { var _a; this.subject.appendChild((_a = this.node) !== null && _a !== void 0 ? _a : this.subject); } remove() { var _a; (_a = this.node) === null || _a === void 0 ? void 0 : _a.remove(); } unhide(toUnhide, originalDisplayValue) { toUnhide === null || toUnhide === void 0 ? void 0 : toUnhide.style.setProperty('display', originalDisplayValue !== null && originalDisplayValue !== void 0 ? originalDisplayValue : ''); } hide(toHide) { toHide === null || toHide === void 0 ? void 0 : toHide.style.setProperty('display', 'none'); } } class AbstractNodeEffectFactory extends AbstractEffectFactory { } class InsertNodeEffect extends AbstractNodeEffect { constructor(state, subject, node) { super(state, subject, node); if (this.node instanceof Element) { this.addMutatorIdsToTree(node, true); } this.append(); } modified() { var _a; if (!((_a = this.node) === null || _a === void 0 ? void 0 : _a.isConnected)) { this.addMutatorIdsToTree(this.node, true); this.append(); } super.modified(); } revert() { var _a; // Revert the children before the parent in case some downstream changes are dependent on this change. super.revert(); if (this.subject === ((_a = this.node) === null || _a === void 0 ? void 0 : _a.parentNode)) { this.remove(); } } } class InsertNodeEffectFactory extends AbstractNodeEffectFactory { constructor(node, clone = true) { super(); this._node = node; this._clone = clone; } createInstance(state, subject) { if (elementHasMutateSrc(subject, state.mutator.id)) { throw new Error('Evolv AI: A previous mutation from this mutator has been identified by the Collector.' + ' This indicates it will be overwritten or may cause an infinite loop.' + ' Please make your selector more specific.'); } return new InsertNodeEffect(state, subject, this._clone ? this._node.cloneNode(true) : this._node); } } var InsertPosition; (function (InsertPosition) { InsertPosition[InsertPosition["APPEND"] = 0] = "APPEND"; InsertPosition[InsertPosition["PREPEND"] = 1] = "PREPEND"; InsertPosition[InsertPosition["BEFORE"] = 2] = "BEFORE"; InsertPosition[InsertPosition["AFTER"] = 3] = "AFTER"; })(InsertPosition || (InsertPosition = {})); class Locator { constructor(collectorName, context, handler) { var _a; this._collectorName = collectorName; this._context = context; this._clone = (_a = this._context.get('clone')) !== null && _a !== void 0 ? _a : false; this._handler = handler; } static insertNode(subject, toInsert, insertPosition, clone) { var _a, _b, _c; const node = clone ? toInsert.cloneNode(true) : toInsert; switch (insertPosition) { case InsertPosition.APPEND: if (subject.lastChild !== node) { subject.appendChild(node); } break; case InsertPosition.PREPEND: const { firstChild } = subject; if (!firstChild) { subject.appendChild(node); } else if (firstChild !== node) { subject.insertBefore(node, firstChild); } break; case InsertPosition.BEFORE: if (subject.previousSibling !== node) { (_a = subject.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(node, subject); } break; case InsertPosition.AFTER: const { nextSibling } = subject; if (!nextSibling) { (_b = subject.parentNode) === null || _b === void 0 ? void 0 : _b.appendChild(node); } else if (nextSibling !== node) { (_c = subject.parentNode) === null || _c === void 0 ? void 0 : _c.insertBefore(node, nextSibling); } break; } return node; } createRelativeInserter(selector, position) { this._context.set('position', position); return (subject, toInsert) => { let siblings; if (this._clone) { siblings = subject.querySelectorAll(selector); } else { siblings = [subject.querySelector(selector)]; } const nodes = []; siblings.forEach((sibling) => { nodes.push(Locator.insertNode(sibling, toInsert, position, this._clone)); }); return nodes; }; } createInserter(position) { this._context.set('position', position); return (subject, toInsert) => { return [Locator.insertNode(subject, toInsert, position, this._clone)]; }; } append() { this._context.set('reverse', false); return this._handler(this._collectorName, this._context, this.createInserter(InsertPosition.APPEND)); } prepend() { this._context.set('reverse', true); return this._handler(this._collectorName, this._context, this.createInserter(InsertPosition.PREPEND)); } before(selector) { let inserter; this._context.set('reverse', false); if (selector) { inserter = this.createRelativeInserter(selector, InsertPosition.BEFORE); } else { inserter = this.createInserter(InsertPosition.BEFORE); } return this._handler(this._collectorName, this._context, inserter); } after(selector) { let inserter; this._context.set('reverse', true); if (selector) { inserter = this.createRelativeInserter(selector, InsertPosition.AFTER); } else { inserter = this.createInserter(InsertPosition.AFTER); } return this._handler(this._collectorName, this._context, inserter); } } class Placeholder extends HTMLElement { get linked() { return this._linked; } constructor() { super(); this._shadow = this.attachShadow({ mode: 'open' }); } connectedCallback() { this._shadow.innerHTML = ` <style> :host { display: none !important; } </style> `; } link(value) { this._linked = value; } restore() { if (this._linked) { this.replaceWith(this._linked); } else { this.remove(); } } } const initPlaceholder = () => { if (!customElements.get('mutate-placeholder')) { customElements.define('mutate-placeholder', Placeholder); } }; function isEqual(left, right) { if (left.size !== right.size) { return false; } for (const a of left) { if (!right.has(a)) { return false; } } return true; } function valueAt(set, position) { for (const value of set.entries()) { { return value[0]; } } } // The traversal needed to find an element inserted by the method corresponding to InsertPosition const INSERT_POSITION_TRAVERSAL = { 0: 'lastChild', // append 1: 'firstChild', // prepend 2: 'previousSibling', // before 3: 'nextSibling', // after }; class RelocateNodeEffect extends AbstractEffect { get factory() { return this._factory; } get target() { let resolvedTarget; const handleTargetType = (target) => { let resolvedCollector; let targetCollectorName; // Static targets can use the cached targetCollector if (typeof this._target === 'string' && this._targetCollectors.size) { resolvedCollector = valueAt(this._targetCollectors); } else if (typeof target === 'string') { targetCollectorName = SCOPE(target, this.state.collector.scope); resolvedCollector = this.state.mutator.collectors.get(targetCollectorName); } if (!resolvedCollector) { this._log.error(`Mutator.relocate(): Collector '${targetCollectorName}' not found`); return; } if (resolvedCollector.elements.some((element) => { var _a, _b; return (_b = (_a = element.__evolv__) === null || _a === void 0 ? void 0 : _a.collectors) === null || _b === void 0 ? void 0 : _b.has(this.state.collector.id); })) { this._log.error(`Mutator.relocate(): Cannot relocate an element onto itself`); return; } if (!this._targetCollectors.has(resolvedCollector)) { resolvedCollector.addListener(() => this.modified()); this._targetCollectors.add(resolvedCollector); } // Static collectors can contain disconnected elements return ((resolvedCollector === null || resolvedCollector === void 0 ? void 0 : resolvedCollector.elements.find((element) => element.isConnected)) || undefined); }; if (typeof this._target === 'function') { resolvedTarget = handleTargetType(this._target(this.state, this.subject)); } else { resolvedTarget = handleTargetType(this._target); } return resolvedTarget; } constructor(state, subject, target, inserter, context, factory) { super(state, subject, factory); this._target = target; this._reverse = context.get('reverse'); this._position = context.get('position'); this._inserter = inserter; this._targetCollectors = new Set(); this._firstTraversal = INSERT_POSITION_TRAVERSAL[this._position]; this._siblingTraversal = this._reverse ? 'nextSibling' : 'previousSibling'; this._log = this.state.collector.log; this.initialized(); } // Traverses relocated nodes and finds the position to place the subject such that it maintains // the original document order. If it is already in the correct position no action is taken because Locator.insertNode() // checks before placing. _relocate() { const subject = this.subject; const target = this.target; if (target && subject.contains(target)) { this._log.error(`Mutator.relocate(): Target cannot be a descendant of a node being relocated`); return; } if (!target) { return; } const { nodes, relocatedNodes } = this.factory; // The first node to check for its position relative to the subject let currentRelocatedNode = this._nextNodeNotSubject(target, nodes, relocatedNodes); // "Ahead" means ahead of the position to insert subject in the direction of traversal. // For append() and before() Locators, ahead is the nextSibling, for prepend() // and after() ahead is the previousSibling. let isCurrentNodeAbsentOrAhead = this._isNodeAhead(subject, currentRelocatedNode, nodes); let targetNode; let inserter; if (!currentRelocatedNode) { targetNode = target; inserter = this._inserter; } else { for (let i = 0; i < nodes.length; i++) { if (isCurrentNodeAbsentOrAhead) { targetNode = currentRelocatedNode; inserter = this._createSiblingInserter(true); break; } const nextRelocatedNode = this._nextNodeNotSubject(currentRelocatedNode, nodes, relocatedNodes, false); const isNextNodeAbsentOrAhead = this._isNodeAhead(subject, nextRelocatedNode, nodes); if (isNextNodeAbsentOrAhead) { targetNode = currentRelocatedNode; inserter = this._createSiblingInserter(false); break; } currentRelocatedNode = nextRelocatedNode; isCurrentNodeAbsentOrAhead = isNextNodeAbsentOrAhead; } } if (!targetNode) { return; } const placeholder = (!relocatedNodes.has(subject) && this._createPlaceholder(subject)) || null; if (inserter) { inserter(targetNode, subject); } // Placeholder is only defined if it's created in this pass if (subject && placeholder) { this.factory.setRelocatedNode(subject, placeholder); } } _nextNodeNotSubject(startNode, nodes, relocatedNodes, isCurrent = true) { if (!startNode) { return null; } const firstTraversal = isCurrent && (!relocatedNodes.has(startNode) || this.subject === nodes[0]) ? this._firstTraversal : this._siblingTraversal; const traversed = startNode[firstTraversal]; const nextNotSubject = traversed === this.subject ? traversed === null || traversed === void 0 ? void 0 : traversed[this._siblingTraversal] : traversed; return nextNotSubject && nodes.includes(nextNotSubject) ? nextNotSubject : null; } _isNodeAhead(node, otherNode, nodes) { if (!otherNode) { return true; } // `nodes` have already been sorted by document order return this._reverse !== nodes.indexOf(otherNode) < nodes.indexOf(node); // XOR } _createSiblingInserter(isCurrent) { const insertPosition = this._reverse === isCurrent ? InsertPosition.BEFORE : InsertPosition.AFTER; // XNOR return (targetNode, toInsert) => [ Locator.insertNode(targetNode, toInsert, insertPosition), ]; // insertNode returns inserted node } _createPlaceholder(node) { var _a; const placeholder = new Placeholder(); placeholder.link(node); (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(placeholder, node); return placeholder; } // An orphaned node is one that's been relocated and its placeholder has been removed _removeOrphanedNodes() { var _a; for (const [node, placeholder] of this.factory.relocatedNodes.entries()) { if (!placeholder.isConnected && !this.factory.restoredNodes.includes(node)) { (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node); } } } // An orphaned placeholder's node has been removed. _removeOrphanedPlaceholders() { for (const [node, placeholder] of this.factory.relocatedNodes.entries()) { if (!node.isConnected) { placeholder.remove(); } } } _restoreNodes() { for (const [node, placeholder] of this.factory.relocatedNodes.entries()) { if (placeholder.isConnected) { this.factory.addRestoredNode(node); placeholder.restore(); } } } initialized() { this.factory.setNodes(this.state.collector.elements); this._relocate(); } modified() { var _a; if (!((_a = this.factory) === null || _a === void 0 ? void 0 : _a.once)) { this._relocate(); } super.modified(); } // public override removed() { // this.factory.setNodes(this.state.collector.elements as Node[]); // if (this.target?.isConnected) { // this.factory.deleteRelocatedNode(this.subject); // } // } revert() { // Revert the children before the parent in case some downstream changes are dependent on this change. super.revert(); this._restoreNodes(); this._removeOrphanedNodes(); this._removeOrphanedPlaceholders(); } } class RelocateNodeEffectFactory extends AbstractEffectFactory { get nodes() { return this._nodesSorted; } get relocatedNodes() { return new Map(this._relocatedNodes); } get restoredNodes() { return [...this._restoredNodes]; } constructor(target, inserter, context) { super(); this._target = target; this._inserter = inserter; this._context = context; this._nodesPrevious = []; this._nodesSorted = []; this._relocatedNodes = new Map(); this._restoredNodes = new Set(); initPlaceholder(); } setNodes(nodes) { if (areEqual$1(nodes, this._nodesPrevious)) { return; } // If there are no nodes fall back to references stored in relocatedNodes. Allows resiliency // if a target is destroyed and replaced. const resolvedNodes = nodes.length ? nodes : [...this._relocatedNodes.keys()]; this._nodesPrevious = nodes; this._nodesSorted = resolvedNodes.sort((a, b) => { const placementA = this._relocatedNodes.get(a) || a; const placementB = this._relocatedNodes.get(b) || b; const position = placementB.compareDocumentPosition(placementA); if (position & Node.DOCUMENT_POSITION_FOLLOWING) { return 1; // b should be after a } else if (position & Node.DOCUMENT_POSITION_PRECEDING) { return -1; // b should be before a } return 0; }); } setRelocatedNode(node, placeholder) { this._relocatedNodes.set(node, placeholder); } deleteRelocatedNode(node) { this._relocatedNodes.delete(node); } addRestoredNode(node) { this._restoredNodes.add(node); } createInstance(state, subject) { return new RelocateNodeEffect(state, subject, this._target, this._inserter, this._context, this); } } class RemoveNodeEffect extends AbstractNodeEffect { constructor(state, subject, node) { super(state, subject, node); this._toRemove = node !== null && node !== void 0 ? node : subject; this._originalDisplayValue = this._toRemove.style.display; this.hide(this._toRemove); } modified() { this.hide(this._toRemove); super.modified(); } revert() { // Revert the children before the parent in case some downstream changes are dependent on this change. super.revert(); this.unhide(this._toRemove, this._originalDisplayValue); } } class RemoveNodeEffectFactory extends AbstractNodeEffectFactory { constructor(node) { super(); this._node = node; } createInstance(state, subject) { return new RemoveNodeEffect(state, subject, this._node); } } class ReplaceNodeEffect extends AbstractNodeEffect { constructor(state, subject, node) { super(state, subject, node); this.append(); } modified() { this.append(); super.modified(); } revert() { // Revert the children before the parent in case some downstream changes are dependent on this change. super.revert(); this.remove(); } } class ReplaceNodeEffectFactory extends AbstractNodeEffectFactory { constructor(node) { super(); this._node = node; } createInstance(state, subject) { return new ReplaceNodeEffect(state, subject, this._node); } } class StyleModifiedEffect extends AbstractEffect { constructor(state, subject, name, value, factory) { super(state, subject, factory); this.TRANSITION_DEFINITION = 'evolvOriginalTransition'; this.TRANSITION_DEFINITION_INLINE = 'evolvOriginalTransitionInline'; this._previousValue = null; this._name = name; this._value = value; this._camelCaseName = name.replace(/-([a-z])/g, (g) => g[1].toUpperCase()); this._set(this.subject, this._value); } _set(subject, value) { const actualValue = this.computeValue(subject, value, this._name); if (subject instanceof HTMLElement && (this._computedValueFor !== actualValue || subject.style[this._camelCaseName] !== this._computedValue)) { this.pauseTransitions(this.subject); this._previousValue = subject.style[this._camelCaseName]; subject.style[this._camelCaseName] = actualValue; this._computedValueFor = actualValue; this._computedValue = subject.style[this._camelCaseName]; setTimeout(() => { this.resumeTransitions(this.subject); }, 0); } } modified() { var _a; if (!((_a = this.factory) === null || _a === void 0 ? void 0 : _a.once)) { this._set(this.subject, this._value); } super.modified(); } revert() { // Revert the children before the parent in case some downstream changes are dependent on this change. super.revert(); if (this.subject instanceof HTMLElement) { this.pauseTransitions(this.subject); this.subject.style[this._camelCaseName] = this._previousValue; setTimeout(() => { this.resumeTransitions(this.subject); }, 0); } } pauseTransitions(subject) { if (subject instanceof HTMLElement) { this.setAndStoreIsTransitionInline(subject); this.setAndStoreTransitionDefinition(subject); if (this.getTransitionDefinition(subject)) { subject.style.transition = 'none'; } } } resumeTransitions(subject) { if (subject instanceof HTMLElement && this.getTransitionDefinition(subject)) { if (this.isTransitionInline(subject)) { subject.style.transition = this.getTransitionDefinition(subject); } else { subject.style.transition = ''; } this.clearStoredTransitionData(subject); } } // We need to know if transition was originally set inline or by style to correctly revert. // Use a data attribute to store this information to be visible across multiple StyleModifiedEffects setAndStoreIsTransitionInline(subject) { const unset = subject.dataset[this.TRANSITION_DEFINITION_INLINE] === undefined; if (unset) { subject.dataset[this.TRANSITION_DEFINITION_INLINE] = (!!subject.style .transition).toString(); } } isTransitionInline(subject) { return subject.dataset[this.TRANSITION_DEFINITION_INLINE] === 'true'; } setAndStoreTransitionDefinition(subject) { const storedTransition = this.getTransitionDefinition(subject); if (!storedTransition) { subject.dataset[this.TRANSITION_DEFINITION] = getComputedStyle(subject).transition; } } getTransitionDefinition(subject) { return subject.dataset[this.TRANSITION_DEFINITION] || ''; } clearStoredTransitionData(subject) { delete subject.dataset[this.TRANSITION_DEFINITION]; delete subject.dataset[this.TRANSITION_DEFINITION_INLINE]; } } class StyleModifiedEffectFactory extends AbstractEffectFactory { constructor(name, value) { super(); this._name = name; this._value = value; } createInstance(state, subject) { return new StyleModifiedEffect(state, subject, this._name, this._value, this); } } class TextModifiedEffect extends AbstractEffect { get text() { const handleTextType = (text) => { if (typeof text === 'string') { return text; } else if (text === undefined || text === null) { return ''; } else { try { return String(text); } catch (_a) { return ''; } } }; return this._text instanceof Function ? handleTextType(this._text(this.state, this.subject, this._originalText)) : handleTextType(this._text); } constructor(state, subject, text, factory) { super(state, subject, factory); this._text = text; this._originalText = subject.textContent; this._set(this.text); } _set(value) { const element = this.subject; if (element.textContent !== value) { element.textContent = value; } } modified() { var _a; if (!((_a = this.factory) === null || _a === void 0 ? void 0 : _a.once)) { this._set(this.text);