snappymail/vendors/knockout/src/utils.domNodeDisposal.js

106 lines
4.5 KiB
JavaScript

ko.utils.domNodeDisposal = new (function () {
var domDataKey = ko.utils.domData.nextKey();
var cleanableNodeTypes = { 1: true, 8: true, 9: true }; // Element, Comment, Document
var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document
function getDisposeCallbacksCollection(node, createIfNotFound) {
var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
if ((allDisposeCallbacks === undefined) && createIfNotFound) {
allDisposeCallbacks = [];
ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
}
return allDisposeCallbacks;
}
function destroyCallbacksCollection(node) {
ko.utils.domData.set(node, domDataKey, undefined);
}
function cleanSingleNode(node) {
// Run all the dispose callbacks
var callbacks = getDisposeCallbacksCollection(node, false);
if (callbacks) {
callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
for (var i = 0; i < callbacks.length; i++)
callbacks[i](node);
}
// Erase the DOM data
ko.utils.domData.clear(node);
// Perform cleanup needed by external libraries (currently only jQuery, but can be extended)
ko.utils.domNodeDisposal["cleanExternalData"](node);
// Clear any immediate-child comment nodes, as these wouldn't have been found by
// node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
if (cleanableNodeTypesWithDescendants[node.nodeType]) {
cleanNodesInList(node.childNodes, true/*onlyComments*/);
}
}
function 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-- && ko.utils.arrayIndexOf(cleanedNodes, nodeList[i]) == -1) {}
}
}
}
}
return {
addDisposeCallback : function(node, callback) {
if (typeof callback != "function")
throw new Error("Callback must be a function");
getDisposeCallbacksCollection(node, true).push(callback);
},
removeDisposeCallback : function(node, callback) {
var callbacksCollection = getDisposeCallbacksCollection(node, false);
if (callbacksCollection) {
ko.utils.arrayRemoveItem(callbacksCollection, callback);
if (callbacksCollection.length == 0)
destroyCallbacksCollection(node);
}
},
cleanNode : function(node) {
ko.dependencyDetection.ignore(function () {
// First clean this node, where applicable
if (cleanableNodeTypes[node.nodeType]) {
cleanSingleNode(node);
// ... then its descendants, where applicable
if (cleanableNodeTypesWithDescendants[node.nodeType]) {
cleanNodesInList(node.getElementsByTagName("*"));
}
}
});
return node;
},
removeNode : function(node) {
ko.cleanNode(node);
if (node.parentNode)
node.parentNode.removeChild(node);
},
'cleanExternalData' : function (node) {
// Special support for jQuery here because it's so commonly used.
// Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
// so notify it to tear down any resources associated with the node & descendants here.
if (jQuery && jQuery['cleanData'])
jQuery['cleanData']([node]);
}
};
})();
ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
ko.exportSymbol('cleanNode', ko.cleanNode);
ko.exportSymbol('removeNode', ko.removeNode);
ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);