hyperapp/index.js

415 lines
9.9 KiB
JavaScript
Raw Permalink Normal View History

2020-07-11 10:11:14 +02:00
var SSR_NODE = 1
var TEXT_NODE = 3
2019-04-26 13:54:44 +02:00
var EMPTY_OBJ = {}
var EMPTY_ARR = []
2020-05-17 15:31:37 +02:00
var SVG_NS = "http://www.w3.org/2000/svg"
2020-07-11 10:11:14 +02:00
var id = (a) => a
2019-04-26 13:54:44 +02:00
var map = EMPTY_ARR.map
2018-07-09 05:34:32 +02:00
var isArray = Array.isArray
2019-03-03 18:21:56 +01:00
2020-07-11 10:11:14 +02:00
var createClass = (obj) => {
var out = ""
2020-07-11 10:11:14 +02:00
if (typeof obj === "string") return obj
2019-04-30 21:31:38 +02:00
2020-07-11 10:11:14 +02:00
if (isArray(obj)) {
for (var k = 0, tmp; k < obj.length; k++) {
if ((tmp = createClass(obj[k]))) {
2019-04-26 13:54:44 +02:00
out += (out && " ") + tmp
}
}
} else {
2020-07-11 10:11:14 +02:00
for (var k in obj) {
if (obj[k]) out += (out && " ") + k
}
}
return out
}
2020-05-25 13:59:46 +02:00
var shouldRestart = (a, b) => {
2020-07-11 10:11:14 +02:00
for (var k in { ...a, ...b }) {
2021-01-22 18:37:05 +01:00
if (typeof (isArray(a[k]) ? a[k][0] : a[k]) === "function") {
b[k] = a[k]
2020-07-11 10:11:14 +02:00
} else if (a[k] !== b[k]) return true
}
}
var patchSubs = (oldSubs, newSubs = EMPTY_ARR, dispatch) => {
2019-04-26 13:54:44 +02:00
for (
2020-05-16 10:39:29 +02:00
var subs = [], i = 0, oldSub, newSub;
2019-04-26 13:54:44 +02:00
i < oldSubs.length || i < newSubs.length;
i++
) {
oldSub = oldSubs[i]
newSub = newSubs[i]
2019-04-26 13:54:44 +02:00
subs.push(
newSub && newSub !== true
2019-04-26 13:54:44 +02:00
? !oldSub ||
newSub[0] !== oldSub[0] ||
shouldRestart(newSub[1], oldSub[1])
? [
newSub[0],
newSub[1],
(oldSub && oldSub[2](), newSub[0](dispatch, newSub[1])),
2019-04-26 13:54:44 +02:00
]
: oldSub
: oldSub && oldSub[2]()
)
}
2019-06-05 20:48:28 +02:00
return subs
}
2020-07-11 10:11:14 +02:00
var getKey = (vdom) => (vdom == null ? vdom : vdom.key)
2020-05-25 13:59:46 +02:00
var patchProperty = (node, key, oldValue, newValue, listener, isSvg) => {
if (key === "style") {
2020-05-25 13:59:46 +02:00
for (var k in { ...oldValue, ...newValue }) {
2019-04-26 13:54:44 +02:00
oldValue = newValue == null || newValue[k] == null ? "" : newValue[k]
if (k[0] === "-") {
2019-05-02 10:47:34 +02:00
node[key].setProperty(k, oldValue)
2018-07-09 05:34:32 +02:00
} else {
2019-05-02 10:47:34 +02:00
node[key][k] = oldValue
2017-12-05 04:53:56 +01:00
}
}
2019-05-02 10:47:34 +02:00
} else if (key[0] === "o" && key[1] === "n") {
if (
!((node.events || (node.events = {}))[(key = key.slice(2))] = newValue)
) {
2019-05-02 10:47:34 +02:00
node.removeEventListener(key, listener)
2019-04-26 13:54:44 +02:00
} else if (!oldValue) {
node.addEventListener(key, listener)
}
} else if (!isSvg && key !== "list" && key !== "form" && key in node) {
2019-05-02 10:47:34 +02:00
node[key] = newValue == null ? "" : newValue
} else if (newValue == null || newValue === false) {
2019-05-02 10:47:34 +02:00
node.removeAttribute(key)
2019-03-15 14:10:05 +01:00
} else {
2019-05-02 10:47:34 +02:00
node.setAttribute(key, newValue)
}
2018-07-09 05:34:32 +02:00
}
2020-05-25 13:59:46 +02:00
var createNode = (vdom, listener, isSvg) => {
var props = vdom.props
2019-04-26 13:54:44 +02:00
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)
2018-07-09 05:34:32 +02:00
for (var k in props) {
2019-04-26 13:54:44 +02:00
patchProperty(node, k, null, props[k], listener, isSvg)
}
2020-05-17 15:32:07 +02:00
for (var i = 0; i < vdom.children.length; i++) {
2019-04-26 13:54:44 +02:00
node.appendChild(
createNode(
2020-05-21 14:43:46 +02:00
(vdom.children[i] = maybeVNode(vdom.children[i])),
2019-04-26 13:54:44 +02:00
listener,
isSvg
)
2018-07-09 05:34:32 +02:00
)
}
return (vdom.node = node)
2018-07-09 05:34:32 +02:00
}
2020-05-25 13:59:46 +02:00
var patch = (parent, node, oldVNode, newVNode, listener, isSvg) => {
2019-04-26 13:54:44 +02:00
if (oldVNode === newVNode) {
2018-07-09 05:34:32 +02:00
} else if (
2019-04-26 13:54:44 +02:00
oldVNode != null &&
oldVNode.type === TEXT_NODE &&
newVNode.type === TEXT_NODE
2018-07-09 05:34:32 +02:00
) {
if (oldVNode.tag !== newVNode.tag) node.nodeValue = newVNode.tag
} else if (oldVNode == null || oldVNode.tag !== newVNode.tag) {
2019-04-26 13:54:44 +02:00
node = parent.insertBefore(
2020-05-21 14:43:46 +02:00
createNode((newVNode = maybeVNode(newVNode)), listener, isSvg),
2019-04-26 13:54:44 +02:00
node
2018-07-09 05:34:32 +02:00
)
2019-06-05 20:48:28 +02:00
if (oldVNode != null) {
parent.removeChild(oldVNode.node)
}
2019-04-26 13:54:44 +02:00
} else {
var tmpVKid
var oldVKid
2018-07-09 05:34:32 +02:00
2019-04-26 13:54:44 +02:00
var oldKey
var newKey
2018-07-09 05:34:32 +02:00
2020-05-25 13:59:46 +02:00
var oldProps = oldVNode.props
var newProps = newVNode.props
2018-07-09 05:34:32 +02:00
2019-04-26 13:54:44 +02:00
var oldVKids = oldVNode.children
var newVKids = newVNode.children
2018-07-09 05:34:32 +02:00
2019-04-26 13:54:44 +02:00
var oldHead = 0
var newHead = 0
var oldTail = oldVKids.length - 1
var newTail = newVKids.length - 1
2018-07-09 05:34:32 +02:00
isSvg = isSvg || newVNode.tag === "svg"
2018-07-09 05:34:32 +02:00
2020-05-25 13:59:46 +02:00
for (var i in { ...oldProps, ...newProps }) {
2019-04-26 13:54:44 +02:00
if (
(i === "value" || i === "selected" || i === "checked"
? node[i]
2020-05-25 13:59:46 +02:00
: oldProps[i]) !== newProps[i]
2019-04-26 13:54:44 +02:00
) {
2020-05-25 13:59:46 +02:00
patchProperty(node, i, oldProps[i], newProps[i], listener, isSvg)
2019-04-26 13:54:44 +02:00
}
}
2018-07-09 05:34:32 +02:00
2019-04-26 13:54:44 +02:00
while (newHead <= newTail && oldHead <= oldTail) {
if (
(oldKey = getKey(oldVKids[oldHead])) == null ||
oldKey !== getKey(newVKids[newHead])
) {
break
}
2018-07-09 05:34:32 +02:00
patch(
2019-04-26 13:54:44 +02:00
node,
oldVKids[oldHead].node,
oldVKids[oldHead],
2020-05-21 14:43:46 +02:00
(newVKids[newHead] = maybeVNode(
2019-04-26 13:54:44 +02:00
newVKids[newHead++],
oldVKids[oldHead++]
)),
2019-04-26 13:54:44 +02:00
listener,
2018-07-09 05:34:32 +02:00
isSvg
)
}
2018-03-07 15:04:00 +01:00
2019-04-26 13:54:44 +02:00
while (newHead <= newTail && oldHead <= oldTail) {
if (
(oldKey = getKey(oldVKids[oldTail])) == null ||
oldKey !== getKey(newVKids[newTail])
) {
break
}
patch(
2019-04-26 13:54:44 +02:00
node,
oldVKids[oldTail].node,
oldVKids[oldTail],
2020-05-21 14:43:46 +02:00
(newVKids[newTail] = maybeVNode(
2019-04-26 13:54:44 +02:00
newVKids[newTail--],
oldVKids[oldTail--]
)),
listener,
2018-07-09 05:34:32 +02:00
isSvg
)
}
2019-04-26 13:54:44 +02:00
if (oldHead > oldTail) {
while (newHead <= newTail) {
node.insertBefore(
createNode(
2020-05-21 14:43:46 +02:00
(newVKids[newHead] = maybeVNode(newVKids[newHead++])),
2019-04-26 13:54:44 +02:00
listener,
2018-07-09 05:34:32 +02:00
isSvg
),
2019-04-26 13:54:44 +02:00
(oldVKid = oldVKids[oldHead]) && oldVKid.node
2018-07-09 05:34:32 +02:00
)
}
2019-04-26 13:54:44 +02:00
} else if (newHead > newTail) {
while (oldHead <= oldTail) {
node.removeChild(oldVKids[oldHead++].node)
}
} else {
2020-05-16 10:39:29 +02:00
for (var keyed = {}, newKeyed = {}, i = oldHead; i <= oldTail; i++) {
2019-04-26 13:54:44 +02:00
if ((oldKey = oldVKids[i].key) != null) {
keyed[oldKey] = oldVKids[i]
2019-03-13 06:55:52 +01:00
}
}
2019-03-03 18:21:56 +01:00
2019-04-26 13:54:44 +02:00
while (newHead <= newTail) {
oldKey = getKey((oldVKid = oldVKids[oldHead]))
2019-03-03 18:21:56 +01:00
newKey = getKey(
2020-05-21 14:43:46 +02:00
(newVKids[newHead] = maybeVNode(newVKids[newHead], oldVKid))
)
2018-07-09 05:34:32 +02:00
if (
2019-04-26 13:54:44 +02:00
newKeyed[oldKey] ||
(newKey != null && newKey === getKey(oldVKids[oldHead + 1]))
2018-07-09 05:34:32 +02:00
) {
2019-04-26 13:54:44 +02:00
if (oldKey == null) {
node.removeChild(oldVKid.node)
2018-07-09 05:34:32 +02:00
}
2019-04-26 13:54:44 +02:00
oldHead++
2018-07-09 05:34:32 +02:00
continue
2018-03-07 05:54:08 +01:00
}
if (newKey == null || oldVNode.type === SSR_NODE) {
2019-04-26 13:54:44 +02:00
if (oldKey == null) {
patch(
2019-04-26 13:54:44 +02:00
node,
oldVKid && oldVKid.node,
oldVKid,
newVKids[newHead],
listener,
2018-07-09 05:34:32 +02:00
isSvg
)
2019-04-26 13:54:44 +02:00
newHead++
2018-03-07 05:54:08 +01:00
}
2019-04-26 13:54:44 +02:00
oldHead++
2018-03-07 05:54:08 +01:00
} else {
2019-04-26 13:54:44 +02:00
if (oldKey === newKey) {
patch(
2019-04-26 13:54:44 +02:00
node,
oldVKid.node,
oldVKid,
newVKids[newHead],
listener,
2018-07-09 05:34:32 +02:00
isSvg
)
2019-03-03 18:21:56 +01:00
newKeyed[newKey] = true
2019-04-26 13:54:44 +02:00
oldHead++
2018-07-09 05:34:32 +02:00
} else {
2019-04-26 13:54:44 +02:00
if ((tmpVKid = keyed[newKey]) != null) {
patch(
2019-04-26 13:54:44 +02:00
node,
node.insertBefore(tmpVKid.node, oldVKid && oldVKid.node),
tmpVKid,
newVKids[newHead],
listener,
2018-07-09 05:34:32 +02:00
isSvg
)
2019-03-03 18:21:56 +01:00
newKeyed[newKey] = true
2018-07-09 05:34:32 +02:00
} else {
patch(
2019-04-26 13:54:44 +02:00
node,
oldVKid && oldVKid.node,
2018-07-09 05:34:32 +02:00
null,
2019-04-26 13:54:44 +02:00
newVKids[newHead],
listener,
2018-07-09 05:34:32 +02:00
isSvg
)
}
}
2019-04-26 13:54:44 +02:00
newHead++
2018-03-07 05:54:08 +01:00
}
}
2019-04-26 13:54:44 +02:00
while (oldHead <= oldTail) {
if (getKey((oldVKid = oldVKids[oldHead++])) == null) {
node.removeChild(oldVKid.node)
2018-07-09 05:34:32 +02:00
}
}
2019-04-26 13:54:44 +02:00
for (var i in keyed) {
if (newKeyed[i] == null) {
node.removeChild(keyed[i].node)
2018-07-09 05:34:32 +02:00
}
}
}
}
2019-05-03 01:34:55 +02:00
return (newVNode.node = node)
}
2020-05-25 13:59:46 +02:00
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
}
2020-05-25 13:59:46 +02:00
var maybeVNode = (newVNode, oldVNode) =>
newVNode !== true && newVNode !== false && newVNode
? typeof newVNode.tag === "function"
? ((!oldVNode ||
2020-05-21 14:43:46 +02:00
oldVNode.memo == null ||
propsChanged(oldVNode.memo, newVNode.memo)) &&
((oldVNode = newVNode.tag(newVNode.memo)).memo = newVNode.memo),
oldVNode)
: newVNode
: text("")
2020-05-25 13:59:46 +02:00
var recycleNode = (node) =>
node.nodeType === TEXT_NODE
? text(node.nodeValue, node)
: createVNode(
2019-04-26 13:54:44 +02:00
node.nodeName.toLowerCase(),
EMPTY_OBJ,
map.call(node.childNodes, recycleNode),
SSR_NODE,
node
)
var createVNode = (tag, { key, ...props }, children, type, node) => ({
tag,
2020-07-11 10:11:14 +02:00
props,
key,
2020-07-11 10:11:14 +02:00
children,
type,
node,
2020-07-11 10:11:14 +02:00
})
export var memo = (tag, memo) => ({ tag, memo })
2020-07-11 10:11:14 +02:00
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,
}) => {
2020-05-12 21:35:28 +02:00
var vdom = node && recycleNode(node)
2019-04-26 13:54:44 +02:00
var subs = []
2020-05-12 21:35:28 +02:00
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)
2022-05-12 14:49:41 +02:00
if (view && !busy) requestAnimationFrame(render, (busy = true))
}
2018-07-09 05:34:32 +02:00
}
2020-05-25 13:59:46 +02:00
var render = () =>
(node = patch(
2020-05-16 08:58:14 +02:00
node.parentNode,
node,
vdom,
(vdom = view(state)),
listener,
(busy = false)
2020-05-25 13:59:46 +02:00
))
2018-07-09 05:34:32 +02:00
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(
2022-03-02 13:25:20 +01:00
(fx) => fx && fx !== true && (fx[0] || fx)(dispatch, fx[1]),
update(action[0])
)
: update(action)
))(init),
dispatch
)
}