solarite 0.3.0 → 0.3.2

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.
Files changed (34) hide show
  1. package/dist/Solarite-debug.js +411 -524
  2. package/dist/Solarite.js +394 -507
  3. package/dist/Solarite.min.js +2 -2
  4. package/package.json +5 -6
  5. package/readme.md +2 -2
  6. package/src/{solarite/ExprPath.js → ExprPath.js} +43 -58
  7. package/src/{solarite/Globals.js → Globals.js} +4 -4
  8. package/src/{util/MultiValueMap.js → MultiValueMap.js} +1 -32
  9. package/src/{solarite/NodeGroup.js → NodeGroup.js} +30 -26
  10. package/src/{solarite/Shell.js → Shell.js} +8 -7
  11. package/src/Solarite.d.ts +62 -0
  12. package/src/{solarite/Solarite.js → Solarite.js} +13 -15
  13. package/src/{solarite/Template.js → Template.js} +1 -1
  14. package/src/{solarite/Util.js → Util.js} +49 -45
  15. package/src/{solarite/createSolarite.js → createSolarite.js} +12 -10
  16. package/src/{solarite/getArg.js → getArg.js} +24 -3
  17. package/src/{solarite/h.js → h.js} +4 -12
  18. package/src/unused/FastLookupArray.js +54 -0
  19. package/src/unused/Hashes.js +339 -0
  20. package/src/unused/InUse.test.js +92 -0
  21. package/src/unused/InUseMap.js +98 -0
  22. package/src/unused/LinkedList.js +117 -0
  23. package/src/unused/LinkedList.test.js +115 -0
  24. package/src/unused/Misc.js +13 -0
  25. package/src/unused/Perf.js +47 -0
  26. package/src/unused/TrackedArray.js +54 -0
  27. package/src/unused/WeakArray.js +33 -0
  28. package/src/{solarite/watch.js → watch.js} +4 -4
  29. package/src/util/Util.js +0 -101
  30. /package/src/{solarite/HtmlParser.js → HtmlParser.js} +0 -0
  31. /package/src/{util/Errors.js → assert.js} +0 -0
  32. /package/src/{util/delve.js → delve.js} +0 -0
  33. /package/src/{solarite/hash.js → hash.js} +0 -0
  34. /package/src/{solarite/udomdiff.js → udomdiff.js} +0 -0
package/dist/Solarite.js CHANGED
@@ -1,98 +1,92 @@
1
- /**
2
- * @typedef {Array|function(...*)} Callbacks
3
- * @property {function(function)} push
4
- * @property {function()} remove
5
- * @property {function()} pause
6
- * @property {function()} resume
7
- * */
1
+
2
+
3
+ let lastObjectId = 1>>>0; // Is a 32-bit int faster to increment than JavaScript's Number, which is a 64-bit float?
4
+ let objectIds = new WeakMap();
8
5
 
6
+ /**
7
+ * @param obj {Object|string|Node}
8
+ * @returns {string} */
9
+ function getObjectId(obj) {
10
+ // if (typeof obj === 'function')
11
+ // return obj.toString(); // This fails to detect when a function's bound variables changes.
12
+
13
+ let result = objectIds.get(obj);
14
+ if (!result) { // convert to string, store in result, then add 1 to lastObjectId.
15
+ result = '~\f' + (lastObjectId++); // We use a unique, 2-byte prefix to ensure it doesn't collide w/ strings not from getObjectId()
16
+ objectIds.set(obj, result);
17
+ }
18
+ return result;
19
+ }
9
20
 
10
21
  /**
11
- * A place for functions that have no other home. */
12
- var Util$1 = {
22
+ * Control how JSON.stringify() handles Nodes and Functions.
23
+ * Normally, we'd pass a replacer() function argument to JSON.stringify() to handle Nodes and Functions.
24
+ * But that makes JSON.stringify() take twice as long to run.
25
+ * Adding a toJSON method globally on these object prototypes doesn't incur that performance penalty. */
26
+ let isHashing = true;
27
+ function toJSON() {
28
+ return isHashing ? getObjectId(this) : this
29
+ }
13
30
 
14
- /**
15
- * Create an array-like object that stores a group of callbacks.
16
- * Supports all array functions and properties like push() and .length.
17
- * Can be called directly.
18
- *
19
- * @param functions {function[]}
20
- * @return {Callbacks|function}
21
- *
22
- * @example
23
- * var c = Util.callback();
24
- * var f = () => console.log(3);
25
- * c.push(f);
26
- * c();
27
- * c.remove(f);
28
- * c();
29
- */
30
- callback(...functions) {
31
- var paused = false;
32
-
33
- // Make it callable. When we call it, call all callbacks() with the given args.
34
- let result = async function(...args) {
35
- let result2 = [];
36
- if (!paused)
37
- for (let i=0; i<result.length; i++)
38
- result2.push(result[i](...args));
39
- return await Promise.all(result2);
40
- };
41
-
42
- // Make it iterable.
43
- result[Symbol.iterator] = function() {
44
- let index = 0;
45
- return {
46
- next: () => index < result.length
47
- ? {value: result[index++], done: false}
48
- : {done: true}
49
- };
50
- };
51
31
 
52
- // Use properties from Array
53
- for (let prop of Object.getOwnPropertyNames(Array.prototype))
54
- if (prop !== 'length' && prop !== 'constructor')
55
- result[prop] = Array.prototype[prop];
56
-
57
- result.l = 0; // Internal length
58
- Object.defineProperty(result, 'length', {
59
- get() { return result.l },
60
- set(val) { result.l = val;}
61
- });
62
-
63
- // Add the remove() function.
64
- result.remove = func => {
65
- let idx = result.findIndex(item => item === func);
66
- if (idx !== -1)
67
- result.splice(idx, 1);
68
- };
69
- result.pause = () => paused = true;
32
+ // Node.prototype.toJSON = toJSON;
33
+ // Function.prototype.toJSON = toJSON;
70
34
 
71
- result.resume = () => paused = false;
72
35
 
73
- // Add initial functions
74
- for (let f of functions)
75
- result.push(f);
36
+ /**
37
+ * Get a string that uniquely maps to the values of the given object.
38
+ * If a value in obj changes, calling getObjectHash(obj) will then return a different hash.
39
+ * This is used by NodeGroupManager to create a hash that represents the current values of a NodeGroup.
40
+ *
41
+ * Relies on the Node and Function prototypes being overridden above.
42
+ *
43
+ * Note that passing an integer may collide with the number we get from hashing an object.
44
+ * But we don't handle that case because we need max performance and Solarite never passes integers to this function.
45
+ *
46
+ * @param obj {*}
47
+ * @returns {string} */
48
+ function getObjectHash(obj) {
76
49
 
77
- return result;
78
- },
50
+ // Sometimes these get unassigned by Chrome and Brave 119, as well as Firefox, seemingly randomly!
51
+ // The same tests sometimes pass, sometimes fail, even after browser and OS restarts.
52
+ // So we check the assignments on every run of getObjectHash()
53
+ if (Node.prototype.toJSON !== toJSON) {
54
+ Node.prototype.toJSON = toJSON;
55
+ if (Function.prototype.toJSON !== toJSON) // Will it only unmap one but not the other?
56
+ Function.prototype.toJSON = toJSON;
57
+ }
79
58
 
80
- /**
81
- * Use an array as the value of a map, appending to it when we add.
82
- * Used by watch.js.
83
- * @param map {Map|WeakMap|Object}
84
- * @param key
85
- * @param value */
86
- mapArrayAdd(map, key, value) {
87
- let result = map.get(key);
88
- if (!result) {
89
- result = [value];
90
- map.set(key, result);
59
+ let result;
60
+ isHashing = true;
61
+ try {
62
+ result = JSON.stringify(obj);
63
+ }
64
+ catch(e) {
65
+ result = getObjectHashCircular(obj);
66
+ }
67
+ isHashing = false;
68
+ return result;
69
+ }
70
+
71
+ /**
72
+ * Slower hashing method that supports.
73
+ * @param obj
74
+ * @returns {string} */
75
+ function getObjectHashCircular(obj) {
76
+
77
+ //console.log('circular')
78
+ // Slower version that handles circular references.
79
+ // Just adding any callback at all, even one that just returns the value, makes JSON.stringify() twice as slow.
80
+ const seen = new Set();
81
+ return JSON.stringify(obj, (key, value) => {
82
+ if (typeof value === 'object' && value !== null) {
83
+ if (seen.has(value))
84
+ return getObjectId(value);
85
+ seen.add(value);
91
86
  }
92
- else
93
- result.push(value);
94
- },
95
- };
87
+ return value;
88
+ });
89
+ }
96
90
 
97
91
  var Globals;
98
92
 
@@ -119,15 +113,15 @@ function reset() {
119
113
  div: document.createElement("div"),
120
114
 
121
115
  /**
122
- * @type {Object<string, Class<Node>>} A map from built-in tag names to the constructors that create them. */
116
+ * @type {Record<string, Class<Node>>} A map from built-in tag names to the constructors that create them. */
123
117
  elementClasses: {},
124
118
 
125
- /** @type {Object<string, boolean>} Key is tag-name.propName. Value is whether it's an attribute.*/
119
+ /** @type {Record<string, boolean>} Key is tag-name.propName. Value is whether it's an attribute.*/
126
120
  htmlProps: {},
127
121
 
128
122
  /**
129
123
  * Used by ExprPath.applyEventAttrib()
130
- * @type {WeakMap<Node, Object<eventName:string, [original:function, bound:function, args:*[]]>>} */
124
+ * @type {WeakMap<Node, Record<eventName:string, [original:function, bound:function, args:*[]]>>} */
131
125
  nodeEvents: new WeakMap(),
132
126
 
133
127
  /**
@@ -162,7 +156,7 @@ function reset() {
162
156
  * A map of individual untagged strings to their Templates.
163
157
  * This way we don't keep creating new Templates for the same string when re-rendering.
164
158
  * This is used by ExprPath.applyExactNodes()
165
- * @type {Object<string, Template>} */
159
+ * @type {Record<string, Template>} */
166
160
  //stringTemplates: {},
167
161
 
168
162
  reset,
@@ -221,6 +215,21 @@ let d = {};
221
215
 
222
216
  let Util = {
223
217
 
218
+ /**
219
+ * Returns true if they're the same.
220
+ * @param a
221
+ * @param b
222
+ * @returns {boolean} */
223
+ arraySame(a, b) {
224
+ let aLength = a.length;
225
+ if (aLength !== b.length)
226
+ return false;
227
+ for (let i=0; i<aLength; i++)
228
+ if (a[i] !== b[i])
229
+ return false;
230
+ return true; // the same.
231
+ },
232
+
224
233
  bindId(root, el) {
225
234
  let id = el.getAttribute('data-id') || el.getAttribute('id');
226
235
  if (id) { // If something hasn't removed the id.
@@ -261,7 +270,6 @@ let Util = {
261
270
  }
262
271
  },
263
272
 
264
-
265
273
  /**
266
274
  * Convert a Proper Case name to a name with dashes.
267
275
  * Dashes will be placed between letters and numbers.
@@ -329,17 +337,17 @@ let Util = {
329
337
  * for (const item of flatten(complexArray))
330
338
  * console.log(item); // Outputs: 1, 2, 3, 4, 5, 6, { a: 'object' }, 7, 8, 9
331
339
  */
332
- *flatten(value) {
333
- if (Array.isArray(value)) {
334
- for (const item of value) {
335
- yield* Util.flatten(item); // Recursively flatten arrays
336
- }
337
- } else if (typeof value === 'function') {
338
- const result = value();
339
- yield* Util.flatten(result); // Recursively flatten the result of a function
340
- } else
341
- yield value; // Yield primitive values as is
342
- },
340
+ // *flatten(value) {
341
+ // if (Array.isArray(value)) {
342
+ // for (const item of value) {
343
+ // yield* Util.flatten(item); // Recursively flatten arrays
344
+ // }
345
+ // } else if (typeof value === 'function') {
346
+ // const result = value();
347
+ // yield* Util.flatten(result); // Recursively flatten the result of a function
348
+ // } else
349
+ // yield value; // Yield primitive values as is
350
+ // },
343
351
 
344
352
  /**
345
353
  * Get the value of an input as the most appropriate JavaScript type.
@@ -361,6 +369,10 @@ let Util = {
361
369
  return node.value; // String
362
370
  },
363
371
 
372
+ isEvent(attrName) {
373
+ return attrName.startsWith('on') && attrName in Globals$1.div;
374
+ },
375
+
364
376
  /**
365
377
  * @param el {HTMLElement}
366
378
  * @param prop {string}
@@ -400,9 +412,10 @@ let Util = {
400
412
  return val === undefined || val === false || val === null;
401
413
  },
402
414
 
415
+ /*
403
416
  isPrimitive(val) {
404
417
  return typeof val === 'string' || typeof val === 'number'
405
- },
418
+ },*/
406
419
 
407
420
  /**
408
421
  * If val is a function, evaluate it recursively until the result is not a function.
@@ -420,6 +433,22 @@ let Util = {
420
433
  return val;
421
434
  },
422
435
 
436
+ /**
437
+ * Use an array as the value of a map, appending to it when we add.
438
+ * Used by watch.js.
439
+ * @param map {Map|WeakMap|Object}
440
+ * @param key
441
+ * @param value */
442
+ mapArrayAdd(map, key, value) {
443
+ let result = map.get(key);
444
+ if (!result) {
445
+ result = [value];
446
+ map.set(key, result);
447
+ }
448
+ else
449
+ result.push(value);
450
+ },
451
+
423
452
  /**
424
453
  * Remove nodes from the beginning and end that are not:
425
454
  * 1. Elements.
@@ -436,288 +465,24 @@ let Util = {
436
465
 
437
466
  // Trim from the start
438
467
  while (result.length > 0 && shouldTrimNode(result[0]))
439
- result.shift();
440
-
441
- // Trim from the end
442
- while (result.length > 0 && shouldTrimNode(result[result.length - 1]))
443
- result.pop();
444
-
445
- return result;
446
- }
447
- };
448
-
449
-
450
-
451
- let isEvent = attrName => attrName.startsWith('on') && attrName in Globals$1.div;
452
-
453
-
454
-
455
-
456
-
457
- /**
458
- * Returns true if they're the same.
459
- * @param a
460
- * @param b
461
- * @returns {boolean} */
462
- function arraySame(a, b) {
463
- let aLength = a.length;
464
- if (aLength !== b.length)
465
- return false;
466
- for (let i=0; i<aLength; i++)
467
- if (a[i] !== b[i])
468
- return false;
469
- return true; // the same.
470
- }
471
-
472
-
473
-
474
-
475
-
476
- // For debugging only
477
-
478
-
479
- function defineClass(Class, tagName, extendsTag) {
480
- if (!customElements[getName](Class)) { // If not previously defined.
481
- tagName = tagName || Util.camelToDashes(Class.name);
482
- if (!tagName.includes('-'))
483
- tagName += '-element';
484
-
485
- let options = null;
486
- if (extendsTag)
487
- options = {extends: extendsTag};
488
-
489
- customElements[define](tagName, Class, options);
490
- }
491
- }
492
-
493
- /**
494
- * Create a version of the Solarite class that extends from the given tag name.
495
- * Reasons to inherit from this instead of HTMLElement. None of these are all that useful.
496
- * 1. customElements.define() is called automatically when you create the first instance.
497
- * 2. Calls render() when added to the DOM, if it hasn't been called already.
498
- * 3. Child elements are added before constructor is called. But they're also passed to the constructor. (deprecated?)
499
- * 4. We can use this.html = r`...` to set html. (deprecated)
500
- * 5. We have the onConnect, onFirstConnect, and onDisconnect methods.
501
- * Can't figure out how to have these work standalone though, and still be synchronous.
502
- * 6. Can we extend from other element types like TR?
503
- * 7. Shows default text if render() function isn't defined.
504
- *
505
- * Advantages to inheriting from HTMLElement
506
- * 1. Minimization won't break when it renames the Class and we call customElements.define() on the wrong name.
507
- * 2. We can inherit from things like HTMLTableRowElement directly.
508
- * 3. There's less magic, since everyone is familiar with defining custom elements.
509
- *
510
- * @param extendsTag {?string}
511
- * @return {Class} */
512
- function createSolarite(extendsTag=null) {
513
-
514
- let BaseClass = HTMLElement;
515
- if (extendsTag && !extendsTag.includes('-')) {
516
- extendsTag = extendsTag.toLowerCase();
517
-
518
- BaseClass = Globals$1.elementClasses[extendsTag];
519
- if (!BaseClass) { // TODO: Use Cache
520
- BaseClass = document.createElement(extendsTag).constructor;
521
- Globals$1.elementClasses[extendsTag] = BaseClass;
522
- }
523
- }
524
-
525
- /**
526
- * Intercept the construct call to auto-define the class before the constructor is called.
527
- * @type {HTMLElement} */
528
- let HTMLElementAutoDefine = new Proxy(BaseClass, {
529
- construct(Parent, args, Class) {
530
- defineClass(Class, null, extendsTag);
531
-
532
- // This is a good place to manipulate any args before they're sent to the constructor.
533
- // Such as loading them from attributes, if I could find a way to do so.
534
-
535
- // This line is equivalent the to super() call.
536
- return Reflect.construct(Parent, args, Class);
537
- }
538
- });
539
-
540
- return class Solarite extends HTMLElementAutoDefine {
541
-
542
-
543
- /**
544
- * TODO: Make these standalone functions.
545
- * Callbacks.
546
- * Use onConnect.push(() => ...); to add new callbacks. */
547
- onConnect = Util$1.callback();
548
-
549
- onFirstConnect = Util$1.callback();
550
- onDisconnect = Util$1.callback();
551
-
552
- /**
553
- * @param options {RenderOptions} */
554
- constructor(options={}) {
555
- super();
556
-
557
- // TODO: Is options.render ever used?
558
- if (options.render===true)
559
- this.render();
560
-
561
- else if (options.render===false)
562
- Globals$1.rendered.add(this); // Don't render on connectedCallback()
563
-
564
- // Add slot children before constructor code executes.
565
- // This breaks the styleStaticNested test.
566
- // PendingChildren is setup in NodeGroup.createNewComponent()
567
- // TODO: Match named slots.
568
- //let ch = Globals.pendingChildren.pop();
569
- //if (ch) // TODO: how could there be a slot before render is called?
570
- // (this.querySelector('slot') || this).append(...ch);
571
-
572
- /** @deprecated
573
- Object.defineProperty(this, 'html', {
574
- set(html) {
575
- Globals.rendered.add(this);
576
- if (typeof html === 'string') {
577
- console.warn("Assigning to this.html without the r template prefix.")
578
- this.innerHTML = html;
579
- }
580
- else
581
- this.modifications = r(this, html, options);
582
- }
583
- })*/
584
-
585
- /*
586
- let pthis = new Proxy(this, {
587
- get(obj, prop) {
588
- return Reflect.get(obj, prop)
589
- }
590
- });
591
- this.render = this.render.bind(pthis);
592
- */
593
- }
594
-
595
- /**
596
- * Call render() only if it hasn't already been called. */
597
- renderFirstTime() {
598
- if (!Globals$1.rendered.has(this) && this.render)
599
- this.render();
600
- }
601
-
602
- /**
603
- * Called automatically by the browser. */
604
- connectedCallback() {
605
- this.renderFirstTime();
606
- if (!Globals$1.connected.has(this)) {
607
- Globals$1.connected.add(this);
608
- this.onFirstConnect();
609
- }
610
- this.onConnect();
611
- }
612
-
613
- disconnectedCallback() {
614
- this.onDisconnect();
615
- }
616
-
617
-
618
- static define(tagName=null) {
619
- defineClass(this, tagName, extendsTag);
620
- }
621
- }
622
- }
623
-
624
- // Trick to prevent minifier from renaming this method.
625
- let define = 'define';
626
- let getName = 'getName';
627
-
628
-
629
-
630
- let lastObjectId = 1>>>0; // Is a 32-bit int faster to increment than JavaScript's Number, which is a 64-bit float?
631
- let objectIds = new WeakMap();
632
-
633
- /**
634
- * @param obj {Object|string|Node}
635
- * @returns {string} */
636
- function getObjectId(obj) {
637
- // if (typeof obj === 'function')
638
- // return obj.toString(); // This fails to detect when a function's bound variables changes.
639
-
640
- let result = objectIds.get(obj);
641
- if (!result) { // convert to string, store in result, then add 1 to lastObjectId.
642
- result = '~\f' + (lastObjectId++); // We use a unique, 2-byte prefix to ensure it doesn't collide w/ strings not from getObjectId()
643
- objectIds.set(obj, result);
644
- }
645
- return result;
646
- }
647
-
648
- /**
649
- * Control how JSON.stringify() handles Nodes and Functions.
650
- * Normally, we'd pass a replacer() function argument to JSON.stringify() to handle Nodes and Functions.
651
- * But that makes JSON.stringify() take twice as long to run.
652
- * Adding a toJSON method globally on these object prototypes doesn't incur that performance penalty. */
653
- let isHashing = true;
654
- function toJSON() {
655
- return isHashing ? getObjectId(this) : this
656
- }
657
-
658
-
659
- // Node.prototype.toJSON = toJSON;
660
- // Function.prototype.toJSON = toJSON;
661
-
662
-
663
- /**
664
- * Get a string that uniquely maps to the values of the given object.
665
- * If a value in obj changes, calling getObjectHash(obj) will then return a different hash.
666
- * This is used by NodeGroupManager to create a hash that represents the current values of a NodeGroup.
667
- *
668
- * Relies on the Node and Function prototypes being overridden above.
669
- *
670
- * Note that passing an integer may collide with the number we get from hashing an object.
671
- * But we don't handle that case because we need max performance and Solarite never passes integers to this function.
672
- *
673
- * @param obj {*}
674
- * @returns {string} */
675
- function getObjectHash(obj) {
676
-
677
- // Sometimes these get unassigned by Chrome and Brave 119, as well as Firefox, seemingly randomly!
678
- // The same tests sometimes pass, sometimes fail, even after browser and OS restarts.
679
- // So we check the assignments on every run of getObjectHash()
680
- if (Node.prototype.toJSON !== toJSON) {
681
- Node.prototype.toJSON = toJSON;
682
- if (Function.prototype.toJSON !== toJSON) // Will it only unmap one but not the other?
683
- Function.prototype.toJSON = toJSON;
684
- }
468
+ result.shift();
685
469
 
686
- let result;
687
- isHashing = true;
688
- try {
689
- result = JSON.stringify(obj);
690
- }
691
- catch(e) {
692
- result = getObjectHashCircular(obj);
470
+ // Trim from the end
471
+ while (result.length > 0 && shouldTrimNode(result[result.length - 1]))
472
+ result.pop();
473
+
474
+ return result;
693
475
  }
694
- isHashing = false;
695
- return result;
696
- }
476
+ };
697
477
 
698
- /**
699
- * Slower hashing method that supports.
700
- * @param obj
701
- * @returns {string} */
702
- function getObjectHashCircular(obj) {
703
478
 
704
- //console.log('circular')
705
- // Slower version that handles circular references.
706
- // Just adding any callback at all, even one that just returns the value, makes JSON.stringify() twice as slow.
707
- const seen = new Set();
708
- return JSON.stringify(obj, (key, value) => {
709
- if (typeof value === 'object' && value !== null) {
710
- if (seen.has(value))
711
- return getObjectId(value);
712
- seen.add(value);
713
- }
714
- return value;
715
- });
716
- }
479
+
480
+ // For debugging only
481
+
717
482
 
718
483
  class MultiValueMap {
719
484
 
720
- /** @type {Object<string, Set>} */
485
+ /** @type {Record<string, Set>} */
721
486
  data = {};
722
487
 
723
488
  // Set a new value for a key
@@ -811,37 +576,6 @@ class MultiValueMap {
811
576
  return result;
812
577
  }
813
578
 
814
-
815
- /**
816
- * Try to delete an item that matches the key and the isPreferred function.
817
- * if not the latter, just delete any item that matches the key.
818
- * @param key {string}
819
- * @returns {*|undefined} The deleted item. */
820
- deletePreferred(key, parent) {
821
- let result;
822
- let data = this.data;
823
- let set = data[key];
824
- if (!set)
825
- return undefined;
826
-
827
- for (let val of set) {
828
- if (val?.parentNode === parent) {
829
- set.delete(val);
830
- result = val;
831
- break;
832
- }
833
- }
834
- if (!result) {
835
- [result] = set;
836
- set.delete(result);
837
- }
838
-
839
- if (set.size === 0)
840
- delete data[key];
841
-
842
- return result;
843
- }
844
-
845
579
  hasValue(val) {
846
580
  let data = this.data;
847
581
  let names = [];
@@ -1237,7 +971,7 @@ class ExprPath {
1237
971
 
1238
972
 
1239
973
  // This pre-check makes it a few percent faster?
1240
- let same = arraySame(oldNodes, newNodes);
974
+ let same = Util.arraySame(oldNodes, newNodes);
1241
975
  if (!same) {
1242
976
 
1243
977
  path.nodesCache = newNodes; // Replaces value set by path.getNodes()
@@ -1259,6 +993,17 @@ class ExprPath {
1259
993
  for (let ng of oldNodeGroups)
1260
994
  if (!ng.startNode.parentNode)
1261
995
  ng.removeAndSaveOrphans();
996
+
997
+ // Instantiate components created within ${...} expressions.
998
+ // Embedded style tags are handled elsewhere, but where?
999
+ for (let el of newNodes) {
1000
+ if (el instanceof HTMLElement) {
1001
+ if (el.hasAttribute('solarite-placeholder'))
1002
+ this.parentNg.instantiateComponent(el);
1003
+ for (let child of el.querySelectorAll('[solarite-placeholder]'))
1004
+ this.parentNg.instantiateComponent(child);
1005
+ }
1006
+ }
1262
1007
  }
1263
1008
 
1264
1009
 
@@ -1433,50 +1178,10 @@ class ExprPath {
1433
1178
  // Arrays and functions.
1434
1179
  // I tried iterating over the result of a generator function to avoid this recursion and simplify the code,
1435
1180
  // but that consistently made the js-framework-benchmarks a few percentage points slower.
1436
- else {
1181
+ else
1437
1182
  this.exprToTemplates(expr, template => {
1438
1183
  this.applyExactNodes(template, newNodes, secondPass);
1439
1184
  });
1440
-
1441
- }
1442
-
1443
- // Old version
1444
- /*else if (Array.isArray(expr))
1445
- for (let subExpr of expr)
1446
- this.applyExactNodes(subExpr, newNodes, secondPass);
1447
-
1448
- else if (typeof expr === 'function') {
1449
- // TODO: One ExprPath can have multiple expr functions.
1450
- // But if using it as a watch, it should only have one at the top level.
1451
- // So maybe this is ok.
1452
- Globals.currentExprPath = this; // Used by watch()
1453
-
1454
- this.watchFunction = expr; // TODO: Only do this if it's a top level function.
1455
- let result = expr(); // As expr accesses watched variables, watch() uses Globals.currentExprPath to mark where those watched variables are being used.
1456
- Globals.currentExprPath = null;
1457
-
1458
- this.applyExactNodes(result, newNodes, secondPass);
1459
- }
1460
-
1461
- // String
1462
- else {
1463
- // Convert expression to a string.
1464
- let stringExpr = expr;
1465
- if (expr === undefined || expr === false || expr === null) // Util.isFalsy()
1466
- stringExpr = '';
1467
- else if (typeof expr !== 'string')
1468
- stringExpr = expr + '';
1469
-
1470
- // Get the same Template for the same string each time.
1471
- let template = Globals.stringTemplates[stringExpr];
1472
- if (!template) {
1473
- template = new Template([stringExpr], []);
1474
- Globals.stringTemplates[stringExpr] = template;
1475
- }
1476
-
1477
- // Recurse.
1478
- this.applyExactNodes(template, newNodes, secondPass);
1479
- }*/
1480
1185
  }
1481
1186
 
1482
1187
  applyMultipleAttribs(node, expr) {
@@ -1496,16 +1201,30 @@ class ExprPath {
1496
1201
  Globals$1.currentExprPath = null;
1497
1202
  }
1498
1203
 
1499
- let attrs = (expr +'') // Split string into multiple attributes.
1500
- .split(/([\w-]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s]+))/g)
1501
- .map(text => text.trim())
1502
- .filter(text => text.length);
1204
+ // Attribute as name: value object.
1205
+ if (typeof expr === 'object') {
1206
+ for (let name in expr) {
1207
+ let value = expr[name];
1208
+ if (value === undefined || value === false || value === null)
1209
+ continue;
1210
+ node.setAttribute(name, value);
1211
+ this.attrNames.add(name);
1212
+ }
1213
+ }
1503
1214
 
1504
- for (let attr of attrs) {
1505
- let [name, value] = attr.split(/\s*=\s*/); // split on first equals.
1506
- value = (value || '').replace(/^(['"])(.*)\1$/, '$2'); // trim value quotes if they match.
1507
- node.setAttribute(name, value);
1508
- this.attrNames.add(name);
1215
+ // Attributes as string
1216
+ else {
1217
+ let attrs = (expr + '') // Split string into multiple attributes.
1218
+ .split(/([\w-]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s]+))/g)
1219
+ .map(text => text.trim())
1220
+ .filter(text => text.length);
1221
+
1222
+ for (let attr of attrs) {
1223
+ let [name, value] = attr.split(/\s*=\s*/); // split on first equals.
1224
+ value = (value || '').replace(/^(['"])(.*)\1$/, '$2'); // trim value quotes if they match.
1225
+ node.setAttribute(name, value);
1226
+ this.attrNames.add(name);
1227
+ }
1509
1228
  }
1510
1229
  }
1511
1230
 
@@ -1616,7 +1335,7 @@ class ExprPath {
1616
1335
  // Copies the attribute to the property when the input event fires.
1617
1336
  // value=${[this, 'value]'}
1618
1337
  // checked=${[this, 'isAgree']}
1619
- // This same logic is in NodeGroup.createNewComponent() for components.
1338
+ // This same logic is in NodeGroup.instantiateComponent() for components.
1620
1339
  if (Util.isPath(expr)) {
1621
1340
  let [obj, path] = [expr[0], expr.slice(1)];
1622
1341
 
@@ -2156,7 +1875,7 @@ class Shell {
2156
1875
  /** @type {int[][]} Array of paths. Used by activateEmbeds() to quickly find components. */
2157
1876
  staticComponents = [];
2158
1877
 
2159
- /** @type {{path:int[], attribs:Object<string, string>}[]} */
1878
+ /** @type {{path:int[], attribs:Record<string, string>}[]} */
2160
1879
  //componentAttribs = [];
2161
1880
 
2162
1881
 
@@ -2215,7 +1934,7 @@ class Shell {
2215
1934
  let parts = attr.value.split(/[\ue000-\uf8ff]/g);
2216
1935
  if (parts.length > 1) {
2217
1936
  let nonEmptyParts = (parts.length === 2 && !parts[0].length && !parts[1].length) ? null : parts;
2218
- let type = isEvent(attr.name) ? ExprPathType.Event : ExprPathType.AttribValue;
1937
+ let type = Util.isEvent(attr.name) ? ExprPathType.Event : ExprPathType.AttribValue;
2219
1938
 
2220
1939
  this.paths.push(new ExprPath(null, node, type, attr.name, nonEmptyParts));
2221
1940
  placeholdersUsed += parts.length - 1;
@@ -2343,11 +2062,12 @@ class Shell {
2343
2062
  function addToken(token, context) {
2344
2063
 
2345
2064
  if (context === HtmlParser.Tag) {
2346
- // Find Solarite Components tags and append -solarite-placeholder to their tag names.
2065
+ // Find Solarite Components tags and append -solarite-placeholder to their tag names
2066
+ // and give them a solarite-placeholder attribute so we can easily find them later.
2347
2067
  // This way we can gather their constructor arguments and their children before we call their constructor.
2348
- // Later, NodeGroup.createNewComponent() will replace them with the real components.
2068
+ // Later, NodeGroup.instantiateComponent() will replace them with the real components.
2349
2069
  // Ctrl+F "solarite-placeholder" in project to find all code that manages subcomponents.
2350
- token = token.replace(/^<\/?[a-z][a-z0-9]*-[a-z0-9-]+/i, match => match + '-solarite-placeholder');
2070
+ token = token.replace(/^<\/?[a-z][a-z0-9]*-[a-z0-9-]+/i, match => match + '-solarite-placeholder solarite-placeholder');
2351
2071
  }
2352
2072
  tokens.push(token);
2353
2073
  }
@@ -2510,7 +2230,7 @@ class NodeGroup {
2510
2230
  // Apply exprs
2511
2231
  this.applyExprs(template.exprs);
2512
2232
 
2513
- this.activateStaticComponents(staticComponents);
2233
+ this.instantiateStaticComponents(staticComponents);
2514
2234
  }
2515
2235
  else if (shell)
2516
2236
  this.activateEmbeds(fragment, shell);
@@ -2603,7 +2323,7 @@ class NodeGroup {
2603
2323
  // Think of having two adjacent components.
2604
2324
  // But the dynamicAttribsAdjacet test already passes.
2605
2325
 
2606
- // If a component:
2326
+ // If expr is an attribute in a component:
2607
2327
  // 1. Instantiate it if it hasn't already been, sending all expr's to its constructor.
2608
2328
  // 2. Otherwise send them to its render function.
2609
2329
  // Components with no expressions as attributes are instead activated in activateEmbeds().
@@ -2640,6 +2360,8 @@ class NodeGroup {
2640
2360
  // TODO: Only do this if we have ExprPaths within styles?
2641
2361
  this.updateStyles();
2642
2362
 
2363
+
2364
+
2643
2365
  // Invalidate the nodes cache because we just changed it.
2644
2366
  this.nodesCache = null;
2645
2367
 
@@ -2662,23 +2384,25 @@ class NodeGroup {
2662
2384
  // then we could re-use the hash and logic from NodeManager?
2663
2385
  let newHash = getObjectHash(props);
2664
2386
 
2665
- let isPreHtmlElement = el.tagName.endsWith('-SOLARITE-PLACEHOLDER');
2387
+ let isPreHtmlElement = el.hasAttribute('solarite-placeholder');
2666
2388
  let isPreIsElement = el.hasAttribute('_is');
2667
2389
 
2668
2390
 
2669
2391
  // Instantiate a placeholder.
2670
2392
  if (isPreHtmlElement || isPreIsElement)
2671
- el = this.createNewComponent(el, isPreHtmlElement, props);
2393
+ el = this.instantiateComponent(el, isPreHtmlElement, props);
2672
2394
 
2673
2395
  // Call render() with the same params that would've been passed to the constructor.
2396
+ // We do this even if the arguments haven't changed, so we can let the child component
2397
+ // compare the arguments and then decide for itself whether it wants to re-render.
2674
2398
  else if (el.render) {
2675
- let oldHash = Globals$1.componentArgsHash.get(el);
2676
- if (oldHash !== newHash) {
2399
+ //let oldHash = Globals.componentArgsHash.get(el);
2400
+ //if (oldHash !== newHash) { // Only if not changed.
2677
2401
  let args = {};
2678
2402
  for (let name in props || {})
2679
2403
  args[Util.dashesToCamel(name)] = props[name];
2680
2404
  el.render(args); // Pass new values of props to render so it can decide how it wants to respond.
2681
- }
2405
+ //}
2682
2406
  }
2683
2407
 
2684
2408
  Globals$1.componentArgsHash.set(el, newHash);
@@ -2691,10 +2415,10 @@ class NodeGroup {
2691
2415
  * The logic of this function is complex and could use cleaning up.
2692
2416
  *
2693
2417
  * @param el
2694
- * @param isPreHtmlElement
2418
+ * @param isPreHtmlElement {?boolean} True if the element's tag name ends with -solarite-placeholder
2695
2419
  * @param props {Object} Attributes with dynamic values.
2696
2420
  * @return {HTMLElement} */
2697
- createNewComponent(el, isPreHtmlElement=undefined, props=undefined) {
2421
+ instantiateComponent(el, isPreHtmlElement=undefined, props=undefined) {
2698
2422
  if (isPreHtmlElement === undefined)
2699
2423
  isPreHtmlElement = !el.hasAttribute('_is');
2700
2424
 
@@ -2708,24 +2432,24 @@ class NodeGroup {
2708
2432
  if (!Constructor)
2709
2433
  throw new Error(`The custom tag name ${tagName} is not registered.`)
2710
2434
 
2711
- let args = {};
2435
+ let attribs = {};
2712
2436
  for (let name in props || {})
2713
- args[Util.dashesToCamel(name)] = props[name];
2437
+ attribs[Util.dashesToCamel(name)] = props[name];
2714
2438
 
2715
2439
  // Pass other attribs to constructor, since otherwise they're not yet set on the element,
2716
2440
  // and the constructor would otherwise have no way to see them.
2717
2441
  if (el.attributes.length) {
2718
2442
  for (let attrib of el.attributes) {
2719
2443
  let attribName = Util.dashesToCamel(attrib.name);
2720
- if (!args.hasOwnProperty(attribName))
2721
- args[attribName] = attrib.value;
2444
+ if (!attribs.hasOwnProperty(attribName) && attribName !== 'solarite-placeholder')
2445
+ attribs[attribName] = attrib.value;
2722
2446
  }
2723
2447
  }
2724
2448
 
2725
2449
  // Create the web component.
2726
2450
  // Get the children that aren't Solarite's comment placeholders.
2727
- let ch = [...el.childNodes].filter(node => node.nodeType !== Node.COMMENT_NODE || !node.nodeValue.startsWith('ExprPath'));
2728
- let newEl = new Constructor(args, ch);
2451
+ let children = [...el.childNodes].filter(node => node.nodeType !== Node.COMMENT_NODE || !node.nodeValue.startsWith('ExprPath'));
2452
+ let newEl = new Constructor(attribs, children);
2729
2453
 
2730
2454
  if (!isPreHtmlElement)
2731
2455
  newEl.setAttribute('is', el.getAttribute('is').toLowerCase());
@@ -2759,7 +2483,7 @@ class NodeGroup {
2759
2483
 
2760
2484
  // Copy attributes over.
2761
2485
  for (let attrib of el.attributes)
2762
- if (attrib.name !== '_is')
2486
+ if (attrib.name !== '_is' && attrib.name !== 'solarite-placeholder')
2763
2487
  newEl.setAttribute(attrib.name, attrib.value);
2764
2488
 
2765
2489
  // Set dynamic attributes if they are primitive types.
@@ -2854,9 +2578,9 @@ class NodeGroup {
2854
2578
  let result = [];
2855
2579
 
2856
2580
  // static components. These are WebComponents that do not have any constructor arguments that are expressions.
2857
- // Those are instead created by applyExpr() which calls applyComponentExprs() which calls createNewcomponent().
2581
+ // Those are instead created by applyExpr() which calls applyComponentExprs() which calls instantiateComponent().
2858
2582
  // Maybe someday these two paths will be merged?
2859
- // Must happen before ids because createNewComponent will replace the element.
2583
+ // Must happen before ids because instantiateComponent will replace the element.
2860
2584
  for (let path of shell.staticComponents) {
2861
2585
  if (pathOffset)
2862
2586
  path = path.slice(0, -pathOffset);
@@ -2870,13 +2594,13 @@ class NodeGroup {
2870
2594
  return result;
2871
2595
  }
2872
2596
 
2873
- activateStaticComponents(staticComponents) {
2597
+ instantiateStaticComponents(staticComponents) {
2874
2598
  for (let el of staticComponents)
2875
- this.createNewComponent(el);
2599
+ this.instantiateComponent(el);
2876
2600
  }
2877
2601
 
2878
2602
  /**
2879
- * @param root {HTMLElement}
2603
+ * @param root {HTMLElement|DocumentFragment}
2880
2604
  * @param shell {Shell}
2881
2605
  * @param pathOffset {int} */
2882
2606
  activateEmbeds(root, shell, pathOffset=0) {
@@ -2986,7 +2710,7 @@ class RootNodeGroup extends NodeGroup {
2986
2710
 
2987
2711
  // Copy attributes
2988
2712
  for (let attrib of fragment.children[0].attributes)
2989
- if (!el.hasAttribute(attrib.name))
2713
+ if (!el.hasAttribute(attrib.name) && attrib.name !== 'solarite-placeholder')
2990
2714
  el.setAttribute(attrib.name, attrib.value);
2991
2715
 
2992
2716
  // Go one level deeper into all of shell's paths.
@@ -3046,7 +2770,7 @@ class RootNodeGroup extends NodeGroup {
3046
2770
  // Apply exprs
3047
2771
  this.applyExprs(template.exprs);
3048
2772
 
3049
- this.activateStaticComponents(staticComponents);
2773
+ this.instantiateStaticComponents(staticComponents);
3050
2774
  }
3051
2775
  }
3052
2776
  }
@@ -3070,7 +2794,7 @@ function getSingleEl(fragment) {
3070
2794
  * @returns {boolean} */
3071
2795
  function isReplaceEl(fragment, el) {
3072
2796
  return fragment.children.length===1
3073
- && el.tagName.includes('-')
2797
+ && el.tagName.includes('-') // TODO: Check for solarite-placeholder attribute instead?
3074
2798
  && fragment.children[0].tagName.replace('-SOLARITE-PLACEHOLDER', '') === el.tagName;
3075
2799
  }
3076
2800
 
@@ -3233,6 +2957,8 @@ class Template {
3233
2957
  * @return {Node|HTMLElement|Template} */
3234
2958
  function h(htmlStrings=undefined, ...exprs) {
3235
2959
 
2960
+ if (htmlStrings === undefined && !exprs.length && arguments.length)
2961
+ throw new Error('h() cannot be called with undefined.');
3236
2962
 
3237
2963
  // TODO: Make this a more flat if/else and call other functions for the logic.
3238
2964
  if (htmlStrings instanceof Node) {
@@ -3255,7 +2981,7 @@ function h(htmlStrings=undefined, ...exprs) {
3255
2981
  }
3256
2982
 
3257
2983
  // 2. Render template created by #4 to element.
3258
- else if (exprs[0] instanceof Template) {
2984
+ else { // instanceof Template
3259
2985
  let options = exprs[1];
3260
2986
  template.render(parent, options);
3261
2987
 
@@ -3266,16 +2992,6 @@ function h(htmlStrings=undefined, ...exprs) {
3266
2992
  parent.append(this.rootNg.getParentNode());
3267
2993
  }
3268
2994
  }
3269
-
3270
-
3271
-
3272
- // null for expr[0], remove whole element.
3273
- // This path never happens?
3274
- else {
3275
- throw new Error('unsupported');
3276
- //let ngm = NodeGroupManager.get(parent);
3277
- //ngm.render(null, exprs[1])
3278
- }
3279
2995
  }
3280
2996
 
3281
2997
  // 3. Path if used as a template tag.
@@ -3412,7 +3128,7 @@ let renderF = 'render';
3412
3128
  * @param el {HTMLElement}
3413
3129
  * @param attributeName {string} Attribute name. Not case-sensitive.
3414
3130
  * @param defaultValue {*} Default value to use if attribute doesn't exist.
3415
- * @param type {ArgType|function|*[]}
3131
+ * @param type {ArgType|function|Class|*[]}
3416
3132
  * If an array, use the value if it's in the array, otherwise return undefined.
3417
3133
  * If it's a function, pass the value to the function and return the result.
3418
3134
  * @param fallback {*} If the defaultValue is undefiend and type can't be parsed as the given type, use this value.
@@ -3427,8 +3143,11 @@ function getArg(el, attributeName, defaultValue=undefined, type=ArgType.String,
3427
3143
  if (Array.isArray(type))
3428
3144
  return type.includes(val) ? val : fallback;
3429
3145
 
3430
- if (typeof type === 'function')
3431
- return type(val);
3146
+ if (typeof type === 'function') {
3147
+ return type.constructor
3148
+ ? new type(val) // arg type is custom Class
3149
+ : type(val); // arg type is custom function
3150
+ }
3432
3151
 
3433
3152
  // If bool, it's true as long as it exists and its value isn't falsey.
3434
3153
  if (type===ArgType.Bool) {
@@ -3470,6 +3189,24 @@ function getArg(el, attributeName, defaultValue=undefined, type=ArgType.String,
3470
3189
  }
3471
3190
  }
3472
3191
 
3192
+
3193
+ /**
3194
+ * Experimental. Set multiple arguments/attributes all at once.
3195
+ * @param el {HTMLElement}
3196
+ * @param args {Record<string, any>}
3197
+ * @param types {Record<string, ArgType|function|Class>}
3198
+ *
3199
+ * @example
3200
+ * constructor({user, path}={}) {
3201
+ * setArgs(this, arguments[0], {user: User, path: ArgType.String});
3202
+ * }
3203
+ */
3204
+ function setArgs(el, args, types) {
3205
+ for (let name in args)
3206
+ this[name] = getArg(el, name, args[name], types[name] || ArgType.String);
3207
+ }
3208
+
3209
+
3473
3210
  /**
3474
3211
  * @enum */
3475
3212
  var ArgType = {
@@ -3498,6 +3235,158 @@ var ArgType = {
3498
3235
  Eval: 'Eval'
3499
3236
  };
3500
3237
 
3238
+ function defineClass(Class, tagName, extendsTag) {
3239
+ if (!customElements[getName](Class)) { // If not previously defined.
3240
+ tagName = tagName || Util.camelToDashes(Class.name);
3241
+ if (!tagName.includes('-'))
3242
+ tagName += '-element';
3243
+
3244
+ let options = null;
3245
+ if (extendsTag)
3246
+ options = {extends: extendsTag};
3247
+
3248
+ customElements[define](tagName, Class, options);
3249
+ }
3250
+ }
3251
+
3252
+ /**
3253
+ * Create a version of the Solarite class that extends from the given tag name.
3254
+ * Reasons to inherit from this instead of HTMLElement. None of these are all that useful.
3255
+ * 1. customElements.define() is called automatically when you create the first instance.
3256
+ * 2. Calls render() when added to the DOM, if it hasn't been called already.
3257
+ * 3. Child elements are added before constructor is called. But they're also passed to the constructor. (deprecated?)
3258
+ * 4. We can use this.html = r`...` to set html. (deprecated)
3259
+ * 5. We have the onConnect, onFirstConnect, and onDisconnect methods.
3260
+ * Can't figure out how to have these work standalone though, and still be synchronous.
3261
+ * 6. Can we extend from other element types like TR?
3262
+ * 7. Shows default text if render() function isn't defined.
3263
+ *
3264
+ * Advantages to inheriting from HTMLElement
3265
+ * 1. Minimization won't break when it renames the Class and we call customElements.define() on the wrong name.
3266
+ * 2. We can inherit from things like HTMLTableRowElement directly.
3267
+ * 3. There's less magic, since everyone is familiar with defining custom elements.
3268
+ *
3269
+ * @param extendsTag {?string}
3270
+ * @return {Class} */
3271
+ function createSolarite(extendsTag=null) {
3272
+
3273
+ let BaseClass = HTMLElement;
3274
+ if (extendsTag && !extendsTag.includes('-')) {
3275
+ extendsTag = extendsTag.toLowerCase();
3276
+
3277
+ BaseClass = Globals$1.elementClasses[extendsTag];
3278
+ if (!BaseClass) { // TODO: Use Cache
3279
+ BaseClass = document.createElement(extendsTag).constructor;
3280
+ Globals$1.elementClasses[extendsTag] = BaseClass;
3281
+ }
3282
+ }
3283
+
3284
+ /**
3285
+ * Intercept the construct call to auto-define the class before the constructor is called.
3286
+ * @type {HTMLElement} */
3287
+ let HTMLElementAutoDefine = new Proxy(BaseClass, {
3288
+ construct(Parent, args, Class) {
3289
+ defineClass(Class, null, extendsTag);
3290
+
3291
+ // This is a good place to manipulate any args before they're sent to the constructor.
3292
+ // Such as loading them from attributes, if I could find a way to do so.
3293
+
3294
+ // This line is equivalent the to super() call.
3295
+ return Reflect.construct(Parent, args, Class);
3296
+ }
3297
+ });
3298
+
3299
+ return class Solarite extends HTMLElementAutoDefine {
3300
+
3301
+
3302
+ /**
3303
+ * TODO: Make these standalone functions.
3304
+ * Callbacks.
3305
+ * Use onConnect.push(() => ...); to add new callbacks. */
3306
+ onConnect;
3307
+
3308
+ onFirstConnect;
3309
+ onDisconnect;
3310
+
3311
+ /**
3312
+ * @param options {RenderOptions} */
3313
+ constructor(options={}) {
3314
+ super();
3315
+
3316
+ // TODO: Is options.render ever used?
3317
+ if (options.render===true)
3318
+ this.render();
3319
+
3320
+ else if (options.render===false)
3321
+ Globals$1.rendered.add(this); // Don't render on connectedCallback()
3322
+
3323
+ // Add slot children before constructor code executes.
3324
+ // This breaks the styleStaticNested test.
3325
+ // PendingChildren is setup in NodeGroup.instantiateComponent()
3326
+ // TODO: Match named slots.
3327
+ //let ch = Globals.pendingChildren.pop();
3328
+ //if (ch) // TODO: how could there be a slot before render is called?
3329
+ // (this.querySelector('slot') || this).append(...ch);
3330
+
3331
+ /** @deprecated
3332
+ Object.defineProperty(this, 'html', {
3333
+ set(html) {
3334
+ Globals.rendered.add(this);
3335
+ if (typeof html === 'string') {
3336
+ console.warn("Assigning to this.html without the r template prefix.")
3337
+ this.innerHTML = html;
3338
+ }
3339
+ else
3340
+ this.modifications = r(this, html, options);
3341
+ }
3342
+ })*/
3343
+
3344
+ /*
3345
+ let pthis = new Proxy(this, {
3346
+ get(obj, prop) {
3347
+ return Reflect.get(obj, prop)
3348
+ }
3349
+ });
3350
+ this.render = this.render.bind(pthis);
3351
+ */
3352
+ }
3353
+
3354
+ /**
3355
+ * Call render() only if it hasn't already been called. */
3356
+ renderFirstTime() {
3357
+ if (!Globals$1.rendered.has(this) && this.render)
3358
+ this.render();
3359
+ }
3360
+
3361
+ /**
3362
+ * Called automatically by the browser. */
3363
+ connectedCallback() {
3364
+ this.renderFirstTime();
3365
+ if (!Globals$1.connected.has(this)) {
3366
+ Globals$1.connected.add(this);
3367
+ if (this.onFirstConnect)
3368
+ this.onFirstConnect();
3369
+ }
3370
+ if (this.onConnect)
3371
+ this.onConnect();
3372
+ }
3373
+
3374
+ disconnectedCallback() {
3375
+ if (this.onDisconnect)
3376
+ this.onDisconnect();
3377
+ }
3378
+
3379
+
3380
+ static define(tagName=null) {
3381
+ defineClass(this, tagName, extendsTag);
3382
+ }
3383
+ }
3384
+ }
3385
+
3386
+ // Trick to prevent minifier from renaming this method.
3387
+ let define = 'define';
3388
+ let getName = 'getName';
3389
+
3501
3390
  /**
3502
3391
  * Solarite JavasCript UI library.
3503
3392
  * MIT License
@@ -3507,15 +3396,13 @@ var ArgType = {
3507
3396
  /**
3508
3397
  * TODO: The Proxy and the multiple base classes mess up 'instanceof Solarite'
3509
3398
  * @type {Node|Class<HTMLElement>|function(tagName:string):Node|Class<HTMLElement>} */
3510
- let Solarite = new Proxy(createSolarite(), {
3399
+ const Solarite = new Proxy(createSolarite(), {
3511
3400
  apply(self, _, args) {
3512
3401
  return createSolarite(...args)
3513
3402
  }
3514
3403
  });
3515
- let getInputValue = Util.getInputValue;
3516
3404
 
3517
- //Experimental:
3518
3405
  //export {default as watch, renderWatched} from './watch.js'; // unfinished
3519
3406
 
3520
3407
  export default h;
3521
- export { ArgType, Globals$1 as Globals, Solarite, Util as SolariteUtil, Template, delve, getArg, getInputValue, h, h as r };
3408
+ export { ArgType, Globals$1 as Globals, Solarite, Util as SolariteUtil, Template, delve, getArg, h, h as r, setArgs };