@xysfe/memento-core
Version:
record and replay the web
431 lines (428 loc) • 17.7 kB
JavaScript
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 };