closures 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,759 @@
1
+ let EMPTY_OBJECT = {},
2
+ REF_SINGLE = 1, // ref with a single dom node
3
+ REF_ARRAY = 4, // ref with an array od nodes
4
+ REF_PARENT = 8, // ref with a child ref
5
+ SVG_NS = "http://www.w3.org/2000/svg",
6
+ DOM_PROPS_DIRECTIVES = {
7
+ selected: propDirective("selected"),
8
+ checked: propDirective("checked"),
9
+ value: propDirective("value"),
10
+ innerHTML: propDirective("innerHTML"),
11
+ },
12
+ DEFAULT_ENV = {
13
+ isSvg: false,
14
+ directives: DOM_PROPS_DIRECTIVES,
15
+ },
16
+ ON_REMOVES = [],
17
+ MOUNTING = [],
18
+ XLINK_NS = "http://www.w3.org/1999/xlink",
19
+ NS_ATTRS = { show: XLINK_NS, actuate: XLINK_NS, href: XLINK_NS },
20
+ NUM = 1,
21
+ VTYPE_ELEMENT = 1,
22
+ VTYPE_FUNCTION = 2,
23
+ VTYPE_COMPONENT = 4,
24
+ CLOSURE_TO_CMP = new WeakMap(),
25
+ CMP_TO_CLOSURE = new WeakMap(),
26
+ CHILDS = new WeakMap(),
27
+ ID = _ => (NUM++).toString(),
28
+ noop = _ => {},
29
+ isFn = x => typeof x === 'function',
30
+ isStr = x => typeof x === 'string',
31
+ isObj = x => x !== null && typeof x === 'object',
32
+ isArr = x => Array.isArray(x),
33
+ isEmpty = c => c === null || c === false || c === undefined || (isArr(c) && c.length === 0),
34
+ isNonEmptyArray = c => isArr(c) && c.length > 0,
35
+ isLeaf = c => isStr(c) || typeof c === "number",
36
+ isElement = c => c && c.vtype === VTYPE_ELEMENT,
37
+ isRenderFunction = c => c && c.vtype === VTYPE_FUNCTION,
38
+ isComponent = c => c && c.vtype === VTYPE_COMPONENT,
39
+ isValidComponentType = c => c && isFn(c.mount);
40
+
41
+ export const m = h;
42
+
43
+ export const onRemove = f => ON_REMOVES.push(f);
44
+
45
+ export function h(__tag, ...children) {
46
+ let props = children[0];
47
+ if (props && isObj(props) && !isArr(props) && !(props.__tag || props.props))
48
+ children.shift();
49
+ else props = EMPTY_OBJECT;
50
+
51
+ props =
52
+ children.length > 1
53
+ ? Object.assign({}, props, { children })
54
+ : children.length === 1
55
+ ? Object.assign({}, props, { children: children[0] })
56
+ : props;
57
+
58
+ // parse tag
59
+ if (isStr(__tag)) {
60
+ let idx = __tag.indexOf('.');
61
+ if (~idx) {
62
+ let className = __tag.slice(idx + 1).replace(/\./g, ' ')
63
+ props.class = props.class ? className + ' ' + String(props.class) : className;
64
+ __tag = __tag.slice(0, idx);
65
+ }
66
+ __tag = __tag ?? 'div';
67
+ }
68
+
69
+ return jsx(__tag, props, props.key);
70
+ }
71
+
72
+ export function jsx(__tag, props, key) {
73
+ if (key !== key) throw new Error("Invalid NaN key");
74
+ let vtype =
75
+ isStr(__tag)
76
+ ? VTYPE_ELEMENT
77
+ : isValidComponentType(__tag)
78
+ ? VTYPE_COMPONENT
79
+ : isFn(__tag)
80
+ ? VTYPE_FUNCTION
81
+ : undefined;
82
+ if (vtype === undefined) throw new Error("Invalid VNode type");
83
+ return {
84
+ vtype,
85
+ __tag,
86
+ key,
87
+ props,
88
+ };
89
+ }
90
+
91
+ export const Fragment = props => props.children;
92
+
93
+ export function render(vnode, parentDomNode, options = {}) {
94
+ let rootRef = parentDomNode.$$PETIT_DOM_REF;
95
+ let env = Object.assign({}, DEFAULT_ENV);
96
+ Object.assign(env.directives, options.directives);
97
+ if (rootRef == null) {
98
+ let ref = mount(vnode, env);
99
+ parentDomNode.$$PETIT_DOM_REF = { ref, vnode };
100
+ parentDomNode.textContent = "";
101
+ insertDom(parentDomNode, ref, null);
102
+ } else {
103
+ rootRef.ref = patchInPlace(
104
+ parentDomNode,
105
+ vnode,
106
+ rootRef.vnode,
107
+ rootRef.ref,
108
+ env
109
+ );
110
+ rootRef.vnode = vnode;
111
+ }
112
+ }
113
+
114
+ // non exports
115
+ class Renderer {
116
+ constructor(props, env) {
117
+ this.props = props;
118
+ this._STATE_ = {
119
+ env,
120
+ vnode: null,
121
+ parentDomNode: null,
122
+ ref: mount(null)
123
+ };
124
+ this.render = this.render.bind(this);
125
+ }
126
+
127
+ setProps(props) {
128
+ this.oldProps = this.props;
129
+ this.props = props;
130
+ }
131
+
132
+ render(vnode) {
133
+ let state = this._STATE_;
134
+ let oldVNode = state.vnode;
135
+ state.vnode = vnode;
136
+ if (state.parentDomNode === null) {
137
+ let parentNode = getParentNode(state.ref);
138
+ if (parentNode === null) {
139
+ state.ref = mount(vnode, state.env);
140
+ return;
141
+ } else {
142
+ state.parentDomNode = parentNode;
143
+ }
144
+ }
145
+ // here we are sure state.parentDOMNode is defined
146
+ state.ref = patchInPlace(
147
+ state.parentDomNode,
148
+ vnode,
149
+ oldVNode,
150
+ state.ref,
151
+ state.env
152
+ );
153
+ }
154
+ }
155
+
156
+ function mount(vnode, env = DEFAULT_ENV) {
157
+ if (isEmpty(vnode)) {
158
+ return {
159
+ type: REF_SINGLE,
160
+ node: document.createComment("NULL"),
161
+ };
162
+ } else if (isLeaf(vnode)) {
163
+ return {
164
+ type: REF_SINGLE,
165
+ node: document.createTextNode(vnode),
166
+ };
167
+ } else if (isElement(vnode)) {
168
+ let node;
169
+ let { __tag, props } = vnode;
170
+ if (__tag === "svg" && !env.isSvg) {
171
+ env = Object.assign({}, env, { isSVG: true });
172
+ }
173
+ // TODO : {is} for custom elements
174
+ if (!env.isSVG) {
175
+ node = document.createElement(__tag);
176
+ } else {
177
+ node = document.createElementNS(SVG_NS, __tag);
178
+ }
179
+
180
+ // element lifecycle hooks
181
+ if (isFn(props.oncreate)) props.oncreate(node);
182
+
183
+ mountAttributes(node, props, env);
184
+ let childrenRef =
185
+ props.children == null ? props.children : mount(props.children, env);
186
+ /**
187
+ * We need to insert content before setting interactive props
188
+ * that rely on children been present (e.g select)
189
+ */
190
+ if (childrenRef != null) insertDom(node, childrenRef);
191
+ mountDirectives(node, props, env);
192
+ return {
193
+ type: REF_SINGLE,
194
+ node,
195
+ children: childrenRef,
196
+ };
197
+ } else if (isNonEmptyArray(vnode)) {
198
+ return {
199
+ type: REF_ARRAY,
200
+ children: vnode.map((child) => mount(child, env)),
201
+ };
202
+ } else if (isRenderFunction(vnode)) {
203
+ let childVNode = vnode.__tag(vnode.props);
204
+
205
+ if (isFn(childVNode)) {
206
+ let cmp,
207
+ view = childVNode,
208
+ id = ID(),
209
+ cmps = CLOSURE_TO_CMP.get(vnode.__tag) ?? {};
210
+
211
+ vnode.id = id;
212
+ cmp = toClosureCmp(vnode, view)
213
+
214
+ cmps[id] = cmp;
215
+ CLOSURE_TO_CMP.set(vnode.__tag, cmps);
216
+ CMP_TO_CLOSURE.set(cmp, vnode.__tag);
217
+ return mount(cmp);
218
+ }
219
+
220
+ let childRef = mount(childVNode, env);
221
+ return {
222
+ type: REF_PARENT,
223
+ childRef,
224
+ childState: childVNode,
225
+ };
226
+ } else if (isComponent(vnode)) {
227
+ let renderer = new Renderer(vnode.props, env);
228
+
229
+ let parentCmp;
230
+ if (parentCmp = MOUNTING[MOUNTING.length - 1]) {
231
+ let child_closure = CMP_TO_CLOSURE.get(vnode),
232
+ childs = CHILDS.get(parentCmp) || [],
233
+ idx = childs.length;
234
+
235
+ // attach method to remove self from parent
236
+ vnode.__tag.removeFromParent = _ => childs.splice(idx, 1);
237
+
238
+ childs.push({
239
+ id: vnode.id,
240
+ vtype: VTYPE_FUNCTION,
241
+ __tag: child_closure
242
+ });
243
+
244
+ CHILDS.set(parentCmp, childs);
245
+ }
246
+
247
+ vnode.__tag.mount(renderer);
248
+ return {
249
+ type: REF_PARENT,
250
+ childRef: renderer._STATE_.ref,
251
+ childState: renderer,
252
+ };
253
+ } else if (vnode instanceof Node) {
254
+ return {
255
+ type: REF_SINGLE,
256
+ node: vnode,
257
+ };
258
+ }
259
+ if (vnode === undefined) {
260
+ throw new Error("mount: vnode is undefined!");
261
+ }
262
+
263
+ throw new Error("mount: Invalid Vnode!");
264
+ }
265
+
266
+ function patch(
267
+ parentDomNode,
268
+ newVNode,
269
+ oldVNode,
270
+ ref,
271
+ env = DEFAULT_ENV
272
+ ) {
273
+ if (isObj(oldVNode) && isObj(newVNode))
274
+ newVNode.id = oldVNode.id;
275
+
276
+ if (oldVNode === newVNode) {
277
+ return ref;
278
+ } else if (isEmpty(newVNode) && isEmpty(oldVNode)) {
279
+ return ref;
280
+ } else if (isLeaf(newVNode) && isLeaf(oldVNode)) {
281
+ ref.node.nodeValue = newVNode;
282
+ return ref;
283
+ } else if (
284
+ isElement(newVNode) &&
285
+ isElement(oldVNode) &&
286
+ newVNode.__tag === oldVNode.__tag
287
+ ) {
288
+ if (newVNode.__tag === "svg" && !env.isSvg) {
289
+ env = Object.assign({}, env, { isSVG: true });
290
+ }
291
+ patchAttributes(ref.node, newVNode.props, oldVNode.props, env);
292
+ let oldChildren = oldVNode.props.children;
293
+ let newChildren = newVNode.props.children;
294
+ if (oldChildren == null) {
295
+ if (newChildren != null) {
296
+ ref.children = mount(newChildren, env);
297
+ insertDom(ref.node, ref.children);
298
+ }
299
+ } else {
300
+ if (newChildren == null) {
301
+ ref.node.textContent = "";
302
+ unmount(oldChildren, ref.children, env);
303
+ ref.children = null;
304
+ } else {
305
+ ref.children = patchInPlace(
306
+ ref.node,
307
+ newChildren,
308
+ oldChildren,
309
+ ref.children,
310
+ env
311
+ );
312
+ }
313
+ }
314
+ patchDirectives(ref.node, newVNode.props, oldVNode.props, env);
315
+ return ref;
316
+ } else if (isNonEmptyArray(newVNode) && isNonEmptyArray(oldVNode)) {
317
+ patchChildren(parentDomNode, newVNode, oldVNode, ref, env);
318
+ return ref;
319
+ } else if (
320
+ isRenderFunction(newVNode) &&
321
+ isRenderFunction(oldVNode) &&
322
+ newVNode.__tag === oldVNode.__tag
323
+ ) {
324
+ let renderFn = newVNode.__tag;
325
+ let shouldUpdate =
326
+ renderFn.shouldUpdate != null
327
+ ? renderFn.shouldUpdate(oldVNode.props, newVNode.props)
328
+ : defaultShouldUpdate(oldVNode.props, newVNode.props);
329
+ if (shouldUpdate) {
330
+ let cmp,
331
+ id = oldVNode.id,
332
+ cmps = CLOSURE_TO_CMP.get(renderFn);
333
+
334
+ if (cmps && id && (cmp = cmps[id])) {
335
+ return patch(
336
+ parentDomNode,
337
+ { ...cmp, props: newVNode.props },
338
+ cmp,
339
+ ref
340
+ );
341
+ }
342
+
343
+ let childVNode = renderFn(newVNode.props);
344
+ let childRef = patch(
345
+ parentDomNode,
346
+ childVNode,
347
+ ref.childState,
348
+ ref.childRef,
349
+ env
350
+ );
351
+ // We need to return a new ref in order for parent patches to
352
+ // properly replace changing DOM nodes
353
+ if (childRef !== ref.childRef) {
354
+ return {
355
+ type: REF_PARENT,
356
+ childRef,
357
+ childState: childVNode,
358
+ };
359
+ } else {
360
+ ref.childState = childVNode;
361
+ return ref;
362
+ }
363
+ } else {
364
+ return ref;
365
+ }
366
+ } else if (
367
+ isComponent(newVNode) &&
368
+ isComponent(oldVNode) &&
369
+ newVNode.__tag === oldVNode.__tag
370
+ ) {
371
+ let renderer = ref.childState;
372
+ let state = renderer._STATE_;
373
+ state.env = env;
374
+ state.parentNode = parentDomNode;
375
+ renderer.setProps(newVNode.props);
376
+ newVNode.__tag.patch(renderer);
377
+ if (ref.childRef !== state.ref) {
378
+ return {
379
+ type: REF_PARENT,
380
+ childRef: state.ref,
381
+ childState: renderer,
382
+ };
383
+ } else {
384
+ return ref;
385
+ }
386
+ } else if (newVNode instanceof Node && oldVNode instanceof Node) {
387
+ ref.node = newVNode;
388
+ return ref;
389
+ } else {
390
+ return mount(newVNode, env);
391
+ }
392
+ }
393
+
394
+ /**
395
+ * Execute any compoenent specific unmount code
396
+ */
397
+ function unmount(vnode, ref, env) {
398
+ // if (vnode instanceof Node || isEmpty(vnode) || isLeaf(vnode)) return;
399
+ if (isElement(vnode)) {
400
+ unmountDirectives(ref.node, vnode.props, env);
401
+ if (vnode.props.children != null)
402
+ unmount(vnode.props.children, ref.children, env);
403
+ } else if (isNonEmptyArray(vnode)) {
404
+ vnode.forEach((childVNode, index) =>
405
+ unmount(childVNode, ref.children[index], env)
406
+ );
407
+ } else if (isRenderFunction(vnode)) {
408
+ let cmp, cmps = CLOSURE_TO_CMP.get(vnode.__tag);
409
+ if (cmps && vnode.id && (cmp = cmps[vnode.id])) {
410
+ delete cmps[vnode.id];
411
+ CMP_TO_CLOSURE.delete(cmp);
412
+ return unmount(cmp, ref, env);
413
+ }
414
+
415
+ unmount(ref.childState, ref.childRef, env);
416
+ } else if (isComponent(vnode)) {
417
+ vnode.__tag.unmount(ref.childState);
418
+ }
419
+ }
420
+
421
+ function patchInPlace(parentDomNode, newVNode, oldVNode, ref, env) {
422
+ let newRef = patch(parentDomNode, newVNode, oldVNode, ref, env);
423
+ if (newRef !== ref) {
424
+ replaceDom(parentDomNode, newRef, ref);
425
+ unmount(oldVNode, ref, env);
426
+ }
427
+ return newRef;
428
+ }
429
+
430
+ function patchChildren(parentDomNode, newChildren, oldchildren, ref, env) {
431
+ // We need to retreive the next sibling before the old children
432
+ // get eventually removed from the current DOM document
433
+ let nextNode = getNextSibling(ref);
434
+ let children = Array(newChildren.length);
435
+ let refChildren = ref.children;
436
+ let newStart = 0,
437
+ oldStart = 0,
438
+ newEnd = newChildren.length - 1,
439
+ oldEnd = oldchildren.length - 1;
440
+ let oldVNode, newVNode, oldRef, newRef, refMap;
441
+
442
+ while (newStart <= newEnd && oldStart <= oldEnd) {
443
+ if (refChildren[oldStart] === null) {
444
+ oldStart++;
445
+ continue;
446
+ }
447
+ if (refChildren[oldEnd] === null) {
448
+ oldEnd--;
449
+ continue;
450
+ }
451
+
452
+ oldVNode = oldchildren[oldStart];
453
+ newVNode = newChildren[newStart];
454
+ if (newVNode.key === oldVNode.key) {
455
+ oldRef = refChildren[oldStart];
456
+ newRef = children[newStart] = patchInPlace(
457
+ parentDomNode,
458
+ newVNode,
459
+ oldVNode,
460
+ oldRef,
461
+ env
462
+ );
463
+ newStart++;
464
+ oldStart++;
465
+ continue;
466
+ }
467
+
468
+ oldVNode = oldchildren[oldEnd];
469
+ newVNode = newChildren[newEnd];
470
+ if (newVNode.key === oldVNode.key) {
471
+ oldRef = refChildren[oldEnd];
472
+ newRef = children[newEnd] = patchInPlace(
473
+ parentDomNode,
474
+ newVNode,
475
+ oldVNode,
476
+ oldRef,
477
+ env
478
+ );
479
+ newEnd--;
480
+ oldEnd--;
481
+ continue;
482
+ }
483
+
484
+ if (refMap == null) {
485
+ refMap = {};
486
+ for (let i = oldStart; i <= oldEnd; i++) {
487
+ oldVNode = oldchildren[i];
488
+ if (oldVNode.key != null) {
489
+ refMap[oldVNode.key] = i;
490
+ }
491
+ }
492
+ }
493
+
494
+ newVNode = newChildren[newStart];
495
+ let idx = newVNode.key != null ? refMap[newVNode.key] : null;
496
+ if (idx != null) {
497
+ oldVNode = oldchildren[idx];
498
+ oldRef = refChildren[idx];
499
+ newRef = children[newStart] = patch(
500
+ parentDomNode,
501
+ newVNode,
502
+ oldVNode,
503
+ oldRef,
504
+ env
505
+ );
506
+ insertDom(parentDomNode, newRef, getDomNode(refChildren[oldStart]));
507
+ if (newRef !== oldRef) {
508
+ removeDom(parentDomNode, oldRef);
509
+ unmount(oldVNode, oldRef, env);
510
+ }
511
+ refChildren[idx] = null;
512
+ } else {
513
+ newRef = children[newStart] = mount(newVNode, env);
514
+ insertDom(parentDomNode, newRef, getDomNode(refChildren[oldStart]));
515
+ }
516
+ newStart++;
517
+ }
518
+
519
+ let beforeNode =
520
+ newEnd < newChildren.length - 1
521
+ ? getDomNode(children[newEnd + 1])
522
+ : nextNode;
523
+ while (newStart <= newEnd) {
524
+ let newRef = mount(newChildren[newStart], env);
525
+ children[newStart] = newRef;
526
+ insertDom(parentDomNode, newRef, beforeNode);
527
+ newStart++;
528
+ }
529
+ while (oldStart <= oldEnd) {
530
+ oldRef = refChildren[oldStart];
531
+ if (oldRef != null) {
532
+ removeDom(parentDomNode, oldRef);
533
+ unmount(oldchildren[oldStart], oldRef, env);
534
+ }
535
+ oldStart++;
536
+ }
537
+ ref.children = children;
538
+ }
539
+
540
+ function defaultShouldUpdate(p1, p2) {
541
+ if (p1 === p2) return false;
542
+ for (let key in p2) {
543
+ if (p1[key] !== p2[key]) return true;
544
+ }
545
+ return false;
546
+ }
547
+
548
+ function propDirective(prop) {
549
+ return {
550
+ mount(element, value) {``
551
+ element[prop] = value;
552
+ },
553
+ patch(element, newValue, oldValue) {
554
+ if (newValue !== oldValue) {
555
+ element[prop] = newValue;
556
+ }
557
+ },
558
+ unmount(element, _) {
559
+ element[prop] = null;
560
+ },
561
+ };
562
+ }
563
+
564
+ function getDomNode(ref) {
565
+ if (ref.type === REF_SINGLE) {
566
+ return ref.node;
567
+ } else if (ref.type === REF_ARRAY) {
568
+ return getDomNode(ref.children[0]);
569
+ } else if (ref.type === REF_PARENT) {
570
+ return getDomNode(ref.childRef);
571
+ }
572
+ throw new Error("Unkown ref type " + JSON.stringify(ref));
573
+ }
574
+
575
+ function getParentNode(ref) {
576
+ if (ref.type === REF_SINGLE) {
577
+ return ref.node.parentNode;
578
+ } else if (ref.type === REF_ARRAY) {
579
+ return getParentNode(ref.children[0]);
580
+ } else if (ref.type === REF_PARENT) {
581
+ return getParentNode(ref.childRef);
582
+ }
583
+ throw new Error("Unkown ref type " + ref);
584
+ }
585
+
586
+ function getNextSibling(ref) {
587
+ if (ref.type === REF_SINGLE) {
588
+ return ref.node.nextSibling;
589
+ } else if (ref.type === REF_ARRAY) {
590
+ return getNextSibling(ref.children[ref.children.length - 1]);
591
+ } else if (ref.type === REF_PARENT) {
592
+ return getNextSibling(ref.childRef);
593
+ }
594
+ throw new Error("Unkown ref type " + JSON.stringify(ref));
595
+ }
596
+
597
+ function insertDom(parent, ref, nextSibling) {
598
+ if (ref.type === REF_SINGLE) {
599
+ parent.insertBefore(ref.node, nextSibling);
600
+ } else if (ref.type === REF_ARRAY) {
601
+ ref.children.forEach((ch) => {
602
+ insertDom(parent, ch, nextSibling);
603
+ });
604
+ } else if (ref.type === REF_PARENT) {
605
+ insertDom(parent, ref.childRef, nextSibling);
606
+ } else {
607
+ throw new Error("Unkown ref type " + JSON.stringify(ref));
608
+ }
609
+ }
610
+
611
+ function removeDom(parent, ref) {
612
+ if (ref.type === REF_SINGLE) {
613
+ parent.removeChild(ref.node);
614
+ } else if (ref.type === REF_ARRAY) {
615
+ ref.children.forEach((ch) => {
616
+ removeDom(parent, ch);
617
+ });
618
+ } else if (ref.type === REF_PARENT) {
619
+ removeDom(parent, ref.childRef);
620
+ } else {
621
+ throw new Error("Unkown ref type " + ref);
622
+ }
623
+ }
624
+
625
+ function replaceDom(parent, newRef, oldRef) {
626
+ insertDom(parent, newRef, getDomNode(oldRef));
627
+ removeDom(parent, oldRef);
628
+ }
629
+
630
+ function mountDirectives(domElement, props, env) {
631
+ for (let key in props) {
632
+ if (key in env.directives) {
633
+ env.directives[key].mount(domElement, props[key]);
634
+ }
635
+ }
636
+ }
637
+
638
+ function patchDirectives(domElement, newProps, oldProps, env) {
639
+ for (let key in newProps) {
640
+ if (key in env.directives) {
641
+ env.directives[key].patch(domElement, newProps[key], oldProps[key]);
642
+ }
643
+ }
644
+ for (let key in oldProps) {
645
+ if (key in env.directives && !(key in newProps)) {
646
+ env.directives[key].unmount(domElement, oldProps[key]);
647
+ }
648
+ }
649
+ }
650
+
651
+ function unmountDirectives(domElement, props, env) {
652
+ for (let key in props) {
653
+ if (key in env.directives) {
654
+ env.directives[key].unmount(domElement, props[key]);
655
+ }
656
+ }
657
+ }
658
+
659
+ function mountAttributes(domElement, props, env) {
660
+ for (var key in props) {
661
+ if (key === "key" || key === "children" || key in env.directives) continue;
662
+ if (key.startsWith("on")) {
663
+ let cmp = MOUNTING[MOUNTING.length - 1];
664
+ domElement[key.toLowerCase()] = cmp ? cmp.__tag.event(props[key]) : props[key];
665
+ } else {
666
+ setDOMAttribute(domElement, key, props[key], env.isSVG);
667
+ }
668
+ }
669
+ }
670
+
671
+ function patchAttributes(domElement, newProps, oldProps, env) {
672
+ for (var key in newProps) {
673
+ if (key === "key" || key === "children" || key in env.directives) continue;
674
+ var oldValue = oldProps[key];
675
+ var newValue = newProps[key];
676
+ if (oldValue !== newValue) {
677
+ if (key.startsWith("on")) {
678
+ let cmp = MOUNTING[MOUNTING.length - 1];
679
+ domElement[key.toLowerCase()] = cmp ? cmp.__tag.event(newValue) : newValue;
680
+ } else {
681
+ setDOMAttribute(domElement, key, newValue, env.isSVG);
682
+ }
683
+ }
684
+ }
685
+ for (key in oldProps) {
686
+ if (
687
+ key === "key" ||
688
+ key === "children" ||
689
+ key in env.directives ||
690
+ key in newProps
691
+ )
692
+ continue;
693
+ if (key.startsWith("on")) {
694
+ domElement[key.toLowerCase()] = null;
695
+ } else {
696
+ domElement.removeAttribute(key);
697
+ }
698
+ }
699
+ }
700
+
701
+ function setDOMAttribute(el, attr, value, isSVG) {
702
+ if (value === true) {
703
+ el.setAttribute(attr, "");
704
+ } else if (value === false) {
705
+ el.removeAttribute(attr);
706
+ } else {
707
+ var namespace = isSVG ? NS_ATTRS[attr] : undefined;
708
+ if (namespace !== undefined) {
709
+ el.setAttributeNS(namespace, attr, value);
710
+ } else {
711
+ el.setAttribute(attr, value);
712
+ }
713
+ }
714
+ }
715
+
716
+ function toClosureCmp(vnode, view) {
717
+ let onRemove = ON_REMOVES.pop() ?? noop,
718
+ cmp = {
719
+ key: vnode.key,
720
+ id: vnode.id,
721
+ vtype: VTYPE_COMPONENT,
722
+ props: vnode.props || {},
723
+ __tag: {
724
+ view,
725
+ removeFromParent: noop,
726
+ event: noop,
727
+ mount: render,
728
+ patch: render,
729
+ unmount: _ => {
730
+ onRemove();
731
+ let childs;
732
+ if (childs = CHILDS.get(cmp)) {
733
+ for (let i = 0; i < childs.length; i++) {
734
+ unmount(childs[i], EMPTY_OBJECT);
735
+ }
736
+
737
+ CHILDS.delete(cmp);
738
+ }
739
+
740
+ cmp.__tag.removeFromParent();
741
+ }
742
+ }
743
+ };
744
+
745
+ function render(ctx) {
746
+ cmp.__tag.event = fn => ev => {
747
+ fn(ev);
748
+ MOUNTING.push(cmp);
749
+ ctx.render(view({ ...ctx.props }));
750
+ MOUNTING.pop();
751
+ };
752
+
753
+ MOUNTING.push(cmp);
754
+ ctx.render(view({ ...ctx.props }));
755
+ MOUNTING.pop();
756
+ }
757
+
758
+ return cmp;
759
+ }