vendor/assets/javascripts/cable_ready.js in cable_ready-2.0.1 vs vendor/assets/javascripts/cable_ready.js in cable_ready-2.0.2
- old
+ new
@@ -1,108 +1,878 @@
-(function () {
- "use strict";
+var CableReady =
+/******/ (function(modules) { // webpackBootstrap
+/******/ // The module cache
+/******/ var installedModules = {};
+/******/
+/******/ // The require function
+/******/ function __webpack_require__(moduleId) {
+/******/
+/******/ // Check if module is in cache
+/******/ if(installedModules[moduleId]) {
+/******/ return installedModules[moduleId].exports;
+/******/ }
+/******/ // Create a new module (and put it into the cache)
+/******/ var module = installedModules[moduleId] = {
+/******/ i: moduleId,
+/******/ l: false,
+/******/ exports: {}
+/******/ };
+/******/
+/******/ // Execute the module function
+/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ // Flag the module as loaded
+/******/ module.l = true;
+/******/
+/******/ // Return the exports of the module
+/******/ return module.exports;
+/******/ }
+/******/
+/******/
+/******/ // expose the modules object (__webpack_modules__)
+/******/ __webpack_require__.m = modules;
+/******/
+/******/ // expose the module cache
+/******/ __webpack_require__.c = installedModules;
+/******/
+/******/ // define getter function for harmony exports
+/******/ __webpack_require__.d = function(exports, name, getter) {
+/******/ if(!__webpack_require__.o(exports, name)) {
+/******/ Object.defineProperty(exports, name, {
+/******/ configurable: false,
+/******/ enumerable: true,
+/******/ get: getter
+/******/ });
+/******/ }
+/******/ };
+/******/
+/******/ // getDefaultExport function for compatibility with non-harmony modules
+/******/ __webpack_require__.n = function(module) {
+/******/ var getter = module && module.__esModule ?
+/******/ function getDefault() { return module['default']; } :
+/******/ function getModuleExports() { return module; };
+/******/ __webpack_require__.d(getter, 'a', getter);
+/******/ return getter;
+/******/ };
+/******/
+/******/ // Object.prototype.hasOwnProperty.call
+/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ // __webpack_public_path__
+/******/ __webpack_require__.p = "";
+/******/
+/******/ // Load entry module and return exports
+/******/ return __webpack_require__(__webpack_require__.s = 0);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
- var CableReadyOperations = {
- // DOM Events .....................................................................................................
+"use strict";
- dispatchEvent: function (config) {
- if (CableReady.debug) { console.log("CableReady.dispatchEvent", config); }
- var target = document.querySelector(config.selector) || window;
- var event = new Event(config.name);
- event.detail = config.detail;
- target.dispatchEvent(event);
- },
- // Element Mutations ..............................................................................................
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.perform = undefined;
- innerHtml: function (config) {
- if (CableReady.debug) { console.log("CableReady.innerHTML", config); }
- document.querySelector(config.selector).innerHTML = config.html;
- if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
- },
+var _morphdom = __webpack_require__(1);
- textContent: function (config) {
- if (CableReady.debug) { console.log("CableReady.textContent", config); }
- document.querySelector(config.selector).textContent = config.text;
- },
+var _morphdom2 = _interopRequireDefault(_morphdom);
- insertAdjacentHtml: function (config) {
- if (CableReady.debug) { console.log("CableReady.insertAdjacentHTML", config); }
- document.querySelector(config.selector).insertAdjacentHTML(config.position || "beforeend", config.html);
- if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
- },
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
- insertAdjacentText: function (config) {
- if (CableReady.debug) { console.log("CableReady.insertAdjacentText", config); }
- document.querySelector(config.querySelector).insertAdjacentText(config.position || "beforeend", config.text);
- },
+var DOMOperations = {
+ // DOM Events ..............................................................................................
- remove: function (config) {
- if (CableReady.debug) { console.log("CableReady.remove", config); }
- document.querySelector(config.selector).remove();
- if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
- },
+ dispatchEvent: function dispatchEvent(config) {
+ var target = document.querySelector(config.selector) || window;
+ var event = new Event(config.name);
+ event.detail = config.detail;
+ target.dispatchEvent(event);
+ },
- replace: function (config) {
- if (CableReady.debug) { console.log("CableReady.replace", config); }
- var element = document.querySelector(config.selector);
- var div = document.createElement("div");
- div.innerHTML = config.html;
- element.parentNode.replaceChild(div.firstElementChild, element);
- if (config.focusSelector) { document.querySelector(config.focusSelector).focus(); }
- },
+ // Element Mutations .......................................................................................
- setValue: function (config) {
- if (CableReady.debug) { console.log("CableReady.setValue", config); }
- document.querySelector(config.selector).value = config.value;
- },
+ morph: function morph(config) {
+ (0, _morphdom2.default)(document.querySelector(config.selector), config.html);
+ if (config.focusSelector) {
+ document.querySelector(config.focusSelector).focus();
+ }
+ },
- // Attribute Mutations ............................................................................................
+ innerHtml: function innerHtml(config) {
+ document.querySelector(config.selector).innerHTML = config.html;
+ if (config.focusSelector) {
+ document.querySelector(config.focusSelector).focus();
+ }
+ },
- setAttribute: function (config) {
- if (CableReady.debug) { console.log("CableReady.setAttribute", config); }
- document.querySelector(config.selector).setAttribute(config.name, config.value);
- },
+ textContent: function textContent(config) {
+ document.querySelector(config.selector).textContent = config.text;
+ },
- removeAttribute: function (config) {
- if (CableReady.debug) { console.log("CableReady.removeAttribute", config); }
- document.querySelector(config.selector).removeAttribute(config.name);
- },
+ insertAdjacentHtml: function insertAdjacentHtml(config) {
+ document.querySelector(config.selector).insertAdjacentHTML(config.position || "beforeend", config.html);
+ if (config.focusSelector) {
+ document.querySelector(config.focusSelector).focus();
+ }
+ },
- // CSS Class Mutations ............................................................................................
+ insertAdjacentText: function insertAdjacentText(config) {
+ document.querySelector(config.querySelector).insertAdjacentText(config.position || "beforeend", config.text);
+ },
- addCssClass: function (config) {
- if (CableReady.debug) { console.log("CableReady.addCssClass", config); }
- document.querySelector(config.selector).classList.add(config.name);
+ remove: function remove(config) {
+ document.querySelector(config.selector).remove();
+ if (config.focusSelector) {
+ document.querySelector(config.focusSelector).focus();
+ }
+ },
+
+ replace: function replace(config) {
+ var element = document.querySelector(config.selector);
+ var div = document.createElement("div");
+ div.innerHTML = config.html;
+ if (config.focusSelector) {
+ document.querySelector(config.focusSelector).focus();
+ }
+ },
+
+ setValue: function setValue(config) {
+ document.querySelector(config.selector).value = config.value;
+ },
+
+ // Attribute Mutations .....................................................................................
+
+ setAttribute: function setAttribute(config) {
+ document.querySelector(config.selector).setAttribute(config.name, config.value);
+ },
+
+ removeAttribute: function removeAttribute(config) {
+ document.querySelector(config.selector).removeAttribute(config.name);
+ },
+
+ // CSS Class Mutations .....................................................................................
+
+ addCssClass: function addCssClass(config) {
+ document.querySelector(config.selector).classList.add(config.name);
+ },
+
+ removeCssClass: function removeCssClass(config) {
+ document.querySelector(config.selector).classList.remove(config.name);
+ },
+
+ // Dataset Mutations .......................................................................................
+
+ setDatasetProperty: function setDatasetProperty(config) {
+ document.querySelector(config.selector).dataset[config.name] = config.value;
+ }
+};
+
+var perform = exports.perform = function perform(operations) {
+ for (var name in operations) {
+ if (operations.hasOwnProperty(name)) {
+ var entries = operations[name];
+ for (var i = 0; i < entries.length; i++) {
+ try {
+ DOMOperations[name](entries[i]);
+ } catch (e) {
+ console.log("CableReady detected an error in " + name + "! " + e.message);
+ }
+ }
+ }
+ }
+};
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var range; // Create a range object for efficently rendering strings to elements.
+var NS_XHTML = 'http://www.w3.org/1999/xhtml';
+
+var doc = typeof document === 'undefined' ? undefined : document;
+
+var testEl = doc ?
+ doc.body || doc.createElement('div') :
+ {};
+
+// Fixes <https://github.com/patrick-steele-idem/morphdom/issues/32>
+// (IE7+ support) <=IE7 does not support el.hasAttribute(name)
+var actualHasAttributeNS;
+
+if (testEl.hasAttributeNS) {
+ actualHasAttributeNS = function(el, namespaceURI, name) {
+ return el.hasAttributeNS(namespaceURI, name);
+ };
+} else if (testEl.hasAttribute) {
+ actualHasAttributeNS = function(el, namespaceURI, name) {
+ return el.hasAttribute(name);
+ };
+} else {
+ actualHasAttributeNS = function(el, namespaceURI, name) {
+ return el.getAttributeNode(namespaceURI, name) != null;
+ };
+}
+
+var hasAttributeNS = actualHasAttributeNS;
+
+
+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];
+}
+
+/**
+ * Returns true if two node's names are the same.
+ *
+ * NOTE: We don't bother checking `namespaceURI` because you will never find two HTML elements with the same
+ * nodeName and different namespace URIs.
+ *
+ * @param {Element} a
+ * @param {Element} b The target element
+ * @return {boolean}
+ */
+function compareNodeNames(fromEl, toEl) {
+ var fromNodeName = fromEl.nodeName;
+ var toNodeName = toEl.nodeName;
+
+ if (fromNodeName === toNodeName) {
+ return true;
+ }
+
+ if (toEl.actualize &&
+ fromNodeName.charCodeAt(0) < 91 && /* from tag name is upper case */
+ toNodeName.charCodeAt(0) > 90 /* target tag name is lower case */) {
+ // If the target element is a virtual DOM node then we may need to normalize the tag name
+ // before comparing. Normal HTML elements that are in the "http://www.w3.org/1999/xhtml"
+ // are converted to upper case
+ return fromNodeName === toNodeName.toUpperCase();
+ } else {
+ return false;
+ }
+}
+
+/**
+ * Create an element, optionally with a known namespace URI.
+ *
+ * @param {string} name the element name, e.g. 'div' or 'svg'
+ * @param {string} [namespaceURI] the element's namespace URI, i.e. the value of
+ * its `xmlns` attribute or its inferred namespace.
+ *
+ * @return {Element}
+ */
+function createElementNS(name, namespaceURI) {
+ return !namespaceURI || namespaceURI === NS_XHTML ?
+ doc.createElement(name) :
+ doc.createElementNS(namespaceURI, name);
+}
+
+/**
+ * Copies the children of one DOM element to another DOM element
+ */
+function moveChildren(fromEl, toEl) {
+ var curChild = fromEl.firstChild;
+ while (curChild) {
+ var nextChild = curChild.nextSibling;
+ toEl.appendChild(curChild);
+ curChild = nextChild;
+ }
+ return toEl;
+}
+
+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);
+ }
+ }
+ }
+
+ // Remove any extra attributes found on the original DOM element that
+ // weren't found on the target element.
+ attrs = fromNode.attributes;
+
+ for (i = attrs.length - 1; i >= 0; --i) {
+ attr = attrs[i];
+ if (attr.specified !== false) {
+ attrName = attr.name;
+ attrNamespaceURI = attr.namespaceURI;
+
+ if (attrNamespaceURI) {
+ attrName = attr.localName || attrName;
+
+ if (!hasAttributeNS(toNode, attrNamespaceURI, attrName)) {
+ fromNode.removeAttributeNS(attrNamespaceURI, attrName);
+ }
+ } else {
+ if (!hasAttributeNS(toNode, null, attrName)) {
+ fromNode.removeAttribute(attrName);
+ }
+ }
+ }
+ }
+}
+
+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 = {
+ /**
+ * Needed for IE. Apparently IE doesn't think that "selected" is an
+ * attribute when reading over the attributes using selectEl.attributes
+ */
+ OPTION: function(fromEl, toEl) {
+ syncBooleanAttrProp(fromEl, toEl, 'selected');
},
+ /**
+ * The "value" attribute is special for the <input> element since it sets
+ * the initial value. Changing the "value" attribute without changing the
+ * "value" property will have no effect since it is only used to the set the
+ * initial value. Similar for the "checked" attribute, and "disabled".
+ */
+ INPUT: function(fromEl, toEl) {
+ syncBooleanAttrProp(fromEl, toEl, 'checked');
+ syncBooleanAttrProp(fromEl, toEl, 'disabled');
- removeCssClass: function (config) {
- if (CableReady.debug) { console.log("CableReady.removeCssClass", config); }
- document.querySelector(config.selector).classList.remove(config.name);
+ if (fromEl.value !== toEl.value) {
+ fromEl.value = toEl.value;
+ }
+
+ if (!hasAttributeNS(toEl, null, 'value')) {
+ fromEl.removeAttribute('value');
+ }
},
- // Dataset Mutations ..............................................................................................
+ TEXTAREA: function(fromEl, toEl) {
+ var newValue = toEl.value;
+ if (fromEl.value !== newValue) {
+ fromEl.value = newValue;
+ }
- setDatasetProperty: function (config) {
- if (CableReady.debug) { console.log("CableReady.setDatasetProperty", config); }
- document.querySelector(config.selector).dataset[config.name] = config.value;
- }
- };
+ var firstChild = fromEl.firstChild;
+ if (firstChild) {
+ // Needed for IE. Apparently IE sets the placeholder as the
+ // node value and vise versa. This ignores an empty update.
+ var oldValue = firstChild.nodeValue;
- window.CableReady = {
- debug: false,
- perform: function (operations) {
- for (var name in operations) {
- if (operations.hasOwnProperty(name)) {
- var entries = operations[name];
- for (var i = 0; i < entries.length; i++) {
- try {
- CableReadyOperations[name](entries[i]);
- } catch (e) {
- console.log("CableReady detected an error! " + e.message);
+ if (oldValue == newValue || (!newValue && oldValue == fromEl.placeholder)) {
+ return;
}
- }
+
+ firstChild.nodeValue = newValue;
}
- }
+ },
+ SELECT: function(fromEl, toEl) {
+ if (!hasAttributeNS(toEl, null, 'multiple')) {
+ var selectedIndex = -1;
+ var i = 0;
+ var curChild = toEl.firstChild;
+ while(curChild) {
+ var nodeName = curChild.nodeName;
+ if (nodeName && nodeName.toUpperCase() === 'OPTION') {
+ if (hasAttributeNS(curChild, null, 'selected')) {
+ selectedIndex = i;
+ break;
+ }
+ i++;
+ }
+ curChild = curChild.nextSibling;
+ }
+
+ fromEl.selectedIndex = i;
+ }
}
- };
-})();
+};
+
+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;
+
+ // This object is used as a lookup to quickly find all keyed elements in the original DOM tree.
+ 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))) {
+ // If we are skipping keyed nodes then we add the key
+ // to a list so that it can be handled at the very end.
+ addKeyedRemoval(key);
+ } else {
+ // Only report the node as discarded if it is not keyed. We do this because
+ // at the end we loop through all keyed elements that were unmatched
+ // and then discard them in one final pass.
+ onNodeDiscarded(curChild);
+ if (curChild.firstChild) {
+ walkDiscardedChildNodes(curChild, skipKeyedNodes);
+ }
+ }
+
+ curChild = curChild.nextSibling;
+ }
+ }
+ }
+
+ /**
+ * Removes a DOM node out of the original DOM
+ *
+ * @param {Node} node The node to remove
+ * @param {Node} parentNode The nodes parent
+ * @param {Boolean} skipKeyedNodes If true then elements with keys will be skipped and not discarded.
+ * @return {undefined}
+ */
+ function removeNode(node, parentNode, skipKeyedNodes) {
+ if (onBeforeNodeDiscarded(node) === false) {
+ return;
+ }
+
+ if (parentNode) {
+ parentNode.removeChild(node);
+ }
+
+ onNodeDiscarded(node);
+ walkDiscardedChildNodes(node, skipKeyedNodes);
+ }
+
+ // // TreeWalker implementation is no faster, but keeping this around in case this changes in the future
+ // function indexTree(root) {
+ // var treeWalker = document.createTreeWalker(
+ // root,
+ // NodeFilter.SHOW_ELEMENT);
+ //
+ // var el;
+ // while((el = treeWalker.nextNode())) {
+ // var key = getNodeKey(el);
+ // if (key) {
+ // fromNodesLookup[key] = el;
+ // }
+ // }
+ // }
+
+ // // NodeIterator implementation is no faster, but keeping this around in case this changes in the future
+ //
+ // function indexTree(node) {
+ // var nodeIterator = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT);
+ // var el;
+ // while((el = nodeIterator.nextNode())) {
+ // var key = getNodeKey(el);
+ // if (key) {
+ // fromNodesLookup[key] = el;
+ // }
+ // }
+ // }
+
+ function indexTree(node) {
+ if (node.nodeType === ELEMENT_NODE) {
+ var curChild = node.firstChild;
+ while (curChild) {
+ var key = getNodeKey(curChild);
+ if (key) {
+ fromNodesLookup[key] = curChild;
+ }
+
+ // Walk recursively
+ 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 morphEl(fromEl, toEl, childrenOnly) {
+ var toElKey = getNodeKey(toEl);
+ var curFromNodeKey;
+
+ if (toElKey) {
+ // If an element with an ID is being morphed then it is will be in the final
+ // DOM so clear it out of the saved elements collection
+ 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) {
+ // Both nodes being compared are Element nodes
+
+ 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 (curFromNodeChild.nextSibling === 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 moving the original DOM node into the current position and morph
+ // it.
+
+ // 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);
+
+ fromNextSibling = curFromNodeChild.nextSibling;
+
+ 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 */);
+ }
+
+ 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;
+ }
+
+ 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.
+ 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;
+ }
+
+ }
+ }
+
+ 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 */);
+ }
+
+ curFromNodeChild = fromNextSibling;
+ }
+
+ // 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);
+ 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;
+ }
+
+ // 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))) {
+ // 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;
+ }
+ }
+
+ var specialElHandler = specialElHandlers[fromEl.nodeName];
+ if (specialElHandler) {
+ specialElHandler(fromEl, toEl);
+ }
+ } // END: morphEl(...)
+
+ var morphedNode = fromNode;
+ var morphedNodeType = morphedNode.nodeType;
+ var toNodeType = toNode.nodeType;
+
+ if (!childrenOnly) {
+ // Handle the case where we are given two DOM nodes that are not
+ // compatible (e.g. <div> --> <span> or <div> --> TEXT)
+ if (morphedNodeType === ELEMENT_NODE) {
+ if (toNodeType === ELEMENT_NODE) {
+ if (!compareNodeNames(fromNode, toNode)) {
+ onNodeDiscarded(fromNode);
+ morphedNode = moveChildren(fromNode, createElementNS(toNode.nodeName, toNode.namespaceURI));
+ }
+ } else {
+ // Going from an element node to a text node
+ morphedNode = toNode;
+ }
+ } else if (morphedNodeType === TEXT_NODE || morphedNodeType === COMMENT_NODE) { // Text or comment node
+ if (toNodeType === morphedNodeType) {
+ if (morphedNode.nodeValue !== toNode.nodeValue) {
+ morphedNode.nodeValue = toNode.nodeValue;
+ }
+
+ return morphedNode;
+ } else {
+ // Text node to something else
+ morphedNode = toNode;
+ }
+ }
+ }
+
+ if (morphedNode === toNode) {
+ // The "to node" was not compatible with the "from node" so we had to
+ // toss out the "from node" and use the "to node"
+ onNodeDiscarded(fromNode);
+ } else {
+ morphEl(morphedNode, toNode, childrenOnly);
+
+ // We now need to loop over any keyed nodes that might need to be
+ // removed. We only do the removal if we know that the keyed node
+ // never found a match. When a keyed node is matched up we remove
+ // it out of fromNodesLookup and we use fromNodesLookup to determine
+ // if a keyed node has been matched up or not
+ 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);
+ }
+ // If we had to swap out the from node with a new node because the old
+ // node was not compatible with the target node then we need to
+ // replace the old DOM node in the original DOM tree. This is only
+ // possible if the original DOM node was part of a DOM tree which
+ // we know is the case if it has a parent node.
+ fromNode.parentNode.replaceChild(morphedNode, fromNode);
+ }
+
+ return morphedNode;
+ };
+}
+
+var morphdom = morphdomFactory(morphAttrs);
+
+module.exports = morphdom;
+
+
+/***/ })
+/******/ ]);
\ No newline at end of file