-
Notifications
You must be signed in to change notification settings - Fork 0
/
in-html.js
134 lines (119 loc) · 3.34 KB
/
in-html.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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
TODO Escape
https://html.spec.whatwg.org/multipage/named-characters.html#named-character-references
& => &
< => <
> => >
" => "
*/
/**
* tagged template litteral tag`text` => 'text'
* @param {Array} strings
* @returns {string}
*/
export function tag(strings) {
let t = strings[0]
for (let i=1; i<arguments.length; ++i) t += arguments[i] + strings[i]
return t
}
/**
* querySelector $`sel` | $(`sel`) | $(`sel`, parent)
* @param {string|Array} selector
* @param {Node} element
* @returns {Element}
*/
export function $(selector, parent=document) {
return parent.querySelector( Array.isArray(selector) ? tag.apply(null, arguments) : selector)
}
/**
* querySelectorAll $$`sel` | $$(`sel`) | $$(`sel`, parent)
* @param {string|Array} selector
* @param {Node} parent
* @returns {Array<Element>}
*/
export function $$(selector, parent=document) {
return parent.querySelectorAll(Array.isArray(selector) ? tag.apply(null, arguments) : selector)
}
/**
* html`text` | html(`text`)
* @param {string|Array} txt
* @returns {DocumentFragment}
*/
export function html(txt) {
return document.createRange().createContextualFragment(
Array.isArray(txt) ? tag.apply(null, arguments) : txt
)
}
/**
* load(`./text.html`)
* @param {string} txt
* @returns {Promise<DocumentFragment>}
*/
export function load(path) {
return fetch(path).then( res => res.text().then( txt => html( txt ) ) )
}
function getNode(selection) {
const node = selection.nodeName ? selection : selection[0] === '<' ? html(selection) : $(selection)
return node.content || node
}
function $ids(el) {
const ids = Object.create(null)
if (el.id) (ids[el.id] = el).removeAttribute('id')
for (const kid of el.querySelectorAll('[id]')) (ids[kid.id] = kid).removeAttribute('id')
return ids
}
/**
* @param {string|Element|DocumentFragment} template
* @param {Function} decorator
* @returns {Function}
*/
export function cast(template, decorator) {
const model = getNode(template)
return function(v,k) {
const el = model.cloneNode(true),
res = decorator.call(this, $ids(el), v, k)
return res?.nodeType ? res : el
}
}
/**
* @param {string|Element|DocumentFragment} parent
* @param {Function} factory
* @param {Object} param2
* @returns {Element|DocumentFragment}
*/
export function list(parent, factory, { getKey, after=null, before=null }={} ) {
const kin = getNode(parent)
let last = Object.create(null),
updater = kin.update
kin.update = !updater ? updateList
: function(...args) { updateList.call(this, ...args); updater.call(this, ...args) }
function updateList(arr) {
const kids = Object.create(null)
let spot = after ? after.nextSibling : kin.firstChild
if (!arr.length && !before && !after) kin.textContent = ''
else {
for (let i = 0; i < arr.length; ++i) {
const key = getKey?.(arr[i], i, arr) || i
let kid = last[key]
//create or update kid
if (kid) kid.update && kid.update(arr[i], key, arr)
else kid = factory(arr[i], i, arr)
kids[key] = kid
//place kid
if (!spot) kin.appendChild(kid)
else if (kid === spot.nextSibling) kin.removeChild(spot)
else if (kid !== spot) kin.insertBefore(kid, spot)
spot = kid.nextSibling
}
//delete remaining
while (spot !== before) {
const next = spot.nextSibling
kin.removeChild(spot)
spot = next
}
}
last = kids
return this
}
return kin
}