UNPKG

mutation-summary

Version:
415 lines 18.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MutationProjection = void 0; var TreeChanges_1 = require("./TreeChanges"); var NodeMap_1 = require("./NodeMap"); var Movement_1 = require("./Movement"); var ChildListChange_1 = require("./ChildListChange"); var MutationProjection = /** @class */ (function () { // TOOD(any) function MutationProjection(rootNode, mutations, selectors, calcReordered, calcOldPreviousSibling) { this.rootNode = rootNode; this.mutations = mutations; this.selectors = selectors; this.calcReordered = calcReordered; this.calcOldPreviousSibling = calcOldPreviousSibling; this.treeChanges = new TreeChanges_1.TreeChanges(rootNode, mutations); this.entered = []; this.exited = []; this.stayedIn = new NodeMap_1.NodeMap(); this.visited = new NodeMap_1.NodeMap(); this.childListChangeMap = undefined; this.characterDataOnly = undefined; this.matchCache = undefined; this.processMutations(); } MutationProjection.prototype.processMutations = function () { if (!this.treeChanges.anyParentsChanged && !this.treeChanges.anyAttributesChanged) return; var changedNodes = this.treeChanges.keys(); for (var i = 0; i < changedNodes.length; i++) { this.visitNode(changedNodes[i], undefined); } }; MutationProjection.prototype.visitNode = function (node, parentReachable) { if (this.visited.has(node)) return; this.visited.set(node, true); var change = this.treeChanges.get(node); var reachable = parentReachable; // node inherits its parent's reachability change unless // its parentNode was mutated. if ((change && change.childList) || reachable == undefined) reachable = this.treeChanges.reachabilityChange(node); if (reachable === Movement_1.Movement.STAYED_OUT) return; // Cache match results for sub-patterns. this.matchabilityChange(node); if (reachable === Movement_1.Movement.ENTERED) { this.entered.push(node); } else if (reachable === Movement_1.Movement.EXITED) { this.exited.push(node); this.ensureHasOldPreviousSiblingIfNeeded(node); } else if (reachable === Movement_1.Movement.STAYED_IN) { var movement = Movement_1.Movement.STAYED_IN; if (change && change.childList) { if (change.oldParentNode !== node.parentNode) { movement = Movement_1.Movement.REPARENTED; this.ensureHasOldPreviousSiblingIfNeeded(node); } else if (this.calcReordered && this.wasReordered(node)) { movement = Movement_1.Movement.REORDERED; } } this.stayedIn.set(node, movement); } if (reachable === Movement_1.Movement.STAYED_IN) return; // reachable === ENTERED || reachable === EXITED. for (var child = node.firstChild; child; child = child.nextSibling) { this.visitNode(child, reachable); } }; MutationProjection.prototype.ensureHasOldPreviousSiblingIfNeeded = function (node) { if (!this.calcOldPreviousSibling) return; this.processChildlistChanges(); var parentNode = node.parentNode; var nodeChange = this.treeChanges.get(node); if (nodeChange && nodeChange.oldParentNode) parentNode = nodeChange.oldParentNode; var change = this.childListChangeMap.get(parentNode); if (!change) { change = new ChildListChange_1.ChildListChange(); this.childListChangeMap.set(parentNode, change); } if (!change.oldPrevious.has(node)) { change.oldPrevious.set(node, node.previousSibling); } }; MutationProjection.prototype.getChanged = function (summary, selectors, characterDataOnly) { this.selectors = selectors; this.characterDataOnly = characterDataOnly; for (var i = 0; i < this.entered.length; i++) { var node = this.entered[i]; var matchable = this.matchabilityChange(node); if (matchable === Movement_1.Movement.ENTERED || matchable === Movement_1.Movement.STAYED_IN) summary.added.push(node); } var stayedInNodes = this.stayedIn.keys(); for (var i = 0; i < stayedInNodes.length; i++) { var node = stayedInNodes[i]; var matchable = this.matchabilityChange(node); if (matchable === Movement_1.Movement.ENTERED) { summary.added.push(node); } else if (matchable === Movement_1.Movement.EXITED) { summary.removed.push(node); } else if (matchable === Movement_1.Movement.STAYED_IN && (summary.reparented || summary.reordered)) { var movement = this.stayedIn.get(node); if (summary.reparented && movement === Movement_1.Movement.REPARENTED) summary.reparented.push(node); else if (summary.reordered && movement === Movement_1.Movement.REORDERED) summary.reordered.push(node); } } for (var i = 0; i < this.exited.length; i++) { var node = this.exited[i]; var matchable = this.matchabilityChange(node); if (matchable === Movement_1.Movement.EXITED || matchable === Movement_1.Movement.STAYED_IN) summary.removed.push(node); } }; MutationProjection.prototype.getOldParentNode = function (node) { var change = this.treeChanges.get(node); if (change && change.childList) return change.oldParentNode ? change.oldParentNode : null; var reachabilityChange = this.treeChanges.reachabilityChange(node); if (reachabilityChange === Movement_1.Movement.STAYED_OUT || reachabilityChange === Movement_1.Movement.ENTERED) throw Error('getOldParentNode requested on invalid node.'); return node.parentNode; }; MutationProjection.prototype.getOldPreviousSibling = function (node) { var parentNode = node.parentNode; var nodeChange = this.treeChanges.get(node); if (nodeChange && nodeChange.oldParentNode) parentNode = nodeChange.oldParentNode; var change = this.childListChangeMap.get(parentNode); if (!change) throw Error('getOldPreviousSibling requested on invalid node.'); return change.oldPrevious.get(node); }; MutationProjection.prototype.getOldAttribute = function (element, attrName) { var change = this.treeChanges.get(element); if (!change || !change.attributes) throw Error('getOldAttribute requested on invalid node.'); var value = change.getAttributeOldValue(attrName); if (value === undefined) throw Error('getOldAttribute requested for unchanged attribute name.'); return value; }; MutationProjection.prototype.attributeChangedNodes = function (includeAttributes) { if (!this.treeChanges.anyAttributesChanged) return {}; // No attributes mutations occurred. var attributeFilter; var caseInsensitiveFilter; if (includeAttributes) { attributeFilter = {}; caseInsensitiveFilter = {}; for (var i = 0; i < includeAttributes.length; i++) { var attrName = includeAttributes[i]; attributeFilter[attrName] = true; caseInsensitiveFilter[attrName.toLowerCase()] = attrName; } } var result = {}; var nodes = this.treeChanges.keys(); for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; var change = this.treeChanges.get(node); if (!change.attributes) continue; if (Movement_1.Movement.STAYED_IN !== this.treeChanges.reachabilityChange(node) || Movement_1.Movement.STAYED_IN !== this.matchabilityChange(node)) { continue; } var element = node; var changedAttrNames = change.getAttributeNamesMutated(); for (var j = 0; j < changedAttrNames.length; j++) { var attrName = changedAttrNames[j]; if (attributeFilter && !attributeFilter[attrName] && !(change.isCaseInsensitive && caseInsensitiveFilter[attrName])) { continue; } var oldValue = change.getAttributeOldValue(attrName); if (oldValue === element.getAttribute(attrName)) continue; if (caseInsensitiveFilter && change.isCaseInsensitive) attrName = caseInsensitiveFilter[attrName]; result[attrName] = result[attrName] || []; result[attrName].push(element); } } return result; }; MutationProjection.prototype.getOldCharacterData = function (node) { var change = this.treeChanges.get(node); if (!change || !change.characterData) throw Error('getOldCharacterData requested on invalid node.'); return change.characterDataOldValue; }; MutationProjection.prototype.getCharacterDataChanged = function () { if (!this.treeChanges.anyCharacterDataChanged) return []; // No characterData mutations occurred. var nodes = this.treeChanges.keys(); var result = []; for (var i = 0; i < nodes.length; i++) { var target = nodes[i]; if (Movement_1.Movement.STAYED_IN !== this.treeChanges.reachabilityChange(target)) continue; var change = this.treeChanges.get(target); if (!change.characterData || target.textContent == change.characterDataOldValue) continue; result.push(target); } return result; }; MutationProjection.prototype.computeMatchabilityChange = function (selector, el) { if (!this.matchCache) this.matchCache = []; if (!this.matchCache[selector.uid]) this.matchCache[selector.uid] = new NodeMap_1.NodeMap(); var cache = this.matchCache[selector.uid]; var result = cache.get(el); if (result === undefined) { result = selector.matchabilityChange(el, this.treeChanges.get(el)); cache.set(el, result); } return result; }; MutationProjection.prototype.matchabilityChange = function (node) { var _this = this; // TODO(rafaelw): Include PI, CDATA? // Only include text nodes. if (this.characterDataOnly) { switch (node.nodeType) { case Node.COMMENT_NODE: case Node.TEXT_NODE: return Movement_1.Movement.STAYED_IN; default: return Movement_1.Movement.STAYED_OUT; } } // No element filter. Include all nodes. if (!this.selectors) return Movement_1.Movement.STAYED_IN; // Element filter. Exclude non-elements. if (node.nodeType !== Node.ELEMENT_NODE) return Movement_1.Movement.STAYED_OUT; var el = node; var matchChanges = this.selectors.map(function (selector) { return _this.computeMatchabilityChange(selector, el); }); var accum = Movement_1.Movement.STAYED_OUT; var i = 0; while (accum !== Movement_1.Movement.STAYED_IN && i < matchChanges.length) { switch (matchChanges[i]) { case Movement_1.Movement.STAYED_IN: accum = Movement_1.Movement.STAYED_IN; break; case Movement_1.Movement.ENTERED: if (accum === Movement_1.Movement.EXITED) accum = Movement_1.Movement.STAYED_IN; else accum = Movement_1.Movement.ENTERED; break; case Movement_1.Movement.EXITED: if (accum === Movement_1.Movement.ENTERED) accum = Movement_1.Movement.STAYED_IN; else accum = Movement_1.Movement.EXITED; break; } i++; } return accum; }; MutationProjection.prototype.getChildlistChange = function (el) { var change = this.childListChangeMap.get(el); if (!change) { change = new ChildListChange_1.ChildListChange(); this.childListChangeMap.set(el, change); } return change; }; MutationProjection.prototype.processChildlistChanges = function () { if (this.childListChangeMap) return; this.childListChangeMap = new NodeMap_1.NodeMap(); var _loop_1 = function (i) { var mutation = this_1.mutations[i]; if (mutation.type != 'childList') return "continue"; if (this_1.treeChanges.reachabilityChange(mutation.target) !== Movement_1.Movement.STAYED_IN && !this_1.calcOldPreviousSibling) return "continue"; var change = this_1.getChildlistChange(mutation.target); var oldPrevious = mutation.previousSibling; var recordOldPrevious = function (node, previous) { if (!node || change.oldPrevious.has(node) || change.added.has(node) || change.maybeMoved.has(node)) return; if (previous && (change.added.has(previous) || change.maybeMoved.has(previous))) return; change.oldPrevious.set(node, previous); }; for (var j = 0; j < mutation.removedNodes.length; j++) { var node = mutation.removedNodes[j]; recordOldPrevious(node, oldPrevious); if (change.added.has(node)) { change.added.delete(node); } else { change.removed.set(node, true); change.maybeMoved.delete(node); } oldPrevious = node; } recordOldPrevious(mutation.nextSibling, oldPrevious); for (var j = 0; j < mutation.addedNodes.length; j++) { var node = mutation.addedNodes[j]; if (change.removed.has(node)) { change.removed.delete(node); change.maybeMoved.set(node, true); } else { change.added.set(node, true); } } }; var this_1 = this; for (var i = 0; i < this.mutations.length; i++) { _loop_1(i); } }; MutationProjection.prototype.wasReordered = function (node) { if (!this.treeChanges.anyParentsChanged) return false; this.processChildlistChanges(); var parentNode = node.parentNode; var nodeChange = this.treeChanges.get(node); if (nodeChange && nodeChange.oldParentNode) parentNode = nodeChange.oldParentNode; var change = this.childListChangeMap.get(parentNode); if (!change) return false; if (change.moved) return change.moved.get(node); change.moved = new NodeMap_1.NodeMap(); var pendingMoveDecision = new NodeMap_1.NodeMap(); function isMoved(node) { if (!node) return false; if (!change.maybeMoved.has(node)) return false; var didMove = change.moved.get(node); if (didMove !== undefined) return didMove; if (pendingMoveDecision.has(node)) { didMove = true; } else { pendingMoveDecision.set(node, true); didMove = getPrevious(node) !== getOldPrevious(node); } if (pendingMoveDecision.has(node)) { pendingMoveDecision.delete(node); change.moved.set(node, didMove); } else { didMove = change.moved.get(node); } return didMove; } var oldPreviousCache = new NodeMap_1.NodeMap(); function getOldPrevious(node) { var oldPrevious = oldPreviousCache.get(node); if (oldPrevious !== undefined) return oldPrevious; oldPrevious = change.oldPrevious.get(node); while (oldPrevious && (change.removed.has(oldPrevious) || isMoved(oldPrevious))) { oldPrevious = getOldPrevious(oldPrevious); } if (oldPrevious === undefined) oldPrevious = node.previousSibling; oldPreviousCache.set(node, oldPrevious); return oldPrevious; } var previousCache = new NodeMap_1.NodeMap(); function getPrevious(node) { if (previousCache.has(node)) return previousCache.get(node); var previous = node.previousSibling; while (previous && (change.added.has(previous) || isMoved(previous))) previous = previous.previousSibling; previousCache.set(node, previous); return previous; } change.maybeMoved.keys().forEach(isMoved); return change.moved.get(node); }; return MutationProjection; }()); exports.MutationProjection = MutationProjection; //# sourceMappingURL=MutationProjection.js.map