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
@@ -1,98 +1,101 @@
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
+ //#IFDEV
2
+ /*@__NO_SIDE_EFFECTS__*/
3
+ function assert(val) {
4
+ if (!val) {
5
+ debugger;
6
+ throw new Error('Assertion failed: ' + val);
7
+ }
8
+ }
8
9
 
10
+ //#ENDIF
11
+
12
+ let lastObjectId = 1>>>0; // Is a 32-bit int faster to increment than JavaScript's Number, which is a 64-bit float?
13
+ let objectIds = new WeakMap();
9
14
 
10
15
  /**
11
- * A place for functions that have no other home. */
12
- var Util$1 = {
16
+ * @param obj {Object|string|Node}
17
+ * @returns {string} */
18
+ function getObjectId(obj) {
19
+ // if (typeof obj === 'function')
20
+ // return obj.toString(); // This fails to detect when a function's bound variables changes.
21
+
22
+ let result = objectIds.get(obj);
23
+ if (!result) { // convert to string, store in result, then add 1 to lastObjectId.
24
+ result = '~\f' + (lastObjectId++); // We use a unique, 2-byte prefix to ensure it doesn't collide w/ strings not from getObjectId()
25
+ objectIds.set(obj, result);
26
+ }
27
+ return result;
28
+ }
13
29
 
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
- };
30
+ /**
31
+ * Control how JSON.stringify() handles Nodes and Functions.
32
+ * Normally, we'd pass a replacer() function argument to JSON.stringify() to handle Nodes and Functions.
33
+ * But that makes JSON.stringify() take twice as long to run.
34
+ * Adding a toJSON method globally on these object prototypes doesn't incur that performance penalty. */
35
+ let isHashing = true;
36
+ function toJSON() {
37
+ return isHashing ? getObjectId(this) : this
38
+ }
51
39
 
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
40
 
57
- result.l = 0; // Internal length
58
- Object.defineProperty(result, 'length', {
59
- get() { return result.l },
60
- set(val) { result.l = val;}
61
- });
41
+ // Node.prototype.toJSON = toJSON;
42
+ // Function.prototype.toJSON = toJSON;
62
43
 
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;
70
44
 
71
- result.resume = () => paused = false;
45
+ /**
46
+ * Get a string that uniquely maps to the values of the given object.
47
+ * If a value in obj changes, calling getObjectHash(obj) will then return a different hash.
48
+ * This is used by NodeGroupManager to create a hash that represents the current values of a NodeGroup.
49
+ *
50
+ * Relies on the Node and Function prototypes being overridden above.
51
+ *
52
+ * Note that passing an integer may collide with the number we get from hashing an object.
53
+ * But we don't handle that case because we need max performance and Solarite never passes integers to this function.
54
+ *
55
+ * @param obj {*}
56
+ * @returns {string} */
57
+ function getObjectHash(obj) {
72
58
 
73
- // Add initial functions
74
- for (let f of functions)
75
- result.push(f);
59
+ // Sometimes these get unassigned by Chrome and Brave 119, as well as Firefox, seemingly randomly!
60
+ // The same tests sometimes pass, sometimes fail, even after browser and OS restarts.
61
+ // So we check the assignments on every run of getObjectHash()
62
+ if (Node.prototype.toJSON !== toJSON) {
63
+ Node.prototype.toJSON = toJSON;
64
+ if (Function.prototype.toJSON !== toJSON) // Will it only unmap one but not the other?
65
+ Function.prototype.toJSON = toJSON;
66
+ }
76
67
 
77
- return result;
78
- },
68
+ let result;
69
+ isHashing = true;
70
+ try {
71
+ result = JSON.stringify(obj);
72
+ }
73
+ catch(e) {
74
+ result = getObjectHashCircular(obj);
75
+ }
76
+ isHashing = false;
77
+ return result;
78
+ }
79
79
 
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);
80
+ /**
81
+ * Slower hashing method that supports.
82
+ * @param obj
83
+ * @returns {string} */
84
+ function getObjectHashCircular(obj) {
85
+
86
+ //console.log('circular')
87
+ // Slower version that handles circular references.
88
+ // Just adding any callback at all, even one that just returns the value, makes JSON.stringify() twice as slow.
89
+ const seen = new Set();
90
+ return JSON.stringify(obj, (key, value) => {
91
+ if (typeof value === 'object' && value !== null) {
92
+ if (seen.has(value))
93
+ return getObjectId(value);
94
+ seen.add(value);
91
95
  }
92
- else
93
- result.push(value);
94
- },
95
- };
96
+ return value;
97
+ });
98
+ }
96
99
 
97
100
  var Globals;
98
101
 
@@ -119,15 +122,15 @@ function reset() {
119
122
  div: document.createElement("div"),
120
123
 
121
124
  /**
122
- * @type {Object<string, Class<Node>>} A map from built-in tag names to the constructors that create them. */
125
+ * @type {Record<string, Class<Node>>} A map from built-in tag names to the constructors that create them. */
123
126
  elementClasses: {},
124
127
 
125
- /** @type {Object<string, boolean>} Key is tag-name.propName. Value is whether it's an attribute.*/
128
+ /** @type {Record<string, boolean>} Key is tag-name.propName. Value is whether it's an attribute.*/
126
129
  htmlProps: {},
127
130
 
128
131
  /**
129
132
  * Used by ExprPath.applyEventAttrib()
130
- * @type {WeakMap<Node, Object<eventName:string, [original:function, bound:function, args:*[]]>>} */
133
+ * @type {WeakMap<Node, Record<eventName:string, [original:function, bound:function, args:*[]]>>} */
131
134
  nodeEvents: new WeakMap(),
132
135
 
133
136
  /**
@@ -162,7 +165,7 @@ function reset() {
162
165
  * A map of individual untagged strings to their Templates.
163
166
  * This way we don't keep creating new Templates for the same string when re-rendering.
164
167
  * This is used by ExprPath.applyExactNodes()
165
- * @type {Object<string, Template>} */
168
+ * @type {Record<string, Template>} */
166
169
  //stringTemplates: {},
167
170
 
168
171
  reset,
@@ -221,6 +224,21 @@ let d = {};
221
224
 
222
225
  let Util = {
223
226
 
227
+ /**
228
+ * Returns true if they're the same.
229
+ * @param a
230
+ * @param b
231
+ * @returns {boolean} */
232
+ arraySame(a, b) {
233
+ let aLength = a.length;
234
+ if (aLength !== b.length)
235
+ return false;
236
+ for (let i=0; i<aLength; i++)
237
+ if (a[i] !== b[i])
238
+ return false;
239
+ return true; // the same.
240
+ },
241
+
224
242
  bindId(root, el) {
225
243
  let id = el.getAttribute('data-id') || el.getAttribute('id');
226
244
  if (id) { // If something hasn't removed the id.
@@ -261,7 +279,6 @@ let Util = {
261
279
  }
262
280
  },
263
281
 
264
-
265
282
  /**
266
283
  * Convert a Proper Case name to a name with dashes.
267
284
  * Dashes will be placed between letters and numbers.
@@ -329,17 +346,17 @@ let Util = {
329
346
  * for (const item of flatten(complexArray))
330
347
  * console.log(item); // Outputs: 1, 2, 3, 4, 5, 6, { a: 'object' }, 7, 8, 9
331
348
  */
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
- },
349
+ // *flatten(value) {
350
+ // if (Array.isArray(value)) {
351
+ // for (const item of value) {
352
+ // yield* Util.flatten(item); // Recursively flatten arrays
353
+ // }
354
+ // } else if (typeof value === 'function') {
355
+ // const result = value();
356
+ // yield* Util.flatten(result); // Recursively flatten the result of a function
357
+ // } else
358
+ // yield value; // Yield primitive values as is
359
+ // },
343
360
 
344
361
  /**
345
362
  * Get the value of an input as the most appropriate JavaScript type.
@@ -361,6 +378,10 @@ let Util = {
361
378
  return node.value; // String
362
379
  },
363
380
 
381
+ isEvent(attrName) {
382
+ return attrName.startsWith('on') && attrName in Globals$1.div;
383
+ },
384
+
364
385
  /**
365
386
  * @param el {HTMLElement}
366
387
  * @param prop {string}
@@ -400,9 +421,10 @@ let Util = {
400
421
  return val === undefined || val === false || val === null;
401
422
  },
402
423
 
424
+ /*
403
425
  isPrimitive(val) {
404
426
  return typeof val === 'string' || typeof val === 'number'
405
- },
427
+ },*/
406
428
 
407
429
  /**
408
430
  * If val is a function, evaluate it recursively until the result is not a function.
@@ -420,6 +442,22 @@ let Util = {
420
442
  return val;
421
443
  },
422
444
 
445
+ /**
446
+ * Use an array as the value of a map, appending to it when we add.
447
+ * Used by watch.js.
448
+ * @param map {Map|WeakMap|Object}
449
+ * @param key
450
+ * @param value */
451
+ mapArrayAdd(map, key, value) {
452
+ let result = map.get(key);
453
+ if (!result) {
454
+ result = [value];
455
+ map.set(key, result);
456
+ }
457
+ else
458
+ result.push(value);
459
+ },
460
+
423
461
  /**
424
462
  * Remove nodes from the beginning and end that are not:
425
463
  * 1. Elements.
@@ -448,31 +486,6 @@ let Util = {
448
486
 
449
487
 
450
488
 
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
489
  // For debugging only
477
490
  //#IFDEV
478
491
  function setIndent(items, level=1) {
@@ -508,284 +521,36 @@ function nodeToArrayTree(node, callback=null) {
508
521
  }
509
522
  }
510
523
 
511
- //let closingTag = `</${node.nodeName.toLowerCase()}>`;
512
-
513
- result.push(openingTag, ...childrenArray);
514
- } else if (node.nodeType === 3) {
515
- result.push("'"+node.nodeValue+"'");
516
- }
517
-
518
- return result;
519
- }
520
-
521
-
522
- function flattenAndIndent(inputArray, indent = "") {
523
- let result = [];
524
-
525
- for (let item of inputArray) {
526
- if (Array.isArray(item)) {
527
- // Recursively handle nested arrays with increased indentation
528
- result = result.concat(flattenAndIndent(item, indent + " "));
529
- } else {
530
- result.push(indent + item);
531
- }
532
- }
533
-
534
- return result;
535
- }
536
- //#ENDIF
537
-
538
- function defineClass(Class, tagName, extendsTag) {
539
- if (!customElements[getName](Class)) { // If not previously defined.
540
- tagName = tagName || Util.camelToDashes(Class.name);
541
- if (!tagName.includes('-'))
542
- tagName += '-element';
543
-
544
- let options = null;
545
- if (extendsTag)
546
- options = {extends: extendsTag};
547
-
548
- customElements[define](tagName, Class, options);
549
- }
550
- }
551
-
552
- /**
553
- * Create a version of the Solarite class that extends from the given tag name.
554
- * Reasons to inherit from this instead of HTMLElement. None of these are all that useful.
555
- * 1. customElements.define() is called automatically when you create the first instance.
556
- * 2. Calls render() when added to the DOM, if it hasn't been called already.
557
- * 3. Child elements are added before constructor is called. But they're also passed to the constructor. (deprecated?)
558
- * 4. We can use this.html = r`...` to set html. (deprecated)
559
- * 5. We have the onConnect, onFirstConnect, and onDisconnect methods.
560
- * Can't figure out how to have these work standalone though, and still be synchronous.
561
- * 6. Can we extend from other element types like TR?
562
- * 7. Shows default text if render() function isn't defined.
563
- *
564
- * Advantages to inheriting from HTMLElement
565
- * 1. Minimization won't break when it renames the Class and we call customElements.define() on the wrong name.
566
- * 2. We can inherit from things like HTMLTableRowElement directly.
567
- * 3. There's less magic, since everyone is familiar with defining custom elements.
568
- *
569
- * @param extendsTag {?string}
570
- * @return {Class} */
571
- function createSolarite(extendsTag=null) {
572
-
573
- let BaseClass = HTMLElement;
574
- if (extendsTag && !extendsTag.includes('-')) {
575
- extendsTag = extendsTag.toLowerCase();
576
-
577
- BaseClass = Globals$1.elementClasses[extendsTag];
578
- if (!BaseClass) { // TODO: Use Cache
579
- BaseClass = document.createElement(extendsTag).constructor;
580
- Globals$1.elementClasses[extendsTag] = BaseClass;
581
- }
582
- }
583
-
584
- /**
585
- * Intercept the construct call to auto-define the class before the constructor is called.
586
- * @type {HTMLElement} */
587
- let HTMLElementAutoDefine = new Proxy(BaseClass, {
588
- construct(Parent, args, Class) {
589
- defineClass(Class, null, extendsTag);
590
-
591
- // This is a good place to manipulate any args before they're sent to the constructor.
592
- // Such as loading them from attributes, if I could find a way to do so.
593
-
594
- // This line is equivalent the to super() call.
595
- return Reflect.construct(Parent, args, Class);
596
- }
597
- });
598
-
599
- return class Solarite extends HTMLElementAutoDefine {
600
-
601
-
602
- /**
603
- * TODO: Make these standalone functions.
604
- * Callbacks.
605
- * Use onConnect.push(() => ...); to add new callbacks. */
606
- onConnect = Util$1.callback();
607
-
608
- onFirstConnect = Util$1.callback();
609
- onDisconnect = Util$1.callback();
610
-
611
- /**
612
- * @param options {RenderOptions} */
613
- constructor(options={}) {
614
- super();
615
-
616
- // TODO: Is options.render ever used?
617
- if (options.render===true)
618
- this.render();
619
-
620
- else if (options.render===false)
621
- Globals$1.rendered.add(this); // Don't render on connectedCallback()
622
-
623
- // Add slot children before constructor code executes.
624
- // This breaks the styleStaticNested test.
625
- // PendingChildren is setup in NodeGroup.createNewComponent()
626
- // TODO: Match named slots.
627
- //let ch = Globals.pendingChildren.pop();
628
- //if (ch) // TODO: how could there be a slot before render is called?
629
- // (this.querySelector('slot') || this).append(...ch);
630
-
631
- /** @deprecated
632
- Object.defineProperty(this, 'html', {
633
- set(html) {
634
- Globals.rendered.add(this);
635
- if (typeof html === 'string') {
636
- console.warn("Assigning to this.html without the r template prefix.")
637
- this.innerHTML = html;
638
- }
639
- else
640
- this.modifications = r(this, html, options);
641
- }
642
- })*/
643
-
644
- /*
645
- let pthis = new Proxy(this, {
646
- get(obj, prop) {
647
- return Reflect.get(obj, prop)
648
- }
649
- });
650
- this.render = this.render.bind(pthis);
651
- */
652
- }
653
-
654
- /**
655
- * Call render() only if it hasn't already been called. */
656
- renderFirstTime() {
657
- if (!Globals$1.rendered.has(this) && this.render)
658
- this.render();
659
- }
660
-
661
- /**
662
- * Called automatically by the browser. */
663
- connectedCallback() {
664
- this.renderFirstTime();
665
- if (!Globals$1.connected.has(this)) {
666
- Globals$1.connected.add(this);
667
- this.onFirstConnect();
668
- }
669
- this.onConnect();
670
- }
671
-
672
- disconnectedCallback() {
673
- this.onDisconnect();
674
- }
675
-
676
-
677
- static define(tagName=null) {
678
- defineClass(this, tagName, extendsTag);
679
- }
680
- }
681
- }
682
-
683
- // Trick to prevent minifier from renaming this method.
684
- let define = 'define';
685
- let getName = 'getName';
686
-
687
- //#IFDEV
688
- /*@__NO_SIDE_EFFECTS__*/
689
- function assert(val) {
690
- if (!val) {
691
- debugger;
692
- throw new Error('Assertion failed: ' + val);
693
- }
694
- }
695
-
696
- //#ENDIF
697
-
698
- let lastObjectId = 1>>>0; // Is a 32-bit int faster to increment than JavaScript's Number, which is a 64-bit float?
699
- let objectIds = new WeakMap();
700
-
701
- /**
702
- * @param obj {Object|string|Node}
703
- * @returns {string} */
704
- function getObjectId(obj) {
705
- // if (typeof obj === 'function')
706
- // return obj.toString(); // This fails to detect when a function's bound variables changes.
707
-
708
- let result = objectIds.get(obj);
709
- if (!result) { // convert to string, store in result, then add 1 to lastObjectId.
710
- result = '~\f' + (lastObjectId++); // We use a unique, 2-byte prefix to ensure it doesn't collide w/ strings not from getObjectId()
711
- objectIds.set(obj, result);
712
- }
713
- return result;
714
- }
715
-
716
- /**
717
- * Control how JSON.stringify() handles Nodes and Functions.
718
- * Normally, we'd pass a replacer() function argument to JSON.stringify() to handle Nodes and Functions.
719
- * But that makes JSON.stringify() take twice as long to run.
720
- * Adding a toJSON method globally on these object prototypes doesn't incur that performance penalty. */
721
- let isHashing = true;
722
- function toJSON() {
723
- return isHashing ? getObjectId(this) : this
724
- }
725
-
726
-
727
- // Node.prototype.toJSON = toJSON;
728
- // Function.prototype.toJSON = toJSON;
729
-
730
-
731
- /**
732
- * Get a string that uniquely maps to the values of the given object.
733
- * If a value in obj changes, calling getObjectHash(obj) will then return a different hash.
734
- * This is used by NodeGroupManager to create a hash that represents the current values of a NodeGroup.
735
- *
736
- * Relies on the Node and Function prototypes being overridden above.
737
- *
738
- * Note that passing an integer may collide with the number we get from hashing an object.
739
- * But we don't handle that case because we need max performance and Solarite never passes integers to this function.
740
- *
741
- * @param obj {*}
742
- * @returns {string} */
743
- function getObjectHash(obj) {
744
-
745
- // Sometimes these get unassigned by Chrome and Brave 119, as well as Firefox, seemingly randomly!
746
- // The same tests sometimes pass, sometimes fail, even after browser and OS restarts.
747
- // So we check the assignments on every run of getObjectHash()
748
- if (Node.prototype.toJSON !== toJSON) {
749
- Node.prototype.toJSON = toJSON;
750
- if (Function.prototype.toJSON !== toJSON) // Will it only unmap one but not the other?
751
- Function.prototype.toJSON = toJSON;
752
- }
753
-
754
- let result;
755
- isHashing = true;
756
- try {
757
- result = JSON.stringify(obj);
758
- }
759
- catch(e) {
760
- result = getObjectHashCircular(obj);
524
+ //let closingTag = `</${node.nodeName.toLowerCase()}>`;
525
+
526
+ result.push(openingTag, ...childrenArray);
527
+ } else if (node.nodeType === 3) {
528
+ result.push("'"+node.nodeValue+"'");
761
529
  }
762
- isHashing = false;
530
+
763
531
  return result;
764
532
  }
765
533
 
766
- /**
767
- * Slower hashing method that supports.
768
- * @param obj
769
- * @returns {string} */
770
- function getObjectHashCircular(obj) {
771
534
 
772
- //console.log('circular')
773
- // Slower version that handles circular references.
774
- // Just adding any callback at all, even one that just returns the value, makes JSON.stringify() twice as slow.
775
- const seen = new Set();
776
- return JSON.stringify(obj, (key, value) => {
777
- if (typeof value === 'object' && value !== null) {
778
- if (seen.has(value))
779
- return getObjectId(value);
780
- seen.add(value);
535
+ function flattenAndIndent(inputArray, indent = "") {
536
+ let result = [];
537
+
538
+ for (let item of inputArray) {
539
+ if (Array.isArray(item)) {
540
+ // Recursively handle nested arrays with increased indentation
541
+ result = result.concat(flattenAndIndent(item, indent + " "));
542
+ } else {
543
+ result.push(indent + item);
781
544
  }
782
- return value;
783
- });
784
- }
545
+ }
546
+
547
+ return result;
548
+ }
549
+ //#ENDIF
785
550
 
786
551
  class MultiValueMap {
787
552
 
788
- /** @type {Object<string, Set>} */
553
+ /** @type {Record<string, Set>} */
789
554
  data = {};
790
555
 
791
556
  // Set a new value for a key
@@ -879,37 +644,6 @@ class MultiValueMap {
879
644
  return result;
880
645
  }
881
646
 
882
-
883
- /**
884
- * Try to delete an item that matches the key and the isPreferred function.
885
- * if not the latter, just delete any item that matches the key.
886
- * @param key {string}
887
- * @returns {*|undefined} The deleted item. */
888
- deletePreferred(key, parent) {
889
- let result;
890
- let data = this.data;
891
- let set = data[key];
892
- if (!set)
893
- return undefined;
894
-
895
- for (let val of set) {
896
- if (val?.parentNode === parent) {
897
- set.delete(val);
898
- result = val;
899
- break;
900
- }
901
- }
902
- if (!result) {
903
- [result] = set;
904
- set.delete(result);
905
- }
906
-
907
- if (set.size === 0)
908
- delete data[key];
909
-
910
- return result;
911
- }
912
-
913
647
  hasValue(val) {
914
648
  let data = this.data;
915
649
  let names = [];
@@ -1349,7 +1083,7 @@ class ExprPath {
1349
1083
 
1350
1084
 
1351
1085
  // This pre-check makes it a few percent faster?
1352
- let same = arraySame(oldNodes, newNodes);
1086
+ let same = Util.arraySame(oldNodes, newNodes);
1353
1087
  if (!same) {
1354
1088
 
1355
1089
  path.nodesCache = newNodes; // Replaces value set by path.getNodes()
@@ -1371,6 +1105,17 @@ class ExprPath {
1371
1105
  for (let ng of oldNodeGroups)
1372
1106
  if (!ng.startNode.parentNode)
1373
1107
  ng.removeAndSaveOrphans();
1108
+
1109
+ // Instantiate components created within ${...} expressions.
1110
+ // Embedded style tags are handled elsewhere, but where?
1111
+ for (let el of newNodes) {
1112
+ if (el instanceof HTMLElement) {
1113
+ if (el.hasAttribute('solarite-placeholder'))
1114
+ this.parentNg.instantiateComponent(el);
1115
+ for (let child of el.querySelectorAll('[solarite-placeholder]'))
1116
+ this.parentNg.instantiateComponent(child);
1117
+ }
1118
+ }
1374
1119
  }
1375
1120
 
1376
1121
 
@@ -1547,50 +1292,10 @@ class ExprPath {
1547
1292
  // Arrays and functions.
1548
1293
  // I tried iterating over the result of a generator function to avoid this recursion and simplify the code,
1549
1294
  // but that consistently made the js-framework-benchmarks a few percentage points slower.
1550
- else {
1295
+ else
1551
1296
  this.exprToTemplates(expr, template => {
1552
1297
  this.applyExactNodes(template, newNodes, secondPass);
1553
1298
  });
1554
-
1555
- }
1556
-
1557
- // Old version
1558
- /*else if (Array.isArray(expr))
1559
- for (let subExpr of expr)
1560
- this.applyExactNodes(subExpr, newNodes, secondPass);
1561
-
1562
- else if (typeof expr === 'function') {
1563
- // TODO: One ExprPath can have multiple expr functions.
1564
- // But if using it as a watch, it should only have one at the top level.
1565
- // So maybe this is ok.
1566
- Globals.currentExprPath = this; // Used by watch()
1567
-
1568
- this.watchFunction = expr; // TODO: Only do this if it's a top level function.
1569
- let result = expr(); // As expr accesses watched variables, watch() uses Globals.currentExprPath to mark where those watched variables are being used.
1570
- Globals.currentExprPath = null;
1571
-
1572
- this.applyExactNodes(result, newNodes, secondPass);
1573
- }
1574
-
1575
- // String
1576
- else {
1577
- // Convert expression to a string.
1578
- let stringExpr = expr;
1579
- if (expr === undefined || expr === false || expr === null) // Util.isFalsy()
1580
- stringExpr = '';
1581
- else if (typeof expr !== 'string')
1582
- stringExpr = expr + '';
1583
-
1584
- // Get the same Template for the same string each time.
1585
- let template = Globals.stringTemplates[stringExpr];
1586
- if (!template) {
1587
- template = new Template([stringExpr], []);
1588
- Globals.stringTemplates[stringExpr] = template;
1589
- }
1590
-
1591
- // Recurse.
1592
- this.applyExactNodes(template, newNodes, secondPass);
1593
- }*/
1594
1299
  }
1595
1300
 
1596
1301
  applyMultipleAttribs(node, expr) {
@@ -1610,16 +1315,30 @@ class ExprPath {
1610
1315
  Globals$1.currentExprPath = null;
1611
1316
  }
1612
1317
 
1613
- let attrs = (expr +'') // Split string into multiple attributes.
1614
- .split(/([\w-]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s]+))/g)
1615
- .map(text => text.trim())
1616
- .filter(text => text.length);
1318
+ // Attribute as name: value object.
1319
+ if (typeof expr === 'object') {
1320
+ for (let name in expr) {
1321
+ let value = expr[name];
1322
+ if (value === undefined || value === false || value === null)
1323
+ continue;
1324
+ node.setAttribute(name, value);
1325
+ this.attrNames.add(name);
1326
+ }
1327
+ }
1617
1328
 
1618
- for (let attr of attrs) {
1619
- let [name, value] = attr.split(/\s*=\s*/); // split on first equals.
1620
- value = (value || '').replace(/^(['"])(.*)\1$/, '$2'); // trim value quotes if they match.
1621
- node.setAttribute(name, value);
1622
- this.attrNames.add(name);
1329
+ // Attributes as string
1330
+ else {
1331
+ let attrs = (expr + '') // Split string into multiple attributes.
1332
+ .split(/([\w-]+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s]+))/g)
1333
+ .map(text => text.trim())
1334
+ .filter(text => text.length);
1335
+
1336
+ for (let attr of attrs) {
1337
+ let [name, value] = attr.split(/\s*=\s*/); // split on first equals.
1338
+ value = (value || '').replace(/^(['"])(.*)\1$/, '$2'); // trim value quotes if they match.
1339
+ node.setAttribute(name, value);
1340
+ this.attrNames.add(name);
1341
+ }
1623
1342
  }
1624
1343
  }
1625
1344
 
@@ -1733,7 +1452,7 @@ class ExprPath {
1733
1452
  // Copies the attribute to the property when the input event fires.
1734
1453
  // value=${[this, 'value]'}
1735
1454
  // checked=${[this, 'isAgree']}
1736
- // This same logic is in NodeGroup.createNewComponent() for components.
1455
+ // This same logic is in NodeGroup.instantiateComponent() for components.
1737
1456
  if (Util.isPath(expr)) {
1738
1457
  let [obj, path] = [expr[0], expr.slice(1)];
1739
1458
 
@@ -2338,7 +2057,7 @@ class Shell {
2338
2057
  /** @type {int[][]} Array of paths. Used by activateEmbeds() to quickly find components. */
2339
2058
  staticComponents = [];
2340
2059
 
2341
- /** @type {{path:int[], attribs:Object<string, string>}[]} */
2060
+ /** @type {{path:int[], attribs:Record<string, string>}[]} */
2342
2061
  //componentAttribs = [];
2343
2062
 
2344
2063
 
@@ -2399,7 +2118,7 @@ class Shell {
2399
2118
  let parts = attr.value.split(/[\ue000-\uf8ff]/g);
2400
2119
  if (parts.length > 1) {
2401
2120
  let nonEmptyParts = (parts.length === 2 && !parts[0].length && !parts[1].length) ? null : parts;
2402
- let type = isEvent(attr.name) ? ExprPathType.Event : ExprPathType.AttribValue;
2121
+ let type = Util.isEvent(attr.name) ? ExprPathType.Event : ExprPathType.AttribValue;
2403
2122
 
2404
2123
  this.paths.push(new ExprPath(null, node, type, attr.name, nonEmptyParts));
2405
2124
  placeholdersUsed += parts.length - 1;
@@ -2527,11 +2246,12 @@ class Shell {
2527
2246
  function addToken(token, context) {
2528
2247
 
2529
2248
  if (context === HtmlParser.Tag) {
2530
- // Find Solarite Components tags and append -solarite-placeholder to their tag names.
2249
+ // Find Solarite Components tags and append -solarite-placeholder to their tag names
2250
+ // and give them a solarite-placeholder attribute so we can easily find them later.
2531
2251
  // This way we can gather their constructor arguments and their children before we call their constructor.
2532
- // Later, NodeGroup.createNewComponent() will replace them with the real components.
2252
+ // Later, NodeGroup.instantiateComponent() will replace them with the real components.
2533
2253
  // Ctrl+F "solarite-placeholder" in project to find all code that manages subcomponents.
2534
- token = token.replace(/^<\/?[a-z][a-z0-9]*-[a-z0-9-]+/i, match => match + '-solarite-placeholder');
2254
+ token = token.replace(/^<\/?[a-z][a-z0-9]*-[a-z0-9-]+/i, match => match + '-solarite-placeholder solarite-placeholder');
2535
2255
  }
2536
2256
  tokens.push(token);
2537
2257
  }
@@ -2702,7 +2422,7 @@ class NodeGroup {
2702
2422
  // Apply exprs
2703
2423
  this.applyExprs(template.exprs);
2704
2424
 
2705
- this.activateStaticComponents(staticComponents);
2425
+ this.instantiateStaticComponents(staticComponents);
2706
2426
  }
2707
2427
  else if (shell)
2708
2428
  this.activateEmbeds(fragment, shell);
@@ -2796,7 +2516,7 @@ class NodeGroup {
2796
2516
  // Think of having two adjacent components.
2797
2517
  // But the dynamicAttribsAdjacet test already passes.
2798
2518
 
2799
- // If a component:
2519
+ // If expr is an attribute in a component:
2800
2520
  // 1. Instantiate it if it hasn't already been, sending all expr's to its constructor.
2801
2521
  // 2. Otherwise send them to its render function.
2802
2522
  // Components with no expressions as attributes are instead activated in activateEmbeds().
@@ -2833,6 +2553,8 @@ class NodeGroup {
2833
2553
  // TODO: Only do this if we have ExprPaths within styles?
2834
2554
  this.updateStyles();
2835
2555
 
2556
+
2557
+
2836
2558
  // Invalidate the nodes cache because we just changed it.
2837
2559
  this.nodesCache = null;
2838
2560
 
@@ -2857,23 +2579,25 @@ class NodeGroup {
2857
2579
  // then we could re-use the hash and logic from NodeManager?
2858
2580
  let newHash = getObjectHash(props);
2859
2581
 
2860
- let isPreHtmlElement = el.tagName.endsWith('-SOLARITE-PLACEHOLDER');
2582
+ let isPreHtmlElement = el.hasAttribute('solarite-placeholder');
2861
2583
  let isPreIsElement = el.hasAttribute('_is');
2862
2584
 
2863
2585
 
2864
2586
  // Instantiate a placeholder.
2865
2587
  if (isPreHtmlElement || isPreIsElement)
2866
- el = this.createNewComponent(el, isPreHtmlElement, props);
2588
+ el = this.instantiateComponent(el, isPreHtmlElement, props);
2867
2589
 
2868
2590
  // Call render() with the same params that would've been passed to the constructor.
2591
+ // We do this even if the arguments haven't changed, so we can let the child component
2592
+ // compare the arguments and then decide for itself whether it wants to re-render.
2869
2593
  else if (el.render) {
2870
- let oldHash = Globals$1.componentArgsHash.get(el);
2871
- if (oldHash !== newHash) {
2594
+ //let oldHash = Globals.componentArgsHash.get(el);
2595
+ //if (oldHash !== newHash) { // Only if not changed.
2872
2596
  let args = {};
2873
2597
  for (let name in props || {})
2874
2598
  args[Util.dashesToCamel(name)] = props[name];
2875
2599
  el.render(args); // Pass new values of props to render so it can decide how it wants to respond.
2876
- }
2600
+ //}
2877
2601
  }
2878
2602
 
2879
2603
  Globals$1.componentArgsHash.set(el, newHash);
@@ -2886,10 +2610,10 @@ class NodeGroup {
2886
2610
  * The logic of this function is complex and could use cleaning up.
2887
2611
  *
2888
2612
  * @param el
2889
- * @param isPreHtmlElement
2613
+ * @param isPreHtmlElement {?boolean} True if the element's tag name ends with -solarite-placeholder
2890
2614
  * @param props {Object} Attributes with dynamic values.
2891
2615
  * @return {HTMLElement} */
2892
- createNewComponent(el, isPreHtmlElement=undefined, props=undefined) {
2616
+ instantiateComponent(el, isPreHtmlElement=undefined, props=undefined) {
2893
2617
  if (isPreHtmlElement === undefined)
2894
2618
  isPreHtmlElement = !el.hasAttribute('_is');
2895
2619
 
@@ -2903,24 +2627,24 @@ class NodeGroup {
2903
2627
  if (!Constructor)
2904
2628
  throw new Error(`The custom tag name ${tagName} is not registered.`)
2905
2629
 
2906
- let args = {};
2630
+ let attribs = {};
2907
2631
  for (let name in props || {})
2908
- args[Util.dashesToCamel(name)] = props[name];
2632
+ attribs[Util.dashesToCamel(name)] = props[name];
2909
2633
 
2910
2634
  // Pass other attribs to constructor, since otherwise they're not yet set on the element,
2911
2635
  // and the constructor would otherwise have no way to see them.
2912
2636
  if (el.attributes.length) {
2913
2637
  for (let attrib of el.attributes) {
2914
2638
  let attribName = Util.dashesToCamel(attrib.name);
2915
- if (!args.hasOwnProperty(attribName))
2916
- args[attribName] = attrib.value;
2639
+ if (!attribs.hasOwnProperty(attribName) && attribName !== 'solarite-placeholder')
2640
+ attribs[attribName] = attrib.value;
2917
2641
  }
2918
2642
  }
2919
2643
 
2920
2644
  // Create the web component.
2921
2645
  // Get the children that aren't Solarite's comment placeholders.
2922
- let ch = [...el.childNodes].filter(node => node.nodeType !== Node.COMMENT_NODE || !node.nodeValue.startsWith('ExprPath'));
2923
- let newEl = new Constructor(args, ch);
2646
+ let children = [...el.childNodes].filter(node => node.nodeType !== Node.COMMENT_NODE || !node.nodeValue.startsWith('ExprPath'));
2647
+ let newEl = new Constructor(attribs, children);
2924
2648
 
2925
2649
  if (!isPreHtmlElement)
2926
2650
  newEl.setAttribute('is', el.getAttribute('is').toLowerCase());
@@ -2954,7 +2678,7 @@ class NodeGroup {
2954
2678
 
2955
2679
  // Copy attributes over.
2956
2680
  for (let attrib of el.attributes)
2957
- if (attrib.name !== '_is')
2681
+ if (attrib.name !== '_is' && attrib.name !== 'solarite-placeholder')
2958
2682
  newEl.setAttribute(attrib.name, attrib.value);
2959
2683
 
2960
2684
  // Set dynamic attributes if they are primitive types.
@@ -3114,9 +2838,9 @@ class NodeGroup {
3114
2838
  let result = [];
3115
2839
 
3116
2840
  // static components. These are WebComponents that do not have any constructor arguments that are expressions.
3117
- // Those are instead created by applyExpr() which calls applyComponentExprs() which calls createNewcomponent().
2841
+ // Those are instead created by applyExpr() which calls applyComponentExprs() which calls instantiateComponent().
3118
2842
  // Maybe someday these two paths will be merged?
3119
- // Must happen before ids because createNewComponent will replace the element.
2843
+ // Must happen before ids because instantiateComponent will replace the element.
3120
2844
  for (let path of shell.staticComponents) {
3121
2845
  if (pathOffset)
3122
2846
  path = path.slice(0, -pathOffset);
@@ -3130,13 +2854,13 @@ class NodeGroup {
3130
2854
  return result;
3131
2855
  }
3132
2856
 
3133
- activateStaticComponents(staticComponents) {
2857
+ instantiateStaticComponents(staticComponents) {
3134
2858
  for (let el of staticComponents)
3135
- this.createNewComponent(el);
2859
+ this.instantiateComponent(el);
3136
2860
  }
3137
2861
 
3138
2862
  /**
3139
- * @param root {HTMLElement}
2863
+ * @param root {HTMLElement|DocumentFragment}
3140
2864
  * @param shell {Shell}
3141
2865
  * @param pathOffset {int} */
3142
2866
  activateEmbeds(root, shell, pathOffset=0) {
@@ -3246,7 +2970,7 @@ class RootNodeGroup extends NodeGroup {
3246
2970
 
3247
2971
  // Copy attributes
3248
2972
  for (let attrib of fragment.children[0].attributes)
3249
- if (!el.hasAttribute(attrib.name))
2973
+ if (!el.hasAttribute(attrib.name) && attrib.name !== 'solarite-placeholder')
3250
2974
  el.setAttribute(attrib.name, attrib.value);
3251
2975
 
3252
2976
  // Go one level deeper into all of shell's paths.
@@ -3306,7 +3030,7 @@ class RootNodeGroup extends NodeGroup {
3306
3030
  // Apply exprs
3307
3031
  this.applyExprs(template.exprs);
3308
3032
 
3309
- this.activateStaticComponents(staticComponents);
3033
+ this.instantiateStaticComponents(staticComponents);
3310
3034
  }
3311
3035
  }
3312
3036
  }
@@ -3330,7 +3054,7 @@ function getSingleEl(fragment) {
3330
3054
  * @returns {boolean} */
3331
3055
  function isReplaceEl(fragment, el) {
3332
3056
  return fragment.children.length===1
3333
- && el.tagName.includes('-')
3057
+ && el.tagName.includes('-') // TODO: Check for solarite-placeholder attribute instead?
3334
3058
  && fragment.children[0].tagName.replace('-SOLARITE-PLACEHOLDER', '') === el.tagName;
3335
3059
  }
3336
3060
 
@@ -3502,6 +3226,8 @@ class Template {
3502
3226
  * @return {Node|HTMLElement|Template} */
3503
3227
  function h(htmlStrings=undefined, ...exprs) {
3504
3228
 
3229
+ if (htmlStrings === undefined && !exprs.length && arguments.length)
3230
+ throw new Error('h() cannot be called with undefined.');
3505
3231
 
3506
3232
  // TODO: Make this a more flat if/else and call other functions for the logic.
3507
3233
  if (htmlStrings instanceof Node) {
@@ -3524,7 +3250,7 @@ function h(htmlStrings=undefined, ...exprs) {
3524
3250
  }
3525
3251
 
3526
3252
  // 2. Render template created by #4 to element.
3527
- else if (exprs[0] instanceof Template) {
3253
+ else { // instanceof Template
3528
3254
  let options = exprs[1];
3529
3255
  template.render(parent, options);
3530
3256
 
@@ -3535,16 +3261,6 @@ function h(htmlStrings=undefined, ...exprs) {
3535
3261
  parent.append(this.rootNg.getParentNode());
3536
3262
  }
3537
3263
  }
3538
-
3539
-
3540
-
3541
- // null for expr[0], remove whole element.
3542
- // This path never happens?
3543
- else {
3544
- throw new Error('unsupported');
3545
- //let ngm = NodeGroupManager.get(parent);
3546
- //ngm.render(null, exprs[1])
3547
- }
3548
3264
  }
3549
3265
 
3550
3266
  // 3. Path if used as a template tag.
@@ -3681,7 +3397,7 @@ let renderF = 'render';
3681
3397
  * @param el {HTMLElement}
3682
3398
  * @param attributeName {string} Attribute name. Not case-sensitive.
3683
3399
  * @param defaultValue {*} Default value to use if attribute doesn't exist.
3684
- * @param type {ArgType|function|*[]}
3400
+ * @param type {ArgType|function|Class|*[]}
3685
3401
  * If an array, use the value if it's in the array, otherwise return undefined.
3686
3402
  * If it's a function, pass the value to the function and return the result.
3687
3403
  * @param fallback {*} If the defaultValue is undefiend and type can't be parsed as the given type, use this value.
@@ -3696,8 +3412,11 @@ function getArg(el, attributeName, defaultValue=undefined, type=ArgType.String,
3696
3412
  if (Array.isArray(type))
3697
3413
  return type.includes(val) ? val : fallback;
3698
3414
 
3699
- if (typeof type === 'function')
3700
- return type(val);
3415
+ if (typeof type === 'function') {
3416
+ return type.constructor
3417
+ ? new type(val) // arg type is custom Class
3418
+ : type(val); // arg type is custom function
3419
+ }
3701
3420
 
3702
3421
  // If bool, it's true as long as it exists and its value isn't falsey.
3703
3422
  if (type===ArgType.Bool) {
@@ -3739,6 +3458,24 @@ function getArg(el, attributeName, defaultValue=undefined, type=ArgType.String,
3739
3458
  }
3740
3459
  }
3741
3460
 
3461
+
3462
+ /**
3463
+ * Experimental. Set multiple arguments/attributes all at once.
3464
+ * @param el {HTMLElement}
3465
+ * @param args {Record<string, any>}
3466
+ * @param types {Record<string, ArgType|function|Class>}
3467
+ *
3468
+ * @example
3469
+ * constructor({user, path}={}) {
3470
+ * setArgs(this, arguments[0], {user: User, path: ArgType.String});
3471
+ * }
3472
+ */
3473
+ function setArgs(el, args, types) {
3474
+ for (let name in args)
3475
+ this[name] = getArg(el, name, args[name], types[name] || ArgType.String);
3476
+ }
3477
+
3478
+
3742
3479
  /**
3743
3480
  * @enum */
3744
3481
  var ArgType = {
@@ -3767,6 +3504,158 @@ var ArgType = {
3767
3504
  Eval: 'Eval'
3768
3505
  };
3769
3506
 
3507
+ function defineClass(Class, tagName, extendsTag) {
3508
+ if (!customElements[getName](Class)) { // If not previously defined.
3509
+ tagName = tagName || Util.camelToDashes(Class.name);
3510
+ if (!tagName.includes('-'))
3511
+ tagName += '-element';
3512
+
3513
+ let options = null;
3514
+ if (extendsTag)
3515
+ options = {extends: extendsTag};
3516
+
3517
+ customElements[define](tagName, Class, options);
3518
+ }
3519
+ }
3520
+
3521
+ /**
3522
+ * Create a version of the Solarite class that extends from the given tag name.
3523
+ * Reasons to inherit from this instead of HTMLElement. None of these are all that useful.
3524
+ * 1. customElements.define() is called automatically when you create the first instance.
3525
+ * 2. Calls render() when added to the DOM, if it hasn't been called already.
3526
+ * 3. Child elements are added before constructor is called. But they're also passed to the constructor. (deprecated?)
3527
+ * 4. We can use this.html = r`...` to set html. (deprecated)
3528
+ * 5. We have the onConnect, onFirstConnect, and onDisconnect methods.
3529
+ * Can't figure out how to have these work standalone though, and still be synchronous.
3530
+ * 6. Can we extend from other element types like TR?
3531
+ * 7. Shows default text if render() function isn't defined.
3532
+ *
3533
+ * Advantages to inheriting from HTMLElement
3534
+ * 1. Minimization won't break when it renames the Class and we call customElements.define() on the wrong name.
3535
+ * 2. We can inherit from things like HTMLTableRowElement directly.
3536
+ * 3. There's less magic, since everyone is familiar with defining custom elements.
3537
+ *
3538
+ * @param extendsTag {?string}
3539
+ * @return {Class} */
3540
+ function createSolarite(extendsTag=null) {
3541
+
3542
+ let BaseClass = HTMLElement;
3543
+ if (extendsTag && !extendsTag.includes('-')) {
3544
+ extendsTag = extendsTag.toLowerCase();
3545
+
3546
+ BaseClass = Globals$1.elementClasses[extendsTag];
3547
+ if (!BaseClass) { // TODO: Use Cache
3548
+ BaseClass = document.createElement(extendsTag).constructor;
3549
+ Globals$1.elementClasses[extendsTag] = BaseClass;
3550
+ }
3551
+ }
3552
+
3553
+ /**
3554
+ * Intercept the construct call to auto-define the class before the constructor is called.
3555
+ * @type {HTMLElement} */
3556
+ let HTMLElementAutoDefine = new Proxy(BaseClass, {
3557
+ construct(Parent, args, Class) {
3558
+ defineClass(Class, null, extendsTag);
3559
+
3560
+ // This is a good place to manipulate any args before they're sent to the constructor.
3561
+ // Such as loading them from attributes, if I could find a way to do so.
3562
+
3563
+ // This line is equivalent the to super() call.
3564
+ return Reflect.construct(Parent, args, Class);
3565
+ }
3566
+ });
3567
+
3568
+ return class Solarite extends HTMLElementAutoDefine {
3569
+
3570
+
3571
+ /**
3572
+ * TODO: Make these standalone functions.
3573
+ * Callbacks.
3574
+ * Use onConnect.push(() => ...); to add new callbacks. */
3575
+ onConnect;
3576
+
3577
+ onFirstConnect;
3578
+ onDisconnect;
3579
+
3580
+ /**
3581
+ * @param options {RenderOptions} */
3582
+ constructor(options={}) {
3583
+ super();
3584
+
3585
+ // TODO: Is options.render ever used?
3586
+ if (options.render===true)
3587
+ this.render();
3588
+
3589
+ else if (options.render===false)
3590
+ Globals$1.rendered.add(this); // Don't render on connectedCallback()
3591
+
3592
+ // Add slot children before constructor code executes.
3593
+ // This breaks the styleStaticNested test.
3594
+ // PendingChildren is setup in NodeGroup.instantiateComponent()
3595
+ // TODO: Match named slots.
3596
+ //let ch = Globals.pendingChildren.pop();
3597
+ //if (ch) // TODO: how could there be a slot before render is called?
3598
+ // (this.querySelector('slot') || this).append(...ch);
3599
+
3600
+ /** @deprecated
3601
+ Object.defineProperty(this, 'html', {
3602
+ set(html) {
3603
+ Globals.rendered.add(this);
3604
+ if (typeof html === 'string') {
3605
+ console.warn("Assigning to this.html without the r template prefix.")
3606
+ this.innerHTML = html;
3607
+ }
3608
+ else
3609
+ this.modifications = r(this, html, options);
3610
+ }
3611
+ })*/
3612
+
3613
+ /*
3614
+ let pthis = new Proxy(this, {
3615
+ get(obj, prop) {
3616
+ return Reflect.get(obj, prop)
3617
+ }
3618
+ });
3619
+ this.render = this.render.bind(pthis);
3620
+ */
3621
+ }
3622
+
3623
+ /**
3624
+ * Call render() only if it hasn't already been called. */
3625
+ renderFirstTime() {
3626
+ if (!Globals$1.rendered.has(this) && this.render)
3627
+ this.render();
3628
+ }
3629
+
3630
+ /**
3631
+ * Called automatically by the browser. */
3632
+ connectedCallback() {
3633
+ this.renderFirstTime();
3634
+ if (!Globals$1.connected.has(this)) {
3635
+ Globals$1.connected.add(this);
3636
+ if (this.onFirstConnect)
3637
+ this.onFirstConnect();
3638
+ }
3639
+ if (this.onConnect)
3640
+ this.onConnect();
3641
+ }
3642
+
3643
+ disconnectedCallback() {
3644
+ if (this.onDisconnect)
3645
+ this.onDisconnect();
3646
+ }
3647
+
3648
+
3649
+ static define(tagName=null) {
3650
+ defineClass(this, tagName, extendsTag);
3651
+ }
3652
+ }
3653
+ }
3654
+
3655
+ // Trick to prevent minifier from renaming this method.
3656
+ let define = 'define';
3657
+ let getName = 'getName';
3658
+
3770
3659
  /**
3771
3660
  * Solarite JavasCript UI library.
3772
3661
  * MIT License
@@ -3776,15 +3665,13 @@ var ArgType = {
3776
3665
  /**
3777
3666
  * TODO: The Proxy and the multiple base classes mess up 'instanceof Solarite'
3778
3667
  * @type {Node|Class<HTMLElement>|function(tagName:string):Node|Class<HTMLElement>} */
3779
- let Solarite = new Proxy(createSolarite(), {
3668
+ const Solarite = new Proxy(createSolarite(), {
3780
3669
  apply(self, _, args) {
3781
3670
  return createSolarite(...args)
3782
3671
  }
3783
3672
  });
3784
- let getInputValue = Util.getInputValue;
3785
3673
 
3786
- //Experimental:
3787
3674
  //export {default as watch, renderWatched} from './watch.js'; // unfinished
3788
3675
 
3789
3676
  export default h;
3790
- export { ArgType, Globals$1 as Globals, Solarite, Util as SolariteUtil, Template, delve, getArg, getInputValue, h, h as r };
3677
+ export { ArgType, Globals$1 as Globals, Solarite, Util as SolariteUtil, Template, delve, getArg, h, h as r, setArgs };