Socket
Socket
Sign inDemoInstall

morphdom

Package Overview
Dependencies
0
Maintainers
1
Versions
70
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.2 to 0.1.3

test/fixtures/autotest/ids-nested-2/from.html

215

lib/index.js

@@ -12,3 +12,3 @@ var specialAttrHandlers = {

INPUT$checked: function(el, value) {
el.checked = value == null ? false : true;
el.checked = (value == null) ? false : true;
}

@@ -63,10 +63,2 @@ };

function saveEl(morpher, el) {
morpher.saved[el.id] = el;
}
function morphEl(morpher, fromNode, toNode) {
morpher.tasks.push(new MorphElTask(fromNode, toNode));
}
function moveChildren(from, to) {

@@ -82,29 +74,43 @@ var curChild = from.firstChild;

function removeNode(morpher, node, parentNode) {
if (morpher.onBeforeRemoveNode(node) !== false) {
parentNode.removeChild(node);
function morphdom(fromNode, toNode, options) {
if (!options) {
options = {};
}
var savedEls = {}; // Used to save off DOM elements with IDs
var unmatchedEls = {};
var onFromNodeFound = options.onFromNodeFound || noop;
var onBeforeMorphEl = options.onBeforeMorphEl || noop;
var onBeforeMorphElChildren = options.onBeforeMorphElChildren || noop;
function removeNodeHelper(node) {
var id = node.id;
// If the node has an ID then save it off since we will want
// to reuse it in case the target DOM tree has a DOM element
// with the same ID
if (node.id) {
saveEl(morpher, node);
} else {
morpher.onFromNodeRemoved(node);
if (id) {
savedEls[id] = node;
}
if (node.nodeType === 1) {
var curChild = node.firstChild;
while(curChild) {
onFromNodeFound(curChild);
removeNodeHelper(curChild);
curChild = curChild.nextSibling;
}
}
}
}
function MorphElTask(fromNode, toNode) {
this.from = fromNode;
this.to = toNode;
this.node = null; // The morphed node
}
function removeNode(node, parentNode, alreadyVisited) {
parentNode.removeChild(node);
MorphElTask.prototype = {
run: function(morpher) {
var fromNode = this.from;
var toNode = this.to;
if (!alreadyVisited) {
removeNodeHelper(node);
}
}
if (morpher.onBeforeMorphEl(fromNode, toNode) === false) {
function morphEl(fromNode, toNode, alreadyVisited) {
if (onBeforeMorphEl(fromNode, toNode) === false) {
return;

@@ -115,3 +121,3 @@ }

if (morpher.onBeforeMorphElChildren(fromNode, toNode) === false) {
if (onBeforeMorphElChildren(fromNode, toNode) === false) {
return;

@@ -127,2 +133,3 @@ }

var savedEl;
var unmatchedEl;

@@ -134,9 +141,7 @@ outer: while(curToNodeChild) {

if (curToNodeId && (savedEl = morpher.saved[curToNodeId])) {
delete morpher.saved[curToNodeId]; // Do some cleanup since we need to know which nodes were actually
// completely removed from the DOM
if (curToNodeId && (savedEl = savedEls[curToNodeId])) {
// We are reusing a "from node" with an ID that matches
// the ID of the current "to node"
fromNode.insertBefore(savedEl, curFromNodeChild);
morphEl(morpher, savedEl, curToNodeChild);
morphEl(savedEl, curToNodeChild, true);
curToNodeChild = toNextSibling;

@@ -147,5 +152,16 @@ continue;

while(curFromNodeChild) {
morpher.onFromNodeFound(curFromNodeChild);
var curFromNodeId = curFromNodeChild.id;
fromNextSibling = curFromNodeChild.nextSibling;
fromNextSibling = curFromNodeChild.nextSibling;
if (!alreadyVisited) {
onFromNodeFound(curFromNodeChild);
if (curFromNodeId && (unmatchedEl = unmatchedEls[curFromNodeId])) {
unmatchedEl.parentNode.replaceChild(curFromNodeChild, unmatchedEl);
morphEl(curFromNodeChild, unmatchedEl, alreadyVisited);
curFromNodeChild = fromNextSibling;
continue;
}
}
var curFromNodeType = curFromNodeChild.nodeType;

@@ -159,7 +175,7 @@

// We have compatible DOM elements
if (curFromNodeChild.id || curToNodeId) {
if (curFromNodeId || curToNodeId) {
// If either DOM element has an ID then we handle
// those differently since we want to match up
// by ID
if (curToNodeId === curFromNodeChild.id) {
if (curToNodeId === curFromNodeId) {
isCompatible = true;

@@ -175,3 +191,3 @@ }

// task to morph the compatible DOM elements
morphEl(morpher, curFromNodeChild, curToNodeChild);
morphEl(curFromNodeChild, curToNodeChild, alreadyVisited);
}

@@ -191,3 +207,3 @@ } else if (curFromNodeType === 3) { // Both nodes being compared are Text nodes

// No compatible match so remove the old node from the DOM
removeNode(morpher, curFromNodeChild, fromNode);
removeNode(curFromNodeChild, fromNode, alreadyVisited);

@@ -197,2 +213,17 @@ curFromNodeChild = fromNextSibling;

if (curToNodeId) {
if ((savedEl = savedEls[curToNodeId])) {
morphEl(savedEl, curToNodeChild, true);
curToNodeChild = savedEl; // We want to append the saved element instead
} else {
// The current DOM element in the target tree has an ID
// but we did not find a match in any of the corresponding
// siblings. We just put the target element in the old DOM tree
// but if we later find an element in the old DOM tree that has
// a matching ID then we will replace the target element
// with the corresponding old element and morph the old element
unmatchedEls[curToNodeId] = curToNodeChild;
}
}
// If we got this far then we did not find a candidate match for our "to node"

@@ -210,88 +241,58 @@ // and we exhausted all of the children "from" nodes. Therefore, we will just

while(curFromNodeChild) {
morpher.onFromNodeFound(curFromNodeChild);
if (!alreadyVisited) {
onFromNodeFound(curFromNodeChild);
}
fromNextSibling = curFromNodeChild.nextSibling;
removeNode(morpher, curFromNodeChild, fromNode);
removeNode(curFromNodeChild, fromNode, alreadyVisited);
curFromNodeChild = fromNextSibling;
}
}
};
function Morpher(options) {
if (!options) {
options = {};
}
var morphedNode = fromNode;
var morphedNodeType = morphedNode.nodeType;
var toNodeType = toNode.nodeType;
// NOTE: We use a task stack to handle DOM trees of any size by
// avoiding recursion
this.tasks = [];
// Invoke the callback for the top-level "from node". We'll handle all
// of the nested nodes in the code that does the morph element task
onFromNodeFound(fromNode);
this.saved = {}; // Used to save off DOM elements with IDs
this.onFromNodeFound = options.onFromNodeFound || noop;
this.onFromNodeRemoved = options.onFromNodeRemoved || noop;
this.onBeforeMorphEl = options.onBeforeMorphEl || noop;
this.onBeforeMorphElChildren = options.onBeforeMorphElChildren || noop;
this.onBeforeRemoveNode = options.onBeforeRemoveNode || noop;
}
Morpher.prototype = {
morph: function(fromNode, toNode) {
var tasks = this.tasks;
var morphedNode = fromNode;
var morphedNodeType = morphedNode.nodeType;
var toNodeType = toNode.nodeType;
// Invoke the callback for the top-level "from node". We'll handle all
// of the nested nodes in the code that does the morph element task
this.onFromNodeFound(fromNode);
// Handle the case where we are given two DOM nodes that are not
// compatible (e.g. <div> --> <span> or <div> --> TEXT)
if (morphedNodeType === 1) {
if (toNodeType === 1) {
if (morphedNode.tagName !== toNode.tagName) {
morphedNode = moveChildren(morphedNode, document.createElement(toNode.tagName));
}
} else {
// Going from an element node to a text node
return toNode;
// Handle the case where we are given two DOM nodes that are not
// compatible (e.g. <div> --> <span> or <div> --> TEXT)
if (morphedNodeType === 1) {
if (toNodeType === 1) {
if (morphedNode.tagName !== toNode.tagName) {
morphedNode = moveChildren(morphedNode, document.createElement(toNode.tagName));
}
} else if (morphedNodeType === 3) { // Text node
if (toNodeType === 3) {
morphedNode.nodeValue = toNode.nodeValue;
return morphedNode;
} else {
// Text node to something else
return toNode;
}
} else {
// Going from an element node to a text node
return toNode;
}
morphEl(this, morphedNode, toNode);
// Keep going until there is no more scheduled work
while(tasks.length) {
tasks.pop().run(this);
} else if (morphedNodeType === 3) { // Text node
if (toNodeType === 3) {
morphedNode.nodeValue = toNode.nodeValue;
return morphedNode;
} else {
// Text node to something else
return toNode;
}
}
// Fire the "onFromNodeRemoved" event for any saved elements
// that never found a new home in the morphed DOM
var savedEls = this.saved;
for (var savedElId in savedEls) {
if (savedEls.hasOwnProperty(savedElId)) {
this.onFromNodeRemoved(savedEls[savedElId]);
}
}
morphEl(morphedNode, toNode);
if (morphedNode !== fromNode && fromNode.parentNode) {
fromNode.parentNode.replaceChild(morphedNode, fromNode);
}
// Fire the "onFromNodeRemoved" event for any saved elements
// that never found a new home in the morphed DOM
// for (var savedElId in savedEls) {
// if (savedEls.hasOwnProperty(savedElId)) {
// onFromNodeRemoved(savedEls[savedElId]);
// }
// }
return morphedNode;
if (morphedNode !== fromNode && fromNode.parentNode) {
fromNode.parentNode.replaceChild(morphedNode, fromNode);
}
};
function morphdom(oldNode, newNode, options) {
var morpher = new Morpher(options);
return morpher.morph(oldNode, newNode);
return morphedNode;
}
module.exports = morphdom;

@@ -29,3 +29,3 @@ {

"dependencies": {},
"version": "0.1.2"
"version": "0.1.3"
}

@@ -103,17 +103,19 @@ var chai = require('chai');

function markDescendentsRemoved(node) {
var curNode = node.firstChild;
while(curNode) {
if (curNode.$testOnFromNodeFlag) {
throw new Error('Descendent of removed node was incorrectly visited. Node: ' + curNode);
}
function isNodeInTree(node, rootNode) {
if (node == null) {
throw new Error('Invalid arguments');
}
var currentNode = node;
curNode.$testRemovedDescendentFlag = true;
if (curNode.nodeType === 1) {
markDescendentsRemoved(curNode);
while (true) {
if (currentNode == null) {
return false;
} else if (currentNode == rootNode) {
return true;
}
curNode = curNode.nextSibling;
currentNode = currentNode.parentNode;
}
return false;
}

@@ -144,19 +146,6 @@

node.$testOnFromNodeFlag = true;
}
function onFromNodeRemoved(node) {
if (node.$testOnFromNodeRemovedFlag) {
throw new Error('Duplicate onFromNodeRemoved for: ' + node);
}
node.$testOnFromNodeRemovedFlag = true;
markDescendentsRemoved(node);
}
var morphedNode = morphdom(fromNode, toNode, {
onFromNodeFound: onFromNodeFound,
onFromNodeRemoved: onFromNodeRemoved
onFromNodeFound: onFromNodeFound
});

@@ -194,9 +183,15 @@

allFromNodes.forEach(function(node) {
if (node.$testOnFromNodeFlag && node.$testRemovedDescendentFlag) {
throw new Error('Descendent of a removed "from" node was visited. Node: ' + node);
if (!node.$testOnFromNodeFlag) {
throw new Error('"from" node was not reported as visited. Node: ' + node);
}
if (!node.$testOnFromNodeFlag && !node.$testRemovedDescendentFlag) {
throw new Error('"from" node not found during morph ' + node);
}
// if (isNodeInTree(node, morphedNode)) {
// if (node.$testOnFromNodeRemovedFlag) {
// throw new Error('onFromNodeRemoved(node) called for node that is in the final DOM tree: ' + node);
// }
// } else {
// if (!node.$testOnFromNodeRemovedFlag) {
// throw new Error('"from" node was removed but onFromNodeRemoved(node) was not called: ' + node);
// }
// }
});

@@ -203,0 +198,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc