UNPKG

moy-dom

Version:

A flexiable Virtual DOM library for building modern web interface.

934 lines (829 loc) 27 kB
/** * moy-dom v1.1.3 * (jp) 2018 murakami * @license MIT */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.moyDom = {}))); }(this, (function (exports) { 'use strict'; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); function makeKeyIndexAndFree(list) { var keyIndex = new Map(), free = []; var itemKey = void 0; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = list.entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _ref = _step.value; var _ref2 = slicedToArray(_ref, 2); var index = _ref2[0]; var item = _ref2[1]; itemKey = item.key; if (itemKey !== undefined) { keyIndex.set(itemKey, index); } else { free.push(item); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return { keyIndex: keyIndex, free: free }; } /** * [remove remove list item] * @param {[Number]} simulateIndex [index] * @param {[Array]} simulateList [list] * @param {[Object]} changes [changes] * @return {[undefined]} [undefined] */ function remove(simulateIndex, simulateList, changes) { simulateList.splice(simulateIndex, 1); changes.push({ type: 0, index: simulateIndex }); } /** * [move move list two item] * @param {[Number]} newIndex [toIndex] * @param {[Number]} simulateFindIndex [fromIndex] * @param {[Array]} simulateList [List] * @param {[changes]} changes [changes] * @return {[unfined]} [undefined] */ function move(newIndex, simulateFindIndex, simulateList, changes) { var _ref = [simulateList[simulateFindIndex], simulateList[newIndex]]; simulateList[newIndex] = _ref[0]; simulateList[simulateFindIndex] = _ref[1]; changes.push({ type: 1, toIndex: newIndex, fromIndex: simulateFindIndex }); } /** * [insert insert a given location item to a list] * @param {[Number]} newIndex [index] * @param {[Any]} newItem [item] * @param {[Array]} simulateList [list] * @param {[Object]} changes [changes] * @return {[undefined]} [undefined] */ function insert(newIndex, newItem, simulateList, changes) { simulateList.splice(newIndex, 0, newItem); changes.push({ type: 2, index: newIndex, item: newItem }); } /** * [append append a child to a list] * @param {[Any]} newItem [child] * @param {[Array]} simulateList [list] * @param {[Object]} changes [changes] * @return {[undefined]} [undefined] */ function append(newItem, simulateList, changes) { simulateList.push(newItem); //no valid changes.push({ type: 3, item: newItem }); } /** * [listDiff diff two List] * @param {[Array]} newList [the new List] * @param {[Array]} oldList [the old List] * @return {[Object]} [a children property for deep compare and a changes for two list's diffs] * * @changes's type * 0 -> remove * 1 -> move * 2 -> insert * 3 -> append */ function listDiff(newList, oldList) { var _makeKeyIndexAndFree = makeKeyIndexAndFree(newList), newKeyIndex = _makeKeyIndexAndFree.keyIndex, newFree = _makeKeyIndexAndFree.free, children = [], changes = [], newFreeLength = newFree.length; var newFreeIndex = 0, hittingItemKeyIndex = void 0, oldItemKey = void 0; // first we get old list same struct children for deep compare var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = oldList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var oldItem = _step.value; oldItemKey = oldItem.key; if (oldItemKey !== undefined) { hittingItemKeyIndex = newKeyIndex.get(oldItemKey); children.push(hittingItemKeyIndex !== undefined ? newList[hittingItemKeyIndex] : null); } else { // original place children.push(newFreeIndex < newFreeLength ? newFree[newFreeIndex++] : null); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } var simulateList = [].concat(children); // the simulate for list changes var simulateIndex = 0, simulateLength = simulateList.length; // remove null items represent not exit items in new list while (simulateIndex < simulateLength) { if (simulateList[simulateIndex] === null) { remove(simulateIndex, simulateList, changes); simulateLength--; } else { simulateIndex++; } } simulateIndex = 0; var newIndex = 0, newLength = newList.length, simulateKeyIndex = void 0, // may need simulateKeyIndex for deep compare simulateItem = void 0, simulateItemKey = void 0, newItem = void 0, newItemKey = void 0; // point to point compare while (simulateIndex < simulateLength && newIndex < newLength) { simulateItem = simulateList[simulateIndex]; simulateItemKey = simulateItem.key; newItem = newList[newIndex]; newItemKey = newItem.key; // the same node if (newItemKey === simulateItemKey) { simulateIndex++; newIndex++; } else { if (!simulateKeyIndex) { // get simulateKeyIndex simulateKeyIndex = makeKeyIndexAndFree(simulateList).keyIndex; } if (hittingItemKeyIndex = simulateKeyIndex.get(newItemKey)) { // find the same key in old list, just move it move(newIndex++, hittingItemKeyIndex, simulateList, changes); simulateIndex++; } else { // otherwise inset a new one insert(newIndex, newItem, simulateList, changes); simulateLength++; simulateIndex++; newIndex++; } } } // rest new list item should be append while (newIndex < newLength) { append(newList[newIndex++], simulateList, changes); } return { children: children, changes: changes }; } /** * [diffProps diff two object(props)] * @param {[Object]} newTreeProps [the new props] * @param {[Object]} oldTreeProps [the old props] * @return {[Object]} [null for no changes, Object for changes] */ function diffProps(newTreeProps, oldTreeProps) { var propPatches = {}; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = Object.entries(oldTreeProps)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _ref = _step.value; var _ref2 = slicedToArray(_ref, 2); var key = _ref2[0]; var value = _ref2[1]; if (newTreeProps[key] !== value) { propPatches[key] = newTreeProps[key]; // undefined represents remove the attribute } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = Object.entries(newTreeProps)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _ref3 = _step2.value; var _ref4 = slicedToArray(_ref3, 2); var _key = _ref4[0]; var _value = _ref4[1]; if (!Reflect.has(oldTreeProps, _key)) { propPatches[_key] = _value; } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } for (var propPatch in propPatches) { return propPatches; } return null; } /** * [diffChildren diff two children list] * @param {[Array]} newChildren [the Element Array for old children] * @param {[Array]} oldChildren [the Element Array for new children] * @param {[Number]} index [current node index] * @param {[Object]} patches [patches] * @param {[Object]} currentPatches [current node patches] * @return {[undefined]} [undefined] */ var diffChildren = function diffChildren(newChildren, oldChildren, index, patches, currentPatches) { var _listDiff = listDiff(newChildren, oldChildren), children = _listDiff.children, changes = _listDiff.changes; if (changes.length) { currentPatches.push({ // remove, move, insert, append node type: 1, changes: changes }); } newChildren = children; // children -> newChildren for deep compare var leftNode = null, currentNodeIndex = index; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = oldChildren.entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _ref = _step.value; var _ref2 = slicedToArray(_ref, 2); var _index = _ref2[0]; var oldChild = _ref2[1]; var newChild = newChildren[_index]; currentNodeIndex += 1 + (leftNode && leftNode.count ? leftNode.count : 0); dfsWalk(newChild, oldChild, currentNodeIndex, patches); leftNode = oldChild; } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } }; /** * [dfsWalk diff two node tree] * @param {[Element]} newTree [new tree] * @param {[Element]} oldTree [old tree] * @param {[Number]} index [current tree node index] * @param {[Object]} patches [patches] * @return {[undefined]} [undefined] */ function dfsWalk(newTree, oldTree, index, patches) { var currentPatches = []; if (newTree === null) ; else if (Object.prototype.toString.call(newTree) === '[object String]' && Object.prototype.toString.call(oldTree) === '[object String]') { newTree !== oldTree && currentPatches.push({ type: 3, // text node content: newTree }); } else if (newTree.tagName === oldTree.tagName && newTree.key === oldTree.key) { var propPatches = diffProps(newTree.props, oldTree.props); if (propPatches) { currentPatches.push({ type: 2, // props changed props: propPatches }); } if (!newTree.props.ignore) { diffChildren(newTree.children, oldTree.children, index, patches, currentPatches); } } else { currentPatches.push({ type: 0, // not the same node, replace it node: newTree }); } if (currentPatches.length) { patches[index] = currentPatches; } } function diff(newTree, oldTree) { var index = 0, patches = {}; if (oldTree === null) { if (newTree !== null) { patches.rootWithNull = { type: 4, // append newNode vnode: newTree }; } return patches; } else { if (newTree === null) { patches.rootWithNull = { type: 5 // remove oldNode }; return patches; } } if (Object.prototype.toString.call(newTree) !== '[object Element]') { newTree += ''; } if (Object.prototype.toString.call(oldTree) !== '[object Element]') { oldTree += ''; } dfsWalk(newTree, oldTree, index, patches); return patches; } var domAttrs = { selected: true, checked: true, muted: true, value: true }, boolAttrs = { allowfullscreen: true, async: true, autofocus: true, autoplay: true, compact: true, controls: true, declare: true, default: true, defaultchecked: true, defaultmuted: true, defaultselected: true, defer: true, disabled: true, enabled: true, formnovalidate: true, hidden: true, indeterminate: true, inert: true, ismap: true, itemscope: true, loop: true, multiple: true, nohref: true, noresize: true, noshade: true, novalidate: true, nowrap: true, open: true, pauseonexit: true, readonly: true, required: true, reversed: true, scoped: true, seamless: true, sortable: true, translate: true, truespeed: true, typemustmatch: true, visible: true }; /** * [setProps set node propertities with given props] * @param {[DOMElement]} node [the node] * @param {[Object]} props [given props] * @return {[undefined]} [undefined] */ function setProps(node, props) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = Object.entries(props)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _ref = _step.value; var _ref2 = slicedToArray(_ref, 2); var key = _ref2[0]; var value = _ref2[1]; if (domAttrs[key] || /^on\w+/.test(key)) { node[key] = value === undefined ? null : value; } else if (boolAttrs[key]) { if (value) { node.setAttribute(key, key); } else { node.removeAttribute(key); } } else { if (value === undefined) { node.removeAttribute(key); } else { node.setAttribute(key, value); } } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } /** * [renderChildren render node's children for given changes] * @param {[DOMElement]} node [description] * @param {[Array]} changes [the given changes] * @return {[undefined]} [undefined] */ function renderChildren(node, changes) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = changes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _ref = _step.value; var index = _ref.index, type = _ref.type, item = _ref.item, toIndex = _ref.toIndex, fromIndex = _ref.fromIndex; if (type === 0) { //remove node.removeChild(node.childNodes[index]); } else if (type === 1) { // move node.insertBefore(node.childNodes[toIndex], node.childNodes[fromIndex]); node.insertBefore(node.childNodes[fromIndex], node.childNodes[toIndex]); } else if (type === 2) { // insert var insertNode = Object.prototype.toString.call(item) === '[object Element]' ? item.render() : document.createTextNode(item); node.insertBefore(insertNode, node.childNodes[index]); } else if (type === 3) { // append var _insertNode = Object.prototype.toString.call(item) === '[object Element]' ? item.render() : document.createTextNode(item); node.appendChild(_insertNode); } else { throw new Error('Unknown reorder type ' + type); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } /** * [applyPatches apply the patches to the node] * @param {[DOMElement]} node [the node] * @param {[patches]} currentPatches [the patches] * @return {[undefined]} [undefined] */ function applyPatches(node, currentPatches) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = currentPatches[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var currentPatch = _step.value; switch (currentPatch.type) { case 0: { // replace var newNode = Object.prototype.toString.call(currentPatch.node) === '[object Element]' ? currentPatch.node.render() : document.createTextNode(currentPatch.node); node.parentNode.replaceChild(newNode, node); break; } case 1: { // remove, move, insert, append node renderChildren(node, currentPatch.changes); break; } case 2: { //props setProps(node, currentPatch.props); break; } case 3: { // text node node.textContent = currentPatch.content; break; } default: { throw new Error('Unknown patch type ' + currentPatch.type); } } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } /** * [dfsWalk apply patches for given node(recursion)] * @param {[DOMElements]} node [the node] * @param {[Object]} walker [the current node index, Object for deep compare changed the index] * @param {[patches]} patches [the patches] * @return {[undefined]} [undefined] */ function dfsWalk$1(node, walker, patches) { var currentPatches = patches[walker.index]; var len = node.childNodes.length; for (var i = 0; i < len; i++) { var child = node.childNodes[i]; walker.index++; dfsWalk$1(child, walker, patches); } if (currentPatches) { applyPatches(node, currentPatches); } } function patch(node, patches) { var rootWithNullPatch = patches.rootWithNull; if (rootWithNullPatch) { var type = rootWithNullPatch.type, vnode = rootWithNullPatch.vnode; if (type === 4) { var newNode = Object.prototype.toString.call(vnode) === '[object Element]' ? vnode.render() : document.createTextNode(vnode); node.appendChild(newNode); } if (type === 5) { node.removeChild(node.lastChild); } return; } var walker = { index: 0 }; dfsWalk$1(node.lastChild, walker, patches); } var Element = function () { function Element(tagName, props, children) { classCallCheck(this, Element); this.tagName = tagName; this.props = props; this.key = props.key; var count = 0, childrenFilter = [], length = children.length, child = void 0; for (var i = 0; i < length; i++) { child = children[i]; if (child !== null) { if (Object.prototype.toString.call(child) === '[object Element]') { count += child.count + 1; childrenFilter.push(child); } else { count++; childrenFilter.push('' + child); } } } this.children = childrenFilter; this.count = count; } createClass(Element, [{ key: 'render', value: function render() { var el = document.createElement(this.tagName); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = Object.entries(this.props)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _ref = _step.value; var _ref2 = slicedToArray(_ref, 2); var propKey = _ref2[0]; var propValue = _ref2[1]; if (domAttrs[propKey] || /^on\w+/.test(propKey)) { el[propKey] = propValue === undefined ? null : propValue; } else if (boolAttrs[propKey]) { if (propValue) { el.setAttribute(propKey, propKey); } } else { if (propValue !== undefined) { el.setAttribute(propKey, propValue); } } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = this.children[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _child = _step2.value; el.appendChild(Object.prototype.toString.call(_child) === '[object Element]' ? _child.render() : document.createTextNode(_child)); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } return el; } }, { key: Symbol.toStringTag, get: function get$$1() { return 'Element'; } }], [{ key: 'of', value: function of(tagName) { var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; for (var _len = arguments.length, children = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { children[_key - 2] = arguments[_key]; } if (Object.prototype.toString.call(props) !== '[object Object]') { children.unshift(props); props = {}; } return new Element(tagName, props, children); } }]); return Element; }(); var appRoot = void 0, appGetNode = void 0, appCurrentNode = void 0; var render = function render(root, getNode) { appCurrentNode = getNode(); appRoot = root; appGetNode = getNode; if (appCurrentNode !== null) { appRoot.appendChild(Object.prototype.toString.call(appCurrentNode) === '[object Element]' ? appCurrentNode.render() : document.createTextNode(appCurrentNode)); } }; var reRender = function reRender() { var node = appGetNode(); patch(appRoot, diff(node, appCurrentNode)); appCurrentNode = node; }; exports.Element = Element; exports.render = render; exports.reRender = reRender; Object.defineProperty(exports, '__esModule', { value: true }); })));