UNPKG

@xysfe/memento-core

Version:

record and replay the web

431 lines (428 loc) 17.7 kB
import { __spread, __values } from '../../node_modules/tslib/tslib.es6.js'; import { transformAttribute, serializeNodeWithId } from '../../node_modules/@xysfe/memento-snapshot/es/memento-snapshot.js'; import { isBlocked, mirror, isAncestorRemoved } from '../utils.js'; var moveKey = function (id, parentId) { return id + "@" + parentId; }; function isINode(n) { return '__sn' in n; } function isNodeInLinkedList(n) { return '__ln' in n; } var DoubleLinkedList = (function () { function DoubleLinkedList() { this.length = 0; this.head = null; } DoubleLinkedList.prototype.get = function (position) { if (position >= this.length) { throw new Error('Position outside of list range'); } var current = this.head; for (var index = 0; index < position; index++) { current = (current === null || current === void 0 ? void 0 : current.next) || null; } return current; }; DoubleLinkedList.prototype.addNode = function (n) { var node = { value: n, previous: null, next: null, }; n.__ln = node; if (n.previousSibling && isNodeInLinkedList(n.previousSibling)) { var current = n.previousSibling.__ln.next; node.next = current; node.previous = n.previousSibling.__ln; n.previousSibling.__ln.next = node; if (current) { current.previous = node; } } else if (n.nextSibling && isNodeInLinkedList(n.nextSibling) && n.nextSibling.__ln.previous) { var current = n.nextSibling.__ln.previous; node.previous = current; node.next = n.nextSibling.__ln; n.nextSibling.__ln.previous = node; if (current) { current.next = node; } } else { if (this.head) { this.head.previous = node; } node.next = this.head; this.head = node; } this.length++; }; DoubleLinkedList.prototype.removeNode = function (n) { var current = n.__ln; if (!this.head) { return; } if (!current.previous) { this.head = current.next; if (this.head) { this.head.previous = null; } } else { current.previous.next = current.next; if (current.next) { current.next.previous = current.previous; } } if (n.__ln) { delete n.__ln; } this.length--; }; return DoubleLinkedList; }()); var MutationBuffer = (function () { function MutationBuffer(cb, blockClass, inlineStylesheet, maskInputOptions, blockElements, asyncClass) { var _this = this; if (blockElements === void 0) { blockElements = []; } this.texts = []; this.attributes = []; this.removes = []; this.adds = []; this.movedMap = {}; this.addedSet = new Set(); this.movedSet = new Set(); this.droppedSet = new Set(); this.processMutations = function (mutations) { var e_1, _a, e_2, _b, e_3, _c, e_4, _d; _this.reset(); mutations.forEach(_this.processMutation); var texts = _this.texts; var attributes = _this.attributes; var removes = _this.removes; var adds = _this.adds; var addedSet = _this.addedSet; var movedSet = _this.movedSet; var droppedSet = _this.droppedSet; var changedSet = __spread(addedSet, movedSet); var asyncMutations = []; var addList = new DoubleLinkedList(); var getNextId = function (n) { var nextId = n.nextSibling && mirror.getId(n.nextSibling); if (nextId === -1 && isBlocked(n.nextSibling, _this.blockClass)) { nextId = null; } return nextId; }; var asyncPush = false; if (_this.asyncClass) { try { for (var changedSet_1 = __values(changedSet), changedSet_1_1 = changedSet_1.next(); !changedSet_1_1.done; changedSet_1_1 = changedSet_1.next()) { var item = changedSet_1_1.value; if (isBlocked(item.parentNode, _this.asyncClass)) { asyncPush = true; break; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (changedSet_1_1 && !changedSet_1_1.done && (_a = changedSet_1.return)) _a.call(changedSet_1); } finally { if (e_1) throw e_1.error; } } if (!asyncPush) { try { for (var attributes_1 = __values(attributes), attributes_1_1 = attributes_1.next(); !attributes_1_1.done; attributes_1_1 = attributes_1.next()) { var attribute = attributes_1_1.value; if (isBlocked(attribute.node.parentNode, _this.asyncClass)) { asyncPush = true; break; } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (attributes_1_1 && !attributes_1_1.done && (_b = attributes_1.return)) _b.call(attributes_1); } finally { if (e_2) throw e_2.error; } } } } var _pushAdd = function (n) { if (!n.parentNode) { return; } var parentId = mirror.getId(n.parentNode); var nextId = getNextId(n); if (parentId === -1 || nextId === -1) { return addList.addNode(n); } adds.push({ parentId: parentId, nextId: nextId, node: serializeNodeWithId(n, document, mirror.map, _this.blockClass, true, _this.inlineStylesheet, _this.maskInputOptions, _this.blockElements), }); }; var pushAdd = function (n) { if (asyncPush) { asyncMutations.push(new Promise(function (resolve) { _this.requestIdleCallback(function () { _pushAdd(n); resolve(); }); })); } else { _pushAdd(n); } }; try { for (var movedSet_1 = __values(movedSet), movedSet_1_1 = movedSet_1.next(); !movedSet_1_1.done; movedSet_1_1 = movedSet_1.next()) { var n = movedSet_1_1.value; pushAdd(n); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (movedSet_1_1 && !movedSet_1_1.done && (_c = movedSet_1.return)) _c.call(movedSet_1); } finally { if (e_3) throw e_3.error; } } try { for (var addedSet_1 = __values(addedSet), addedSet_1_1 = addedSet_1.next(); !addedSet_1_1.done; addedSet_1_1 = addedSet_1.next()) { var n = addedSet_1_1.value; if (!isAncestorInSet(droppedSet, n) && !isParentRemoved(removes, n)) { pushAdd(n); } else if (isAncestorInSet(movedSet, n)) { pushAdd(n); } else { droppedSet.add(n); } } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (addedSet_1_1 && !addedSet_1_1.done && (_d = addedSet_1.return)) _d.call(addedSet_1); } finally { if (e_4) throw e_4.error; } } var handleQueue = function () { var candidate = null; while (addList.length) { var node = null; if (candidate) { var parentId = mirror.getId(candidate.value.parentNode); var nextId = getNextId(candidate.value); if (parentId !== -1 && nextId !== -1) { node = candidate; } } if (!node) { for (var index = addList.length - 1; index >= 0; index--) { var _node = addList.get(index); var parentId = mirror.getId(_node.value.parentNode); var nextId = getNextId(_node.value); if (parentId !== -1 && nextId !== -1) { node = _node; break; } } } if (!node) { while (addList.head) { addList.removeNode(addList.head.value); } break; } candidate = node.previous; addList.removeNode(node.value); _pushAdd(node.value); } }; if (asyncPush) { var timestamp_1 = Date.now(); Promise.all(asyncMutations).then(function () { handleQueue(); _this.emit(texts, attributes, removes, adds, timestamp_1); }); } else { handleQueue(); _this.emit(texts, attributes, removes, adds); } }; this.emit = function (texts, attributes, removes, adds, t) { var payload = { texts: texts .map(function (text) { return ({ id: mirror.getId(text.node), value: text.value, }); }) .filter(function (text) { return mirror.has(text.id); }), attributes: attributes .map(function (attribute) { return ({ id: mirror.getId(attribute.node), attributes: attribute.attributes, }); }) .filter(function (attribute) { return mirror.has(attribute.id); }), removes: removes, adds: adds, timestamp: t, }; if (!payload.texts.length && !payload.attributes.length && !payload.removes.length && !payload.adds.length) { return; } _this.emissionCallback(payload); }; this.processMutation = function (m) { switch (m.type) { case 'characterData': { var value = m.target.textContent; if (!isBlocked(m.target, _this.blockClass, _this.blockElements) && value !== m.oldValue) { _this.texts.push({ value: value, node: m.target, }); } break; } case 'attributes': { var value = m.target.getAttribute(m.attributeName); if (isBlocked(m.target, _this.blockClass, _this.blockElements) || value === m.oldValue) { return; } var item = _this.attributes.find(function (a) { return a.node === m.target; }); if (!item) { item = { node: m.target, attributes: {}, }; _this.attributes.push(item); } item.attributes[m.attributeName] = transformAttribute(document, m.attributeName, value); var tagName = m.target.tagName.toLowerCase().trim(); var isStaticRes = (tagName === 'img' && m.attributeName === 'src') || (tagName === 'link' && m.attributeName === 'href'); var rel = m.target.rel; var isPreload = rel === 'dns-prefetch' || rel === 'preload'; if (isStaticRes && !isPreload && !/^data:image/i.test(item.attributes[m.attributeName])) { item.attributes["_memimage_" + m.attributeName] = item.attributes[m.attributeName]; } break; } case 'childList': { m.addedNodes.forEach(function (n) { return _this.genAdds(n, m.target); }); m.removedNodes.forEach(function (n) { var nodeId = mirror.getId(n); var parentId = mirror.getId(m.target); if (isBlocked(n, _this.blockClass, _this.blockElements) || isBlocked(m.target, _this.blockClass, _this.blockElements)) { return; } if (_this.addedSet.has(n)) { deepDelete(_this.addedSet, n); _this.droppedSet.add(n); } else if (_this.addedSet.has(m.target) && nodeId === -1) ; else if (isAncestorRemoved(m.target)) ; else if (_this.movedSet.has(n) && _this.movedMap[moveKey(nodeId, parentId)]) { deepDelete(_this.movedSet, n); } else { _this.removes.push({ parentId: parentId, id: nodeId, }); } mirror.removeNodeFromMap(n); }); break; } } }; this.genAdds = function (n, target) { if (isBlocked(n.parentNode, _this.blockClass, _this.blockElements)) { return; } if (isINode(n)) { _this.movedSet.add(n); var targetId = null; if (target && isINode(target)) { targetId = target.__sn.id; } if (targetId) { _this.movedMap[moveKey(n.__sn.id, targetId)] = true; } } else { _this.addedSet.add(n); _this.droppedSet.delete(n); } n.childNodes.forEach(function (childN) { return _this.genAdds(childN); }); }; this.reset = function () { _this.texts = []; _this.attributes = []; _this.removes = []; _this.adds = []; _this.addedSet = new Set(); _this.movedSet = new Set(); _this.droppedSet = new Set(); _this.movedMap = {}; }; this.requestIdleCallback = function (callback, options) { if (window.requestIdleCallback) { window.requestIdleCallback(callback, options); } else { var timeout = (options || { timeout: 0 }).timeout; window.setTimeout(callback, timeout); } }; this.blockClass = blockClass; this.blockElements = blockElements; this.asyncClass = asyncClass; this.inlineStylesheet = inlineStylesheet; this.maskInputOptions = maskInputOptions; this.emissionCallback = cb; } return MutationBuffer; }()); function deepDelete(addsSet, n) { addsSet.delete(n); n.childNodes.forEach(function (childN) { return deepDelete(addsSet, childN); }); } function isParentRemoved(removes, n) { var parentNode = n.parentNode; if (!parentNode) { return false; } var parentId = mirror.getId(parentNode); if (removes.some(function (r) { return r.id === parentId; })) { return true; } return isParentRemoved(removes, parentNode); } function isAncestorInSet(set, n) { var parentNode = n.parentNode; if (!parentNode) { return false; } if (set.has(parentNode)) { return true; } return isAncestorInSet(set, parentNode); } export { MutationBuffer as default };