UNPKG

tiny-dream

Version:

A tiny Incremental DOM implementation for modern browsers.

160 lines (131 loc) 3.36 kB
import createElement from './create-element' import createTextNode from './create-text-node' import getNodeData from './get-node-data' import { hasOwnProperty, detectFocusedAncestors, moveBefore } from './utils' let doc = null let currentNode = null let currentParent = null const markFocused = (focusedAncestors, focused) => { for (let i = 0; i < focusedAncestors.length; i += 1) { getNodeData(focusedAncestors[i]).focused = focused } } const matches = (matchNode, nameOrCtor, key, typeId) => { const data = getNodeData(matchNode) return nameOrCtor === data.nameOrCtor && typeId === data.typeId && key == data.key } const alignWithDOM = (nameOrCtor, key, typeId) => { if (currentNode && matches(currentNode, nameOrCtor, key, typeId)) { return } const parentData = getNodeData(currentParent) const keyMap = parentData && parentData.keyMap let node if (key) { const keyNode = keyMap[key] if (keyNode) { if (matches(keyNode, nameOrCtor, key, typeId)) { node = keyNode } else { getNodeData(keyNode).key = null } } } if (!node) { if (nameOrCtor === '#text') { node = createTextNode(doc) } else { node = createElement(doc, currentParent, nameOrCtor, key, typeId) } if (key) { keyMap[key] = node } } if (getNodeData(node).focused) { moveBefore(currentParent, node, currentNode) } else { currentParent.insertBefore(node, currentNode) } currentNode = node } const clearUnvisitedDOM = (parentNode, startNode, endNode) => { const data = getNodeData(parentNode) const keyMap = data.keyMap let child = startNode while (child !== endNode) { const next = child.nextSibling const key = getNodeData(child).key parentNode.removeChild(child) if (key) { delete keyMap[key] } child = next } } const enterNode = () => { currentParent = currentNode currentNode = null } const getNextNode = () => { if (currentNode) { return currentNode.nextSibling } return currentParent.firstChild } const nextNode = () => { currentNode = getNextNode() } const exitNode = () => { clearUnvisitedDOM(currentParent, getNextNode(), null) currentNode = currentParent currentParent = currentParent.parentNode } const open = (nameOrCtor, key, typeId) => { nextNode() alignWithDOM(nameOrCtor, key, typeId) enterNode() return currentParent } const close = () => { exitNode() return currentNode } const text = function() { nextNode() alignWithDOM('#text', null) return currentNode } const component = (c, data) => { if (hasOwnProperty(c, 'render')) { c.render.apply(null, [data]) } } const patch = (node, fn, data) => { const prevDoc = doc const prevCurrentNode = currentNode const prevCurrentParent = currentParent let previousInAttributes = false let previousInSkip = false doc = (node && node.ownerDocument) || document currentParent = node && node.parentNode const focusedAncestors = detectFocusedAncestors(node, currentParent) markFocused(focusedAncestors, true) currentNode = node enterNode() fn(data) exitNode() markFocused(focusedAncestors, false) doc = prevDoc currentNode = prevCurrentNode currentParent = prevCurrentParent return node } export { open, close, text, component, patch }