/** * Recreate `html` in `target` by making only small adjustments. * * *The following section explains details about the current implementation. * These are likely to change in the future.* * * The algorithms is relatively simple. It just iterates through all top level * nodes. If a node has a different `nodeType` (e.g. text or element) or a * different `nodeName` (e.g. div or ul) it is replaced completely. Otherwise, * only the nodes's attributes are updated and the algorithm proceeds with the * node's children recursively. * * Note that non-attribute properties (e.g. `value`) are lost in the first case * and preserved in the second. * * If the algorithm encounters an element with the class `muu-isolate` it does * not recurse into its children. This way, you can protect dynamically * generated content from being overwritten. * * All classes prefixed with `muu-` will be preserved. * * @module update-dom * @param {Node} target * @param {string} html */ define('update-dom', ['js-helpers'], function(_) { "use strict"; var updateAttributes = function(target, source) { var muuClasses = _.filter(target.classList, function(cls) { return cls.lastIndexOf('muu-', 0) === 0; }); var targetAttrNames = _.map(target.attributes, function(item) { return item.name; }); var sourceAttrNames = _.map(source.attributes, function(item) { return item.name; }); _.forEach(targetAttrNames, function(name) { // NOTE: ie8.js creates some attribute if (!source.hasAttribute(name) && name.substr(0, 7) !== '__IE8__') { target.removeAttribute(name); } }); _.forEach(sourceAttrNames, function(name) { if (target.getAttribute(name) !== source.getAttribute(name)) { target.setAttribute(name, source.getAttribute(name)); } }); _.forEach(muuClasses, function(cls) { target.classList.add(cls); }); }; var updateDOM = function(target, source) { var nt = target.childNodes.length; var ns = source.childNodes.length; var offset = 0; for (var i = ns; i < nt; i++) { target.removeChild(target.childNodes[ns]); } for (i = nt; i < ns; i++) { target.appendChild(source.childNodes[nt]); } for (i = 0; i < nt && i < ns; i++) { var tchild = target.childNodes[i]; var schild = source.childNodes[i - offset]; if (tchild.nodeType === schild.nodeType && tchild.nodeName === schild.nodeName && tchild.type === schild.type) { if (tchild.nodeType === 1) { updateAttributes(tchild, schild); } else if (tchild.nodeType === 3) { tchild.nodeValue = schild.nodeValue; } if (tchild.nodeType !== 3 && !tchild.classList.contains('muu-isolate')) { updateDOM(tchild, schild); } } else { tchild.parentNode.replaceChild(schild, tchild); offset += 1; } } }; return function(target, html) { var tmp = document.createElement('div'); tmp.innerHTML = html; updateDOM(target, tmp); } });