lively 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
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";var DOCUMENT_FRAGMENT_NODE=11;function morphAttrs(fromNode,toNode){var toNodeAttrs=toNode.attributes;var attr;var attrName;var attrNamespaceURI;var attrValue;var fromValue;if(toNode.nodeType===DOCUMENT_FRAGMENT_NODE||fromNode.nodeType===DOCUMENT_FRAGMENT_NODE){return}for(var i=toNodeAttrs.length-1;i>=0;i--){attr=toNodeAttrs[i];attrName=attr.name;attrNamespaceURI=attr.namespaceURI;attrValue=attr.value;if(attrNamespaceURI){attrName=attr.localName||attrName;fromValue=fromNode.getAttributeNS(attrNamespaceURI,attrName);if(fromValue!==attrValue){if(attr.prefix==="xmlns"){attrName=attr.name}fromNode.setAttributeNS(attrNamespaceURI,attrName,attrValue)}}else{fromValue=fromNode.getAttribute(attrName);if(fromValue!==attrValue){fromNode.setAttribute(attrName,attrValue)}}}var fromNodeAttrs=fromNode.attributes;for(var d=fromNodeAttrs.length-1;d>=0;d--){attr=fromNodeAttrs[d];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;var HAS_TEMPLATE_SUPPORT=!!doc&&"content"in doc.createElement("template");var HAS_RANGE_SUPPORT=!!doc&&doc.createRange&&"createContextualFragment"in doc.createRange();function createFragmentFromTemplate(str){var template=doc.createElement("template");template.innerHTML=str;return template.content.childNodes[0]}function createFragmentFromRange(str){if(!range){range=doc.createRange();range.selectNode(doc.body)}var fragment=range.createContextualFragment(str);return fragment.childNodes[0]}function createFragmentFromWrap(str){var fragment=doc.createElement("body");fragment.innerHTML=str;return fragment.childNodes[0]}function toElement(str){str=str.trim();if(HAS_TEMPLATE_SUPPORT){return createFragmentFromTemplate(str)}else if(HAS_RANGE_SUPPORT){return createFragmentFromRange(str)}return createFragmentFromWrap(str)}function compareNodeNames(fromEl,toEl){var fromNodeName=fromEl.nodeName;var toNodeName=toEl.nodeName;var fromCodeStart,toCodeStart;if(fromNodeName===toNodeName){return true}fromCodeStart=fromNodeName.charCodeAt(0);toCodeStart=toNodeName.charCodeAt(0);if(fromCodeStart<=90&&toCodeStart>=97){return fromNodeName===toNodeName.toUpperCase()}else if(toCodeStart<=90&&fromCodeStart>=97){return toNodeName===fromNodeName.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$1=11;var TEXT_NODE=3;var COMMENT_NODE=8;function noop(){}function defaultGetNodeKey(node){if(node){return node.getAttribute&&node.getAttribute("id")||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"||fromNode.nodeName==="BODY"){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=Object.create(null);var keyedRemovalList=[];function addKeyedRemoval(key){keyedRemovalList.push(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$1){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)}else{handleNodeAdded(curChild)}}else{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(!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{if(toNode.isSameNode&&toNode.isSameNode(morphedNode)){return}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});
@@ -0,0 +1,757 @@
1
+ 'use strict';
2
+
3
+ var DOCUMENT_FRAGMENT_NODE = 11;
4
+
5
+ function morphAttrs(fromNode, toNode) {
6
+ var toNodeAttrs = toNode.attributes;
7
+ var attr;
8
+ var attrName;
9
+ var attrNamespaceURI;
10
+ var attrValue;
11
+ var fromValue;
12
+
13
+ // document-fragments dont have attributes so lets not do anything
14
+ if (toNode.nodeType === DOCUMENT_FRAGMENT_NODE || fromNode.nodeType === DOCUMENT_FRAGMENT_NODE) {
15
+ return;
16
+ }
17
+
18
+ // update attributes on original DOM element
19
+ for (var i = toNodeAttrs.length - 1; i >= 0; i--) {
20
+ attr = toNodeAttrs[i];
21
+ attrName = attr.name;
22
+ attrNamespaceURI = attr.namespaceURI;
23
+ attrValue = attr.value;
24
+
25
+ if (attrNamespaceURI) {
26
+ attrName = attr.localName || attrName;
27
+ fromValue = fromNode.getAttributeNS(attrNamespaceURI, attrName);
28
+
29
+ if (fromValue !== attrValue) {
30
+ if (attr.prefix === 'xmlns'){
31
+ attrName = attr.name; // It's not allowed to set an attribute with the XMLNS namespace without specifying the `xmlns` prefix
32
+ }
33
+ fromNode.setAttributeNS(attrNamespaceURI, attrName, attrValue);
34
+ }
35
+ } else {
36
+ fromValue = fromNode.getAttribute(attrName);
37
+
38
+ if (fromValue !== attrValue) {
39
+ fromNode.setAttribute(attrName, attrValue);
40
+ }
41
+ }
42
+ }
43
+
44
+ // Remove any extra attributes found on the original DOM element that
45
+ // weren't found on the target element.
46
+ var fromNodeAttrs = fromNode.attributes;
47
+
48
+ for (var d = fromNodeAttrs.length - 1; d >= 0; d--) {
49
+ attr = fromNodeAttrs[d];
50
+ attrName = attr.name;
51
+ attrNamespaceURI = attr.namespaceURI;
52
+
53
+ if (attrNamespaceURI) {
54
+ attrName = attr.localName || attrName;
55
+
56
+ if (!toNode.hasAttributeNS(attrNamespaceURI, attrName)) {
57
+ fromNode.removeAttributeNS(attrNamespaceURI, attrName);
58
+ }
59
+ } else {
60
+ if (!toNode.hasAttribute(attrName)) {
61
+ fromNode.removeAttribute(attrName);
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ var range; // Create a range object for efficently rendering strings to elements.
68
+ var NS_XHTML = 'http://www.w3.org/1999/xhtml';
69
+
70
+ var doc = typeof document === 'undefined' ? undefined : document;
71
+ var HAS_TEMPLATE_SUPPORT = !!doc && 'content' in doc.createElement('template');
72
+ var HAS_RANGE_SUPPORT = !!doc && doc.createRange && 'createContextualFragment' in doc.createRange();
73
+
74
+ function createFragmentFromTemplate(str) {
75
+ var template = doc.createElement('template');
76
+ template.innerHTML = str;
77
+ return template.content.childNodes[0];
78
+ }
79
+
80
+ function createFragmentFromRange(str) {
81
+ if (!range) {
82
+ range = doc.createRange();
83
+ range.selectNode(doc.body);
84
+ }
85
+
86
+ var fragment = range.createContextualFragment(str);
87
+ return fragment.childNodes[0];
88
+ }
89
+
90
+ function createFragmentFromWrap(str) {
91
+ var fragment = doc.createElement('body');
92
+ fragment.innerHTML = str;
93
+ return fragment.childNodes[0];
94
+ }
95
+
96
+ /**
97
+ * This is about the same
98
+ * var html = new DOMParser().parseFromString(str, 'text/html');
99
+ * return html.body.firstChild;
100
+ *
101
+ * @method toElement
102
+ * @param {String} str
103
+ */
104
+ function toElement(str) {
105
+ str = str.trim();
106
+ if (HAS_TEMPLATE_SUPPORT) {
107
+ // avoid restrictions on content for things like `<tr><th>Hi</th></tr>` which
108
+ // createContextualFragment doesn't support
109
+ // <template> support not available in IE
110
+ return createFragmentFromTemplate(str);
111
+ } else if (HAS_RANGE_SUPPORT) {
112
+ return createFragmentFromRange(str);
113
+ }
114
+
115
+ return createFragmentFromWrap(str);
116
+ }
117
+
118
+ /**
119
+ * Returns true if two node's names are the same.
120
+ *
121
+ * NOTE: We don't bother checking `namespaceURI` because you will never find two HTML elements with the same
122
+ * nodeName and different namespace URIs.
123
+ *
124
+ * @param {Element} a
125
+ * @param {Element} b The target element
126
+ * @return {boolean}
127
+ */
128
+ function compareNodeNames(fromEl, toEl) {
129
+ var fromNodeName = fromEl.nodeName;
130
+ var toNodeName = toEl.nodeName;
131
+ var fromCodeStart, toCodeStart;
132
+
133
+ if (fromNodeName === toNodeName) {
134
+ return true;
135
+ }
136
+
137
+ fromCodeStart = fromNodeName.charCodeAt(0);
138
+ toCodeStart = toNodeName.charCodeAt(0);
139
+
140
+ // If the target element is a virtual DOM node or SVG node then we may
141
+ // need to normalize the tag name before comparing. Normal HTML elements that are
142
+ // in the "http://www.w3.org/1999/xhtml"
143
+ // are converted to upper case
144
+ if (fromCodeStart <= 90 && toCodeStart >= 97) { // from is upper and to is lower
145
+ return fromNodeName === toNodeName.toUpperCase();
146
+ } else if (toCodeStart <= 90 && fromCodeStart >= 97) { // to is upper and from is lower
147
+ return toNodeName === fromNodeName.toUpperCase();
148
+ } else {
149
+ return false;
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Create an element, optionally with a known namespace URI.
155
+ *
156
+ * @param {string} name the element name, e.g. 'div' or 'svg'
157
+ * @param {string} [namespaceURI] the element's namespace URI, i.e. the value of
158
+ * its `xmlns` attribute or its inferred namespace.
159
+ *
160
+ * @return {Element}
161
+ */
162
+ function createElementNS(name, namespaceURI) {
163
+ return !namespaceURI || namespaceURI === NS_XHTML ?
164
+ doc.createElement(name) :
165
+ doc.createElementNS(namespaceURI, name);
166
+ }
167
+
168
+ /**
169
+ * Copies the children of one DOM element to another DOM element
170
+ */
171
+ function moveChildren(fromEl, toEl) {
172
+ var curChild = fromEl.firstChild;
173
+ while (curChild) {
174
+ var nextChild = curChild.nextSibling;
175
+ toEl.appendChild(curChild);
176
+ curChild = nextChild;
177
+ }
178
+ return toEl;
179
+ }
180
+
181
+ function syncBooleanAttrProp(fromEl, toEl, name) {
182
+ if (fromEl[name] !== toEl[name]) {
183
+ fromEl[name] = toEl[name];
184
+ if (fromEl[name]) {
185
+ fromEl.setAttribute(name, '');
186
+ } else {
187
+ fromEl.removeAttribute(name);
188
+ }
189
+ }
190
+ }
191
+
192
+ var specialElHandlers = {
193
+ OPTION: function(fromEl, toEl) {
194
+ var parentNode = fromEl.parentNode;
195
+ if (parentNode) {
196
+ var parentName = parentNode.nodeName.toUpperCase();
197
+ if (parentName === 'OPTGROUP') {
198
+ parentNode = parentNode.parentNode;
199
+ parentName = parentNode && parentNode.nodeName.toUpperCase();
200
+ }
201
+ if (parentName === 'SELECT' && !parentNode.hasAttribute('multiple')) {
202
+ if (fromEl.hasAttribute('selected') && !toEl.selected) {
203
+ // Workaround for MS Edge bug where the 'selected' attribute can only be
204
+ // removed if set to a non-empty value:
205
+ // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12087679/
206
+ fromEl.setAttribute('selected', 'selected');
207
+ fromEl.removeAttribute('selected');
208
+ }
209
+ // We have to reset select element's selectedIndex to -1, otherwise setting
210
+ // fromEl.selected using the syncBooleanAttrProp below has no effect.
211
+ // The correct selectedIndex will be set in the SELECT special handler below.
212
+ parentNode.selectedIndex = -1;
213
+ }
214
+ }
215
+ syncBooleanAttrProp(fromEl, toEl, 'selected');
216
+ },
217
+ /**
218
+ * The "value" attribute is special for the <input> element since it sets
219
+ * the initial value. Changing the "value" attribute without changing the
220
+ * "value" property will have no effect since it is only used to the set the
221
+ * initial value. Similar for the "checked" attribute, and "disabled".
222
+ */
223
+ INPUT: function(fromEl, toEl) {
224
+ syncBooleanAttrProp(fromEl, toEl, 'checked');
225
+ syncBooleanAttrProp(fromEl, toEl, 'disabled');
226
+
227
+ if (fromEl.value !== toEl.value) {
228
+ fromEl.value = toEl.value;
229
+ }
230
+
231
+ if (!toEl.hasAttribute('value')) {
232
+ fromEl.removeAttribute('value');
233
+ }
234
+ },
235
+
236
+ TEXTAREA: function(fromEl, toEl) {
237
+ var newValue = toEl.value;
238
+ if (fromEl.value !== newValue) {
239
+ fromEl.value = newValue;
240
+ }
241
+
242
+ var firstChild = fromEl.firstChild;
243
+ if (firstChild) {
244
+ // Needed for IE. Apparently IE sets the placeholder as the
245
+ // node value and vise versa. This ignores an empty update.
246
+ var oldValue = firstChild.nodeValue;
247
+
248
+ if (oldValue == newValue || (!newValue && oldValue == fromEl.placeholder)) {
249
+ return;
250
+ }
251
+
252
+ firstChild.nodeValue = newValue;
253
+ }
254
+ },
255
+ SELECT: function(fromEl, toEl) {
256
+ if (!toEl.hasAttribute('multiple')) {
257
+ var selectedIndex = -1;
258
+ var i = 0;
259
+ // We have to loop through children of fromEl, not toEl since nodes can be moved
260
+ // from toEl to fromEl directly when morphing.
261
+ // At the time this special handler is invoked, all children have already been morphed
262
+ // and appended to / removed from fromEl, so using fromEl here is safe and correct.
263
+ var curChild = fromEl.firstChild;
264
+ var optgroup;
265
+ var nodeName;
266
+ while(curChild) {
267
+ nodeName = curChild.nodeName && curChild.nodeName.toUpperCase();
268
+ if (nodeName === 'OPTGROUP') {
269
+ optgroup = curChild;
270
+ curChild = optgroup.firstChild;
271
+ } else {
272
+ if (nodeName === 'OPTION') {
273
+ if (curChild.hasAttribute('selected')) {
274
+ selectedIndex = i;
275
+ break;
276
+ }
277
+ i++;
278
+ }
279
+ curChild = curChild.nextSibling;
280
+ if (!curChild && optgroup) {
281
+ curChild = optgroup.nextSibling;
282
+ optgroup = null;
283
+ }
284
+ }
285
+ }
286
+
287
+ fromEl.selectedIndex = selectedIndex;
288
+ }
289
+ }
290
+ };
291
+
292
+ var ELEMENT_NODE = 1;
293
+ var DOCUMENT_FRAGMENT_NODE$1 = 11;
294
+ var TEXT_NODE = 3;
295
+ var COMMENT_NODE = 8;
296
+
297
+ function noop() {}
298
+
299
+ function defaultGetNodeKey(node) {
300
+ if (node) {
301
+ return (node.getAttribute && node.getAttribute('id')) || node.id;
302
+ }
303
+ }
304
+
305
+ function morphdomFactory(morphAttrs) {
306
+
307
+ return function morphdom(fromNode, toNode, options) {
308
+ if (!options) {
309
+ options = {};
310
+ }
311
+
312
+ if (typeof toNode === 'string') {
313
+ if (fromNode.nodeName === '#document' || fromNode.nodeName === 'HTML' || fromNode.nodeName === 'BODY') {
314
+ var toNodeHtml = toNode;
315
+ toNode = doc.createElement('html');
316
+ toNode.innerHTML = toNodeHtml;
317
+ } else {
318
+ toNode = toElement(toNode);
319
+ }
320
+ }
321
+
322
+ var getNodeKey = options.getNodeKey || defaultGetNodeKey;
323
+ var onBeforeNodeAdded = options.onBeforeNodeAdded || noop;
324
+ var onNodeAdded = options.onNodeAdded || noop;
325
+ var onBeforeElUpdated = options.onBeforeElUpdated || noop;
326
+ var onElUpdated = options.onElUpdated || noop;
327
+ var onBeforeNodeDiscarded = options.onBeforeNodeDiscarded || noop;
328
+ var onNodeDiscarded = options.onNodeDiscarded || noop;
329
+ var onBeforeElChildrenUpdated = options.onBeforeElChildrenUpdated || noop;
330
+ var childrenOnly = options.childrenOnly === true;
331
+
332
+ // This object is used as a lookup to quickly find all keyed elements in the original DOM tree.
333
+ var fromNodesLookup = Object.create(null);
334
+ var keyedRemovalList = [];
335
+
336
+ function addKeyedRemoval(key) {
337
+ keyedRemovalList.push(key);
338
+ }
339
+
340
+ function walkDiscardedChildNodes(node, skipKeyedNodes) {
341
+ if (node.nodeType === ELEMENT_NODE) {
342
+ var curChild = node.firstChild;
343
+ while (curChild) {
344
+
345
+ var key = undefined;
346
+
347
+ if (skipKeyedNodes && (key = getNodeKey(curChild))) {
348
+ // If we are skipping keyed nodes then we add the key
349
+ // to a list so that it can be handled at the very end.
350
+ addKeyedRemoval(key);
351
+ } else {
352
+ // Only report the node as discarded if it is not keyed. We do this because
353
+ // at the end we loop through all keyed elements that were unmatched
354
+ // and then discard them in one final pass.
355
+ onNodeDiscarded(curChild);
356
+ if (curChild.firstChild) {
357
+ walkDiscardedChildNodes(curChild, skipKeyedNodes);
358
+ }
359
+ }
360
+
361
+ curChild = curChild.nextSibling;
362
+ }
363
+ }
364
+ }
365
+
366
+ /**
367
+ * Removes a DOM node out of the original DOM
368
+ *
369
+ * @param {Node} node The node to remove
370
+ * @param {Node} parentNode The nodes parent
371
+ * @param {Boolean} skipKeyedNodes If true then elements with keys will be skipped and not discarded.
372
+ * @return {undefined}
373
+ */
374
+ function removeNode(node, parentNode, skipKeyedNodes) {
375
+ if (onBeforeNodeDiscarded(node) === false) {
376
+ return;
377
+ }
378
+
379
+ if (parentNode) {
380
+ parentNode.removeChild(node);
381
+ }
382
+
383
+ onNodeDiscarded(node);
384
+ walkDiscardedChildNodes(node, skipKeyedNodes);
385
+ }
386
+
387
+ // // TreeWalker implementation is no faster, but keeping this around in case this changes in the future
388
+ // function indexTree(root) {
389
+ // var treeWalker = document.createTreeWalker(
390
+ // root,
391
+ // NodeFilter.SHOW_ELEMENT);
392
+ //
393
+ // var el;
394
+ // while((el = treeWalker.nextNode())) {
395
+ // var key = getNodeKey(el);
396
+ // if (key) {
397
+ // fromNodesLookup[key] = el;
398
+ // }
399
+ // }
400
+ // }
401
+
402
+ // // NodeIterator implementation is no faster, but keeping this around in case this changes in the future
403
+ //
404
+ // function indexTree(node) {
405
+ // var nodeIterator = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT);
406
+ // var el;
407
+ // while((el = nodeIterator.nextNode())) {
408
+ // var key = getNodeKey(el);
409
+ // if (key) {
410
+ // fromNodesLookup[key] = el;
411
+ // }
412
+ // }
413
+ // }
414
+
415
+ function indexTree(node) {
416
+ if (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE$1) {
417
+ var curChild = node.firstChild;
418
+ while (curChild) {
419
+ var key = getNodeKey(curChild);
420
+ if (key) {
421
+ fromNodesLookup[key] = curChild;
422
+ }
423
+
424
+ // Walk recursively
425
+ indexTree(curChild);
426
+
427
+ curChild = curChild.nextSibling;
428
+ }
429
+ }
430
+ }
431
+
432
+ indexTree(fromNode);
433
+
434
+ function handleNodeAdded(el) {
435
+ onNodeAdded(el);
436
+
437
+ var curChild = el.firstChild;
438
+ while (curChild) {
439
+ var nextSibling = curChild.nextSibling;
440
+
441
+ var key = getNodeKey(curChild);
442
+ if (key) {
443
+ var unmatchedFromEl = fromNodesLookup[key];
444
+ // if we find a duplicate #id node in cache, replace `el` with cache value
445
+ // and morph it to the child node.
446
+ if (unmatchedFromEl && compareNodeNames(curChild, unmatchedFromEl)) {
447
+ curChild.parentNode.replaceChild(unmatchedFromEl, curChild);
448
+ morphEl(unmatchedFromEl, curChild);
449
+ } else {
450
+ handleNodeAdded(curChild);
451
+ }
452
+ } else {
453
+ // recursively call for curChild and it's children to see if we find something in
454
+ // fromNodesLookup
455
+ handleNodeAdded(curChild);
456
+ }
457
+
458
+ curChild = nextSibling;
459
+ }
460
+ }
461
+
462
+ function cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey) {
463
+ // We have processed all of the "to nodes". If curFromNodeChild is
464
+ // non-null then we still have some from nodes left over that need
465
+ // to be removed
466
+ while (curFromNodeChild) {
467
+ var fromNextSibling = curFromNodeChild.nextSibling;
468
+ if ((curFromNodeKey = getNodeKey(curFromNodeChild))) {
469
+ // Since the node is keyed it might be matched up later so we defer
470
+ // the actual removal to later
471
+ addKeyedRemoval(curFromNodeKey);
472
+ } else {
473
+ // NOTE: we skip nested keyed nodes from being removed since there is
474
+ // still a chance they will be matched up later
475
+ removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
476
+ }
477
+ curFromNodeChild = fromNextSibling;
478
+ }
479
+ }
480
+
481
+ function morphEl(fromEl, toEl, childrenOnly) {
482
+ var toElKey = getNodeKey(toEl);
483
+
484
+ if (toElKey) {
485
+ // If an element with an ID is being morphed then it will be in the final
486
+ // DOM so clear it out of the saved elements collection
487
+ delete fromNodesLookup[toElKey];
488
+ }
489
+
490
+ if (!childrenOnly) {
491
+ // optional
492
+ if (onBeforeElUpdated(fromEl, toEl) === false) {
493
+ return;
494
+ }
495
+
496
+ // update attributes on original DOM element first
497
+ morphAttrs(fromEl, toEl);
498
+ // optional
499
+ onElUpdated(fromEl);
500
+
501
+ if (onBeforeElChildrenUpdated(fromEl, toEl) === false) {
502
+ return;
503
+ }
504
+ }
505
+
506
+ if (fromEl.nodeName !== 'TEXTAREA') {
507
+ morphChildren(fromEl, toEl);
508
+ } else {
509
+ specialElHandlers.TEXTAREA(fromEl, toEl);
510
+ }
511
+ }
512
+
513
+ function morphChildren(fromEl, toEl) {
514
+ var curToNodeChild = toEl.firstChild;
515
+ var curFromNodeChild = fromEl.firstChild;
516
+ var curToNodeKey;
517
+ var curFromNodeKey;
518
+
519
+ var fromNextSibling;
520
+ var toNextSibling;
521
+ var matchingFromEl;
522
+
523
+ // walk the children
524
+ outer: while (curToNodeChild) {
525
+ toNextSibling = curToNodeChild.nextSibling;
526
+ curToNodeKey = getNodeKey(curToNodeChild);
527
+
528
+ // walk the fromNode children all the way through
529
+ while (curFromNodeChild) {
530
+ fromNextSibling = curFromNodeChild.nextSibling;
531
+
532
+ if (curToNodeChild.isSameNode && curToNodeChild.isSameNode(curFromNodeChild)) {
533
+ curToNodeChild = toNextSibling;
534
+ curFromNodeChild = fromNextSibling;
535
+ continue outer;
536
+ }
537
+
538
+ curFromNodeKey = getNodeKey(curFromNodeChild);
539
+
540
+ var curFromNodeType = curFromNodeChild.nodeType;
541
+
542
+ // this means if the curFromNodeChild doesnt have a match with the curToNodeChild
543
+ var isCompatible = undefined;
544
+
545
+ if (curFromNodeType === curToNodeChild.nodeType) {
546
+ if (curFromNodeType === ELEMENT_NODE) {
547
+ // Both nodes being compared are Element nodes
548
+
549
+ if (curToNodeKey) {
550
+ // The target node has a key so we want to match it up with the correct element
551
+ // in the original DOM tree
552
+ if (curToNodeKey !== curFromNodeKey) {
553
+ // The current element in the original DOM tree does not have a matching key so
554
+ // let's check our lookup to see if there is a matching element in the original
555
+ // DOM tree
556
+ if ((matchingFromEl = fromNodesLookup[curToNodeKey])) {
557
+ if (fromNextSibling === matchingFromEl) {
558
+ // Special case for single element removals. To avoid removing the original
559
+ // DOM node out of the tree (since that can break CSS transitions, etc.),
560
+ // we will instead discard the current node and wait until the next
561
+ // iteration to properly match up the keyed target element with its matching
562
+ // element in the original tree
563
+ isCompatible = false;
564
+ } else {
565
+ // We found a matching keyed element somewhere in the original DOM tree.
566
+ // Let's move the original DOM node into the current position and morph
567
+ // it.
568
+
569
+ // NOTE: We use insertBefore instead of replaceChild because we want to go through
570
+ // the `removeNode()` function for the node that is being discarded so that
571
+ // all lifecycle hooks are correctly invoked
572
+ fromEl.insertBefore(matchingFromEl, curFromNodeChild);
573
+
574
+ // fromNextSibling = curFromNodeChild.nextSibling;
575
+
576
+ if (curFromNodeKey) {
577
+ // Since the node is keyed it might be matched up later so we defer
578
+ // the actual removal to later
579
+ addKeyedRemoval(curFromNodeKey);
580
+ } else {
581
+ // NOTE: we skip nested keyed nodes from being removed since there is
582
+ // still a chance they will be matched up later
583
+ removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
584
+ }
585
+
586
+ curFromNodeChild = matchingFromEl;
587
+ }
588
+ } else {
589
+ // The nodes are not compatible since the "to" node has a key and there
590
+ // is no matching keyed node in the source tree
591
+ isCompatible = false;
592
+ }
593
+ }
594
+ } else if (curFromNodeKey) {
595
+ // The original has a key
596
+ isCompatible = false;
597
+ }
598
+
599
+ isCompatible = isCompatible !== false && compareNodeNames(curFromNodeChild, curToNodeChild);
600
+ if (isCompatible) {
601
+ // We found compatible DOM elements so transform
602
+ // the current "from" node to match the current
603
+ // target DOM node.
604
+ // MORPH
605
+ morphEl(curFromNodeChild, curToNodeChild);
606
+ }
607
+
608
+ } else if (curFromNodeType === TEXT_NODE || curFromNodeType == COMMENT_NODE) {
609
+ // Both nodes being compared are Text or Comment nodes
610
+ isCompatible = true;
611
+ // Simply update nodeValue on the original node to
612
+ // change the text value
613
+ if (curFromNodeChild.nodeValue !== curToNodeChild.nodeValue) {
614
+ curFromNodeChild.nodeValue = curToNodeChild.nodeValue;
615
+ }
616
+
617
+ }
618
+ }
619
+
620
+ if (isCompatible) {
621
+ // Advance both the "to" child and the "from" child since we found a match
622
+ // Nothing else to do as we already recursively called morphChildren above
623
+ curToNodeChild = toNextSibling;
624
+ curFromNodeChild = fromNextSibling;
625
+ continue outer;
626
+ }
627
+
628
+ // No compatible match so remove the old node from the DOM and continue trying to find a
629
+ // match in the original DOM. However, we only do this if the from node is not keyed
630
+ // since it is possible that a keyed node might match up with a node somewhere else in the
631
+ // target tree and we don't want to discard it just yet since it still might find a
632
+ // home in the final DOM tree. After everything is done we will remove any keyed nodes
633
+ // that didn't find a home
634
+ if (curFromNodeKey) {
635
+ // Since the node is keyed it might be matched up later so we defer
636
+ // the actual removal to later
637
+ addKeyedRemoval(curFromNodeKey);
638
+ } else {
639
+ // NOTE: we skip nested keyed nodes from being removed since there is
640
+ // still a chance they will be matched up later
641
+ removeNode(curFromNodeChild, fromEl, true /* skip keyed nodes */);
642
+ }
643
+
644
+ curFromNodeChild = fromNextSibling;
645
+ } // END: while(curFromNodeChild) {}
646
+
647
+ // If we got this far then we did not find a candidate match for
648
+ // our "to node" and we exhausted all of the children "from"
649
+ // nodes. Therefore, we will just append the current "to" node
650
+ // to the end
651
+ if (curToNodeKey && (matchingFromEl = fromNodesLookup[curToNodeKey]) && compareNodeNames(matchingFromEl, curToNodeChild)) {
652
+ fromEl.appendChild(matchingFromEl);
653
+ // MORPH
654
+ morphEl(matchingFromEl, curToNodeChild);
655
+ } else {
656
+ var onBeforeNodeAddedResult = onBeforeNodeAdded(curToNodeChild);
657
+ if (onBeforeNodeAddedResult !== false) {
658
+ if (onBeforeNodeAddedResult) {
659
+ curToNodeChild = onBeforeNodeAddedResult;
660
+ }
661
+
662
+ if (curToNodeChild.actualize) {
663
+ curToNodeChild = curToNodeChild.actualize(fromEl.ownerDocument || doc);
664
+ }
665
+ fromEl.appendChild(curToNodeChild);
666
+ handleNodeAdded(curToNodeChild);
667
+ }
668
+ }
669
+
670
+ curToNodeChild = toNextSibling;
671
+ curFromNodeChild = fromNextSibling;
672
+ }
673
+
674
+ cleanupFromEl(fromEl, curFromNodeChild, curFromNodeKey);
675
+
676
+ var specialElHandler = specialElHandlers[fromEl.nodeName];
677
+ if (specialElHandler) {
678
+ specialElHandler(fromEl, toEl);
679
+ }
680
+ } // END: morphChildren(...)
681
+
682
+ var morphedNode = fromNode;
683
+ var morphedNodeType = morphedNode.nodeType;
684
+ var toNodeType = toNode.nodeType;
685
+
686
+ if (!childrenOnly) {
687
+ // Handle the case where we are given two DOM nodes that are not
688
+ // compatible (e.g. <div> --> <span> or <div> --> TEXT)
689
+ if (morphedNodeType === ELEMENT_NODE) {
690
+ if (toNodeType === ELEMENT_NODE) {
691
+ if (!compareNodeNames(fromNode, toNode)) {
692
+ onNodeDiscarded(fromNode);
693
+ morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI));
694
+ }
695
+ } else {
696
+ // Going from an element node to a text node
697
+ morphedNode = toNode;
698
+ }
699
+ } else if (morphedNodeType === TEXT_NODE || morphedNodeType === COMMENT_NODE) { // Text or comment node
700
+ if (toNodeType === morphedNodeType) {
701
+ if (morphedNode.nodeValue !== toNode.nodeValue) {
702
+ morphedNode.nodeValue = toNode.nodeValue;
703
+ }
704
+
705
+ return morphedNode;
706
+ } else {
707
+ // Text node to something else
708
+ morphedNode = toNode;
709
+ }
710
+ }
711
+ }
712
+
713
+ if (morphedNode === toNode) {
714
+ // The "to node" was not compatible with the "from node" so we had to
715
+ // toss out the "from node" and use the "to node"
716
+ onNodeDiscarded(fromNode);
717
+ } else {
718
+ if (toNode.isSameNode && toNode.isSameNode(morphedNode)) {
719
+ return;
720
+ }
721
+
722
+ morphEl(morphedNode, toNode, childrenOnly);
723
+
724
+ // We now need to loop over any keyed nodes that might need to be
725
+ // removed. We only do the removal if we know that the keyed node
726
+ // never found a match. When a keyed node is matched up we remove
727
+ // it out of fromNodesLookup and we use fromNodesLookup to determine
728
+ // if a keyed node has been matched up or not
729
+ if (keyedRemovalList) {
730
+ for (var i=0, len=keyedRemovalList.length; i<len; i++) {
731
+ var elToRemove = fromNodesLookup[keyedRemovalList[i]];
732
+ if (elToRemove) {
733
+ removeNode(elToRemove, elToRemove.parentNode, false);
734
+ }
735
+ }
736
+ }
737
+ }
738
+
739
+ if (!childrenOnly && morphedNode !== fromNode && fromNode.parentNode) {
740
+ if (morphedNode.actualize) {
741
+ morphedNode = morphedNode.actualize(fromNode.ownerDocument || doc);
742
+ }
743
+ // If we had to swap out the from node with a new node because the old
744
+ // node was not compatible with the target node then we need to
745
+ // replace the old DOM node in the original DOM tree. This is only
746
+ // possible if the original DOM node was part of a DOM tree which
747
+ // we know is the case if it has a parent node.
748
+ fromNode.parentNode.replaceChild(morphedNode, fromNode);
749
+ }
750
+
751
+ return morphedNode;
752
+ };
753
+ }
754
+
755
+ var morphdom = morphdomFactory(morphAttrs);
756
+
757
+ module.exports = morphdom;