-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
83 lines (77 loc) · 2.68 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/* global document HTMLElement */
let mydocument, myElement
try {
mydocument = document
myElement = HTMLElement
} catch (_) {}
const isStr = s => typeof s === 'string'
const isFn = f => typeof f === 'function'
const isNum = n => typeof n === 'number'
const nullish = x => [null, undefined, false].includes(x)
function elemFromExpr (expr, document = mydocument, HTMLElement = myElement) {
let [rawtag, props, ...children] = expr
const [subtag, ...subtags] = rawtag.split('>')
const [idtag, id] = subtag.split('#')
const [tag, ...classes] = idtag.split('.')
// it's so hard to say "hey are you a bare object or what"
if (isStr(props) || Array.isArray(props) || props instanceof HTMLElement || isFn(props) || isNum(props) || nullish(props)) {
children = [props, ...children]
props = {}
}
const rootelem = document.createElement(tag)
let elem = rootelem
if (id) elem.setAttribute('id', id)
if (classes.length) elem.setAttribute('class', classes.join(' '))
for (const subtag of subtags) {
const subelem = elemFromExpr([subtag], document, HTMLElement)
elem.appendChild(subelem)
elem = subelem
}
Object.entries(props || {}).forEach(([k, v]) => { k.startsWith('on') ? elem[k] = v : elem.setAttribute(k, v) })
for (const body of children) {
if (nullish(body)) continue
const node = alchemize(body, document, HTMLElement)
elem.appendChild(node)
}
return rootelem
}
export function alchemize (expr, document = mydocument, HTMLElement = myElement) {
if (!Array.isArray(expr)) {
if (expr instanceof HTMLElement) {
return expr
} else if (isFn(expr)) {
return alchemize(expr(), document, HTMLElement)
} else if (isStr(expr)) {
return document.createTextNode(expr)
} else if (isNum(expr)) {
return document.createTextNode(String(expr))
} else {
throw new Error(`What? ${expr}`)
}
} else {
if (expr.length === 0) {
return document.createElement('span')
} else {
if (isStr(expr[0])) {
return elemFromExpr(expr, document, HTMLElement)
} else {
const div = document.createElement('div')
for (const elem of expr.map((x) => alchemize(x, document, HTMLElement))) {
div.appendChild(elem)
}
return div
}
}
}
}
// insert unescaped text content
export const profane = (tagName, safeString, document = mydocument) => {
const elem = document.createElement(tagName)
elem.setHTMLUnsafe(safeString)
return elem
}
// util
export const snag = (elemId, document = mydocument) =>
document.getElementById(elemId)
export const listento = (elemId, eventName, callback, document = mydocument) =>
snag(elemId, document).addEventListener(eventName, callback)