* Implement support for variadic arguments to `m.fragment` While I was at it, I refactored the common logic out of `hyperscript`. * Add a missed change from #2326 * Update docs + changelog [skip ci] * Explain rationale for `hyperscriptVnode`'s calling convention This way, it doesn't get erroneously "cleaned up" into something worse, and so it's clearer how it'd be potentially optimized once ES5 support is dropped.
100 lines
2.8 KiB
JavaScript
100 lines
2.8 KiB
JavaScript
"use strict"
|
|
|
|
var Vnode = require("../render/vnode")
|
|
var hyperscriptVnode = require("./hyperscriptVnode")
|
|
|
|
var selectorParser = /(?:(^|#|\.)([^#\.\[\]]+))|(\[(.+?)(?:\s*=\s*("|'|)((?:\\["'\]]|.)*?)\5)?\])/g
|
|
var selectorCache = {}
|
|
var hasOwn = {}.hasOwnProperty
|
|
|
|
function isEmpty(object) {
|
|
for (var key in object) if (hasOwn.call(object, key)) return false
|
|
return true
|
|
}
|
|
|
|
function compileSelector(selector) {
|
|
var match, tag = "div", classes = [], attrs = {}
|
|
while (match = selectorParser.exec(selector)) {
|
|
var type = match[1], value = match[2]
|
|
if (type === "" && value !== "") tag = value
|
|
else if (type === "#") attrs.id = value
|
|
else if (type === ".") classes.push(value)
|
|
else if (match[3][0] === "[") {
|
|
var attrValue = match[6]
|
|
if (attrValue) attrValue = attrValue.replace(/\\(["'])/g, "$1").replace(/\\\\/g, "\\")
|
|
if (match[4] === "class") classes.push(attrValue)
|
|
else attrs[match[4]] = attrValue === "" ? attrValue : attrValue || true
|
|
}
|
|
}
|
|
if (classes.length > 0) attrs.className = classes.join(" ")
|
|
return selectorCache[selector] = {tag: tag, attrs: attrs}
|
|
}
|
|
|
|
function execSelector(state, vnode) {
|
|
var attrs = vnode.attrs
|
|
var children = Vnode.normalizeChildren(vnode.children)
|
|
var hasClass = hasOwn.call(attrs, "class")
|
|
var className = hasClass ? attrs.class : attrs.className
|
|
|
|
vnode.tag = state.tag
|
|
vnode.attrs = null
|
|
vnode.children = undefined
|
|
|
|
if (!isEmpty(state.attrs) && !isEmpty(attrs)) {
|
|
var newAttrs = {}
|
|
|
|
for (var key in attrs) {
|
|
if (hasOwn.call(attrs, key)) newAttrs[key] = attrs[key]
|
|
}
|
|
|
|
attrs = newAttrs
|
|
}
|
|
|
|
for (var key in state.attrs) {
|
|
if (hasOwn.call(state.attrs, key) && key !== "className" && !hasOwn.call(attrs, key)){
|
|
attrs[key] = state.attrs[key]
|
|
}
|
|
}
|
|
if (className != null || state.attrs.className != null) attrs.className =
|
|
className != null
|
|
? state.attrs.className != null
|
|
? String(state.attrs.className) + " " + String(className)
|
|
: className
|
|
: state.attrs.className != null
|
|
? state.attrs.className
|
|
: null
|
|
|
|
if (hasClass) attrs.class = null
|
|
|
|
for (var key in attrs) {
|
|
if (hasOwn.call(attrs, key) && key !== "key") {
|
|
vnode.attrs = attrs
|
|
break
|
|
}
|
|
}
|
|
|
|
if (Array.isArray(children) && children.length === 1 && children[0] != null && children[0].tag === "#") {
|
|
vnode.text = children[0].children
|
|
} else {
|
|
vnode.children = children
|
|
}
|
|
|
|
return vnode
|
|
}
|
|
|
|
function hyperscript(selector) {
|
|
if (selector == null || typeof selector !== "string" && typeof selector !== "function" && typeof selector.view !== "function") {
|
|
throw Error("The selector must be either a string or a component.");
|
|
}
|
|
|
|
var vnode = hyperscriptVnode.apply(1, arguments)
|
|
|
|
if (typeof selector === "string") {
|
|
return execSelector(selectorCache[selector] || compileSelector(selector), vnode)
|
|
} else {
|
|
vnode.tag = selector
|
|
return vnode
|
|
}
|
|
}
|
|
|
|
module.exports = hyperscript
|