aberdeen 0.0.17 → 0.1.0

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.
package/README.md CHANGED
@@ -116,3 +116,15 @@ mount(document.body, () => {
116
116
  ## Reference documentation
117
117
 
118
118
  https://vanviegen.github.io/aberdeen/modules.html
119
+
120
+
121
+ ## Roadmap
122
+
123
+ - [x] Support for (dis)appear transitions.
124
+ - [x] A better alternative for scheduleTask.
125
+ - [ ] A simple router.
126
+ - [ ] Architecture document.
127
+ - [x] Optimistic client-side predictions.
128
+ - [ ] SVG support.
129
+ - [ ] More user friendly documentation generator.
130
+ - [ ] Performance profiling and tuning regarding lists.
@@ -2,21 +2,43 @@ interface QueueRunner {
2
2
  queueOrder: number;
3
3
  queueRun(): void;
4
4
  }
5
+ type Patch = Map<ObsCollection, Map<any, [any, any]>>;
5
6
  /**
6
- * Schedule a function to be executed by Aberdeen's internal task queue. This
7
- * can be useful to batch together DOM layout read operations and DOM write
8
- * operations, so that we're not forcing the browser to do more layout calculations
9
- * than needed. Also, unlike setTimeout or requestAnimationFrame, this doesn't
10
- * give the browser the chance to render partial DOM states to the screen (which
11
- * would be seen as glitches/flashes).
12
- * @param func The function to be called soon.
13
- * @param order Higher mean later. Defaults to 0, which would still be *after* all
14
- * node/observe redraws have been handled.
7
+ * Schedule a DOM read operation to be executed in Aberdeen's internal task queue.
15
8
  *
16
- * **EXPERIMENTAL:** There's a good chance this function will be replaced by
17
- * something that explicitly addresses DOM layout reads and DOM writes.
9
+ * This function is used to batch DOM read operations together, avoiding unnecessary
10
+ * layout recalculations and improving browser performance. A DOM read operation should
11
+ * only *read* from the DOM, such as measuring element dimensions or retrieving computed styles.
12
+ *
13
+ * By batching DOM reads separately from DOM writes, this prevents the browser from
14
+ * interleaving layout reads and writes, which can force additional layout recalculations.
15
+ * This helps reduce visual glitches and flashes by ensuring the browser doesn't render
16
+ * intermediate DOM states during updates.
17
+ *
18
+ * Unlike `setTimeout` or `requestAnimationFrame`, this mechanism ensures that DOM read
19
+ * operations happen before any DOM writes in the same queue cycle, minimizing layout thrashing.
20
+ *
21
+ * @param func The function to be executed as a DOM read operation.
22
+ */
23
+ export declare function scheduleDomReader(func: () => void): void;
24
+ /**
25
+ * Schedule a DOM write operation to be executed in Aberdeen's internal task queue.
26
+ *
27
+ * This function is used to batch DOM write operations together, avoiding unnecessary
28
+ * layout recalculations and improving browser performance. A DOM write operation should
29
+ * only *write* to the DOM, such as modifying element properties or applying styles.
30
+ *
31
+ * By batching DOM writes separately from DOM reads, this prevents the browser from
32
+ * interleaving layout reads and writes, which can force additional layout recalculations.
33
+ * This helps reduce visual glitches and flashes by ensuring the browser doesn't render
34
+ * intermediate DOM states during updates.
35
+ *
36
+ * Unlike `setTimeout` or `requestAnimationFrame`, this mechanism ensures that DOM write
37
+ * operations happen after all DOM reads in the same queue cycle, minimizing layout thrashing.
38
+ *
39
+ * @param func The function to be executed as a DOM write operation.
18
40
  */
19
- export declare function scheduleTask(func: () => void, order?: number): void;
41
+ export declare function scheduleDomWriter(func: () => void): void;
20
42
  type SortKeyType = number | string | Array<number | string>;
21
43
  interface Observer {
22
44
  onChange(index: any, newData: DatumType, oldData: DatumType): void;
@@ -630,21 +652,47 @@ export declare function mount(parentElement: Element | undefined, func: () => vo
630
652
  */
631
653
  export declare function peek<T>(func: () => T): T;
632
654
  /** Do a grow transition for the given element. This is meant to be used as a
633
- * handler for the `create` property.
634
- *
635
- * @param el The element to transition.
636
- *
637
- * The transition doesn't look great for table elements, and may have problems
638
- * for other specific cases as well.
639
- */
655
+ * handler for the `create` property.
656
+ *
657
+ * @param el The element to transition.
658
+ *
659
+ * The transition doesn't look great for table elements, and may have problems
660
+ * for other specific cases as well.
661
+ */
640
662
  export declare function grow(el: HTMLElement): void;
641
663
  /** Do a shrink transition for the given element, and remove it from the DOM
642
- * afterwards. This is meant to be used as a handler for the `destroy` property.
643
- *
644
- * @param el The element to transition and remove.
664
+ * afterwards. This is meant to be used as a handler for the `destroy` property.
665
+ *
666
+ * @param el The element to transition and remove.
667
+ *
668
+ * The transition doesn't look great for table elements, and may have problems
669
+ * for other specific cases as well.
670
+ */
671
+ export declare function shrink(el: HTMLElement): void;
672
+ /**
673
+ * Run the provided function, while treating all changes to Observables as predictions,
674
+ * meaning they will be reverted when changes come back from the server (or some other
675
+ * async source).
676
+ * @param predictFunc The function to run. It will generally modify some Observables
677
+ * to immediately reflect state (as closely as possible) that we expect the server
678
+ * to communicate back to us later on.
679
+ * @returns A `Patch` object. Don't modify it. This is only meant to be passed to `applyCanon`.
680
+ */
681
+ export declare function applyPrediction(predictFunc: () => void): Patch;
682
+ /**
683
+ * Temporarily revert all outstanding predictions, optionally run the provided function
684
+ * (which will generally make authoritative changes to the data based on a server response),
685
+ * and then attempt to reapply the predictions on top of the new canonical state, dropping
686
+ * any predictions that can no longer be applied cleanly (the data has been modified) or
687
+ * that were specified in `dropPredictions`.
645
688
  *
646
- * The transition doesn't look great for table elements, and may have problems
647
- * for other specific cases as well.
689
+ * All of this is done such that redraws are only triggered if the overall effect is an
690
+ * actual change to an `Observable`.
691
+ * @param canonFunc The function to run without any predictions applied. This will typically
692
+ * make authoritative changes to the data, based on a server response.
693
+ * @param dropPredictions An optional list of predictions (as returned by `applyPrediction`)
694
+ * to undo. Typically, when a server response for a certain request is being handled,
695
+ * you'd want to drop the prediction that was done for that request.
648
696
  */
649
- export declare function shrink(el: HTMLElement): void;
697
+ export declare function applyCanon(canonFunc?: (() => void), dropPredictions?: Array<Patch>): void;
650
698
  export {};
package/dist/aberdeen.js CHANGED
@@ -2,6 +2,8 @@ let queueArray = [];
2
2
  let queueSet = new Set();
3
3
  let queueOrdered = true;
4
4
  let runQueueDepth = 0;
5
+ let queueIndex;
6
+ let recordingPatch;
5
7
  function queue(runner) {
6
8
  if (queueSet.has(runner))
7
9
  return;
@@ -19,42 +21,73 @@ function queue(runner) {
19
21
  }
20
22
  function runQueue() {
21
23
  onCreateEnabled = true;
22
- for (let index = 0; index < queueArray.length;) {
24
+ for (queueIndex = 0; queueIndex < queueArray.length;) {
25
+ // Sort queue if new unordered items have been added since last time.
23
26
  if (!queueOrdered) {
24
- queueArray.splice(0, index);
25
- index = 0;
26
- // Order queued observers by depth, lowest first
27
+ queueArray.splice(0, queueIndex);
28
+ queueIndex = 0;
29
+ // Order queued observers by depth, lowest first.
27
30
  queueArray.sort((a, b) => a.queueOrder - b.queueOrder);
28
31
  queueOrdered = true;
29
32
  }
33
+ // Process the rest of what's currently in the queue.
30
34
  let batchEndIndex = queueArray.length;
31
- for (; index < batchEndIndex && queueOrdered; index++) {
32
- let runner = queueArray[index];
35
+ for (; queueIndex < batchEndIndex && queueOrdered; queueIndex++) {
36
+ let runner = queueArray[queueIndex];
33
37
  queueSet.delete(runner);
34
38
  runner.queueRun();
35
39
  }
40
+ // If new items have been added to the queue while processing the previous
41
+ // batch, we'll need to run this loop again.
36
42
  runQueueDepth++;
37
43
  }
38
44
  queueArray.length = 0;
45
+ queueIndex = undefined;
39
46
  runQueueDepth = 0;
40
47
  onCreateEnabled = false;
41
48
  }
49
+ let scheduleOrder = 1000;
42
50
  /**
43
- * Schedule a function to be executed by Aberdeen's internal task queue. This
44
- * can be useful to batch together DOM layout read operations and DOM write
45
- * operations, so that we're not forcing the browser to do more layout calculations
46
- * than needed. Also, unlike setTimeout or requestAnimationFrame, this doesn't
47
- * give the browser the chance to render partial DOM states to the screen (which
48
- * would be seen as glitches/flashes).
49
- * @param func The function to be called soon.
50
- * @param order Higher mean later. Defaults to 0, which would still be *after* all
51
- * node/observe redraws have been handled.
51
+ * Schedule a DOM read operation to be executed in Aberdeen's internal task queue.
52
52
  *
53
- * **EXPERIMENTAL:** There's a good chance this function will be replaced by
54
- * something that explicitly addresses DOM layout reads and DOM writes.
53
+ * This function is used to batch DOM read operations together, avoiding unnecessary
54
+ * layout recalculations and improving browser performance. A DOM read operation should
55
+ * only *read* from the DOM, such as measuring element dimensions or retrieving computed styles.
56
+ *
57
+ * By batching DOM reads separately from DOM writes, this prevents the browser from
58
+ * interleaving layout reads and writes, which can force additional layout recalculations.
59
+ * This helps reduce visual glitches and flashes by ensuring the browser doesn't render
60
+ * intermediate DOM states during updates.
61
+ *
62
+ * Unlike `setTimeout` or `requestAnimationFrame`, this mechanism ensures that DOM read
63
+ * operations happen before any DOM writes in the same queue cycle, minimizing layout thrashing.
64
+ *
65
+ * @param func The function to be executed as a DOM read operation.
66
+ */
67
+ export function scheduleDomReader(func) {
68
+ let order = (queueIndex != null && queueIndex < queueArray.length && queueArray[queueIndex].queueOrder >= 1000) ? ((queueArray[queueIndex].queueOrder + 1) & (~1)) : 1000;
69
+ queue({ queueOrder: order, queueRun: func });
70
+ }
71
+ /**
72
+ * Schedule a DOM write operation to be executed in Aberdeen's internal task queue.
73
+ *
74
+ * This function is used to batch DOM write operations together, avoiding unnecessary
75
+ * layout recalculations and improving browser performance. A DOM write operation should
76
+ * only *write* to the DOM, such as modifying element properties or applying styles.
77
+ *
78
+ * By batching DOM writes separately from DOM reads, this prevents the browser from
79
+ * interleaving layout reads and writes, which can force additional layout recalculations.
80
+ * This helps reduce visual glitches and flashes by ensuring the browser doesn't render
81
+ * intermediate DOM states during updates.
82
+ *
83
+ * Unlike `setTimeout` or `requestAnimationFrame`, this mechanism ensures that DOM write
84
+ * operations happen after all DOM reads in the same queue cycle, minimizing layout thrashing.
85
+ *
86
+ * @param func The function to be executed as a DOM write operation.
55
87
  */
56
- export function scheduleTask(func, order = 0) {
57
- queue({ queueOrder: 1000 + order, queueRun: func });
88
+ export function scheduleDomWriter(func) {
89
+ let order = (queueIndex != null && queueIndex < queueArray.length && queueArray[queueIndex].queueOrder >= 1000) ? (queueArray[queueIndex].queueOrder | 1) : 1001;
90
+ queue({ queueOrder: order, queueRun: func });
58
91
  }
59
92
  /**
60
93
  * Given an integer number, a string or an array of these, this function returns a string that can be used
@@ -490,12 +523,17 @@ class ObsCollection {
490
523
  obsSet.delete(observer);
491
524
  }
492
525
  emitChange(index, newData, oldData) {
493
- let obsSet = this.observers.get(index);
494
- if (obsSet)
495
- obsSet.forEach(observer => observer.onChange(index, newData, oldData));
496
- obsSet = this.observers.get(ANY_INDEX);
497
- if (obsSet)
498
- obsSet.forEach(observer => observer.onChange(index, newData, oldData));
526
+ if (recordingPatch) {
527
+ addToPatch(recordingPatch, this, index, newData, oldData);
528
+ }
529
+ else {
530
+ let obsSet = this.observers.get(index);
531
+ if (obsSet)
532
+ obsSet.forEach(observer => observer.onChange(index, newData, oldData));
533
+ obsSet = this.observers.get(ANY_INDEX);
534
+ if (obsSet)
535
+ obsSet.forEach(observer => observer.onChange(index, newData, oldData));
536
+ }
499
537
  }
500
538
  _clean(observer) {
501
539
  this.removeObserver(ANY_INDEX, observer);
@@ -1182,7 +1220,7 @@ export class Store {
1182
1220
  result.forEach((value, key) => {
1183
1221
  out.set(key, value);
1184
1222
  });
1185
- keys = Array.from(result.keys());
1223
+ keys = [...result.keys()];
1186
1224
  }
1187
1225
  else {
1188
1226
  return;
@@ -1627,56 +1665,171 @@ function getGrowShrinkProps(el) {
1627
1665
  { marginBottom: `-${el.offsetHeight / 2}px`, marginTop: `-${el.offsetHeight / 2}px`, transform: "scaleY(0)" };
1628
1666
  }
1629
1667
  /** Do a grow transition for the given element. This is meant to be used as a
1630
- * handler for the `create` property.
1631
- *
1632
- * @param el The element to transition.
1633
- *
1634
- * The transition doesn't look great for table elements, and may have problems
1635
- * for other specific cases as well.
1636
- */
1668
+ * handler for the `create` property.
1669
+ *
1670
+ * @param el The element to transition.
1671
+ *
1672
+ * The transition doesn't look great for table elements, and may have problems
1673
+ * for other specific cases as well.
1674
+ */
1637
1675
  export function grow(el) {
1638
1676
  // This timeout is to await all other elements having been added to the Dom
1639
- scheduleTask(() => {
1677
+ scheduleDomReader(() => {
1640
1678
  // Make the element size 0 using transforms and negative margins.
1641
1679
  // This causes a browser layout, as we're querying el.offset<>.
1642
1680
  let props = getGrowShrinkProps(el);
1643
1681
  // The timeout is in order to batch all reads and then all writes when there
1644
1682
  // are multiple simultaneous grow transitions.
1645
- scheduleTask(() => {
1683
+ scheduleDomWriter(() => {
1646
1684
  Object.assign(el.style, props);
1647
1685
  // This timeout is to combine multiple transitions into a single browser layout
1648
- scheduleTask(() => {
1686
+ scheduleDomReader(() => {
1649
1687
  // Make sure the layouting has been performed, to cause transitions to trigger
1650
1688
  el.offsetHeight;
1651
- // Do the transitions
1652
- el.style.transition = GROW_SHRINK_TRANSITION;
1653
- for (let prop in props)
1654
- el.style[prop] = "";
1655
- setTimeout(() => {
1656
- // Reset the element to a clean state
1657
- el.style.transition = "";
1658
- }, FADE_TIME);
1659
- }, 2);
1660
- }, 1);
1689
+ scheduleDomWriter(() => {
1690
+ // Do the transitions
1691
+ el.style.transition = GROW_SHRINK_TRANSITION;
1692
+ for (let prop in props)
1693
+ el.style[prop] = "";
1694
+ setTimeout(() => {
1695
+ // Reset the element to a clean state
1696
+ el.style.transition = "";
1697
+ }, FADE_TIME);
1698
+ });
1699
+ });
1700
+ });
1661
1701
  });
1662
1702
  }
1663
1703
  /** Do a shrink transition for the given element, and remove it from the DOM
1664
- * afterwards. This is meant to be used as a handler for the `destroy` property.
1665
- *
1666
- * @param el The element to transition and remove.
1704
+ * afterwards. This is meant to be used as a handler for the `destroy` property.
1705
+ *
1706
+ * @param el The element to transition and remove.
1707
+ *
1708
+ * The transition doesn't look great for table elements, and may have problems
1709
+ * for other specific cases as well.
1710
+ */
1711
+ export function shrink(el) {
1712
+ scheduleDomReader(() => {
1713
+ const props = getGrowShrinkProps(el);
1714
+ // The timeout is in order to batch all reads and then all writes when there
1715
+ // are multiple simultaneous shrink transitions.
1716
+ scheduleDomWriter(() => {
1717
+ el.style.transition = GROW_SHRINK_TRANSITION;
1718
+ Object.assign(el.style, props);
1719
+ setTimeout(() => el.remove(), FADE_TIME);
1720
+ });
1721
+ });
1722
+ }
1723
+ function recordPatch(func) {
1724
+ if (recordingPatch)
1725
+ throw new Error(`already recording a patch`);
1726
+ recordingPatch = new Map();
1727
+ try {
1728
+ func();
1729
+ }
1730
+ catch (e) {
1731
+ recordingPatch = undefined;
1732
+ throw e;
1733
+ }
1734
+ const result = recordingPatch;
1735
+ recordingPatch = undefined;
1736
+ return result;
1737
+ }
1738
+ function addToPatch(patch, collection, index, newData, oldData) {
1739
+ let collectionMap = patch.get(collection);
1740
+ if (collectionMap == null) {
1741
+ collectionMap = new Map();
1742
+ patch.set(collection, collectionMap);
1743
+ }
1744
+ let prev = collectionMap.get(index);
1745
+ collectionMap.set(index, [newData, prev == null ? oldData : prev[1]]);
1746
+ }
1747
+ function emitPatch(patch) {
1748
+ for (let [collection, collectionMap] of patch) {
1749
+ for (let [index, [newData, oldData]] of collectionMap) {
1750
+ collection.emitChange(index, newData, oldData);
1751
+ }
1752
+ }
1753
+ }
1754
+ function mergePatch(target, source, reverse = false) {
1755
+ for (let [collection, collectionMap] of source) {
1756
+ for (let [index, [newData, oldData]] of collectionMap) {
1757
+ addToPatch(target, collection, index, reverse ? oldData : newData, reverse ? newData : oldData);
1758
+ }
1759
+ }
1760
+ }
1761
+ function silentlyApplyPatch(patch, force = false) {
1762
+ for (let [collection, collectionMap] of patch) {
1763
+ for (let [index, [newData, oldData]] of collectionMap) {
1764
+ let actualData = collection.rawGet(index);
1765
+ if (actualData !== oldData) {
1766
+ if (force)
1767
+ handleError(new Error(`Applying invalid patch: data ${actualData} is unequal to expected old data ${oldData} for index ${index}`));
1768
+ else
1769
+ return false;
1770
+ }
1771
+ }
1772
+ }
1773
+ for (let [collection, collectionMap] of patch) {
1774
+ for (let [index, [newData, oldData]] of collectionMap) {
1775
+ collection.rawSet(index, newData);
1776
+ }
1777
+ }
1778
+ return true;
1779
+ }
1780
+ const appliedPredictions = [];
1781
+ /**
1782
+ * Run the provided function, while treating all changes to Observables as predictions,
1783
+ * meaning they will be reverted when changes come back from the server (or some other
1784
+ * async source).
1785
+ * @param predictFunc The function to run. It will generally modify some Observables
1786
+ * to immediately reflect state (as closely as possible) that we expect the server
1787
+ * to communicate back to us later on.
1788
+ * @returns A `Patch` object. Don't modify it. This is only meant to be passed to `applyCanon`.
1789
+ */
1790
+ export function applyPrediction(predictFunc) {
1791
+ let patch = recordPatch(predictFunc);
1792
+ appliedPredictions.push(patch);
1793
+ emitPatch(patch);
1794
+ return patch;
1795
+ }
1796
+ /**
1797
+ * Temporarily revert all outstanding predictions, optionally run the provided function
1798
+ * (which will generally make authoritative changes to the data based on a server response),
1799
+ * and then attempt to reapply the predictions on top of the new canonical state, dropping
1800
+ * any predictions that can no longer be applied cleanly (the data has been modified) or
1801
+ * that were specified in `dropPredictions`.
1667
1802
  *
1668
- * The transition doesn't look great for table elements, and may have problems
1669
- * for other specific cases as well.
1803
+ * All of this is done such that redraws are only triggered if the overall effect is an
1804
+ * actual change to an `Observable`.
1805
+ * @param canonFunc The function to run without any predictions applied. This will typically
1806
+ * make authoritative changes to the data, based on a server response.
1807
+ * @param dropPredictions An optional list of predictions (as returned by `applyPrediction`)
1808
+ * to undo. Typically, when a server response for a certain request is being handled,
1809
+ * you'd want to drop the prediction that was done for that request.
1670
1810
  */
1671
- export function shrink(el) {
1672
- const props = getGrowShrinkProps(el);
1673
- // The timeout is in order to batch all reads and then all writes when there
1674
- // are multiple simultaneous shrink transitions.
1675
- scheduleTask(() => {
1676
- el.style.transition = GROW_SHRINK_TRANSITION;
1677
- Object.assign(el.style, props);
1678
- setTimeout(() => el.remove(), FADE_TIME);
1679
- }, 1);
1811
+ export function applyCanon(canonFunc, dropPredictions = []) {
1812
+ let resultPatch = new Map();
1813
+ for (let prediction of appliedPredictions)
1814
+ mergePatch(resultPatch, prediction, true);
1815
+ silentlyApplyPatch(resultPatch, true);
1816
+ for (let prediction of dropPredictions) {
1817
+ let pos = appliedPredictions.indexOf(prediction);
1818
+ if (pos >= 0)
1819
+ appliedPredictions.splice(pos, 1);
1820
+ }
1821
+ if (canonFunc)
1822
+ mergePatch(resultPatch, recordPatch(canonFunc));
1823
+ for (let idx = 0; idx < appliedPredictions.length; idx++) {
1824
+ if (silentlyApplyPatch(appliedPredictions[idx])) {
1825
+ mergePatch(resultPatch, appliedPredictions[idx]);
1826
+ }
1827
+ else {
1828
+ appliedPredictions.splice(idx, 1);
1829
+ idx--;
1830
+ }
1831
+ }
1832
+ emitPatch(resultPatch);
1680
1833
  }
1681
1834
  // @ts-ignore
1682
1835
  // istanbul ignore next
@@ -1 +1 @@
1
- let e,t=[],i=new Set,n=!0,r=0;function s(e){if(!i.has(e)){if(r>42)throw new Error("Too many recursive updates from observes");t.length?e.queueOrder<t[t.length-1].queueOrder&&(n=!1):setTimeout(o,0),t.push(e),i.add(e)}}function o(){w=!0;for(let e=0;e<t.length;){n||(t.splice(0,e),e=0,t.sort((e,t)=>e.queueOrder-t.queueOrder),n=!0);let s=t.length;for(;e<s&&n;e++){let n=t[e];i.delete(n),n.queueRun()}r++}t.length=0,r=0,w=!1}export function scheduleTask(e,t=0){s({queueOrder:1e3+t,queueRun:e})}function a(e){if("string"==typeof e)return e+"";{let t=function(e,t){let i="";for(;e>0;)i+=String.fromCharCode(t?65535-e%65533:2+e%65533),e=Math.floor(e/65533);return i}(Math.abs(Math.round(e)),e<0);return String.fromCharCode(128+(e>0?t.length:-t.length))+t}}class l{constructor(e,t,i){this.cleaners=[],this.isDead=!1,this.parentElement=e,this.precedingSibling=t,this.queueOrder=i}findPrecedingNode(e){let t,i=this;for(;(t=i.precedingSibling)&&t!==e;){if(t instanceof Node)return t;let e=t.findLastNode();if(e)return e;i=t}}findLastNode(){if(this.lastChild)return this.lastChild instanceof Node?this.lastChild:this.lastChild.findLastNode()||this.lastChild.findPrecedingNode(this.precedingSibling)}addNode(e){if(!this.parentElement)throw new k(!0);let t=this.findLastNode()||this.findPrecedingNode();this.parentElement.insertBefore(e,t?t.nextSibling:this.parentElement.firstChild),this.lastChild=e}remove(){if(this.parentElement){let e=this.findLastNode();if(e){let t=this.findPrecedingNode();for(t=t?t.nextSibling:this.parentElement.firstChild,this.lastChild=void 0;;){if(!t)return I(1);const i=t;t=i.nextSibling||void 0;let n=b.get(i);if(n&&i instanceof Element?!0!==n&&("function"==typeof n?n(i):x(i,n),b.set(i,!0)):this.parentElement.removeChild(i),i===e)break}}}this._clean()}_clean(){this.isDead=!0;for(let e of this.cleaners)e._clean(this);this.cleaners.length=0}onChange(e,t,i){s(this)}}class h extends l{constructor(e,t,i,n){super(e,t,i),this.renderer=n}queueRun(){e&&I(2),this.isDead||(this.remove(),this.isDead=!1,this.update())}update(){let t=e;e=this;try{this.renderer()}catch(e){N(e)}e=t}}class d{constructor(e,t,i){this.scope=e,this.collection=t,this.triggerCount=i,this.count=t.getCount(),t.addObserver(f,this),e.cleaners.push(this)}onChange(e,t,i){void 0===t?!this.triggerCount&&--this.count||s(this.scope):void 0===i&&(!this.triggerCount&&this.count++||s(this.scope))}_clean(){this.collection.removeObserver(f,this)}}class c extends l{constructor(e,t,i,n,r,s){super(e,t,i),this.byPosition=[],this.byIndex=new Map,this.newIndexes=new Set,this.removedIndexes=new Set,this.collection=n,this.renderer=r,this.makeSortKey=s}onChange(e,t,i){void 0===i?this.removedIndexes.has(e)?this.removedIndexes.delete(e):(this.newIndexes.add(e),s(this)):void 0===t&&(this.newIndexes.has(e)?this.newIndexes.delete(e):(this.removedIndexes.add(e),s(this)))}queueRun(){if(this.isDead)return;let e=this.removedIndexes;this.removedIndexes=new Set,e.forEach(e=>{this.removeChild(e)}),e=this.newIndexes,this.newIndexes=new Set,e.forEach(e=>{this.addChild(e)})}_clean(){super._clean(),this.collection.observers.delete(this);for(const[e,t]of this.byIndex)t._clean();this.byPosition.length=0,this.byIndex.clear()}renderInitial(){if(!e)return I(3);let t=e;this.collection.iterateIndexes(this),e=t}addChild(e){let t=new u(this.parentElement,void 0,this.queueOrder+1,this,e);this.byIndex.set(e,t),t.update()}removeChild(e){let t=this.byIndex.get(e);if(!t)return I(6);t.remove(),this.byIndex.delete(e),this.removeFromPosition(t)}findPosition(e){let t=this.byPosition,i=0,n=t.length;if(!n||e>t[n-1].sortStr)return n;for(;i<n;){let r=i+n>>1;t[r].sortStr<e?i=r+1:n=r}return i}insertAtPosition(e){let t=this.findPosition(e.sortStr);this.byPosition.splice(t,0,e);let i=this.byPosition[t+1];i?(e.precedingSibling=i.precedingSibling,i.precedingSibling=e):(e.precedingSibling=this.lastChild||this.precedingSibling,this.lastChild=e)}removeFromPosition(e){if(""===e.sortStr)return;let t=this.findPosition(e.sortStr);for(;;){if(this.byPosition[t]===e){if(this.byPosition.splice(t,1),t<this.byPosition.length){let i=this.byPosition[t];if(!i)return I(8);if(i.precedingSibling!==e)return I(13);i.precedingSibling=e.precedingSibling}else{if(e!==this.lastChild)return I(12);this.lastChild=e.precedingSibling===this.precedingSibling?void 0:e.precedingSibling}return}if(++t>=this.byPosition.length||this.byPosition[t].sortStr!==e.sortStr)return I(5)}}}class u extends l{constructor(e,t,i,n,r){super(e,t,i),this.sortStr="",this.parent=n,this.itemIndex=r}queueRun(){e&&I(4),this.isDead||(this.remove(),this.isDead=!1,this.update())}update(){let t=e;e=this;let i,n=new Store(this.parent.collection,this.itemIndex);try{i=this.parent.makeSortKey(n)}catch(e){N(e)}let r=this.sortStr,s=null==i?"":(o=i)instanceof Array?o.map(a).join(""):a(o);var o;if(""!==r&&r!==s&&this.parent.removeFromPosition(this),this.sortStr=s,""!==s){s!==r&&this.parent.insertAtPosition(this);try{this.parent.renderer(n)}catch(e){N(e)}}e=t}}const f={};class p{constructor(){this.observers=new Map}addObserver(e,t){t=t;let i=this.observers.get(e);if(i){if(i.has(t))return!1;i.add(t)}else this.observers.set(e,new Set([t]));return!0}removeObserver(e,t){this.observers.get(e).delete(t)}emitChange(e,t,i){let n=this.observers.get(e);n&&n.forEach(n=>n.onChange(e,t,i)),n=this.observers.get(f),n&&n.forEach(n=>n.onChange(e,t,i))}_clean(e){this.removeObserver(f,e)}setIndex(e,t,i){const n=this.rawGet(e);if(!(n instanceof p)||t instanceof Store||!n.merge(t,i)){let i=C(t);i!==n&&(this.rawSet(e,i),this.emitChange(e,i,n))}}}class g extends p{constructor(){super(...arguments),this.data=[]}getType(){return"array"}getRecursive(t){e&&this.addObserver(f,e)&&e.cleaners.push(this);let i=[];for(let e=0;e<this.data.length;e++){let n=this.data[e];i.push(n instanceof p?t?n.getRecursive(t-1):new Store(this,e):n)}return i}rawGet(e){return this.data[e]}rawSet(e,t){if(e!==(0|e)||e<0||e>999999)throw new Error("Invalid array index "+JSON.stringify(e));for(this.data[e]=t;this.data.length>0&&void 0===this.data[this.data.length-1];)this.data.pop()}merge(e,t){if(!(e instanceof Array))return!1;for(let i=0;i<e.length;i++)this.setIndex(i,e[i],t);if(t&&this.data.length>e.length){for(let t=e.length;t<this.data.length;t++){let e=this.data[t];void 0!==e&&this.emitChange(t,void 0,e)}this.data.length=e.length}return!0}iterateIndexes(e){for(let t=0;t<this.data.length;t++)void 0!==this.data[t]&&e.addChild(t)}normalizeIndex(e){if("number"==typeof e)return e;if("string"==typeof e){let t=0|e;if(e.length&&t==e)return e}throw new Error("Invalid array index "+JSON.stringify(e))}getCount(){return this.data.length}}class m extends p{constructor(){super(...arguments),this.data=new Map}getType(){return"map"}getRecursive(t){e&&this.addObserver(f,e)&&e.cleaners.push(this);let i=new Map;return this.data.forEach((e,n)=>{i.set(n,e instanceof p?t?e.getRecursive(t-1):new Store(this,n):e)}),i}rawGet(e){return this.data.get(e)}rawSet(e,t){void 0===t?this.data.delete(e):this.data.set(e,t)}merge(e,t){return e instanceof Map&&(e.forEach((e,i)=>{this.setIndex(i,e,t)}),t&&this.data.forEach((t,i)=>{e.has(i)||this.setIndex(i,void 0,!1)}),!0)}iterateIndexes(e){this.data.forEach((t,i)=>{e.addChild(i)})}normalizeIndex(e){return e}getCount(){return this.data.size}}class v extends m{getType(){return"object"}getRecursive(t){e&&this.addObserver(f,e)&&e.cleaners.push(this);let i={};return this.data.forEach((e,n)=>{i[n]=e instanceof p?t?e.getRecursive(t-1):new Store(this,n):e}),i}merge(e,t){if(!e||e.constructor!==Object)return!1;for(let i in e)this.setIndex(i,e[i],t);return t&&this.data.forEach((t,i)=>{e.hasOwnProperty(i)||this.setIndex(i,void 0,!1)}),!0}normalizeIndex(e){let t=typeof e;if("string"===t)return e;if("number"===t)return""+e;throw new Error("Invalid object index "+JSON.stringify(e))}getCount(){let e=0;for(let t of this.data)e++;return e}}export class Store{constructor(e,t){if(void 0===t)this.collection=new g,this.idx=0,void 0!==e&&this.collection.rawSet(0,C(e));else{if(!(e instanceof p))throw new Error("1st parameter should be an ObsCollection if the 2nd is also given");this.collection=e,this.idx=t}}index(){return this.idx}_clean(e){this.collection.removeObserver(this.idx,e)}get(...e){return this.query({path:e})}peek(...e){return this.query({path:e,peek:!0})}getNumber(...e){return this.query({path:e,type:"number"})}getString(...e){return this.query({path:e,type:"string"})}getBoolean(...e){return this.query({path:e,type:"boolean"})}getFunction(...e){return this.query({path:e,type:"function"})}getArray(...e){return this.query({path:e,type:"array"})}getObject(...e){return this.query({path:e,type:"object"})}getMap(...e){return this.query({path:e,type:"map"})}getOr(e,...t){let i=typeof e;return"object"===i&&(e instanceof Map?i="map":e instanceof Array&&(i="array")),this.query({type:i,defaultValue:e,path:t})}query(t){if(t.peek&&e){let i=e;e=void 0;let n=this.query(t);return e=i,n}let i=(t.path&&t.path.length?this.ref(...t.path):this)._observe();if(t.type&&(void 0!==i||void 0===t.defaultValue)){let e=i instanceof p?i.getType():null===i?"null":typeof i;if(e!==t.type)throw new TypeError(`Expecting ${t.type} but got ${e}`)}return i instanceof p?i.getRecursive(null==t.depth?-1:t.depth-1):void 0===i?t.defaultValue:i}isEmpty(...t){let i=this.ref(...t)._observe();if(i instanceof p){if(e){return!new d(e,i,!1).count}return!i.getCount()}if(void 0===i)return!0;throw new Error("isEmpty() expects a collection or undefined, but got "+JSON.stringify(i))}count(...t){let i=this.ref(...t)._observe();if(i instanceof p){if(e){return new d(e,i,!0).count}return i.getCount()}if(void 0===i)return 0;throw new Error("count() expects a collection or undefined, but got "+JSON.stringify(i))}getType(...e){let t=this.ref(...e)._observe();return t instanceof p?t.getType():null===t?"null":typeof t}set(...e){let t=e.pop(),i=this.makeRef(...e);i.collection.setIndex(i.idx,t,!0)}merge(...e){let t=e.pop(),i=this.makeRef(...e);i.collection.setIndex(i.idx,t,!1)}delete(...e){let t=this.makeRef(...e);t.collection.setIndex(t.idx,void 0,!0)}push(...e){let t=e.pop(),i=this.makeRef(...e),n=i.collection.rawGet(i.idx);if(void 0===n)n=new g,i.collection.setIndex(i.idx,n,!0);else if(!(n instanceof g))throw new Error("push() is only allowed for an array or undefined (which would become an array)");let r=C(t),s=n.data.length;return n.data.push(r),n.emitChange(s,r,void 0),s}modify(e){this.set(e(this.query({peek:!0})))}ref(...e){let t=this;for(let i=0;i<e.length;i++){let n=t._observe();if(!(n instanceof p)){if(void 0!==n)throw new Error(`Value ${JSON.stringify(n)} is not a collection (nor undefined) in step ${i} of $(${JSON.stringify(e)})`);return new y}t=new Store(n,n.normalizeIndex(e[i]))}return t}makeRef(...e){let t=this;for(let i=0;i<e.length;i++){let n=t.collection.rawGet(t.idx);if(!(n instanceof p)){if(void 0!==n)throw new Error(`Value ${JSON.stringify(n)} is not a collection (nor undefined) in step ${i} of $(${JSON.stringify(e)})`);n=new v,t.collection.rawSet(t.idx,n),t.collection.emitChange(t.idx,n,void 0)}t=new Store(n,n.normalizeIndex(e[i]))}return t}_observe(){return e&&this.collection.addObserver(this.idx,e)&&e.cleaners.push(this),this.collection.rawGet(this.idx)}onEach(...t){let i=O,n=t.pop();if("function"!=typeof t[t.length-1]||"function"!=typeof n&&null!=n||(null!=n&&(i=n),n=t.pop()),"function"!=typeof n)throw new Error("onEach() expects a render function as its last argument but got "+JSON.stringify(n));if(!e)throw new k(!1);let r=this.ref(...t)._observe();if(r instanceof p){let t=new c(e.parentElement,e.lastChild||e.precedingSibling,e.queueOrder+1,r,n,i);r.addObserver(f,t),e.cleaners.push(t),e.lastChild=t,t.renderInitial()}else if(void 0!==r)throw new Error("onEach() attempted on a value that is neither a collection nor undefined")}map(e){let t=new Store(new Map);return this.onEach(i=>{let n=e(i);if(void 0!==n){let e=i.index();t.set(e,n),clean(()=>{t.delete(e)})}}),t}multiMap(e){let t=new Store(new Map);return this.onEach(i=>{let n,r=e(i);if(r.constructor===Object){for(let e in r)t.set(e,r[e]);n=Object.keys(r)}else{if(!(r instanceof Map))return;r.forEach((e,i)=>{t.set(i,e)}),n=Array.from(r.keys())}n.length&&clean(()=>{for(let e of n)t.delete(e)})}),t}isDetached(){return!1}dump(){let e=this.getType();"array"===e||"object"===e||"map"===e?(text("<"+e+">"),node("ul",()=>{this.onEach(e=>{node("li",()=>{text(JSON.stringify(e.index())+": "),e.dump()})})})):text(JSON.stringify(this.get()))}}class y extends Store{isDetached(){return!0}}let w=!1,b=new WeakMap;function x(e,t){e.classList.add(t),setTimeout(()=>e.remove(),2e3)}export function node(t="",...i){if(!e)throw new k(!0);let n;if(t instanceof Element)n=t;else{let e,i=t.indexOf(".");i>=0&&(e=t.substr(i+1),t=t.substr(0,i)),n=document.createElement(t||"div"),e&&(n.className=e.replaceAll("."," "))}e.addNode(n);for(let t of i){let i=typeof t;if("function"===i){let i=new h(n,void 0,e.queueOrder+1,t);w?(w=!1,i.update(),w=!0):i.update(),e.cleaners.push(i)}else if("string"===i||"number"===i)n.textContent=t;else if("object"===i&&t&&t.constructor===Object)for(let e in t)E(n,e,t[e]);else if(t instanceof Store)S(n,t);else if(null!=t)throw new Error("Unexpected argument "+JSON.stringify(t))}}export function html(t){if(!e||!e.parentElement)throw new k(!0);let i=document.createElement(e.parentElement.tagName);for(i.innerHTML=""+t;i.firstChild;)e.addNode(i.firstChild)}function S(e,t){let i,n,r=e.getAttribute("type"),s=t.query({peek:!0});"checkbox"===r?(void 0===s&&t.set(e.checked),i=t=>e.checked=t,n=()=>t.set(e.checked)):"radio"===r?(void 0===s&&e.checked&&t.set(e.value),i=t=>e.checked=t===e.value,n=()=>{e.checked&&t.set(e.value)}):(n=()=>t.set("number"===r||"range"===r?""===e.value?null:+e.value:e.value),void 0===s&&n(),i=t=>{e.value!==t&&(e.value=t)}),observe(()=>{i(t.get())}),e.addEventListener("input",n),clean(()=>{e.removeEventListener("input",n)})}export function text(t){if(!e)throw new k(!0);null!=t&&e.addNode(document.createTextNode(t))}export function prop(t,i){if(!e||!e.parentElement)throw new k(!0);if("object"==typeof t)for(let i in t)E(e.parentElement,i,t[i]);else E(e.parentElement,t,i)}export function getParentElement(){if(!e||!e.parentElement)throw new k(!0);return e.parentElement}export function clean(t){if(!e)throw new k(!1);e.cleaners.push({_clean:t})}export function observe(e){mount(void 0,e)}export function mount(t,i){let n;t||!e?n=new h(t,void 0,0,i):(n=new h(e.parentElement,e.lastChild||e.precedingSibling,e.queueOrder+1,i),e.lastChild=n),n.update(),e&&e.cleaners.push(n)}export function peek(t){let i=e;e=void 0;try{return t()}finally{e=i}}function E(e,t,i){if("create"===t)w&&("function"==typeof i?i(e):(e.classList.add(i),setTimeout((function(){e.classList.remove(i)}),0)));else if("destroy"===t)b.set(e,i);else if("function"==typeof i)e.addEventListener(t,i),clean(()=>e.removeEventListener(t,i));else if("value"===t||"className"===t||"selectedIndex"===t||!0===i||!1===i)e[t]=i;else if("text"===t)e.textContent=i;else if("class"!==t&&"className"!==t||"object"!=typeof i)"style"===t&&"object"==typeof i?Object.assign(e.style,i):e.setAttribute(t,i);else for(let t in i)i[t]?e.classList.add(t):e.classList.remove(t)}function C(e){if("object"==typeof e&&e){if(e instanceof Store)return e._observe();if(e instanceof Map){let t=new m;return e.forEach((e,i)=>{let n=C(e);void 0!==n&&t.rawSet(i,n)}),t}if(e instanceof Array){let t=new g;for(let i=0;i<e.length;i++){let n=C(e[i]);void 0!==n&&t.rawSet(i,n)}return t}if(e.constructor===Object){let t=new v;for(let i in e){let n=C(e[i]);void 0!==n&&t.rawSet(i,n)}return t}return e}return e}function O(e){return e.index()}function I(e){let t=new Error("Aberdeen internal error "+e);setTimeout(()=>{throw t},0)}function N(e){setTimeout(()=>{throw e},0)}class k extends Error{constructor(e){super(`Operation not permitted outside of ${e?"a mount":"an observe"}() scope`)}}function q(e){const t=e.parentElement?getComputedStyle(e.parentElement):{};return"flex"===t.display&&(t.flexDirection||"").startsWith("row")?{marginLeft:`-${e.offsetWidth/2}px`,marginRight:`-${e.offsetWidth/2}px`,transform:"scaleX(0)"}:{marginBottom:`-${e.offsetHeight/2}px`,marginTop:`-${e.offsetHeight/2}px`,transform:"scaleY(0)"}}export function grow(e){scheduleTask(()=>{let t=q(e);scheduleTask(()=>{Object.assign(e.style,t),scheduleTask(()=>{e.offsetHeight,e.style.transition="margin 400ms ease-out, transform 400ms ease-out";for(let i in t)e.style[i]="";setTimeout(()=>{e.style.transition=""},400)},2)},1)})}export function shrink(e){const t=q(e);scheduleTask(()=>{e.style.transition="margin 400ms ease-out, transform 400ms ease-out",Object.assign(e.style,t),setTimeout(()=>e.remove(),400)},1)}String.prototype.replaceAll||(String.prototype.replaceAll=function(e,t){return this.split(e).join(t)});
1
+ let e,t,n=[],i=new Set,r=!0,s=0;function o(e){if(!i.has(e)){if(s>42)throw new Error("Too many recursive updates from observes");n.length?e.queueOrder<n[n.length-1].queueOrder&&(r=!1):setTimeout(l,0),n.push(e),i.add(e)}}function l(){for(b=!0,e=0;e<n.length;){r||(n.splice(0,e),e=0,n.sort((e,t)=>e.queueOrder-t.queueOrder),r=!0);let t=n.length;for(;e<t&&r;e++){let t=n[e];i.delete(t),t.queueRun()}s++}n.length=0,e=void 0,s=0,b=!1}let a;export function scheduleDomReader(t){o({queueOrder:null!=e&&e<n.length&&n[e].queueOrder>=1e3?n[e].queueOrder+1&-2:1e3,queueRun:t})}export function scheduleDomWriter(t){o({queueOrder:null!=e&&e<n.length&&n[e].queueOrder>=1e3?1|n[e].queueOrder:1001,queueRun:t})}function h(e){if("string"==typeof e)return e+"";{let t=function(e,t){let n="";for(;e>0;)n+=String.fromCharCode(t?65535-e%65533:2+e%65533),e=Math.floor(e/65533);return n}(Math.abs(Math.round(e)),e<0);return String.fromCharCode(128+(e>0?t.length:-t.length))+t}}class d{constructor(e,t,n){this.cleaners=[],this.isDead=!1,this.parentElement=e,this.precedingSibling=t,this.queueOrder=n}findPrecedingNode(e){let t,n=this;for(;(t=n.precedingSibling)&&t!==e;){if(t instanceof Node)return t;let e=t.findLastNode();if(e)return e;n=t}}findLastNode(){if(this.lastChild)return this.lastChild instanceof Node?this.lastChild:this.lastChild.findLastNode()||this.lastChild.findPrecedingNode(this.precedingSibling)}addNode(e){if(!this.parentElement)throw new k(!0);let t=this.findLastNode()||this.findPrecedingNode();this.parentElement.insertBefore(e,t?t.nextSibling:this.parentElement.firstChild),this.lastChild=e}remove(){if(this.parentElement){let e=this.findLastNode();if(e){let t=this.findPrecedingNode();for(t=t?t.nextSibling:this.parentElement.firstChild,this.lastChild=void 0;;){if(!t)return N(1);const n=t;t=n.nextSibling||void 0;let i=S.get(n);if(i&&n instanceof Element?!0!==i&&("function"==typeof i?i(n):E(n,i),S.set(n,!0)):this.parentElement.removeChild(n),n===e)break}}}this._clean()}_clean(){this.isDead=!0;for(let e of this.cleaners)e._clean(this);this.cleaners.length=0}onChange(e,t,n){o(this)}}class u extends d{constructor(e,t,n,i){super(e,t,n),this.renderer=i}queueRun(){a&&N(2),this.isDead||(this.remove(),this.isDead=!1,this.update())}update(){let e=a;a=this;try{this.renderer()}catch(e){P(e)}a=e}}class c{constructor(e,t,n){this.scope=e,this.collection=t,this.triggerCount=n,this.count=t.getCount(),t.addObserver(g,this),e.cleaners.push(this)}onChange(e,t,n){void 0===t?!this.triggerCount&&--this.count||o(this.scope):void 0===n&&(!this.triggerCount&&this.count++||o(this.scope))}_clean(){this.collection.removeObserver(g,this)}}class f extends d{constructor(e,t,n,i,r,s){super(e,t,n),this.byPosition=[],this.byIndex=new Map,this.newIndexes=new Set,this.removedIndexes=new Set,this.collection=i,this.renderer=r,this.makeSortKey=s}onChange(e,t,n){void 0===n?this.removedIndexes.has(e)?this.removedIndexes.delete(e):(this.newIndexes.add(e),o(this)):void 0===t&&(this.newIndexes.has(e)?this.newIndexes.delete(e):(this.removedIndexes.add(e),o(this)))}queueRun(){if(this.isDead)return;let e=this.removedIndexes;this.removedIndexes=new Set,e.forEach(e=>{this.removeChild(e)}),e=this.newIndexes,this.newIndexes=new Set,e.forEach(e=>{this.addChild(e)})}_clean(){super._clean(),this.collection.observers.delete(this);for(const[e,t]of this.byIndex)t._clean();this.byPosition.length=0,this.byIndex.clear()}renderInitial(){if(!a)return N(3);let e=a;this.collection.iterateIndexes(this),a=e}addChild(e){let t=new p(this.parentElement,void 0,this.queueOrder+1,this,e);this.byIndex.set(e,t),t.update()}removeChild(e){let t=this.byIndex.get(e);if(!t)return N(6);t.remove(),this.byIndex.delete(e),this.removeFromPosition(t)}findPosition(e){let t=this.byPosition,n=0,i=t.length;if(!i||e>t[i-1].sortStr)return i;for(;n<i;){let r=n+i>>1;t[r].sortStr<e?n=r+1:i=r}return n}insertAtPosition(e){let t=this.findPosition(e.sortStr);this.byPosition.splice(t,0,e);let n=this.byPosition[t+1];n?(e.precedingSibling=n.precedingSibling,n.precedingSibling=e):(e.precedingSibling=this.lastChild||this.precedingSibling,this.lastChild=e)}removeFromPosition(e){if(""===e.sortStr)return;let t=this.findPosition(e.sortStr);for(;;){if(this.byPosition[t]===e){if(this.byPosition.splice(t,1),t<this.byPosition.length){let n=this.byPosition[t];if(!n)return N(8);if(n.precedingSibling!==e)return N(13);n.precedingSibling=e.precedingSibling}else{if(e!==this.lastChild)return N(12);this.lastChild=e.precedingSibling===this.precedingSibling?void 0:e.precedingSibling}return}if(++t>=this.byPosition.length||this.byPosition[t].sortStr!==e.sortStr)return N(5)}}}class p extends d{constructor(e,t,n,i,r){super(e,t,n),this.sortStr="",this.parent=i,this.itemIndex=r}queueRun(){a&&N(4),this.isDead||(this.remove(),this.isDead=!1,this.update())}update(){let e=a;a=this;let t,n=new Store(this.parent.collection,this.itemIndex);try{t=this.parent.makeSortKey(n)}catch(e){P(e)}let i=this.sortStr,r=null==t?"":(s=t)instanceof Array?s.map(h).join(""):h(s);var s;if(""!==i&&i!==r&&this.parent.removeFromPosition(this),this.sortStr=r,""!==r){r!==i&&this.parent.insertAtPosition(this);try{this.parent.renderer(n)}catch(e){P(e)}}a=e}}const g={};class m{constructor(){this.observers=new Map}addObserver(e,t){t=t;let n=this.observers.get(e);if(n){if(n.has(t))return!1;n.add(t)}else this.observers.set(e,new Set([t]));return!0}removeObserver(e,t){this.observers.get(e).delete(t)}emitChange(e,n,i){if(t)M(t,this,e,n,i);else{let t=this.observers.get(e);t&&t.forEach(t=>t.onChange(e,n,i)),t=this.observers.get(g),t&&t.forEach(t=>t.onChange(e,n,i))}}_clean(e){this.removeObserver(g,e)}setIndex(e,t,n){const i=this.rawGet(e);if(!(i instanceof m)||t instanceof Store||!i.merge(t,n)){let n=I(t);n!==i&&(this.rawSet(e,n),this.emitChange(e,n,i))}}}class v extends m{constructor(){super(...arguments),this.data=[]}getType(){return"array"}getRecursive(e){a&&this.addObserver(g,a)&&a.cleaners.push(this);let t=[];for(let n=0;n<this.data.length;n++){let i=this.data[n];t.push(i instanceof m?e?i.getRecursive(e-1):new Store(this,n):i)}return t}rawGet(e){return this.data[e]}rawSet(e,t){if(e!==(0|e)||e<0||e>999999)throw new Error("Invalid array index "+JSON.stringify(e));for(this.data[e]=t;this.data.length>0&&void 0===this.data[this.data.length-1];)this.data.pop()}merge(e,t){if(!(e instanceof Array))return!1;for(let n=0;n<e.length;n++)this.setIndex(n,e[n],t);if(t&&this.data.length>e.length){for(let t=e.length;t<this.data.length;t++){let e=this.data[t];void 0!==e&&this.emitChange(t,void 0,e)}this.data.length=e.length}return!0}iterateIndexes(e){for(let t=0;t<this.data.length;t++)void 0!==this.data[t]&&e.addChild(t)}normalizeIndex(e){if("number"==typeof e)return e;if("string"==typeof e){let t=0|e;if(e.length&&t==e)return e}throw new Error("Invalid array index "+JSON.stringify(e))}getCount(){return this.data.length}}class y extends m{constructor(){super(...arguments),this.data=new Map}getType(){return"map"}getRecursive(e){a&&this.addObserver(g,a)&&a.cleaners.push(this);let t=new Map;return this.data.forEach((n,i)=>{t.set(i,n instanceof m?e?n.getRecursive(e-1):new Store(this,i):n)}),t}rawGet(e){return this.data.get(e)}rawSet(e,t){void 0===t?this.data.delete(e):this.data.set(e,t)}merge(e,t){return e instanceof Map&&(e.forEach((e,n)=>{this.setIndex(n,e,t)}),t&&this.data.forEach((t,n)=>{e.has(n)||this.setIndex(n,void 0,!1)}),!0)}iterateIndexes(e){this.data.forEach((t,n)=>{e.addChild(n)})}normalizeIndex(e){return e}getCount(){return this.data.size}}class w extends y{getType(){return"object"}getRecursive(e){a&&this.addObserver(g,a)&&a.cleaners.push(this);let t={};return this.data.forEach((n,i)=>{t[i]=n instanceof m?e?n.getRecursive(e-1):new Store(this,i):n}),t}merge(e,t){if(!e||e.constructor!==Object)return!1;for(let n in e)this.setIndex(n,e[n],t);return t&&this.data.forEach((t,n)=>{e.hasOwnProperty(n)||this.setIndex(n,void 0,!1)}),!0}normalizeIndex(e){let t=typeof e;if("string"===t)return e;if("number"===t)return""+e;throw new Error("Invalid object index "+JSON.stringify(e))}getCount(){let e=0;for(let t of this.data)e++;return e}}export class Store{constructor(e,t){if(void 0===t)this.collection=new v,this.idx=0,void 0!==e&&this.collection.rawSet(0,I(e));else{if(!(e instanceof m))throw new Error("1st parameter should be an ObsCollection if the 2nd is also given");this.collection=e,this.idx=t}}index(){return this.idx}_clean(e){this.collection.removeObserver(this.idx,e)}get(...e){return this.query({path:e})}peek(...e){return this.query({path:e,peek:!0})}getNumber(...e){return this.query({path:e,type:"number"})}getString(...e){return this.query({path:e,type:"string"})}getBoolean(...e){return this.query({path:e,type:"boolean"})}getFunction(...e){return this.query({path:e,type:"function"})}getArray(...e){return this.query({path:e,type:"array"})}getObject(...e){return this.query({path:e,type:"object"})}getMap(...e){return this.query({path:e,type:"map"})}getOr(e,...t){let n=typeof e;return"object"===n&&(e instanceof Map?n="map":e instanceof Array&&(n="array")),this.query({type:n,defaultValue:e,path:t})}query(e){if(e.peek&&a){let t=a;a=void 0;let n=this.query(e);return a=t,n}let t=(e.path&&e.path.length?this.ref(...e.path):this)._observe();if(e.type&&(void 0!==t||void 0===e.defaultValue)){let n=t instanceof m?t.getType():null===t?"null":typeof t;if(n!==e.type)throw new TypeError(`Expecting ${e.type} but got ${n}`)}return t instanceof m?t.getRecursive(null==e.depth?-1:e.depth-1):void 0===t?e.defaultValue:t}isEmpty(...e){let t=this.ref(...e)._observe();if(t instanceof m){if(a){return!new c(a,t,!1).count}return!t.getCount()}if(void 0===t)return!0;throw new Error("isEmpty() expects a collection or undefined, but got "+JSON.stringify(t))}count(...e){let t=this.ref(...e)._observe();if(t instanceof m){if(a){return new c(a,t,!0).count}return t.getCount()}if(void 0===t)return 0;throw new Error("count() expects a collection or undefined, but got "+JSON.stringify(t))}getType(...e){let t=this.ref(...e)._observe();return t instanceof m?t.getType():null===t?"null":typeof t}set(...e){let t=e.pop(),n=this.makeRef(...e);n.collection.setIndex(n.idx,t,!0)}merge(...e){let t=e.pop(),n=this.makeRef(...e);n.collection.setIndex(n.idx,t,!1)}delete(...e){let t=this.makeRef(...e);t.collection.setIndex(t.idx,void 0,!0)}push(...e){let t=e.pop(),n=this.makeRef(...e),i=n.collection.rawGet(n.idx);if(void 0===i)i=new v,n.collection.setIndex(n.idx,i,!0);else if(!(i instanceof v))throw new Error("push() is only allowed for an array or undefined (which would become an array)");let r=I(t),s=i.data.length;return i.data.push(r),i.emitChange(s,r,void 0),s}modify(e){this.set(e(this.query({peek:!0})))}ref(...e){let t=this;for(let n=0;n<e.length;n++){let i=t._observe();if(!(i instanceof m)){if(void 0!==i)throw new Error(`Value ${JSON.stringify(i)} is not a collection (nor undefined) in step ${n} of $(${JSON.stringify(e)})`);return new x}t=new Store(i,i.normalizeIndex(e[n]))}return t}makeRef(...e){let t=this;for(let n=0;n<e.length;n++){let i=t.collection.rawGet(t.idx);if(!(i instanceof m)){if(void 0!==i)throw new Error(`Value ${JSON.stringify(i)} is not a collection (nor undefined) in step ${n} of $(${JSON.stringify(e)})`);i=new w,t.collection.rawSet(t.idx,i),t.collection.emitChange(t.idx,i,void 0)}t=new Store(i,i.normalizeIndex(e[n]))}return t}_observe(){return a&&this.collection.addObserver(this.idx,a)&&a.cleaners.push(this),this.collection.rawGet(this.idx)}onEach(...e){let t=q,n=e.pop();if("function"!=typeof e[e.length-1]||"function"!=typeof n&&null!=n||(null!=n&&(t=n),n=e.pop()),"function"!=typeof n)throw new Error("onEach() expects a render function as its last argument but got "+JSON.stringify(n));if(!a)throw new k(!1);let i=this.ref(...e)._observe();if(i instanceof m){let e=new f(a.parentElement,a.lastChild||a.precedingSibling,a.queueOrder+1,i,n,t);i.addObserver(g,e),a.cleaners.push(e),a.lastChild=e,e.renderInitial()}else if(void 0!==i)throw new Error("onEach() attempted on a value that is neither a collection nor undefined")}map(e){let t=new Store(new Map);return this.onEach(n=>{let i=e(n);if(void 0!==i){let e=n.index();t.set(e,i),clean(()=>{t.delete(e)})}}),t}multiMap(e){let t=new Store(new Map);return this.onEach(n=>{let i,r=e(n);if(r.constructor===Object){for(let e in r)t.set(e,r[e]);i=Object.keys(r)}else{if(!(r instanceof Map))return;r.forEach((e,n)=>{t.set(n,e)}),i=[...r.keys()]}i.length&&clean(()=>{for(let e of i)t.delete(e)})}),t}isDetached(){return!1}dump(){let e=this.getType();"array"===e||"object"===e||"map"===e?(text("<"+e+">"),node("ul",()=>{this.onEach(e=>{node("li",()=>{text(JSON.stringify(e.index())+": "),e.dump()})})})):text(JSON.stringify(this.get()))}}class x extends Store{isDetached(){return!0}}let b=!1,S=new WeakMap;function E(e,t){e.classList.add(t),setTimeout(()=>e.remove(),2e3)}export function node(e="",...t){if(!a)throw new k(!0);let n;if(e instanceof Element)n=e;else{let t,i=e.indexOf(".");i>=0&&(t=e.substr(i+1),e=e.substr(0,i)),n=document.createElement(e||"div"),t&&(n.className=t.replaceAll("."," "))}a.addNode(n);for(let e of t){let t=typeof e;if("function"===t){let t=new u(n,void 0,a.queueOrder+1,e);b?(b=!1,t.update(),b=!0):t.update(),a.cleaners.push(t)}else if("string"===t||"number"===t)n.textContent=e;else if("object"===t&&e&&e.constructor===Object)for(let t in e)O(n,t,e[t]);else if(e instanceof Store)C(n,e);else if(null!=e)throw new Error("Unexpected argument "+JSON.stringify(e))}}export function html(e){if(!a||!a.parentElement)throw new k(!0);let t=document.createElement(a.parentElement.tagName);for(t.innerHTML=""+e;t.firstChild;)a.addNode(t.firstChild)}function C(e,t){let n,i,r=e.getAttribute("type"),s=t.query({peek:!0});"checkbox"===r?(void 0===s&&t.set(e.checked),n=t=>e.checked=t,i=()=>t.set(e.checked)):"radio"===r?(void 0===s&&e.checked&&t.set(e.value),n=t=>e.checked=t===e.value,i=()=>{e.checked&&t.set(e.value)}):(i=()=>t.set("number"===r||"range"===r?""===e.value?null:+e.value:e.value),void 0===s&&i(),n=t=>{e.value!==t&&(e.value=t)}),observe(()=>{n(t.get())}),e.addEventListener("input",i),clean(()=>{e.removeEventListener("input",i)})}export function text(e){if(!a)throw new k(!0);null!=e&&a.addNode(document.createTextNode(e))}export function prop(e,t){if(!a||!a.parentElement)throw new k(!0);if("object"==typeof e)for(let t in e)O(a.parentElement,t,e[t]);else O(a.parentElement,e,t)}export function getParentElement(){if(!a||!a.parentElement)throw new k(!0);return a.parentElement}export function clean(e){if(!a)throw new k(!1);a.cleaners.push({_clean:e})}export function observe(e){mount(void 0,e)}export function mount(e,t){let n;e||!a?n=new u(e,void 0,0,t):(n=new u(a.parentElement,a.lastChild||a.precedingSibling,a.queueOrder+1,t),a.lastChild=n),n.update(),a&&a.cleaners.push(n)}export function peek(e){let t=a;a=void 0;try{return e()}finally{a=t}}function O(e,t,n){if("create"===t)b&&("function"==typeof n?n(e):(e.classList.add(n),setTimeout((function(){e.classList.remove(n)}),0)));else if("destroy"===t)S.set(e,n);else if("function"==typeof n)e.addEventListener(t,n),clean(()=>e.removeEventListener(t,n));else if("value"===t||"className"===t||"selectedIndex"===t||!0===n||!1===n)e[t]=n;else if("text"===t)e.textContent=n;else if("class"!==t&&"className"!==t||"object"!=typeof n)"style"===t&&"object"==typeof n?Object.assign(e.style,n):e.setAttribute(t,n);else for(let t in n)n[t]?e.classList.add(t):e.classList.remove(t)}function I(e){if("object"==typeof e&&e){if(e instanceof Store)return e._observe();if(e instanceof Map){let t=new y;return e.forEach((e,n)=>{let i=I(e);void 0!==i&&t.rawSet(n,i)}),t}if(e instanceof Array){let t=new v;for(let n=0;n<e.length;n++){let i=I(e[n]);void 0!==i&&t.rawSet(n,i)}return t}if(e.constructor===Object){let t=new w;for(let n in e){let i=I(e[n]);void 0!==i&&t.rawSet(n,i)}return t}return e}return e}function q(e){return e.index()}function N(e){let t=new Error("Aberdeen internal error "+e);setTimeout(()=>{throw t},0)}function P(e){setTimeout(()=>{throw e},0)}class k extends Error{constructor(e){super(`Operation not permitted outside of ${e?"a mount":"an observe"}() scope`)}}function R(e){const t=e.parentElement?getComputedStyle(e.parentElement):{};return"flex"===t.display&&(t.flexDirection||"").startsWith("row")?{marginLeft:`-${e.offsetWidth/2}px`,marginRight:`-${e.offsetWidth/2}px`,transform:"scaleX(0)"}:{marginBottom:`-${e.offsetHeight/2}px`,marginTop:`-${e.offsetHeight/2}px`,transform:"scaleY(0)"}}export function grow(e){scheduleDomReader(()=>{let t=R(e);scheduleDomWriter(()=>{Object.assign(e.style,t),scheduleDomReader(()=>{e.offsetHeight,scheduleDomWriter(()=>{e.style.transition="margin 400ms ease-out, transform 400ms ease-out";for(let n in t)e.style[n]="";setTimeout(()=>{e.style.transition=""},400)})})})})}export function shrink(e){scheduleDomReader(()=>{const t=R(e);scheduleDomWriter(()=>{e.style.transition="margin 400ms ease-out, transform 400ms ease-out",Object.assign(e.style,t),setTimeout(()=>e.remove(),400)})})}function j(e){if(t)throw new Error("already recording a patch");t=new Map;try{e()}catch(e){throw t=void 0,e}const n=t;return t=void 0,n}function M(e,t,n,i,r){let s=e.get(t);null==s&&(s=new Map,e.set(t,s));let o=s.get(n);s.set(n,[i,null==o?r:o[1]])}function T(e){for(let[t,n]of e)for(let[e,[i,r]]of n)t.emitChange(e,i,r)}function D(e,t,n=!1){for(let[i,r]of t)for(let[t,[s,o]]of r)M(e,i,t,n?o:s,n?s:o)}function $(e,t=!1){for(let[n,i]of e)for(let[e,[r,s]]of i){let i=n.rawGet(e);if(i!==s){if(!t)return!1;P(new Error(`Applying invalid patch: data ${i} is unequal to expected old data ${s} for index ${e}`))}}for(let[t,n]of e)for(let[e,[i,r]]of n)t.rawSet(e,i);return!0}const _=[];export function applyPrediction(e){let t=j(e);return _.push(t),T(t),t}export function applyCanon(e,t=[]){let n=new Map;for(let e of _)D(n,e,!0);$(n,!0);for(let e of t){let t=_.indexOf(e);t>=0&&_.splice(t,1)}e&&D(n,j(e));for(let e=0;e<_.length;e++)$(_[e])?D(n,_[e]):(_.splice(e,1),e--);T(n)}String.prototype.replaceAll||(String.prototype.replaceAll=function(e,t){return this.split(e).join(t)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aberdeen",
3
- "version": "0.0.17",
3
+ "version": "0.1.0",
4
4
  "description": "A TypeScript/JavaScript library for quickly building performant declarative user interfaces without the use of a virtual DOM.",
5
5
  "main": "dist/aberdeen.js",
6
6
  "types": "dist/aberdeen.d.js",