rasti 4.0.0-alpha.5 → 4.0.0-alpha.7

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
@@ -1,7 +1,7 @@
1
1
  <p align="center">
2
2
  <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.0-alpha.5/docs/logo-dark.svg">
4
- <img alt="Rasti.js" src="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.0-alpha.5/docs/logo.svg" height="120">
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.0-alpha.7/docs/logo-dark.svg">
4
+ <img alt="Rasti.js" src="https://cdn.jsdelivr.net/gh/8tentaculos/rasti@v4.0.0-alpha.7/docs/logo.svg" height="120">
5
5
  </picture>
6
6
  </p>
7
7
 
@@ -182,10 +182,12 @@ For detailed information on how to use **Rasti**, refer to the [API documentatio
182
182
 
183
183
  ## Version History
184
184
 
185
- - **[v4.0.0](https://github.com/8tentaculos/rasti/releases/tag/v4.0.0)** - Current version with simpler event handling, smarter component reuse, and granular DOM updates
186
- - **[v3.0.0](https://github.com/8tentaculos/rasti/releases/tag/v3.0.0)** - Introduced **Containers**, dynamic element tags, **Component** tags, `partial` method, and dropped ES5 support
187
- - **[v2.0.0](https://github.com/8tentaculos/rasti/releases/tag/v2.0.0)** - Introduced **Components**
188
- - **[v1.0.0](https://github.com/8tentaculos/rasti/releases/tag/v1.0.0)** - Initial release
185
+ We strive to minimize breaking changes between major versions. However, if you're migrating between major versions, please refer to the release notes below for details on any breaking changes and migration tips.
186
+
187
+ - **[v4.0.0](https://github.com/8tentaculos/rasti/releases/tag/v4.0.0)**
188
+ - **[v3.0.0](https://github.com/8tentaculos/rasti/releases/tag/v3.0.0)**
189
+ - **[v2.0.0](https://github.com/8tentaculos/rasti/releases/tag/v2.0.0)**
190
+ - **[v1.0.0](https://github.com/8tentaculos/rasti/releases/tag/v1.0.0)**
189
191
 
190
192
  ## License
191
193
 
package/dist/rasti.js CHANGED
@@ -830,7 +830,7 @@
830
830
  * Ensure that the view has a unique id at `this.uid`.
831
831
  */
832
832
  ensureUid() {
833
- if (!this.uid) this.uid = `r-${++View.uid}`;
833
+ if (!this.uid) this.uid = `r${++View.uid}`;
834
834
  }
835
835
 
836
836
  /**
@@ -879,7 +879,7 @@
879
879
  * @return {Rasti.View} Return `this` for chaining.
880
880
  */
881
881
  removeElement() {
882
- this.el.parentNode.removeChild(this.el);
882
+ this.el.remove();
883
883
  // Return `this` for chaining.
884
884
  return this;
885
885
  }
@@ -1258,7 +1258,7 @@
1258
1258
  * @param {Function} options.getAttributes Function that returns the attributes object for the element.
1259
1259
  * @private
1260
1260
  */
1261
- class Element {
1261
+ let Element$1 = class Element {
1262
1262
  constructor(options) {
1263
1263
  this.getSelector = options.getSelector;
1264
1264
  this.getAttributes = options.getAttributes;
@@ -1299,7 +1299,7 @@
1299
1299
  }
1300
1300
  });
1301
1301
  }
1302
- }
1302
+ };
1303
1303
 
1304
1304
  /**
1305
1305
  * Properties that should be mirrored from source to target.
@@ -1459,7 +1459,7 @@
1459
1459
  * @param {Function} options.getEnd Function that returns the end comment marker text.
1460
1460
  * @param {any} options.expression The expression to be evaluated for the interpolation.
1461
1461
  * @param {Function} options.isComponent Function that checks if an element is a component root element.
1462
- * @param {Function} options.isElement Function that checks if an element has the Rasti data attribute.
1462
+ * @param {Function} options.containsComponent Function that checks if an element contains a component.
1463
1463
  * @private
1464
1464
  */
1465
1465
  class Interpolation {
@@ -1467,8 +1467,8 @@
1467
1467
  this.getStart = options.getStart;
1468
1468
  this.getEnd = options.getEnd;
1469
1469
  this.expression = options.expression;
1470
- this.isComponent = options.isComponent;
1471
- this.isElement = options.isElement;
1470
+ this.shouldSkipFind = options.shouldSkipFind;
1471
+ this.shouldSkipSync = options.shouldSkipSync;
1472
1472
  this.previousItems = [];
1473
1473
  }
1474
1474
 
@@ -1478,12 +1478,12 @@
1478
1478
  * @param {Node} parent The parent node to search in.
1479
1479
  */
1480
1480
  hydrate(parent) {
1481
- const startComment = findComment(parent, this.getStart(), this.isComponent);
1482
- const endComment = findComment(parent, this.getEnd(), this.isComponent, startComment);
1481
+ const startMark = findComment(parent, this.getStart(), this.shouldSkipFind);
1482
+ const endMark = findComment(parent, this.getEnd(), this.shouldSkipFind, startMark);
1483
1483
 
1484
1484
  this.ref = [
1485
- startComment,
1486
- endComment
1485
+ startMark,
1486
+ endMark
1487
1487
  ];
1488
1488
  }
1489
1489
 
@@ -1493,39 +1493,67 @@
1493
1493
  * @param {DocumentFragment} fragment The new content fragment to insert.
1494
1494
  * @param {Element} targetElement The target element to replace with the new fragment.
1495
1495
  */
1496
- update(fragment, targetElement) {
1497
- if (targetElement) {
1498
- targetElement.replaceWith(fragment.firstChild);
1499
- return;
1500
- }
1496
+ update(fragment, handleComponents) {
1497
+ let divider;
1498
+ // Reference to marker elements.
1499
+ const [startMark, endMark] = this.ref;
1501
1500
 
1502
- const [startComment, endComment] = this.ref;
1503
-
1504
- const currentFirstElement = startComment.nextSibling;
1505
- const currentEmpty = currentFirstElement === endComment;
1506
- const currentSingleChildElement = !currentEmpty && currentFirstElement.nextSibling === endComment;
1507
- const fragmentChildren = fragment.children;
1501
+ const currentFirstElement = startMark.nextSibling;
1502
+ // Check if the interpolation is empty.
1503
+ const currentEmpty = currentFirstElement === endMark;
1504
+ // Check if the interpolation has a single child element.
1505
+ const currentSingleChildElement = !currentEmpty && currentFirstElement.nextSibling === endMark;
1508
1506
 
1509
1507
  if (currentEmpty) {
1510
- // Interpolation is empty. Insert the fragment.
1511
- endComment.parentNode.insertBefore(fragment, endComment);
1512
- } else if (currentSingleChildElement && fragmentChildren.length === 1) {
1513
- if (!this.isElement(currentFirstElement)) {
1514
- // There is a single child element that is not a component's root element. Sync node attributes and content.
1515
- syncNode(currentFirstElement, fragmentChildren[0]);
1508
+ // The interpolation is empty. Insert the fragment.
1509
+ endMark.before(fragment);
1510
+ } else if (
1511
+ currentSingleChildElement &&
1512
+ fragment.children.length === 1 &&
1513
+ !this.shouldSkipSync(currentFirstElement) &&
1514
+ !this.shouldSkipSync(fragment.firstChild)
1515
+ ) {
1516
+ // The interpolation has a single child element and the new fragment has a single child element.
1517
+ // The elements doesn't contain a component. Sync node attributes and content.
1518
+ syncNode(currentFirstElement, fragment.firstChild);
1519
+ } else {
1520
+ // The interpolation has multiple child elements or the new fragment has multiple child elements.
1521
+ // Insert a divider comment before the end marker and insert the new fragment after the divider.
1522
+ divider = document.createComment('');
1523
+ endMark.before(divider);
1524
+ endMark.before(fragment);
1525
+ }
1526
+ // Handle the components in the fragment. Execute the handler function.
1527
+ handleComponents();
1528
+
1529
+ if (divider) {
1530
+ // Remove old content and divider.
1531
+ const startMark = this.ref[0];
1532
+ if (startMark.nextSibling === divider) {
1533
+ divider.remove();
1516
1534
  } else {
1517
- // There is a single child element that is a component's root element. Replace it with the new child element.
1518
- currentFirstElement.replaceWith(fragmentChildren[0]);
1535
+ const range = document.createRange();
1536
+ range.setStartAfter(this.ref[0]);
1537
+ range.setEndAfter(divider);
1538
+ range.deleteContents();
1519
1539
  }
1520
- } else {
1521
- // Interpolation is not empty. Replace the interpolation content.
1522
- const range = document.createRange();
1523
- range.setStartAfter(startComment);
1524
- range.setEndBefore(endComment);
1525
- range.deleteContents();
1526
- range.insertNode(fragment);
1527
1540
  }
1528
1541
  }
1542
+
1543
+ /**
1544
+ * Update the interpolation content with a new fragment. This is used for container components.
1545
+ * @param {Element} element The element to replace with the new fragment.
1546
+ * @param {DocumentFragment} fragment The new content fragment to insert.
1547
+ * @param {Function} handleComponents Function to handle the components in the fragment before removing the old content.
1548
+ */
1549
+ updateElement(element, fragment, handleComponents) {
1550
+ const divider = document.createComment('');
1551
+ element.after(divider);
1552
+ divider.after(fragment.firstChild);
1553
+ handleComponents();
1554
+ if (element.nextSibling === divider) element.remove();
1555
+ divider.remove();
1556
+ }
1529
1557
  }
1530
1558
 
1531
1559
  /**
@@ -1578,6 +1606,29 @@
1578
1606
  return html.join(' ');
1579
1607
  }
1580
1608
 
1609
+ /**
1610
+ * Replaces an existing DOM node with a new node, preserving internal DOM state.
1611
+ * Uses moveBefore if available, otherwise falls back to before.
1612
+ *
1613
+ * @param {Node} oldNode The existing DOM node to replace.
1614
+ * @param {Node} newNode The new DOM node to replace the old node with.
1615
+ * @module
1616
+ * @private
1617
+ */
1618
+ function replaceNode(oldNode, newNode) {
1619
+ if (Element.prototype.moveBefore) {
1620
+ oldNode.parentNode.moveBefore(newNode, oldNode);
1621
+ oldNode.remove();
1622
+ } else {
1623
+ const activeElement = document.activeElement;
1624
+ oldNode.before(newNode);
1625
+ oldNode.remove();
1626
+ if (activeElement && activeElement !== document.activeElement && newNode.contains(activeElement)) {
1627
+ activeElement.focus();
1628
+ }
1629
+ }
1630
+ }
1631
+
1581
1632
  /**
1582
1633
  * Same as getResult, but pass context as argument to the expression.
1583
1634
  * Used to evaluate expressions in the context of a component.
@@ -1598,13 +1649,13 @@
1598
1649
  const isComponent = (el) => !!(el && el.dataset && el.dataset[Component.DATASET_ELEMENT] && el.dataset[Component.DATASET_ELEMENT].endsWith('-1'));
1599
1650
 
1600
1651
  /**
1601
- * Check if an element has the Rasti data attribute.
1602
- * This includes both component root elements and regular tracked elements.
1652
+ * Check if an element contains a component.
1653
+ * It checks if the element is a component root element or if it contains a component.
1603
1654
  * @param {Element} el The element to check.
1604
- * @return {boolean} True if the element has the data attribute.
1655
+ * @return {boolean} True if the element contains a component.
1605
1656
  * @private
1606
1657
  */
1607
- const isElement = (el) => !!(el && el.dataset && el.dataset[Component.DATASET_ELEMENT]);
1658
+ const containsElement = (el) => !!(el && el.dataset && el.dataset[Component.DATASET_ELEMENT]) || !!el.querySelector(`[${Component.ATTRIBUTE_ELEMENT}]`);
1608
1659
 
1609
1660
  /**
1610
1661
  * Generate string with placeholders for interpolated expressions.
@@ -1979,17 +2030,23 @@
1979
2030
  const PH = Component.PLACEHOLDER('(\\d+)');
1980
2031
  const attributes = [];
1981
2032
  // Parse attributes string with support for placeholders in both names and values.
1982
- const regExp = new RegExp(`(${PH}|[\\w-]+)(?:=(["']?)(?:${PH}|((?:.?(?!["']?\\s+(?:\\S+)=|\\s*/?[>"']))+.))\\3)?`, 'g');
2033
+ const regExp = new RegExp(`(?:${PH}|([\\w-]+))(?:=(["']?)(?:${PH}|((?:.?(?!["']?\\s+(?:\\S+)=|\\s*/?[>"']))+.))?\\3)?`, 'g');
1983
2034
 
1984
2035
  let attributeMatch;
1985
2036
  while ((attributeMatch = regExp.exec(attributesStr)) !== null) {
1986
- const [, attribute, attributeIdx, quotes, valueIdx, value] = attributeMatch;
2037
+ const [, attributeIdx, attribute, quotes, valueIdx, value] = attributeMatch;
2038
+
2039
+ const hasQuotes = !!quotes;
1987
2040
 
1988
- const attr = typeof attributeIdx !== 'undefined' ? expressions[parseInt(attributeIdx, 10)] : attribute;
1989
- const val = typeof valueIdx !== 'undefined' ? expressions[parseInt(valueIdx, 10)] : value;
2041
+ let attr = typeof attributeIdx !== 'undefined' ? expressions[parseInt(attributeIdx, 10)] : attribute;
2042
+ let val = typeof valueIdx !== 'undefined' ? expressions[parseInt(valueIdx, 10)] : value;
2043
+
2044
+ if (hasQuotes && typeof val === 'undefined') {
2045
+ val = '';
2046
+ }
1990
2047
 
1991
2048
  if (typeof val !== 'undefined') {
1992
- attributes.push([attr, val, !!quotes]);
2049
+ attributes.push([attr, val, hasQuotes]);
1993
2050
  } else {
1994
2051
  attributes.push([attr]);
1995
2052
  }
@@ -2175,9 +2232,8 @@
2175
2232
  recycle(parent, props) {
2176
2233
  if (parent) {
2177
2234
  // Locate the placeholder comment and replace it with the real nodes
2178
- const toBeReplaced = findComment(parent, Component.MARKER_RECYCLED(this.uid), isComponent);
2179
- // Replace it with this.el.
2180
- toBeReplaced.replaceWith(this.el);
2235
+ const placeholder = findComment(parent, Component.MARKER_RECYCLED(this.uid), isComponent);
2236
+ replaceNode(placeholder, this.el);
2181
2237
  }
2182
2238
  // Update props.
2183
2239
  if (props) {
@@ -2253,8 +2309,8 @@
2253
2309
  /**
2254
2310
  * Render a template part.
2255
2311
  * @param {any} part - The template part.
2256
- * @param {function} addChild - The addChild function.
2257
- * @param {Array} items - The items array.
2312
+ * @param {function} addChild - The addChild function. It should handle children and return a HTML string.
2313
+ * @param {Array} items - The items array. It is used to store the items that are parsed.
2258
2314
  * @return {string} The rendered template part.
2259
2315
  * @private
2260
2316
  */
@@ -2316,6 +2372,24 @@
2316
2372
  * Used internally on the render process.
2317
2373
  * Use it for server-side rendering or static site generation.
2318
2374
  * @return {string} The rendered component.
2375
+ * @example
2376
+ * import { Component } from 'rasti';
2377
+ * const Button = Component.create`
2378
+ * <button class="button">Click me</button>
2379
+ * `;
2380
+ * const App = Component.create`
2381
+ * <div>
2382
+ * <${Button}>Click me</${Button}>
2383
+ * </div>
2384
+ * `;
2385
+ *
2386
+ * const app = new App();
2387
+ *
2388
+ * console.log(app.toString());
2389
+ * // <div data-rasti-el="r1-1"><!--rasti-s-r1-1--><button class="button" data-rasti-el="r2-1">Click me</button><!--rasti-e-r1-1--></div>
2390
+ *
2391
+ * console.log(`${app}`);
2392
+ * // <div data-rasti-el="r1-1"><!--rasti-s-r1-1--><button class="button" data-rasti-el="r2-1">Click me</button><!--rasti-e-r1-1--></div>
2319
2393
  */
2320
2394
  toString() {
2321
2395
  // Normally there won't be any children, but if there are, destroy them.
@@ -2359,8 +2433,6 @@
2359
2433
  this.eventsManager.reset();
2360
2434
  // Reset position tracking.
2361
2435
  this.pathManager.reset();
2362
- // Store active element.
2363
- const activeElement = this.isContainer() ? null : document.activeElement;
2364
2436
  // Update elements.
2365
2437
  this.template.elements.forEach(element => element.update());
2366
2438
  // Store previous children.
@@ -2373,7 +2445,9 @@
2373
2445
  const recycledChildren = [];
2374
2446
 
2375
2447
  this.pathManager.increment();
2376
-
2448
+ // `addChild` handler is called from `renderTemplatePart` for every component.
2449
+ // It should handle children and return a HTML string.
2450
+ // In this case, where the component updates, it handles children recycling.
2377
2451
  const addChild = component => {
2378
2452
  let out = component;
2379
2453
  let found = null;
@@ -2422,13 +2496,19 @@
2422
2496
  }
2423
2497
  // Parse the rendered content and get the fragment.
2424
2498
  const fragment = parseHTML(rendered);
2425
- // Add recycled components to children and move them to the new template in the fragment.
2426
- recycledChildren.forEach(recycled => recycle(recycled, fragment));
2427
- // Add new children. Hydrate them and update the interpolation.
2428
- nextChildren.forEach(child => this.addChild(child).hydrate(fragment));
2429
- // Update the interpolation.
2430
- const targetElement = this.isContainer() ? this.el : null;
2431
- interpolation.update(fragment, targetElement);
2499
+
2500
+ const handleComponents = (parent) => () => {
2501
+ // Add recycled components to children and move them to the new template in the fragment.
2502
+ recycledChildren.forEach(recycled => recycle(recycled, parent));
2503
+ // Add new children. Hydrate them and update the interpolation.
2504
+ nextChildren.forEach(child => this.addChild(child).hydrate(parent));
2505
+ };
2506
+
2507
+ if (this.isContainer()) {
2508
+ interpolation.updateElement(this.el, fragment, handleComponents(this.el.parentNode));
2509
+ } else {
2510
+ interpolation.update(fragment, handleComponents(this.el));
2511
+ }
2432
2512
  });
2433
2513
  // Destroy unused children.
2434
2514
  previousChildren.forEach(prev => {
@@ -2443,10 +2523,6 @@
2443
2523
  this.delegateEvents();
2444
2524
  }
2445
2525
  }
2446
- // Restore focus.
2447
- if (activeElement && this.el.contains(activeElement)) {
2448
- activeElement.focus();
2449
- }
2450
2526
  // Call onUpdate lifecycle method.
2451
2527
  this.onUpdate.call(this);
2452
2528
  // Return this for chaining.
@@ -2542,16 +2618,49 @@
2542
2618
  }
2543
2619
 
2544
2620
  /**
2545
- * Mount the component into the dom.
2546
- * It instantiate the Component view using options,
2547
- * appends its element into the DOM (if `el` is provided).
2548
- * The `onMount` lifecycle method will be called after the component is added to the DOM.
2549
- * And returns the view instance.
2621
+ * Mount the component into the DOM.
2622
+ * Creates a new component instance with the provided options and optionally mounts it into the DOM.
2623
+ *
2624
+ * **Mounting modes:**
2625
+ * - **Normal mount** (default): Renders the component as HTML and appends it to the provided element. Use this for client-side rendering.
2626
+ * - **Hydration mode**: Assumes the DOM already contains the component's HTML (from server-side rendering).
2627
+ *
2628
+ * If `el` is not provided, the component is instantiated but not mounted (the same as using `new Component(options)`). You can mount it later by calling `render()` and appending the element (`this.el`) to the DOM.
2629
+ *
2550
2630
  * @static
2551
- * @param {object} [options] The view options.
2552
- * @param {node} [el] Dom element to append the view element.
2553
- * @param {boolean} [hydrate] If true, the view will hydrate existing DOM.
2631
+ * @param {object} [options={}] The component options. These will be passed to the constructor and can include
2632
+ * `model`, `state`, `props`, lifecycle methods, and any other component-specific options.
2633
+ * @param {node} [el] The DOM element where the component will be mounted. If provided, the component will be
2634
+ * rendered and appended to this element. If not provided, the component is created but not mounted.
2635
+ * @param {boolean} [hydrate=false] If `true`, enables hydration mode for server-side rendering. The component will
2636
+ * assume the DOM already contains its HTML structure and will only hydrate it.
2637
+ * If `false` (default), the component will be rendered from scratch and appended to `el`.
2554
2638
  * @return {Component} The component instance.
2639
+ * @example
2640
+ * import { Component, Model } from 'rasti';
2641
+ *
2642
+ * const Button = Component.create`
2643
+ * <button class="${({ props }) => props.className}">
2644
+ * ${({ props }) => props.label}
2645
+ * </button>
2646
+ * `;
2647
+ *
2648
+ * // Normal mount: render and append to DOM.
2649
+ * const button = Button.mount({
2650
+ * label: 'Click me'
2651
+ * }, document.body);
2652
+ *
2653
+ * // Create without mounting (mount later).
2654
+ * const button2 = Button.mount({ className : 'secondary', label : 'Save' });
2655
+ * // Later, render and append it to the DOM.
2656
+ * document.body.appendChild(button2.render().el);
2657
+ *
2658
+ * // Hydration mode: hydrate existing server-rendered HTML
2659
+ * // Assuming document.body already contains the HTML structure of the button.
2660
+ * const hydratedButton = Button.mount({
2661
+ * className : 'primary',
2662
+ * label : 'Click me'
2663
+ * }, document.body, true);
2555
2664
  */
2556
2665
  static mount(options, el, hydrate) {
2557
2666
  // Instantiate component.
@@ -2559,14 +2668,14 @@
2559
2668
  // If `el` is passed, mount component.
2560
2669
  if (el) {
2561
2670
  if (hydrate) {
2562
- // Generate subcomponents.
2671
+ // Hydrate existing DOM, only generate subcomponents calling `toString`.
2563
2672
  component.toString();
2564
- // Hydrate existing DOM.
2565
- component.hydrate(el);
2566
2673
  } else {
2567
- // Append element to the DOM.
2568
- el.append(component.render().el);
2674
+ // Render the component and append it to the provided element.
2675
+ el.append(parseHTML(component));
2569
2676
  }
2677
+ // Hydrate in both cases.
2678
+ component.hydrate(el);
2570
2679
  }
2571
2680
  // Return component instance.
2572
2681
  return component;
@@ -2723,7 +2832,7 @@
2723
2832
  return this.extend({
2724
2833
  template() {
2725
2834
  return {
2726
- elements : elements.map(element => new Element({
2835
+ elements : elements.map(element => new Element$1({
2727
2836
  getSelector : element.getSelector.bind(this),
2728
2837
  getAttributes : element.getAttributes.bind(this)
2729
2838
  })),
@@ -2731,8 +2840,8 @@
2731
2840
  getStart : interpolation.getStart.bind(this),
2732
2841
  getEnd : interpolation.getEnd.bind(this),
2733
2842
  expression : interpolation.expression,
2734
- isComponent,
2735
- isElement
2843
+ shouldSkipFind : isComponent,
2844
+ shouldSkipSync : containsElement
2736
2845
  })),
2737
2846
  parts,
2738
2847
  };
@@ -2755,14 +2864,14 @@
2755
2864
  /*
2756
2865
  * Placeholders used to temporarily replace expressions in the template.
2757
2866
  */
2758
- Component.PLACEHOLDER = (idx) => `__RASTI-${idx}__`;
2867
+ Component.PLACEHOLDER = (idx) => `__RASTI_PH_${idx}__`;
2759
2868
 
2760
2869
  /*
2761
2870
  * Markers used to identify interpolation and recycled components.
2762
2871
  */
2763
- Component.MARKER_RECYCLED = (uid) => `rasti-recycled-${uid}`;
2764
- Component.MARKER_START = (uid) => `rasti-start-${uid}`;
2765
- Component.MARKER_END = (uid) => `rasti-end-${uid}`;
2872
+ Component.MARKER_RECYCLED = (uid) => `rasti-r-${uid}`;
2873
+ Component.MARKER_START = (uid) => `rasti-s-${uid}`;
2874
+ Component.MARKER_END = (uid) => `rasti-e-${uid}`;
2766
2875
 
2767
2876
  /**
2768
2877
  * Components are a special kind of `View` that is designed to be easily composable,
package/dist/rasti.min.js CHANGED
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Rasti={})}(this,(function(t){"use strict";function e(t){if("function"!=typeof t)throw new TypeError("Listener must be a function")}class s{on(t,s){return e(s),this.listeners||(this.listeners={}),this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(s),()=>this.off(t,s)}once(t,s){e(s);const i=(...e)=>{s(...e),this.off(t,i)};return this.on(t,i)}off(t,e){this.listeners&&(t?this.listeners[t]&&(e?(this.listeners[t]=this.listeners[t].filter((t=>t!==e)),this.listeners[t].length||delete this.listeners[t]):delete this.listeners[t],Object.keys(this.listeners).length||delete this.listeners):delete this.listeners)}emit(t,...e){this.listeners&&this.listeners[t]&&this.listeners[t].slice().forEach((t=>t(...e)))}listenTo(t,e,s){return t.on(e,s),this.listeningTo||(this.listeningTo=[]),this.listeningTo.push({emitter:t,type:e,listener:s}),()=>this.stopListening(t,e,s)}listenToOnce(t,s,i){e(i);const n=(...e)=>{i(...e),this.stopListening(t,s,n)};return this.listenTo(t,s,n)}stopListening(t,e,s){this.listeningTo&&(this.listeningTo=this.listeningTo.filter((i=>!(!t||t===i.emitter&&!e||t===i.emitter&&e===i.type&&!s||t===i.emitter&&e===i.type&&s===i.listener)||(i.emitter.off(i.type,i.listener),!1))),this.listeningTo.length||delete this.listeningTo)}}const i=(t,e,...s)=>"function"!=typeof t?t:t.apply(e,s);class n extends s{constructor(){super(),this.preinitialize.apply(this,arguments),this.attributes=Object.assign({},i(this.defaults,this),this.parse.apply(this,arguments)),this.previous={},Object.keys(this.attributes).forEach(this.defineAttribute.bind(this))}preinitialize(){}defineAttribute(t){Object.defineProperty(this,`${this.constructor.attributePrefix}${t}`,{get:()=>this.get(t),set:e=>{this.set(t,e)}})}get(t){return this.attributes[t]}set(t,e,...s){let i,n;"object"==typeof t?(i=t,n=[e,...s]):(i={[t]:e},n=s);const r=this._changing;this._changing=!0;const h={};r||(this.previous=Object.assign({},this.attributes)),Object.keys(i).forEach((t=>{i[t]!==this.attributes[t]&&(h[t]=i[t],this.attributes[t]=i[t])}));const o=Object.keys(h);if(o.length&&(this._pending=["change",this,h,...n]),o.forEach((t=>{this.emit(`change:${t}`,this,i[t],...n)})),r)return this;for(;this._pending;){const t=this._pending;this._pending=null,this.emit.apply(this,t)}return this._pending=null,this._changing=!1,this}parse(t){return t}toJSON(){return Object.assign({},this.attributes)}}n.attributePrefix="";const r=["el","tag","attributes","events","model","template","onDestroy"];class h extends s{constructor(t={}){super(),this.preinitialize.apply(this,arguments),this.delegatedEventListeners=[],this.children=[],this.destroyQueue=[],this.viewOptions=[],r.forEach((e=>{e in t&&(this[e]=t[e],this.viewOptions.push(e))})),this.ensureUid(),this.ensureElement()}preinitialize(){}$(t){return this.el.querySelector(t)}$$(t){return this.el.querySelectorAll(t)}destroy(){return this.destroyChildren(),this.undelegateEvents(),this.stopListening(),this.off(),this.destroyQueue.forEach((t=>t())),this.destroyQueue=[],this.onDestroy.apply(this,arguments),this.destroyed=!0,this}onDestroy(){}addChild(t){return this.children.push(t),t}destroyChildren(){this.children.forEach((t=>t.destroy())),this.children=[]}ensureUid(){this.uid||(this.uid="r-"+ ++h.uid)}ensureElement(){if(this.el)this.el=i(this.el,this);else{const t=i(this.tag,this),e=i(this.attributes,this);this.el=this.createElement(t,e)}this.delegateEvents()}createElement(t="div",e={}){let s=document.createElement(t);return Object.keys(e).forEach((t=>s.setAttribute(t,e[t]))),s}removeElement(){return this.el.parentNode.removeChild(this.el),this}delegateEvents(t){if(t||(t=i(this.events,this)),!t)return this;this.delegatedEventListeners.length&&this.undelegateEvents();let s={};return Object.keys(t).forEach((i=>{const n=i.split(" "),r=n.shift(),h=n.join(" ");let o=t[i];"string"==typeof o&&(o=this[o]),e(o),s[r]||(s[r]=[]),s[r].push({selector:h,listener:o})})),Object.keys(s).forEach((t=>{const e=e=>{s[t].forEach((({selector:t,listener:s})=>{if(!t)return void s.call(this,e,this,this.el);let i=e.target;for(;i&&i!==this.el;)i.matches&&i.matches(t)&&s.call(this,e,this,i),i=i.parentElement}))};this.delegatedEventListeners.push({type:t,listener:e}),this.el.addEventListener(t,e)})),this}undelegateEvents(){return this.delegatedEventListeners.forEach((({type:t,listener:e})=>{this.el.removeEventListener(t,e)})),this.delegatedEventListeners=[],this}render(){return this.template&&(this.el.innerHTML=this.template(this.model)),this}static sanitize(t){return`${t}`.replace(/[&<>"']/g,(t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"}[t])))}}h.uid=0;class o{constructor(t){this.value=t}toString(){return this.value}}class a{constructor(t){this.items=t}}class l{constructor(){this.listeners=[],this.types=new Set,this.previousSize=0}addListener(t,e){return this.types.add(e),this.listeners.push(t),this.listeners.length-1}reset(){this.listeners=[],this.previousSize=this.types.size}hasPendingTypes(){return this.types.size>this.previousSize}}class c{constructor(){}reset(){this.paused=0,this.previous=this.tracked||new Map,this.tracked=new Map,this.positionStack=[0]}push(){this.positionStack.push(0)}pop(){this.positionStack.pop()}increment(){this.positionStack[this.positionStack.length-1]++}pause(){this.paused++}resume(){this.paused--}getPath(){return this.positionStack.join("-")}track(t){return 0===this.paused&&this.tracked.set(this.getPath(),t),t}findRecyclable(t){const e=this.previous.get(this.getPath());return e&&e.constructor===t&&!e.key?e:null}}const u=["value","checked","selected"];class p{constructor(t){this.getSelector=t.getSelector,this.getAttributes=t.getAttributes,this.previousAttributes={}}hydrate(t){this.ref=t.querySelector(this.getSelector())}update(){const t=this.getAttributes(),{remove:e,add:s}=function(t,e={}){const s={},i=[];return Object.keys(t).forEach((i=>{let n=t[i];n!==e[i]&&(!0===n?s[i]="":!1!==n&&(null==n&&(n=""),s[i]=n))})),Object.keys(e).forEach((s=>{(!(s in t)||e[s]!==t[s]&&!1===t[s])&&i.push(s)})),{add:s,remove:i}}(t,this.previousAttributes);this.previousAttributes=t,e.forEach((t=>{this.ref.removeAttribute(t),u.includes(t)&&t in this.ref&&(this.ref[t]="value"===t&&"")})),Object.keys(s).forEach((t=>{const e=s[t];this.ref.setAttribute(t,e),u.includes(t)&&t in this.ref&&(this.ref[t]="value"===t?e:!1!==e&&"false"!==e)}))}}const d=["value","checked","selected"];function f(t,e){t.nodeType===e.nodeType?t.nodeType!==Node.TEXT_NODE?t.tagName===e.tagName?(((t,e)=>{const s=e.attributes,i=t.attributes,n=new Set;for(let e=0,i=s.length;e<i;e++){const{name:i,value:r}=s[e];n.add(i),t.getAttribute(i)!==r&&t.setAttribute(i,r)}for(let e=i.length-1;e>=0;e--){const{name:s}=i[e];n.has(s)||t.removeAttribute(s)}for(let s=0,i=d.length;s<i;s++){const i=d[s];i in t&&t[i]!==e[i]&&(t[i]=e[i])}})(t,e),((t,e)=>{const s=t.childNodes,i=e.childNodes,n=s.length;if(n!==i.length)return!1;for(let t=0;t<n;t++)if(!s[t].isEqualNode(i[t]))return!1;return!0})(t,e)||((t,e)=>{const s=Array.from(e.childNodes);t.replaceChildren(...s)})(t,e)):t.replaceWith(e):t.nodeValue!==e.nodeValue&&(t.nodeValue=e.nodeValue):t.replaceWith(e)}function g(t,e,s=(()=>!1),i){let n=i||t.firstChild;for(;n;){if(n.nodeType===Node.COMMENT_NODE&&n.data.trim()===e)return n;if(n.nodeType!==Node.ELEMENT_NODE||s(n)||!n.firstChild){for(;n&&!n.nextSibling;)if(n=n.parentNode,!n||n===t)return null;n&&(n=n.nextSibling)}else n=n.firstChild}return null}class E{constructor(t){this.getStart=t.getStart,this.getEnd=t.getEnd,this.expression=t.expression,this.isComponent=t.isComponent,this.isElement=t.isElement,this.previousItems=[]}hydrate(t){const e=g(t,this.getStart(),this.isComponent),s=g(t,this.getEnd(),this.isComponent,e);this.ref=[e,s]}update(t,e){if(e)return void e.replaceWith(t.firstChild);const[s,i]=this.ref,n=s.nextSibling,r=n===i,h=!r&&n.nextSibling===i,o=t.children;if(r)i.parentNode.insertBefore(t,i);else if(h&&1===o.length)this.isElement(n)?n.replaceWith(o[0]):f(n,o[0]);else{const e=document.createRange();e.setStartAfter(s),e.setEndBefore(i),e.deleteContents(),e.insertNode(t)}}}const m=t=>t.reduce(((t,e)=>(Array.isArray(e)?t.push(...m(e)):t.push(e),t)),[]);function y(t){const e=document.createElement("template");return e.innerHTML=`${t}`.trim(),e.content}function T(t){const e=[];return Object.keys(t).forEach((s=>{let i=t[s];!0===i?e.push(s):!1!==i&&(null==i&&(i=""),e.push(`${s}="${i}"`))})),e.join(" ")}const b=(t,e)=>i(t,e,e),v=t=>!!(t&&t.dataset&&t.dataset[_.DATASET_ELEMENT]&&t.dataset[_.DATASET_ELEMENT].endsWith("-1")),A=t=>!!(t&&t.dataset&&t.dataset[_.DATASET_ELEMENT]),R=(t,e)=>t.reduce(((t,s,i)=>(t.push(s),void 0!==e[i]&&t.push(_.PLACEHOLDER(i)),t)),[]).join(""),L=(t,e)=>{const s=_.PLACEHOLDER("(\\d+)"),i=t.match(new RegExp(`^${s}$`));if(i)return[e[parseInt(i[1],10)]];const n=new RegExp(`${s}`,"g"),r=[];let h,o=0;for(;null!==(h=n.exec(t));){const s=t.slice(o,h.index);r.push(_.markAsSafeHTML(s),e[parseInt(h[1],10)]),o=h.index+h[0].length}return r.push(_.markAsSafeHTML(t.slice(o))),r},C=(t,e)=>t.reduce(((t,s)=>{const i=e(s[0]);if(1===s.length)"object"==typeof i?t=Object.assign(t,i):"string"==typeof i&&(t[i]=!0);else{const n=s[2]?e(s[1]):s[1];t[i]=n}return t}),{}),$=(t,e,s)=>{const i={};return Object.keys(t).forEach((n=>{const r=n.match(/on(([A-Z]{1}[a-z]+)+)/);if(r&&r[1]){const h=r[1].toLowerCase(),o=t[n];if(o){const t=e.addListener(o,h);i[s(h)]=t}}else i[n]=t[n]})),i},S=(t,e,s=!1)=>{const i=_.PLACEHOLDER("(\\d+)"),n=new Map;return s||(t=t.replace(new RegExp(i,"g"),((t,s)=>{const i=e[s];if(i&&i.prototype instanceof _){if(n.has(i))return n.get(i);n.set(i,t)}return t}))),t.replace(new RegExp(`<(${i})([^>]*)>([\\s\\S]*?)</\\1>|<(${i})([^>]*)/>`,"g"),((t,s,i,n,r,h,o,l)=>{let c,u,p;if(s?(c=e[i],u=n):(c=void 0!==o?e[o]:h,u=l),!(c.prototype instanceof _))return t;if(s){const t=S(r,e,!0),s=x(t,e);p=L(s,e)}const d=w(u,e);return e.push((function(){const t=C(d,(t=>b(t,this)));return p&&(t.renderChildren=()=>new a(p.map((t=>b(t,this))))),c.mount(t)})),_.PLACEHOLDER(e.length-1)}))},M=(t,e)=>{const s=_.PLACEHOLDER("(?:\\d+)");return t.replace(new RegExp(`<(${s}|[a-z]+[1-6]?)(?:\\s*)((?:"[^"]*"|'[^']*'|[^>])*)(/?>)`,"gi"),e)},O=(t,e,s)=>{const n=_.PLACEHOLDER("(?:\\d+)");if(t.match(new RegExp(`^\\s*${n}\\s*$`)))return t;if(!t.match(new RegExp(`^\\s*<([a-z]+[1-6]?|${n})([^>]*)>([\\s\\S]*?)</(\\1|${n})>\\s*$|^\\s*<([a-z]+[1-6]?|${n})([^>]*)/>\\s*$`)))throw new SyntaxError(`Template must have a single root element or be a container component: "${t.trim()}"`);let r=0;return M(t,((t,h,o,a)=>{const l=0===r,c=++r;if(!l&&!o.match(new RegExp(n)))return t;const u=w(o,e),p=t=>`${t}-${c}`,d=s.length;s.push({getSelector:function(){return`[${_.ATTRIBUTE_ELEMENT}="${p(this.uid)}"]`},getAttributes:function(){const t=$(C(u,(t=>b(t,this))),this.eventsManager,(t=>_.ATTRIBUTE_EVENT(t,this.uid)));return l&&this.attributes&&Object.assign(t,i(this.attributes,this)),t[_.ATTRIBUTE_ELEMENT]=p(this.uid),t}}),e.push((function(){const t=this.template.elements[d],e=t.getAttributes.call(this);return t.previousAttributes=e,_.markAsSafeHTML(T(e))}));return`<${h} ${_.PLACEHOLDER(e.length-1)}${a}`}))},x=(t,e)=>{const s=_.PLACEHOLDER("(?:\\d+)");return M(t,((t,i,n,r)=>{if(!n.match(new RegExp(s)))return t;const h=w(n,e),o=function(){return $(C(h,(t=>b(t,this))),this.eventsManager,(t=>_.ATTRIBUTE_EVENT(t,this.uid)))};e.push((function(){const t=o.call(this);return _.markAsSafeHTML(T(t))}));return`<${i} ${_.PLACEHOLDER(e.length-1)}${r}`}))},k=(t,e,s)=>{const i=_.PLACEHOLDER("(\\d+)");let n=0;return t.replace(new RegExp(i,"g"),(function(i,r,h){const o=t.substring(0,h);if(o.lastIndexOf("<")>o.lastIndexOf(">"))return i;const a=++n;const l=s.length;return s.push({getStart:function(){return _.MARKER_START(`${this.uid}-${a}`)},getEnd:function(){return _.MARKER_END(`${this.uid}-${a}`)},expression:e[r]}),e.push((function(){return this.template.interpolations[l]})),_.PLACEHOLDER(e.length-1)}))},w=(t,e)=>{const s=_.PLACEHOLDER("(\\d+)"),i=[],n=new RegExp(`(${s}|[\\w-]+)(?:=(["']?)(?:${s}|((?:.?(?!["']?\\s+(?:\\S+)=|\\s*/?[>"']))+.))\\3)?`,"g");let r;for(;null!==(r=n.exec(t));){const[,t,s,n,h,o]=r,a=void 0!==s?e[parseInt(s,10)]:t,l=void 0!==h?e[parseInt(h,10)]:o;void 0!==l?i.push([a,l,!!n]):i.push([a])}return i},N=["key","state","onCreate","onChange","onHydrate","onRecycle","onUpdate"];class _ extends h{constructor(t={}){super(...arguments),this.componentOptions=[],N.forEach((e=>{e in t&&(this[e]=t[e],this.componentOptions.push(e))}));const e={};Object.keys(t).forEach((s=>{this.viewOptions.includes(s)||this.componentOptions.includes(s)||(e[s]=t[s])})),this.props=new n(e),this.options=t,this.partial=this.partial.bind(this),this.onChange=this.onChange.bind(this),this.onCreate.apply(this,arguments)}events(){const t={};return this.eventsManager.types.forEach((s=>{const i=_.ATTRIBUTE_EVENT(s,this.uid),n=function(t,s,n){const r=n.getAttribute(i);if(r){let i=this.eventsManager.listeners[parseInt(r,10)];"string"==typeof i&&(i=this[i]),e(i),i.call(this,t,s,n)}};t[`${s} [${i}]`]=n,t[s]=n})),t}ensureElement(){this.eventsManager=new l,this.pathManager=new c,this.template=i(this.template,this),this.el&&(this.el=i(this.el,this),this.toString(),this.hydrate(this.el.parentNode))}isContainer(){return 0===this.template.elements.length&&1===this.template.interpolations.length}subscribe(t,e="change",s=this.onChange){return t.on&&this.listenTo(t,e,s),this}hydrate(t){return["model","state","props"].forEach((t=>{this[t]&&this.subscribe(this[t])})),this.isContainer()?(this.children[0].hydrate(t),this.el=this.children[0].el):(this.template.elements.forEach(((e,s)=>{0===s?(e.hydrate(t),this.el?e.ref=this.el:this.el=e.ref):e.hydrate(this.el)})),this.delegateEvents(),this.template.interpolations.forEach((t=>t.hydrate(this.el))),this.children.forEach((t=>t.hydrate(this.el)))),this.onHydrate.call(this),this}recycle(t,e){if(t){g(t,_.MARKER_RECYCLED(this.uid),v).replaceWith(this.el)}return e&&this.props.set(e),this.onRecycle.call(this),this}getRecycledMarker(){return`\x3c!--${_.MARKER_RECYCLED(this.uid)}--\x3e`}partial(t,...e){const s=L(x(S(R(t,e).trim(),e),e),e).map((t=>b(t,this)));return new a(s)}renderTemplatePart(t,e,s=[]){const i=b(t,this),n=t=>{if(null==t||!1===t||!0===t)return"";if(t instanceof o)return s.push(t),t;if(t instanceof _)return s.push(t),e(t);if(t instanceof a){if(1===t.items.length)return n(t.items[0]);this.pathManager.push();const e=t.items.map((t=>(this.pathManager.increment(),n(t)))).join("");return this.pathManager.pop(),e}if(Array.isArray(t)){this.pathManager.pause();const e=m(t).map(n).join("");return this.pathManager.resume(),e}if(t instanceof E){this.pathManager.increment();const e=this.isContainer()?"":`\x3c!--${t.getStart()}--\x3e`,i=this.isContainer()?"":`\x3c!--${t.getEnd()}--\x3e`,r=n(b(t.expression,this));return t.previousItems=s,`${e}${r}${i}`}const i=_.sanitize(t);return s.push(i),i};return`${n(i)}`}toString(){this.destroyChildren(),this.eventsManager.reset(),this.pathManager.reset();const t=t=>(this.pathManager.track(t),this.addChild(t));return this.template.parts.map((e=>this.renderTemplatePart(e,t))).join("")}render(){if(this.destroyed)return this;if(!this.el){const t=y(this);return this.hydrate(t),this}this.eventsManager.reset(),this.pathManager.reset();const t=this.isContainer()?null:document.activeElement;this.template.elements.forEach((t=>t.update()));const e=this.children;return this.children=[],this.template.interpolations.forEach((t=>{const s=[],i=[];this.pathManager.increment();const n=t.previousItems,r=[],h=this.renderTemplatePart(t.expression,(t=>{let n=t,r=null;return r=t.key?e.find((e=>e.key===t.key)):this.pathManager.findRecyclable(t.constructor),r?(n=r.getRecycledMarker(),i.push([r,t]),r.key||this.pathManager.track(r)):(s.push(t),this.pathManager.track(t)),n}),r);t.previousItems=r;const o=([t,e],s)=>{this.addChild(t).recycle(s,e.props.toJSON()),e.destroy()};if(1===n.length&&n[0]instanceof _&&1===r.length&&r[0]instanceof _&&1===i.length)return void o(i[0],null);const a=y(h);i.forEach((t=>o(t,a))),s.forEach((t=>this.addChild(t).hydrate(a)));const l=this.isContainer()?this.el:null;t.update(a,l)})),e.forEach((t=>{this.children.indexOf(t)<0&&t.destroy()})),this.isContainer()?this.el=this.children[0].el:this.eventsManager.hasPendingTypes()&&this.delegateEvents(),t&&this.el.contains(t)&&t.focus(),this.onUpdate.call(this),this}onCreate(){}onChange(){this.render()}onHydrate(){}onRecycle(){}onUpdate(){}onDestroy(){}static markAsSafeHTML(t){return new o(t)}static extend(t){const e=this;class s extends e{}return Object.assign(s.prototype,"function"==typeof t?t(e.prototype):t),s}static mount(t,e,s){const i=new this(t);return e&&(s?(i.toString(),i.hydrate(e)):e.append(i.render().el)),i}static create(t,...e){"function"==typeof t&&(e=[t],t=["",""]);const s=[],i=[],n=L(k(O(S(R(t,e).trim(),e),e,s),e,i),e);return this.extend({template(){return{elements:s.map((t=>new p({getSelector:t.getSelector.bind(this),getAttributes:t.getAttributes.bind(this)}))),interpolations:i.map((t=>new E({getStart:t.getStart.bind(this),getEnd:t.getEnd.bind(this),expression:t.expression,isComponent:v,isElement:A}))),parts:n}}})}}_.ATTRIBUTE_ELEMENT="data-rasti-el",_.ATTRIBUTE_EVENT=(t,e)=>`data-rasti-on-${t}-${e}`,_.DATASET_ELEMENT="rastiEl",_.PLACEHOLDER=t=>`__RASTI-${t}__`,_.MARKER_RECYCLED=t=>`rasti-recycled-${t}`,_.MARKER_START=t=>`rasti-start-${t}`,_.MARKER_END=t=>`rasti-end-${t}`;var j=_.create`<div></div>`;t.Component=j,t.Emitter=s,t.Model=n,t.View=h}));
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Rasti={})}(this,(function(t){"use strict";function e(t){if("function"!=typeof t)throw new TypeError("Listener must be a function")}class s{on(t,s){return e(s),this.listeners||(this.listeners={}),this.listeners[t]||(this.listeners[t]=[]),this.listeners[t].push(s),()=>this.off(t,s)}once(t,s){e(s);const i=(...e)=>{s(...e),this.off(t,i)};return this.on(t,i)}off(t,e){this.listeners&&(t?this.listeners[t]&&(e?(this.listeners[t]=this.listeners[t].filter((t=>t!==e)),this.listeners[t].length||delete this.listeners[t]):delete this.listeners[t],Object.keys(this.listeners).length||delete this.listeners):delete this.listeners)}emit(t,...e){this.listeners&&this.listeners[t]&&this.listeners[t].slice().forEach((t=>t(...e)))}listenTo(t,e,s){return t.on(e,s),this.listeningTo||(this.listeningTo=[]),this.listeningTo.push({emitter:t,type:e,listener:s}),()=>this.stopListening(t,e,s)}listenToOnce(t,s,i){e(i);const n=(...e)=>{i(...e),this.stopListening(t,s,n)};return this.listenTo(t,s,n)}stopListening(t,e,s){this.listeningTo&&(this.listeningTo=this.listeningTo.filter((i=>!(!t||t===i.emitter&&!e||t===i.emitter&&e===i.type&&!s||t===i.emitter&&e===i.type&&s===i.listener)||(i.emitter.off(i.type,i.listener),!1))),this.listeningTo.length||delete this.listeningTo)}}const i=(t,e,...s)=>"function"!=typeof t?t:t.apply(e,s);class n extends s{constructor(){super(),this.preinitialize.apply(this,arguments),this.attributes=Object.assign({},i(this.defaults,this),this.parse.apply(this,arguments)),this.previous={},Object.keys(this.attributes).forEach(this.defineAttribute.bind(this))}preinitialize(){}defineAttribute(t){Object.defineProperty(this,`${this.constructor.attributePrefix}${t}`,{get:()=>this.get(t),set:e=>{this.set(t,e)}})}get(t){return this.attributes[t]}set(t,e,...s){let i,n;"object"==typeof t?(i=t,n=[e,...s]):(i={[t]:e},n=s);const r=this._changing;this._changing=!0;const h={};r||(this.previous=Object.assign({},this.attributes)),Object.keys(i).forEach((t=>{i[t]!==this.attributes[t]&&(h[t]=i[t],this.attributes[t]=i[t])}));const o=Object.keys(h);if(o.length&&(this._pending=["change",this,h,...n]),o.forEach((t=>{this.emit(`change:${t}`,this,i[t],...n)})),r)return this;for(;this._pending;){const t=this._pending;this._pending=null,this.emit.apply(this,t)}return this._pending=null,this._changing=!1,this}parse(t){return t}toJSON(){return Object.assign({},this.attributes)}}n.attributePrefix="";const r=["el","tag","attributes","events","model","template","onDestroy"];class h extends s{constructor(t={}){super(),this.preinitialize.apply(this,arguments),this.delegatedEventListeners=[],this.children=[],this.destroyQueue=[],this.viewOptions=[],r.forEach((e=>{e in t&&(this[e]=t[e],this.viewOptions.push(e))})),this.ensureUid(),this.ensureElement()}preinitialize(){}$(t){return this.el.querySelector(t)}$$(t){return this.el.querySelectorAll(t)}destroy(){return this.destroyChildren(),this.undelegateEvents(),this.stopListening(),this.off(),this.destroyQueue.forEach((t=>t())),this.destroyQueue=[],this.onDestroy.apply(this,arguments),this.destroyed=!0,this}onDestroy(){}addChild(t){return this.children.push(t),t}destroyChildren(){this.children.forEach((t=>t.destroy())),this.children=[]}ensureUid(){this.uid||(this.uid="r"+ ++h.uid)}ensureElement(){if(this.el)this.el=i(this.el,this);else{const t=i(this.tag,this),e=i(this.attributes,this);this.el=this.createElement(t,e)}this.delegateEvents()}createElement(t="div",e={}){let s=document.createElement(t);return Object.keys(e).forEach((t=>s.setAttribute(t,e[t]))),s}removeElement(){return this.el.remove(),this}delegateEvents(t){if(t||(t=i(this.events,this)),!t)return this;this.delegatedEventListeners.length&&this.undelegateEvents();let s={};return Object.keys(t).forEach((i=>{const n=i.split(" "),r=n.shift(),h=n.join(" ");let o=t[i];"string"==typeof o&&(o=this[o]),e(o),s[r]||(s[r]=[]),s[r].push({selector:h,listener:o})})),Object.keys(s).forEach((t=>{const e=e=>{s[t].forEach((({selector:t,listener:s})=>{if(!t)return void s.call(this,e,this,this.el);let i=e.target;for(;i&&i!==this.el;)i.matches&&i.matches(t)&&s.call(this,e,this,i),i=i.parentElement}))};this.delegatedEventListeners.push({type:t,listener:e}),this.el.addEventListener(t,e)})),this}undelegateEvents(){return this.delegatedEventListeners.forEach((({type:t,listener:e})=>{this.el.removeEventListener(t,e)})),this.delegatedEventListeners=[],this}render(){return this.template&&(this.el.innerHTML=this.template(this.model)),this}static sanitize(t){return`${t}`.replace(/[&<>"']/g,(t=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"}[t])))}}h.uid=0;class o{constructor(t){this.value=t}toString(){return this.value}}class a{constructor(t){this.items=t}}class l{constructor(){this.listeners=[],this.types=new Set,this.previousSize=0}addListener(t,e){return this.types.add(e),this.listeners.push(t),this.listeners.length-1}reset(){this.listeners=[],this.previousSize=this.types.size}hasPendingTypes(){return this.types.size>this.previousSize}}class c{constructor(){}reset(){this.paused=0,this.previous=this.tracked||new Map,this.tracked=new Map,this.positionStack=[0]}push(){this.positionStack.push(0)}pop(){this.positionStack.pop()}increment(){this.positionStack[this.positionStack.length-1]++}pause(){this.paused++}resume(){this.paused--}getPath(){return this.positionStack.join("-")}track(t){return 0===this.paused&&this.tracked.set(this.getPath(),t),t}findRecyclable(t){const e=this.previous.get(this.getPath());return e&&e.constructor===t&&!e.key?e:null}}const u=["value","checked","selected"];let d=class{constructor(t){this.getSelector=t.getSelector,this.getAttributes=t.getAttributes,this.previousAttributes={}}hydrate(t){this.ref=t.querySelector(this.getSelector())}update(){const t=this.getAttributes(),{remove:e,add:s}=function(t,e={}){const s={},i=[];return Object.keys(t).forEach((i=>{let n=t[i];n!==e[i]&&(!0===n?s[i]="":!1!==n&&(null==n&&(n=""),s[i]=n))})),Object.keys(e).forEach((s=>{(!(s in t)||e[s]!==t[s]&&!1===t[s])&&i.push(s)})),{add:s,remove:i}}(t,this.previousAttributes);this.previousAttributes=t,e.forEach((t=>{this.ref.removeAttribute(t),u.includes(t)&&t in this.ref&&(this.ref[t]="value"===t&&"")})),Object.keys(s).forEach((t=>{const e=s[t];this.ref.setAttribute(t,e),u.includes(t)&&t in this.ref&&(this.ref[t]="value"===t?e:!1!==e&&"false"!==e)}))}};const p=["value","checked","selected"];function f(t,e){t.nodeType===e.nodeType?t.nodeType!==Node.TEXT_NODE?t.tagName===e.tagName?(((t,e)=>{const s=e.attributes,i=t.attributes,n=new Set;for(let e=0,i=s.length;e<i;e++){const{name:i,value:r}=s[e];n.add(i),t.getAttribute(i)!==r&&t.setAttribute(i,r)}for(let e=i.length-1;e>=0;e--){const{name:s}=i[e];n.has(s)||t.removeAttribute(s)}for(let s=0,i=p.length;s<i;s++){const i=p[s];i in t&&t[i]!==e[i]&&(t[i]=e[i])}})(t,e),((t,e)=>{const s=t.childNodes,i=e.childNodes,n=s.length;if(n!==i.length)return!1;for(let t=0;t<n;t++)if(!s[t].isEqualNode(i[t]))return!1;return!0})(t,e)||((t,e)=>{const s=Array.from(e.childNodes);t.replaceChildren(...s)})(t,e)):t.replaceWith(e):t.nodeValue!==e.nodeValue&&(t.nodeValue=e.nodeValue):t.replaceWith(e)}function g(t,e,s=(()=>!1),i){let n=i||t.firstChild;for(;n;){if(n.nodeType===Node.COMMENT_NODE&&n.data.trim()===e)return n;if(n.nodeType!==Node.ELEMENT_NODE||s(n)||!n.firstChild){for(;n&&!n.nextSibling;)if(n=n.parentNode,!n||n===t)return null;n&&(n=n.nextSibling)}else n=n.firstChild}return null}class E{constructor(t){this.getStart=t.getStart,this.getEnd=t.getEnd,this.expression=t.expression,this.shouldSkipFind=t.shouldSkipFind,this.shouldSkipSync=t.shouldSkipSync,this.previousItems=[]}hydrate(t){const e=g(t,this.getStart(),this.shouldSkipFind),s=g(t,this.getEnd(),this.shouldSkipFind,e);this.ref=[e,s]}update(t,e){let s;const[i,n]=this.ref,r=i.nextSibling,h=r===n,o=!h&&r.nextSibling===n;if(h?n.before(t):!o||1!==t.children.length||this.shouldSkipSync(r)||this.shouldSkipSync(t.firstChild)?(s=document.createComment(""),n.before(s),n.before(t)):f(r,t.firstChild),e(),s){if(this.ref[0].nextSibling===s)s.remove();else{const t=document.createRange();t.setStartAfter(this.ref[0]),t.setEndAfter(s),t.deleteContents()}}}updateElement(t,e,s){const i=document.createComment("");t.after(i),i.after(e.firstChild),s(),t.nextSibling===i&&t.remove(),i.remove()}}const m=t=>t.reduce(((t,e)=>(Array.isArray(e)?t.push(...m(e)):t.push(e),t)),[]);function y(t){const e=document.createElement("template");return e.innerHTML=`${t}`.trim(),e.content}function b(t){const e=[];return Object.keys(t).forEach((s=>{let i=t[s];!0===i?e.push(s):!1!==i&&(null==i&&(i=""),e.push(`${s}="${i}"`))})),e.join(" ")}const T=(t,e)=>i(t,e,e),v=t=>!!(t&&t.dataset&&t.dataset[N.DATASET_ELEMENT]&&t.dataset[N.DATASET_ELEMENT].endsWith("-1")),A=t=>!!(t&&t.dataset&&t.dataset[N.DATASET_ELEMENT])||!!t.querySelector(`[${N.ATTRIBUTE_ELEMENT}]`),S=(t,e)=>t.reduce(((t,s,i)=>(t.push(s),void 0!==e[i]&&t.push(N.PLACEHOLDER(i)),t)),[]).join(""),R=(t,e)=>{const s=N.PLACEHOLDER("(\\d+)"),i=t.match(new RegExp(`^${s}$`));if(i)return[e[parseInt(i[1],10)]];const n=new RegExp(`${s}`,"g"),r=[];let h,o=0;for(;null!==(h=n.exec(t));){const s=t.slice(o,h.index);r.push(N.markAsSafeHTML(s),e[parseInt(h[1],10)]),o=h.index+h[0].length}return r.push(N.markAsSafeHTML(t.slice(o))),r},L=(t,e)=>t.reduce(((t,s)=>{const i=e(s[0]);if(1===s.length)"object"==typeof i?t=Object.assign(t,i):"string"==typeof i&&(t[i]=!0);else{const n=s[2]?e(s[1]):s[1];t[i]=n}return t}),{}),$=(t,e,s)=>{const i={};return Object.keys(t).forEach((n=>{const r=n.match(/on(([A-Z]{1}[a-z]+)+)/);if(r&&r[1]){const h=r[1].toLowerCase(),o=t[n];if(o){const t=e.addListener(o,h);i[s(h)]=t}}else i[n]=t[n]})),i},C=(t,e,s=!1)=>{const i=N.PLACEHOLDER("(\\d+)"),n=new Map;return s||(t=t.replace(new RegExp(i,"g"),((t,s)=>{const i=e[s];if(i&&i.prototype instanceof N){if(n.has(i))return n.get(i);n.set(i,t)}return t}))),t.replace(new RegExp(`<(${i})([^>]*)>([\\s\\S]*?)</\\1>|<(${i})([^>]*)/>`,"g"),((t,s,i,n,r,h,o,l)=>{let c,u,d;if(s?(c=e[i],u=n):(c=void 0!==o?e[o]:h,u=l),!(c.prototype instanceof N))return t;if(s){const t=C(r,e,!0),s=x(t,e);d=R(s,e)}const p=_(u,e);return e.push((function(){const t=L(p,(t=>T(t,this)));return d&&(t.renderChildren=()=>new a(d.map((t=>T(t,this))))),c.mount(t)})),N.PLACEHOLDER(e.length-1)}))},M=(t,e)=>{const s=N.PLACEHOLDER("(?:\\d+)");return t.replace(new RegExp(`<(${s}|[a-z]+[1-6]?)(?:\\s*)((?:"[^"]*"|'[^']*'|[^>])*)(/?>)`,"gi"),e)},k=(t,e,s)=>{const n=N.PLACEHOLDER("(?:\\d+)");if(t.match(new RegExp(`^\\s*${n}\\s*$`)))return t;if(!t.match(new RegExp(`^\\s*<([a-z]+[1-6]?|${n})([^>]*)>([\\s\\S]*?)</(\\1|${n})>\\s*$|^\\s*<([a-z]+[1-6]?|${n})([^>]*)/>\\s*$`)))throw new SyntaxError(`Template must have a single root element or be a container component: "${t.trim()}"`);let r=0;return M(t,((t,h,o,a)=>{const l=0===r,c=++r;if(!l&&!o.match(new RegExp(n)))return t;const u=_(o,e),d=t=>`${t}-${c}`,p=s.length;s.push({getSelector:function(){return`[${N.ATTRIBUTE_ELEMENT}="${d(this.uid)}"]`},getAttributes:function(){const t=$(L(u,(t=>T(t,this))),this.eventsManager,(t=>N.ATTRIBUTE_EVENT(t,this.uid)));return l&&this.attributes&&Object.assign(t,i(this.attributes,this)),t[N.ATTRIBUTE_ELEMENT]=d(this.uid),t}}),e.push((function(){const t=this.template.elements[p],e=t.getAttributes.call(this);return t.previousAttributes=e,N.markAsSafeHTML(b(e))}));return`<${h} ${N.PLACEHOLDER(e.length-1)}${a}`}))},x=(t,e)=>{const s=N.PLACEHOLDER("(?:\\d+)");return M(t,((t,i,n,r)=>{if(!n.match(new RegExp(s)))return t;const h=_(n,e),o=function(){return $(L(h,(t=>T(t,this))),this.eventsManager,(t=>N.ATTRIBUTE_EVENT(t,this.uid)))};e.push((function(){const t=o.call(this);return N.markAsSafeHTML(b(t))}));return`<${i} ${N.PLACEHOLDER(e.length-1)}${r}`}))},O=(t,e,s)=>{const i=N.PLACEHOLDER("(\\d+)");let n=0;return t.replace(new RegExp(i,"g"),(function(i,r,h){const o=t.substring(0,h);if(o.lastIndexOf("<")>o.lastIndexOf(">"))return i;const a=++n;const l=s.length;return s.push({getStart:function(){return N.MARKER_START(`${this.uid}-${a}`)},getEnd:function(){return N.MARKER_END(`${this.uid}-${a}`)},expression:e[r]}),e.push((function(){return this.template.interpolations[l]})),N.PLACEHOLDER(e.length-1)}))},_=(t,e)=>{const s=N.PLACEHOLDER("(\\d+)"),i=[],n=new RegExp(`(?:${s}|([\\w-]+))(?:=(["']?)(?:${s}|((?:.?(?!["']?\\s+(?:\\S+)=|\\s*/?[>"']))+.))?\\3)?`,"g");let r;for(;null!==(r=n.exec(t));){const[,t,s,n,h,o]=r,a=!!n;let l=void 0!==t?e[parseInt(t,10)]:s,c=void 0!==h?e[parseInt(h,10)]:o;a&&void 0===c&&(c=""),void 0!==c?i.push([l,c,a]):i.push([l])}return i},w=["key","state","onCreate","onChange","onHydrate","onRecycle","onUpdate"];class N extends h{constructor(t={}){super(...arguments),this.componentOptions=[],w.forEach((e=>{e in t&&(this[e]=t[e],this.componentOptions.push(e))}));const e={};Object.keys(t).forEach((s=>{this.viewOptions.includes(s)||this.componentOptions.includes(s)||(e[s]=t[s])})),this.props=new n(e),this.options=t,this.partial=this.partial.bind(this),this.onChange=this.onChange.bind(this),this.onCreate.apply(this,arguments)}events(){const t={};return this.eventsManager.types.forEach((s=>{const i=N.ATTRIBUTE_EVENT(s,this.uid),n=function(t,s,n){const r=n.getAttribute(i);if(r){let i=this.eventsManager.listeners[parseInt(r,10)];"string"==typeof i&&(i=this[i]),e(i),i.call(this,t,s,n)}};t[`${s} [${i}]`]=n,t[s]=n})),t}ensureElement(){this.eventsManager=new l,this.pathManager=new c,this.template=i(this.template,this),this.el&&(this.el=i(this.el,this),this.toString(),this.hydrate(this.el.parentNode))}isContainer(){return 0===this.template.elements.length&&1===this.template.interpolations.length}subscribe(t,e="change",s=this.onChange){return t.on&&this.listenTo(t,e,s),this}hydrate(t){return["model","state","props"].forEach((t=>{this[t]&&this.subscribe(this[t])})),this.isContainer()?(this.children[0].hydrate(t),this.el=this.children[0].el):(this.template.elements.forEach(((e,s)=>{0===s?(e.hydrate(t),this.el?e.ref=this.el:this.el=e.ref):e.hydrate(this.el)})),this.delegateEvents(),this.template.interpolations.forEach((t=>t.hydrate(this.el))),this.children.forEach((t=>t.hydrate(this.el)))),this.onHydrate.call(this),this}recycle(t,e){if(t){!function(t,e){if(Element.prototype.moveBefore)t.parentNode.moveBefore(e,t),t.remove();else{const s=document.activeElement;t.before(e),t.remove(),s&&s!==document.activeElement&&e.contains(s)&&s.focus()}}(g(t,N.MARKER_RECYCLED(this.uid),v),this.el)}return e&&this.props.set(e),this.onRecycle.call(this),this}getRecycledMarker(){return`\x3c!--${N.MARKER_RECYCLED(this.uid)}--\x3e`}partial(t,...e){const s=R(x(C(S(t,e).trim(),e),e),e).map((t=>T(t,this)));return new a(s)}renderTemplatePart(t,e,s=[]){const i=T(t,this),n=t=>{if(null==t||!1===t||!0===t)return"";if(t instanceof o)return s.push(t),t;if(t instanceof N)return s.push(t),e(t);if(t instanceof a){if(1===t.items.length)return n(t.items[0]);this.pathManager.push();const e=t.items.map((t=>(this.pathManager.increment(),n(t)))).join("");return this.pathManager.pop(),e}if(Array.isArray(t)){this.pathManager.pause();const e=m(t).map(n).join("");return this.pathManager.resume(),e}if(t instanceof E){this.pathManager.increment();const e=this.isContainer()?"":`\x3c!--${t.getStart()}--\x3e`,i=this.isContainer()?"":`\x3c!--${t.getEnd()}--\x3e`,r=n(T(t.expression,this));return t.previousItems=s,`${e}${r}${i}`}const i=N.sanitize(t);return s.push(i),i};return`${n(i)}`}toString(){this.destroyChildren(),this.eventsManager.reset(),this.pathManager.reset();const t=t=>(this.pathManager.track(t),this.addChild(t));return this.template.parts.map((e=>this.renderTemplatePart(e,t))).join("")}render(){if(this.destroyed)return this;if(!this.el){const t=y(this);return this.hydrate(t),this}this.eventsManager.reset(),this.pathManager.reset(),this.template.elements.forEach((t=>t.update()));const t=this.children;return this.children=[],this.template.interpolations.forEach((e=>{const s=[],i=[];this.pathManager.increment();const n=e.previousItems,r=[],h=this.renderTemplatePart(e.expression,(e=>{let n=e,r=null;return r=e.key?t.find((t=>t.key===e.key)):this.pathManager.findRecyclable(e.constructor),r?(n=r.getRecycledMarker(),i.push([r,e]),r.key||this.pathManager.track(r)):(s.push(e),this.pathManager.track(e)),n}),r);e.previousItems=r;const o=([t,e],s)=>{this.addChild(t).recycle(s,e.props.toJSON()),e.destroy()};if(1===n.length&&n[0]instanceof N&&1===r.length&&r[0]instanceof N&&1===i.length)return void o(i[0],null);const a=y(h),l=t=>()=>{i.forEach((e=>o(e,t))),s.forEach((e=>this.addChild(e).hydrate(t)))};this.isContainer()?e.updateElement(this.el,a,l(this.el.parentNode)):e.update(a,l(this.el))})),t.forEach((t=>{this.children.indexOf(t)<0&&t.destroy()})),this.isContainer()?this.el=this.children[0].el:this.eventsManager.hasPendingTypes()&&this.delegateEvents(),this.onUpdate.call(this),this}onCreate(){}onChange(){this.render()}onHydrate(){}onRecycle(){}onUpdate(){}onDestroy(){}static markAsSafeHTML(t){return new o(t)}static extend(t){const e=this;class s extends e{}return Object.assign(s.prototype,"function"==typeof t?t(e.prototype):t),s}static mount(t,e,s){const i=new this(t);return e&&(s?i.toString():e.append(y(i)),i.hydrate(e)),i}static create(t,...e){"function"==typeof t&&(e=[t],t=["",""]);const s=[],i=[],n=R(O(k(C(S(t,e).trim(),e),e,s),e,i),e);return this.extend({template(){return{elements:s.map((t=>new d({getSelector:t.getSelector.bind(this),getAttributes:t.getAttributes.bind(this)}))),interpolations:i.map((t=>new E({getStart:t.getStart.bind(this),getEnd:t.getEnd.bind(this),expression:t.expression,shouldSkipFind:v,shouldSkipSync:A}))),parts:n}}})}}N.ATTRIBUTE_ELEMENT="data-rasti-el",N.ATTRIBUTE_EVENT=(t,e)=>`data-rasti-on-${t}-${e}`,N.DATASET_ELEMENT="rastiEl",N.PLACEHOLDER=t=>`__RASTI_PH_${t}__`,N.MARKER_RECYCLED=t=>`rasti-r-${t}`,N.MARKER_START=t=>`rasti-s-${t}`,N.MARKER_END=t=>`rasti-e-${t}`;var j=N.create`<div></div>`;t.Component=j,t.Emitter=s,t.Model=n,t.View=h}));