Socket
Socket
Sign inDemoInstall

morphdom

Package Overview
Dependencies
Maintainers
6
Versions
71
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

morphdom - npm Package Compare versions

Comparing version 2.5.3 to 2.5.4

docs/old-benchmark.md

299

dist/morphdom-esm.js

@@ -74,2 +74,3 @@ function morphAttrs(fromNode, toNode) {

range = doc.createRange();
range.selectNode(doc.body);
}

@@ -257,2 +258,3 @@

var ELEMENT_NODE = 1;
var DOCUMENT_FRAGMENT_NODE = 11;
var TEXT_NODE = 3;

@@ -382,3 +384,3 @@ var COMMENT_NODE = 8;

function indexTree(node) {
if (node.nodeType === ELEMENT_NODE) {
if (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE) {
var curChild = node.firstChild;

@@ -413,3 +415,3 @@ while (curChild) {

curChild.parentNode.replaceChild(unmatchedFromEl, curChild);
morphChildren(unmatchedFromEl, curChild);
morphEl(unmatchedFromEl, curChild);
}

@@ -423,5 +425,23 @@ }

function morphChildren(fromEl, toEl, childrenOnly) {
function cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey) {
// 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) {
var fromNextSibling = curFromNodeChild.nextSibling;
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
curFromNodeChild = fromNextSibling;
}
}
function morphEl(fromEl, toEl, childrenOnly) {
var toElKey = getNodeKey(toEl);
var curFromNodeKey;

@@ -453,167 +473,131 @@ if (toElKey) {

}
if (fromEl.nodeName !== 'TEXTAREA') {
var curToNodeChild = toEl.firstChild;
var curFromNodeChild = fromEl.firstChild;
var curToNodeKey;
morphChildren(fromEl, toEl);
} else {
specialElHandlers.TEXTAREA(fromEl, toEl);
}
}
var fromNextSibling;
var toNextSibling;
var matchingFromEl;
function morphChildren(fromEl, toEl) {
var curToNodeChild = toEl.firstChild;
var curFromNodeChild = fromEl.firstChild;
var curToNodeKey;
var curFromNodeKey;
// walk the children
outer: while (curToNodeChild) {
toNextSibling = curToNodeChild.nextSibling;
curToNodeKey = getNodeKey(curToNodeChild);
var fromNextSibling;
var toNextSibling;
var matchingFromEl;
// walk the fromNode children all the way through
while (curFromNodeChild) {
fromNextSibling = curFromNodeChild.nextSibling;
// walk the children
outer: while (curToNodeChild) {
toNextSibling = curToNodeChild.nextSibling;
curToNodeKey = getNodeKey(curToNodeChild);
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
// walk the fromNode children all the way through
while (curFromNodeChild) {
fromNextSibling = curFromNodeChild.nextSibling;
curFromNodeKey = getNodeKey(curFromNodeChild);
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
var curFromNodeType = curFromNodeChild.nodeType;
curFromNodeKey = getNodeKey(curFromNodeChild);
var isCompatible = undefined;
var curFromNodeType = curFromNodeChild.nodeType;
if (curFromNodeType === curToNodeChild.nodeType) {
if (curFromNodeType === ELEMENT_NODE) {
// Both nodes being compared are Element nodes
// this means if the curFromNodeChild doesnt have a match with the curToNodeChild
var isCompatible = undefined;
if (curToNodeKey) {
// The target node has a key so we want to match it up with the correct element
// in the original DOM tree
if (curToNodeKey !== curFromNodeKey) {
// The current element in the original DOM tree does not have a matching key so
// let's check our lookup to see if there is a matching element in the original
// DOM tree
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
if (fromNextSibling === matchingFromEl) {
// Special case for single element removals. To avoid removing the original
// DOM node out of the tree (since that can break CSS transitions, etc.),
// we will instead discard the current node and wait until the next
// iteration to properly match up the keyed target element with its matching
// element in the original tree
isCompatible = false;
} else {
// We found a matching keyed element somewhere in the original DOM tree.
// Let's move the original DOM node into the current position and morph
// it.
if (curFromNodeType === curToNodeChild.nodeType) {
if (curFromNodeType === ELEMENT_NODE) {
// Both nodes being compared are Element nodes
// NOTE: We use insertBefore instead of replaceChild because we want to go through
// the `removeNode()` function for the node that is being discarded so that
// all lifecycle hooks are correctly invoked
fromEl.insertBefore(matchingFromEl, curFromNodeChild);
if (curToNodeKey) {
// The target node has a key so we want to match it up with the correct element
// in the original DOM tree
if (curToNodeKey !== curFromNodeKey) {
// The current element in the original DOM tree does not have a matching key so
// let's check our lookup to see if there is a matching element in the original
// DOM tree
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
if (fromNextSibling === matchingFromEl) {
// Special case for single element removals. To avoid removing the original
// DOM node out of the tree (since that can break CSS transitions, etc.),
// we will instead discard the current node and wait until the next
// iteration to properly match up the keyed target element with its matching
// element in the original tree
isCompatible = false;
} else {
// We found a matching keyed element somewhere in the original DOM tree.
// Let's move the original DOM node into the current position and morph
// it.
// fromNextSibling = curFromNodeChild.nextSibling;
// NOTE: We use insertBefore instead of replaceChild because we want to go through
// the `removeNode()` function for the node that is being discarded so that
// all lifecycle hooks are correctly invoked
fromEl.insertBefore(matchingFromEl, curFromNodeChild);
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
// fromNextSibling = curFromNodeChild.nextSibling;
curFromNodeChild = matchingFromEl;
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
} else {
// The nodes are not compatible since the "to" node has a key and there
// is no matching keyed node in the source tree
isCompatible = false;
curFromNodeChild = matchingFromEl;
}
} else {
// The nodes are not compatible since the "to" node has a key and there
// is no matching keyed node in the source tree
isCompatible = false;
}
} else if (curFromNodeKey) {
// The original has a key
isCompatible = false;
}
} else if (curFromNodeKey) {
// The original has a key
isCompatible = false;
}
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
if (isCompatible) {
// We found compatible DOM elements so transform
// the current "from" node to match the current
// target DOM node.
// MORPH
morphChildren(curFromNodeChild, curToNodeChild);
}
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
if (isCompatible) {
// We found compatible DOM elements so transform
// the current "from" node to match the current
// target DOM node.
// MORPH
morphEl(curFromNodeChild, curToNodeChild);
}
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
// Both nodes being compared are Text or Comment nodes
isCompatible = true;
// Simply update nodeValue on the original node to
// change the text value
if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
}
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
// Both nodes being compared are Text or Comment nodes
isCompatible = true;
// Simply update nodeValue on the original node to
// change the text value
if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
}
}
if (isCompatible) {
// Advance both the "to" child and the "from" child since we found a match
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
}
// No compatible match so remove the old node from the DOM and continue trying to find a
// match in the original DOM. However, we only do this if the from node is not keyed
// since it is possible that a keyed node might match up with a node somewhere else in the
// target tree and we don't want to discard it just yet since it still might find a
// home in the final DOM tree. After everything is done we will remove any keyed nodes
// that didn't find a home
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
if (isCompatible) {
// Advance both the "to" child and the "from" child since we found a match
// Nothing else to do as we already recursively called morphChildren above
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
} // END: while(curFromNodeChild) {}
// 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 (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
fromEl.appendChild(matchingFromEl);
// MORPH
morphChildren(matchingFromEl, curToNodeChild);
} else {
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
if (onBeforeNodeAddedResult !== false) {
if (onBeforeNodeAddedResult) {
curToNodeChild = onBeforeNodeAddedResult;
}
if (curToNodeChild.actualize) {
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
}
fromEl.appendChild(curToNodeChild);
handleNodeAdded(curToNodeChild);
}
continue outer;
}
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
}
// 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;
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
// No compatible match so remove the old node from the DOM and continue trying to find a
// match in the original DOM. However, we only do this if the from node is not keyed
// since it is possible that a keyed node might match up with a node somewhere else in the
// target tree and we don't want to discard it just yet since it still might find a
// home in the final DOM tree. After everything is done we will remove any keyed nodes
// that didn't find a home
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer

@@ -627,6 +611,35 @@ // the actual removal to later

}
curFromNodeChild = fromNextSibling;
} // END: while(curFromNodeChild) {}
// 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 (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
fromEl.appendChild(matchingFromEl);
// MORPH
morphEl(matchingFromEl, curToNodeChild);
} else {
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
if (onBeforeNodeAddedResult !== false) {
if (onBeforeNodeAddedResult) {
curToNodeChild = onBeforeNodeAddedResult;
}
if (curToNodeChild.actualize) {
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
}
fromEl.appendChild(curToNodeChild);
handleNodeAdded(curToNodeChild);
}
}
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
}
cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey);
var specialElHandler = specialElHandlers[fromEl.nodeName];

@@ -674,3 +687,3 @@ if (specialElHandler) {

} else {
morphChildren(morphedNode, toNode, childrenOnly);
morphEl(morphedNode, toNode, childrenOnly);

@@ -677,0 +690,0 @@ // We now need to loop over any keyed nodes that might need to be

@@ -19,2 +19,3 @@ 'use strict';

range = doc.createRange();
range.selectNode(doc.body);
}

@@ -202,2 +203,3 @@

var ELEMENT_NODE = 1;
var DOCUMENT_FRAGMENT_NODE = 11;
var TEXT_NODE = 3;

@@ -327,3 +329,3 @@ var COMMENT_NODE = 8;

function indexTree(node) {
if (node.nodeType === ELEMENT_NODE) {
if (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE) {
var curChild = node.firstChild;

@@ -358,3 +360,3 @@ while (curChild) {

curChild.parentNode.replaceChild(unmatchedFromEl, curChild);
morphChildren(unmatchedFromEl, curChild);
morphEl(unmatchedFromEl, curChild);
}

@@ -368,5 +370,23 @@ }

function morphChildren(fromEl, toEl, childrenOnly) {
function cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey) {
// 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) {
var fromNextSibling = curFromNodeChild.nextSibling;
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
curFromNodeChild = fromNextSibling;
}
}
function morphEl(fromEl, toEl, childrenOnly) {
var toElKey = getNodeKey(toEl);
var curFromNodeKey;

@@ -398,167 +418,131 @@ if (toElKey) {

}
if (fromEl.nodeName !== 'TEXTAREA') {
var curToNodeChild = toEl.firstChild;
var curFromNodeChild = fromEl.firstChild;
var curToNodeKey;
morphChildren(fromEl, toEl);
} else {
specialElHandlers.TEXTAREA(fromEl, toEl);
}
}
var fromNextSibling;
var toNextSibling;
var matchingFromEl;
function morphChildren(fromEl, toEl) {
var curToNodeChild = toEl.firstChild;
var curFromNodeChild = fromEl.firstChild;
var curToNodeKey;
var curFromNodeKey;
// walk the children
outer: while (curToNodeChild) {
toNextSibling = curToNodeChild.nextSibling;
curToNodeKey = getNodeKey(curToNodeChild);
var fromNextSibling;
var toNextSibling;
var matchingFromEl;
// walk the fromNode children all the way through
while (curFromNodeChild) {
fromNextSibling = curFromNodeChild.nextSibling;
// walk the children
outer: while (curToNodeChild) {
toNextSibling = curToNodeChild.nextSibling;
curToNodeKey = getNodeKey(curToNodeChild);
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
// walk the fromNode children all the way through
while (curFromNodeChild) {
fromNextSibling = curFromNodeChild.nextSibling;
curFromNodeKey = getNodeKey(curFromNodeChild);
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
var curFromNodeType = curFromNodeChild.nodeType;
curFromNodeKey = getNodeKey(curFromNodeChild);
var isCompatible = undefined;
var curFromNodeType = curFromNodeChild.nodeType;
if (curFromNodeType === curToNodeChild.nodeType) {
if (curFromNodeType === ELEMENT_NODE) {
// Both nodes being compared are Element nodes
// this means if the curFromNodeChild doesnt have a match with the curToNodeChild
var isCompatible = undefined;
if (curToNodeKey) {
// The target node has a key so we want to match it up with the correct element
// in the original DOM tree
if (curToNodeKey !== curFromNodeKey) {
// The current element in the original DOM tree does not have a matching key so
// let's check our lookup to see if there is a matching element in the original
// DOM tree
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
if (fromNextSibling === matchingFromEl) {
// Special case for single element removals. To avoid removing the original
// DOM node out of the tree (since that can break CSS transitions, etc.),
// we will instead discard the current node and wait until the next
// iteration to properly match up the keyed target element with its matching
// element in the original tree
isCompatible = false;
} else {
// We found a matching keyed element somewhere in the original DOM tree.
// Let's move the original DOM node into the current position and morph
// it.
if (curFromNodeType === curToNodeChild.nodeType) {
if (curFromNodeType === ELEMENT_NODE) {
// Both nodes being compared are Element nodes
// NOTE: We use insertBefore instead of replaceChild because we want to go through
// the `removeNode()` function for the node that is being discarded so that
// all lifecycle hooks are correctly invoked
fromEl.insertBefore(matchingFromEl, curFromNodeChild);
if (curToNodeKey) {
// The target node has a key so we want to match it up with the correct element
// in the original DOM tree
if (curToNodeKey !== curFromNodeKey) {
// The current element in the original DOM tree does not have a matching key so
// let's check our lookup to see if there is a matching element in the original
// DOM tree
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
if (fromNextSibling === matchingFromEl) {
// Special case for single element removals. To avoid removing the original
// DOM node out of the tree (since that can break CSS transitions, etc.),
// we will instead discard the current node and wait until the next
// iteration to properly match up the keyed target element with its matching
// element in the original tree
isCompatible = false;
} else {
// We found a matching keyed element somewhere in the original DOM tree.
// Let's move the original DOM node into the current position and morph
// it.
// fromNextSibling = curFromNodeChild.nextSibling;
// NOTE: We use insertBefore instead of replaceChild because we want to go through
// the `removeNode()` function for the node that is being discarded so that
// all lifecycle hooks are correctly invoked
fromEl.insertBefore(matchingFromEl, curFromNodeChild);
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
// fromNextSibling = curFromNodeChild.nextSibling;
curFromNodeChild = matchingFromEl;
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
} else {
// The nodes are not compatible since the "to" node has a key and there
// is no matching keyed node in the source tree
isCompatible = false;
curFromNodeChild = matchingFromEl;
}
} else {
// The nodes are not compatible since the "to" node has a key and there
// is no matching keyed node in the source tree
isCompatible = false;
}
} else if (curFromNodeKey) {
// The original has a key
isCompatible = false;
}
} else if (curFromNodeKey) {
// The original has a key
isCompatible = false;
}
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
if (isCompatible) {
// We found compatible DOM elements so transform
// the current "from" node to match the current
// target DOM node.
// MORPH
morphChildren(curFromNodeChild, curToNodeChild);
}
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
if (isCompatible) {
// We found compatible DOM elements so transform
// the current "from" node to match the current
// target DOM node.
// MORPH
morphEl(curFromNodeChild, curToNodeChild);
}
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
// Both nodes being compared are Text or Comment nodes
isCompatible = true;
// Simply update nodeValue on the original node to
// change the text value
if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
}
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
// Both nodes being compared are Text or Comment nodes
isCompatible = true;
// Simply update nodeValue on the original node to
// change the text value
if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
}
}
if (isCompatible) {
// Advance both the "to" child and the "from" child since we found a match
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
}
// No compatible match so remove the old node from the DOM and continue trying to find a
// match in the original DOM. However, we only do this if the from node is not keyed
// since it is possible that a keyed node might match up with a node somewhere else in the
// target tree and we don't want to discard it just yet since it still might find a
// home in the final DOM tree. After everything is done we will remove any keyed nodes
// that didn't find a home
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
if (isCompatible) {
// Advance both the "to" child and the "from" child since we found a match
// Nothing else to do as we already recursively called morphChildren above
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
} // END: while(curFromNodeChild) {}
// 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 (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
fromEl.appendChild(matchingFromEl);
// MORPH
morphChildren(matchingFromEl, curToNodeChild);
} else {
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
if (onBeforeNodeAddedResult !== false) {
if (onBeforeNodeAddedResult) {
curToNodeChild = onBeforeNodeAddedResult;
}
if (curToNodeChild.actualize) {
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
}
fromEl.appendChild(curToNodeChild);
handleNodeAdded(curToNodeChild);
}
continue outer;
}
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
}
// 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;
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
// No compatible match so remove the old node from the DOM and continue trying to find a
// match in the original DOM. However, we only do this if the from node is not keyed
// since it is possible that a keyed node might match up with a node somewhere else in the
// target tree and we don't want to discard it just yet since it still might find a
// home in the final DOM tree. After everything is done we will remove any keyed nodes
// that didn't find a home
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer

@@ -572,6 +556,35 @@ // the actual removal to later

}
curFromNodeChild = fromNextSibling;
} // END: while(curFromNodeChild) {}
// 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 (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
fromEl.appendChild(matchingFromEl);
// MORPH
morphEl(matchingFromEl, curToNodeChild);
} else {
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
if (onBeforeNodeAddedResult !== false) {
if (onBeforeNodeAddedResult) {
curToNodeChild = onBeforeNodeAddedResult;
}
if (curToNodeChild.actualize) {
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
}
fromEl.appendChild(curToNodeChild);
handleNodeAdded(curToNodeChild);
}
}
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
}
cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey);
var specialElHandler = specialElHandlers[fromEl.nodeName];

@@ -619,3 +632,3 @@ if (specialElHandler) {

} else {
morphChildren(morphedNode, toNode, childrenOnly);
morphEl(morphedNode, toNode, childrenOnly);

@@ -622,0 +635,0 @@ // We now need to loop over any keyed nodes that might need to be

@@ -80,2 +80,3 @@ (function (global, factory) {

range = doc.createRange();
range.selectNode(doc.body);
}

@@ -263,2 +264,3 @@

var ELEMENT_NODE = 1;
var DOCUMENT_FRAGMENT_NODE = 11;
var TEXT_NODE = 3;

@@ -388,3 +390,3 @@ var COMMENT_NODE = 8;

function indexTree(node) {
if (node.nodeType === ELEMENT_NODE) {
if (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE) {
var curChild = node.firstChild;

@@ -419,3 +421,3 @@ while (curChild) {

curChild.parentNode.replaceChild(unmatchedFromEl, curChild);
morphChildren(unmatchedFromEl, curChild);
morphEl(unmatchedFromEl, curChild);
}

@@ -429,5 +431,23 @@ }

function morphChildren(fromEl, toEl, childrenOnly) {
function cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey) {
// 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) {
var fromNextSibling = curFromNodeChild.nextSibling;
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
curFromNodeChild = fromNextSibling;
}
}
function morphEl(fromEl, toEl, childrenOnly) {
var toElKey = getNodeKey(toEl);
var curFromNodeKey;

@@ -459,167 +479,131 @@ if (toElKey) {

}
if (fromEl.nodeName !== 'TEXTAREA') {
var curToNodeChild = toEl.firstChild;
var curFromNodeChild = fromEl.firstChild;
var curToNodeKey;
morphChildren(fromEl, toEl);
} else {
specialElHandlers.TEXTAREA(fromEl, toEl);
}
}
var fromNextSibling;
var toNextSibling;
var matchingFromEl;
function morphChildren(fromEl, toEl) {
var curToNodeChild = toEl.firstChild;
var curFromNodeChild = fromEl.firstChild;
var curToNodeKey;
var curFromNodeKey;
// walk the children
outer: while (curToNodeChild) {
toNextSibling = curToNodeChild.nextSibling;
curToNodeKey = getNodeKey(curToNodeChild);
var fromNextSibling;
var toNextSibling;
var matchingFromEl;
// walk the fromNode children all the way through
while (curFromNodeChild) {
fromNextSibling = curFromNodeChild.nextSibling;
// walk the children
outer: while (curToNodeChild) {
toNextSibling = curToNodeChild.nextSibling;
curToNodeKey = getNodeKey(curToNodeChild);
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
// walk the fromNode children all the way through
while (curFromNodeChild) {
fromNextSibling = curFromNodeChild.nextSibling;
curFromNodeKey = getNodeKey(curFromNodeChild);
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
var curFromNodeType = curFromNodeChild.nodeType;
curFromNodeKey = getNodeKey(curFromNodeChild);
var isCompatible = undefined;
var curFromNodeType = curFromNodeChild.nodeType;
if (curFromNodeType === curToNodeChild.nodeType) {
if (curFromNodeType === ELEMENT_NODE) {
// Both nodes being compared are Element nodes
// this means if the curFromNodeChild doesnt have a match with the curToNodeChild
var isCompatible = undefined;
if (curToNodeKey) {
// The target node has a key so we want to match it up with the correct element
// in the original DOM tree
if (curToNodeKey !== curFromNodeKey) {
// The current element in the original DOM tree does not have a matching key so
// let's check our lookup to see if there is a matching element in the original
// DOM tree
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
if (fromNextSibling === matchingFromEl) {
// Special case for single element removals. To avoid removing the original
// DOM node out of the tree (since that can break CSS transitions, etc.),
// we will instead discard the current node and wait until the next
// iteration to properly match up the keyed target element with its matching
// element in the original tree
isCompatible = false;
} else {
// We found a matching keyed element somewhere in the original DOM tree.
// Let's move the original DOM node into the current position and morph
// it.
if (curFromNodeType === curToNodeChild.nodeType) {
if (curFromNodeType === ELEMENT_NODE) {
// Both nodes being compared are Element nodes
// NOTE: We use insertBefore instead of replaceChild because we want to go through
// the `removeNode()` function for the node that is being discarded so that
// all lifecycle hooks are correctly invoked
fromEl.insertBefore(matchingFromEl, curFromNodeChild);
if (curToNodeKey) {
// The target node has a key so we want to match it up with the correct element
// in the original DOM tree
if (curToNodeKey !== curFromNodeKey) {
// The current element in the original DOM tree does not have a matching key so
// let's check our lookup to see if there is a matching element in the original
// DOM tree
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
if (fromNextSibling === matchingFromEl) {
// Special case for single element removals. To avoid removing the original
// DOM node out of the tree (since that can break CSS transitions, etc.),
// we will instead discard the current node and wait until the next
// iteration to properly match up the keyed target element with its matching
// element in the original tree
isCompatible = false;
} else {
// We found a matching keyed element somewhere in the original DOM tree.
// Let's move the original DOM node into the current position and morph
// it.
// fromNextSibling = curFromNodeChild.nextSibling;
// NOTE: We use insertBefore instead of replaceChild because we want to go through
// the `removeNode()` function for the node that is being discarded so that
// all lifecycle hooks are correctly invoked
fromEl.insertBefore(matchingFromEl, curFromNodeChild);
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
// fromNextSibling = curFromNodeChild.nextSibling;
curFromNodeChild = matchingFromEl;
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
} else {
// The nodes are not compatible since the "to" node has a key and there
// is no matching keyed node in the source tree
isCompatible = false;
curFromNodeChild = matchingFromEl;
}
} else {
// The nodes are not compatible since the "to" node has a key and there
// is no matching keyed node in the source tree
isCompatible = false;
}
} else if (curFromNodeKey) {
// The original has a key
isCompatible = false;
}
} else if (curFromNodeKey) {
// The original has a key
isCompatible = false;
}
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
if (isCompatible) {
// We found compatible DOM elements so transform
// the current "from" node to match the current
// target DOM node.
// MORPH
morphChildren(curFromNodeChild, curToNodeChild);
}
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
if (isCompatible) {
// We found compatible DOM elements so transform
// the current "from" node to match the current
// target DOM node.
// MORPH
morphEl(curFromNodeChild, curToNodeChild);
}
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
// Both nodes being compared are Text or Comment nodes
isCompatible = true;
// Simply update nodeValue on the original node to
// change the text value
if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
}
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
// Both nodes being compared are Text or Comment nodes
isCompatible = true;
// Simply update nodeValue on the original node to
// change the text value
if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
}
}
if (isCompatible) {
// Advance both the "to" child and the "from" child since we found a match
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
}
// No compatible match so remove the old node from the DOM and continue trying to find a
// match in the original DOM. However, we only do this if the from node is not keyed
// since it is possible that a keyed node might match up with a node somewhere else in the
// target tree and we don't want to discard it just yet since it still might find a
// home in the final DOM tree. After everything is done we will remove any keyed nodes
// that didn't find a home
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
if (isCompatible) {
// Advance both the "to" child and the "from" child since we found a match
// Nothing else to do as we already recursively called morphChildren above
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
} // END: while(curFromNodeChild) {}
// 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 (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
fromEl.appendChild(matchingFromEl);
// MORPH
morphChildren(matchingFromEl, curToNodeChild);
} else {
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
if (onBeforeNodeAddedResult !== false) {
if (onBeforeNodeAddedResult) {
curToNodeChild = onBeforeNodeAddedResult;
}
if (curToNodeChild.actualize) {
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
}
fromEl.appendChild(curToNodeChild);
handleNodeAdded(curToNodeChild);
}
continue outer;
}
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
}
// 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;
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
// No compatible match so remove the old node from the DOM and continue trying to find a
// match in the original DOM. However, we only do this if the from node is not keyed
// since it is possible that a keyed node might match up with a node somewhere else in the
// target tree and we don't want to discard it just yet since it still might find a
// home in the final DOM tree. After everything is done we will remove any keyed nodes
// that didn't find a home
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer

@@ -633,6 +617,35 @@ // the actual removal to later

}
curFromNodeChild = fromNextSibling;
} // END: while(curFromNodeChild) {}
// 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 (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
fromEl.appendChild(matchingFromEl);
// MORPH
morphEl(matchingFromEl, curToNodeChild);
} else {
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
if (onBeforeNodeAddedResult !== false) {
if (onBeforeNodeAddedResult) {
curToNodeChild = onBeforeNodeAddedResult;
}
if (curToNodeChild.actualize) {
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
}
fromEl.appendChild(curToNodeChild);
handleNodeAdded(curToNodeChild);
}
}
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
}
cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey);
var specialElHandler = specialElHandlers[fromEl.nodeName];

@@ -680,3 +693,3 @@ if (specialElHandler) {

} else {
morphChildren(morphedNode, toNode, childrenOnly);
morphEl(morphedNode, toNode, childrenOnly);

@@ -683,0 +696,0 @@ // We now need to loop over any keyed nodes that might need to be

@@ -1,1 +0,1 @@

(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=global||self,global.morphdom=factory())})(this,function(){"use strict";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;attrNamespaceURI=attr.namespaceURI;attrValue=attr.value;if(attrNamespaceURI){attrName=attr.localName||attrName;fromValue=fromNode.getAttributeNS(attrNamespaceURI,attrName);if(fromValue!==attrValue){fromNode.setAttributeNS(attrNamespaceURI,attrName,attrValue)}}else{fromValue=fromNode.getAttribute(attrName);if(fromValue!==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;attrNamespaceURI=attr.namespaceURI;if(attrNamespaceURI){attrName=attr.localName||attrName;if(!toNode.hasAttributeNS(attrNamespaceURI,attrName)){fromNode.removeAttributeNS(attrNamespaceURI,attrName)}}else{if(!toNode.hasAttribute(attrName)){fromNode.removeAttribute(attrName)}}}}}var range;var NS_XHTML="http://www.w3.org/1999/xhtml";var doc=typeof document==="undefined"?undefined:document;function toElement(str){if(!range&&doc.createRange){range=doc.createRange()}var fragment;if(range&&range.createContextualFragment){fragment=range.createContextualFragment(str)}else{fragment=doc.createElement("body");fragment.innerHTML=str}return fragment.childNodes[0]}function compareNodeNames(fromEl,toEl){var fromNodeName=fromEl.nodeName;var toNodeName=toEl.nodeName;if(fromNodeName===toNodeName){return true}if(toEl.actualize&&fromNodeName.charCodeAt(0)<91&&toNodeName.charCodeAt(0)>90){return fromNodeName===toNodeName.toUpperCase()}else{return false}}function createElementNS(name,namespaceURI){return!namespaceURI||namespaceURI===NS_XHTML?doc.createElement(name):doc.createElementNS(namespaceURI,name)}function moveChildren(fromEl,toEl){var curChild=fromEl.firstChild;while(curChild){var nextChild=curChild.nextSibling;toEl.appendChild(curChild);curChild=nextChild}return toEl}function syncBooleanAttrProp(fromEl,toEl,name){if(fromEl[name]!==toEl[name]){fromEl[name]=toEl[name];if(fromEl[name]){fromEl.setAttribute(name,"")}else{fromEl.removeAttribute(name)}}}var specialElHandlers={OPTION:function(fromEl,toEl){var parentNode=fromEl.parentNode;if(parentNode){var parentName=parentNode.nodeName.toUpperCase();if(parentName==="OPTGROUP"){parentNode=parentNode.parentNode;parentName=parentNode&&parentNode.nodeName.toUpperCase()}if(parentName==="SELECT"&&!parentNode.hasAttribute("multiple")){if(fromEl.hasAttribute("selected")&&!toEl.selected){fromEl.setAttribute("selected","selected");fromEl.removeAttribute("selected")}parentNode.selectedIndex=-1}}syncBooleanAttrProp(fromEl,toEl,"selected")},INPUT:function(fromEl,toEl){syncBooleanAttrProp(fromEl,toEl,"checked");syncBooleanAttrProp(fromEl,toEl,"disabled");if(fromEl.value!==toEl.value){fromEl.value=toEl.value}if(!toEl.hasAttribute("value")){fromEl.removeAttribute("value")}},TEXTAREA:function(fromEl,toEl){var newValue=toEl.value;if(fromEl.value!==newValue){fromEl.value=newValue}var firstChild=fromEl.firstChild;if(firstChild){var oldValue=firstChild.nodeValue;if(oldValue==newValue||!newValue&&oldValue==fromEl.placeholder){return}firstChild.nodeValue=newValue}},SELECT:function(fromEl,toEl){if(!toEl.hasAttribute("multiple")){var selectedIndex=-1;var i=0;var curChild=fromEl.firstChild;var optgroup;var nodeName;while(curChild){nodeName=curChild.nodeName&&curChild.nodeName.toUpperCase();if(nodeName==="OPTGROUP"){optgroup=curChild;curChild=optgroup.firstChild}else{if(nodeName==="OPTION"){if(curChild.hasAttribute("selected")){selectedIndex=i;break}i++}curChild=curChild.nextSibling;if(!curChild&&optgroup){curChild=optgroup.nextSibling;optgroup=null}}}fromEl.selectedIndex=selectedIndex}}};var ELEMENT_NODE=1;var TEXT_NODE=3;var COMMENT_NODE=8;function noop(){}function defaultGetNodeKey(node){return node.id}function morphdomFactory(morphAttrs){return function morphdom(fromNode,toNode,options){if(!options){options={}}if(typeof toNode==="string"){if(fromNode.nodeName==="#document"||fromNode.nodeName==="HTML"){var toNodeHtml=toNode;toNode=doc.createElement("html");toNode.innerHTML=toNodeHtml}else{toNode=toElement(toNode)}}var getNodeKey=options.getNodeKey||defaultGetNodeKey;var onBeforeNodeAdded=options.onBeforeNodeAdded||noop;var onNodeAdded=options.onNodeAdded||noop;var onBeforeElUpdated=options.onBeforeElUpdated||noop;var onElUpdated=options.onElUpdated||noop;var onBeforeNodeDiscarded=options.onBeforeNodeDiscarded||noop;var onNodeDiscarded=options.onNodeDiscarded||noop;var onBeforeElChildrenUpdated=options.onBeforeElChildrenUpdated||noop;var childrenOnly=options.childrenOnly===true;var fromNodesLookup={};var keyedRemovalList;function addKeyedRemoval(key){if(keyedRemovalList){keyedRemovalList.push(key)}else{keyedRemovalList=[key]}}function walkDiscardedChildNodes(node,skipKeyedNodes){if(node.nodeType===ELEMENT_NODE){var curChild=node.firstChild;while(curChild){var key=undefined;if(skipKeyedNodes&&(key=getNodeKey(curChild))){addKeyedRemoval(key)}else{onNodeDiscarded(curChild);if(curChild.firstChild){walkDiscardedChildNodes(curChild,skipKeyedNodes)}}curChild=curChild.nextSibling}}}function removeNode(node,parentNode,skipKeyedNodes){if(onBeforeNodeDiscarded(node)===false){return}if(parentNode){parentNode.removeChild(node)}onNodeDiscarded(node);walkDiscardedChildNodes(node,skipKeyedNodes)}function indexTree(node){if(node.nodeType===ELEMENT_NODE){var curChild=node.firstChild;while(curChild){var key=getNodeKey(curChild);if(key){fromNodesLookup[key]=curChild}indexTree(curChild);curChild=curChild.nextSibling}}}indexTree(fromNode);function handleNodeAdded(el){onNodeAdded(el);var curChild=el.firstChild;while(curChild){var nextSibling=curChild.nextSibling;var key=getNodeKey(curChild);if(key){var unmatchedFromEl=fromNodesLookup[key];if(unmatchedFromEl&&compareNodeNames(curChild,unmatchedFromEl)){curChild.parentNode.replaceChild(unmatchedFromEl,curChild);morphChildren(unmatchedFromEl,curChild)}}handleNodeAdded(curChild);curChild=nextSibling}}function morphChildren(fromEl,toEl,childrenOnly){var toElKey=getNodeKey(toEl);var curFromNodeKey;if(toElKey){delete fromNodesLookup[toElKey]}if(toNode.isSameNode&&toNode.isSameNode(fromNode)){return}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 curToNodeKey;var fromNextSibling;var toNextSibling;var matchingFromEl;outer:while(curToNodeChild){toNextSibling=curToNodeChild.nextSibling;curToNodeKey=getNodeKey(curToNodeChild);while(curFromNodeChild){fromNextSibling=curFromNodeChild.nextSibling;if(curToNodeChild.isSameNode&&curToNodeChild.isSameNode(curFromNodeChild)){curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling;continue outer}curFromNodeKey=getNodeKey(curFromNodeChild);var curFromNodeType=curFromNodeChild.nodeType;var isCompatible=undefined;if(curFromNodeType===curToNodeChild.nodeType){if(curFromNodeType===ELEMENT_NODE){if(curToNodeKey){if(curToNodeKey!==curFromNodeKey){if(matchingFromEl=fromNodesLookup[curToNodeKey]){if(fromNextSibling===matchingFromEl){isCompatible=false}else{fromEl.insertBefore(matchingFromEl,curFromNodeChild);if(curFromNodeKey){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}curFromNodeChild=matchingFromEl}}else{isCompatible=false}}}else if(curFromNodeKey){isCompatible=false}isCompatible=isCompatible!==false&&compareNodeNames(curFromNodeChild,curToNodeChild);if(isCompatible){morphChildren(curFromNodeChild,curToNodeChild)}}else if(curFromNodeType===TEXT_NODE||curFromNodeType==COMMENT_NODE){isCompatible=true;if(curFromNodeChild.nodeValue!==curToNodeChild.nodeValue){curFromNodeChild.nodeValue=curToNodeChild.nodeValue}}}if(isCompatible){curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling;continue outer}if(curFromNodeKey){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}curFromNodeChild=fromNextSibling}if(curToNodeKey&&(matchingFromEl=fromNodesLookup[curToNodeKey])&&compareNodeNames(matchingFromEl,curToNodeChild)){fromEl.appendChild(matchingFromEl);morphChildren(matchingFromEl,curToNodeChild)}else{var onBeforeNodeAddedResult=onBeforeNodeAdded(curToNodeChild);if(onBeforeNodeAddedResult!==false){if(onBeforeNodeAddedResult){curToNodeChild=onBeforeNodeAddedResult}if(curToNodeChild.actualize){curToNodeChild=curToNodeChild.actualize(fromEl.ownerDocument||doc)}fromEl.appendChild(curToNodeChild);handleNodeAdded(curToNodeChild)}}curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling}while(curFromNodeChild){fromNextSibling=curFromNodeChild.nextSibling;if(curFromNodeKey=getNodeKey(curFromNodeChild)){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}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||morphedNodeType===COMMENT_NODE){if(toNodeType===morphedNodeType){if(morphedNode.nodeValue!==toNode.nodeValue){morphedNode.nodeValue=toNode.nodeValue}return morphedNode}else{morphedNode=toNode}}}if(morphedNode===toNode){onNodeDiscarded(fromNode)}else{morphChildren(morphedNode,toNode,childrenOnly);if(keyedRemovalList){for(var i=0,len=keyedRemovalList.length;i<len;i++){var elToRemove=fromNodesLookup[keyedRemovalList[i]];if(elToRemove){removeNode(elToRemove,elToRemove.parentNode,false)}}}}if(!childrenOnly&&morphedNode!==fromNode&&fromNode.parentNode){if(morphedNode.actualize){morphedNode=morphedNode.actualize(fromNode.ownerDocument||doc)}fromNode.parentNode.replaceChild(morphedNode,fromNode)}return morphedNode}}var morphdom=morphdomFactory(morphAttrs);return morphdom});
(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=global||self,global.morphdom=factory())})(this,function(){"use strict";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;attrNamespaceURI=attr.namespaceURI;attrValue=attr.value;if(attrNamespaceURI){attrName=attr.localName||attrName;fromValue=fromNode.getAttributeNS(attrNamespaceURI,attrName);if(fromValue!==attrValue){fromNode.setAttributeNS(attrNamespaceURI,attrName,attrValue)}}else{fromValue=fromNode.getAttribute(attrName);if(fromValue!==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;attrNamespaceURI=attr.namespaceURI;if(attrNamespaceURI){attrName=attr.localName||attrName;if(!toNode.hasAttributeNS(attrNamespaceURI,attrName)){fromNode.removeAttributeNS(attrNamespaceURI,attrName)}}else{if(!toNode.hasAttribute(attrName)){fromNode.removeAttribute(attrName)}}}}}var range;var NS_XHTML="http://www.w3.org/1999/xhtml";var doc=typeof document==="undefined"?undefined:document;function toElement(str){if(!range&&doc.createRange){range=doc.createRange();range.selectNode(doc.body)}var fragment;if(range&&range.createContextualFragment){fragment=range.createContextualFragment(str)}else{fragment=doc.createElement("body");fragment.innerHTML=str}return fragment.childNodes[0]}function compareNodeNames(fromEl,toEl){var fromNodeName=fromEl.nodeName;var toNodeName=toEl.nodeName;if(fromNodeName===toNodeName){return true}if(toEl.actualize&&fromNodeName.charCodeAt(0)<91&&toNodeName.charCodeAt(0)>90){return fromNodeName===toNodeName.toUpperCase()}else{return false}}function createElementNS(name,namespaceURI){return!namespaceURI||namespaceURI===NS_XHTML?doc.createElement(name):doc.createElementNS(namespaceURI,name)}function moveChildren(fromEl,toEl){var curChild=fromEl.firstChild;while(curChild){var nextChild=curChild.nextSibling;toEl.appendChild(curChild);curChild=nextChild}return toEl}function syncBooleanAttrProp(fromEl,toEl,name){if(fromEl[name]!==toEl[name]){fromEl[name]=toEl[name];if(fromEl[name]){fromEl.setAttribute(name,"")}else{fromEl.removeAttribute(name)}}}var specialElHandlers={OPTION:function(fromEl,toEl){var parentNode=fromEl.parentNode;if(parentNode){var parentName=parentNode.nodeName.toUpperCase();if(parentName==="OPTGROUP"){parentNode=parentNode.parentNode;parentName=parentNode&&parentNode.nodeName.toUpperCase()}if(parentName==="SELECT"&&!parentNode.hasAttribute("multiple")){if(fromEl.hasAttribute("selected")&&!toEl.selected){fromEl.setAttribute("selected","selected");fromEl.removeAttribute("selected")}parentNode.selectedIndex=-1}}syncBooleanAttrProp(fromEl,toEl,"selected")},INPUT:function(fromEl,toEl){syncBooleanAttrProp(fromEl,toEl,"checked");syncBooleanAttrProp(fromEl,toEl,"disabled");if(fromEl.value!==toEl.value){fromEl.value=toEl.value}if(!toEl.hasAttribute("value")){fromEl.removeAttribute("value")}},TEXTAREA:function(fromEl,toEl){var newValue=toEl.value;if(fromEl.value!==newValue){fromEl.value=newValue}var firstChild=fromEl.firstChild;if(firstChild){var oldValue=firstChild.nodeValue;if(oldValue==newValue||!newValue&&oldValue==fromEl.placeholder){return}firstChild.nodeValue=newValue}},SELECT:function(fromEl,toEl){if(!toEl.hasAttribute("multiple")){var selectedIndex=-1;var i=0;var curChild=fromEl.firstChild;var optgroup;var nodeName;while(curChild){nodeName=curChild.nodeName&&curChild.nodeName.toUpperCase();if(nodeName==="OPTGROUP"){optgroup=curChild;curChild=optgroup.firstChild}else{if(nodeName==="OPTION"){if(curChild.hasAttribute("selected")){selectedIndex=i;break}i++}curChild=curChild.nextSibling;if(!curChild&&optgroup){curChild=optgroup.nextSibling;optgroup=null}}}fromEl.selectedIndex=selectedIndex}}};var ELEMENT_NODE=1;var DOCUMENT_FRAGMENT_NODE=11;var TEXT_NODE=3;var COMMENT_NODE=8;function noop(){}function defaultGetNodeKey(node){return node.id}function morphdomFactory(morphAttrs){return function morphdom(fromNode,toNode,options){if(!options){options={}}if(typeof toNode==="string"){if(fromNode.nodeName==="#document"||fromNode.nodeName==="HTML"){var toNodeHtml=toNode;toNode=doc.createElement("html");toNode.innerHTML=toNodeHtml}else{toNode=toElement(toNode)}}var getNodeKey=options.getNodeKey||defaultGetNodeKey;var onBeforeNodeAdded=options.onBeforeNodeAdded||noop;var onNodeAdded=options.onNodeAdded||noop;var onBeforeElUpdated=options.onBeforeElUpdated||noop;var onElUpdated=options.onElUpdated||noop;var onBeforeNodeDiscarded=options.onBeforeNodeDiscarded||noop;var onNodeDiscarded=options.onNodeDiscarded||noop;var onBeforeElChildrenUpdated=options.onBeforeElChildrenUpdated||noop;var childrenOnly=options.childrenOnly===true;var fromNodesLookup={};var keyedRemovalList;function addKeyedRemoval(key){if(keyedRemovalList){keyedRemovalList.push(key)}else{keyedRemovalList=[key]}}function walkDiscardedChildNodes(node,skipKeyedNodes){if(node.nodeType===ELEMENT_NODE){var curChild=node.firstChild;while(curChild){var key=undefined;if(skipKeyedNodes&&(key=getNodeKey(curChild))){addKeyedRemoval(key)}else{onNodeDiscarded(curChild);if(curChild.firstChild){walkDiscardedChildNodes(curChild,skipKeyedNodes)}}curChild=curChild.nextSibling}}}function removeNode(node,parentNode,skipKeyedNodes){if(onBeforeNodeDiscarded(node)===false){return}if(parentNode){parentNode.removeChild(node)}onNodeDiscarded(node);walkDiscardedChildNodes(node,skipKeyedNodes)}function indexTree(node){if(node.nodeType===ELEMENT_NODE||node.nodeType===DOCUMENT_FRAGMENT_NODE){var curChild=node.firstChild;while(curChild){var key=getNodeKey(curChild);if(key){fromNodesLookup[key]=curChild}indexTree(curChild);curChild=curChild.nextSibling}}}indexTree(fromNode);function handleNodeAdded(el){onNodeAdded(el);var curChild=el.firstChild;while(curChild){var nextSibling=curChild.nextSibling;var key=getNodeKey(curChild);if(key){var unmatchedFromEl=fromNodesLookup[key];if(unmatchedFromEl&&compareNodeNames(curChild,unmatchedFromEl)){curChild.parentNode.replaceChild(unmatchedFromEl,curChild);morphEl(unmatchedFromEl,curChild)}}handleNodeAdded(curChild);curChild=nextSibling}}function cleanupFromEl(fromEl,curFromNodeChild,curFromNodeKey){while(curFromNodeChild){var fromNextSibling=curFromNodeChild.nextSibling;if(curFromNodeKey=getNodeKey(curFromNodeChild)){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}curFromNodeChild=fromNextSibling}}function morphEl(fromEl,toEl,childrenOnly){var toElKey=getNodeKey(toEl);if(toElKey){delete fromNodesLookup[toElKey]}if(toNode.isSameNode&&toNode.isSameNode(fromNode)){return}if(!childrenOnly){if(onBeforeElUpdated(fromEl,toEl)===false){return}morphAttrs(fromEl,toEl);onElUpdated(fromEl);if(onBeforeElChildrenUpdated(fromEl,toEl)===false){return}}if(fromEl.nodeName!=="TEXTAREA"){morphChildren(fromEl,toEl)}else{specialElHandlers.TEXTAREA(fromEl,toEl)}}function morphChildren(fromEl,toEl){var curToNodeChild=toEl.firstChild;var curFromNodeChild=fromEl.firstChild;var curToNodeKey;var curFromNodeKey;var fromNextSibling;var toNextSibling;var matchingFromEl;outer:while(curToNodeChild){toNextSibling=curToNodeChild.nextSibling;curToNodeKey=getNodeKey(curToNodeChild);while(curFromNodeChild){fromNextSibling=curFromNodeChild.nextSibling;if(curToNodeChild.isSameNode&&curToNodeChild.isSameNode(curFromNodeChild)){curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling;continue outer}curFromNodeKey=getNodeKey(curFromNodeChild);var curFromNodeType=curFromNodeChild.nodeType;var isCompatible=undefined;if(curFromNodeType===curToNodeChild.nodeType){if(curFromNodeType===ELEMENT_NODE){if(curToNodeKey){if(curToNodeKey!==curFromNodeKey){if(matchingFromEl=fromNodesLookup[curToNodeKey]){if(fromNextSibling===matchingFromEl){isCompatible=false}else{fromEl.insertBefore(matchingFromEl,curFromNodeChild);if(curFromNodeKey){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}curFromNodeChild=matchingFromEl}}else{isCompatible=false}}}else if(curFromNodeKey){isCompatible=false}isCompatible=isCompatible!==false&&compareNodeNames(curFromNodeChild,curToNodeChild);if(isCompatible){morphEl(curFromNodeChild,curToNodeChild)}}else if(curFromNodeType===TEXT_NODE||curFromNodeType==COMMENT_NODE){isCompatible=true;if(curFromNodeChild.nodeValue!==curToNodeChild.nodeValue){curFromNodeChild.nodeValue=curToNodeChild.nodeValue}}}if(isCompatible){curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling;continue outer}if(curFromNodeKey){addKeyedRemoval(curFromNodeKey)}else{removeNode(curFromNodeChild,fromEl,true)}curFromNodeChild=fromNextSibling}if(curToNodeKey&&(matchingFromEl=fromNodesLookup[curToNodeKey])&&compareNodeNames(matchingFromEl,curToNodeChild)){fromEl.appendChild(matchingFromEl);morphEl(matchingFromEl,curToNodeChild)}else{var onBeforeNodeAddedResult=onBeforeNodeAdded(curToNodeChild);if(onBeforeNodeAddedResult!==false){if(onBeforeNodeAddedResult){curToNodeChild=onBeforeNodeAddedResult}if(curToNodeChild.actualize){curToNodeChild=curToNodeChild.actualize(fromEl.ownerDocument||doc)}fromEl.appendChild(curToNodeChild);handleNodeAdded(curToNodeChild)}}curToNodeChild=toNextSibling;curFromNodeChild=fromNextSibling}cleanupFromEl(fromEl,curFromNodeChild,curFromNodeKey);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||morphedNodeType===COMMENT_NODE){if(toNodeType===morphedNodeType){if(morphedNode.nodeValue!==toNode.nodeValue){morphedNode.nodeValue=toNode.nodeValue}return morphedNode}else{morphedNode=toNode}}}if(morphedNode===toNode){onNodeDiscarded(fromNode)}else{morphEl(morphedNode,toNode,childrenOnly);if(keyedRemovalList){for(var i=0,len=keyedRemovalList.length;i<len;i++){var elToRemove=fromNodesLookup[keyedRemovalList[i]];if(elToRemove){removeNode(elToRemove,elToRemove.parentNode,false)}}}}if(!childrenOnly&&morphedNode!==fromNode&&fromNode.parentNode){if(morphedNode.actualize){morphedNode=morphedNode.actualize(fromNode.ownerDocument||doc)}fromNode.parentNode.replaceChild(morphedNode,fromNode)}return morphedNode}}var morphdom=morphdomFactory(morphAttrs);return morphdom});

@@ -76,2 +76,3 @@ 'use strict';

range = doc.createRange();
range.selectNode(doc.body);
}

@@ -259,2 +260,3 @@

var ELEMENT_NODE = 1;
var DOCUMENT_FRAGMENT_NODE = 11;
var TEXT_NODE = 3;

@@ -384,3 +386,3 @@ var COMMENT_NODE = 8;

function indexTree(node) {
if (node.nodeType === ELEMENT_NODE) {
if (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE) {
var curChild = node.firstChild;

@@ -415,3 +417,3 @@ while (curChild) {

curChild.parentNode.replaceChild(unmatchedFromEl, curChild);
morphChildren(unmatchedFromEl, curChild);
morphEl(unmatchedFromEl, curChild);
}

@@ -425,5 +427,23 @@ }

function morphChildren(fromEl, toEl, childrenOnly) {
function cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey) {
// 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) {
var fromNextSibling = curFromNodeChild.nextSibling;
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
curFromNodeChild = fromNextSibling;
}
}
function morphEl(fromEl, toEl, childrenOnly) {
var toElKey = getNodeKey(toEl);
var curFromNodeKey;

@@ -455,167 +475,131 @@ if (toElKey) {

}
if (fromEl.nodeName !== 'TEXTAREA') {
var curToNodeChild = toEl.firstChild;
var curFromNodeChild = fromEl.firstChild;
var curToNodeKey;
morphChildren(fromEl, toEl);
} else {
specialElHandlers.TEXTAREA(fromEl, toEl);
}
}
var fromNextSibling;
var toNextSibling;
var matchingFromEl;
function morphChildren(fromEl, toEl) {
var curToNodeChild = toEl.firstChild;
var curFromNodeChild = fromEl.firstChild;
var curToNodeKey;
var curFromNodeKey;
// walk the children
outer: while (curToNodeChild) {
toNextSibling = curToNodeChild.nextSibling;
curToNodeKey = getNodeKey(curToNodeChild);
var fromNextSibling;
var toNextSibling;
var matchingFromEl;
// walk the fromNode children all the way through
while (curFromNodeChild) {
fromNextSibling = curFromNodeChild.nextSibling;
// walk the children
outer: while (curToNodeChild) {
toNextSibling = curToNodeChild.nextSibling;
curToNodeKey = getNodeKey(curToNodeChild);
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
// walk the fromNode children all the way through
while (curFromNodeChild) {
fromNextSibling = curFromNodeChild.nextSibling;
curFromNodeKey = getNodeKey(curFromNodeChild);
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
var curFromNodeType = curFromNodeChild.nodeType;
curFromNodeKey = getNodeKey(curFromNodeChild);
var isCompatible = undefined;
var curFromNodeType = curFromNodeChild.nodeType;
if (curFromNodeType === curToNodeChild.nodeType) {
if (curFromNodeType === ELEMENT_NODE) {
// Both nodes being compared are Element nodes
// this means if the curFromNodeChild doesnt have a match with the curToNodeChild
var isCompatible = undefined;
if (curToNodeKey) {
// The target node has a key so we want to match it up with the correct element
// in the original DOM tree
if (curToNodeKey !== curFromNodeKey) {
// The current element in the original DOM tree does not have a matching key so
// let's check our lookup to see if there is a matching element in the original
// DOM tree
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
if (fromNextSibling === matchingFromEl) {
// Special case for single element removals. To avoid removing the original
// DOM node out of the tree (since that can break CSS transitions, etc.),
// we will instead discard the current node and wait until the next
// iteration to properly match up the keyed target element with its matching
// element in the original tree
isCompatible = false;
} else {
// We found a matching keyed element somewhere in the original DOM tree.
// Let's move the original DOM node into the current position and morph
// it.
if (curFromNodeType === curToNodeChild.nodeType) {
if (curFromNodeType === ELEMENT_NODE) {
// Both nodes being compared are Element nodes
// NOTE: We use insertBefore instead of replaceChild because we want to go through
// the `removeNode()` function for the node that is being discarded so that
// all lifecycle hooks are correctly invoked
fromEl.insertBefore(matchingFromEl, curFromNodeChild);
if (curToNodeKey) {
// The target node has a key so we want to match it up with the correct element
// in the original DOM tree
if (curToNodeKey !== curFromNodeKey) {
// The current element in the original DOM tree does not have a matching key so
// let's check our lookup to see if there is a matching element in the original
// DOM tree
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
if (fromNextSibling === matchingFromEl) {
// Special case for single element removals. To avoid removing the original
// DOM node out of the tree (since that can break CSS transitions, etc.),
// we will instead discard the current node and wait until the next
// iteration to properly match up the keyed target element with its matching
// element in the original tree
isCompatible = false;
} else {
// We found a matching keyed element somewhere in the original DOM tree.
// Let's move the original DOM node into the current position and morph
// it.
// fromNextSibling = curFromNodeChild.nextSibling;
// NOTE: We use insertBefore instead of replaceChild because we want to go through
// the `removeNode()` function for the node that is being discarded so that
// all lifecycle hooks are correctly invoked
fromEl.insertBefore(matchingFromEl, curFromNodeChild);
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
// fromNextSibling = curFromNodeChild.nextSibling;
curFromNodeChild = matchingFromEl;
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
} else {
// The nodes are not compatible since the "to" node has a key and there
// is no matching keyed node in the source tree
isCompatible = false;
curFromNodeChild = matchingFromEl;
}
} else {
// The nodes are not compatible since the "to" node has a key and there
// is no matching keyed node in the source tree
isCompatible = false;
}
} else if (curFromNodeKey) {
// The original has a key
isCompatible = false;
}
} else if (curFromNodeKey) {
// The original has a key
isCompatible = false;
}
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
if (isCompatible) {
// We found compatible DOM elements so transform
// the current "from" node to match the current
// target DOM node.
// MORPH
morphChildren(curFromNodeChild, curToNodeChild);
}
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
if (isCompatible) {
// We found compatible DOM elements so transform
// the current "from" node to match the current
// target DOM node.
// MORPH
morphEl(curFromNodeChild, curToNodeChild);
}
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
// Both nodes being compared are Text or Comment nodes
isCompatible = true;
// Simply update nodeValue on the original node to
// change the text value
if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
}
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
// Both nodes being compared are Text or Comment nodes
isCompatible = true;
// Simply update nodeValue on the original node to
// change the text value
if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
}
}
if (isCompatible) {
// Advance both the "to" child and the "from" child since we found a match
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
}
// No compatible match so remove the old node from the DOM and continue trying to find a
// match in the original DOM. However, we only do this if the from node is not keyed
// since it is possible that a keyed node might match up with a node somewhere else in the
// target tree and we don't want to discard it just yet since it still might find a
// home in the final DOM tree. After everything is done we will remove any keyed nodes
// that didn't find a home
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
if (isCompatible) {
// Advance both the "to" child and the "from" child since we found a match
// Nothing else to do as we already recursively called morphChildren above
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
} // END: while(curFromNodeChild) {}
// 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 (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
fromEl.appendChild(matchingFromEl);
// MORPH
morphChildren(matchingFromEl, curToNodeChild);
} else {
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
if (onBeforeNodeAddedResult !== false) {
if (onBeforeNodeAddedResult) {
curToNodeChild = onBeforeNodeAddedResult;
}
if (curToNodeChild.actualize) {
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
}
fromEl.appendChild(curToNodeChild);
handleNodeAdded(curToNodeChild);
}
continue outer;
}
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
}
// 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;
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
// No compatible match so remove the old node from the DOM and continue trying to find a
// match in the original DOM. However, we only do this if the from node is not keyed
// since it is possible that a keyed node might match up with a node somewhere else in the
// target tree and we don't want to discard it just yet since it still might find a
// home in the final DOM tree. After everything is done we will remove any keyed nodes
// that didn't find a home
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer

@@ -629,6 +613,35 @@ // the actual removal to later

}
curFromNodeChild = fromNextSibling;
} // END: while(curFromNodeChild) {}
// 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 (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
fromEl.appendChild(matchingFromEl);
// MORPH
morphEl(matchingFromEl, curToNodeChild);
} else {
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
if (onBeforeNodeAddedResult !== false) {
if (onBeforeNodeAddedResult) {
curToNodeChild = onBeforeNodeAddedResult;
}
if (curToNodeChild.actualize) {
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
}
fromEl.appendChild(curToNodeChild);
handleNodeAdded(curToNodeChild);
}
}
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
}
cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey);
var specialElHandler = specialElHandlers[fromEl.nodeName];

@@ -676,3 +689,3 @@ if (specialElHandler) {

} else {
morphChildren(morphedNode, toNode, childrenOnly);
morphEl(morphedNode, toNode, childrenOnly);

@@ -679,0 +692,0 @@ // We now need to loop over any keyed nodes that might need to be

@@ -48,5 +48,5 @@ {

"mocha-chrome": "^2.0.0",
"nanomorph": "^5.4.0",
"rollup": "^1.4.1",
"uglify-js": "^3.5.1",
"umd": "^3.0.1",
"vdom-virtualize": "2.0.0",

@@ -56,3 +56,3 @@ "virtual-dom": "^2.1.1"

"dependencies": {},
"version": "2.5.3",
"version": "2.5.4",
"keywords": [

@@ -59,0 +59,0 @@ "dom",

morphdom
========
![Download count all time](https://img.shields.io/npm/dt/morphdom.svg)
[![Build Status](https://travis-ci.org/patrick-steele-idem/morphdom.svg?branch=master)](https://travis-ci.org/patrick-steele-idem/morphdom)
[![NPM](https://nodei.co/npm/morphdom.png)](https://www.npmjs.com/package/morphdom)

@@ -85,3 +88,3 @@

- **getNodeKey** (`Function(node)`) - Called to get the `Node`'s unique identifier. This is used by `morphdom` to rearrange elements rather than creating and destroying an element that already exists. This defaults to using the `Node`'s `id` attribute.
- **getNodeKey** (`Function(node)`) - Called to get the `Node`'s unique identifier. This is used by `morphdom` to rearrange elements rather than creating and destroying an element that already exists. This defaults to using the `Node`'s `id` property. (Note that form fields must not have a `name` corresponding to forms' DOM properties, e.g. `id`.)
- **onBeforeNodeAdded** (`Function(node)`) - Called before a `Node` in the `to` tree is added to the `from` tree. If this function returns `false` then the node will not be added. Should return the node to be added.

@@ -129,2 +132,19 @@ - **onNodeAdded** (`Function(node)`) - Called after a `Node` in the `to` tree has been added to the `from` tree.

## Can I make morphdom blaze through the DOM tree even faster? Yes.
```js
morphdom(fromNode, toNode, {
onBeforeElUpdated: function(fromEl, toEl) {
// spec - https://dom.spec.whatwg.org/#concept-node-equals
if (fromEl.isEqualNode(toEl)) {
return false
}
return true
}
})
```
This avoids traversing through the entire subtree when you know they are equal. While we haven't added this to the core lib yet due to very minor concerns, this is an easy way to make DOM diffing speeds on par with virtual DOM.
## Isn't the DOM slow?

@@ -155,3 +175,3 @@

There are some drawbacks to using a virtual DOM-based approach even though the _Virtual DOM_ has improved considerably over the last few years. :
There are some drawbacks to using a virtual DOM-based approach even though the _Virtual DOM_ has improved considerably over the last few years:

@@ -167,2 +187,4 @@ - The real DOM is not the source of truth (the persistent virtual DOM tree is the source of truth)

Moreover, we have found that diffing small changes may be faster with actual DOM. As the diffing become larger, the cost of diffs slow down due to IO and virtual dom benefits begin to show.
See the [Benchmarks](#benchmarks) below for a comparison of `morphdom` with [virtual-dom](https://github.com/Matt-Esch/virtual-dom).

@@ -194,3 +216,3 @@

Below are the results on running benchmarks on various DOM transformations for both `morphdom` and [virtual-dom](https://github.com/Matt-Esch/virtual-dom). This benchmark uses a high performance timer (i.e., `window.performance.now()`) if available. For each test the benchmark runner will run `100` iterations. After all of the iterations are completed for one test the average time per iteration is calculated by dividing the total time by the number of iterations.
Below are the results on running benchmarks on various DOM transformations for both `morphdom`, [`nanomorph`](https://github.com/choojs/nanomorph) and [virtual-dom](https://github.com/Matt-Esch/virtual-dom). This benchmark uses a high performance timer (i.e., `window.performance.now()`) if available. For each test the benchmark runner will run `100` iterations. After all of the iterations are completed for one test the average time per iteration is calculated by dividing the total time by the number of iterations.

@@ -203,208 +225,7 @@ To run the benchmarks:

The table below shows some sample benchmark results when running the benchmarks on a MacBook Pro (2.8 GHz Intel Core i7, 16 GB 1600 MHz DDR3). The average time per iteration for each test is shown in the table below:
The table below shows some sample benchmark results when running the benchmarks on a MacBook Pro (2.3 GHz Intel Core i5, 8 GB 2133 MHz LPDDR3). Remember, as noted above, the larger the diff needed to evaluate, the more vdom will perform better. The average time per iteration for each test is shown in the table below:
<div class="results">
<ul>
<li> Total time for morphdom: 359.02ms (winner) </li>
<li> Total time for virtual-dom: 438.30ms </li>
</ul>
<table>
<thead>
<tr>
<td></td>
<td> morphdom </td>
<td> virtual-dom </td>
</tr>
</thead>
<tbody>
<tr>
<td class="test-name"> change-tagname </td>
<td> <b>0.01ms</b> </td>
<td> 0.01ms </td>
</tr>
<tr>
<td class="test-name"> change-tagname-ids </td>
<td> <b>0.01ms</b> </td>
<td> 0.02ms </td>
</tr>
<tr>
<td class="test-name"> data-table </td>
<td> <b>0.22ms</b> </td>
<td> 0.65ms </td>
</tr>
<tr>
<td class="test-name"> data-table2 </td>
<td> <b>0.96ms</b> </td>
<td> 2.00ms </td>
</tr>
<tr>
<td class="test-name"> id-change-tag-name </td>
<td> <b>0.00ms</b> </td>
<td> 0.01ms </td>
</tr>
<tr>
<td class="test-name"> ids-nested </td>
<td> <b>0.01ms</b> </td>
<td> 0.01ms </td>
</tr>
<tr>
<td class="test-name"> ids-nested-2 </td>
<td> 0.02ms </td>
<td> <b>0.02ms</b> </td>
</tr>
<tr>
<td class="test-name"> ids-nested-3 </td>
<td> <b>0.01ms</b> </td>
<td> 0.01ms </td>
</tr>
<tr>
<td class="test-name"> ids-nested-4 </td>
<td> <b>0.02ms</b> </td>
<td> 0.02ms </td>
</tr>
<tr>
<td class="test-name"> ids-nested-5 </td>
<td> <b>0.02ms</b> </td>
<td> 0.04ms </td>
</tr>
<tr>
<td class="test-name"> ids-nested-6 </td>
<td> <b>0.01ms</b> </td>
<td> 0.02ms </td>
</tr>
<tr>
<td class="test-name"> ids-nested-7 </td>
<td> 0.02ms </td>
<td> <b>0.01ms</b> </td>
</tr>
<tr>
<td class="test-name"> ids-prepend </td>
<td> <b>0.02ms</b> </td>
<td> 0.02ms </td>
</tr>
<tr>
<td class="test-name"> input-element </td>
<td> 0.01ms </td>
<td> <b>0.01ms</b> </td>
</tr>
<tr>
<td class="test-name"> input-element-disabled </td>
<td> 0.01ms </td>
<td> <b>0.01ms</b> </td>
</tr>
<tr>
<td class="test-name"> input-element-enabled </td>
<td> 0.01ms </td>
<td> <b>0.01ms</b> </td>
</tr>
<tr>
<td class="test-name"> large </td>
<td> 1.56ms </td>
<td> <b>0.98ms</b> </td>
</tr>
<tr>
<td class="test-name"> lengthen </td>
<td> <b>0.02ms</b> </td>
<td> 0.02ms </td>
</tr>
<tr>
<td class="test-name"> one </td>
<td> <b>0.00ms</b> </td>
<td> 0.01ms </td>
</tr>
<tr>
<td class="test-name"> reverse </td>
<td> <b>0.01ms</b> </td>
<td> 0.02ms </td>
</tr>
<tr>
<td class="test-name"> reverse-ids </td>
<td> 0.03ms </td>
<td> <b>0.02ms</b> </td>
</tr>
<tr>
<td class="test-name"> select-element </td>
<td> 0.04ms </td>
<td> <b>0.03ms</b> </td>
</tr>
<tr>
<td class="test-name"> shorten </td>
<td> 0.02ms </td>
<td> <b>0.01ms</b> </td>
</tr>
<tr>
<td class="test-name"> simple </td>
<td> <b>0.01ms</b> </td>
<td> 0.01ms </td>
</tr>
<tr>
<td class="test-name"> simple-ids </td>
<td> 0.04ms </td>
<td> <b>0.03ms</b> </td>
</tr>
<tr>
<td class="test-name"> simple-text-el </td>
<td> <b>0.01ms</b> </td>
<td> 0.02ms </td>
</tr>
<tr>
<td class="test-name"> svg </td>
<td> 0.02ms </td>
<td> <b>0.01ms</b> </td>
</tr>
<tr>
<td class="test-name"> svg-append </td>
<td> 0.04ms </td>
<td> <b>0.04ms</b> </td>
</tr>
<tr>
<td class="test-name"> svg-append-new </td>
<td> <b>0.01ms</b> </td>
<td> 0.04ms </td>
</tr>
<tr>
<td class="test-name"> svg-no-default-namespace </td>
<td> 0.03ms </td>
<td> <b>0.02ms</b> </td>
</tr>
<tr>
<td class="test-name"> svg-xlink </td>
<td> 0.03ms </td>
<td> <b>0.00ms</b> </td>
</tr>
<tr>
<td class="test-name"> tag-to-text </td>
<td> <b>0.00ms</b> </td>
<td> 0.00ms </td>
</tr>
<tr>
<td class="test-name"> text-to-tag </td>
<td> <b>0.00ms</b> </td>
<td> 0.00ms </td>
</tr>
<tr>
<td class="test-name"> text-to-text </td>
<td> <b>0.00ms</b> </td>
<td> 0.00ms </td>
</tr>
<tr>
<td class="test-name"> textarea </td>
<td> 0.01ms </td>
<td> <b>0.01ms</b> </td>
</tr>
<tr>
<td class="test-name"> todomvc </td>
<td> 0.36ms </td>
<td> <b>0.25ms</b> </td>
</tr>
<tr>
<td class="test-name"> two </td>
<td> <b>0.01ms</b> </td>
<td> 0.01ms </td>
</tr>
</tbody>
</table>
</div>
<div class="results"> <ul> <li> Total time for morphdom: 820.02ms </li> <li> Total time for virtual-dom: 333.81ms (winner) </li> <li> Total time for nanomorph: 3,177.85ms </li> </ul> <table> <thead> <tr> <td></td> <td> morphdom </td> <td> nanomorph </td> <td> virtual-dom </td> </tr> </thead> <tbody> <tr> <td class="test-name"> attr-value-empty-string </td> <td> <b> 0.01ms </b> </td> <td> 0.02ms </td> <td> <b> 0.01ms</b> </td> </tr> <tr> <td class="test-name"> change-tagname </td> <td> 0.01ms </td> <td> <b> 0.00ms</b> </td> <td> 0.01ms </td> </tr> <tr> <td class="test-name"> change-tagname-ids </td> <td> 0.02ms </td> <td> <b> 0.00ms</b> </td> <td> 0.02ms </td> </tr> <tr> <td class="test-name"> data-table </td> <td> <b> 0.60ms</b> </td> <td> 1.38ms </td> <td> 0.80ms </td> </tr> <tr> <td class="test-name"> data-table2 </td> <td> 2.72ms </td> <td> 12.42ms </td> <td> <b> 0.84ms</b> </td> </tr> <tr> <td class="test-name"> equal </td> <td> 0.50ms </td> <td> 1.33ms </td> <td> <b> 0.02ms</b> </td> </tr> <tr> <td class="test-name"> id-change-tag-name </td> <td> <b> 0.03ms</b> </td> <td> 0.04ms </td> <td> 0.04ms </td> </tr> <tr> <td class="test-name"> ids-nested </td> <td> 0.08ms </td> <td> 0.02ms </td> <td> <b> 0.01ms</b> </td> </tr> <tr> <td class="test-name"> ids-nested-2 </td> <td> 0.03ms </td> <td> <b> 0.02ms</b> </td> <td> 0.05ms </td> </tr> <tr> <td class="test-name"> ids-nested-3 </td> <td> 0.03ms </td> <td> <b> 0.01ms</b> </td> <td> 0.02ms </td> </tr> <tr> <td class="test-name"> ids-nested-4 </td> <td> 0.04ms </td> <td> 0.04ms </td> <td> <b> 0.03ms</b> </td> </tr> <tr> <td class="test-name"> ids-nested-5 </td> <td> <b> 0.04ms</b> </td> <td> 0.08ms </td> <td> 0.06ms </td> </tr> <tr> <td class="test-name"> ids-nested-6 </td> <td> <b> 0.03ms</b> </td> <td> 0.03ms </td> <td> 0.03ms </td> </tr> <tr> <td class="test-name"> ids-nested-7 </td> <td> <b> 0.02ms</b> </td> <td> 0.03ms </td> <td> 0.02ms </td> </tr> <tr> <td class="test-name"> ids-prepend </td> <td> 0.03ms </td> <td> 0.03ms </td> <td> <b> 0.02ms</b> </td> </tr> <tr> <td class="test-name"> input-element </td> <td> 0.02ms </td> <td> 0.04ms </td> <td> <b> 0.00ms</b> </td> </tr> <tr> <td class="test-name"> input-element-disabled </td> <td> <b> 0.01ms</b> </td> <td> 0.02ms </td> <td> <b> 0.01ms </b> </td> </tr> <tr> <td class="test-name"> input-element-enabled </td> <td> <b> 0.01ms </b> </td> <td> 0.02ms </td> <td> <b> 0.01ms</b> </td> </tr> <tr> <td class="test-name"> large </td> <td> 2.88ms </td> <td> 12.11ms </td> <td> <b> 0.35ms</b> </td> </tr> <tr> <td class="test-name"> lengthen </td> <td> <b> 0.03ms</b> </td> <td> 0.15ms </td> <td> 0.04ms </td> </tr> <tr> <td class="test-name"> one </td> <td> <b> 0.01ms</b> </td> <td> 0.03ms </td> <td> 0.01ms </td> </tr> <tr> <td class="test-name"> reverse </td> <td> 0.03ms </td> <td> 0.05ms </td> <td> <b> 0.02ms</b> </td> </tr> <tr> <td class="test-name"> reverse-ids </td> <td> 0.05ms </td> <td> 0.07ms </td> <td> <b> 0.02ms</b> </td> </tr> <tr> <td class="test-name"> select-element </td> <td> 0.07ms </td> <td> 0.14ms </td> <td> <b> 0.03ms</b> </td> </tr> <tr> <td class="test-name"> shorten </td> <td> 0.03ms </td> <td> 0.07ms </td> <td> <b> 0.02ms</b> </td> </tr> <tr> <td class="test-name"> simple </td> <td> <b> 0.02ms </b> </td> <td> 0.05ms </td> <td> <b> 0.02ms</b> </td> </tr> <tr> <td class="test-name"> simple-ids </td> <td> 0.05ms </td> <td> 0.09ms </td> <td> <b> 0.04ms</b> </td> </tr> <tr> <td class="test-name"> simple-text-el </td> <td> <b> 0.03ms</b> </td> <td> 0.04ms </td> <td> <b> 0.03ms </b> </td> </tr> <tr> <td class="test-name"> svg </td> <td> 0.03ms </td> <td> 0.05ms </td> <td> <b> 0.01ms</b> </td> </tr> <tr> <td class="test-name"> svg-append </td> <td> <b> 0.06ms</b> </td> <td> 0.06ms </td> <td> 0.07ms </td> </tr> <tr> <td class="test-name"> svg-append-new </td> <td> <b> 0.02ms </b> </td> <td> <b> 0.02ms</b> </td> <td> 0.18ms </td> </tr> <tr> <td class="test-name"> svg-no-default-namespace </td> <td> 0.05ms </td> <td> 0.09ms </td> <td> <b> 0.04ms</b> </td> </tr> <tr> <td class="test-name"> svg-xlink </td> <td> 0.01ms </td> <td> 0.05ms </td> <td> <b> 0.00ms</b> </td> </tr> <tr> <td class="test-name"> tag-to-text </td> <td> <b> 0.00ms </b> </td> <td> <b> 0.00ms</b> </td> <td> 0.01ms </td> </tr> <tr> <td class="test-name"> text-to-tag </td> <td> <b> 0.00ms </b> </td> <td> <b> 0.00ms</b> </td> <td> 0.01ms </td> </tr> <tr> <td class="test-name"> text-to-text </td> <td> <b> 0.00ms</b> </td> <td> 0.01ms </td> <td> 0.00ms </td> </tr> <tr> <td class="test-name"> textarea </td> <td> <b> 0.01ms</b> </td> <td> 0.02ms </td> <td> 0.01ms </td> </tr> <tr> <td class="test-name"> todomvc </td> <td> 0.55ms </td> <td> 3.05ms </td> <td> <b> 0.32ms</b> </td> </tr> <tr> <td class="test-name"> todomvc2 </td> <td> <b> 0.05ms</b> </td> <td> 0.07ms </td> <td> 0.09ms </td> </tr> <tr> <td class="test-name"> two </td> <td> <b> 0.01ms </b> </td> <td> 0.02ms </td> <td> <b> 0.01ms</b> </td> </tr> </tbody> </table></div>
_NOTE: Safari Version 9.1.1 (11601.6.17)_
_NOTE: Chrome 72.0.3626.121_

@@ -411,0 +232,0 @@ # Maintainers

@@ -7,2 +7,3 @@ 'use strict';

var ELEMENT_NODE = 1;
var DOCUMENT_FRAGMENT_NODE = 11;
var TEXT_NODE = 3;

@@ -132,3 +133,3 @@ var COMMENT_NODE = 8;

function indexTree(node) {
if (node.nodeType === ELEMENT_NODE) {
if (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE) {
var curChild = node.firstChild;

@@ -163,3 +164,3 @@ while (curChild) {

curChild.parentNode.replaceChild(unmatchedFromEl, curChild);
morphChildren(unmatchedFromEl, curChild);
morphEl(unmatchedFromEl, curChild);
}

@@ -173,5 +174,23 @@ }

function morphChildren(fromEl, toEl, childrenOnly) {
function cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey) {
// 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) {
var fromNextSibling = curFromNodeChild.nextSibling;
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
curFromNodeChild = fromNextSibling;
}
}
function morphEl(fromEl, toEl, childrenOnly) {
var toElKey = getNodeKey(toEl);
var curFromNodeKey;

@@ -203,167 +222,131 @@ if (toElKey) {

}
if (fromEl.nodeName !== 'TEXTAREA') {
var curToNodeChild = toEl.firstChild;
var curFromNodeChild = fromEl.firstChild;
var curToNodeKey;
morphChildren(fromEl, toEl);
} else {
specialElHandlers.TEXTAREA(fromEl, toEl);
}
}
var fromNextSibling;
var toNextSibling;
var matchingFromEl;
function morphChildren(fromEl, toEl) {
var curToNodeChild = toEl.firstChild;
var curFromNodeChild = fromEl.firstChild;
var curToNodeKey;
var curFromNodeKey;
// walk the children
outer: while (curToNodeChild) {
toNextSibling = curToNodeChild.nextSibling;
curToNodeKey = getNodeKey(curToNodeChild);
var fromNextSibling;
var toNextSibling;
var matchingFromEl;
// walk the fromNode children all the way through
while (curFromNodeChild) {
fromNextSibling = curFromNodeChild.nextSibling;
// walk the children
outer: while (curToNodeChild) {
toNextSibling = curToNodeChild.nextSibling;
curToNodeKey = getNodeKey(curToNodeChild);
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
// walk the fromNode children all the way through
while (curFromNodeChild) {
fromNextSibling = curFromNodeChild.nextSibling;
curFromNodeKey = getNodeKey(curFromNodeChild);
if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
var curFromNodeType = curFromNodeChild.nodeType;
curFromNodeKey = getNodeKey(curFromNodeChild);
var isCompatible = undefined;
var curFromNodeType = curFromNodeChild.nodeType;
if (curFromNodeType === curToNodeChild.nodeType) {
if (curFromNodeType === ELEMENT_NODE) {
// Both nodes being compared are Element nodes
// this means if the curFromNodeChild doesnt have a match with the curToNodeChild
var isCompatible = undefined;
if (curToNodeKey) {
// The target node has a key so we want to match it up with the correct element
// in the original DOM tree
if (curToNodeKey !== curFromNodeKey) {
// The current element in the original DOM tree does not have a matching key so
// let's check our lookup to see if there is a matching element in the original
// DOM tree
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
if (fromNextSibling === matchingFromEl) {
// Special case for single element removals. To avoid removing the original
// DOM node out of the tree (since that can break CSS transitions, etc.),
// we will instead discard the current node and wait until the next
// iteration to properly match up the keyed target element with its matching
// element in the original tree
isCompatible = false;
} else {
// We found a matching keyed element somewhere in the original DOM tree.
// Let's move the original DOM node into the current position and morph
// it.
if (curFromNodeType === curToNodeChild.nodeType) {
if (curFromNodeType === ELEMENT_NODE) {
// Both nodes being compared are Element nodes
// NOTE: We use insertBefore instead of replaceChild because we want to go through
// the `removeNode()` function for the node that is being discarded so that
// all lifecycle hooks are correctly invoked
fromEl.insertBefore(matchingFromEl, curFromNodeChild);
if (curToNodeKey) {
// The target node has a key so we want to match it up with the correct element
// in the original DOM tree
if (curToNodeKey !== curFromNodeKey) {
// The current element in the original DOM tree does not have a matching key so
// let's check our lookup to see if there is a matching element in the original
// DOM tree
if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
if (fromNextSibling === matchingFromEl) {
// Special case for single element removals. To avoid removing the original
// DOM node out of the tree (since that can break CSS transitions, etc.),
// we will instead discard the current node and wait until the next
// iteration to properly match up the keyed target element with its matching
// element in the original tree
isCompatible = false;
} else {
// We found a matching keyed element somewhere in the original DOM tree.
// Let's move the original DOM node into the current position and morph
// it.
// fromNextSibling = curFromNodeChild.nextSibling;
// NOTE: We use insertBefore instead of replaceChild because we want to go through
// the `removeNode()` function for the node that is being discarded so that
// all lifecycle hooks are correctly invoked
fromEl.insertBefore(matchingFromEl, curFromNodeChild);
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
// fromNextSibling = curFromNodeChild.nextSibling;
curFromNodeChild = matchingFromEl;
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
} else {
// The nodes are not compatible since the "to" node has a key and there
// is no matching keyed node in the source tree
isCompatible = false;
curFromNodeChild = matchingFromEl;
}
} else {
// The nodes are not compatible since the "to" node has a key and there
// is no matching keyed node in the source tree
isCompatible = false;
}
} else if (curFromNodeKey) {
// The original has a key
isCompatible = false;
}
} else if (curFromNodeKey) {
// The original has a key
isCompatible = false;
}
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
if (isCompatible) {
// We found compatible DOM elements so transform
// the current "from" node to match the current
// target DOM node.
// MORPH
morphChildren(curFromNodeChild, curToNodeChild);
}
isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
if (isCompatible) {
// We found compatible DOM elements so transform
// the current "from" node to match the current
// target DOM node.
// MORPH
morphEl(curFromNodeChild, curToNodeChild);
}
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
// Both nodes being compared are Text or Comment nodes
isCompatible = true;
// Simply update nodeValue on the original node to
// change the text value
if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
}
} else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
// Both nodes being compared are Text or Comment nodes
isCompatible = true;
// Simply update nodeValue on the original node to
// change the text value
if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
}
}
if (isCompatible) {
// Advance both the "to" child and the "from" child since we found a match
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
continue outer;
}
}
// No compatible match so remove the old node from the DOM and continue trying to find a
// match in the original DOM. However, we only do this if the from node is not keyed
// since it is possible that a keyed node might match up with a node somewhere else in the
// target tree and we don't want to discard it just yet since it still might find a
// home in the final DOM tree. After everything is done we will remove any keyed nodes
// that didn't find a home
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer
// the actual removal to later
addKeyedRemoval(curFromNodeKey);
} else {
// NOTE: we skip nested keyed nodes from being removed since there is
// still a chance they will be matched up later
removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
}
if (isCompatible) {
// Advance both the "to" child and the "from" child since we found a match
// Nothing else to do as we already recursively called morphChildren above
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
} // END: while(curFromNodeChild) {}
// 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 (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
fromEl.appendChild(matchingFromEl);
// MORPH
morphChildren(matchingFromEl, curToNodeChild);
} else {
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
if (onBeforeNodeAddedResult !== false) {
if (onBeforeNodeAddedResult) {
curToNodeChild = onBeforeNodeAddedResult;
}
if (curToNodeChild.actualize) {
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
}
fromEl.appendChild(curToNodeChild);
handleNodeAdded(curToNodeChild);
}
continue outer;
}
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
}
// 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;
if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
// No compatible match so remove the old node from the DOM and continue trying to find a
// match in the original DOM. However, we only do this if the from node is not keyed
// since it is possible that a keyed node might match up with a node somewhere else in the
// target tree and we don't want to discard it just yet since it still might find a
// home in the final DOM tree. After everything is done we will remove any keyed nodes
// that didn't find a home
if (curFromNodeKey) {
// Since the node is keyed it might be matched up later so we defer

@@ -377,6 +360,35 @@ // the actual removal to later

}
curFromNodeChild = fromNextSibling;
} // END: while(curFromNodeChild) {}
// 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 (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
fromEl.appendChild(matchingFromEl);
// MORPH
morphEl(matchingFromEl, curToNodeChild);
} else {
var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
if (onBeforeNodeAddedResult !== false) {
if (onBeforeNodeAddedResult) {
curToNodeChild = onBeforeNodeAddedResult;
}
if (curToNodeChild.actualize) {
curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
}
fromEl.appendChild(curToNodeChild);
handleNodeAdded(curToNodeChild);
}
}
curToNodeChild = toNextSibling;
curFromNodeChild = fromNextSibling;
}
cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey);
var specialElHandler = specialElHandlers[fromEl.nodeName];

@@ -424,3 +436,3 @@ if (specialElHandler) {

} else {
morphChildren(morphedNode, toNode, childrenOnly);
morphEl(morphedNode, toNode, childrenOnly);

@@ -427,0 +439,0 @@ // We now need to loop over any keyed nodes that might need to be

@@ -17,2 +17,3 @@ var range; // Create a range object for efficently rendering strings to elements.

range = doc.createRange();
range.selectNode(doc.body);
}

@@ -19,0 +20,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc