1 | import { vnode } from "./vnode";
|
2 | import * as is from "./is";
|
3 | import { htmlDomApi } from "./htmldomapi";
|
4 | function isUndef(s) {
|
5 | return s === undefined;
|
6 | }
|
7 | function isDef(s) {
|
8 | return s !== undefined;
|
9 | }
|
10 | const emptyNode = vnode("", {}, [], undefined, undefined);
|
11 | function sameVnode(vnode1, vnode2) {
|
12 | var _a, _b;
|
13 | const isSameKey = vnode1.key === vnode2.key;
|
14 | const isSameIs = ((_a = vnode1.data) === null || _a === void 0 ? void 0 : _a.is) === ((_b = vnode2.data) === null || _b === void 0 ? void 0 : _b.is);
|
15 | const isSameSel = vnode1.sel === vnode2.sel;
|
16 | return isSameSel && isSameKey && isSameIs;
|
17 | }
|
18 |
|
19 |
|
20 |
|
21 | function documentFragmentIsNotSupported() {
|
22 | throw new Error("The document fragment is not supported on this platform.");
|
23 | }
|
24 | function isElement(api, vnode) {
|
25 | return api.isElement(vnode);
|
26 | }
|
27 | function isDocumentFragment(api, vnode) {
|
28 | return api.isDocumentFragment(vnode);
|
29 | }
|
30 | function createKeyToOldIdx(children, beginIdx, endIdx) {
|
31 | var _a;
|
32 | const map = {};
|
33 | for (let i = beginIdx; i <= endIdx; ++i) {
|
34 | const key = (_a = children[i]) === null || _a === void 0 ? void 0 : _a.key;
|
35 | if (key !== undefined) {
|
36 | map[key] = i;
|
37 | }
|
38 | }
|
39 | return map;
|
40 | }
|
41 | const hooks = [
|
42 | "create",
|
43 | "update",
|
44 | "remove",
|
45 | "destroy",
|
46 | "pre",
|
47 | "post",
|
48 | ];
|
49 | export function init(modules, domApi, options) {
|
50 | const cbs = {
|
51 | create: [],
|
52 | update: [],
|
53 | remove: [],
|
54 | destroy: [],
|
55 | pre: [],
|
56 | post: [],
|
57 | };
|
58 | const api = domApi !== undefined ? domApi : htmlDomApi;
|
59 | for (const hook of hooks) {
|
60 | for (const module of modules) {
|
61 | const currentHook = module[hook];
|
62 | if (currentHook !== undefined) {
|
63 | cbs[hook].push(currentHook);
|
64 | }
|
65 | }
|
66 | }
|
67 | function emptyNodeAt(elm) {
|
68 | const id = elm.id ? "#" + elm.id : "";
|
69 |
|
70 |
|
71 | const classes = elm.getAttribute("class");
|
72 | const c = classes ? "." + classes.split(" ").join(".") : "";
|
73 | return vnode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm);
|
74 | }
|
75 | function emptyDocumentFragmentAt(frag) {
|
76 | return vnode(undefined, {}, [], undefined, frag);
|
77 | }
|
78 | function createRmCb(childElm, listeners) {
|
79 | return function rmCb() {
|
80 | if (--listeners === 0) {
|
81 | const parent = api.parentNode(childElm);
|
82 | api.removeChild(parent, childElm);
|
83 | }
|
84 | };
|
85 | }
|
86 | function createElm(vnode, insertedVnodeQueue) {
|
87 | var _a, _b, _c, _d;
|
88 | let i;
|
89 | let data = vnode.data;
|
90 | if (data !== undefined) {
|
91 | const init = (_a = data.hook) === null || _a === void 0 ? void 0 : _a.init;
|
92 | if (isDef(init)) {
|
93 | init(vnode);
|
94 | data = vnode.data;
|
95 | }
|
96 | }
|
97 | const children = vnode.children;
|
98 | const sel = vnode.sel;
|
99 | if (sel === "!") {
|
100 | if (isUndef(vnode.text)) {
|
101 | vnode.text = "";
|
102 | }
|
103 | vnode.elm = api.createComment(vnode.text);
|
104 | }
|
105 | else if (sel !== undefined) {
|
106 |
|
107 | const hashIdx = sel.indexOf("#");
|
108 | const dotIdx = sel.indexOf(".", hashIdx);
|
109 | const hash = hashIdx > 0 ? hashIdx : sel.length;
|
110 | const dot = dotIdx > 0 ? dotIdx : sel.length;
|
111 | const tag = hashIdx !== -1 || dotIdx !== -1
|
112 | ? sel.slice(0, Math.min(hash, dot))
|
113 | : sel;
|
114 | const elm = (vnode.elm =
|
115 | isDef(data) && isDef((i = data.ns))
|
116 | ? api.createElementNS(i, tag, data)
|
117 | : api.createElement(tag, data));
|
118 | if (hash < dot)
|
119 | elm.setAttribute("id", sel.slice(hash + 1, dot));
|
120 | if (dotIdx > 0)
|
121 | elm.setAttribute("class", sel.slice(dot + 1).replace(/\./g, " "));
|
122 | for (i = 0; i < cbs.create.length; ++i)
|
123 | cbs.create[i](emptyNode, vnode);
|
124 | if (is.array(children)) {
|
125 | for (i = 0; i < children.length; ++i) {
|
126 | const ch = children[i];
|
127 | if (ch != null) {
|
128 | api.appendChild(elm, createElm(ch, insertedVnodeQueue));
|
129 | }
|
130 | }
|
131 | }
|
132 | else if (is.primitive(vnode.text)) {
|
133 | api.appendChild(elm, api.createTextNode(vnode.text));
|
134 | }
|
135 | const hook = vnode.data.hook;
|
136 | if (isDef(hook)) {
|
137 | (_b = hook.create) === null || _b === void 0 ? void 0 : _b.call(hook, emptyNode, vnode);
|
138 | if (hook.insert) {
|
139 | insertedVnodeQueue.push(vnode);
|
140 | }
|
141 | }
|
142 | }
|
143 | else if (((_c = options === null || options === void 0 ? void 0 : options.experimental) === null || _c === void 0 ? void 0 : _c.fragments) && vnode.children) {
|
144 | const children = vnode.children;
|
145 | vnode.elm = ((_d = api.createDocumentFragment) !== null && _d !== void 0 ? _d : documentFragmentIsNotSupported)();
|
146 | for (i = 0; i < cbs.create.length; ++i)
|
147 | cbs.create[i](emptyNode, vnode);
|
148 | for (i = 0; i < children.length; ++i) {
|
149 | const ch = children[i];
|
150 | if (ch != null) {
|
151 | api.appendChild(vnode.elm, createElm(ch, insertedVnodeQueue));
|
152 | }
|
153 | }
|
154 | }
|
155 | else {
|
156 | vnode.elm = api.createTextNode(vnode.text);
|
157 | }
|
158 | return vnode.elm;
|
159 | }
|
160 | function addVnodes(parentElm, before, vnodes, startIdx, endIdx, insertedVnodeQueue) {
|
161 | for (; startIdx <= endIdx; ++startIdx) {
|
162 | const ch = vnodes[startIdx];
|
163 | if (ch != null) {
|
164 | api.insertBefore(parentElm, createElm(ch, insertedVnodeQueue), before);
|
165 | }
|
166 | }
|
167 | }
|
168 | function invokeDestroyHook(vnode) {
|
169 | var _a, _b;
|
170 | const data = vnode.data;
|
171 | if (data !== undefined) {
|
172 | (_b = (_a = data === null || data === void 0 ? void 0 : data.hook) === null || _a === void 0 ? void 0 : _a.destroy) === null || _b === void 0 ? void 0 : _b.call(_a, vnode);
|
173 | for (let i = 0; i < cbs.destroy.length; ++i)
|
174 | cbs.destroy[i](vnode);
|
175 | if (vnode.children !== undefined) {
|
176 | for (let j = 0; j < vnode.children.length; ++j) {
|
177 | const child = vnode.children[j];
|
178 | if (child != null && typeof child !== "string") {
|
179 | invokeDestroyHook(child);
|
180 | }
|
181 | }
|
182 | }
|
183 | }
|
184 | }
|
185 | function removeVnodes(parentElm, vnodes, startIdx, endIdx) {
|
186 | var _a, _b;
|
187 | for (; startIdx <= endIdx; ++startIdx) {
|
188 | let listeners;
|
189 | let rm;
|
190 | const ch = vnodes[startIdx];
|
191 | if (ch != null) {
|
192 | if (isDef(ch.sel)) {
|
193 | invokeDestroyHook(ch);
|
194 | listeners = cbs.remove.length + 1;
|
195 | rm = createRmCb(ch.elm, listeners);
|
196 | for (let i = 0; i < cbs.remove.length; ++i)
|
197 | cbs.remove[i](ch, rm);
|
198 | const removeHook = (_b = (_a = ch === null || ch === void 0 ? void 0 : ch.data) === null || _a === void 0 ? void 0 : _a.hook) === null || _b === void 0 ? void 0 : _b.remove;
|
199 | if (isDef(removeHook)) {
|
200 | removeHook(ch, rm);
|
201 | }
|
202 | else {
|
203 | rm();
|
204 | }
|
205 | }
|
206 | else {
|
207 |
|
208 | api.removeChild(parentElm, ch.elm);
|
209 | }
|
210 | }
|
211 | }
|
212 | }
|
213 | function updateChildren(parentElm, oldCh, newCh, insertedVnodeQueue) {
|
214 | let oldStartIdx = 0;
|
215 | let newStartIdx = 0;
|
216 | let oldEndIdx = oldCh.length - 1;
|
217 | let oldStartVnode = oldCh[0];
|
218 | let oldEndVnode = oldCh[oldEndIdx];
|
219 | let newEndIdx = newCh.length - 1;
|
220 | let newStartVnode = newCh[0];
|
221 | let newEndVnode = newCh[newEndIdx];
|
222 | let oldKeyToIdx;
|
223 | let idxInOld;
|
224 | let elmToMove;
|
225 | let before;
|
226 | while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
|
227 | if (oldStartVnode == null) {
|
228 | oldStartVnode = oldCh[++oldStartIdx];
|
229 | }
|
230 | else if (oldEndVnode == null) {
|
231 | oldEndVnode = oldCh[--oldEndIdx];
|
232 | }
|
233 | else if (newStartVnode == null) {
|
234 | newStartVnode = newCh[++newStartIdx];
|
235 | }
|
236 | else if (newEndVnode == null) {
|
237 | newEndVnode = newCh[--newEndIdx];
|
238 | }
|
239 | else if (sameVnode(oldStartVnode, newStartVnode)) {
|
240 | patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue);
|
241 | oldStartVnode = oldCh[++oldStartIdx];
|
242 | newStartVnode = newCh[++newStartIdx];
|
243 | }
|
244 | else if (sameVnode(oldEndVnode, newEndVnode)) {
|
245 | patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue);
|
246 | oldEndVnode = oldCh[--oldEndIdx];
|
247 | newEndVnode = newCh[--newEndIdx];
|
248 | }
|
249 | else if (sameVnode(oldStartVnode, newEndVnode)) {
|
250 |
|
251 | patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue);
|
252 | api.insertBefore(parentElm, oldStartVnode.elm, api.nextSibling(oldEndVnode.elm));
|
253 | oldStartVnode = oldCh[++oldStartIdx];
|
254 | newEndVnode = newCh[--newEndIdx];
|
255 | }
|
256 | else if (sameVnode(oldEndVnode, newStartVnode)) {
|
257 |
|
258 | patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
|
259 | api.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
|
260 | oldEndVnode = oldCh[--oldEndIdx];
|
261 | newStartVnode = newCh[++newStartIdx];
|
262 | }
|
263 | else {
|
264 | if (oldKeyToIdx === undefined) {
|
265 | oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
|
266 | }
|
267 | idxInOld = oldKeyToIdx[newStartVnode.key];
|
268 | if (isUndef(idxInOld)) {
|
269 |
|
270 | api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm);
|
271 | }
|
272 | else {
|
273 | elmToMove = oldCh[idxInOld];
|
274 | if (elmToMove.sel !== newStartVnode.sel) {
|
275 | api.insertBefore(parentElm, createElm(newStartVnode, insertedVnodeQueue), oldStartVnode.elm);
|
276 | }
|
277 | else {
|
278 | patchVnode(elmToMove, newStartVnode, insertedVnodeQueue);
|
279 | oldCh[idxInOld] = undefined;
|
280 | api.insertBefore(parentElm, elmToMove.elm, oldStartVnode.elm);
|
281 | }
|
282 | }
|
283 | newStartVnode = newCh[++newStartIdx];
|
284 | }
|
285 | }
|
286 | if (newStartIdx <= newEndIdx) {
|
287 | before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm;
|
288 | addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
|
289 | }
|
290 | if (oldStartIdx <= oldEndIdx) {
|
291 | removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
|
292 | }
|
293 | }
|
294 | function patchVnode(oldVnode, vnode, insertedVnodeQueue) {
|
295 | var _a, _b, _c, _d, _e;
|
296 | const hook = (_a = vnode.data) === null || _a === void 0 ? void 0 : _a.hook;
|
297 | (_b = hook === null || hook === void 0 ? void 0 : hook.prepatch) === null || _b === void 0 ? void 0 : _b.call(hook, oldVnode, vnode);
|
298 | const elm = (vnode.elm = oldVnode.elm);
|
299 | const oldCh = oldVnode.children;
|
300 | const ch = vnode.children;
|
301 | if (oldVnode === vnode)
|
302 | return;
|
303 | if (vnode.data !== undefined) {
|
304 | for (let i = 0; i < cbs.update.length; ++i)
|
305 | cbs.update[i](oldVnode, vnode);
|
306 | (_d = (_c = vnode.data.hook) === null || _c === void 0 ? void 0 : _c.update) === null || _d === void 0 ? void 0 : _d.call(_c, oldVnode, vnode);
|
307 | }
|
308 | if (isUndef(vnode.text)) {
|
309 | if (isDef(oldCh) && isDef(ch)) {
|
310 | if (oldCh !== ch)
|
311 | updateChildren(elm, oldCh, ch, insertedVnodeQueue);
|
312 | }
|
313 | else if (isDef(ch)) {
|
314 | if (isDef(oldVnode.text))
|
315 | api.setTextContent(elm, "");
|
316 | addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
|
317 | }
|
318 | else if (isDef(oldCh)) {
|
319 | removeVnodes(elm, oldCh, 0, oldCh.length - 1);
|
320 | }
|
321 | else if (isDef(oldVnode.text)) {
|
322 | api.setTextContent(elm, "");
|
323 | }
|
324 | }
|
325 | else if (oldVnode.text !== vnode.text) {
|
326 | if (isDef(oldCh)) {
|
327 | removeVnodes(elm, oldCh, 0, oldCh.length - 1);
|
328 | }
|
329 | api.setTextContent(elm, vnode.text);
|
330 | }
|
331 | (_e = hook === null || hook === void 0 ? void 0 : hook.postpatch) === null || _e === void 0 ? void 0 : _e.call(hook, oldVnode, vnode);
|
332 | }
|
333 | return function patch(oldVnode, vnode) {
|
334 | let i, elm, parent;
|
335 | const insertedVnodeQueue = [];
|
336 | for (i = 0; i < cbs.pre.length; ++i)
|
337 | cbs.pre[i]();
|
338 | if (isElement(api, oldVnode)) {
|
339 | oldVnode = emptyNodeAt(oldVnode);
|
340 | }
|
341 | else if (isDocumentFragment(api, oldVnode)) {
|
342 | oldVnode = emptyDocumentFragmentAt(oldVnode);
|
343 | }
|
344 | if (sameVnode(oldVnode, vnode)) {
|
345 | patchVnode(oldVnode, vnode, insertedVnodeQueue);
|
346 | }
|
347 | else {
|
348 | elm = oldVnode.elm;
|
349 | parent = api.parentNode(elm);
|
350 | createElm(vnode, insertedVnodeQueue);
|
351 | if (parent !== null) {
|
352 | api.insertBefore(parent, vnode.elm, api.nextSibling(elm));
|
353 | removeVnodes(parent, [oldVnode], 0, 0);
|
354 | }
|
355 | }
|
356 | for (i = 0; i < insertedVnodeQueue.length; ++i) {
|
357 | insertedVnodeQueue[i].data.hook.insert(insertedVnodeQueue[i]);
|
358 | }
|
359 | for (i = 0; i < cbs.post.length; ++i)
|
360 | cbs.post[i]();
|
361 | return vnode;
|
362 | };
|
363 | }
|
364 |
|
\ | No newline at end of file |