@microsoft/fast-element 2.0.0-beta.1 → 2.0.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.json +348 -0
  2. package/CHANGELOG.md +114 -1
  3. package/dist/dts/components/attributes.d.ts +10 -0
  4. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +49 -25
  5. package/dist/dts/components/fast-definitions.d.ts +43 -9
  6. package/dist/dts/components/fast-element.d.ts +15 -21
  7. package/dist/dts/context.d.ts +157 -0
  8. package/dist/dts/di/di.d.ts +899 -0
  9. package/dist/dts/index.d.ts +2 -2
  10. package/dist/dts/interfaces.d.ts +45 -14
  11. package/dist/dts/metadata.d.ts +25 -0
  12. package/dist/dts/observation/arrays.d.ts +1 -1
  13. package/dist/dts/observation/observable.d.ts +101 -75
  14. package/dist/dts/pending-task.d.ts +20 -0
  15. package/dist/dts/platform.d.ts +7 -0
  16. package/dist/dts/polyfills.d.ts +1 -8
  17. package/dist/dts/state/exports.d.ts +3 -0
  18. package/dist/dts/state/reactive.d.ts +8 -0
  19. package/dist/dts/state/state.d.ts +141 -0
  20. package/dist/dts/state/visitor.d.ts +6 -0
  21. package/dist/dts/state/watch.d.ts +10 -0
  22. package/dist/dts/styles/css-directive.d.ts +2 -2
  23. package/dist/dts/styles/element-styles.d.ts +10 -17
  24. package/dist/dts/styles/host.d.ts +68 -0
  25. package/dist/dts/templating/binding-signal.d.ts +21 -0
  26. package/dist/dts/templating/binding-two-way.d.ts +39 -0
  27. package/dist/dts/templating/binding.d.ts +69 -294
  28. package/dist/dts/templating/children.d.ts +1 -1
  29. package/dist/dts/templating/compiler.d.ts +1 -2
  30. package/dist/dts/templating/html-directive.d.ts +93 -35
  31. package/dist/dts/templating/node-observation.d.ts +4 -5
  32. package/dist/dts/templating/ref.d.ts +5 -13
  33. package/dist/dts/templating/render.d.ts +272 -0
  34. package/dist/dts/templating/repeat.d.ts +20 -75
  35. package/dist/dts/templating/slotted.d.ts +1 -1
  36. package/dist/dts/templating/template.d.ts +12 -61
  37. package/dist/dts/templating/view.d.ts +77 -12
  38. package/dist/dts/templating/when.d.ts +3 -3
  39. package/dist/dts/testing/exports.d.ts +3 -0
  40. package/dist/dts/testing/fakes.d.ts +4 -0
  41. package/dist/dts/testing/fixture.d.ts +84 -0
  42. package/dist/dts/testing/timeout.d.ts +7 -0
  43. package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +0 -0
  44. package/dist/dts/utilities.d.ts +0 -18
  45. package/dist/esm/components/attributes.js +13 -4
  46. package/dist/esm/components/{controller.js → element-controller.js} +188 -109
  47. package/dist/esm/components/fast-definitions.js +38 -28
  48. package/dist/esm/components/fast-element.js +31 -12
  49. package/dist/esm/context.js +163 -0
  50. package/dist/esm/debug.js +36 -4
  51. package/dist/esm/di/di.js +1435 -0
  52. package/dist/esm/index.js +2 -1
  53. package/dist/esm/interfaces.js +4 -0
  54. package/dist/esm/metadata.js +60 -0
  55. package/dist/esm/observation/arrays.js +304 -3
  56. package/dist/esm/observation/observable.js +81 -87
  57. package/dist/esm/pending-task.js +16 -0
  58. package/dist/esm/platform.js +26 -1
  59. package/dist/esm/polyfills.js +1 -55
  60. package/dist/esm/state/exports.js +3 -0
  61. package/dist/esm/state/reactive.js +34 -0
  62. package/dist/esm/state/state.js +148 -0
  63. package/dist/esm/state/visitor.js +28 -0
  64. package/dist/esm/state/watch.js +36 -0
  65. package/dist/esm/styles/css.js +4 -4
  66. package/dist/esm/styles/element-styles.js +14 -33
  67. package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
  68. package/dist/esm/templating/binding-signal.js +83 -0
  69. package/dist/esm/templating/binding-two-way.js +103 -0
  70. package/dist/esm/templating/binding.js +134 -414
  71. package/dist/esm/templating/compiler.js +30 -7
  72. package/dist/esm/templating/html-directive.js +100 -28
  73. package/dist/esm/templating/node-observation.js +9 -8
  74. package/dist/esm/templating/ref.js +4 -12
  75. package/dist/esm/templating/render.js +391 -0
  76. package/dist/esm/templating/repeat.js +96 -72
  77. package/dist/esm/templating/template.js +11 -29
  78. package/dist/esm/templating/view.js +107 -29
  79. package/dist/esm/templating/when.js +5 -4
  80. package/dist/esm/testing/exports.js +3 -0
  81. package/dist/esm/testing/fakes.js +76 -0
  82. package/dist/esm/testing/fixture.js +86 -0
  83. package/dist/esm/testing/timeout.js +24 -0
  84. package/dist/esm/utilities.js +0 -95
  85. package/dist/fast-element.api.json +9278 -10745
  86. package/dist/fast-element.d.ts +707 -813
  87. package/dist/fast-element.debug.js +1229 -944
  88. package/dist/fast-element.debug.min.js +1 -1
  89. package/dist/fast-element.js +1191 -938
  90. package/dist/fast-element.min.js +1 -1
  91. package/dist/fast-element.untrimmed.d.ts +716 -824
  92. package/docs/api-report.md +265 -319
  93. package/package.json +39 -14
  94. package/dist/dts/hooks.d.ts +0 -20
  95. package/dist/dts/observation/behavior.d.ts +0 -19
  96. package/dist/dts/observation/splice-strategies.d.ts +0 -13
  97. package/dist/esm/hooks.js +0 -32
  98. package/dist/esm/observation/splice-strategies.js +0 -400
@@ -28,61 +28,6 @@ if (!globalThis.trustedTypes) {
28
28
  createPolicy: (n, r) => r,
29
29
  };
30
30
  }
31
- // ensure FAST global - duplicated in platform.ts
32
- const propConfig$1 = {
33
- configurable: false,
34
- enumerable: false,
35
- writable: false,
36
- };
37
- if (globalThis.FAST === void 0) {
38
- Reflect.defineProperty(globalThis, "FAST", Object.assign({ value: Object.create(null) }, propConfig$1));
39
- }
40
- const FAST$1 = globalThis.FAST;
41
- if (FAST$1.getById === void 0) {
42
- const storage = Object.create(null);
43
- Reflect.defineProperty(FAST$1, "getById", Object.assign({ value(id, initialize) {
44
- let found = storage[id];
45
- if (found === void 0) {
46
- found = initialize ? (storage[id] = initialize()) : null;
47
- }
48
- return found;
49
- } }, propConfig$1));
50
- }
51
- // duplicated from DOM
52
- const supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
53
- "replace" in CSSStyleSheet.prototype;
54
- function usableStyleTarget(target) {
55
- return target === document ? document.body : target;
56
- }
57
- let id$1 = 0;
58
- const nextStyleId = () => `fast-${++id$1}`;
59
- class StyleElementStrategy {
60
- constructor(styles) {
61
- this.styles = styles;
62
- this.styleClass = nextStyleId();
63
- }
64
- addStylesTo(target) {
65
- target = usableStyleTarget(target);
66
- const styles = this.styles;
67
- const styleClass = this.styleClass;
68
- for (let i = 0; i < styles.length; i++) {
69
- const element = document.createElement("style");
70
- element.innerHTML = styles[i];
71
- element.className = styleClass;
72
- target.append(element);
73
- }
74
- }
75
- removeStylesFrom(target) {
76
- const styles = target.querySelectorAll(`.${this.styleClass}`);
77
- target = usableStyleTarget(target);
78
- for (let i = 0, ii = styles.length; i < ii; ++i) {
79
- target.removeChild(styles[i]);
80
- }
81
- }
82
- }
83
- if (!supportsAdoptedStyleSheets) {
84
- FAST$1.getById(/* KernelServiceId.styleSheetStrategy */ 5, () => StyleElementStrategy);
85
- }
86
31
 
87
32
  // ensure FAST global - duplicated in polyfills.ts and debug.ts
88
33
  const propConfig = {
@@ -112,7 +57,7 @@ if (FAST.error === void 0) {
112
57
  Object.assign(FAST, {
113
58
  warn() { },
114
59
  error(code) {
115
- return new Error(`Code ${code}`);
60
+ return new Error(`Error ${code}`);
116
61
  },
117
62
  addMessages() { },
118
63
  });
@@ -143,10 +88,34 @@ function createTypeRegistry() {
143
88
  return typeToDefinition.get(key);
144
89
  },
145
90
  getForInstance(object) {
91
+ if (object === null || object === void 0) {
92
+ return void 0;
93
+ }
146
94
  return typeToDefinition.get(object.constructor);
147
95
  },
148
96
  });
149
97
  }
98
+ /**
99
+ * Creates a function capable of locating metadata associated with a type.
100
+ * @returns A metadata locator function.
101
+ * @internal
102
+ */
103
+ function createMetadataLocator() {
104
+ const metadataLookup = new WeakMap();
105
+ return function (target) {
106
+ let metadata = metadataLookup.get(target);
107
+ if (metadata === void 0) {
108
+ let currentTarget = Reflect.getPrototypeOf(target);
109
+ while (metadata === void 0 && currentTarget !== null) {
110
+ metadata = metadataLookup.get(currentTarget);
111
+ currentTarget = Reflect.getPrototypeOf(currentTarget);
112
+ }
113
+ metadata = metadata === void 0 ? [] : metadata.slice(0);
114
+ metadataLookup.set(target, metadata);
115
+ }
116
+ return metadata;
117
+ };
118
+ }
150
119
 
151
120
  /**
152
121
  * @internal
@@ -388,6 +357,21 @@ class PropertyChangeNotifier {
388
357
  }
389
358
  }
390
359
 
360
+ /**
361
+ * Describes how the source's lifetime relates to its controller's lifetime.
362
+ * @public
363
+ */
364
+ const SourceLifetime = Object.freeze({
365
+ /**
366
+ * The source to controller lifetime relationship is unknown.
367
+ */
368
+ unknown: void 0,
369
+ /**
370
+ * The source and controller lifetimes are coupled to one another.
371
+ * They can/will be GC'd together.
372
+ */
373
+ coupled: 1,
374
+ });
391
375
  /**
392
376
  * Common Observable APIs.
393
377
  * @public
@@ -396,7 +380,6 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
396
380
  const queueUpdate = Updates.enqueue;
397
381
  const volatileRegex = /(:|&&|\|\||if)/;
398
382
  const notifierLookup = new WeakMap();
399
- const accessorLookup = new WeakMap();
400
383
  let watcher = void 0;
401
384
  let createArrayObserver = (array) => {
402
385
  throw FAST.error(1101 /* Message.needsArrayObservation */);
@@ -411,19 +394,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
411
394
  }
412
395
  return found;
413
396
  }
414
- function getAccessors(target) {
415
- let accessors = accessorLookup.get(target);
416
- if (accessors === void 0) {
417
- let currentTarget = Reflect.getPrototypeOf(target);
418
- while (accessors === void 0 && currentTarget !== null) {
419
- accessors = accessorLookup.get(currentTarget);
420
- currentTarget = Reflect.getPrototypeOf(currentTarget);
421
- }
422
- accessors = accessors === void 0 ? [] : accessors.slice(0);
423
- accessorLookup.set(target, accessors);
424
- }
425
- return accessors;
426
- }
397
+ const getAccessors = createMetadataLocator();
427
398
  class DefaultObservableAccessor {
428
399
  constructor(name) {
429
400
  this.name = name;
@@ -449,10 +420,10 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
449
420
  }
450
421
  }
451
422
  }
452
- class BindingObserverImplementation extends SubscriberSet {
453
- constructor(binding, initialSubscriber, isVolatileBinding = false) {
454
- super(binding, initialSubscriber);
455
- this.binding = binding;
423
+ class ExpressionNotifierImplementation extends SubscriberSet {
424
+ constructor(expression, initialSubscriber, isVolatileBinding = false) {
425
+ super(expression, initialSubscriber);
426
+ this.expression = expression;
456
427
  this.isVolatileBinding = isVolatileBinding;
457
428
  this.needsRefresh = true;
458
429
  this.needsQueue = true;
@@ -467,6 +438,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
467
438
  setMode(isAsync) {
468
439
  this.isAsync = this.needsQueue = isAsync;
469
440
  }
441
+ bind(controller) {
442
+ this.controller = controller;
443
+ const value = this.observe(controller.source, controller.context);
444
+ if (!controller.isBound && this.requiresUnbind(controller)) {
445
+ controller.onUnbind(this);
446
+ }
447
+ return value;
448
+ }
449
+ requiresUnbind(controller) {
450
+ return (controller.sourceLifetime !== SourceLifetime.coupled ||
451
+ this.first !== this.last ||
452
+ this.first.propertySource !== controller.source);
453
+ }
454
+ unbind(controller) {
455
+ this.dispose();
456
+ }
470
457
  observe(source, context) {
471
458
  if (this.needsRefresh && this.last !== null) {
472
459
  this.dispose();
@@ -474,10 +461,19 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
474
461
  const previousWatcher = watcher;
475
462
  watcher = this.needsRefresh ? this : void 0;
476
463
  this.needsRefresh = this.isVolatileBinding;
477
- const result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
478
- watcher = previousWatcher;
464
+ let result;
465
+ try {
466
+ result = this.expression(source, context);
467
+ }
468
+ finally {
469
+ watcher = previousWatcher;
470
+ }
479
471
  return result;
480
472
  }
473
+ // backwards compat with v1 kernel
474
+ disconnect() {
475
+ this.dispose();
476
+ }
481
477
  dispose() {
482
478
  if (this.last !== null) {
483
479
  let current = this.first;
@@ -604,22 +600,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
604
600
  */
605
601
  getAccessors,
606
602
  /**
607
- * Creates a {@link BindingObserver} that can watch the
608
- * provided {@link Binding} for changes.
609
- * @param binding - The binding to observe.
603
+ * Creates a {@link ExpressionNotifier} that can watch the
604
+ * provided {@link Expression} for changes.
605
+ * @param expression - The binding to observe.
610
606
  * @param initialSubscriber - An initial subscriber to changes in the binding value.
611
607
  * @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
612
608
  */
613
- binding(binding, initialSubscriber, isVolatileBinding = this.isVolatileBinding(binding)) {
614
- return new BindingObserverImplementation(binding, initialSubscriber, isVolatileBinding);
609
+ binding(expression, initialSubscriber, isVolatileBinding = this.isVolatileBinding(expression)) {
610
+ return new ExpressionNotifierImplementation(expression, initialSubscriber, isVolatileBinding);
615
611
  },
616
612
  /**
617
613
  * Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
618
614
  * on every evaluation of the value.
619
- * @param binding - The binding to inspect.
615
+ * @param expression - The binding to inspect.
620
616
  */
621
- isVolatileBinding(binding) {
622
- return volatileRegex.test(binding.toString());
617
+ isVolatileBinding(expression) {
618
+ return volatileRegex.test(expression.toString());
623
619
  },
624
620
  });
625
621
  });
@@ -658,73 +654,40 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
658
654
  },
659
655
  };
660
656
  });
661
- class DefaultExecutionContext {
662
- constructor(parentSource = null, parentContext = null) {
663
- this.index = 0;
664
- this.length = 0;
665
- this.parent = parentSource;
666
- this.parentContext = parentContext;
667
- }
668
- get event() {
669
- return contextEvent.get();
670
- }
671
- get isEven() {
672
- return this.index % 2 === 0;
673
- }
674
- get isOdd() {
675
- return this.index % 2 !== 0;
676
- }
677
- get isFirst() {
678
- return this.index === 0;
679
- }
680
- get isInMiddle() {
681
- return !this.isFirst && !this.isLast;
682
- }
683
- get isLast() {
684
- return this.index === this.length - 1;
685
- }
686
- eventDetail() {
687
- return this.event.detail;
688
- }
689
- eventTarget() {
690
- return this.event.target;
691
- }
692
- updatePosition(index, length) {
693
- this.index = index;
694
- this.length = length;
695
- }
696
- createChildContext(parentSource) {
697
- return new DefaultExecutionContext(parentSource, this);
698
- }
699
- createItemContext(index, length) {
700
- const childContext = Object.create(this);
701
- childContext.index = index;
702
- childContext.length = length;
703
- return childContext;
704
- }
705
- }
706
- Observable.defineProperty(DefaultExecutionContext.prototype, "index");
707
- Observable.defineProperty(DefaultExecutionContext.prototype, "length");
708
657
  /**
709
- * The common execution context APIs.
658
+ * Provides additional contextual information available to behaviors and expressions.
710
659
  * @public
711
660
  */
712
661
  const ExecutionContext = Object.freeze({
713
- default: new DefaultExecutionContext(),
714
662
  /**
715
- * Sets the event for the current execution context.
716
- * @param event - The event to set.
717
- * @internal
663
+ * A default execution context.
718
664
  */
719
- setEvent(event) {
720
- contextEvent.set(event);
665
+ default: {
666
+ index: 0,
667
+ length: 0,
668
+ get event() {
669
+ return ExecutionContext.getEvent();
670
+ },
671
+ eventDetail() {
672
+ return this.event.detail;
673
+ },
674
+ eventTarget() {
675
+ return this.event.target;
676
+ },
677
+ },
678
+ /**
679
+ * Gets the current event.
680
+ * @returns An event object.
681
+ */
682
+ getEvent() {
683
+ return contextEvent.get();
721
684
  },
722
685
  /**
723
- * Creates a new root execution context.
724
- * @returns A new execution context.
686
+ * Sets the current event.
687
+ * @param event - An event object.
725
688
  */
726
- create() {
727
- return new DefaultExecutionContext();
689
+ setEvent(event) {
690
+ contextEvent.set(event);
728
691
  },
729
692
  });
730
693
 
@@ -793,10 +756,311 @@ const SpliceStrategySupport = Object.freeze({
793
756
  const reset = new Splice(0, emptyArray, 0);
794
757
  reset.reset = true;
795
758
  const resetSplices = [reset];
759
+ // Note: This function is *based* on the computation of the Levenshtein
760
+ // "edit" distance. The one change is that "updates" are treated as two
761
+ // edits - not one. With Array splices, an update is really a delete
762
+ // followed by an add. By retaining this, we optimize for "keeping" the
763
+ // maximum array items in the original array. For example:
764
+ //
765
+ // 'xxxx123' to '123yyyy'
766
+ //
767
+ // With 1-edit updates, the shortest path would be just to update all seven
768
+ // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
769
+ // leaves the substring '123' intact.
770
+ function calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd) {
771
+ // "Deletion" columns
772
+ const rowCount = oldEnd - oldStart + 1;
773
+ const columnCount = currentEnd - currentStart + 1;
774
+ const distances = new Array(rowCount);
775
+ let north;
776
+ let west;
777
+ // "Addition" rows. Initialize null column.
778
+ for (let i = 0; i < rowCount; ++i) {
779
+ distances[i] = new Array(columnCount);
780
+ distances[i][0] = i;
781
+ }
782
+ // Initialize null row
783
+ for (let j = 0; j < columnCount; ++j) {
784
+ distances[0][j] = j;
785
+ }
786
+ for (let i = 1; i < rowCount; ++i) {
787
+ for (let j = 1; j < columnCount; ++j) {
788
+ if (current[currentStart + j - 1] === old[oldStart + i - 1]) {
789
+ distances[i][j] = distances[i - 1][j - 1];
790
+ }
791
+ else {
792
+ north = distances[i - 1][j] + 1;
793
+ west = distances[i][j - 1] + 1;
794
+ distances[i][j] = north < west ? north : west;
795
+ }
796
+ }
797
+ }
798
+ return distances;
799
+ }
800
+ // This starts at the final weight, and walks "backward" by finding
801
+ // the minimum previous weight recursively until the origin of the weight
802
+ // matrix.
803
+ function spliceOperationsFromEditDistances(distances) {
804
+ let i = distances.length - 1;
805
+ let j = distances[0].length - 1;
806
+ let current = distances[i][j];
807
+ const edits = [];
808
+ while (i > 0 || j > 0) {
809
+ if (i === 0) {
810
+ edits.push(2 /* Edit.add */);
811
+ j--;
812
+ continue;
813
+ }
814
+ if (j === 0) {
815
+ edits.push(3 /* Edit.delete */);
816
+ i--;
817
+ continue;
818
+ }
819
+ const northWest = distances[i - 1][j - 1];
820
+ const west = distances[i - 1][j];
821
+ const north = distances[i][j - 1];
822
+ let min;
823
+ if (west < north) {
824
+ min = west < northWest ? west : northWest;
825
+ }
826
+ else {
827
+ min = north < northWest ? north : northWest;
828
+ }
829
+ if (min === northWest) {
830
+ if (northWest === current) {
831
+ edits.push(0 /* Edit.leave */);
832
+ }
833
+ else {
834
+ edits.push(1 /* Edit.update */);
835
+ current = northWest;
836
+ }
837
+ i--;
838
+ j--;
839
+ }
840
+ else if (min === west) {
841
+ edits.push(3 /* Edit.delete */);
842
+ i--;
843
+ current = west;
844
+ }
845
+ else {
846
+ edits.push(2 /* Edit.add */);
847
+ j--;
848
+ current = north;
849
+ }
850
+ }
851
+ return edits.reverse();
852
+ }
853
+ function sharedPrefix(current, old, searchLength) {
854
+ for (let i = 0; i < searchLength; ++i) {
855
+ if (current[i] !== old[i]) {
856
+ return i;
857
+ }
858
+ }
859
+ return searchLength;
860
+ }
861
+ function sharedSuffix(current, old, searchLength) {
862
+ let index1 = current.length;
863
+ let index2 = old.length;
864
+ let count = 0;
865
+ while (count < searchLength && current[--index1] === old[--index2]) {
866
+ count++;
867
+ }
868
+ return count;
869
+ }
870
+ function intersect(start1, end1, start2, end2) {
871
+ // Disjoint
872
+ if (end1 < start2 || end2 < start1) {
873
+ return -1;
874
+ }
875
+ // Adjacent
876
+ if (end1 === start2 || end2 === start1) {
877
+ return 0;
878
+ }
879
+ // Non-zero intersect, span1 first
880
+ if (start1 < start2) {
881
+ if (end1 < end2) {
882
+ return end1 - start2; // Overlap
883
+ }
884
+ return end2 - start2; // Contained
885
+ }
886
+ // Non-zero intersect, span2 first
887
+ if (end2 < end1) {
888
+ return end2 - start1; // Overlap
889
+ }
890
+ return end1 - start1; // Contained
891
+ }
892
+ /**
893
+ * @remarks
894
+ * Lacking individual splice mutation information, the minimal set of
895
+ * splices can be synthesized given the previous state and final state of an
896
+ * array. The basic approach is to calculate the edit distance matrix and
897
+ * choose the shortest path through it.
898
+ *
899
+ * Complexity: O(l * p)
900
+ * l: The length of the current array
901
+ * p: The length of the old array
902
+ */
903
+ function calc(current, currentStart, currentEnd, old, oldStart, oldEnd) {
904
+ let prefixCount = 0;
905
+ let suffixCount = 0;
906
+ const minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
907
+ if (currentStart === 0 && oldStart === 0) {
908
+ prefixCount = sharedPrefix(current, old, minLength);
909
+ }
910
+ if (currentEnd === current.length && oldEnd === old.length) {
911
+ suffixCount = sharedSuffix(current, old, minLength - prefixCount);
912
+ }
913
+ currentStart += prefixCount;
914
+ oldStart += prefixCount;
915
+ currentEnd -= suffixCount;
916
+ oldEnd -= suffixCount;
917
+ if (currentEnd - currentStart === 0 && oldEnd - oldStart === 0) {
918
+ return emptyArray;
919
+ }
920
+ if (currentStart === currentEnd) {
921
+ const splice = new Splice(currentStart, [], 0);
922
+ while (oldStart < oldEnd) {
923
+ splice.removed.push(old[oldStart++]);
924
+ }
925
+ return [splice];
926
+ }
927
+ else if (oldStart === oldEnd) {
928
+ return [new Splice(currentStart, [], currentEnd - currentStart)];
929
+ }
930
+ const ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
931
+ const splices = [];
932
+ let splice = void 0;
933
+ let index = currentStart;
934
+ let oldIndex = oldStart;
935
+ for (let i = 0; i < ops.length; ++i) {
936
+ switch (ops[i]) {
937
+ case 0 /* Edit.leave */:
938
+ if (splice !== void 0) {
939
+ splices.push(splice);
940
+ splice = void 0;
941
+ }
942
+ index++;
943
+ oldIndex++;
944
+ break;
945
+ case 1 /* Edit.update */:
946
+ if (splice === void 0) {
947
+ splice = new Splice(index, [], 0);
948
+ }
949
+ splice.addedCount++;
950
+ index++;
951
+ splice.removed.push(old[oldIndex]);
952
+ oldIndex++;
953
+ break;
954
+ case 2 /* Edit.add */:
955
+ if (splice === void 0) {
956
+ splice = new Splice(index, [], 0);
957
+ }
958
+ splice.addedCount++;
959
+ index++;
960
+ break;
961
+ case 3 /* Edit.delete */:
962
+ if (splice === void 0) {
963
+ splice = new Splice(index, [], 0);
964
+ }
965
+ splice.removed.push(old[oldIndex]);
966
+ oldIndex++;
967
+ break;
968
+ // no default
969
+ }
970
+ }
971
+ if (splice !== void 0) {
972
+ splices.push(splice);
973
+ }
974
+ return splices;
975
+ }
976
+ function merge(splice, splices) {
977
+ let inserted = false;
978
+ let insertionOffset = 0;
979
+ for (let i = 0; i < splices.length; i++) {
980
+ const current = splices[i];
981
+ current.index += insertionOffset;
982
+ if (inserted) {
983
+ continue;
984
+ }
985
+ const intersectCount = intersect(splice.index, splice.index + splice.removed.length, current.index, current.index + current.addedCount);
986
+ if (intersectCount >= 0) {
987
+ // Merge the two splices
988
+ splices.splice(i, 1);
989
+ i--;
990
+ insertionOffset -= current.addedCount - current.removed.length;
991
+ splice.addedCount += current.addedCount - intersectCount;
992
+ const deleteCount = splice.removed.length + current.removed.length - intersectCount;
993
+ if (!splice.addedCount && !deleteCount) {
994
+ // merged splice is a noop. discard.
995
+ inserted = true;
996
+ }
997
+ else {
998
+ let currentRemoved = current.removed;
999
+ if (splice.index < current.index) {
1000
+ // some prefix of splice.removed is prepended to current.removed.
1001
+ const prepend = splice.removed.slice(0, current.index - splice.index);
1002
+ prepend.push(...currentRemoved);
1003
+ currentRemoved = prepend;
1004
+ }
1005
+ if (splice.index + splice.removed.length >
1006
+ current.index + current.addedCount) {
1007
+ // some suffix of splice.removed is appended to current.removed.
1008
+ const append = splice.removed.slice(current.index + current.addedCount - splice.index);
1009
+ currentRemoved.push(...append);
1010
+ }
1011
+ splice.removed = currentRemoved;
1012
+ if (current.index < splice.index) {
1013
+ splice.index = current.index;
1014
+ }
1015
+ }
1016
+ }
1017
+ else if (splice.index < current.index) {
1018
+ // Insert splice here.
1019
+ inserted = true;
1020
+ splices.splice(i, 0, splice);
1021
+ i++;
1022
+ const offset = splice.addedCount - splice.removed.length;
1023
+ current.index += offset;
1024
+ insertionOffset += offset;
1025
+ }
1026
+ }
1027
+ if (!inserted) {
1028
+ splices.push(splice);
1029
+ }
1030
+ }
1031
+ function project(array, changes) {
1032
+ let splices = [];
1033
+ const initialSplices = [];
1034
+ for (let i = 0, ii = changes.length; i < ii; i++) {
1035
+ merge(changes[i], initialSplices);
1036
+ }
1037
+ for (let i = 0, ii = initialSplices.length; i < ii; ++i) {
1038
+ const splice = initialSplices[i];
1039
+ if (splice.addedCount === 1 && splice.removed.length === 1) {
1040
+ if (splice.removed[0] !== array[splice.index]) {
1041
+ splices.push(splice);
1042
+ }
1043
+ continue;
1044
+ }
1045
+ splices = splices.concat(calc(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
1046
+ }
1047
+ return splices;
1048
+ }
1049
+ /**
1050
+ * A SpliceStrategy that attempts to merge all splices into the minimal set of
1051
+ * splices needed to represent the change from the old array to the new array.
1052
+ * @public
1053
+ */
796
1054
  let defaultSpliceStrategy = Object.freeze({
797
- support: SpliceStrategySupport.splice,
1055
+ support: SpliceStrategySupport.optimized,
798
1056
  normalize(previous, current, changes) {
799
- return previous === void 0 ? changes !== null && changes !== void 0 ? changes : emptyArray : resetSplices;
1057
+ if (previous === void 0) {
1058
+ if (changes === void 0) {
1059
+ return emptyArray;
1060
+ }
1061
+ return changes.length > 1 ? project(current, changes) : changes;
1062
+ }
1063
+ return resetSplices;
800
1064
  },
801
1065
  pop(array, observer, pop, args) {
802
1066
  const notEmpty = array.length > 0;
@@ -981,7 +1245,7 @@ const ArrayObserver = Object.freeze({
981
1245
  * @returns The length of the array.
982
1246
  * @public
983
1247
  */
984
- function length(array) {
1248
+ function lengthOf(array) {
985
1249
  if (!array) {
986
1250
  return 0;
987
1251
  }
@@ -994,7 +1258,6 @@ function length(array) {
994
1258
  return array.length;
995
1259
  }
996
1260
 
997
- const styleSheetCache = new Map();
998
1261
  let DefaultStyleStrategy;
999
1262
  function reduceStyles(styles) {
1000
1263
  return styles
@@ -1065,42 +1328,26 @@ class ElementStyles {
1065
1328
  static setDefaultStrategy(Strategy) {
1066
1329
  DefaultStyleStrategy = Strategy;
1067
1330
  }
1331
+ /**
1332
+ * Normalizes a set of composable style options.
1333
+ * @param styles - The style options to normalize.
1334
+ * @returns A singular ElementStyles instance or undefined.
1335
+ */
1336
+ static normalize(styles) {
1337
+ return styles === void 0
1338
+ ? void 0
1339
+ : Array.isArray(styles)
1340
+ ? new ElementStyles(styles)
1341
+ : styles instanceof ElementStyles
1342
+ ? styles
1343
+ : new ElementStyles([styles]);
1344
+ }
1068
1345
  }
1069
1346
  /**
1070
1347
  * Indicates whether the DOM supports the adoptedStyleSheets feature.
1071
1348
  */
1072
1349
  ElementStyles.supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
1073
1350
  "replace" in CSSStyleSheet.prototype;
1074
- /**
1075
- * https://wicg.github.io/construct-stylesheets/
1076
- * https://developers.google.com/web/updates/2019/02/constructable-stylesheets
1077
- *
1078
- * @internal
1079
- */
1080
- class AdoptedStyleSheetsStrategy {
1081
- constructor(styles) {
1082
- this.sheets = styles.map((x) => {
1083
- if (x instanceof CSSStyleSheet) {
1084
- return x;
1085
- }
1086
- let sheet = styleSheetCache.get(x);
1087
- if (sheet === void 0) {
1088
- sheet = new CSSStyleSheet();
1089
- sheet.replaceSync(x);
1090
- styleSheetCache.set(x, sheet);
1091
- }
1092
- return sheet;
1093
- });
1094
- }
1095
- addStylesTo(target) {
1096
- target.adoptedStyleSheets = [...target.adoptedStyleSheets, ...this.sheets];
1097
- }
1098
- removeStylesFrom(target) {
1099
- const sheets = this.sheets;
1100
- target.adoptedStyleSheets = target.adoptedStyleSheets.filter((x) => sheets.indexOf(x) === -1);
1101
- }
1102
- }
1103
- ElementStyles.setDefaultStrategy(FAST.getById(5 /* KernelServiceId.styleSheetStrategy */, () => AdoptedStyleSheetsStrategy));
1104
1351
 
1105
1352
  const registry$1 = createTypeRegistry();
1106
1353
  /**
@@ -1209,11 +1456,11 @@ class CSSPartial {
1209
1456
  }
1210
1457
  return this.css;
1211
1458
  }
1212
- bind(el) {
1213
- el.$fastController.addStyles(this.styles);
1459
+ addedCallback(controller) {
1460
+ controller.addStyles(this.styles);
1214
1461
  }
1215
- unbind(el) {
1216
- el.$fastController.removeStyles(this.styles);
1462
+ removedCallback(controller) {
1463
+ controller.removeStyles(this.styles);
1217
1464
  }
1218
1465
  }
1219
1466
  CSSDirective.define(CSSPartial);
@@ -1280,9 +1527,9 @@ const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
1280
1527
  const interpolationStart = `${marker}{`;
1281
1528
  const interpolationEnd = `}${marker}`;
1282
1529
  const interpolationEndLength = interpolationEnd.length;
1283
- let id = 0;
1530
+ let id$1 = 0;
1284
1531
  /** @internal */
1285
- const nextId = () => `${marker}-${++id}`;
1532
+ const nextId = () => `${marker}-${++id$1}`;
1286
1533
  /**
1287
1534
  * Common APIs related to markup generation.
1288
1535
  * @public
@@ -1352,6 +1599,67 @@ const Parser = Object.freeze({
1352
1599
  },
1353
1600
  });
1354
1601
 
1602
+ /**
1603
+ * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
1604
+ * control ViewBehaviors.
1605
+ * @public
1606
+ */
1607
+ const ViewBehaviorOrchestrator = Object.freeze({
1608
+ /**
1609
+ * Creates a ViewBehaviorOrchestrator.
1610
+ * @param source - The source to to associate behaviors with.
1611
+ * @returns A ViewBehaviorOrchestrator.
1612
+ */
1613
+ create(source) {
1614
+ const behaviors = [];
1615
+ const targets = {};
1616
+ let unbindables = null;
1617
+ let isConnected = false;
1618
+ return {
1619
+ source,
1620
+ context: ExecutionContext.default,
1621
+ targets,
1622
+ get isBound() {
1623
+ return isConnected;
1624
+ },
1625
+ addBehaviorFactory(factory, target) {
1626
+ const nodeId = factory.nodeId || (factory.nodeId = nextId());
1627
+ factory.id || (factory.id = nextId());
1628
+ this.addTarget(nodeId, target);
1629
+ this.addBehavior(factory.createBehavior());
1630
+ },
1631
+ addTarget(nodeId, target) {
1632
+ targets[nodeId] = target;
1633
+ },
1634
+ addBehavior(behavior) {
1635
+ behaviors.push(behavior);
1636
+ if (isConnected) {
1637
+ behavior.bind(this);
1638
+ }
1639
+ },
1640
+ onUnbind(unbindable) {
1641
+ if (unbindables === null) {
1642
+ unbindables = [];
1643
+ }
1644
+ unbindables.push(unbindable);
1645
+ },
1646
+ connectedCallback(controller) {
1647
+ if (!isConnected) {
1648
+ isConnected = true;
1649
+ behaviors.forEach(x => x.bind(this));
1650
+ }
1651
+ },
1652
+ disconnectedCallback(controller) {
1653
+ if (isConnected) {
1654
+ isConnected = false;
1655
+ if (unbindables !== null) {
1656
+ unbindables.forEach(x => x.unbind(this));
1657
+ }
1658
+ }
1659
+ },
1660
+ };
1661
+ },
1662
+ });
1355
1663
  const registry = createTypeRegistry();
1356
1664
  /**
1357
1665
  * Instructs the template engine to apply behavior to a node.
@@ -1391,6 +1699,22 @@ function htmlDirective(options) {
1391
1699
  HTMLDirective.define(type, options);
1392
1700
  };
1393
1701
  }
1702
+ /**
1703
+ * Captures a binding expression along with related information and capabilities.
1704
+ *
1705
+ * @public
1706
+ */
1707
+ class Binding {
1708
+ /**
1709
+ * Creates a binding.
1710
+ * @param evaluate - Evaluates the binding.
1711
+ * @param isVolatile - Indicates whether the binding is volatile.
1712
+ */
1713
+ constructor(evaluate, isVolatile = false) {
1714
+ this.evaluate = evaluate;
1715
+ this.isVolatile = isVolatile;
1716
+ }
1717
+ }
1394
1718
  /**
1395
1719
  * The type of HTML aspect to target.
1396
1720
  * @public
@@ -1428,26 +1752,22 @@ const Aspect = Object.freeze({
1428
1752
  *
1429
1753
  * @param directive - The directive to assign the aspect to.
1430
1754
  * @param value - The value to base the aspect determination on.
1755
+ * @remarks
1756
+ * If a falsy value is provided, then the content aspect will be assigned.
1431
1757
  */
1432
1758
  assign(directive, value) {
1433
- directive.sourceAspect = value;
1434
1759
  if (!value) {
1760
+ directive.aspectType = Aspect.content;
1435
1761
  return;
1436
1762
  }
1763
+ directive.sourceAspect = value;
1437
1764
  switch (value[0]) {
1438
1765
  case ":":
1439
1766
  directive.targetAspect = value.substring(1);
1440
- switch (directive.targetAspect) {
1441
- case "innerHTML":
1442
- directive.aspectType = Aspect.property;
1443
- break;
1444
- case "classList":
1445
- directive.aspectType = Aspect.tokenList;
1446
- break;
1447
- default:
1448
- directive.aspectType = Aspect.property;
1449
- break;
1450
- }
1767
+ directive.aspectType =
1768
+ directive.targetAspect === "classList"
1769
+ ? Aspect.tokenList
1770
+ : Aspect.property;
1451
1771
  break;
1452
1772
  case "?":
1453
1773
  directive.targetAspect = value.substring(1);
@@ -1458,14 +1778,8 @@ const Aspect = Object.freeze({
1458
1778
  directive.aspectType = Aspect.event;
1459
1779
  break;
1460
1780
  default:
1461
- if (value === "class") {
1462
- directive.targetAspect = "className";
1463
- directive.aspectType = Aspect.property;
1464
- }
1465
- else {
1466
- directive.targetAspect = value;
1467
- directive.aspectType = Aspect.attribute;
1468
- }
1781
+ directive.targetAspect = value;
1782
+ directive.aspectType = Aspect.attribute;
1469
1783
  break;
1470
1784
  }
1471
1785
  },
@@ -1481,13 +1795,10 @@ class StatelessAttachedAttributeDirective {
1481
1795
  */
1482
1796
  constructor(options) {
1483
1797
  this.options = options;
1484
- }
1485
- /**
1486
- * Creates a behavior.
1487
- * @param targets - The targets available for behaviors to be attached to.
1488
- */
1489
- createBehavior(targets) {
1490
- return this;
1798
+ /**
1799
+ * The unique id of the factory.
1800
+ */
1801
+ this.id = nextId();
1491
1802
  }
1492
1803
  /**
1493
1804
  * Creates a placeholder string based on the directive's index within the template.
@@ -1498,6 +1809,13 @@ class StatelessAttachedAttributeDirective {
1498
1809
  createHTML(add) {
1499
1810
  return Markup.attribute(add(this));
1500
1811
  }
1812
+ /**
1813
+ * Creates a behavior.
1814
+ * @param targets - The targets available for behaviors to be attached to.
1815
+ */
1816
+ createBehavior() {
1817
+ return this;
1818
+ }
1501
1819
  }
1502
1820
 
1503
1821
  const createInnerHTMLBinding = globalThis.TrustedHTML
@@ -1509,107 +1827,26 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1509
1827
  throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1510
1828
  }
1511
1829
  : (binding) => binding;
1512
- /**
1513
- * Describes how aspects of an HTML element will be affected by bindings.
1514
- * @public
1515
- */
1516
- const BindingMode = Object.freeze({
1517
- /**
1518
- * Creates a binding mode based on the supplied behavior types.
1519
- * @param UpdateType - The base behavior type used to update aspects.
1520
- * @param EventType - The base behavior type used to respond to events.
1521
- * @returns A new binding mode.
1522
- */
1523
- define(UpdateType, EventType = EventBinding) {
1524
- return Object.freeze({
1525
- [1]: d => new UpdateType(d, DOM.setAttribute),
1526
- [2]: d => new UpdateType(d, DOM.setBooleanAttribute),
1527
- [3]: d => new UpdateType(d, (t, a, v) => (t[a] = v)),
1528
- [4]: d => new (createContentBinding(UpdateType))(d, updateContentTarget),
1529
- [5]: d => new UpdateType(d, updateTokenListTarget),
1530
- [6]: d => new EventType(d),
1531
- });
1532
- },
1533
- });
1534
- /**
1535
- * Describes the configuration for a binding expression.
1536
- * @public
1537
- */
1538
- const BindingConfig = Object.freeze({
1539
- /**
1540
- * Creates a binding configuration based on the provided mode and options.
1541
- * @param mode - The mode to use for the configuration.
1542
- * @param defaultOptions - The default options to use for the configuration.
1543
- * @returns A new binding configuration.
1544
- */
1545
- define(mode, defaultOptions) {
1546
- const config = (options) => {
1547
- return {
1548
- mode: config.mode,
1549
- options: Object.assign({}, defaultOptions, options),
1550
- };
1551
- };
1552
- config.options = defaultOptions;
1553
- config.mode = mode;
1554
- return config;
1555
- },
1556
- });
1557
- /**
1558
- * A base binding behavior for DOM updates.
1559
- * @public
1560
- */
1561
- class UpdateBinding {
1562
- /**
1563
- * Creates an instance of UpdateBinding.
1564
- * @param directive - The directive that has the configuration for this behavior.
1565
- * @param updateTarget - The function used to update the target with the latest value.
1566
- */
1567
- constructor(directive, updateTarget) {
1568
- this.directive = directive;
1569
- this.updateTarget = updateTarget;
1830
+ class OnChangeBinding extends Binding {
1831
+ createObserver(_, subscriber) {
1832
+ return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1570
1833
  }
1571
- /**
1572
- * Bind this behavior to the source.
1573
- * @param source - The source to bind to.
1574
- * @param context - The execution context that the binding is operating within.
1575
- * @param targets - The targets that behaviors in a view can attach to.
1576
- */
1577
- bind(source, context, targets) { }
1578
- /**
1579
- * Unbinds this behavior from the source.
1580
- * @param source - The source to unbind from.
1581
- * @param context - The execution context that the binding is operating within.
1582
- * @param targets - The targets that behaviors in a view can attach to.
1583
- */
1584
- unbind(source, context, targets) { }
1585
- /**
1586
- * Creates a behavior.
1587
- * @param targets - The targets available for behaviors to be attached to.
1588
- */
1589
- createBehavior(targets) {
1834
+ }
1835
+ class OneTimeBinding extends Binding {
1836
+ createObserver() {
1590
1837
  return this;
1591
1838
  }
1839
+ bind(controller) {
1840
+ return this.evaluate(controller.source, controller.context);
1841
+ }
1592
1842
  }
1593
- function createContentBinding(Type) {
1594
- return class extends Type {
1595
- unbind(source, context, targets) {
1596
- super.unbind(source, context, targets);
1597
- const target = targets[this.directive.nodeId];
1598
- const view = target.$fastView;
1599
- if (view !== void 0 && view.isComposed) {
1600
- view.unbind();
1601
- view.needsBindOnly = true;
1602
- }
1603
- }
1604
- };
1605
- }
1606
- function updateContentTarget(target, aspect, value, source, context) {
1843
+ function updateContent(target, aspect, value, controller) {
1607
1844
  // If there's no actual value, then this equates to the
1608
1845
  // empty string for the purposes of content bindings.
1609
1846
  if (value === null || value === undefined) {
1610
1847
  value = "";
1611
1848
  }
1612
- // If the value has a "create" method, then it's a template-like.
1849
+ // If the value has a "create" method, then it's a ContentTemplate.
1613
1850
  if (value.create) {
1614
1851
  target.textContent = "";
1615
1852
  let view = target.$fastView;
@@ -1635,14 +1872,14 @@ function updateContentTarget(target, aspect, value, source, context) {
1635
1872
  // and that there's actually no need to compose it.
1636
1873
  if (!view.isComposed) {
1637
1874
  view.isComposed = true;
1638
- view.bind(source, context);
1875
+ view.bind(controller.source, controller.context);
1639
1876
  view.insertBefore(target);
1640
1877
  target.$fastView = view;
1641
1878
  target.$fastTemplate = value;
1642
1879
  }
1643
1880
  else if (view.needsBindOnly) {
1644
1881
  view.needsBindOnly = false;
1645
- view.bind(source, context);
1882
+ view.bind(controller.source, controller.context);
1646
1883
  }
1647
1884
  }
1648
1885
  else {
@@ -1662,10 +1899,9 @@ function updateContentTarget(target, aspect, value, source, context) {
1662
1899
  target.textContent = value;
1663
1900
  }
1664
1901
  }
1665
- function updateTokenListTarget(target, aspect, value) {
1902
+ function updateTokenList(target, aspect, value) {
1666
1903
  var _a;
1667
- const directive = this.directive;
1668
- const lookup = `${directive.id}-t`;
1904
+ const lookup = `${this.id}-t`;
1669
1905
  const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { c: 0, v: Object.create(null) });
1670
1906
  const versions = state.v;
1671
1907
  let currentVersion = state.c;
@@ -1695,366 +1931,168 @@ function updateTokenListTarget(target, aspect, value) {
1695
1931
  }
1696
1932
  }
1697
1933
  }
1934
+ const setProperty = (t, a, v) => (t[a] = v);
1935
+ const eventTarget = () => void 0;
1698
1936
  /**
1699
- * A binding behavior for one-time bindings.
1937
+ * A directive that applies bindings.
1700
1938
  * @public
1701
1939
  */
1702
- class OneTimeBinding extends UpdateBinding {
1940
+ class HTMLBindingDirective {
1703
1941
  /**
1704
- * Bind this behavior to the source.
1705
- * @param source - The source to bind to.
1706
- * @param context - The execution context that the binding is operating within.
1707
- * @param targets - The targets that behaviors in a view can attach to.
1942
+ * Creates an instance of HTMLBindingDirective.
1943
+ * @param dataBinding - The binding configuration to apply.
1708
1944
  */
1709
- bind(source, context, targets) {
1710
- const directive = this.directive;
1711
- this.updateTarget(targets[directive.nodeId], directive.targetAspect, directive.binding(source, context), source, context);
1712
- }
1713
- }
1714
- const signals = Object.create(null);
1715
- /**
1716
- * A binding behavior for signal bindings.
1717
- * @public
1718
- */
1719
- class SignalBinding extends UpdateBinding {
1720
- constructor() {
1721
- super(...arguments);
1722
- this.handlerProperty = `${this.directive.id}-h`;
1945
+ constructor(dataBinding) {
1946
+ this.dataBinding = dataBinding;
1947
+ this.updateTarget = null;
1948
+ /**
1949
+ * The unique id of the factory.
1950
+ */
1951
+ this.id = nextId();
1952
+ /**
1953
+ * The type of aspect to target.
1954
+ */
1955
+ this.aspectType = Aspect.content;
1956
+ /** @internal */
1957
+ this.bind = this.bindDefault;
1958
+ this.data = `${this.id}-d`;
1723
1959
  }
1724
1960
  /**
1725
- * Bind this behavior to the source.
1726
- * @param source - The source to bind to.
1727
- * @param context - The execution context that the binding is operating within.
1728
- * @param targets - The targets that behaviors in a view can attach to.
1961
+ * Creates HTML to be used within a template.
1962
+ * @param add - Can be used to add behavior factories to a template.
1729
1963
  */
1730
- bind(source, context, targets) {
1731
- const directive = this.directive;
1732
- const target = targets[directive.nodeId];
1733
- const signal = this.getSignal(source, context);
1734
- const handler = (target[this.handlerProperty] = () => {
1735
- this.updateTarget(target, directive.targetAspect, directive.binding(source, context), source, context);
1736
- });
1737
- handler();
1738
- const found = signals[signal];
1739
- if (found) {
1740
- Array.isArray(found)
1741
- ? found.push(handler)
1742
- : (signals[signal] = [found, handler]);
1743
- }
1744
- else {
1745
- signals[signal] = handler;
1746
- }
1964
+ createHTML(add) {
1965
+ return Markup.interpolation(add(this));
1747
1966
  }
1748
1967
  /**
1749
- * Unbinds this behavior from the source.
1750
- * @param source - The source to unbind from.
1751
- * @param context - The execution context that the binding is operating within.
1752
- * @param targets - The targets that behaviors in a view can attach to.
1968
+ * Creates a behavior.
1753
1969
  */
1754
- unbind(source, context, targets) {
1755
- const signal = this.getSignal(source, context);
1756
- const found = signals[signal];
1757
- if (found && Array.isArray(found)) {
1758
- const directive = this.directive;
1759
- const target = targets[directive.nodeId];
1760
- const handler = target[this.handlerProperty];
1761
- const index = found.indexOf(handler);
1762
- if (index !== -1) {
1763
- found.splice(index, 1);
1970
+ createBehavior() {
1971
+ if (this.updateTarget === null) {
1972
+ if (this.targetAspect === "innerHTML") {
1973
+ this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
1974
+ }
1975
+ switch (this.aspectType) {
1976
+ case 1:
1977
+ this.updateTarget = DOM.setAttribute;
1978
+ break;
1979
+ case 2:
1980
+ this.updateTarget = DOM.setBooleanAttribute;
1981
+ break;
1982
+ case 3:
1983
+ this.updateTarget = setProperty;
1984
+ break;
1985
+ case 4:
1986
+ this.bind = this.bindContent;
1987
+ this.updateTarget = updateContent;
1988
+ break;
1989
+ case 5:
1990
+ this.updateTarget = updateTokenList;
1991
+ break;
1992
+ case 6:
1993
+ this.bind = this.bindEvent;
1994
+ this.updateTarget = eventTarget;
1995
+ break;
1996
+ default:
1997
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
1764
1998
  }
1765
1999
  }
1766
- else {
1767
- signals[signal] = void 0;
1768
- }
1769
- }
1770
- getSignal(source, context) {
1771
- const options = this.directive.options;
1772
- return isString(options) ? options : options(source, context);
1773
- }
1774
- /**
1775
- * Sends the specified signal to signaled bindings.
1776
- * @param signal - The signal to send.
1777
- * @public
1778
- */
1779
- static send(signal) {
1780
- const found = signals[signal];
1781
- if (found) {
1782
- Array.isArray(found) ? found.forEach(x => x()) : found();
1783
- }
1784
- }
1785
- }
1786
- /**
1787
- * A binding behavior for bindings that change.
1788
- * @public
1789
- */
1790
- class ChangeBinding extends UpdateBinding {
1791
- /**
1792
- * Creates an instance of ChangeBinding.
1793
- * @param directive - The directive that has the configuration for this behavior.
1794
- * @param updateTarget - The function used to update the target with the latest value.
1795
- */
1796
- constructor(directive, updateTarget) {
1797
- super(directive, updateTarget);
1798
- this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
1799
- this.observerProperty = `${directive.id}-o`;
2000
+ return this;
1800
2001
  }
1801
- /**
1802
- * Returns the binding observer used to update the node.
1803
- * @param target - The target node.
1804
- * @returns A BindingObserver.
1805
- */
1806
- getObserver(target) {
2002
+ /** @internal */
2003
+ bindDefault(controller) {
1807
2004
  var _a;
1808
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = Observable.binding(this.directive.binding, this, this.isBindingVolatile)));
1809
- }
1810
- /**
1811
- * Bind this behavior to the source.
1812
- * @param source - The source to bind to.
1813
- * @param context - The execution context that the binding is operating within.
1814
- * @param targets - The targets that behaviors in a view can attach to.
1815
- */
1816
- bind(source, context, targets) {
1817
- const directive = this.directive;
1818
- const target = targets[directive.nodeId];
1819
- const observer = this.getObserver(target);
2005
+ const target = controller.targets[this.nodeId];
2006
+ const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
1820
2007
  observer.target = target;
1821
- observer.source = source;
1822
- observer.context = context;
1823
- this.updateTarget(target, directive.targetAspect, observer.observe(source, context), source, context);
1824
- }
1825
- /**
1826
- * Unbinds this behavior from the source.
1827
- * @param source - The source to unbind from.
1828
- * @param context - The execution context that the binding is operating within.
1829
- * @param targets - The targets that behaviors in a view can attach to.
1830
- */
1831
- unbind(source, context, targets) {
1832
- const target = targets[this.directive.nodeId];
1833
- const observer = this.getObserver(target);
1834
- observer.dispose();
1835
- observer.target = null;
1836
- observer.source = null;
1837
- observer.context = null;
2008
+ observer.controller = controller;
2009
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2010
+ if (this.updateTarget === updateContent) {
2011
+ controller.onUnbind(this);
2012
+ }
1838
2013
  }
1839
2014
  /** @internal */
1840
- handleChange(binding, observer) {
1841
- const target = observer.target;
1842
- const source = observer.source;
1843
- const context = observer.context;
1844
- this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
2015
+ bindContent(controller) {
2016
+ this.bindDefault(controller);
2017
+ controller.onUnbind(this);
1845
2018
  }
1846
- }
1847
- /**
1848
- * A binding behavior for handling events.
1849
- * @public
1850
- */
1851
- class EventBinding {
1852
- /**
1853
- * Creates an instance of EventBinding.
1854
- * @param directive - The directive that has the configuration for this behavior.
1855
- */
1856
- constructor(directive) {
1857
- this.directive = directive;
1858
- this.sourceProperty = `${directive.id}-s`;
1859
- this.contextProperty = `${directive.id}-c`;
1860
- }
1861
- /**
1862
- * Bind this behavior to the source.
1863
- * @param source - The source to bind to.
1864
- * @param context - The execution context that the binding is operating within.
1865
- * @param targets - The targets that behaviors in a view can attach to.
1866
- */
1867
- bind(source, context, targets) {
1868
- const directive = this.directive;
1869
- const target = targets[directive.nodeId];
1870
- target[this.sourceProperty] = source;
1871
- target[this.contextProperty] = context;
1872
- target.addEventListener(directive.targetAspect, this, directive.options);
1873
- }
1874
- /**
1875
- * Unbinds this behavior from the source.
1876
- * @param source - The source to unbind from.
1877
- * @param context - The execution context that the binding is operating within.
1878
- * @param targets - The targets that behaviors in a view can attach to.
1879
- */
1880
- unbind(source, context, targets) {
1881
- const directive = this.directive;
1882
- const target = targets[directive.nodeId];
1883
- target[this.sourceProperty] = target[this.contextProperty] = null;
1884
- target.removeEventListener(directive.targetAspect, this, directive.options);
2019
+ /** @internal */
2020
+ bindEvent(controller) {
2021
+ const target = controller.targets[this.nodeId];
2022
+ target[this.data] = controller;
2023
+ target.addEventListener(this.targetAspect, this, this.dataBinding.options);
1885
2024
  }
1886
- /**
1887
- * Creates a behavior.
1888
- * @param targets - The targets available for behaviors to be attached to.
1889
- */
1890
- createBehavior(targets) {
1891
- return this;
2025
+ /** @internal */
2026
+ unbind(controller) {
2027
+ const target = controller.targets[this.nodeId];
2028
+ const view = target.$fastView;
2029
+ if (view !== void 0 && view.isComposed) {
2030
+ view.unbind();
2031
+ view.needsBindOnly = true;
2032
+ }
1892
2033
  }
1893
- /**
1894
- * @internal
1895
- */
2034
+ /** @internal */
1896
2035
  handleEvent(event) {
1897
2036
  const target = event.currentTarget;
1898
2037
  ExecutionContext.setEvent(event);
1899
- const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
2038
+ const controller = target[this.data];
2039
+ const result = this.dataBinding.evaluate(controller.source, controller.context);
1900
2040
  ExecutionContext.setEvent(null);
1901
2041
  if (result !== true) {
1902
2042
  event.preventDefault();
1903
2043
  }
1904
2044
  }
1905
- }
1906
- let twoWaySettings = {
1907
- determineChangeEvent() {
1908
- return "change";
1909
- },
1910
- };
1911
- /**
1912
- * A binding behavior for bindings that update in two directions.
1913
- * @public
1914
- */
1915
- class TwoWayBinding extends ChangeBinding {
1916
- /**
1917
- * Bind this behavior to the source.
1918
- * @param source - The source to bind to.
1919
- * @param context - The execution context that the binding is operating within.
1920
- * @param targets - The targets that behaviors in a view can attach to.
1921
- */
1922
- bind(source, context, targets) {
1923
- var _a;
1924
- super.bind(source, context, targets);
1925
- const directive = this.directive;
1926
- const target = targets[directive.nodeId];
1927
- if (!this.changeEvent) {
1928
- this.changeEvent =
1929
- (_a = directive.options.changeEvent) !== null && _a !== void 0 ? _a : twoWaySettings.determineChangeEvent(directive, target);
1930
- }
1931
- target.addEventListener(this.changeEvent, this);
1932
- }
1933
- /**
1934
- * Unbinds this behavior from the source.
1935
- * @param source - The source to unbind from.
1936
- * @param context - The execution context that the binding is operating within.
1937
- * @param targets - The targets that behaviors in a view can attach to.
1938
- */
1939
- unbind(source, context, targets) {
1940
- super.unbind(source, context, targets);
1941
- targets[this.directive.nodeId].removeEventListener(this.changeEvent, this);
1942
- }
1943
2045
  /** @internal */
1944
- handleEvent(event) {
1945
- const directive = this.directive;
1946
- const target = event.currentTarget;
1947
- let value;
1948
- switch (directive.aspectType) {
1949
- case 1:
1950
- value = target.getAttribute(directive.targetAspect);
1951
- break;
1952
- case 2:
1953
- value = target.hasAttribute(directive.targetAspect);
1954
- break;
1955
- case 4:
1956
- value = target.innerText;
1957
- break;
1958
- default:
1959
- value = target[directive.targetAspect];
1960
- break;
1961
- }
1962
- const observer = this.getObserver(target);
1963
- const last = observer.last; // using internal API!!!
1964
- last.propertySource[last.propertyName] = directive.options.fromView(value);
1965
- }
1966
- /**
1967
- * Configures two-way binding.
1968
- * @param settings - The settings to use for the two-way binding system.
1969
- */
1970
- static configure(settings) {
1971
- twoWaySettings = settings;
2046
+ handleChange(binding, observer) {
2047
+ const target = observer.target;
2048
+ const controller = observer.controller;
2049
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
1972
2050
  }
1973
2051
  }
2052
+ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
1974
2053
  /**
1975
- * The default onChange binding configuration.
1976
- * @public
1977
- */
1978
- const onChange = BindingConfig.define(BindingMode.define(ChangeBinding), {});
1979
- /**
1980
- * The default twoWay binding configuration.
1981
- * @public
1982
- */
1983
- const twoWay = BindingConfig.define(BindingMode.define(TwoWayBinding), {
1984
- fromView: v => v,
1985
- });
1986
- /**
1987
- * The default onTime binding configuration.
2054
+ * Creates an standard binding.
2055
+ * @param binding - The binding to refresh when changed.
2056
+ * @param isVolatile - Indicates whether the binding is volatile or not.
2057
+ * @returns A binding configuration.
1988
2058
  * @public
1989
2059
  */
1990
- const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
1991
- once: true,
1992
- });
1993
- const signalMode = BindingMode.define(SignalBinding);
2060
+ function bind(binding, isVolatile = Observable.isVolatileBinding(binding)) {
2061
+ return new OnChangeBinding(binding, isVolatile);
2062
+ }
1994
2063
  /**
1995
- * Creates a signal binding configuration with the supplied options.
1996
- * @param options - The signal name or a binding to use to retrieve the signal name.
2064
+ * Creates a one time binding
2065
+ * @param binding - The binding to refresh when signaled.
1997
2066
  * @returns A binding configuration.
1998
2067
  * @public
1999
2068
  */
2000
- const signal = (options) => {
2001
- return { mode: signalMode, options };
2002
- };
2069
+ function oneTime(binding) {
2070
+ return new OneTimeBinding(binding);
2071
+ }
2003
2072
  /**
2004
- * A directive that applies bindings.
2073
+ * Creates an event listener binding.
2074
+ * @param binding - The binding to invoke when the event is raised.
2075
+ * @param options - Event listener options.
2076
+ * @returns A binding configuration.
2005
2077
  * @public
2006
2078
  */
2007
- class HTMLBindingDirective {
2008
- /**
2009
- * Creates an instance of HTMLBindingDirective.
2010
- * @param binding - The binding to apply.
2011
- * @param mode - The binding mode to use when applying the binding.
2012
- * @param options - The options to configure the binding with.
2013
- */
2014
- constructor(binding, mode, options) {
2015
- this.binding = binding;
2016
- this.mode = mode;
2017
- this.options = options;
2018
- this.factory = null;
2019
- /**
2020
- * The type of aspect to target.
2021
- */
2022
- this.aspectType = Aspect.content;
2023
- }
2024
- /**
2025
- * Creates HTML to be used within a template.
2026
- * @param add - Can be used to add behavior factories to a template.
2027
- */
2028
- createHTML(add) {
2029
- return Markup.interpolation(add(this));
2030
- }
2031
- /**
2032
- * Creates a behavior.
2033
- * @param targets - The targets available for behaviors to be attached to.
2034
- */
2035
- createBehavior(targets) {
2036
- if (this.factory == null) {
2037
- if (this.targetAspect === "innerHTML") {
2038
- this.binding = createInnerHTMLBinding(this.binding);
2039
- }
2040
- this.factory = this.mode[this.aspectType](this);
2041
- }
2042
- return this.factory.createBehavior(targets);
2043
- }
2079
+ function listener(binding, options) {
2080
+ const config = new OnChangeBinding(binding, false);
2081
+ config.options = options;
2082
+ return config;
2044
2083
  }
2045
- HTMLDirective.define(HTMLBindingDirective, { aspected: true });
2046
2084
  /**
2047
- * Creates a binding directive with the specified configuration.
2048
- * @param binding - The binding expression.
2049
- * @param config - The binding configuration.
2050
- * @returns A binding directive.
2085
+ * Normalizes the input value into a binding.
2086
+ * @param value - The value to create the default binding for.
2087
+ * @returns A binding configuration for the provided value.
2051
2088
  * @public
2052
2089
  */
2053
- function bind(binding, config = onChange) {
2054
- if (!("mode" in config)) {
2055
- config = onChange(config);
2056
- }
2057
- return new HTMLBindingDirective(binding, config.mode, config.options);
2090
+ function normalizeBinding(value) {
2091
+ return isFunction(value)
2092
+ ? bind(value)
2093
+ : value instanceof Binding
2094
+ ? value
2095
+ : oneTime(() => value);
2058
2096
  }
2059
2097
 
2060
2098
  function removeNodeSequence(firstNode, lastNode) {
@@ -2078,21 +2116,91 @@ class HTMLView {
2078
2116
  * @param fragment - The html fragment that contains the nodes for this view.
2079
2117
  * @param behaviors - The behaviors to be applied to this view.
2080
2118
  */
2081
- constructor(fragment, factories, targets) {
2082
- this.fragment = fragment;
2083
- this.factories = factories;
2084
- this.targets = targets;
2085
- this.behaviors = null;
2086
- /**
2087
- * The data that the view is bound to.
2088
- */
2089
- this.source = null;
2090
- /**
2091
- * The execution context the view is running within.
2092
- */
2093
- this.context = null;
2094
- this.firstChild = fragment.firstChild;
2095
- this.lastChild = fragment.lastChild;
2119
+ constructor(fragment, factories, targets) {
2120
+ this.fragment = fragment;
2121
+ this.factories = factories;
2122
+ this.targets = targets;
2123
+ this.behaviors = null;
2124
+ this.unbindables = [];
2125
+ /**
2126
+ * The data that the view is bound to.
2127
+ */
2128
+ this.source = null;
2129
+ /**
2130
+ * Indicates whether the controller is bound.
2131
+ */
2132
+ this.isBound = false;
2133
+ /**
2134
+ * Indicates how the source's lifetime relates to the controller's lifetime.
2135
+ */
2136
+ this.sourceLifetime = SourceLifetime.unknown;
2137
+ /**
2138
+ * The execution context the view is running within.
2139
+ */
2140
+ this.context = this;
2141
+ /**
2142
+ * The index of the current item within a repeat context.
2143
+ */
2144
+ this.index = 0;
2145
+ /**
2146
+ * The length of the current collection within a repeat context.
2147
+ */
2148
+ this.length = 0;
2149
+ this.firstChild = fragment.firstChild;
2150
+ this.lastChild = fragment.lastChild;
2151
+ }
2152
+ /**
2153
+ * The current event within an event handler.
2154
+ */
2155
+ get event() {
2156
+ return ExecutionContext.getEvent();
2157
+ }
2158
+ /**
2159
+ * Indicates whether the current item within a repeat context
2160
+ * has an even index.
2161
+ */
2162
+ get isEven() {
2163
+ return this.index % 2 === 0;
2164
+ }
2165
+ /**
2166
+ * Indicates whether the current item within a repeat context
2167
+ * has an odd index.
2168
+ */
2169
+ get isOdd() {
2170
+ return this.index % 2 !== 0;
2171
+ }
2172
+ /**
2173
+ * Indicates whether the current item within a repeat context
2174
+ * is the first item in the collection.
2175
+ */
2176
+ get isFirst() {
2177
+ return this.index === 0;
2178
+ }
2179
+ /**
2180
+ * Indicates whether the current item within a repeat context
2181
+ * is somewhere in the middle of the collection.
2182
+ */
2183
+ get isInMiddle() {
2184
+ return !this.isFirst && !this.isLast;
2185
+ }
2186
+ /**
2187
+ * Indicates whether the current item within a repeat context
2188
+ * is the last item in the collection.
2189
+ */
2190
+ get isLast() {
2191
+ return this.index === this.length - 1;
2192
+ }
2193
+ /**
2194
+ * Returns the typed event detail of a custom event.
2195
+ */
2196
+ eventDetail() {
2197
+ return this.event.detail;
2198
+ }
2199
+ /**
2200
+ * Returns the typed event target of the event.
2201
+ */
2202
+ eventTarget() {
2203
+ return this.event.target;
2096
2204
  }
2097
2205
  /**
2098
2206
  * Appends the view's DOM nodes to the referenced node.
@@ -2110,8 +2218,10 @@ class HTMLView {
2110
2218
  node.parentNode.insertBefore(this.fragment, node);
2111
2219
  }
2112
2220
  else {
2113
- const parentNode = node.parentNode;
2114
2221
  const end = this.lastChild;
2222
+ if (node.previousSibling === end)
2223
+ return;
2224
+ const parentNode = node.parentNode;
2115
2225
  let current = this.firstChild;
2116
2226
  let next;
2117
2227
  while (current !== end) {
@@ -2146,58 +2256,61 @@ class HTMLView {
2146
2256
  removeNodeSequence(this.firstChild, this.lastChild);
2147
2257
  this.unbind();
2148
2258
  }
2259
+ onUnbind(behavior) {
2260
+ this.unbindables.push(behavior);
2261
+ }
2149
2262
  /**
2150
2263
  * Binds a view's behaviors to its binding source.
2151
2264
  * @param source - The binding source for the view's binding behaviors.
2152
2265
  * @param context - The execution context to run the behaviors within.
2153
2266
  */
2154
- bind(source, context) {
2155
- let behaviors = this.behaviors;
2156
- const oldSource = this.source;
2157
- if (oldSource === source) {
2267
+ bind(source, context = this) {
2268
+ if (this.source === source) {
2158
2269
  return;
2159
2270
  }
2160
- this.source = source;
2161
- this.context = context;
2162
- const targets = this.targets;
2163
- if (oldSource !== null) {
2164
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2165
- const current = behaviors[i];
2166
- current.unbind(oldSource, context, targets);
2167
- current.bind(source, context, targets);
2168
- }
2169
- }
2170
- else if (behaviors === null) {
2271
+ let behaviors = this.behaviors;
2272
+ if (behaviors === null) {
2273
+ this.source = source;
2274
+ this.context = context;
2171
2275
  this.behaviors = behaviors = new Array(this.factories.length);
2172
2276
  const factories = this.factories;
2173
2277
  for (let i = 0, ii = factories.length; i < ii; ++i) {
2174
- const behavior = factories[i].createBehavior(targets);
2175
- behavior.bind(source, context, targets);
2278
+ const behavior = factories[i].createBehavior();
2279
+ behavior.bind(this);
2176
2280
  behaviors[i] = behavior;
2177
2281
  }
2178
2282
  }
2179
2283
  else {
2284
+ if (this.source !== null) {
2285
+ this.evaluateUnbindables();
2286
+ }
2287
+ this.isBound = false;
2288
+ this.source = source;
2289
+ this.context = context;
2180
2290
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2181
- behaviors[i].bind(source, context, targets);
2291
+ behaviors[i].bind(this);
2182
2292
  }
2183
2293
  }
2294
+ this.isBound = true;
2184
2295
  }
2185
2296
  /**
2186
2297
  * Unbinds a view's behaviors from its binding source.
2187
2298
  */
2188
2299
  unbind() {
2189
- const oldSource = this.source;
2190
- if (oldSource === null) {
2300
+ if (!this.isBound || this.source === null) {
2191
2301
  return;
2192
2302
  }
2193
- const targets = this.targets;
2194
- const context = this.context;
2195
- const behaviors = this.behaviors;
2196
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2197
- behaviors[i].unbind(oldSource, context, targets);
2198
- }
2303
+ this.evaluateUnbindables();
2199
2304
  this.source = null;
2200
- this.context = null;
2305
+ this.context = this;
2306
+ this.isBound = false;
2307
+ }
2308
+ evaluateUnbindables() {
2309
+ const unbindables = this.unbindables;
2310
+ for (let i = 0, ii = unbindables.length; i < ii; ++i) {
2311
+ unbindables[i].unbind(this);
2312
+ }
2313
+ unbindables.length = 0;
2201
2314
  }
2202
2315
  /**
2203
2316
  * Efficiently disposes of a contiguous range of synthetic view instances.
@@ -2213,6 +2326,8 @@ class HTMLView {
2213
2326
  }
2214
2327
  }
2215
2328
  }
2329
+ Observable.defineProperty(HTMLView.prototype, "index");
2330
+ Observable.defineProperty(HTMLView.prototype, "length");
2216
2331
 
2217
2332
  const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
2218
2333
  const descriptorCache = {};
@@ -2221,6 +2336,22 @@ const next = {
2221
2336
  index: 0,
2222
2337
  node: null,
2223
2338
  };
2339
+ function tryWarn(name) {
2340
+ if (!name.startsWith("fast-")) {
2341
+ FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
2342
+ }
2343
+ }
2344
+ const warningHost = new Proxy(document.createElement("div"), {
2345
+ get(target, property) {
2346
+ tryWarn(property);
2347
+ const value = Reflect.get(target, property);
2348
+ return isFunction(value) ? value.bind(target) : value;
2349
+ },
2350
+ set(target, property, value) {
2351
+ tryWarn(property);
2352
+ return Reflect.set(target, property, value);
2353
+ },
2354
+ });
2224
2355
  class CompilationContext {
2225
2356
  constructor(fragment, directives) {
2226
2357
  this.fragment = fragment;
@@ -2271,7 +2402,7 @@ class CompilationContext {
2271
2402
  const fragment = this.fragment.cloneNode(true);
2272
2403
  const targets = Object.create(this.proto);
2273
2404
  targets.r = fragment;
2274
- targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : fragment;
2405
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
2275
2406
  for (const id of this.nodeIds) {
2276
2407
  targets[id]; // trigger locator
2277
2408
  }
@@ -2288,7 +2419,7 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2288
2419
  let result = null;
2289
2420
  if (parseResult === null) {
2290
2421
  if (includeBasicValues) {
2291
- result = bind(() => attrValue, oneTime);
2422
+ result = new HTMLBindingDirective(oneTime(() => attrValue));
2292
2423
  Aspect.assign(result, attr.name);
2293
2424
  }
2294
2425
  }
@@ -2325,6 +2456,7 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
2325
2456
  }
2326
2457
  else {
2327
2458
  currentNode.textContent = " ";
2459
+ Aspect.assign(currentPart);
2328
2460
  context.addFactory(currentPart, parentId, nodeId, nodeIndex);
2329
2461
  }
2330
2462
  lastNode = currentNode;
@@ -2457,22 +2589,28 @@ const Compiler = {
2457
2589
  return parts[0];
2458
2590
  }
2459
2591
  let sourceAspect;
2592
+ let binding;
2593
+ let isVolatile = false;
2460
2594
  const partCount = parts.length;
2461
2595
  const finalParts = parts.map((x) => {
2462
2596
  if (isString(x)) {
2463
2597
  return () => x;
2464
2598
  }
2465
2599
  sourceAspect = x.sourceAspect || sourceAspect;
2466
- return x.binding;
2600
+ binding = x.dataBinding || binding;
2601
+ isVolatile = isVolatile || x.dataBinding.isVolatile;
2602
+ return x.dataBinding.evaluate;
2467
2603
  });
2468
- const binding = (scope, context) => {
2604
+ const expression = (scope, context) => {
2469
2605
  let output = "";
2470
2606
  for (let i = 0; i < partCount; ++i) {
2471
2607
  output += finalParts[i](scope, context);
2472
2608
  }
2473
2609
  return output;
2474
2610
  };
2475
- const directive = bind(binding);
2611
+ binding.evaluate = expression;
2612
+ binding.isVolatile = isVolatile;
2613
+ const directive = new HTMLBindingDirective(binding);
2476
2614
  Aspect.assign(directive, sourceAspect);
2477
2615
  return directive;
2478
2616
  },
@@ -2510,9 +2648,9 @@ class ViewTemplate {
2510
2648
  * @param hostBindingTarget - An HTML element to target the host bindings at if different from the
2511
2649
  * host that the template is being attached to.
2512
2650
  */
2513
- render(source, host, hostBindingTarget, context) {
2514
- const view = this.create(hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : host);
2515
- view.bind(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
2651
+ render(source, host, hostBindingTarget) {
2652
+ const view = this.create(hostBindingTarget);
2653
+ view.bind(source);
2516
2654
  view.appendTo(host);
2517
2655
  return view;
2518
2656
  }
@@ -2552,12 +2690,12 @@ function html(strings, ...values) {
2552
2690
  let definition;
2553
2691
  html += currentString;
2554
2692
  if (isFunction(currentValue)) {
2555
- html += createAspectedHTML(bind(currentValue), currentString, add);
2693
+ html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2556
2694
  }
2557
2695
  else if (isString(currentValue)) {
2558
2696
  const match = lastAttributeNameRegex.exec(currentString);
2559
2697
  if (match !== null) {
2560
- const directive = bind(() => currentValue, oneTime);
2698
+ const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2561
2699
  Aspect.assign(directive, match[2]);
2562
2700
  html += directive.createHTML(add);
2563
2701
  }
@@ -2565,8 +2703,11 @@ function html(strings, ...values) {
2565
2703
  html += currentValue;
2566
2704
  }
2567
2705
  }
2706
+ else if (currentValue instanceof Binding) {
2707
+ html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2708
+ }
2568
2709
  else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2569
- html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
2710
+ html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2570
2711
  }
2571
2712
  else {
2572
2713
  if (definition.aspected) {
@@ -2579,26 +2720,6 @@ function html(strings, ...values) {
2579
2720
  }
2580
2721
  return new ViewTemplate(html + strings[strings.length - 1], factories);
2581
2722
  }
2582
- /**
2583
- * Transforms a template literal string into a ChildViewTemplate.
2584
- * @param strings - The string fragments that are interpolated with the values.
2585
- * @param values - The values that are interpolated with the string fragments.
2586
- * @remarks
2587
- * The html helper supports interpolation of strings, numbers, binding expressions,
2588
- * other template instances, and Directive instances.
2589
- * @public
2590
- */
2591
- const child = html;
2592
- /**
2593
- * Transforms a template literal string into an ItemViewTemplate.
2594
- * @param strings - The string fragments that are interpolated with the values.
2595
- * @param values - The values that are interpolated with the string fragments.
2596
- * @remarks
2597
- * The html helper supports interpolation of strings, numbers, binding expressions,
2598
- * other template instances, and Directive instances.
2599
- * @public
2600
- */
2601
- const item = html;
2602
2723
 
2603
2724
  /**
2604
2725
  * The runtime behavior for template references.
@@ -2606,20 +2727,12 @@ const item = html;
2606
2727
  */
2607
2728
  class RefDirective extends StatelessAttachedAttributeDirective {
2608
2729
  /**
2609
- * Bind this behavior to the source.
2610
- * @param source - The source to bind to.
2611
- * @param context - The execution context that the binding is operating within.
2612
- * @param targets - The targets that behaviors in a view can attach to.
2730
+ * Bind this behavior.
2731
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2613
2732
  */
2614
- bind(source, context, targets) {
2615
- source[this.options] = targets[this.nodeId];
2733
+ bind(controller) {
2734
+ controller.source[this.options] = controller.targets[this.nodeId];
2616
2735
  }
2617
- /**
2618
- * Unbinds this behavior from the source.
2619
- * @param source - The source to unbind from.
2620
- */
2621
- /* eslint-disable-next-line @typescript-eslint/no-empty-function */
2622
- unbind() { }
2623
2736
  }
2624
2737
  HTMLDirective.define(RefDirective);
2625
2738
  /**
@@ -2631,27 +2744,34 @@ const ref = (propertyName) => new RefDirective(propertyName);
2631
2744
 
2632
2745
  /**
2633
2746
  * A directive that enables basic conditional rendering in a template.
2634
- * @param binding - The condition to test for rendering.
2747
+ * @param condition - The condition to test for rendering.
2635
2748
  * @param templateOrTemplateBinding - The template or a binding that gets
2636
2749
  * the template to render when the condition is true.
2637
2750
  * @public
2638
2751
  */
2639
- function when(binding, templateOrTemplateBinding) {
2640
- const getTemplate = isFunction(templateOrTemplateBinding)
2752
+ function when(condition, templateOrTemplateBinding) {
2753
+ const dataBinding = isFunction(condition) ? condition : () => condition;
2754
+ const templateBinding = isFunction(templateOrTemplateBinding)
2641
2755
  ? templateOrTemplateBinding
2642
2756
  : () => templateOrTemplateBinding;
2643
- return (source, context) => binding(source, context) ? getTemplate(source, context) : null;
2757
+ return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
2644
2758
  }
2645
2759
 
2646
2760
  const defaultRepeatOptions = Object.freeze({
2647
2761
  positioning: false,
2648
2762
  recycle: true,
2649
2763
  });
2650
- function bindWithoutPositioning(view, items, index, context) {
2651
- view.bind(items[index], context);
2764
+ function bindWithoutPositioning(view, items, index, controller) {
2765
+ view.context.parent = controller.source;
2766
+ view.context.parentContext = controller.context;
2767
+ view.bind(items[index]);
2652
2768
  }
2653
- function bindWithPositioning(view, items, index, context) {
2654
- view.bind(items[index], context.createItemContext(index, items.length));
2769
+ function bindWithPositioning(view, items, index, controller) {
2770
+ view.context.parent = controller.source;
2771
+ view.context.parentContext = controller.context;
2772
+ view.context.length = items.length;
2773
+ view.context.index = index;
2774
+ view.bind(items[index]);
2655
2775
  }
2656
2776
  /**
2657
2777
  * A behavior that renders a template for each item in an array.
@@ -2661,57 +2781,45 @@ class RepeatBehavior {
2661
2781
  /**
2662
2782
  * Creates an instance of RepeatBehavior.
2663
2783
  * @param location - The location in the DOM to render the repeat.
2664
- * @param itemsBinding - The array to render.
2784
+ * @param dataBinding - The array to render.
2665
2785
  * @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
2666
2786
  * @param templateBinding - The template to render for each item.
2667
2787
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2668
2788
  * @param options - Options used to turn on special repeat features.
2669
2789
  */
2670
- constructor(location, itemsBinding, isItemsBindingVolatile, templateBinding, isTemplateBindingVolatile, options) {
2671
- this.location = location;
2672
- this.itemsBinding = itemsBinding;
2673
- this.templateBinding = templateBinding;
2674
- this.options = options;
2675
- this.source = null;
2790
+ constructor(directive) {
2791
+ this.directive = directive;
2676
2792
  this.views = [];
2677
2793
  this.items = null;
2678
2794
  this.itemsObserver = null;
2679
- this.context = void 0;
2680
- this.childContext = void 0;
2681
2795
  this.bindView = bindWithoutPositioning;
2682
- this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
2683
- this.templateBindingObserver = Observable.binding(templateBinding, this, isTemplateBindingVolatile);
2684
- if (options.positioning) {
2796
+ this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2797
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
2798
+ if (directive.options.positioning) {
2685
2799
  this.bindView = bindWithPositioning;
2686
2800
  }
2687
2801
  }
2688
2802
  /**
2689
- * Bind this behavior to the source.
2690
- * @param source - The source to bind to.
2691
- * @param context - The execution context that the binding is operating within.
2803
+ * Bind this behavior.
2804
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2692
2805
  */
2693
- bind(source, context) {
2694
- this.source = source;
2695
- this.context = context;
2696
- this.childContext = context.createChildContext(source);
2697
- this.items = this.itemsBindingObserver.observe(source, this.context);
2698
- this.template = this.templateBindingObserver.observe(source, this.context);
2806
+ bind(controller) {
2807
+ this.location = controller.targets[this.directive.nodeId];
2808
+ this.controller = controller;
2809
+ this.items = this.itemsBindingObserver.bind(controller);
2810
+ this.template = this.templateBindingObserver.bind(controller);
2699
2811
  this.observeItems(true);
2700
2812
  this.refreshAllViews();
2813
+ controller.onUnbind(this);
2701
2814
  }
2702
2815
  /**
2703
- * Unbinds this behavior from the source.
2704
- * @param source - The source to unbind from.
2816
+ * Unbinds this behavior.
2705
2817
  */
2706
2818
  unbind() {
2707
- this.source = null;
2708
- this.items = null;
2709
2819
  if (this.itemsObserver !== null) {
2710
2820
  this.itemsObserver.unsubscribe(this);
2711
2821
  }
2712
2822
  this.unbindAllViews();
2713
- this.itemsBindingObserver.dispose();
2714
- this.templateBindingObserver.dispose();
2715
2823
  }
2716
2824
  /**
2717
2825
  * Handles changes in the array, its items, and the repeat template.
@@ -2719,15 +2827,18 @@ class RepeatBehavior {
2719
2827
  * @param args - The details about what was changed.
2720
2828
  */
2721
2829
  handleChange(source, args) {
2722
- if (source === this.itemsBinding) {
2723
- this.items = this.itemsBindingObserver.observe(this.source, this.context);
2830
+ if (args === this.itemsBindingObserver) {
2831
+ this.items = this.itemsBindingObserver.bind(this.controller);
2724
2832
  this.observeItems();
2725
2833
  this.refreshAllViews();
2726
2834
  }
2727
- else if (source === this.templateBinding) {
2728
- this.template = this.templateBindingObserver.observe(this.source, this.context);
2835
+ else if (args === this.templateBindingObserver) {
2836
+ this.template = this.templateBindingObserver.bind(this.controller);
2729
2837
  this.refreshAllViews(true);
2730
2838
  }
2839
+ else if (!args[0]) {
2840
+ return;
2841
+ }
2731
2842
  else if (args[0].reset) {
2732
2843
  this.refreshAllViews();
2733
2844
  }
@@ -2752,39 +2863,57 @@ class RepeatBehavior {
2752
2863
  }
2753
2864
  updateViews(splices) {
2754
2865
  const views = this.views;
2755
- const childContext = this.childContext;
2756
- const totalRemoved = [];
2757
2866
  const bindView = this.bindView;
2758
- let removeDelta = 0;
2759
- for (let i = 0, ii = splices.length; i < ii; ++i) {
2760
- const splice = splices[i];
2761
- const removed = splice.removed;
2762
- totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
2763
- removeDelta -= splice.addedCount;
2764
- }
2765
2867
  const items = this.items;
2766
2868
  const template = this.template;
2869
+ const controller = this.controller;
2870
+ const recycle = this.directive.options.recycle;
2871
+ const leftoverViews = [];
2872
+ let leftoverIndex = 0;
2873
+ let availableViews = 0;
2767
2874
  for (let i = 0, ii = splices.length; i < ii; ++i) {
2768
2875
  const splice = splices[i];
2876
+ const removed = splice.removed;
2877
+ let removeIndex = 0;
2769
2878
  let addIndex = splice.index;
2770
2879
  const end = addIndex + splice.addedCount;
2880
+ const removedViews = views.splice(splice.index, removed.length);
2881
+ const totalAvailableViews = (availableViews =
2882
+ leftoverViews.length + removedViews.length);
2771
2883
  for (; addIndex < end; ++addIndex) {
2772
2884
  const neighbor = views[addIndex];
2773
2885
  const location = neighbor ? neighbor.firstChild : this.location;
2774
- const view = this.options.recycle && totalRemoved.length > 0
2775
- ? totalRemoved.shift()
2776
- : template.create();
2886
+ let view;
2887
+ if (recycle && availableViews > 0) {
2888
+ if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
2889
+ view = removedViews[removeIndex];
2890
+ removeIndex++;
2891
+ }
2892
+ else {
2893
+ view = leftoverViews[leftoverIndex];
2894
+ leftoverIndex++;
2895
+ }
2896
+ availableViews--;
2897
+ }
2898
+ else {
2899
+ view = template.create();
2900
+ }
2777
2901
  views.splice(addIndex, 0, view);
2778
- bindView(view, items, addIndex, childContext);
2902
+ bindView(view, items, addIndex, controller);
2779
2903
  view.insertBefore(location);
2780
2904
  }
2905
+ if (removedViews[removeIndex]) {
2906
+ leftoverViews.push(...removedViews.slice(removeIndex));
2907
+ }
2781
2908
  }
2782
- for (let i = 0, ii = totalRemoved.length; i < ii; ++i) {
2783
- totalRemoved[i].dispose();
2909
+ for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
2910
+ leftoverViews[i].dispose();
2784
2911
  }
2785
- if (this.options.positioning) {
2912
+ if (this.directive.options.positioning) {
2786
2913
  for (let i = 0, ii = views.length; i < ii; ++i) {
2787
- views[i].context.updatePosition(i, ii);
2914
+ const context = views[i].context;
2915
+ context.length = i;
2916
+ context.index = ii;
2788
2917
  }
2789
2918
  }
2790
2919
  }
@@ -2793,11 +2922,11 @@ class RepeatBehavior {
2793
2922
  const template = this.template;
2794
2923
  const location = this.location;
2795
2924
  const bindView = this.bindView;
2796
- const childContext = this.childContext;
2925
+ const controller = this.controller;
2797
2926
  let itemsLength = items.length;
2798
2927
  let views = this.views;
2799
2928
  let viewsLength = views.length;
2800
- if (itemsLength === 0 || templateChanged) {
2929
+ if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
2801
2930
  // all views need to be removed
2802
2931
  HTMLView.disposeContiguousBatch(views);
2803
2932
  viewsLength = 0;
@@ -2807,7 +2936,7 @@ class RepeatBehavior {
2807
2936
  this.views = views = new Array(itemsLength);
2808
2937
  for (let i = 0; i < itemsLength; ++i) {
2809
2938
  const view = template.create();
2810
- bindView(view, items, i, childContext);
2939
+ bindView(view, items, i, controller);
2811
2940
  views[i] = view;
2812
2941
  view.insertBefore(location);
2813
2942
  }
@@ -2818,11 +2947,11 @@ class RepeatBehavior {
2818
2947
  for (; i < itemsLength; ++i) {
2819
2948
  if (i < viewsLength) {
2820
2949
  const view = views[i];
2821
- bindView(view, items, i, childContext);
2950
+ bindView(view, items, i, controller);
2822
2951
  }
2823
2952
  else {
2824
2953
  const view = template.create();
2825
- bindView(view, items, i, childContext);
2954
+ bindView(view, items, i, controller);
2826
2955
  views.push(view);
2827
2956
  view.insertBefore(location);
2828
2957
  }
@@ -2847,17 +2976,19 @@ class RepeatBehavior {
2847
2976
  class RepeatDirective {
2848
2977
  /**
2849
2978
  * Creates an instance of RepeatDirective.
2850
- * @param itemsBinding - The binding that provides the array to render.
2979
+ * @param dataBinding - The binding that provides the array to render.
2851
2980
  * @param templateBinding - The template binding used to obtain a template to render for each item in the array.
2852
2981
  * @param options - Options used to turn on special repeat features.
2853
2982
  */
2854
- constructor(itemsBinding, templateBinding, options) {
2855
- this.itemsBinding = itemsBinding;
2983
+ constructor(dataBinding, templateBinding, options) {
2984
+ this.dataBinding = dataBinding;
2856
2985
  this.templateBinding = templateBinding;
2857
2986
  this.options = options;
2987
+ /**
2988
+ * The unique id of the factory.
2989
+ */
2990
+ this.id = nextId();
2858
2991
  ArrayObserver.enable();
2859
- this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
2860
- this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
2861
2992
  }
2862
2993
  /**
2863
2994
  * Creates a placeholder string based on the directive's index within the template.
@@ -2870,16 +3001,23 @@ class RepeatDirective {
2870
3001
  * Creates a behavior for the provided target node.
2871
3002
  * @param target - The node instance to create the behavior for.
2872
3003
  */
2873
- createBehavior(targets) {
2874
- return new RepeatBehavior(targets[this.nodeId], this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
3004
+ createBehavior() {
3005
+ return new RepeatBehavior(this);
2875
3006
  }
2876
3007
  }
2877
3008
  HTMLDirective.define(RepeatDirective);
2878
- function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
2879
- const templateBinding = isFunction(templateOrTemplateBinding)
2880
- ? templateOrTemplateBinding
2881
- : () => templateOrTemplateBinding;
2882
- return new RepeatDirective(itemsBinding, templateBinding, options);
3009
+ /**
3010
+ * A directive that enables list rendering.
3011
+ * @param items - The array to render.
3012
+ * @param template - The template or a template binding used obtain a template
3013
+ * to render for each item in the array.
3014
+ * @param options - Options used to turn on special repeat features.
3015
+ * @public
3016
+ */
3017
+ function repeat(items, template, options = defaultRepeatOptions) {
3018
+ const dataBinding = normalizeBinding(items);
3019
+ const templateBinding = normalizeBinding(template);
3020
+ return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
2883
3021
  }
2884
3022
 
2885
3023
  const selectElements = (value) => value.nodeType === 1;
@@ -2908,11 +3046,12 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
2908
3046
  * @param context - The execution context that the binding is operating within.
2909
3047
  * @param targets - The targets that behaviors in a view can attach to.
2910
3048
  */
2911
- bind(source, context, targets) {
2912
- const target = targets[this.nodeId];
2913
- target[this.sourceProperty] = source;
2914
- this.updateTarget(source, this.computeNodes(target));
3049
+ bind(controller) {
3050
+ const target = controller.targets[this.nodeId];
3051
+ target[this.sourceProperty] = controller.source;
3052
+ this.updateTarget(controller.source, this.computeNodes(target));
2915
3053
  this.observe(target);
3054
+ controller.onUnbind(this);
2916
3055
  }
2917
3056
  /**
2918
3057
  * Unbinds this behavior from the source.
@@ -2920,9 +3059,9 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
2920
3059
  * @param context - The execution context that the binding is operating within.
2921
3060
  * @param targets - The targets that behaviors in a view can attach to.
2922
3061
  */
2923
- unbind(source, context, targets) {
2924
- const target = targets[this.nodeId];
2925
- this.updateTarget(source, emptyArray);
3062
+ unbind(controller) {
3063
+ const target = controller.targets[this.nodeId];
3064
+ this.updateTarget(controller.source, emptyArray);
2926
3065
  this.disconnect(target);
2927
3066
  target[this.sourceProperty] = null;
2928
3067
  }
@@ -3071,6 +3210,16 @@ function children(propertyOrOptions) {
3071
3210
 
3072
3211
  const booleanMode = "boolean";
3073
3212
  const reflectMode = "reflect";
3213
+ /**
3214
+ * Metadata used to configure a custom attribute's behavior.
3215
+ * @public
3216
+ */
3217
+ const AttributeConfiguration = Object.freeze({
3218
+ /**
3219
+ * Locates all attribute configurations associated with a type.
3220
+ */
3221
+ locate: createMetadataLocator(),
3222
+ });
3074
3223
  /**
3075
3224
  * A {@link ValueConverter} that converts to and from `boolean` values.
3076
3225
  * @remarks
@@ -3208,7 +3357,7 @@ class AttributeDefinition {
3208
3357
  */
3209
3358
  static collect(Owner, ...attributeLists) {
3210
3359
  const attributes = [];
3211
- attributeLists.push(Owner.attributes);
3360
+ attributeLists.push(AttributeConfiguration.locate(Owner));
3212
3361
  for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
3213
3362
  const list = attributeLists[i];
3214
3363
  if (list === void 0) {
@@ -3238,9 +3387,7 @@ function attr(configOrTarget, prop) {
3238
3387
  // - @attr({...opts})
3239
3388
  config.property = $prop;
3240
3389
  }
3241
- const attributes = $target.constructor.attributes ||
3242
- ($target.constructor.attributes = []);
3243
- attributes.push(config);
3390
+ AttributeConfiguration.locate($target.constructor).push(config);
3244
3391
  }
3245
3392
  if (arguments.length > 1) {
3246
3393
  // Non invocation:
@@ -3258,25 +3405,24 @@ function attr(configOrTarget, prop) {
3258
3405
 
3259
3406
  const defaultShadowOptions = { mode: "open" };
3260
3407
  const defaultElementOptions = {};
3408
+ const fastElementBaseTypes = new Set();
3261
3409
  const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */, () => createTypeRegistry());
3262
3410
  /**
3263
3411
  * Defines metadata for a FASTElement.
3264
3412
  * @public
3265
3413
  */
3266
3414
  class FASTElementDefinition {
3267
- /**
3268
- * Creates an instance of FASTElementDefinition.
3269
- * @param type - The type this definition is being created for.
3270
- * @param nameOrConfig - The name of the element to define or a config object
3271
- * that describes the element to define.
3272
- */
3273
3415
  constructor(type, nameOrConfig = type.definition) {
3416
+ var _a;
3417
+ this.platformDefined = false;
3274
3418
  if (isString(nameOrConfig)) {
3275
3419
  nameOrConfig = { name: nameOrConfig };
3276
3420
  }
3277
3421
  this.type = type;
3278
3422
  this.name = nameOrConfig.name;
3279
3423
  this.template = nameOrConfig.template;
3424
+ this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
3425
+ const proto = type.prototype;
3280
3426
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3281
3427
  const observedAttributes = new Array(attributes.length);
3282
3428
  const propertyLookup = {};
@@ -3286,9 +3432,13 @@ class FASTElementDefinition {
3286
3432
  observedAttributes[i] = current.attribute;
3287
3433
  propertyLookup[current.name] = current;
3288
3434
  attributeLookup[current.attribute] = current;
3435
+ Observable.defineProperty(proto, current);
3289
3436
  }
3437
+ Reflect.defineProperty(type, "observedAttributes", {
3438
+ value: observedAttributes,
3439
+ enumerable: true,
3440
+ });
3290
3441
  this.attributes = attributes;
3291
- this.observedAttributes = observedAttributes;
3292
3442
  this.propertyLookup = propertyLookup;
3293
3443
  this.attributeLookup = attributeLookup;
3294
3444
  this.shadowOptions =
@@ -3301,43 +3451,50 @@ class FASTElementDefinition {
3301
3451
  nameOrConfig.elementOptions === void 0
3302
3452
  ? defaultElementOptions
3303
3453
  : Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
3304
- this.styles =
3305
- nameOrConfig.styles === void 0
3306
- ? void 0
3307
- : Array.isArray(nameOrConfig.styles)
3308
- ? new ElementStyles(nameOrConfig.styles)
3309
- : nameOrConfig.styles instanceof ElementStyles
3310
- ? nameOrConfig.styles
3311
- : new ElementStyles([nameOrConfig.styles]);
3454
+ this.styles = ElementStyles.normalize(nameOrConfig.styles);
3455
+ fastElementRegistry.register(this);
3312
3456
  }
3313
3457
  /**
3314
3458
  * Indicates if this element has been defined in at least one registry.
3315
3459
  */
3316
3460
  get isDefined() {
3317
- return !!fastElementRegistry.getByType(this.type);
3461
+ return this.platformDefined;
3318
3462
  }
3319
3463
  /**
3320
3464
  * Defines a custom element based on this definition.
3321
3465
  * @param registry - The element registry to define the element in.
3466
+ * @remarks
3467
+ * This operation is idempotent per registry.
3322
3468
  */
3323
- define(registry = customElements) {
3469
+ define(registry = this.registry) {
3324
3470
  const type = this.type;
3325
- if (fastElementRegistry.register(this)) {
3326
- const attributes = this.attributes;
3327
- const proto = type.prototype;
3328
- for (let i = 0, ii = attributes.length; i < ii; ++i) {
3329
- Observable.defineProperty(proto, attributes[i]);
3330
- }
3331
- Reflect.defineProperty(type, "observedAttributes", {
3332
- value: this.observedAttributes,
3333
- enumerable: true,
3334
- });
3335
- }
3336
3471
  if (!registry.get(this.name)) {
3472
+ this.platformDefined = true;
3337
3473
  registry.define(this.name, type, this.elementOptions);
3338
3474
  }
3339
3475
  return this;
3340
3476
  }
3477
+ /**
3478
+ * Creates an instance of FASTElementDefinition.
3479
+ * @param type - The type this definition is being created for.
3480
+ * @param nameOrDef - The name of the element to define or a config object
3481
+ * that describes the element to define.
3482
+ */
3483
+ static compose(type, nameOrDef) {
3484
+ if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
3485
+ return new FASTElementDefinition(class extends type {
3486
+ }, nameOrDef);
3487
+ }
3488
+ return new FASTElementDefinition(type, nameOrDef);
3489
+ }
3490
+ /**
3491
+ * Registers a FASTElement base type.
3492
+ * @param type - The type to register as a base type.
3493
+ * @internal
3494
+ */
3495
+ static registerBaseType(type) {
3496
+ fastElementBaseTypes.add(type);
3497
+ }
3341
3498
  }
3342
3499
  /**
3343
3500
  * Gets the element definition associated with the specified type.
@@ -3350,22 +3507,22 @@ FASTElementDefinition.getByType = fastElementRegistry.getByType;
3350
3507
  */
3351
3508
  FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
3352
3509
 
3353
- const shadowRoots = new WeakMap();
3354
3510
  const defaultEventOptions = {
3355
3511
  bubbles: true,
3356
3512
  composed: true,
3357
3513
  cancelable: true,
3358
3514
  };
3515
+ const isConnectedPropertyName = "isConnected";
3516
+ const shadowRoots = new WeakMap();
3359
3517
  function getShadowRoot(element) {
3360
3518
  var _a, _b;
3361
3519
  return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
3362
3520
  }
3363
- const isConnectedPropertyName = "isConnected";
3364
3521
  /**
3365
3522
  * Controls the lifecycle and rendering of a `FASTElement`.
3366
3523
  * @public
3367
3524
  */
3368
- class Controller extends PropertyChangeNotifier {
3525
+ class ElementController extends PropertyChangeNotifier {
3369
3526
  /**
3370
3527
  * Creates a Controller to control the specified element.
3371
3528
  * @param element - The element to be controlled by this controller.
@@ -3376,12 +3533,12 @@ class Controller extends PropertyChangeNotifier {
3376
3533
  constructor(element, definition) {
3377
3534
  super(element);
3378
3535
  this.boundObservables = null;
3379
- this.behaviors = null;
3380
3536
  this.needsInitialization = true;
3381
3537
  this.hasExistingShadowRoot = false;
3382
3538
  this._template = null;
3383
- this._styles = null;
3384
3539
  this._isConnected = false;
3540
+ this.behaviors = null;
3541
+ this._mainStyles = null;
3385
3542
  /**
3386
3543
  * This allows Observable.getNotifier(...) to return the Controller
3387
3544
  * when the notifier for the Controller itself is being requested. The
@@ -3397,7 +3554,7 @@ class Controller extends PropertyChangeNotifier {
3397
3554
  * If `null` then the element is managing its own rendering.
3398
3555
  */
3399
3556
  this.view = null;
3400
- this.element = element;
3557
+ this.source = element;
3401
3558
  this.definition = definition;
3402
3559
  const shadowOptions = definition.shadowOptions;
3403
3560
  if (shadowOptions !== void 0) {
@@ -3451,9 +3608,9 @@ class Controller extends PropertyChangeNotifier {
3451
3608
  // 1. Template overrides take top precedence.
3452
3609
  if (this._template === null) {
3453
3610
  const definition = this.definition;
3454
- if (this.element.resolveTemplate) {
3611
+ if (this.source.resolveTemplate) {
3455
3612
  // 2. Allow for element instance overrides next.
3456
- this._template = this.element.resolveTemplate();
3613
+ this._template = this.source.resolveTemplate();
3457
3614
  }
3458
3615
  else if (definition.template) {
3459
3616
  // 3. Default to the static definition.
@@ -3472,56 +3629,102 @@ class Controller extends PropertyChangeNotifier {
3472
3629
  }
3473
3630
  }
3474
3631
  /**
3475
- * Gets/sets the primary styles used for the component.
3476
- * @remarks
3477
- * This value can only be accurately read after connect but can be set at any time.
3632
+ * The main set of styles used for the component, independent
3633
+ * of any dynamically added styles.
3478
3634
  */
3479
- get styles() {
3635
+ get mainStyles() {
3480
3636
  var _a;
3481
3637
  // 1. Styles overrides take top precedence.
3482
- if (this._styles === null) {
3638
+ if (this._mainStyles === null) {
3483
3639
  const definition = this.definition;
3484
- if (this.element.resolveStyles) {
3640
+ if (this.source.resolveStyles) {
3485
3641
  // 2. Allow for element instance overrides next.
3486
- this._styles = this.element.resolveStyles();
3642
+ this._mainStyles = this.source.resolveStyles();
3487
3643
  }
3488
3644
  else if (definition.styles) {
3489
3645
  // 3. Default to the static definition.
3490
- this._styles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3646
+ this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3491
3647
  }
3492
3648
  }
3493
- return this._styles;
3649
+ return this._mainStyles;
3494
3650
  }
3495
- set styles(value) {
3496
- if (this._styles === value) {
3651
+ set mainStyles(value) {
3652
+ if (this._mainStyles === value) {
3497
3653
  return;
3498
3654
  }
3499
- if (this._styles !== null) {
3500
- this.removeStyles(this._styles);
3655
+ if (this._mainStyles !== null) {
3656
+ this.removeStyles(this._mainStyles);
3501
3657
  }
3502
- this._styles = value;
3658
+ this._mainStyles = value;
3503
3659
  if (!this.needsInitialization) {
3504
3660
  this.addStyles(value);
3505
3661
  }
3506
3662
  }
3663
+ /**
3664
+ * Adds the behavior to the component.
3665
+ * @param behavior - The behavior to add.
3666
+ */
3667
+ addBehavior(behavior) {
3668
+ var _a, _b;
3669
+ const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3670
+ const count = (_b = targetBehaviors.get(behavior)) !== null && _b !== void 0 ? _b : 0;
3671
+ if (count === 0) {
3672
+ targetBehaviors.set(behavior, 1);
3673
+ behavior.addedCallback && behavior.addedCallback(this);
3674
+ if (behavior.connectedCallback && this.isConnected) {
3675
+ behavior.connectedCallback(this);
3676
+ }
3677
+ }
3678
+ else {
3679
+ targetBehaviors.set(behavior, count + 1);
3680
+ }
3681
+ }
3682
+ /**
3683
+ * Removes the behavior from the component.
3684
+ * @param behavior - The behavior to remove.
3685
+ * @param force - Forces removal even if this behavior was added more than once.
3686
+ */
3687
+ removeBehavior(behavior, force = false) {
3688
+ const targetBehaviors = this.behaviors;
3689
+ if (targetBehaviors === null) {
3690
+ return;
3691
+ }
3692
+ const count = targetBehaviors.get(behavior);
3693
+ if (count === void 0) {
3694
+ return;
3695
+ }
3696
+ if (count === 1 || force) {
3697
+ targetBehaviors.delete(behavior);
3698
+ if (behavior.disconnectedCallback && this.isConnected) {
3699
+ behavior.disconnectedCallback(this);
3700
+ }
3701
+ behavior.removedCallback && behavior.removedCallback(this);
3702
+ }
3703
+ else {
3704
+ targetBehaviors.set(behavior, count - 1);
3705
+ }
3706
+ }
3507
3707
  /**
3508
3708
  * Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot.
3509
3709
  * @param styles - The styles to add.
3510
3710
  */
3511
3711
  addStyles(styles) {
3712
+ var _a;
3512
3713
  if (!styles) {
3513
3714
  return;
3514
3715
  }
3515
- const target = getShadowRoot(this.element) ||
3516
- this.element.getRootNode();
3716
+ const source = this.source;
3517
3717
  if (styles instanceof HTMLElement) {
3718
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : this.source;
3518
3719
  target.append(styles);
3519
3720
  }
3520
- else if (!styles.isAttachedTo(target)) {
3721
+ else if (!styles.isAttachedTo(source)) {
3521
3722
  const sourceBehaviors = styles.behaviors;
3522
- styles.addStylesTo(target);
3723
+ styles.addStylesTo(source);
3523
3724
  if (sourceBehaviors !== null) {
3524
- this.addBehaviors(sourceBehaviors);
3725
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3726
+ this.addBehavior(sourceBehaviors[i]);
3727
+ }
3525
3728
  }
3526
3729
  }
3527
3730
  }
@@ -3530,97 +3733,42 @@ class Controller extends PropertyChangeNotifier {
3530
3733
  * @param styles - the styles to remove.
3531
3734
  */
3532
3735
  removeStyles(styles) {
3736
+ var _a;
3533
3737
  if (!styles) {
3534
3738
  return;
3535
3739
  }
3536
- const target = getShadowRoot(this.element) ||
3537
- this.element.getRootNode();
3740
+ const source = this.source;
3538
3741
  if (styles instanceof HTMLElement) {
3742
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source;
3539
3743
  target.removeChild(styles);
3540
3744
  }
3541
- else if (styles.isAttachedTo(target)) {
3745
+ else if (styles.isAttachedTo(source)) {
3542
3746
  const sourceBehaviors = styles.behaviors;
3543
- styles.removeStylesFrom(target);
3747
+ styles.removeStylesFrom(source);
3544
3748
  if (sourceBehaviors !== null) {
3545
- this.removeBehaviors(sourceBehaviors);
3546
- }
3547
- }
3548
- }
3549
- /**
3550
- * Adds behaviors to this element.
3551
- * @param behaviors - The behaviors to add.
3552
- */
3553
- addBehaviors(behaviors) {
3554
- var _a;
3555
- const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3556
- const length = behaviors.length;
3557
- const behaviorsToBind = [];
3558
- for (let i = 0; i < length; ++i) {
3559
- const behavior = behaviors[i];
3560
- if (targetBehaviors.has(behavior)) {
3561
- targetBehaviors.set(behavior, targetBehaviors.get(behavior) + 1);
3562
- }
3563
- else {
3564
- targetBehaviors.set(behavior, 1);
3565
- behaviorsToBind.push(behavior);
3566
- }
3567
- }
3568
- if (this._isConnected) {
3569
- const element = this.element;
3570
- const context = ExecutionContext.default;
3571
- for (let i = 0; i < behaviorsToBind.length; ++i) {
3572
- behaviorsToBind[i].bind(element, context);
3573
- }
3574
- }
3575
- }
3576
- /**
3577
- * Removes behaviors from this element.
3578
- * @param behaviors - The behaviors to remove.
3579
- * @param force - Forces unbinding of behaviors.
3580
- */
3581
- removeBehaviors(behaviors, force = false) {
3582
- const targetBehaviors = this.behaviors;
3583
- if (targetBehaviors === null) {
3584
- return;
3585
- }
3586
- const length = behaviors.length;
3587
- const behaviorsToUnbind = [];
3588
- for (let i = 0; i < length; ++i) {
3589
- const behavior = behaviors[i];
3590
- if (targetBehaviors.has(behavior)) {
3591
- const count = targetBehaviors.get(behavior) - 1;
3592
- count === 0 || force
3593
- ? targetBehaviors.delete(behavior) && behaviorsToUnbind.push(behavior)
3594
- : targetBehaviors.set(behavior, count);
3595
- }
3596
- }
3597
- if (this._isConnected) {
3598
- const element = this.element;
3599
- const context = ExecutionContext.default;
3600
- for (let i = 0; i < behaviorsToUnbind.length; ++i) {
3601
- behaviorsToUnbind[i].unbind(element, context);
3749
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3750
+ this.addBehavior(sourceBehaviors[i]);
3751
+ }
3602
3752
  }
3603
3753
  }
3604
3754
  }
3605
3755
  /**
3606
3756
  * Runs connected lifecycle behavior on the associated element.
3607
3757
  */
3608
- onConnectedCallback() {
3758
+ connect() {
3609
3759
  if (this._isConnected) {
3610
3760
  return;
3611
3761
  }
3612
- const element = this.element;
3613
- const context = ExecutionContext.default;
3614
3762
  if (this.needsInitialization) {
3615
3763
  this.finishInitialization();
3616
3764
  }
3617
3765
  else if (this.view !== null) {
3618
- this.view.bind(element, context);
3766
+ this.view.bind(this.source);
3619
3767
  }
3620
3768
  const behaviors = this.behaviors;
3621
3769
  if (behaviors !== null) {
3622
- for (const behavior of behaviors.keys()) {
3623
- behavior.bind(element, context);
3770
+ for (const key of behaviors.keys()) {
3771
+ key.connectedCallback && key.connectedCallback(this);
3624
3772
  }
3625
3773
  }
3626
3774
  this.setIsConnected(true);
@@ -3628,21 +3776,18 @@ class Controller extends PropertyChangeNotifier {
3628
3776
  /**
3629
3777
  * Runs disconnected lifecycle behavior on the associated element.
3630
3778
  */
3631
- onDisconnectedCallback() {
3779
+ disconnect() {
3632
3780
  if (!this._isConnected) {
3633
3781
  return;
3634
3782
  }
3635
3783
  this.setIsConnected(false);
3636
- const view = this.view;
3637
- if (view !== null) {
3638
- view.unbind();
3784
+ if (this.view !== null) {
3785
+ this.view.unbind();
3639
3786
  }
3640
3787
  const behaviors = this.behaviors;
3641
3788
  if (behaviors !== null) {
3642
- const element = this.element;
3643
- const context = ExecutionContext.default;
3644
- for (const behavior of behaviors.keys()) {
3645
- behavior.unbind(element, context);
3789
+ for (const key of behaviors.keys()) {
3790
+ key.disconnectedCallback && key.disconnectedCallback(this);
3646
3791
  }
3647
3792
  }
3648
3793
  }
@@ -3655,7 +3800,7 @@ class Controller extends PropertyChangeNotifier {
3655
3800
  onAttributeChangedCallback(name, oldValue, newValue) {
3656
3801
  const attrDef = this.definition.attributeLookup[name];
3657
3802
  if (attrDef !== void 0) {
3658
- attrDef.onAttributeChangedCallback(this.element, newValue);
3803
+ attrDef.onAttributeChangedCallback(this.source, newValue);
3659
3804
  }
3660
3805
  }
3661
3806
  /**
@@ -3668,12 +3813,12 @@ class Controller extends PropertyChangeNotifier {
3668
3813
  */
3669
3814
  emit(type, detail, options) {
3670
3815
  if (this._isConnected) {
3671
- return this.element.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3816
+ return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3672
3817
  }
3673
3818
  return false;
3674
3819
  }
3675
3820
  finishInitialization() {
3676
- const element = this.element;
3821
+ const element = this.source;
3677
3822
  const boundObservables = this.boundObservables;
3678
3823
  // If we have any observables that were bound, re-apply their values.
3679
3824
  if (boundObservables !== null) {
@@ -3685,15 +3830,15 @@ class Controller extends PropertyChangeNotifier {
3685
3830
  this.boundObservables = null;
3686
3831
  }
3687
3832
  this.renderTemplate(this.template);
3688
- this.addStyles(this.styles);
3833
+ this.addStyles(this.mainStyles);
3689
3834
  this.needsInitialization = false;
3690
3835
  }
3691
3836
  renderTemplate(template) {
3692
3837
  var _a;
3693
- const element = this.element;
3694
3838
  // When getting the host to render to, we start by looking
3695
3839
  // up the shadow root. If there isn't one, then that means
3696
3840
  // we're doing a Light DOM render to the element's direct children.
3841
+ const element = this.source;
3697
3842
  const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
3698
3843
  if (this.view !== null) {
3699
3844
  // If there's already a view, we need to unbind and remove through dispose.
@@ -3710,6 +3855,8 @@ class Controller extends PropertyChangeNotifier {
3710
3855
  if (template) {
3711
3856
  // If a new template was provided, render it.
3712
3857
  this.view = template.render(element, host, element);
3858
+ this.view.sourceLifetime =
3859
+ SourceLifetime.coupled;
3713
3860
  }
3714
3861
  }
3715
3862
  /**
@@ -3729,31 +3876,136 @@ class Controller extends PropertyChangeNotifier {
3729
3876
  if (definition === void 0) {
3730
3877
  throw FAST.error(1401 /* Message.missingElementDefinition */);
3731
3878
  }
3732
- return (element.$fastController = new Controller(element, definition));
3879
+ return (element.$fastController = new ElementController(element, definition));
3880
+ }
3881
+ }
3882
+ /**
3883
+ * Converts a styleTarget into the operative target. When the provided target is an Element
3884
+ * that is a FASTElement, the function will return the ShadowRoot for that element. Otherwise,
3885
+ * it will return the root node for the element.
3886
+ * @param target
3887
+ * @returns
3888
+ */
3889
+ function normalizeStyleTarget(target) {
3890
+ var _a;
3891
+ if ("adoptedStyleSheets" in target) {
3892
+ return target;
3893
+ }
3894
+ else {
3895
+ return ((_a = getShadowRoot(target)) !== null && _a !== void 0 ? _a : target.getRootNode());
3896
+ }
3897
+ }
3898
+ // Default StyleStrategy implementations are defined in this module because they
3899
+ // require access to element shadowRoots, and we don't want to leak shadowRoot
3900
+ // objects out of this module.
3901
+ /**
3902
+ * https://wicg.github.io/construct-stylesheets/
3903
+ * https://developers.google.com/web/updates/2019/02/constructable-stylesheets
3904
+ *
3905
+ * @internal
3906
+ */
3907
+ class AdoptedStyleSheetsStrategy {
3908
+ constructor(styles) {
3909
+ const styleSheetCache = AdoptedStyleSheetsStrategy.styleSheetCache;
3910
+ this.sheets = styles.map((x) => {
3911
+ if (x instanceof CSSStyleSheet) {
3912
+ return x;
3913
+ }
3914
+ let sheet = styleSheetCache.get(x);
3915
+ if (sheet === void 0) {
3916
+ sheet = new CSSStyleSheet();
3917
+ sheet.replaceSync(x);
3918
+ styleSheetCache.set(x, sheet);
3919
+ }
3920
+ return sheet;
3921
+ });
3922
+ }
3923
+ addStylesTo(target) {
3924
+ const t = normalizeStyleTarget(target);
3925
+ t.adoptedStyleSheets = [...t.adoptedStyleSheets, ...this.sheets];
3926
+ }
3927
+ removeStylesFrom(target) {
3928
+ const t = normalizeStyleTarget(target);
3929
+ const sheets = this.sheets;
3930
+ t.adoptedStyleSheets = t.adoptedStyleSheets.filter((x) => sheets.indexOf(x) === -1);
3931
+ }
3932
+ }
3933
+ AdoptedStyleSheetsStrategy.styleSheetCache = new Map();
3934
+ let id = 0;
3935
+ const nextStyleId = () => `fast-${++id}`;
3936
+ function usableStyleTarget(target) {
3937
+ return target === document ? document.body : target;
3938
+ }
3939
+ /**
3940
+ * @internal
3941
+ */
3942
+ class StyleElementStrategy {
3943
+ constructor(styles) {
3944
+ this.styles = styles;
3945
+ this.styleClass = nextStyleId();
3946
+ }
3947
+ addStylesTo(target) {
3948
+ target = usableStyleTarget(normalizeStyleTarget(target));
3949
+ const styles = this.styles;
3950
+ const styleClass = this.styleClass;
3951
+ for (let i = 0; i < styles.length; i++) {
3952
+ const element = document.createElement("style");
3953
+ element.innerHTML = styles[i];
3954
+ element.className = styleClass;
3955
+ target.append(element);
3956
+ }
3957
+ }
3958
+ removeStylesFrom(target) {
3959
+ target = usableStyleTarget(normalizeStyleTarget(target));
3960
+ const styles = target.querySelectorAll(`.${this.styleClass}`);
3961
+ styles[0].parentNode;
3962
+ for (let i = 0, ii = styles.length; i < ii; ++i) {
3963
+ target.removeChild(styles[i]);
3964
+ }
3733
3965
  }
3734
3966
  }
3967
+ ElementStyles.setDefaultStrategy(ElementStyles.supportsAdoptedStyleSheets
3968
+ ? AdoptedStyleSheetsStrategy
3969
+ : StyleElementStrategy);
3735
3970
 
3736
3971
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
3737
3972
  function createFASTElement(BaseType) {
3738
- return class extends BaseType {
3973
+ const type = class extends BaseType {
3739
3974
  constructor() {
3740
3975
  /* eslint-disable-next-line */
3741
3976
  super();
3742
- Controller.forCustomElement(this);
3977
+ ElementController.forCustomElement(this);
3743
3978
  }
3744
3979
  $emit(type, detail, options) {
3745
3980
  return this.$fastController.emit(type, detail, options);
3746
3981
  }
3747
3982
  connectedCallback() {
3748
- this.$fastController.onConnectedCallback();
3983
+ this.$fastController.connect();
3749
3984
  }
3750
3985
  disconnectedCallback() {
3751
- this.$fastController.onDisconnectedCallback();
3986
+ this.$fastController.disconnect();
3752
3987
  }
3753
3988
  attributeChangedCallback(name, oldValue, newValue) {
3754
3989
  this.$fastController.onAttributeChangedCallback(name, oldValue, newValue);
3755
3990
  }
3756
3991
  };
3992
+ FASTElementDefinition.registerBaseType(type);
3993
+ return type;
3994
+ }
3995
+ function compose(type, nameOrDef) {
3996
+ if (isFunction(type)) {
3997
+ return FASTElementDefinition.compose(type, nameOrDef);
3998
+ }
3999
+ return FASTElementDefinition.compose(this, type);
4000
+ }
4001
+ function define(type, nameOrDef) {
4002
+ if (isFunction(type)) {
4003
+ return FASTElementDefinition.compose(type, nameOrDef).define().type;
4004
+ }
4005
+ return FASTElementDefinition.compose(this, type).define().type;
4006
+ }
4007
+ function from(BaseType) {
4008
+ return createFASTElement(BaseType);
3757
4009
  }
3758
4010
  /**
3759
4011
  * A minimal base class for FASTElements that also provides
@@ -3766,18 +4018,19 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3766
4018
  * provided base type.
3767
4019
  * @param BaseType - The base element type to inherit from.
3768
4020
  */
3769
- from(BaseType) {
3770
- return createFASTElement(BaseType);
3771
- },
4021
+ from,
3772
4022
  /**
3773
4023
  * Defines a platform custom element based on the provided type and definition.
3774
4024
  * @param type - The custom element type to define.
3775
4025
  * @param nameOrDef - The name of the element to define or a definition object
3776
4026
  * that describes the element to define.
3777
4027
  */
3778
- define(type, nameOrDef) {
3779
- return new FASTElementDefinition(type, nameOrDef).define().type;
3780
- },
4028
+ define,
4029
+ /**
4030
+ * Defines metadata for a FASTElement which can be used to later define the element.
4031
+ * @public
4032
+ */
4033
+ compose,
3781
4034
  });
3782
4035
  /**
3783
4036
  * Decorator: Defines a platform custom element based on `FASTElement`.
@@ -3788,8 +4041,8 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3788
4041
  function customElement(nameOrDef) {
3789
4042
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
3790
4043
  return function (type) {
3791
- new FASTElementDefinition(type, nameOrDef).define();
4044
+ define(type, nameOrDef);
3792
4045
  };
3793
4046
  }
3794
4047
 
3795
- export { AdoptedStyleSheetsStrategy, ArrayObserver, Aspect, AttributeDefinition, BindingConfig, BindingMode, CSSDirective, ChangeBinding, ChildrenDirective, Compiler, Controller, DOM, ElementStyles, EventBinding, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, OneTimeBinding, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SignalBinding, SlottedDirective, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, TwoWayBinding, UpdateBinding, Updates, ViewTemplate, attr, bind, booleanConverter, child, children, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, item, length, nullableNumberConverter, observable, onChange, oneTime, ref, repeat, signal, slotted, twoWay, volatile, when };
4048
+ export { ArrayObserver, Aspect, AttributeConfiguration, AttributeDefinition, Binding, CSSDirective, ChildrenDirective, Compiler, DOM, ElementController, ElementStyles, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, SourceLifetime, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewBehaviorOrchestrator, ViewTemplate, attr, bind, booleanConverter, children, createMetadataLocator, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };