Comparing version 1.3.1 to 1.4.0
@@ -6,4 +6,44 @@ Changelog | ||
## 1.0.x | ||
## 1.4.x | ||
### 1.4.0 | ||
- Make attributes and elements namespace-aware ([@shawnbot](https://github.com/shawnbot)) | ||
## 1.3.x | ||
### 1.3.1 | ||
- Upgraded to `lasso@^2` | ||
- Fixed tests | ||
### 1.3.0 | ||
- Support full page html diff ([@DylanPiercey](https://github.com/DylanPiercey)) | ||
## 1.2.x | ||
### 1.2.0 | ||
- Improve node lifecycle options ([@callum](https://github.com/callum)) | ||
## 1.1.x | ||
### 1.1.4 | ||
- Checking in `dist/` files into the git repo | ||
- Deleted `.cache/` from npm package | ||
### 1.1.3 | ||
- Added a minified UMD distribution file | ||
### 1.1.2 | ||
- Minor internal changes | ||
### 1.1.1 | ||
- Updated `package.json` | ||
### 1.1.0 | ||
@@ -13,2 +53,4 @@ | ||
## 1.0.x | ||
### 1.0.4 | ||
@@ -15,0 +57,0 @@ |
@@ -5,14 +5,25 @@ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.morphdom = f()}})(function(){var define,module,exports;module={exports:(exports={})}; | ||
var testEl = typeof document !== 'undefined' ? document.body || document.createElement('div') : {}; | ||
var testEl = (typeof document !== 'undefined') ? | ||
document.body || document.createElement('div') : | ||
{}; | ||
// Fixes https://github.com/patrick-steele-idem/morphdom/issues/32 (IE7+ support) | ||
// <=IE7 does not support el.hasAttribute(name) | ||
var hasAttribute; | ||
if (testEl.hasAttribute) { | ||
hasAttribute = function hasAttribute(el, name) { | ||
var XHTML = 'http://www.w3.org/1999/xhtml'; | ||
var ELEMENT_NODE = Node.ELEMENT_NODE; | ||
var TEXT_NODE = Node.TEXT_NODE; | ||
// Fixes <https://github.com/patrick-steele-idem/morphdom/issues/32> | ||
// (IE7+ support) <=IE7 does not support el.hasAttribute(name) | ||
var hasAttributeNS; | ||
if (testEl.hasAttributeNS) { | ||
hasAttributeNS = function(el, namespaceURI, name) { | ||
return el.hasAttributeNS(namespaceURI, name); | ||
}; | ||
} else if (testEl.hasAttribute) { | ||
hasAttributeNS = function(el, namespaceURI, name) { | ||
return el.hasAttribute(name); | ||
}; | ||
} else { | ||
hasAttribute = function hasAttribute(el, name) { | ||
return el.getAttributeNode(name); | ||
hasAttributeNS = function(el, namespaceURI, name) { | ||
return !!el.getAttributeNode(name); | ||
}; | ||
@@ -27,5 +38,5 @@ } | ||
} | ||
return true; | ||
} | ||
function toElement(str) { | ||
@@ -49,8 +60,8 @@ if (!range && document.createRange) { | ||
/** | ||
* Needed for IE. Apparently IE doesn't think | ||
* that "selected" is an attribute when reading | ||
* over the attributes using selectEl.attributes | ||
* Needed for IE. Apparently IE doesn't think that "selected" is an | ||
* attribute when reading over the attributes using selectEl.attributes | ||
*/ | ||
OPTION: function(fromEl, toEl) { | ||
if ((fromEl.selected = toEl.selected)) { | ||
fromEl.selected = toEl.selected; | ||
if (fromEl.selected) { | ||
fromEl.setAttribute('selected', ''); | ||
@@ -62,21 +73,28 @@ } else { | ||
/** | ||
* The "value" attribute is special for the <input> element | ||
* since it sets the initial value. Changing the "value" | ||
* attribute without changing the "value" property will have | ||
* no effect since it is only used to the set the initial value. | ||
* Similar for the "checked" attribute. | ||
* The "value" attribute is special for the <input> element since it sets | ||
* the initial value. Changing the "value" attribute without changing the | ||
* "value" property will have no effect since it is only used to the set the | ||
* initial value. Similar for the "checked" attribute, and "disabled". | ||
*/ | ||
INPUT: function(fromEl, toEl) { | ||
fromEl.checked = toEl.checked; | ||
if (fromEl.checked) { | ||
fromEl.setAttribute('checked', ''); | ||
} else { | ||
fromEl.removeAttribute('checked'); | ||
} | ||
if (fromEl.value != toEl.value) { | ||
if (fromEl.value !== toEl.value) { | ||
fromEl.value = toEl.value; | ||
} | ||
if (!hasAttribute(toEl, 'checked')) { | ||
fromEl.removeAttribute('checked'); | ||
if (!hasAttributeNS(toEl, null, 'value')) { | ||
fromEl.removeAttribute('value'); | ||
} | ||
if (!hasAttribute(toEl, 'value')) { | ||
fromEl.removeAttribute('value'); | ||
fromEl.disabled = toEl.disabled; | ||
if (fromEl.disabled) { | ||
fromEl.setAttribute('disabled', ''); | ||
} else { | ||
fromEl.removeAttribute('disabled'); | ||
} | ||
@@ -87,3 +105,3 @@ }, | ||
var newValue = toEl.value; | ||
if (fromEl.value != newValue) { | ||
if (fromEl.value !== newValue) { | ||
fromEl.value = newValue; | ||
@@ -101,9 +119,36 @@ } | ||
/** | ||
* Loop over all of the attributes on the target node and make sure the | ||
* original DOM node has the same attributes. If an attribute | ||
* found on the original node is not on the new node then remove it from | ||
* the original node | ||
* @param {HTMLElement} fromNode | ||
* @param {HTMLElement} toNode | ||
* Returns true if two node's names and namespace URIs are the same. | ||
* | ||
* @param {Element} a | ||
* @param {Element} b | ||
* @return {boolean} | ||
*/ | ||
var compareNodeNames = function(a, b) { | ||
return a.nodeName === b.nodeName && | ||
a.namespaceURI === b.namespaceURI; | ||
}; | ||
/** | ||
* Create an element, optionally with a known namespace URI. | ||
* | ||
* @param {string} name the element name, e.g. 'div' or 'svg' | ||
* @param {string} [namespaceURI] the element's namespace URI, i.e. the value of | ||
* its `xmlns` attribute or its inferred namespace. | ||
* | ||
* @return {Element} | ||
*/ | ||
function createElementNS(name, namespaceURI) { | ||
return !namespaceURI || namespaceURI === XHTML ? | ||
document.createElement(name) : | ||
document.createElementNS(namespaceURI, name); | ||
} | ||
/** | ||
* Loop over all of the attributes on the target node and make sure the original | ||
* DOM node has the same attributes. If an attribute found on the original node | ||
* is not on the new node then remove it from the original node. | ||
* | ||
* @param {Element} fromNode | ||
* @param {Element} toNode | ||
*/ | ||
function morphAttrs(fromNode, toNode) { | ||
@@ -114,13 +159,20 @@ var attrs = toNode.attributes; | ||
var attrName; | ||
var attrNamespaceURI; | ||
var attrValue; | ||
var foundAttrs = {}; | ||
var fromValue; | ||
for (i=attrs.length-1; i>=0; i--) { | ||
for (i = attrs.length - 1; i >= 0; i--) { | ||
attr = attrs[i]; | ||
if (attr.specified !== false) { | ||
attrName = attr.name; | ||
attrValue = attr.value; | ||
foundAttrs[attrName] = true; | ||
attrName = attr.name; | ||
attrValue = attr.value; | ||
attrNamespaceURI = attr.namespaceURI; | ||
if (fromNode.getAttribute(attrName) !== attrValue) { | ||
fromValue = attrNamespaceURI ? | ||
fromNode.getAttributeNS(attrNamespaceURI, attrName) : | ||
fromNode.getAttribute(attrName); | ||
if (fromValue !== attrValue) { | ||
if (attrNamespaceURI) { | ||
fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue); | ||
} else { | ||
fromNode.setAttribute(attrName, attrValue); | ||
@@ -131,12 +183,14 @@ } | ||
// Delete any extra attributes found on the original DOM element that weren't | ||
// found on the target element. | ||
// Remove any extra attributes found on the original DOM element that | ||
// weren't found on the target element. | ||
attrs = fromNode.attributes; | ||
for (i=attrs.length-1; i>=0; i--) { | ||
for (i = attrs.length - 1; i >= 0; i--) { | ||
attr = attrs[i]; | ||
if (attr.specified !== false) { | ||
attrName = attr.name; | ||
if (!foundAttrs.hasOwnProperty(attrName)) { | ||
fromNode.removeAttribute(attrName); | ||
attrNamespaceURI = attr.namespaceURI; | ||
if (!hasAttributeNS(toNode, attrNamespaceURI, attrName)) { | ||
fromNode.removeAttributeNode(attr); | ||
} | ||
@@ -152,3 +206,3 @@ } | ||
var curChild = fromEl.firstChild; | ||
while(curChild) { | ||
while (curChild) { | ||
var nextChild = curChild.nextSibling; | ||
@@ -180,2 +234,9 @@ toEl.appendChild(curChild); | ||
// XXX optimization: if the nodes are equal, don't morph them | ||
/* | ||
if (fromNode.isEqualNode(toNode)) { | ||
return fromNode; | ||
} | ||
*/ | ||
var savedEls = {}; // Used to save off DOM elements with IDs | ||
@@ -207,5 +268,5 @@ var unmatchedEls = {}; | ||
if (node.nodeType === 1) { | ||
if (node.nodeType === ELEMENT_NODE) { | ||
var curChild = node.firstChild; | ||
while(curChild) { | ||
while (curChild) { | ||
removeNodeHelper(curChild, nestedInSavedEl || id); | ||
@@ -218,5 +279,5 @@ curChild = curChild.nextSibling; | ||
function walkDiscardedChildNodes(node) { | ||
if (node.nodeType === 1) { | ||
if (node.nodeType === ELEMENT_NODE) { | ||
var curChild = node.firstChild; | ||
while(curChild) { | ||
while (curChild) { | ||
@@ -276,3 +337,3 @@ | ||
if (fromEl.tagName != 'TEXTAREA') { | ||
if (fromEl.nodeName !== 'TEXTAREA') { | ||
var curToNodeChild = toEl.firstChild; | ||
@@ -287,7 +348,7 @@ var curFromNodeChild = fromEl.firstChild; | ||
outer: while(curToNodeChild) { | ||
outer: while (curToNodeChild) { | ||
toNextSibling = curToNodeChild.nextSibling; | ||
curToNodeId = getNodeKey(curToNodeChild); | ||
while(curFromNodeChild) { | ||
while (curFromNodeChild) { | ||
var curFromNodeId = getNodeKey(curFromNodeChild); | ||
@@ -310,9 +371,10 @@ fromNextSibling = curFromNodeChild.nextSibling; | ||
if (curFromNodeType === 1) { // Both nodes being compared are Element nodes | ||
if (curFromNodeChild.tagName === curToNodeChild.tagName) { | ||
// Both nodes being compared are Element nodes | ||
if (curFromNodeType === ELEMENT_NODE) { | ||
if (compareNodeNames(curFromNodeChild, curToNodeChild)) { | ||
// We have compatible DOM elements | ||
if (curFromNodeId || curToNodeId) { | ||
// If either DOM element has an ID then we handle | ||
// those differently since we want to match up | ||
// by ID | ||
// If either DOM element has an ID then we | ||
// handle those differently since we want to | ||
// match up by ID | ||
if (curToNodeId === curFromNodeId) { | ||
@@ -327,9 +389,12 @@ isCompatible = true; | ||
if (isCompatible) { | ||
// We found compatible DOM elements so transform the current "from" node | ||
// to match the current target DOM node. | ||
// We found compatible DOM elements so transform | ||
// the current "from" node to match the current | ||
// target DOM node. | ||
morphEl(curFromNodeChild, curToNodeChild, alreadyVisited); | ||
} | ||
} else if (curFromNodeType === 3) { // Both nodes being compared are Text nodes | ||
// Both nodes being compared are Text nodes | ||
} else if (curFromNodeType === TEXT_NODE) { | ||
isCompatible = true; | ||
// Simply update nodeValue on the original node to change the text value | ||
// Simply update nodeValue on the original node to | ||
// change the text value | ||
curFromNodeChild.nodeValue = curToNodeChild.nodeValue; | ||
@@ -345,4 +410,4 @@ } | ||
// No compatible match so remove the old node from the DOM and continue trying | ||
// to find a match in the original DOM | ||
// No compatible match so remove the old node from the DOM | ||
// and continue trying to find a match in the original DOM | ||
removeNode(curFromNodeChild, fromEl, alreadyVisited); | ||
@@ -355,10 +420,12 @@ curFromNodeChild = fromNextSibling; | ||
morphEl(savedEl, curToNodeChild, true); | ||
curToNodeChild = savedEl; // We want to append the saved element instead | ||
// We want to append the saved element instead | ||
curToNodeChild = savedEl; | ||
} else { | ||
// The current DOM element in the target tree has an ID | ||
// but we did not find a match in any of the corresponding | ||
// siblings. We just put the target element in the old DOM tree | ||
// but if we later find an element in the old DOM tree that has | ||
// a matching ID then we will replace the target element | ||
// with the corresponding old element and morph the old element | ||
// but we did not find a match in any of the | ||
// corresponding siblings. We just put the target | ||
// element in the old DOM tree but if we later find an | ||
// element in the old DOM tree that has a matching ID | ||
// then we will replace the target element with the | ||
// corresponding old element and morph the old element | ||
unmatchedEls[curToNodeId] = curToNodeChild; | ||
@@ -368,5 +435,6 @@ } | ||
// If we got this far then we did not find a candidate match for our "to node" | ||
// and we exhausted all of the children "from" nodes. Therefore, we will just | ||
// append the current "to node" to the end | ||
// If we got this far then we did not find a candidate match for | ||
// our "to node" and we exhausted all of the children "from" | ||
// nodes. Therefore, we will just append the current "to node" | ||
// to the end | ||
if (onBeforeNodeAdded(curToNodeChild) !== false) { | ||
@@ -377,8 +445,10 @@ fromEl.appendChild(curToNodeChild); | ||
if (curToNodeChild.nodeType === 1 && (curToNodeId || curToNodeChild.firstChild)) { | ||
// The element that was just added to the original DOM may have | ||
// some nested elements with a key/ID that needs to be matched up | ||
// with other elements. We'll add the element to a list so that we | ||
// can later process the nested elements if there are any unmatched | ||
// keyed elements that were discarded | ||
if (curToNodeChild.nodeType === ELEMENT_NODE && | ||
(curToNodeId || curToNodeChild.firstChild)) { | ||
// The element that was just added to the original DOM may | ||
// have some nested elements with a key/ID that needs to be | ||
// matched up with other elements. We'll add the element to | ||
// a list so that we can later process the nested elements | ||
// if there are any unmatched keyed elements that were | ||
// discarded | ||
movedEls.push(curToNodeChild); | ||
@@ -391,5 +461,6 @@ } | ||
// We have processed all of the "to nodes". If curFromNodeChild is non-null then | ||
// we still have some from nodes left over that need to be removed | ||
while(curFromNodeChild) { | ||
// We have processed all of the "to nodes". If curFromNodeChild is | ||
// non-null then we still have some from nodes left over that need | ||
// to be removed | ||
while (curFromNodeChild) { | ||
fromNextSibling = curFromNodeChild.nextSibling; | ||
@@ -401,3 +472,3 @@ removeNode(curFromNodeChild, fromEl, alreadyVisited); | ||
var specialElHandler = specialElHandlers[fromEl.tagName]; | ||
var specialElHandler = specialElHandlers[fromEl.nodeName]; | ||
if (specialElHandler) { | ||
@@ -415,7 +486,7 @@ specialElHandler(fromEl, toEl); | ||
// compatible (e.g. <div> --> <span> or <div> --> TEXT) | ||
if (morphedNodeType === 1) { | ||
if (toNodeType === 1) { | ||
if (fromNode.tagName !== toNode.tagName) { | ||
if (morphedNodeType === ELEMENT_NODE) { | ||
if (toNodeType === ELEMENT_NODE) { | ||
if (!compareNodeNames(fromNode, toNode)) { | ||
onNodeDiscarded(fromNode); | ||
morphedNode = moveChildren(fromNode, document.createElement(toNode.tagName)); | ||
morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI)); | ||
} | ||
@@ -426,4 +497,4 @@ } else { | ||
} | ||
} else if (morphedNodeType === 3) { // Text node | ||
if (toNodeType === 3) { | ||
} else if (morphedNodeType === TEXT_NODE) { // Text node | ||
if (toNodeType === TEXT_NODE) { | ||
morphedNode.nodeValue = toNode.nodeValue; | ||
@@ -439,4 +510,4 @@ return morphedNode; | ||
if (morphedNode === toNode) { | ||
// The "to node" was not compatible with the "from node" | ||
// so we had to toss out the "from node" and use the "to node" | ||
// The "to node" was not compatible with the "from node" so we had to | ||
// toss out the "from node" and use the "to node" | ||
onNodeDiscarded(fromNode); | ||
@@ -447,12 +518,11 @@ } else { | ||
/** | ||
* What we will do here is walk the tree for the DOM element | ||
* that was moved from the target DOM tree to the original | ||
* DOM tree and we will look for keyed elements that could | ||
* be matched to keyed elements that were earlier discarded. | ||
* If we find a match then we will move the saved element | ||
* into the final DOM tree | ||
* What we will do here is walk the tree for the DOM element that was | ||
* moved from the target DOM tree to the original DOM tree and we will | ||
* look for keyed elements that could be matched to keyed elements that | ||
* were earlier discarded. If we find a match then we will move the | ||
* saved element into the final DOM tree. | ||
*/ | ||
var handleMovedEl = function(el) { | ||
var curChild = el.firstChild; | ||
while(curChild) { | ||
while (curChild) { | ||
var nextSibling = curChild.nextSibling; | ||
@@ -463,5 +533,6 @@ | ||
var savedEl = savedEls[key]; | ||
if (savedEl && (curChild.tagName === savedEl.tagName)) { | ||
if (savedEl && compareNodeNames(curChild, savedEl)) { | ||
curChild.parentNode.replaceChild(savedEl, curChild); | ||
morphEl(savedEl, curChild, true /* already visited the saved el tree */); | ||
// true: already visited the saved el tree | ||
morphEl(savedEl, curChild, true); | ||
curChild = nextSibling; | ||
@@ -475,3 +546,3 @@ if (empty(savedEls)) { | ||
if (curChild.nodeType === 1) { | ||
if (curChild.nodeType === ELEMENT_NODE) { | ||
handleMovedEl(curChild); | ||
@@ -478,0 +549,0 @@ } |
@@ -1,1 +0,1 @@ | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.morphdom=f()}})(function(){var define,module,exports;module={exports:exports={}};var range;var testEl=typeof document!=="undefined"?document.body||document.createElement("div"):{};var hasAttribute;if(testEl.hasAttribute){hasAttribute=function hasAttribute(el,name){return el.hasAttribute(name)}}else{hasAttribute=function hasAttribute(el,name){return el.getAttributeNode(name)}}function empty(o){for(var k in o){if(o.hasOwnProperty(k)){return false}}return true}function toElement(str){if(!range&&document.createRange){range=document.createRange();range.selectNode(document.body)}var fragment;if(range&&range.createContextualFragment){fragment=range.createContextualFragment(str)}else{fragment=document.createElement("body");fragment.innerHTML=str}return fragment.childNodes[0]}var specialElHandlers={OPTION:function(fromEl,toEl){if(fromEl.selected=toEl.selected){fromEl.setAttribute("selected","")}else{fromEl.removeAttribute("selected","")}},INPUT:function(fromEl,toEl){fromEl.checked=toEl.checked;if(fromEl.value!=toEl.value){fromEl.value=toEl.value}if(!hasAttribute(toEl,"checked")){fromEl.removeAttribute("checked")}if(!hasAttribute(toEl,"value")){fromEl.removeAttribute("value")}},TEXTAREA:function(fromEl,toEl){var newValue=toEl.value;if(fromEl.value!=newValue){fromEl.value=newValue}if(fromEl.firstChild){fromEl.firstChild.nodeValue=newValue}}};function noop(){}function morphAttrs(fromNode,toNode){var attrs=toNode.attributes;var i;var attr;var attrName;var attrValue;var foundAttrs={};for(i=attrs.length-1;i>=0;i--){attr=attrs[i];if(attr.specified!==false){attrName=attr.name;attrValue=attr.value;foundAttrs[attrName]=true;if(fromNode.getAttribute(attrName)!==attrValue){fromNode.setAttribute(attrName,attrValue)}}}attrs=fromNode.attributes;for(i=attrs.length-1;i>=0;i--){attr=attrs[i];if(attr.specified!==false){attrName=attr.name;if(!foundAttrs.hasOwnProperty(attrName)){fromNode.removeAttribute(attrName)}}}}function moveChildren(fromEl,toEl){var curChild=fromEl.firstChild;while(curChild){var nextChild=curChild.nextSibling;toEl.appendChild(curChild);curChild=nextChild}return toEl}function defaultGetNodeKey(node){return node.id}function morphdom(fromNode,toNode,options){if(!options){options={}}if(typeof toNode==="string"){if(fromNode.nodeName==="#document"||fromNode.nodeName==="HTML"){var toNodeHtml=toNode;toNode=document.createElement("html");toNode.innerHTML=toNodeHtml}else{toNode=toElement(toNode)}}var savedEls={};var unmatchedEls={};var getNodeKey=options.getNodeKey||defaultGetNodeKey;var onBeforeNodeAdded=options.onBeforeNodeAdded||noop;var onNodeAdded=options.onNodeAdded||noop;var onBeforeElUpdated=options.onBeforeElUpdated||options.onBeforeMorphEl||noop;var onElUpdated=options.onElUpdated||noop;var onBeforeNodeDiscarded=options.onBeforeNodeDiscarded||noop;var onNodeDiscarded=options.onNodeDiscarded||noop;var onBeforeElChildrenUpdated=options.onBeforeElChildrenUpdated||options.onBeforeMorphElChildren||noop;var childrenOnly=options.childrenOnly===true;var movedEls=[];function removeNodeHelper(node,nestedInSavedEl){var id=getNodeKey(node);if(id){savedEls[id]=node}else if(!nestedInSavedEl){onNodeDiscarded(node)}if(node.nodeType===1){var curChild=node.firstChild;while(curChild){removeNodeHelper(curChild,nestedInSavedEl||id);curChild=curChild.nextSibling}}}function walkDiscardedChildNodes(node){if(node.nodeType===1){var curChild=node.firstChild;while(curChild){if(!getNodeKey(curChild)){onNodeDiscarded(curChild);walkDiscardedChildNodes(curChild)}curChild=curChild.nextSibling}}}function removeNode(node,parentNode,alreadyVisited){if(onBeforeNodeDiscarded(node)===false){return}parentNode.removeChild(node);if(alreadyVisited){if(!getNodeKey(node)){onNodeDiscarded(node);walkDiscardedChildNodes(node)}}else{removeNodeHelper(node)}}function morphEl(fromEl,toEl,alreadyVisited,childrenOnly){var toElKey=getNodeKey(toEl);if(toElKey){delete savedEls[toElKey]}if(!childrenOnly){if(onBeforeElUpdated(fromEl,toEl)===false){return}morphAttrs(fromEl,toEl);onElUpdated(fromEl);if(onBeforeElChildrenUpdated(fromEl,toEl)===false){return}}if(fromEl.tagName!="TEXTAREA"){var curToNodeChild=toEl.firstChild;var curFromNodeChild=fromEl.firstChild;var curToNodeId;var fromNextSibling;var toNextSibling;var savedEl;var unmatchedEl;outer:while(curToNodeChild){toNextSibling=curToNodeChild.nextSibling;curToNodeId=getNodeKey(curToNodeChild);while(curFromNodeChild){var curFromNodeId=getNodeKey(curFromNodeChild);fromNextSibling=curFromNodeChild.nextSibling;if(!alreadyVisited){if(curFromNodeId&&(unmatchedEl=unmatchedEls[curFromNodeId])){unmatchedEl.parentNode.replaceChild(curFromNodeChild,unmatchedEl);morphEl(curFromNodeChild,unmatchedEl,alreadyVisited);curFromNodeChild=fromNextSibling;continue}}var curFromNodeType=curFromNodeChild.nodeType;if(curFromNodeType===curToNodeChild.nodeType){var isCompatible=false;if(curFromNodeType===1){if(curFromNodeChild.tagName===curToNodeChild.tagName){if(curFromNodeId||curToNodeId){if(curToNodeId===curFromNodeId){isCompatible=true}}else{isCompatible=true}}if(isCompatible){morphEl(curFromNodeChild,curToNodeChild,alreadyVisited)}}else if(curFromNodeType===3){isCompatible=true;curFromNodeChild.nodeValue=curToNodeChild.nodeValue}if(isCompatible){curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling;continue outer}}removeNode(curFromNodeChild,fromEl,alreadyVisited);curFromNodeChild=fromNextSibling}if(curToNodeId){if(savedEl=savedEls[curToNodeId]){morphEl(savedEl,curToNodeChild,true);curToNodeChild=savedEl}else{unmatchedEls[curToNodeId]=curToNodeChild}}if(onBeforeNodeAdded(curToNodeChild)!==false){fromEl.appendChild(curToNodeChild);onNodeAdded(curToNodeChild)}if(curToNodeChild.nodeType===1&&(curToNodeId||curToNodeChild.firstChild)){movedEls.push(curToNodeChild)}curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling}while(curFromNodeChild){fromNextSibling=curFromNodeChild.nextSibling;removeNode(curFromNodeChild,fromEl,alreadyVisited);curFromNodeChild=fromNextSibling}}var specialElHandler=specialElHandlers[fromEl.tagName];if(specialElHandler){specialElHandler(fromEl,toEl)}}var morphedNode=fromNode;var morphedNodeType=morphedNode.nodeType;var toNodeType=toNode.nodeType;if(!childrenOnly){if(morphedNodeType===1){if(toNodeType===1){if(fromNode.tagName!==toNode.tagName){onNodeDiscarded(fromNode);morphedNode=moveChildren(fromNode,document.createElement(toNode.tagName))}}else{morphedNode=toNode}}else if(morphedNodeType===3){if(toNodeType===3){morphedNode.nodeValue=toNode.nodeValue;return morphedNode}else{morphedNode=toNode}}}if(morphedNode===toNode){onNodeDiscarded(fromNode)}else{morphEl(morphedNode,toNode,false,childrenOnly);var handleMovedEl=function(el){var curChild=el.firstChild;while(curChild){var nextSibling=curChild.nextSibling;var key=getNodeKey(curChild);if(key){var savedEl=savedEls[key];if(savedEl&&curChild.tagName===savedEl.tagName){curChild.parentNode.replaceChild(savedEl,curChild);morphEl(savedEl,curChild,true);curChild=nextSibling;if(empty(savedEls)){return false}continue}}if(curChild.nodeType===1){handleMovedEl(curChild)}curChild=nextSibling}};if(!empty(savedEls)){handleMovedElsLoop:while(movedEls.length){var movedElsTemp=movedEls;movedEls=[];for(var i=0;i<movedElsTemp.length;i++){if(handleMovedEl(movedElsTemp[i])===false){break handleMovedElsLoop}}}}for(var savedElId in savedEls){if(savedEls.hasOwnProperty(savedElId)){var savedEl=savedEls[savedElId];onNodeDiscarded(savedEl);walkDiscardedChildNodes(savedEl)}}}if(!childrenOnly&&morphedNode!==fromNode&&fromNode.parentNode){fromNode.parentNode.replaceChild(morphedNode,fromNode)}return morphedNode}module.exports=morphdom;return module.exports}); | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.morphdom=f()}})(function(){var define,module,exports;module={exports:exports={}};var range;var testEl=typeof document!=="undefined"?document.body||document.createElement("div"):{};var XHTML="http://www.w3.org/1999/xhtml";var ELEMENT_NODE=Node.ELEMENT_NODE;var TEXT_NODE=Node.TEXT_NODE;var hasAttributeNS;if(testEl.hasAttributeNS){hasAttributeNS=function(el,namespaceURI,name){return el.hasAttributeNS(namespaceURI,name)}}else if(testEl.hasAttribute){hasAttributeNS=function(el,namespaceURI,name){return el.hasAttribute(name)}}else{hasAttributeNS=function(el,namespaceURI,name){return!!el.getAttributeNode(name)}}function empty(o){for(var k in o){if(o.hasOwnProperty(k)){return false}}return true}function toElement(str){if(!range&&document.createRange){range=document.createRange();range.selectNode(document.body)}var fragment;if(range&&range.createContextualFragment){fragment=range.createContextualFragment(str)}else{fragment=document.createElement("body");fragment.innerHTML=str}return fragment.childNodes[0]}var specialElHandlers={OPTION:function(fromEl,toEl){fromEl.selected=toEl.selected;if(fromEl.selected){fromEl.setAttribute("selected","")}else{fromEl.removeAttribute("selected","")}},INPUT:function(fromEl,toEl){fromEl.checked=toEl.checked;if(fromEl.checked){fromEl.setAttribute("checked","")}else{fromEl.removeAttribute("checked")}if(fromEl.value!==toEl.value){fromEl.value=toEl.value}if(!hasAttributeNS(toEl,null,"value")){fromEl.removeAttribute("value")}fromEl.disabled=toEl.disabled;if(fromEl.disabled){fromEl.setAttribute("disabled","")}else{fromEl.removeAttribute("disabled")}},TEXTAREA:function(fromEl,toEl){var newValue=toEl.value;if(fromEl.value!==newValue){fromEl.value=newValue}if(fromEl.firstChild){fromEl.firstChild.nodeValue=newValue}}};function noop(){}var compareNodeNames=function(a,b){return a.nodeName===b.nodeName&&a.namespaceURI===b.namespaceURI};function createElementNS(name,namespaceURI){return!namespaceURI||namespaceURI===XHTML?document.createElement(name):document.createElementNS(namespaceURI,name)}function morphAttrs(fromNode,toNode){var attrs=toNode.attributes;var i;var attr;var attrName;var attrNamespaceURI;var attrValue;var fromValue;for(i=attrs.length-1;i>=0;i--){attr=attrs[i];attrName=attr.name;attrValue=attr.value;attrNamespaceURI=attr.namespaceURI;fromValue=attrNamespaceURI?fromNode.getAttributeNS(attrNamespaceURI,attrName):fromNode.getAttribute(attrName);if(fromValue!==attrValue){if(attrNamespaceURI){fromNode.setAttributeNS(attrNamespaceURI,attrName,attrValue)}else{fromNode.setAttribute(attrName,attrValue)}}}attrs=fromNode.attributes;for(i=attrs.length-1;i>=0;i--){attr=attrs[i];if(attr.specified!==false){attrName=attr.name;attrNamespaceURI=attr.namespaceURI;if(!hasAttributeNS(toNode,attrNamespaceURI,attrName)){fromNode.removeAttributeNode(attr)}}}}function moveChildren(fromEl,toEl){var curChild=fromEl.firstChild;while(curChild){var nextChild=curChild.nextSibling;toEl.appendChild(curChild);curChild=nextChild}return toEl}function defaultGetNodeKey(node){return node.id}function morphdom(fromNode,toNode,options){if(!options){options={}}if(typeof toNode==="string"){if(fromNode.nodeName==="#document"||fromNode.nodeName==="HTML"){var toNodeHtml=toNode;toNode=document.createElement("html");toNode.innerHTML=toNodeHtml}else{toNode=toElement(toNode)}}var savedEls={};var unmatchedEls={};var getNodeKey=options.getNodeKey||defaultGetNodeKey;var onBeforeNodeAdded=options.onBeforeNodeAdded||noop;var onNodeAdded=options.onNodeAdded||noop;var onBeforeElUpdated=options.onBeforeElUpdated||options.onBeforeMorphEl||noop;var onElUpdated=options.onElUpdated||noop;var onBeforeNodeDiscarded=options.onBeforeNodeDiscarded||noop;var onNodeDiscarded=options.onNodeDiscarded||noop;var onBeforeElChildrenUpdated=options.onBeforeElChildrenUpdated||options.onBeforeMorphElChildren||noop;var childrenOnly=options.childrenOnly===true;var movedEls=[];function removeNodeHelper(node,nestedInSavedEl){var id=getNodeKey(node);if(id){savedEls[id]=node}else if(!nestedInSavedEl){onNodeDiscarded(node)}if(node.nodeType===ELEMENT_NODE){var curChild=node.firstChild;while(curChild){removeNodeHelper(curChild,nestedInSavedEl||id);curChild=curChild.nextSibling}}}function walkDiscardedChildNodes(node){if(node.nodeType===ELEMENT_NODE){var curChild=node.firstChild;while(curChild){if(!getNodeKey(curChild)){onNodeDiscarded(curChild);walkDiscardedChildNodes(curChild)}curChild=curChild.nextSibling}}}function removeNode(node,parentNode,alreadyVisited){if(onBeforeNodeDiscarded(node)===false){return}parentNode.removeChild(node);if(alreadyVisited){if(!getNodeKey(node)){onNodeDiscarded(node);walkDiscardedChildNodes(node)}}else{removeNodeHelper(node)}}function morphEl(fromEl,toEl,alreadyVisited,childrenOnly){var toElKey=getNodeKey(toEl);if(toElKey){delete savedEls[toElKey]}if(!childrenOnly){if(onBeforeElUpdated(fromEl,toEl)===false){return}morphAttrs(fromEl,toEl);onElUpdated(fromEl);if(onBeforeElChildrenUpdated(fromEl,toEl)===false){return}}if(fromEl.nodeName!=="TEXTAREA"){var curToNodeChild=toEl.firstChild;var curFromNodeChild=fromEl.firstChild;var curToNodeId;var fromNextSibling;var toNextSibling;var savedEl;var unmatchedEl;outer:while(curToNodeChild){toNextSibling=curToNodeChild.nextSibling;curToNodeId=getNodeKey(curToNodeChild);while(curFromNodeChild){var curFromNodeId=getNodeKey(curFromNodeChild);fromNextSibling=curFromNodeChild.nextSibling;if(!alreadyVisited){if(curFromNodeId&&(unmatchedEl=unmatchedEls[curFromNodeId])){unmatchedEl.parentNode.replaceChild(curFromNodeChild,unmatchedEl);morphEl(curFromNodeChild,unmatchedEl,alreadyVisited);curFromNodeChild=fromNextSibling;continue}}var curFromNodeType=curFromNodeChild.nodeType;if(curFromNodeType===curToNodeChild.nodeType){var isCompatible=false;if(curFromNodeType===ELEMENT_NODE){if(compareNodeNames(curFromNodeChild,curToNodeChild)){if(curFromNodeId||curToNodeId){if(curToNodeId===curFromNodeId){isCompatible=true}}else{isCompatible=true}}if(isCompatible){morphEl(curFromNodeChild,curToNodeChild,alreadyVisited)}}else if(curFromNodeType===TEXT_NODE){isCompatible=true;curFromNodeChild.nodeValue=curToNodeChild.nodeValue}if(isCompatible){curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling;continue outer}}removeNode(curFromNodeChild,fromEl,alreadyVisited);curFromNodeChild=fromNextSibling}if(curToNodeId){if(savedEl=savedEls[curToNodeId]){morphEl(savedEl,curToNodeChild,true);curToNodeChild=savedEl}else{unmatchedEls[curToNodeId]=curToNodeChild}}if(onBeforeNodeAdded(curToNodeChild)!==false){fromEl.appendChild(curToNodeChild);onNodeAdded(curToNodeChild)}if(curToNodeChild.nodeType===ELEMENT_NODE&&(curToNodeId||curToNodeChild.firstChild)){movedEls.push(curToNodeChild)}curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling}while(curFromNodeChild){fromNextSibling=curFromNodeChild.nextSibling;removeNode(curFromNodeChild,fromEl,alreadyVisited);curFromNodeChild=fromNextSibling}}var specialElHandler=specialElHandlers[fromEl.nodeName];if(specialElHandler){specialElHandler(fromEl,toEl)}}var morphedNode=fromNode;var morphedNodeType=morphedNode.nodeType;var toNodeType=toNode.nodeType;if(!childrenOnly){if(morphedNodeType===ELEMENT_NODE){if(toNodeType===ELEMENT_NODE){if(!compareNodeNames(fromNode,toNode)){onNodeDiscarded(fromNode);morphedNode=moveChildren(fromNode,createElementNS(toNode.nodeName,toNode.namespaceURI))}}else{morphedNode=toNode}}else if(morphedNodeType===TEXT_NODE){if(toNodeType===TEXT_NODE){morphedNode.nodeValue=toNode.nodeValue;return morphedNode}else{morphedNode=toNode}}}if(morphedNode===toNode){onNodeDiscarded(fromNode)}else{morphEl(morphedNode,toNode,false,childrenOnly);var handleMovedEl=function(el){var curChild=el.firstChild;while(curChild){var nextSibling=curChild.nextSibling;var key=getNodeKey(curChild);if(key){var savedEl=savedEls[key];if(savedEl&&compareNodeNames(curChild,savedEl)){curChild.parentNode.replaceChild(savedEl,curChild);morphEl(savedEl,curChild,true);curChild=nextSibling;if(empty(savedEls)){return false}continue}}if(curChild.nodeType===ELEMENT_NODE){handleMovedEl(curChild)}curChild=nextSibling}};if(!empty(savedEls)){handleMovedElsLoop:while(movedEls.length){var movedElsTemp=movedEls;movedEls=[];for(var i=0;i<movedElsTemp.length;i++){if(handleMovedEl(movedElsTemp[i])===false){break handleMovedElsLoop}}}}for(var savedElId in savedEls){if(savedEls.hasOwnProperty(savedElId)){var savedEl=savedEls[savedElId];onNodeDiscarded(savedEl);walkDiscardedChildNodes(savedEl)}}}if(!childrenOnly&&morphedNode!==fromNode&&fromNode.parentNode){fromNode.parentNode.replaceChild(morphedNode,fromNode)}return morphedNode}module.exports=morphdom;return module.exports}); |
267
lib/index.js
// Create a range object for efficently rendering strings to elements. | ||
var range; | ||
var testEl = typeof document !== 'undefined' ? document.body || document.createElement('div') : {}; | ||
var testEl = (typeof document !== 'undefined') ? | ||
document.body || document.createElement('div') : | ||
{}; | ||
// Fixes https://github.com/patrick-steele-idem/morphdom/issues/32 (IE7+ support) | ||
// <=IE7 does not support el.hasAttribute(name) | ||
var hasAttribute; | ||
if (testEl.hasAttribute) { | ||
hasAttribute = function hasAttribute(el, name) { | ||
var XHTML = 'http://www.w3.org/1999/xhtml'; | ||
var ELEMENT_NODE = Node.ELEMENT_NODE; | ||
var TEXT_NODE = Node.TEXT_NODE; | ||
// Fixes <https://github.com/patrick-steele-idem/morphdom/issues/32> | ||
// (IE7+ support) <=IE7 does not support el.hasAttribute(name) | ||
var hasAttributeNS; | ||
if (testEl.hasAttributeNS) { | ||
hasAttributeNS = function(el, namespaceURI, name) { | ||
return el.hasAttributeNS(namespaceURI, name); | ||
}; | ||
} else if (testEl.hasAttribute) { | ||
hasAttributeNS = function(el, namespaceURI, name) { | ||
return el.hasAttribute(name); | ||
}; | ||
} else { | ||
hasAttribute = function hasAttribute(el, name) { | ||
return el.getAttributeNode(name); | ||
hasAttributeNS = function(el, namespaceURI, name) { | ||
return !!el.getAttributeNode(name); | ||
}; | ||
@@ -25,5 +36,5 @@ } | ||
} | ||
return true; | ||
} | ||
function toElement(str) { | ||
@@ -47,8 +58,8 @@ if (!range && document.createRange) { | ||
/** | ||
* Needed for IE. Apparently IE doesn't think | ||
* that "selected" is an attribute when reading | ||
* over the attributes using selectEl.attributes | ||
* Needed for IE. Apparently IE doesn't think that "selected" is an | ||
* attribute when reading over the attributes using selectEl.attributes | ||
*/ | ||
OPTION: function(fromEl, toEl) { | ||
if ((fromEl.selected = toEl.selected)) { | ||
fromEl.selected = toEl.selected; | ||
if (fromEl.selected) { | ||
fromEl.setAttribute('selected', ''); | ||
@@ -60,21 +71,28 @@ } else { | ||
/** | ||
* The "value" attribute is special for the <input> element | ||
* since it sets the initial value. Changing the "value" | ||
* attribute without changing the "value" property will have | ||
* no effect since it is only used to the set the initial value. | ||
* Similar for the "checked" attribute. | ||
* The "value" attribute is special for the <input> element since it sets | ||
* the initial value. Changing the "value" attribute without changing the | ||
* "value" property will have no effect since it is only used to the set the | ||
* initial value. Similar for the "checked" attribute, and "disabled". | ||
*/ | ||
INPUT: function(fromEl, toEl) { | ||
fromEl.checked = toEl.checked; | ||
if (fromEl.checked) { | ||
fromEl.setAttribute('checked', ''); | ||
} else { | ||
fromEl.removeAttribute('checked'); | ||
} | ||
if (fromEl.value != toEl.value) { | ||
if (fromEl.value !== toEl.value) { | ||
fromEl.value = toEl.value; | ||
} | ||
if (!hasAttribute(toEl, 'checked')) { | ||
fromEl.removeAttribute('checked'); | ||
if (!hasAttributeNS(toEl, null, 'value')) { | ||
fromEl.removeAttribute('value'); | ||
} | ||
if (!hasAttribute(toEl, 'value')) { | ||
fromEl.removeAttribute('value'); | ||
fromEl.disabled = toEl.disabled; | ||
if (fromEl.disabled) { | ||
fromEl.setAttribute('disabled', ''); | ||
} else { | ||
fromEl.removeAttribute('disabled'); | ||
} | ||
@@ -85,3 +103,3 @@ }, | ||
var newValue = toEl.value; | ||
if (fromEl.value != newValue) { | ||
if (fromEl.value !== newValue) { | ||
fromEl.value = newValue; | ||
@@ -99,9 +117,36 @@ } | ||
/** | ||
* Loop over all of the attributes on the target node and make sure the | ||
* original DOM node has the same attributes. If an attribute | ||
* found on the original node is not on the new node then remove it from | ||
* the original node | ||
* @param {HTMLElement} fromNode | ||
* @param {HTMLElement} toNode | ||
* Returns true if two node's names and namespace URIs are the same. | ||
* | ||
* @param {Element} a | ||
* @param {Element} b | ||
* @return {boolean} | ||
*/ | ||
var compareNodeNames = function(a, b) { | ||
return a.nodeName === b.nodeName && | ||
a.namespaceURI === b.namespaceURI; | ||
}; | ||
/** | ||
* Create an element, optionally with a known namespace URI. | ||
* | ||
* @param {string} name the element name, e.g. 'div' or 'svg' | ||
* @param {string} [namespaceURI] the element's namespace URI, i.e. the value of | ||
* its `xmlns` attribute or its inferred namespace. | ||
* | ||
* @return {Element} | ||
*/ | ||
function createElementNS(name, namespaceURI) { | ||
return !namespaceURI || namespaceURI === XHTML ? | ||
document.createElement(name) : | ||
document.createElementNS(namespaceURI, name); | ||
} | ||
/** | ||
* Loop over all of the attributes on the target node and make sure the original | ||
* DOM node has the same attributes. If an attribute found on the original node | ||
* is not on the new node then remove it from the original node. | ||
* | ||
* @param {Element} fromNode | ||
* @param {Element} toNode | ||
*/ | ||
function morphAttrs(fromNode, toNode) { | ||
@@ -112,13 +157,20 @@ var attrs = toNode.attributes; | ||
var attrName; | ||
var attrNamespaceURI; | ||
var attrValue; | ||
var foundAttrs = {}; | ||
var fromValue; | ||
for (i=attrs.length-1; i>=0; i--) { | ||
for (i = attrs.length - 1; i >= 0; i--) { | ||
attr = attrs[i]; | ||
if (attr.specified !== false) { | ||
attrName = attr.name; | ||
attrValue = attr.value; | ||
foundAttrs[attrName] = true; | ||
attrName = attr.name; | ||
attrValue = attr.value; | ||
attrNamespaceURI = attr.namespaceURI; | ||
if (fromNode.getAttribute(attrName) !== attrValue) { | ||
fromValue = attrNamespaceURI ? | ||
fromNode.getAttributeNS(attrNamespaceURI, attrName) : | ||
fromNode.getAttribute(attrName); | ||
if (fromValue !== attrValue) { | ||
if (attrNamespaceURI) { | ||
fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue); | ||
} else { | ||
fromNode.setAttribute(attrName, attrValue); | ||
@@ -129,12 +181,14 @@ } | ||
// Delete any extra attributes found on the original DOM element that weren't | ||
// found on the target element. | ||
// Remove any extra attributes found on the original DOM element that | ||
// weren't found on the target element. | ||
attrs = fromNode.attributes; | ||
for (i=attrs.length-1; i>=0; i--) { | ||
for (i = attrs.length - 1; i >= 0; i--) { | ||
attr = attrs[i]; | ||
if (attr.specified !== false) { | ||
attrName = attr.name; | ||
if (!foundAttrs.hasOwnProperty(attrName)) { | ||
fromNode.removeAttribute(attrName); | ||
attrNamespaceURI = attr.namespaceURI; | ||
if (!hasAttributeNS(toNode, attrNamespaceURI, attrName)) { | ||
fromNode.removeAttributeNode(attr); | ||
} | ||
@@ -150,3 +204,3 @@ } | ||
var curChild = fromEl.firstChild; | ||
while(curChild) { | ||
while (curChild) { | ||
var nextChild = curChild.nextSibling; | ||
@@ -178,2 +232,9 @@ toEl.appendChild(curChild); | ||
// XXX optimization: if the nodes are equal, don't morph them | ||
/* | ||
if (fromNode.isEqualNode(toNode)) { | ||
return fromNode; | ||
} | ||
*/ | ||
var savedEls = {}; // Used to save off DOM elements with IDs | ||
@@ -205,5 +266,5 @@ var unmatchedEls = {}; | ||
if (node.nodeType === 1) { | ||
if (node.nodeType === ELEMENT_NODE) { | ||
var curChild = node.firstChild; | ||
while(curChild) { | ||
while (curChild) { | ||
removeNodeHelper(curChild, nestedInSavedEl || id); | ||
@@ -216,5 +277,5 @@ curChild = curChild.nextSibling; | ||
function walkDiscardedChildNodes(node) { | ||
if (node.nodeType === 1) { | ||
if (node.nodeType === ELEMENT_NODE) { | ||
var curChild = node.firstChild; | ||
while(curChild) { | ||
while (curChild) { | ||
@@ -274,3 +335,3 @@ | ||
if (fromEl.tagName != 'TEXTAREA') { | ||
if (fromEl.nodeName !== 'TEXTAREA') { | ||
var curToNodeChild = toEl.firstChild; | ||
@@ -285,7 +346,7 @@ var curFromNodeChild = fromEl.firstChild; | ||
outer: while(curToNodeChild) { | ||
outer: while (curToNodeChild) { | ||
toNextSibling = curToNodeChild.nextSibling; | ||
curToNodeId = getNodeKey(curToNodeChild); | ||
while(curFromNodeChild) { | ||
while (curFromNodeChild) { | ||
var curFromNodeId = getNodeKey(curFromNodeChild); | ||
@@ -308,9 +369,10 @@ fromNextSibling = curFromNodeChild.nextSibling; | ||
if (curFromNodeType === 1) { // Both nodes being compared are Element nodes | ||
if (curFromNodeChild.tagName === curToNodeChild.tagName) { | ||
// Both nodes being compared are Element nodes | ||
if (curFromNodeType === ELEMENT_NODE) { | ||
if (compareNodeNames(curFromNodeChild, curToNodeChild)) { | ||
// We have compatible DOM elements | ||
if (curFromNodeId || curToNodeId) { | ||
// If either DOM element has an ID then we handle | ||
// those differently since we want to match up | ||
// by ID | ||
// If either DOM element has an ID then we | ||
// handle those differently since we want to | ||
// match up by ID | ||
if (curToNodeId === curFromNodeId) { | ||
@@ -325,9 +387,12 @@ isCompatible = true; | ||
if (isCompatible) { | ||
// We found compatible DOM elements so transform the current "from" node | ||
// to match the current target DOM node. | ||
// We found compatible DOM elements so transform | ||
// the current "from" node to match the current | ||
// target DOM node. | ||
morphEl(curFromNodeChild, curToNodeChild, alreadyVisited); | ||
} | ||
} else if (curFromNodeType === 3) { // Both nodes being compared are Text nodes | ||
// Both nodes being compared are Text nodes | ||
} else if (curFromNodeType === TEXT_NODE) { | ||
isCompatible = true; | ||
// Simply update nodeValue on the original node to change the text value | ||
// Simply update nodeValue on the original node to | ||
// change the text value | ||
curFromNodeChild.nodeValue = curToNodeChild.nodeValue; | ||
@@ -343,4 +408,4 @@ } | ||
// No compatible match so remove the old node from the DOM and continue trying | ||
// to find a match in the original DOM | ||
// No compatible match so remove the old node from the DOM | ||
// and continue trying to find a match in the original DOM | ||
removeNode(curFromNodeChild, fromEl, alreadyVisited); | ||
@@ -353,10 +418,12 @@ curFromNodeChild = fromNextSibling; | ||
morphEl(savedEl, curToNodeChild, true); | ||
curToNodeChild = savedEl; // We want to append the saved element instead | ||
// We want to append the saved element instead | ||
curToNodeChild = savedEl; | ||
} else { | ||
// The current DOM element in the target tree has an ID | ||
// but we did not find a match in any of the corresponding | ||
// siblings. We just put the target element in the old DOM tree | ||
// but if we later find an element in the old DOM tree that has | ||
// a matching ID then we will replace the target element | ||
// with the corresponding old element and morph the old element | ||
// but we did not find a match in any of the | ||
// corresponding siblings. We just put the target | ||
// element in the old DOM tree but if we later find an | ||
// element in the old DOM tree that has a matching ID | ||
// then we will replace the target element with the | ||
// corresponding old element and morph the old element | ||
unmatchedEls[curToNodeId] = curToNodeChild; | ||
@@ -366,5 +433,6 @@ } | ||
// If we got this far then we did not find a candidate match for our "to node" | ||
// and we exhausted all of the children "from" nodes. Therefore, we will just | ||
// append the current "to node" to the end | ||
// If we got this far then we did not find a candidate match for | ||
// our "to node" and we exhausted all of the children "from" | ||
// nodes. Therefore, we will just append the current "to node" | ||
// to the end | ||
if (onBeforeNodeAdded(curToNodeChild) !== false) { | ||
@@ -375,8 +443,10 @@ fromEl.appendChild(curToNodeChild); | ||
if (curToNodeChild.nodeType === 1 && (curToNodeId || curToNodeChild.firstChild)) { | ||
// The element that was just added to the original DOM may have | ||
// some nested elements with a key/ID that needs to be matched up | ||
// with other elements. We'll add the element to a list so that we | ||
// can later process the nested elements if there are any unmatched | ||
// keyed elements that were discarded | ||
if (curToNodeChild.nodeType === ELEMENT_NODE && | ||
(curToNodeId || curToNodeChild.firstChild)) { | ||
// The element that was just added to the original DOM may | ||
// have some nested elements with a key/ID that needs to be | ||
// matched up with other elements. We'll add the element to | ||
// a list so that we can later process the nested elements | ||
// if there are any unmatched keyed elements that were | ||
// discarded | ||
movedEls.push(curToNodeChild); | ||
@@ -389,5 +459,6 @@ } | ||
// We have processed all of the "to nodes". If curFromNodeChild is non-null then | ||
// we still have some from nodes left over that need to be removed | ||
while(curFromNodeChild) { | ||
// We have processed all of the "to nodes". If curFromNodeChild is | ||
// non-null then we still have some from nodes left over that need | ||
// to be removed | ||
while (curFromNodeChild) { | ||
fromNextSibling = curFromNodeChild.nextSibling; | ||
@@ -399,3 +470,3 @@ removeNode(curFromNodeChild, fromEl, alreadyVisited); | ||
var specialElHandler = specialElHandlers[fromEl.tagName]; | ||
var specialElHandler = specialElHandlers[fromEl.nodeName]; | ||
if (specialElHandler) { | ||
@@ -413,7 +484,7 @@ specialElHandler(fromEl, toEl); | ||
// compatible (e.g. <div> --> <span> or <div> --> TEXT) | ||
if (morphedNodeType === 1) { | ||
if (toNodeType === 1) { | ||
if (fromNode.tagName !== toNode.tagName) { | ||
if (morphedNodeType === ELEMENT_NODE) { | ||
if (toNodeType === ELEMENT_NODE) { | ||
if (!compareNodeNames(fromNode, toNode)) { | ||
onNodeDiscarded(fromNode); | ||
morphedNode = moveChildren(fromNode, document.createElement(toNode.tagName)); | ||
morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI)); | ||
} | ||
@@ -424,4 +495,4 @@ } else { | ||
} | ||
} else if (morphedNodeType === 3) { // Text node | ||
if (toNodeType === 3) { | ||
} else if (morphedNodeType === TEXT_NODE) { // Text node | ||
if (toNodeType === TEXT_NODE) { | ||
morphedNode.nodeValue = toNode.nodeValue; | ||
@@ -437,4 +508,4 @@ return morphedNode; | ||
if (morphedNode === toNode) { | ||
// The "to node" was not compatible with the "from node" | ||
// so we had to toss out the "from node" and use the "to node" | ||
// The "to node" was not compatible with the "from node" so we had to | ||
// toss out the "from node" and use the "to node" | ||
onNodeDiscarded(fromNode); | ||
@@ -445,12 +516,11 @@ } else { | ||
/** | ||
* What we will do here is walk the tree for the DOM element | ||
* that was moved from the target DOM tree to the original | ||
* DOM tree and we will look for keyed elements that could | ||
* be matched to keyed elements that were earlier discarded. | ||
* If we find a match then we will move the saved element | ||
* into the final DOM tree | ||
* What we will do here is walk the tree for the DOM element that was | ||
* moved from the target DOM tree to the original DOM tree and we will | ||
* look for keyed elements that could be matched to keyed elements that | ||
* were earlier discarded. If we find a match then we will move the | ||
* saved element into the final DOM tree. | ||
*/ | ||
var handleMovedEl = function(el) { | ||
var curChild = el.firstChild; | ||
while(curChild) { | ||
while (curChild) { | ||
var nextSibling = curChild.nextSibling; | ||
@@ -461,5 +531,6 @@ | ||
var savedEl = savedEls[key]; | ||
if (savedEl && (curChild.tagName === savedEl.tagName)) { | ||
if (savedEl && compareNodeNames(curChild, savedEl)) { | ||
curChild.parentNode.replaceChild(savedEl, curChild); | ||
morphEl(savedEl, curChild, true /* already visited the saved el tree */); | ||
// true: already visited the saved el tree | ||
morphEl(savedEl, curChild, true); | ||
curChild = nextSibling; | ||
@@ -473,3 +544,3 @@ if (empty(savedEls)) { | ||
if (curChild.nodeType === 1) { | ||
if (curChild.nodeType === ELEMENT_NODE) { | ||
handleMovedEl(curChild); | ||
@@ -476,0 +547,0 @@ } |
@@ -39,3 +39,3 @@ { | ||
"dependencies": {}, | ||
"version": "1.3.1", | ||
"version": "1.4.0", | ||
"keywords": [ | ||
@@ -42,0 +42,0 @@ "dom", |
@@ -58,2 +58,7 @@ morphdom | ||
# Browser Support | ||
- IE7+ and any modern browser | ||
- Proper namespace support added in `v1.4.0` | ||
# API | ||
@@ -527,3 +532,3 @@ | ||
_NOTE: Safari 9.0.2 (11601.3.9) | ||
_NOTE: Safari 9.0.2 (11601.3.9)_ | ||
@@ -530,0 +535,0 @@ # Maintainers |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
72690
1024
549