closures 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,766 @@
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
+ const m = h;
42
+
43
+ const onRemove = f => ON_REMOVES.push(f);
44
+
45
+ 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
+ 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
+ const Fragment = props => props.children;
92
+
93
+ 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
+ }
760
+
761
+ exports.Fragment = Fragment;
762
+ exports.h = h;
763
+ exports.jsx = jsx;
764
+ exports.m = m;
765
+ exports.onRemove = onRemove;
766
+ exports.render = render;