@microsoft/fast-element 2.0.0-beta.6 → 2.0.0-beta.8

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 (64) hide show
  1. package/CHANGELOG.json +78 -0
  2. package/CHANGELOG.md +25 -1
  3. package/dist/dts/components/attributes.d.ts +10 -0
  4. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +24 -25
  5. package/dist/dts/components/fast-definitions.d.ts +28 -3
  6. package/dist/dts/components/fast-element.d.ts +2 -2
  7. package/dist/dts/di/di.d.ts +41 -0
  8. package/dist/dts/index.d.ts +2 -2
  9. package/dist/dts/observation/observable.d.ts +86 -47
  10. package/dist/dts/pending-task.d.ts +20 -0
  11. package/dist/dts/platform.d.ts +6 -0
  12. package/dist/dts/styles/css-directive.d.ts +2 -2
  13. package/dist/dts/styles/element-styles.d.ts +3 -3
  14. package/dist/dts/styles/host.d.ts +68 -0
  15. package/dist/dts/templating/binding-signal.d.ts +2 -2
  16. package/dist/dts/templating/binding-two-way.d.ts +11 -3
  17. package/dist/dts/templating/binding.d.ts +21 -119
  18. package/dist/dts/templating/children.d.ts +1 -1
  19. package/dist/dts/templating/html-directive.d.ts +69 -39
  20. package/dist/dts/templating/node-observation.d.ts +4 -5
  21. package/dist/dts/templating/ref.d.ts +5 -13
  22. package/dist/dts/templating/render.d.ts +15 -20
  23. package/dist/dts/templating/repeat.d.ts +11 -16
  24. package/dist/dts/templating/slotted.d.ts +1 -1
  25. package/dist/dts/templating/template.d.ts +4 -4
  26. package/dist/dts/templating/view.d.ts +68 -9
  27. package/dist/dts/templating/when.d.ts +1 -1
  28. package/dist/dts/testing/exports.d.ts +1 -0
  29. package/dist/dts/testing/fakes.d.ts +4 -0
  30. package/dist/dts/testing/fixture.d.ts +0 -6
  31. package/dist/esm/components/attributes.js +13 -4
  32. package/dist/esm/components/{controller.js → element-controller.js} +95 -105
  33. package/dist/esm/components/fast-definitions.js +3 -1
  34. package/dist/esm/components/fast-element.js +4 -4
  35. package/dist/esm/di/di.js +87 -3
  36. package/dist/esm/index.js +2 -1
  37. package/dist/esm/observation/observable.js +59 -126
  38. package/dist/esm/pending-task.js +16 -0
  39. package/dist/esm/platform.js +24 -0
  40. package/dist/esm/styles/css.js +4 -4
  41. package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
  42. package/dist/esm/templating/binding-signal.js +21 -17
  43. package/dist/esm/templating/binding-two-way.js +32 -27
  44. package/dist/esm/templating/binding.js +73 -177
  45. package/dist/esm/templating/html-directive.js +78 -7
  46. package/dist/esm/templating/node-observation.js +9 -8
  47. package/dist/esm/templating/ref.js +4 -12
  48. package/dist/esm/templating/render.js +30 -31
  49. package/dist/esm/templating/repeat.js +37 -38
  50. package/dist/esm/templating/template.js +3 -4
  51. package/dist/esm/templating/view.js +98 -29
  52. package/dist/esm/testing/exports.js +1 -0
  53. package/dist/esm/testing/fakes.js +76 -0
  54. package/dist/esm/testing/fixture.js +1 -3
  55. package/dist/fast-element.api.json +5720 -5385
  56. package/dist/fast-element.d.ts +510 -399
  57. package/dist/fast-element.debug.js +497 -514
  58. package/dist/fast-element.debug.min.js +1 -1
  59. package/dist/fast-element.js +497 -514
  60. package/dist/fast-element.min.js +1 -1
  61. package/dist/fast-element.untrimmed.d.ts +519 -405
  62. package/docs/api-report.md +197 -129
  63. package/package.json +5 -1
  64. package/dist/dts/observation/behavior.d.ts +0 -19
@@ -204,10 +204,34 @@ function createTypeRegistry() {
204
204
  return typeToDefinition.get(key);
205
205
  },
206
206
  getForInstance(object) {
207
+ if (object === null || object === void 0) {
208
+ return void 0;
209
+ }
207
210
  return typeToDefinition.get(object.constructor);
208
211
  },
209
212
  });
210
213
  }
214
+ /**
215
+ * Creates a function capable of locating metadata associated with a type.
216
+ * @returns A metadata locator function.
217
+ * @internal
218
+ */
219
+ function createMetadataLocator() {
220
+ const metadataLookup = new WeakMap();
221
+ return function (target) {
222
+ let metadata = metadataLookup.get(target);
223
+ if (metadata === void 0) {
224
+ let currentTarget = Reflect.getPrototypeOf(target);
225
+ while (metadata === void 0 && currentTarget !== null) {
226
+ metadata = metadataLookup.get(currentTarget);
227
+ currentTarget = Reflect.getPrototypeOf(currentTarget);
228
+ }
229
+ metadata = metadata === void 0 ? [] : metadata.slice(0);
230
+ metadataLookup.set(target, metadata);
231
+ }
232
+ return metadata;
233
+ };
234
+ }
211
235
 
212
236
  /**
213
237
  * @internal
@@ -449,6 +473,21 @@ class PropertyChangeNotifier {
449
473
  }
450
474
  }
451
475
 
476
+ /**
477
+ * Describes how the source's lifetime relates to its controller's lifetime.
478
+ * @public
479
+ */
480
+ const SourceLifetime = Object.freeze({
481
+ /**
482
+ * The source to controller lifetime relationship is unknown.
483
+ */
484
+ unknown: void 0,
485
+ /**
486
+ * The source and controller lifetimes are coupled to one another.
487
+ * They can/will be GC'd together.
488
+ */
489
+ coupled: 1,
490
+ });
452
491
  /**
453
492
  * Common Observable APIs.
454
493
  * @public
@@ -457,7 +496,6 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
457
496
  const queueUpdate = Updates.enqueue;
458
497
  const volatileRegex = /(:|&&|\|\||if)/;
459
498
  const notifierLookup = new WeakMap();
460
- const accessorLookup = new WeakMap();
461
499
  let watcher = void 0;
462
500
  let createArrayObserver = (array) => {
463
501
  throw FAST.error(1101 /* Message.needsArrayObservation */);
@@ -472,19 +510,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
472
510
  }
473
511
  return found;
474
512
  }
475
- function getAccessors(target) {
476
- let accessors = accessorLookup.get(target);
477
- if (accessors === void 0) {
478
- let currentTarget = Reflect.getPrototypeOf(target);
479
- while (accessors === void 0 && currentTarget !== null) {
480
- accessors = accessorLookup.get(currentTarget);
481
- currentTarget = Reflect.getPrototypeOf(currentTarget);
482
- }
483
- accessors = accessors === void 0 ? [] : accessors.slice(0);
484
- accessorLookup.set(target, accessors);
485
- }
486
- return accessors;
487
- }
513
+ const getAccessors = createMetadataLocator();
488
514
  class DefaultObservableAccessor {
489
515
  constructor(name) {
490
516
  this.name = name;
@@ -528,6 +554,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
528
554
  setMode(isAsync) {
529
555
  this.isAsync = this.needsQueue = isAsync;
530
556
  }
557
+ bind(controller) {
558
+ this.controller = controller;
559
+ const value = this.observe(controller.source, controller.context);
560
+ if (!controller.isBound && this.requiresUnbind(controller)) {
561
+ controller.onUnbind(this);
562
+ }
563
+ return value;
564
+ }
565
+ requiresUnbind(controller) {
566
+ return (controller.sourceLifetime !== SourceLifetime.coupled ||
567
+ this.first !== this.last ||
568
+ this.first.propertySource !== controller.source);
569
+ }
570
+ unbind(controller) {
571
+ this.dispose();
572
+ }
531
573
  observe(source, context) {
532
574
  if (this.needsRefresh && this.last !== null) {
533
575
  this.dispose();
@@ -537,7 +579,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
537
579
  this.needsRefresh = this.isVolatileBinding;
538
580
  let result;
539
581
  try {
540
- result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
582
+ result = this.binding(source, context);
541
583
  }
542
584
  finally {
543
585
  watcher = previousWatcher;
@@ -728,123 +770,38 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
728
770
  * Provides additional contextual information available to behaviors and expressions.
729
771
  * @public
730
772
  */
731
- class ExecutionContext {
732
- constructor(parentSource = null, parentContext = null) {
733
- /**
734
- * The index of the current item within a repeat context.
735
- */
736
- this.index = 0;
737
- /**
738
- * The length of the current collection within a repeat context.
739
- */
740
- this.length = 0;
741
- this.parent = parentSource;
742
- this.parentContext = parentContext;
743
- }
773
+ const ExecutionContext = Object.freeze({
744
774
  /**
745
- * The current event within an event handler.
775
+ * A default execution context.
746
776
  */
747
- get event() {
748
- return contextEvent.get();
749
- }
750
- /**
751
- * Indicates whether the current item within a repeat context
752
- * has an even index.
753
- */
754
- get isEven() {
755
- return this.index % 2 === 0;
756
- }
757
- /**
758
- * Indicates whether the current item within a repeat context
759
- * has an odd index.
760
- */
761
- get isOdd() {
762
- return this.index % 2 !== 0;
763
- }
764
- /**
765
- * Indicates whether the current item within a repeat context
766
- * is the first item in the collection.
767
- */
768
- get isFirst() {
769
- return this.index === 0;
770
- }
771
- /**
772
- * Indicates whether the current item within a repeat context
773
- * is somewhere in the middle of the collection.
774
- */
775
- get isInMiddle() {
776
- return !this.isFirst && !this.isLast;
777
- }
778
- /**
779
- * Indicates whether the current item within a repeat context
780
- * is the last item in the collection.
781
- */
782
- get isLast() {
783
- return this.index === this.length - 1;
784
- }
785
- /**
786
- * Returns the typed event detail of a custom event.
787
- */
788
- eventDetail() {
789
- return this.event.detail;
790
- }
791
- /**
792
- * Returns the typed event target of the event.
793
- */
794
- eventTarget() {
795
- return this.event.target;
796
- }
797
- /**
798
- * Updates the position/size on a context associated with a list item.
799
- * @param index - The new index of the item.
800
- * @param length - The new length of the list.
801
- */
802
- updatePosition(index, length) {
803
- this.index = index;
804
- this.length = length;
805
- }
806
- /**
807
- * Creates a new execution context descendent from the current context.
808
- * @param source - The source for the context if different than the parent.
809
- * @returns A child execution context.
810
- */
811
- createChildContext(parentSource) {
812
- return new ExecutionContext(parentSource, this);
813
- }
777
+ default: {
778
+ index: 0,
779
+ length: 0,
780
+ get event() {
781
+ return ExecutionContext.getEvent();
782
+ },
783
+ eventDetail() {
784
+ return this.event.detail;
785
+ },
786
+ eventTarget() {
787
+ return this.event.target;
788
+ },
789
+ },
814
790
  /**
815
- * Creates a new execution context descent suitable for use in list rendering.
816
- * @param item - The list item to serve as the source.
817
- * @param index - The index of the item in the list.
818
- * @param length - The length of the list.
791
+ * Gets the current event.
792
+ * @returns An event object.
819
793
  */
820
- createItemContext(index, length) {
821
- const childContext = Object.create(this);
822
- childContext.index = index;
823
- childContext.length = length;
824
- return childContext;
825
- }
794
+ getEvent() {
795
+ return contextEvent.get();
796
+ },
826
797
  /**
827
- * Sets the event for the current execution context.
828
- * @param event - The event to set.
829
- * @internal
798
+ * Sets the current event.
799
+ * @param event - An event object.
830
800
  */
831
- static setEvent(event) {
801
+ setEvent(event) {
832
802
  contextEvent.set(event);
833
- }
834
- /**
835
- * Creates a new root execution context.
836
- * @returns A new execution context.
837
- */
838
- static create() {
839
- return new ExecutionContext();
840
- }
841
- }
842
- /**
843
- * The default execution context.
844
- */
845
- ExecutionContext.default = new ExecutionContext();
846
- Observable.defineProperty(ExecutionContext.prototype, "index");
847
- Observable.defineProperty(ExecutionContext.prototype, "length");
803
+ },
804
+ });
848
805
 
849
806
  /**
850
807
  * A splice map is a representation of how a previous array of items
@@ -1642,11 +1599,11 @@ class CSSPartial {
1642
1599
  }
1643
1600
  return this.css;
1644
1601
  }
1645
- bind(el) {
1646
- el.$fastController.addStyles(this.styles);
1602
+ addedCallback(controller) {
1603
+ controller.addStyles(this.styles);
1647
1604
  }
1648
- unbind(el) {
1649
- el.$fastController.removeStyles(this.styles);
1605
+ removedCallback(controller) {
1606
+ controller.removeStyles(this.styles);
1650
1607
  }
1651
1608
  }
1652
1609
  CSSDirective.define(CSSPartial);
@@ -1785,6 +1742,67 @@ const Parser = Object.freeze({
1785
1742
  },
1786
1743
  });
1787
1744
 
1745
+ /**
1746
+ * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
1747
+ * control ViewBehaviors.
1748
+ * @public
1749
+ */
1750
+ const ViewBehaviorOrchestrator = Object.freeze({
1751
+ /**
1752
+ * Creates a ViewBehaviorOrchestrator.
1753
+ * @param source - The source to to associate behaviors with.
1754
+ * @returns A ViewBehaviorOrchestrator.
1755
+ */
1756
+ create(source) {
1757
+ const behaviors = [];
1758
+ const targets = {};
1759
+ let unbindables = null;
1760
+ let isConnected = false;
1761
+ return {
1762
+ source,
1763
+ context: ExecutionContext.default,
1764
+ targets,
1765
+ get isBound() {
1766
+ return isConnected;
1767
+ },
1768
+ addBehaviorFactory(factory, target) {
1769
+ const nodeId = factory.nodeId || (factory.nodeId = nextId());
1770
+ factory.id || (factory.id = nextId());
1771
+ this.addTarget(nodeId, target);
1772
+ this.addBehavior(factory.createBehavior());
1773
+ },
1774
+ addTarget(nodeId, target) {
1775
+ targets[nodeId] = target;
1776
+ },
1777
+ addBehavior(behavior) {
1778
+ behaviors.push(behavior);
1779
+ if (isConnected) {
1780
+ behavior.bind(this);
1781
+ }
1782
+ },
1783
+ onUnbind(unbindable) {
1784
+ if (unbindables === null) {
1785
+ unbindables = [];
1786
+ }
1787
+ unbindables.push(unbindable);
1788
+ },
1789
+ connectedCallback(controller) {
1790
+ if (!isConnected) {
1791
+ isConnected = true;
1792
+ behaviors.forEach(x => x.bind(this));
1793
+ }
1794
+ },
1795
+ disconnectedCallback(controller) {
1796
+ if (isConnected) {
1797
+ isConnected = false;
1798
+ if (unbindables !== null) {
1799
+ unbindables.forEach(x => x.unbind(this));
1800
+ }
1801
+ }
1802
+ },
1803
+ };
1804
+ },
1805
+ });
1788
1806
  const registry = createTypeRegistry();
1789
1807
  /**
1790
1808
  * Instructs the template engine to apply behavior to a node.
@@ -1830,6 +1848,15 @@ function htmlDirective(options) {
1830
1848
  * @public
1831
1849
  */
1832
1850
  class Binding {
1851
+ /**
1852
+ * Creates a binding.
1853
+ * @param evaluate - Evaluates the binding.
1854
+ * @param isVolatile - Indicates whether the binding is volatile.
1855
+ */
1856
+ constructor(evaluate, isVolatile = false) {
1857
+ this.evaluate = evaluate;
1858
+ this.isVolatile = isVolatile;
1859
+ }
1833
1860
  }
1834
1861
  /**
1835
1862
  * The type of HTML aspect to target.
@@ -1929,13 +1956,6 @@ class StatelessAttachedAttributeDirective {
1929
1956
  */
1930
1957
  this.id = nextId();
1931
1958
  }
1932
- /**
1933
- * Creates a behavior.
1934
- * @param targets - The targets available for behaviors to be attached to.
1935
- */
1936
- createBehavior(targets) {
1937
- return this;
1938
- }
1939
1959
  /**
1940
1960
  * Creates a placeholder string based on the directive's index within the template.
1941
1961
  * @param index - The index of the directive within the template.
@@ -1945,6 +1965,13 @@ class StatelessAttachedAttributeDirective {
1945
1965
  createHTML(add) {
1946
1966
  return Markup.attribute(add(this));
1947
1967
  }
1968
+ /**
1969
+ * Creates a behavior.
1970
+ * @param targets - The targets available for behaviors to be attached to.
1971
+ */
1972
+ createBehavior() {
1973
+ return this;
1974
+ }
1948
1975
  }
1949
1976
 
1950
1977
  const createInnerHTMLBinding = globalThis.TrustedHTML
@@ -1957,29 +1984,19 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1957
1984
  }
1958
1985
  : (binding) => binding;
1959
1986
  class OnChangeBinding extends Binding {
1960
- constructor(evaluate, isVolatile) {
1961
- super();
1962
- this.evaluate = evaluate;
1963
- this.isVolatile = isVolatile;
1964
- }
1965
1987
  createObserver(_, subscriber) {
1966
1988
  return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1967
1989
  }
1968
1990
  }
1969
1991
  class OneTimeBinding extends Binding {
1970
- constructor(evaluate) {
1971
- super();
1972
- this.evaluate = evaluate;
1973
- }
1974
1992
  createObserver() {
1975
1993
  return this;
1976
1994
  }
1977
- observe(source, context) {
1978
- return this.evaluate(source, context);
1995
+ bind(controller) {
1996
+ return this.evaluate(controller.source, controller.context);
1979
1997
  }
1980
- dispose() { }
1981
1998
  }
1982
- function updateContentTarget(target, aspect, value, source, context) {
1999
+ function updateContent(target, aspect, value, controller) {
1983
2000
  // If there's no actual value, then this equates to the
1984
2001
  // empty string for the purposes of content bindings.
1985
2002
  if (value === null || value === undefined) {
@@ -2011,14 +2028,14 @@ function updateContentTarget(target, aspect, value, source, context) {
2011
2028
  // and that there's actually no need to compose it.
2012
2029
  if (!view.isComposed) {
2013
2030
  view.isComposed = true;
2014
- view.bind(source, context);
2031
+ view.bind(controller.source);
2015
2032
  view.insertBefore(target);
2016
2033
  target.$fastView = view;
2017
2034
  target.$fastTemplate = value;
2018
2035
  }
2019
2036
  else if (view.needsBindOnly) {
2020
2037
  view.needsBindOnly = false;
2021
- view.bind(source, context);
2038
+ view.bind(controller.source);
2022
2039
  }
2023
2040
  }
2024
2041
  else {
@@ -2038,10 +2055,9 @@ function updateContentTarget(target, aspect, value, source, context) {
2038
2055
  target.textContent = value;
2039
2056
  }
2040
2057
  }
2041
- function updateTokenListTarget(target, aspect, value) {
2058
+ function updateTokenList(target, aspect, value) {
2042
2059
  var _a;
2043
- const directive = this.directive;
2044
- const lookup = `${directive.id}-t`;
2060
+ const lookup = `${this.id}-t`;
2045
2061
  const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { c: 0, v: Object.create(null) });
2046
2062
  const versions = state.v;
2047
2063
  let currentVersion = state.c;
@@ -2071,154 +2087,8 @@ function updateTokenListTarget(target, aspect, value) {
2071
2087
  }
2072
2088
  }
2073
2089
  }
2074
- /**
2075
- * A binding behavior for bindings that change.
2076
- * @public
2077
- */
2078
- class BindingBehavior {
2079
- /**
2080
- * Creates an instance of ChangeBinding.
2081
- * @param directive - The directive that has the configuration for this behavior.
2082
- * @param updateTarget - The function used to update the target with the latest value.
2083
- */
2084
- constructor(directive, updateTarget) {
2085
- this.directive = directive;
2086
- this.updateTarget = updateTarget;
2087
- this.observerProperty = `${directive.id}-o`;
2088
- }
2089
- /**
2090
- * Bind this behavior to the source.
2091
- * @param source - The source to bind to.
2092
- * @param context - The execution context that the binding is operating within.
2093
- * @param targets - The targets that behaviors in a view can attach to.
2094
- */
2095
- bind(source, context, targets) {
2096
- const directive = this.directive;
2097
- const target = targets[directive.nodeId];
2098
- const observer = this.getObserver(target);
2099
- observer.target = target;
2100
- observer.source = source;
2101
- observer.context = context;
2102
- this.updateTarget(target, directive.targetAspect, observer.observe(source, context), source, context);
2103
- }
2104
- /**
2105
- * Unbinds this behavior from the source.
2106
- * @param source - The source to unbind from.
2107
- * @param context - The execution context that the binding is operating within.
2108
- * @param targets - The targets that behaviors in a view can attach to.
2109
- */
2110
- unbind(source, context, targets) {
2111
- const target = targets[this.directive.nodeId];
2112
- const observer = this.getObserver(target);
2113
- observer.dispose();
2114
- observer.target = null;
2115
- observer.source = null;
2116
- observer.context = null;
2117
- }
2118
- /** @internal */
2119
- handleChange(binding, observer) {
2120
- const target = observer.target;
2121
- const source = observer.source;
2122
- const context = observer.context;
2123
- this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
2124
- }
2125
- /**
2126
- * Returns the binding observer used to update the node.
2127
- * @param target - The target node.
2128
- * @returns A BindingObserver.
2129
- */
2130
- getObserver(target) {
2131
- var _a;
2132
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = this.directive.dataBinding.createObserver(this.directive, this)));
2133
- }
2134
- /**
2135
- * Creates a behavior.
2136
- * @param targets - The targets available for behaviors to be attached to.
2137
- */
2138
- createBehavior(targets) {
2139
- return this;
2140
- }
2141
- }
2142
- /**
2143
- * A special binding behavior that can bind node content.
2144
- * @public
2145
- */
2146
- class ContentBehavior extends BindingBehavior {
2147
- /**
2148
- * Unbinds this behavior from the source.
2149
- * @param source - The source to unbind from.
2150
- * @param context - The execution context that the binding is operating within.
2151
- * @param targets - The targets that behaviors in a view can attach to.
2152
- */
2153
- unbind(source, context, targets) {
2154
- super.unbind(source, context, targets);
2155
- const target = targets[this.directive.nodeId];
2156
- const view = target.$fastView;
2157
- if (view !== void 0 && view.isComposed) {
2158
- view.unbind();
2159
- view.needsBindOnly = true;
2160
- }
2161
- }
2162
- }
2163
- /**
2164
- * A binding behavior for handling events.
2165
- * @public
2166
- */
2167
- class EventBehavior {
2168
- /**
2169
- * Creates an instance of EventBinding.
2170
- * @param directive - The directive that has the configuration for this behavior.
2171
- */
2172
- constructor(directive) {
2173
- this.directive = directive;
2174
- this.sourceProperty = `${directive.id}-s`;
2175
- this.contextProperty = `${directive.id}-c`;
2176
- }
2177
- /**
2178
- * Bind this behavior to the source.
2179
- * @param source - The source to bind to.
2180
- * @param context - The execution context that the binding is operating within.
2181
- * @param targets - The targets that behaviors in a view can attach to.
2182
- */
2183
- bind(source, context, targets) {
2184
- const directive = this.directive;
2185
- const target = targets[directive.nodeId];
2186
- target[this.sourceProperty] = source;
2187
- target[this.contextProperty] = context;
2188
- target.addEventListener(directive.targetAspect, this, directive.dataBinding.options);
2189
- }
2190
- /**
2191
- * Unbinds this behavior from the source.
2192
- * @param source - The source to unbind from.
2193
- * @param context - The execution context that the binding is operating within.
2194
- * @param targets - The targets that behaviors in a view can attach to.
2195
- */
2196
- unbind(source, context, targets) {
2197
- const directive = this.directive;
2198
- const target = targets[directive.nodeId];
2199
- target[this.sourceProperty] = target[this.contextProperty] = null;
2200
- target.removeEventListener(directive.targetAspect, this, directive.dataBinding.options);
2201
- }
2202
- /**
2203
- * Creates a behavior.
2204
- * @param targets - The targets available for behaviors to be attached to.
2205
- */
2206
- createBehavior(targets) {
2207
- return this;
2208
- }
2209
- /**
2210
- * @internal
2211
- */
2212
- handleEvent(event) {
2213
- const target = event.currentTarget;
2214
- ExecutionContext.setEvent(event);
2215
- const result = this.directive.dataBinding.evaluate(target[this.sourceProperty], target[this.contextProperty]);
2216
- ExecutionContext.setEvent(null);
2217
- if (result !== true) {
2218
- event.preventDefault();
2219
- }
2220
- }
2221
- }
2090
+ const setProperty = (t, a, v) => (t[a] = v);
2091
+ const eventTarget = () => void 0;
2222
2092
  /**
2223
2093
  * A directive that applies bindings.
2224
2094
  * @public
@@ -2230,7 +2100,7 @@ class HTMLBindingDirective {
2230
2100
  */
2231
2101
  constructor(dataBinding) {
2232
2102
  this.dataBinding = dataBinding;
2233
- this.factory = null;
2103
+ this.updateTarget = null;
2234
2104
  /**
2235
2105
  * The unique id of the factory.
2236
2106
  */
@@ -2239,6 +2109,9 @@ class HTMLBindingDirective {
2239
2109
  * The type of aspect to target.
2240
2110
  */
2241
2111
  this.aspectType = Aspect.content;
2112
+ /** @internal */
2113
+ this.bind = this.bindDefault;
2114
+ this.data = `${this.id}-d`;
2242
2115
  }
2243
2116
  /**
2244
2117
  * Creates HTML to be used within a template.
@@ -2249,37 +2122,87 @@ class HTMLBindingDirective {
2249
2122
  }
2250
2123
  /**
2251
2124
  * Creates a behavior.
2252
- * @param targets - The targets available for behaviors to be attached to.
2253
2125
  */
2254
- createBehavior(targets) {
2255
- if (this.factory == null) {
2126
+ createBehavior() {
2127
+ if (this.updateTarget === null) {
2256
2128
  if (this.targetAspect === "innerHTML") {
2257
2129
  this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
2258
2130
  }
2259
2131
  switch (this.aspectType) {
2260
2132
  case 1:
2261
- this.factory = new BindingBehavior(this, DOM.setAttribute);
2133
+ this.updateTarget = DOM.setAttribute;
2262
2134
  break;
2263
2135
  case 2:
2264
- this.factory = new BindingBehavior(this, DOM.setBooleanAttribute);
2136
+ this.updateTarget = DOM.setBooleanAttribute;
2265
2137
  break;
2266
2138
  case 3:
2267
- this.factory = new BindingBehavior(this, (t, a, v) => (t[a] = v));
2139
+ this.updateTarget = setProperty;
2268
2140
  break;
2269
2141
  case 4:
2270
- this.factory = new ContentBehavior(this, updateContentTarget);
2142
+ this.bind = this.bindContent;
2143
+ this.updateTarget = updateContent;
2271
2144
  break;
2272
2145
  case 5:
2273
- this.factory = new BindingBehavior(this, updateTokenListTarget);
2146
+ this.updateTarget = updateTokenList;
2274
2147
  break;
2275
2148
  case 6:
2276
- this.factory = new EventBehavior(this);
2149
+ this.bind = this.bindEvent;
2150
+ this.updateTarget = eventTarget;
2277
2151
  break;
2278
2152
  default:
2279
2153
  throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2280
2154
  }
2281
2155
  }
2282
- return this.factory.createBehavior(targets);
2156
+ return this;
2157
+ }
2158
+ /** @internal */
2159
+ bindDefault(controller) {
2160
+ var _a;
2161
+ const target = controller.targets[this.nodeId];
2162
+ const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
2163
+ observer.target = target;
2164
+ observer.controller = controller;
2165
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2166
+ if (this.updateTarget === updateContent) {
2167
+ controller.onUnbind(this);
2168
+ }
2169
+ }
2170
+ /** @internal */
2171
+ bindContent(controller) {
2172
+ this.bindDefault(controller);
2173
+ controller.onUnbind(this);
2174
+ }
2175
+ /** @internal */
2176
+ bindEvent(controller) {
2177
+ const target = controller.targets[this.nodeId];
2178
+ target[this.data] = controller;
2179
+ target.addEventListener(this.targetAspect, this, this.dataBinding.options);
2180
+ }
2181
+ /** @internal */
2182
+ unbind(controller) {
2183
+ const target = controller.targets[this.nodeId];
2184
+ const view = target.$fastView;
2185
+ if (view !== void 0 && view.isComposed) {
2186
+ view.unbind();
2187
+ view.needsBindOnly = true;
2188
+ }
2189
+ }
2190
+ /** @internal */
2191
+ handleEvent(event) {
2192
+ const target = event.currentTarget;
2193
+ ExecutionContext.setEvent(event);
2194
+ const controller = target[this.data];
2195
+ const result = this.dataBinding.evaluate(controller.source, controller.context);
2196
+ ExecutionContext.setEvent(null);
2197
+ if (result !== true) {
2198
+ event.preventDefault();
2199
+ }
2200
+ }
2201
+ /** @internal */
2202
+ handleChange(binding, observer) {
2203
+ const target = observer.target;
2204
+ const controller = observer.controller;
2205
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2283
2206
  }
2284
2207
  }
2285
2208
  HTMLDirective.define(HTMLBindingDirective, { aspected: true });
@@ -2354,17 +2277,83 @@ class HTMLView {
2354
2277
  this.factories = factories;
2355
2278
  this.targets = targets;
2356
2279
  this.behaviors = null;
2280
+ this.unbindables = [];
2357
2281
  /**
2358
2282
  * The data that the view is bound to.
2359
2283
  */
2360
2284
  this.source = null;
2285
+ this.isBound = false;
2286
+ this.selfContained = false;
2361
2287
  /**
2362
- * The execution context the view is running within.
2288
+ * The index of the current item within a repeat context.
2289
+ */
2290
+ this.index = 0;
2291
+ /**
2292
+ * The length of the current collection within a repeat context.
2363
2293
  */
2364
- this.context = null;
2294
+ this.length = 0;
2365
2295
  this.firstChild = fragment.firstChild;
2366
2296
  this.lastChild = fragment.lastChild;
2367
2297
  }
2298
+ /**
2299
+ * The execution context the view is running within.
2300
+ */
2301
+ get context() {
2302
+ return this;
2303
+ }
2304
+ /**
2305
+ * The current event within an event handler.
2306
+ */
2307
+ get event() {
2308
+ return ExecutionContext.getEvent();
2309
+ }
2310
+ /**
2311
+ * Indicates whether the current item within a repeat context
2312
+ * has an even index.
2313
+ */
2314
+ get isEven() {
2315
+ return this.index % 2 === 0;
2316
+ }
2317
+ /**
2318
+ * Indicates whether the current item within a repeat context
2319
+ * has an odd index.
2320
+ */
2321
+ get isOdd() {
2322
+ return this.index % 2 !== 0;
2323
+ }
2324
+ /**
2325
+ * Indicates whether the current item within a repeat context
2326
+ * is the first item in the collection.
2327
+ */
2328
+ get isFirst() {
2329
+ return this.index === 0;
2330
+ }
2331
+ /**
2332
+ * Indicates whether the current item within a repeat context
2333
+ * is somewhere in the middle of the collection.
2334
+ */
2335
+ get isInMiddle() {
2336
+ return !this.isFirst && !this.isLast;
2337
+ }
2338
+ /**
2339
+ * Indicates whether the current item within a repeat context
2340
+ * is the last item in the collection.
2341
+ */
2342
+ get isLast() {
2343
+ return this.index === this.length - 1;
2344
+ }
2345
+ /**
2346
+ * Returns the typed event detail of a custom event.
2347
+ */
2348
+ eventDetail() {
2349
+ return this.event.detail;
2350
+ }
2351
+ /**
2352
+ * Returns the typed event target of the event.
2353
+ */
2354
+ eventTarget() {
2355
+ return this.event.target;
2356
+ }
2368
2357
  /**
2369
2358
  * Appends the view's DOM nodes to the referenced node.
2370
2359
  * @param node - The parent node to append the view's DOM nodes to.
@@ -2419,58 +2408,58 @@ class HTMLView {
2419
2408
  removeNodeSequence(this.firstChild, this.lastChild);
2420
2409
  this.unbind();
2421
2410
  }
2411
+ onUnbind(behavior) {
2412
+ this.unbindables.push(behavior);
2413
+ }
2422
2414
  /**
2423
2415
  * Binds a view's behaviors to its binding source.
2424
2416
  * @param source - The binding source for the view's binding behaviors.
2425
2417
  * @param context - The execution context to run the behaviors within.
2426
2418
  */
2427
- bind(source, context) {
2428
- let behaviors = this.behaviors;
2429
- const oldSource = this.source;
2430
- if (oldSource === source) {
2419
+ bind(source) {
2420
+ if (this.source === source) {
2431
2421
  return;
2432
2422
  }
2433
- this.source = source;
2434
- this.context = context;
2435
- const targets = this.targets;
2436
- if (oldSource !== null) {
2437
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2438
- const current = behaviors[i];
2439
- current.unbind(oldSource, context, targets);
2440
- current.bind(source, context, targets);
2441
- }
2442
- }
2443
- else if (behaviors === null) {
2423
+ let behaviors = this.behaviors;
2424
+ if (behaviors === null) {
2425
+ this.source = source;
2444
2426
  this.behaviors = behaviors = new Array(this.factories.length);
2445
2427
  const factories = this.factories;
2446
2428
  for (let i = 0, ii = factories.length; i < ii; ++i) {
2447
- const behavior = factories[i].createBehavior(targets);
2448
- behavior.bind(source, context, targets);
2429
+ const behavior = factories[i].createBehavior();
2430
+ behavior.bind(this);
2449
2431
  behaviors[i] = behavior;
2450
2432
  }
2451
2433
  }
2452
2434
  else {
2435
+ if (this.source !== null) {
2436
+ this.evaluateUnbindables();
2437
+ }
2438
+ this.isBound = false;
2439
+ this.source = source;
2453
2440
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2454
- behaviors[i].bind(source, context, targets);
2441
+ behaviors[i].bind(this);
2455
2442
  }
2456
2443
  }
2444
+ this.isBound = true;
2457
2445
  }
2458
2446
  /**
2459
2447
  * Unbinds a view's behaviors from its binding source.
2460
2448
  */
2461
2449
  unbind() {
2462
- const oldSource = this.source;
2463
- if (oldSource === null) {
2450
+ if (!this.isBound || this.source === null) {
2464
2451
  return;
2465
2452
  }
2466
- const targets = this.targets;
2467
- const context = this.context;
2468
- const behaviors = this.behaviors;
2469
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2470
- behaviors[i].unbind(oldSource, context, targets);
2471
- }
2453
+ this.evaluateUnbindables();
2472
2454
  this.source = null;
2473
- this.context = null;
2455
+ this.isBound = false;
2456
+ }
2457
+ evaluateUnbindables() {
2458
+ const unbindables = this.unbindables;
2459
+ for (let i = 0, ii = unbindables.length; i < ii; ++i) {
2460
+ unbindables[i].unbind(this);
2461
+ }
2462
+ unbindables.length = 0;
2474
2463
  }
2475
2464
  /**
2476
2465
  * Efficiently disposes of a contiguous range of synthetic view instances.
@@ -2486,6 +2475,8 @@ class HTMLView {
2486
2475
  }
2487
2476
  }
2488
2477
  }
2478
+ Observable.defineProperty(HTMLView.prototype, "index");
2479
+ Observable.defineProperty(HTMLView.prototype, "length");
2489
2480
 
2490
2481
  const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
2491
2482
  const descriptorCache = {};
@@ -2806,9 +2797,9 @@ class ViewTemplate {
2806
2797
  * @param hostBindingTarget - An HTML element to target the host bindings at if different from the
2807
2798
  * host that the template is being attached to.
2808
2799
  */
2809
- render(source, host, hostBindingTarget, context) {
2810
- const view = this.create(hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : host);
2811
- view.bind(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
2800
+ render(source, host, hostBindingTarget) {
2801
+ const view = this.create(hostBindingTarget);
2802
+ view.bind(source);
2812
2803
  view.appendTo(host);
2813
2804
  return view;
2814
2805
  }
@@ -2885,20 +2876,12 @@ function html(strings, ...values) {
2885
2876
  */
2886
2877
  class RefDirective extends StatelessAttachedAttributeDirective {
2887
2878
  /**
2888
- * Bind this behavior to the source.
2889
- * @param source - The source to bind to.
2890
- * @param context - The execution context that the binding is operating within.
2891
- * @param targets - The targets that behaviors in a view can attach to.
2879
+ * Bind this behavior.
2880
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2892
2881
  */
2893
- bind(source, context, targets) {
2894
- source[this.options] = targets[this.nodeId];
2882
+ bind(controller) {
2883
+ controller.source[this.options] = controller.targets[this.nodeId];
2895
2884
  }
2896
- /**
2897
- * Unbinds this behavior from the source.
2898
- * @param source - The source to unbind from.
2899
- */
2900
- /* eslint-disable-next-line @typescript-eslint/no-empty-function */
2901
- unbind() { }
2902
2885
  }
2903
2886
  HTMLDirective.define(RefDirective);
2904
2887
  /**
@@ -2927,11 +2910,17 @@ const defaultRepeatOptions = Object.freeze({
2927
2910
  positioning: false,
2928
2911
  recycle: true,
2929
2912
  });
2930
- function bindWithoutPositioning(view, items, index, context) {
2931
- view.bind(items[index], context);
2913
+ function bindWithoutPositioning(view, items, index, controller) {
2914
+ view.context.parent = controller.source;
2915
+ view.context.parentContext = controller.context;
2916
+ view.bind(items[index]);
2932
2917
  }
2933
- function bindWithPositioning(view, items, index, context) {
2934
- view.bind(items[index], context.createItemContext(index, items.length));
2918
+ function bindWithPositioning(view, items, index, controller) {
2919
+ view.context.parent = controller.source;
2920
+ view.context.parentContext = controller.context;
2921
+ view.context.length = items.length;
2922
+ view.context.index = index;
2923
+ view.bind(items[index]);
2935
2924
  }
2936
2925
  /**
2937
2926
  * A behavior that renders a template for each item in an array.
@@ -2947,15 +2936,11 @@ class RepeatBehavior {
2947
2936
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2948
2937
  * @param options - Options used to turn on special repeat features.
2949
2938
  */
2950
- constructor(directive, location) {
2939
+ constructor(directive) {
2951
2940
  this.directive = directive;
2952
- this.location = location;
2953
- this.source = null;
2954
2941
  this.views = [];
2955
2942
  this.items = null;
2956
2943
  this.itemsObserver = null;
2957
- this.context = void 0;
2958
- this.childContext = void 0;
2959
2944
  this.bindView = bindWithoutPositioning;
2960
2945
  this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2961
2946
  this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
@@ -2964,32 +2949,26 @@ class RepeatBehavior {
2964
2949
  }
2965
2950
  }
2966
2951
  /**
2967
- * Bind this behavior to the source.
2968
- * @param source - The source to bind to.
2969
- * @param context - The execution context that the binding is operating within.
2952
+ * Bind this behavior.
2953
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2970
2954
  */
2971
- bind(source, context) {
2972
- this.source = source;
2973
- this.context = context;
2974
- this.childContext = context.createChildContext(source);
2975
- this.items = this.itemsBindingObserver.observe(source, this.context);
2976
- this.template = this.templateBindingObserver.observe(source, this.context);
2955
+ bind(controller) {
2956
+ this.location = controller.targets[this.directive.nodeId];
2957
+ this.controller = controller;
2958
+ this.items = this.itemsBindingObserver.bind(controller);
2959
+ this.template = this.templateBindingObserver.bind(controller);
2977
2960
  this.observeItems(true);
2978
2961
  this.refreshAllViews();
2962
+ controller.onUnbind(this);
2979
2963
  }
2980
2964
  /**
2981
- * Unbinds this behavior from the source.
2982
- * @param source - The source to unbind from.
2965
+ * Unbinds this behavior.
2983
2966
  */
2984
2967
  unbind() {
2985
- this.source = null;
2986
- this.items = null;
2987
2968
  if (this.itemsObserver !== null) {
2988
2969
  this.itemsObserver.unsubscribe(this);
2989
2970
  }
2990
2971
  this.unbindAllViews();
2991
- this.itemsBindingObserver.dispose();
2992
- this.templateBindingObserver.dispose();
2993
2972
  }
2994
2973
  /**
2995
2974
  * Handles changes in the array, its items, and the repeat template.
@@ -2998,12 +2977,12 @@ class RepeatBehavior {
2998
2977
  */
2999
2978
  handleChange(source, args) {
3000
2979
  if (args === this.itemsBindingObserver) {
3001
- this.items = this.itemsBindingObserver.observe(this.source, this.context);
2980
+ this.items = this.itemsBindingObserver.bind(this.controller);
3002
2981
  this.observeItems();
3003
2982
  this.refreshAllViews();
3004
2983
  }
3005
2984
  else if (args === this.templateBindingObserver) {
3006
- this.template = this.templateBindingObserver.observe(this.source, this.context);
2985
+ this.template = this.templateBindingObserver.bind(this.controller);
3007
2986
  this.refreshAllViews(true);
3008
2987
  }
3009
2988
  else if (!args[0]) {
@@ -3033,10 +3012,10 @@ class RepeatBehavior {
3033
3012
  }
3034
3013
  updateViews(splices) {
3035
3014
  const views = this.views;
3036
- const childContext = this.childContext;
3037
3015
  const bindView = this.bindView;
3038
3016
  const items = this.items;
3039
3017
  const template = this.template;
3018
+ const controller = this.controller;
3040
3019
  const recycle = this.directive.options.recycle;
3041
3020
  const leftoverViews = [];
3042
3021
  let leftoverIndex = 0;
@@ -3048,13 +3027,14 @@ class RepeatBehavior {
3048
3027
  let addIndex = splice.index;
3049
3028
  const end = addIndex + splice.addedCount;
3050
3029
  const removedViews = views.splice(splice.index, removed.length);
3051
- availableViews = leftoverViews.length + removedViews.length;
3030
+ const totalAvailableViews = (availableViews =
3031
+ leftoverViews.length + removedViews.length);
3052
3032
  for (; addIndex < end; ++addIndex) {
3053
3033
  const neighbor = views[addIndex];
3054
3034
  const location = neighbor ? neighbor.firstChild : this.location;
3055
3035
  let view;
3056
3036
  if (recycle && availableViews > 0) {
3057
- if (removeIndex <= availableViews && removedViews.length > 0) {
3037
+ if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
3058
3038
  view = removedViews[removeIndex];
3059
3039
  removeIndex++;
3060
3040
  }
@@ -3068,7 +3048,7 @@ class RepeatBehavior {
3068
3048
  view = template.create();
3069
3049
  }
3070
3050
  views.splice(addIndex, 0, view);
3071
- bindView(view, items, addIndex, childContext);
3051
+ bindView(view, items, addIndex, controller);
3072
3052
  view.insertBefore(location);
3073
3053
  }
3074
3054
  if (removedViews[removeIndex]) {
@@ -3080,7 +3060,9 @@ class RepeatBehavior {
3080
3060
  }
3081
3061
  if (this.directive.options.positioning) {
3082
3062
  for (let i = 0, ii = views.length; i < ii; ++i) {
3083
- views[i].context.updatePosition(i, ii);
3063
+ const context = views[i].context;
3064
+ context.length = i;
3065
+ context.index = ii;
3084
3066
  }
3085
3067
  }
3086
3068
  }
@@ -3089,7 +3071,7 @@ class RepeatBehavior {
3089
3071
  const template = this.template;
3090
3072
  const location = this.location;
3091
3073
  const bindView = this.bindView;
3092
- const childContext = this.childContext;
3074
+ const controller = this.controller;
3093
3075
  let itemsLength = items.length;
3094
3076
  let views = this.views;
3095
3077
  let viewsLength = views.length;
@@ -3103,7 +3085,7 @@ class RepeatBehavior {
3103
3085
  this.views = views = new Array(itemsLength);
3104
3086
  for (let i = 0; i < itemsLength; ++i) {
3105
3087
  const view = template.create();
3106
- bindView(view, items, i, childContext);
3088
+ bindView(view, items, i, controller);
3107
3089
  views[i] = view;
3108
3090
  view.insertBefore(location);
3109
3091
  }
@@ -3114,11 +3096,11 @@ class RepeatBehavior {
3114
3096
  for (; i < itemsLength; ++i) {
3115
3097
  if (i < viewsLength) {
3116
3098
  const view = views[i];
3117
- bindView(view, items, i, childContext);
3099
+ bindView(view, items, i, controller);
3118
3100
  }
3119
3101
  else {
3120
3102
  const view = template.create();
3121
- bindView(view, items, i, childContext);
3103
+ bindView(view, items, i, controller);
3122
3104
  views.push(view);
3123
3105
  view.insertBefore(location);
3124
3106
  }
@@ -3168,8 +3150,8 @@ class RepeatDirective {
3168
3150
  * Creates a behavior for the provided target node.
3169
3151
  * @param target - The node instance to create the behavior for.
3170
3152
  */
3171
- createBehavior(targets) {
3172
- return new RepeatBehavior(this, targets[this.nodeId]);
3153
+ createBehavior() {
3154
+ return new RepeatBehavior(this);
3173
3155
  }
3174
3156
  }
3175
3157
  HTMLDirective.define(RepeatDirective);
@@ -3213,11 +3195,12 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3213
3195
  * @param context - The execution context that the binding is operating within.
3214
3196
  * @param targets - The targets that behaviors in a view can attach to.
3215
3197
  */
3216
- bind(source, context, targets) {
3217
- const target = targets[this.nodeId];
3218
- target[this.sourceProperty] = source;
3219
- this.updateTarget(source, this.computeNodes(target));
3198
+ bind(controller) {
3199
+ const target = controller.targets[this.nodeId];
3200
+ target[this.sourceProperty] = controller.source;
3201
+ this.updateTarget(controller.source, this.computeNodes(target));
3220
3202
  this.observe(target);
3203
+ controller.onUnbind(this);
3221
3204
  }
3222
3205
  /**
3223
3206
  * Unbinds this behavior from the source.
@@ -3225,9 +3208,9 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3225
3208
  * @param context - The execution context that the binding is operating within.
3226
3209
  * @param targets - The targets that behaviors in a view can attach to.
3227
3210
  */
3228
- unbind(source, context, targets) {
3229
- const target = targets[this.nodeId];
3230
- this.updateTarget(source, emptyArray);
3211
+ unbind(controller) {
3212
+ const target = controller.targets[this.nodeId];
3213
+ this.updateTarget(controller.source, emptyArray);
3231
3214
  this.disconnect(target);
3232
3215
  target[this.sourceProperty] = null;
3233
3216
  }
@@ -3376,6 +3359,16 @@ function children(propertyOrOptions) {
3376
3359
 
3377
3360
  const booleanMode = "boolean";
3378
3361
  const reflectMode = "reflect";
3362
+ /**
3363
+ * Metadata used to configure a custom attribute's behavior.
3364
+ * @public
3365
+ */
3366
+ const AttributeConfiguration = Object.freeze({
3367
+ /**
3368
+ * Locates all attribute configurations associated with a type.
3369
+ */
3370
+ locate: createMetadataLocator(),
3371
+ });
3379
3372
  /**
3380
3373
  * A {@link ValueConverter} that converts to and from `boolean` values.
3381
3374
  * @remarks
@@ -3513,7 +3506,7 @@ class AttributeDefinition {
3513
3506
  */
3514
3507
  static collect(Owner, ...attributeLists) {
3515
3508
  const attributes = [];
3516
- attributeLists.push(Owner.attributes);
3509
+ attributeLists.push(AttributeConfiguration.locate(Owner));
3517
3510
  for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
3518
3511
  const list = attributeLists[i];
3519
3512
  if (list === void 0) {
@@ -3543,9 +3536,7 @@ function attr(configOrTarget, prop) {
3543
3536
  // - @attr({...opts})
3544
3537
  config.property = $prop;
3545
3538
  }
3546
- const attributes = $target.constructor.attributes ||
3547
- ($target.constructor.attributes = []);
3548
- attributes.push(config);
3539
+ AttributeConfiguration.locate($target.constructor).push(config);
3549
3540
  }
3550
3541
  if (arguments.length > 1) {
3551
3542
  // Non invocation:
@@ -3570,6 +3561,7 @@ const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */
3570
3561
  */
3571
3562
  class FASTElementDefinition {
3572
3563
  constructor(type, nameOrConfig = type.definition) {
3564
+ var _a;
3573
3565
  this.platformDefined = false;
3574
3566
  if (isString(nameOrConfig)) {
3575
3567
  nameOrConfig = { name: nameOrConfig };
@@ -3577,6 +3569,7 @@ class FASTElementDefinition {
3577
3569
  this.type = type;
3578
3570
  this.name = nameOrConfig.name;
3579
3571
  this.template = nameOrConfig.template;
3572
+ this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
3580
3573
  const proto = type.prototype;
3581
3574
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3582
3575
  const observedAttributes = new Array(attributes.length);
@@ -3621,7 +3614,7 @@ class FASTElementDefinition {
3621
3614
  * @remarks
3622
3615
  * This operation is idempotent per registry.
3623
3616
  */
3624
- define(registry = customElements) {
3617
+ define(registry = this.registry) {
3625
3618
  const type = this.type;
3626
3619
  if (!registry.get(this.name)) {
3627
3620
  this.platformDefined = true;
@@ -3655,22 +3648,22 @@ FASTElementDefinition.getByType = fastElementRegistry.getByType;
3655
3648
  */
3656
3649
  FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
3657
3650
 
3658
- const shadowRoots = new WeakMap();
3659
3651
  const defaultEventOptions = {
3660
3652
  bubbles: true,
3661
3653
  composed: true,
3662
3654
  cancelable: true,
3663
3655
  };
3656
+ const isConnectedPropertyName = "isConnected";
3657
+ const shadowRoots = new WeakMap();
3664
3658
  function getShadowRoot(element) {
3665
3659
  var _a, _b;
3666
3660
  return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
3667
3661
  }
3668
- const isConnectedPropertyName = "isConnected";
3669
3662
  /**
3670
3663
  * Controls the lifecycle and rendering of a `FASTElement`.
3671
3664
  * @public
3672
3665
  */
3673
- class Controller extends PropertyChangeNotifier {
3666
+ class ElementController extends PropertyChangeNotifier {
3674
3667
  /**
3675
3668
  * Creates a Controller to control the specified element.
3676
3669
  * @param element - The element to be controlled by this controller.
@@ -3681,12 +3674,12 @@ class Controller extends PropertyChangeNotifier {
3681
3674
  constructor(element, definition) {
3682
3675
  super(element);
3683
3676
  this.boundObservables = null;
3684
- this.behaviors = null;
3685
3677
  this.needsInitialization = true;
3686
3678
  this.hasExistingShadowRoot = false;
3687
3679
  this._template = null;
3688
- this._styles = null;
3689
3680
  this._isConnected = false;
3681
+ this.behaviors = null;
3682
+ this._mainStyles = null;
3690
3683
  /**
3691
3684
  * This allows Observable.getNotifier(...) to return the Controller
3692
3685
  * when the notifier for the Controller itself is being requested. The
@@ -3702,7 +3695,7 @@ class Controller extends PropertyChangeNotifier {
3702
3695
  * If `null` then the element is managing its own rendering.
3703
3696
  */
3704
3697
  this.view = null;
3705
- this.element = element;
3698
+ this.source = element;
3706
3699
  this.definition = definition;
3707
3700
  const shadowOptions = definition.shadowOptions;
3708
3701
  if (shadowOptions !== void 0) {
@@ -3756,9 +3749,9 @@ class Controller extends PropertyChangeNotifier {
3756
3749
  // 1. Template overrides take top precedence.
3757
3750
  if (this._template === null) {
3758
3751
  const definition = this.definition;
3759
- if (this.element.resolveTemplate) {
3752
+ if (this.source.resolveTemplate) {
3760
3753
  // 2. Allow for element instance overrides next.
3761
- this._template = this.element.resolveTemplate();
3754
+ this._template = this.source.resolveTemplate();
3762
3755
  }
3763
3756
  else if (definition.template) {
3764
3757
  // 3. Default to the static definition.
@@ -3777,48 +3770,92 @@ class Controller extends PropertyChangeNotifier {
3777
3770
  }
3778
3771
  }
3779
3772
  /**
3780
- * Gets/sets the primary styles used for the component.
3781
- * @remarks
3782
- * This value can only be accurately read after connect but can be set at any time.
3773
+ * The main set of styles used for the component, independent
3774
+ * of any dynamically added styles.
3783
3775
  */
3784
- get styles() {
3776
+ get mainStyles() {
3785
3777
  var _a;
3786
3778
  // 1. Styles overrides take top precedence.
3787
- if (this._styles === null) {
3779
+ if (this._mainStyles === null) {
3788
3780
  const definition = this.definition;
3789
- if (this.element.resolveStyles) {
3781
+ if (this.source.resolveStyles) {
3790
3782
  // 2. Allow for element instance overrides next.
3791
- this._styles = this.element.resolveStyles();
3783
+ this._mainStyles = this.source.resolveStyles();
3792
3784
  }
3793
3785
  else if (definition.styles) {
3794
3786
  // 3. Default to the static definition.
3795
- this._styles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3787
+ this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3796
3788
  }
3797
3789
  }
3798
- return this._styles;
3790
+ return this._mainStyles;
3799
3791
  }
3800
- set styles(value) {
3801
- if (this._styles === value) {
3792
+ set mainStyles(value) {
3793
+ if (this._mainStyles === value) {
3802
3794
  return;
3803
3795
  }
3804
- if (this._styles !== null) {
3805
- this.removeStyles(this._styles);
3796
+ if (this._mainStyles !== null) {
3797
+ this.removeStyles(this._mainStyles);
3806
3798
  }
3807
- this._styles = value;
3799
+ this._mainStyles = value;
3808
3800
  if (!this.needsInitialization) {
3809
3801
  this.addStyles(value);
3810
3802
  }
3811
3803
  }
3804
+ /**
3805
+ * Adds the behavior to the component.
3806
+ * @param behavior - The behavior to add.
3807
+ */
3808
+ addBehavior(behavior) {
3809
+ var _a, _b;
3810
+ const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3811
+ const count = (_b = targetBehaviors.get(behavior)) !== null && _b !== void 0 ? _b : 0;
3812
+ if (count === 0) {
3813
+ targetBehaviors.set(behavior, 1);
3814
+ behavior.addedCallback && behavior.addedCallback(this);
3815
+ if (behavior.connectedCallback && this.isConnected) {
3816
+ behavior.connectedCallback(this);
3817
+ }
3818
+ }
3819
+ else {
3820
+ targetBehaviors.set(behavior, count + 1);
3821
+ }
3822
+ }
3823
+ /**
3824
+ * Removes the behavior from the component.
3825
+ * @param behavior - The behavior to remove.
3826
+ * @param force - Forces removal even if this behavior was added more than once.
3827
+ */
3828
+ removeBehavior(behavior, force = false) {
3829
+ const targetBehaviors = this.behaviors;
3830
+ if (targetBehaviors === null) {
3831
+ return;
3832
+ }
3833
+ const count = targetBehaviors.get(behavior);
3834
+ if (count === void 0) {
3835
+ return;
3836
+ }
3837
+ if (count === 1 || force) {
3838
+ targetBehaviors.delete(behavior);
3839
+ if (behavior.disconnectedCallback && this.isConnected) {
3840
+ behavior.disconnectedCallback(this);
3841
+ }
3842
+ behavior.removedCallback && behavior.removedCallback(this);
3843
+ }
3844
+ else {
3845
+ targetBehaviors.set(behavior, count - 1);
3846
+ }
3847
+ }
3812
3848
  /**
3813
3849
  * Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot.
3814
3850
  * @param styles - The styles to add.
3815
3851
  */
3816
3852
  addStyles(styles) {
3853
+ var _a;
3817
3854
  if (!styles) {
3818
3855
  return;
3819
3856
  }
3820
- const target = getShadowRoot(this.element) ||
3821
- this.element.getRootNode();
3857
+ const source = this.source;
3858
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
3822
3859
  if (styles instanceof HTMLElement) {
3823
3860
  target.append(styles);
3824
3861
  }
@@ -3826,7 +3863,9 @@ class Controller extends PropertyChangeNotifier {
3826
3863
  const sourceBehaviors = styles.behaviors;
3827
3864
  styles.addStylesTo(target);
3828
3865
  if (sourceBehaviors !== null) {
3829
- this.addBehaviors(sourceBehaviors);
3866
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3867
+ this.addBehavior(sourceBehaviors[i]);
3868
+ }
3830
3869
  }
3831
3870
  }
3832
3871
  }
@@ -3835,11 +3874,12 @@ class Controller extends PropertyChangeNotifier {
3835
3874
  * @param styles - the styles to remove.
3836
3875
  */
3837
3876
  removeStyles(styles) {
3877
+ var _a;
3838
3878
  if (!styles) {
3839
3879
  return;
3840
3880
  }
3841
- const target = getShadowRoot(this.element) ||
3842
- this.element.getRootNode();
3881
+ const source = this.source;
3882
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
3843
3883
  if (styles instanceof HTMLElement) {
3844
3884
  target.removeChild(styles);
3845
3885
  }
@@ -3847,85 +3887,29 @@ class Controller extends PropertyChangeNotifier {
3847
3887
  const sourceBehaviors = styles.behaviors;
3848
3888
  styles.removeStylesFrom(target);
3849
3889
  if (sourceBehaviors !== null) {
3850
- this.removeBehaviors(sourceBehaviors);
3851
- }
3852
- }
3853
- }
3854
- /**
3855
- * Adds behaviors to this element.
3856
- * @param behaviors - The behaviors to add.
3857
- */
3858
- addBehaviors(behaviors) {
3859
- var _a;
3860
- const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3861
- const length = behaviors.length;
3862
- const behaviorsToBind = [];
3863
- for (let i = 0; i < length; ++i) {
3864
- const behavior = behaviors[i];
3865
- if (targetBehaviors.has(behavior)) {
3866
- targetBehaviors.set(behavior, targetBehaviors.get(behavior) + 1);
3867
- }
3868
- else {
3869
- targetBehaviors.set(behavior, 1);
3870
- behaviorsToBind.push(behavior);
3871
- }
3872
- }
3873
- if (this._isConnected) {
3874
- const element = this.element;
3875
- const context = ExecutionContext.default;
3876
- for (let i = 0; i < behaviorsToBind.length; ++i) {
3877
- behaviorsToBind[i].bind(element, context);
3878
- }
3879
- }
3880
- }
3881
- /**
3882
- * Removes behaviors from this element.
3883
- * @param behaviors - The behaviors to remove.
3884
- * @param force - Forces unbinding of behaviors.
3885
- */
3886
- removeBehaviors(behaviors, force = false) {
3887
- const targetBehaviors = this.behaviors;
3888
- if (targetBehaviors === null) {
3889
- return;
3890
- }
3891
- const length = behaviors.length;
3892
- const behaviorsToUnbind = [];
3893
- for (let i = 0; i < length; ++i) {
3894
- const behavior = behaviors[i];
3895
- if (targetBehaviors.has(behavior)) {
3896
- const count = targetBehaviors.get(behavior) - 1;
3897
- count === 0 || force
3898
- ? targetBehaviors.delete(behavior) && behaviorsToUnbind.push(behavior)
3899
- : targetBehaviors.set(behavior, count);
3900
- }
3901
- }
3902
- if (this._isConnected) {
3903
- const element = this.element;
3904
- const context = ExecutionContext.default;
3905
- for (let i = 0; i < behaviorsToUnbind.length; ++i) {
3906
- behaviorsToUnbind[i].unbind(element, context);
3890
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3891
+ this.addBehavior(sourceBehaviors[i]);
3892
+ }
3907
3893
  }
3908
3894
  }
3909
3895
  }
3910
3896
  /**
3911
3897
  * Runs connected lifecycle behavior on the associated element.
3912
3898
  */
3913
- onConnectedCallback() {
3899
+ connect() {
3914
3900
  if (this._isConnected) {
3915
3901
  return;
3916
3902
  }
3917
- const element = this.element;
3918
- const context = ExecutionContext.default;
3919
3903
  if (this.needsInitialization) {
3920
3904
  this.finishInitialization();
3921
3905
  }
3922
3906
  else if (this.view !== null) {
3923
- this.view.bind(element, context);
3907
+ this.view.bind(this.source);
3924
3908
  }
3925
3909
  const behaviors = this.behaviors;
3926
3910
  if (behaviors !== null) {
3927
- for (const behavior of behaviors.keys()) {
3928
- behavior.bind(element, context);
3911
+ for (const key of behaviors.keys()) {
3912
+ key.connectedCallback && key.connectedCallback(this);
3929
3913
  }
3930
3914
  }
3931
3915
  this.setIsConnected(true);
@@ -3933,21 +3917,18 @@ class Controller extends PropertyChangeNotifier {
3933
3917
  /**
3934
3918
  * Runs disconnected lifecycle behavior on the associated element.
3935
3919
  */
3936
- onDisconnectedCallback() {
3920
+ disconnect() {
3937
3921
  if (!this._isConnected) {
3938
3922
  return;
3939
3923
  }
3940
3924
  this.setIsConnected(false);
3941
- const view = this.view;
3942
- if (view !== null) {
3943
- view.unbind();
3925
+ if (this.view !== null) {
3926
+ this.view.unbind();
3944
3927
  }
3945
3928
  const behaviors = this.behaviors;
3946
3929
  if (behaviors !== null) {
3947
- const element = this.element;
3948
- const context = ExecutionContext.default;
3949
- for (const behavior of behaviors.keys()) {
3950
- behavior.unbind(element, context);
3930
+ for (const key of behaviors.keys()) {
3931
+ key.disconnectedCallback && key.disconnectedCallback(this);
3951
3932
  }
3952
3933
  }
3953
3934
  }
@@ -3960,7 +3941,7 @@ class Controller extends PropertyChangeNotifier {
3960
3941
  onAttributeChangedCallback(name, oldValue, newValue) {
3961
3942
  const attrDef = this.definition.attributeLookup[name];
3962
3943
  if (attrDef !== void 0) {
3963
- attrDef.onAttributeChangedCallback(this.element, newValue);
3944
+ attrDef.onAttributeChangedCallback(this.source, newValue);
3964
3945
  }
3965
3946
  }
3966
3947
  /**
@@ -3973,12 +3954,12 @@ class Controller extends PropertyChangeNotifier {
3973
3954
  */
3974
3955
  emit(type, detail, options) {
3975
3956
  if (this._isConnected) {
3976
- return this.element.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3957
+ return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3977
3958
  }
3978
3959
  return false;
3979
3960
  }
3980
3961
  finishInitialization() {
3981
- const element = this.element;
3962
+ const element = this.source;
3982
3963
  const boundObservables = this.boundObservables;
3983
3964
  // If we have any observables that were bound, re-apply their values.
3984
3965
  if (boundObservables !== null) {
@@ -3990,15 +3971,15 @@ class Controller extends PropertyChangeNotifier {
3990
3971
  this.boundObservables = null;
3991
3972
  }
3992
3973
  this.renderTemplate(this.template);
3993
- this.addStyles(this.styles);
3974
+ this.addStyles(this.mainStyles);
3994
3975
  this.needsInitialization = false;
3995
3976
  }
3996
3977
  renderTemplate(template) {
3997
3978
  var _a;
3998
- const element = this.element;
3999
3979
  // When getting the host to render to, we start by looking
4000
3980
  // up the shadow root. If there isn't one, then that means
4001
3981
  // we're doing a Light DOM render to the element's direct children.
3982
+ const element = this.source;
4002
3983
  const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
4003
3984
  if (this.view !== null) {
4004
3985
  // If there's already a view, we need to unbind and remove through dispose.
@@ -4015,6 +3996,8 @@ class Controller extends PropertyChangeNotifier {
4015
3996
  if (template) {
4016
3997
  // If a new template was provided, render it.
4017
3998
  this.view = template.render(element, host, element);
3999
+ this.view.sourceLifetime =
4000
+ SourceLifetime.coupled;
4018
4001
  }
4019
4002
  }
4020
4003
  /**
@@ -4034,7 +4017,7 @@ class Controller extends PropertyChangeNotifier {
4034
4017
  if (definition === void 0) {
4035
4018
  throw FAST.error(1401 /* Message.missingElementDefinition */);
4036
4019
  }
4037
- return (element.$fastController = new Controller(element, definition));
4020
+ return (element.$fastController = new ElementController(element, definition));
4038
4021
  }
4039
4022
  }
4040
4023
 
@@ -4044,16 +4027,16 @@ function createFASTElement(BaseType) {
4044
4027
  constructor() {
4045
4028
  /* eslint-disable-next-line */
4046
4029
  super();
4047
- Controller.forCustomElement(this);
4030
+ ElementController.forCustomElement(this);
4048
4031
  }
4049
4032
  $emit(type, detail, options) {
4050
4033
  return this.$fastController.emit(type, detail, options);
4051
4034
  }
4052
4035
  connectedCallback() {
4053
- this.$fastController.onConnectedCallback();
4036
+ this.$fastController.connect();
4054
4037
  }
4055
4038
  disconnectedCallback() {
4056
- this.$fastController.onDisconnectedCallback();
4039
+ this.$fastController.disconnect();
4057
4040
  }
4058
4041
  attributeChangedCallback(name, oldValue, newValue) {
4059
4042
  this.$fastController.onAttributeChangedCallback(name, oldValue, newValue);
@@ -4113,4 +4096,4 @@ function customElement(nameOrDef) {
4113
4096
  };
4114
4097
  }
4115
4098
 
4116
- export { AdoptedStyleSheetsStrategy, ArrayObserver, Aspect, AttributeDefinition, Binding, BindingBehavior, CSSDirective, ChildrenDirective, Compiler, ContentBehavior, Controller, DOM, ElementStyles, EventBehavior, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewTemplate, attr, bind, booleanConverter, children, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };
4099
+ export { AdoptedStyleSheetsStrategy, 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 };