ko.utils.domNodeDisposal = (() => { var domDataKey = ko.utils.domData.nextKey(); var cleanableNodeTypes = { 1: 1, 8: 1, 9: 1 }; // Element, Comment, Document var cleanableNodeTypesWithDescendants = { 1: 1, 9: 1 }; // Element, Document const getDisposeCallbacksCollection = (node, createIfNotFound) => { var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey); if (createIfNotFound && !allDisposeCallbacks) { allDisposeCallbacks = new Set; ko.utils.domData.set(node, domDataKey, allDisposeCallbacks); } return allDisposeCallbacks; }, destroyCallbacksCollection = node => ko.utils.domData.set(node, domDataKey, null), cleanSingleNode = node => { // Run all the dispose callbacks var callbacks = getDisposeCallbacksCollection(node); if (callbacks) { // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves) (new Set(callbacks)).forEach(callback => callback(node)); } // Erase the DOM data ko.utils.domData.clear(node); // Clear any immediate-child comment nodes, as these wouldn't have been found by // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements) cleanableNodeTypesWithDescendants[node.nodeType] && cleanNodesInList(node.childNodes, true/*onlyComments*/); }, cleanNodesInList = (nodeList, onlyComments) => { var cleanedNodes = [], lastCleanedNode; for (var i = 0; i < nodeList.length; i++) { if (!onlyComments || nodeList[i].nodeType === 8) { cleanSingleNode(cleanedNodes[cleanedNodes.length] = lastCleanedNode = nodeList[i]); if (nodeList[i] !== lastCleanedNode) { while (i-- && !cleanedNodes.includes(nodeList[i])); } } } }; return { addDisposeCallback : (node, callback) => { if (typeof callback != "function") throw new Error("Callback must be a function"); getDisposeCallbacksCollection(node, 1).add(callback); }, removeDisposeCallback : (node, callback) => { var callbacksCollection = getDisposeCallbacksCollection(node); if (callbacksCollection) { callbacksCollection.delete(callback); callbacksCollection.size || destroyCallbacksCollection(node); } }, cleanNode : node => { ko.dependencyDetection.ignore(() => { // First clean this node, where applicable if (cleanableNodeTypes[node.nodeType]) { cleanSingleNode(node); // ... then its descendants, where applicable cleanableNodeTypesWithDescendants[node.nodeType] && cleanNodesInList(node.getElementsByTagName("*")); } }); return node; }, removeNode : node => { ko.cleanNode(node); node.parentNode && node.parentNode.removeChild(node); } }; })(); ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal); ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);