var SSR_NODE = 1 var TEXT_NODE = 3 var EMPTY_OBJ = {} var EMPTY_ARR = [] var SVG_NS = "http://www.w3.org/2000/svg" var id = (a) => a var map = EMPTY_ARR.map var isArray = Array.isArray var createClass = (obj) => { var out = "" if (typeof obj === "string") return obj if (isArray(obj)) { for (var k = 0, tmp; k < obj.length; k++) { if ((tmp = createClass(obj[k]))) { out += (out && " ") + tmp } } } else { for (var k in obj) { if (obj[k]) out += (out && " ") + k } } return out } var shouldRestart = (a, b) => { for (var k in { ...a, ...b }) { if (typeof (isArray(a[k]) ? a[k][0] : a[k]) === "function") { b[k] = a[k] } else if (a[k] !== b[k]) return true } } var patchSubs = (oldSubs, newSubs = EMPTY_ARR, dispatch) => { for ( var subs = [], i = 0, oldSub, newSub; i < oldSubs.length || i < newSubs.length; i++ ) { oldSub = oldSubs[i] newSub = newSubs[i] subs.push( newSub && newSub !== true ? !oldSub || newSub[0] !== oldSub[0] || shouldRestart(newSub[1], oldSub[1]) ? [ newSub[0], newSub[1], (oldSub && oldSub[2](), newSub[0](dispatch, newSub[1])), ] : oldSub : oldSub && oldSub[2]() ) } return subs } var getKey = (vdom) => (vdom == null ? vdom : vdom.key) var patchProperty = (node, key, oldValue, newValue, listener, isSvg) => { if (key === "style") { for (var k in { ...oldValue, ...newValue }) { oldValue = newValue == null || newValue[k] == null ? "" : newValue[k] if (k[0] === "-") { node[key].setProperty(k, oldValue) } else { node[key][k] = oldValue } } } else if (key[0] === "o" && key[1] === "n") { if ( !((node.events || (node.events = {}))[(key = key.slice(2))] = newValue) ) { node.removeEventListener(key, listener) } else if (!oldValue) { node.addEventListener(key, listener) } } else if (!isSvg && key !== "list" && key !== "form" && key in node) { node[key] = newValue == null ? "" : newValue } else if (newValue == null || newValue === false) { node.removeAttribute(key) } else { node.setAttribute(key, newValue) } } var createNode = (vdom, listener, isSvg) => { var props = vdom.props var node = vdom.type === TEXT_NODE ? document.createTextNode(vdom.tag) : (isSvg = isSvg || vdom.tag === "svg") ? document.createElementNS(SVG_NS, vdom.tag, props.is && props) : document.createElement(vdom.tag, props.is && props) for (var k in props) { patchProperty(node, k, null, props[k], listener, isSvg) } for (var i = 0; i < vdom.children.length; i++) { node.appendChild( createNode( (vdom.children[i] = maybeVNode(vdom.children[i])), listener, isSvg ) ) } return (vdom.node = node) } var patch = (parent, node, oldVNode, newVNode, listener, isSvg) => { if (oldVNode === newVNode) { } else if ( oldVNode != null && oldVNode.type === TEXT_NODE && newVNode.type === TEXT_NODE ) { if (oldVNode.tag !== newVNode.tag) node.nodeValue = newVNode.tag } else if (oldVNode == null || oldVNode.tag !== newVNode.tag) { node = parent.insertBefore( createNode((newVNode = maybeVNode(newVNode)), listener, isSvg), node ) if (oldVNode != null) { parent.removeChild(oldVNode.node) } } else { var tmpVKid var oldVKid var oldKey var newKey var oldProps = oldVNode.props var newProps = newVNode.props var oldVKids = oldVNode.children var newVKids = newVNode.children var oldHead = 0 var newHead = 0 var oldTail = oldVKids.length - 1 var newTail = newVKids.length - 1 isSvg = isSvg || newVNode.tag === "svg" for (var i in { ...oldProps, ...newProps }) { if ( (i === "value" || i === "selected" || i === "checked" ? node[i] : oldProps[i]) !== newProps[i] ) { patchProperty(node, i, oldProps[i], newProps[i], listener, isSvg) } } while (newHead <= newTail && oldHead <= oldTail) { if ( (oldKey = getKey(oldVKids[oldHead])) == null || oldKey !== getKey(newVKids[newHead]) ) { break } patch( node, oldVKids[oldHead].node, oldVKids[oldHead], (newVKids[newHead] = maybeVNode( newVKids[newHead++], oldVKids[oldHead++] )), listener, isSvg ) } while (newHead <= newTail && oldHead <= oldTail) { if ( (oldKey = getKey(oldVKids[oldTail])) == null || oldKey !== getKey(newVKids[newTail]) ) { break } patch( node, oldVKids[oldTail].node, oldVKids[oldTail], (newVKids[newTail] = maybeVNode( newVKids[newTail--], oldVKids[oldTail--] )), listener, isSvg ) } if (oldHead > oldTail) { while (newHead <= newTail) { node.insertBefore( createNode( (newVKids[newHead] = maybeVNode(newVKids[newHead++])), listener, isSvg ), (oldVKid = oldVKids[oldHead]) && oldVKid.node ) } } else if (newHead > newTail) { while (oldHead <= oldTail) { node.removeChild(oldVKids[oldHead++].node) } } else { for (var keyed = {}, newKeyed = {}, i = oldHead; i <= oldTail; i++) { if ((oldKey = oldVKids[i].key) != null) { keyed[oldKey] = oldVKids[i] } } while (newHead <= newTail) { oldKey = getKey((oldVKid = oldVKids[oldHead])) newKey = getKey( (newVKids[newHead] = maybeVNode(newVKids[newHead], oldVKid)) ) if ( newKeyed[oldKey] || (newKey != null && newKey === getKey(oldVKids[oldHead + 1])) ) { if (oldKey == null) { node.removeChild(oldVKid.node) } oldHead++ continue } if (newKey == null || oldVNode.type === SSR_NODE) { if (oldKey == null) { patch( node, oldVKid && oldVKid.node, oldVKid, newVKids[newHead], listener, isSvg ) newHead++ } oldHead++ } else { if (oldKey === newKey) { patch( node, oldVKid.node, oldVKid, newVKids[newHead], listener, isSvg ) newKeyed[newKey] = true oldHead++ } else { if ((tmpVKid = keyed[newKey]) != null) { patch( node, node.insertBefore(tmpVKid.node, oldVKid && oldVKid.node), tmpVKid, newVKids[newHead], listener, isSvg ) newKeyed[newKey] = true } else { patch( node, oldVKid && oldVKid.node, null, newVKids[newHead], listener, isSvg ) } } newHead++ } } while (oldHead <= oldTail) { if (getKey((oldVKid = oldVKids[oldHead++])) == null) { node.removeChild(oldVKid.node) } } for (var i in keyed) { if (newKeyed[i] == null) { node.removeChild(keyed[i].node) } } } } return (newVNode.node = node) } var propsChanged = (a, b) => { for (var k in a) if (a[k] !== b[k]) return true for (var k in b) if (a[k] !== b[k]) return true } var maybeVNode = (newVNode, oldVNode) => newVNode !== true && newVNode !== false && newVNode ? typeof newVNode.tag === "function" ? ((!oldVNode || oldVNode.memo == null || propsChanged(oldVNode.memo, newVNode.memo)) && ((oldVNode = newVNode.tag(newVNode.memo)).memo = newVNode.memo), oldVNode) : newVNode : text("") var recycleNode = (node) => node.nodeType === TEXT_NODE ? text(node.nodeValue, node) : createVNode( node.nodeName.toLowerCase(), EMPTY_OBJ, map.call(node.childNodes, recycleNode), SSR_NODE, node ) var createVNode = (tag, { key, ...props }, children, type, node) => ({ tag, props, key, children, type, node, }) export var memo = (tag, memo) => ({ tag, memo }) export var text = (value, node) => createVNode(value, EMPTY_OBJ, EMPTY_ARR, TEXT_NODE, node) export var h = (tag, { class: c, ...props }, children = EMPTY_ARR) => createVNode( tag, { ...props, ...(c ? { class: createClass(c) } : EMPTY_OBJ) }, isArray(children) ? children : [children] ) export var app = ({ node, view, subscriptions, dispatch = id, init = EMPTY_OBJ, }) => { var vdom = node && recycleNode(node) var subs = [] var state var busy var update = (newState) => { if (state !== newState) { if ((state = newState) == null) dispatch = subscriptions = render = id if (subscriptions) subs = patchSubs(subs, subscriptions(state), dispatch) if (view && !busy) requestAnimationFrame(render, (busy = true)) } } var render = () => (node = patch( node.parentNode, node, vdom, (vdom = view(state)), listener, (busy = false) )) var listener = function (event) { dispatch(this.events[event.type], event) } return ( (dispatch = dispatch((action, props) => typeof action === "function" ? dispatch(action(state, props)) : isArray(action) ? typeof action[0] === "function" ? dispatch(action[0], action[1]) : action .slice(1) .map( (fx) => fx && fx !== true && (fx[0] || fx)(dispatch, fx[1]), update(action[0]) ) : update(action) ))(init), dispatch ) }