@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
@@ -143,10 +143,34 @@ function createTypeRegistry() {
143
143
  return typeToDefinition.get(key);
144
144
  },
145
145
  getForInstance(object) {
146
+ if (object === null || object === void 0) {
147
+ return void 0;
148
+ }
146
149
  return typeToDefinition.get(object.constructor);
147
150
  },
148
151
  });
149
152
  }
153
+ /**
154
+ * Creates a function capable of locating metadata associated with a type.
155
+ * @returns A metadata locator function.
156
+ * @internal
157
+ */
158
+ function createMetadataLocator() {
159
+ const metadataLookup = new WeakMap();
160
+ return function (target) {
161
+ let metadata = metadataLookup.get(target);
162
+ if (metadata === void 0) {
163
+ let currentTarget = Reflect.getPrototypeOf(target);
164
+ while (metadata === void 0 && currentTarget !== null) {
165
+ metadata = metadataLookup.get(currentTarget);
166
+ currentTarget = Reflect.getPrototypeOf(currentTarget);
167
+ }
168
+ metadata = metadata === void 0 ? [] : metadata.slice(0);
169
+ metadataLookup.set(target, metadata);
170
+ }
171
+ return metadata;
172
+ };
173
+ }
150
174
 
151
175
  /**
152
176
  * @internal
@@ -388,6 +412,21 @@ class PropertyChangeNotifier {
388
412
  }
389
413
  }
390
414
 
415
+ /**
416
+ * Describes how the source's lifetime relates to its controller's lifetime.
417
+ * @public
418
+ */
419
+ const SourceLifetime = Object.freeze({
420
+ /**
421
+ * The source to controller lifetime relationship is unknown.
422
+ */
423
+ unknown: void 0,
424
+ /**
425
+ * The source and controller lifetimes are coupled to one another.
426
+ * They can/will be GC'd together.
427
+ */
428
+ coupled: 1,
429
+ });
391
430
  /**
392
431
  * Common Observable APIs.
393
432
  * @public
@@ -396,7 +435,6 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
396
435
  const queueUpdate = Updates.enqueue;
397
436
  const volatileRegex = /(:|&&|\|\||if)/;
398
437
  const notifierLookup = new WeakMap();
399
- const accessorLookup = new WeakMap();
400
438
  let watcher = void 0;
401
439
  let createArrayObserver = (array) => {
402
440
  throw FAST.error(1101 /* Message.needsArrayObservation */);
@@ -411,19 +449,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
411
449
  }
412
450
  return found;
413
451
  }
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
- }
452
+ const getAccessors = createMetadataLocator();
427
453
  class DefaultObservableAccessor {
428
454
  constructor(name) {
429
455
  this.name = name;
@@ -467,6 +493,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
467
493
  setMode(isAsync) {
468
494
  this.isAsync = this.needsQueue = isAsync;
469
495
  }
496
+ bind(controller) {
497
+ this.controller = controller;
498
+ const value = this.observe(controller.source, controller.context);
499
+ if (!controller.isBound && this.requiresUnbind(controller)) {
500
+ controller.onUnbind(this);
501
+ }
502
+ return value;
503
+ }
504
+ requiresUnbind(controller) {
505
+ return (controller.sourceLifetime !== SourceLifetime.coupled ||
506
+ this.first !== this.last ||
507
+ this.first.propertySource !== controller.source);
508
+ }
509
+ unbind(controller) {
510
+ this.dispose();
511
+ }
470
512
  observe(source, context) {
471
513
  if (this.needsRefresh && this.last !== null) {
472
514
  this.dispose();
@@ -476,7 +518,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
476
518
  this.needsRefresh = this.isVolatileBinding;
477
519
  let result;
478
520
  try {
479
- result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
521
+ result = this.binding(source, context);
480
522
  }
481
523
  finally {
482
524
  watcher = previousWatcher;
@@ -667,123 +709,38 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
667
709
  * Provides additional contextual information available to behaviors and expressions.
668
710
  * @public
669
711
  */
670
- class ExecutionContext {
671
- constructor(parentSource = null, parentContext = null) {
672
- /**
673
- * The index of the current item within a repeat context.
674
- */
675
- this.index = 0;
676
- /**
677
- * The length of the current collection within a repeat context.
678
- */
679
- this.length = 0;
680
- this.parent = parentSource;
681
- this.parentContext = parentContext;
682
- }
712
+ const ExecutionContext = Object.freeze({
683
713
  /**
684
- * The current event within an event handler.
714
+ * A default execution context.
685
715
  */
686
- get event() {
687
- return contextEvent.get();
688
- }
689
- /**
690
- * Indicates whether the current item within a repeat context
691
- * has an even index.
692
- */
693
- get isEven() {
694
- return this.index % 2 === 0;
695
- }
696
- /**
697
- * Indicates whether the current item within a repeat context
698
- * has an odd index.
699
- */
700
- get isOdd() {
701
- return this.index % 2 !== 0;
702
- }
703
- /**
704
- * Indicates whether the current item within a repeat context
705
- * is the first item in the collection.
706
- */
707
- get isFirst() {
708
- return this.index === 0;
709
- }
710
- /**
711
- * Indicates whether the current item within a repeat context
712
- * is somewhere in the middle of the collection.
713
- */
714
- get isInMiddle() {
715
- return !this.isFirst && !this.isLast;
716
- }
717
- /**
718
- * Indicates whether the current item within a repeat context
719
- * is the last item in the collection.
720
- */
721
- get isLast() {
722
- return this.index === this.length - 1;
723
- }
724
- /**
725
- * Returns the typed event detail of a custom event.
726
- */
727
- eventDetail() {
728
- return this.event.detail;
729
- }
730
- /**
731
- * Returns the typed event target of the event.
732
- */
733
- eventTarget() {
734
- return this.event.target;
735
- }
736
- /**
737
- * Updates the position/size on a context associated with a list item.
738
- * @param index - The new index of the item.
739
- * @param length - The new length of the list.
740
- */
741
- updatePosition(index, length) {
742
- this.index = index;
743
- this.length = length;
744
- }
745
- /**
746
- * Creates a new execution context descendent from the current context.
747
- * @param source - The source for the context if different than the parent.
748
- * @returns A child execution context.
749
- */
750
- createChildContext(parentSource) {
751
- return new ExecutionContext(parentSource, this);
752
- }
716
+ default: {
717
+ index: 0,
718
+ length: 0,
719
+ get event() {
720
+ return ExecutionContext.getEvent();
721
+ },
722
+ eventDetail() {
723
+ return this.event.detail;
724
+ },
725
+ eventTarget() {
726
+ return this.event.target;
727
+ },
728
+ },
753
729
  /**
754
- * Creates a new execution context descent suitable for use in list rendering.
755
- * @param item - The list item to serve as the source.
756
- * @param index - The index of the item in the list.
757
- * @param length - The length of the list.
730
+ * Gets the current event.
731
+ * @returns An event object.
758
732
  */
759
- createItemContext(index, length) {
760
- const childContext = Object.create(this);
761
- childContext.index = index;
762
- childContext.length = length;
763
- return childContext;
764
- }
733
+ getEvent() {
734
+ return contextEvent.get();
735
+ },
765
736
  /**
766
- * Sets the event for the current execution context.
767
- * @param event - The event to set.
768
- * @internal
737
+ * Sets the current event.
738
+ * @param event - An event object.
769
739
  */
770
- static setEvent(event) {
740
+ setEvent(event) {
771
741
  contextEvent.set(event);
772
- }
773
- /**
774
- * Creates a new root execution context.
775
- * @returns A new execution context.
776
- */
777
- static create() {
778
- return new ExecutionContext();
779
- }
780
- }
781
- /**
782
- * The default execution context.
783
- */
784
- ExecutionContext.default = new ExecutionContext();
785
- Observable.defineProperty(ExecutionContext.prototype, "index");
786
- Observable.defineProperty(ExecutionContext.prototype, "length");
742
+ },
743
+ });
787
744
 
788
745
  /**
789
746
  * A splice map is a representation of how a previous array of items
@@ -1581,11 +1538,11 @@ class CSSPartial {
1581
1538
  }
1582
1539
  return this.css;
1583
1540
  }
1584
- bind(el) {
1585
- el.$fastController.addStyles(this.styles);
1541
+ addedCallback(controller) {
1542
+ controller.addStyles(this.styles);
1586
1543
  }
1587
- unbind(el) {
1588
- el.$fastController.removeStyles(this.styles);
1544
+ removedCallback(controller) {
1545
+ controller.removeStyles(this.styles);
1589
1546
  }
1590
1547
  }
1591
1548
  CSSDirective.define(CSSPartial);
@@ -1724,6 +1681,67 @@ const Parser = Object.freeze({
1724
1681
  },
1725
1682
  });
1726
1683
 
1684
+ /**
1685
+ * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
1686
+ * control ViewBehaviors.
1687
+ * @public
1688
+ */
1689
+ const ViewBehaviorOrchestrator = Object.freeze({
1690
+ /**
1691
+ * Creates a ViewBehaviorOrchestrator.
1692
+ * @param source - The source to to associate behaviors with.
1693
+ * @returns A ViewBehaviorOrchestrator.
1694
+ */
1695
+ create(source) {
1696
+ const behaviors = [];
1697
+ const targets = {};
1698
+ let unbindables = null;
1699
+ let isConnected = false;
1700
+ return {
1701
+ source,
1702
+ context: ExecutionContext.default,
1703
+ targets,
1704
+ get isBound() {
1705
+ return isConnected;
1706
+ },
1707
+ addBehaviorFactory(factory, target) {
1708
+ const nodeId = factory.nodeId || (factory.nodeId = nextId());
1709
+ factory.id || (factory.id = nextId());
1710
+ this.addTarget(nodeId, target);
1711
+ this.addBehavior(factory.createBehavior());
1712
+ },
1713
+ addTarget(nodeId, target) {
1714
+ targets[nodeId] = target;
1715
+ },
1716
+ addBehavior(behavior) {
1717
+ behaviors.push(behavior);
1718
+ if (isConnected) {
1719
+ behavior.bind(this);
1720
+ }
1721
+ },
1722
+ onUnbind(unbindable) {
1723
+ if (unbindables === null) {
1724
+ unbindables = [];
1725
+ }
1726
+ unbindables.push(unbindable);
1727
+ },
1728
+ connectedCallback(controller) {
1729
+ if (!isConnected) {
1730
+ isConnected = true;
1731
+ behaviors.forEach(x => x.bind(this));
1732
+ }
1733
+ },
1734
+ disconnectedCallback(controller) {
1735
+ if (isConnected) {
1736
+ isConnected = false;
1737
+ if (unbindables !== null) {
1738
+ unbindables.forEach(x => x.unbind(this));
1739
+ }
1740
+ }
1741
+ },
1742
+ };
1743
+ },
1744
+ });
1727
1745
  const registry = createTypeRegistry();
1728
1746
  /**
1729
1747
  * Instructs the template engine to apply behavior to a node.
@@ -1769,6 +1787,15 @@ function htmlDirective(options) {
1769
1787
  * @public
1770
1788
  */
1771
1789
  class Binding {
1790
+ /**
1791
+ * Creates a binding.
1792
+ * @param evaluate - Evaluates the binding.
1793
+ * @param isVolatile - Indicates whether the binding is volatile.
1794
+ */
1795
+ constructor(evaluate, isVolatile = false) {
1796
+ this.evaluate = evaluate;
1797
+ this.isVolatile = isVolatile;
1798
+ }
1772
1799
  }
1773
1800
  /**
1774
1801
  * The type of HTML aspect to target.
@@ -1868,13 +1895,6 @@ class StatelessAttachedAttributeDirective {
1868
1895
  */
1869
1896
  this.id = nextId();
1870
1897
  }
1871
- /**
1872
- * Creates a behavior.
1873
- * @param targets - The targets available for behaviors to be attached to.
1874
- */
1875
- createBehavior(targets) {
1876
- return this;
1877
- }
1878
1898
  /**
1879
1899
  * Creates a placeholder string based on the directive's index within the template.
1880
1900
  * @param index - The index of the directive within the template.
@@ -1884,6 +1904,13 @@ class StatelessAttachedAttributeDirective {
1884
1904
  createHTML(add) {
1885
1905
  return Markup.attribute(add(this));
1886
1906
  }
1907
+ /**
1908
+ * Creates a behavior.
1909
+ * @param targets - The targets available for behaviors to be attached to.
1910
+ */
1911
+ createBehavior() {
1912
+ return this;
1913
+ }
1887
1914
  }
1888
1915
 
1889
1916
  const createInnerHTMLBinding = globalThis.TrustedHTML
@@ -1896,29 +1923,19 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1896
1923
  }
1897
1924
  : (binding) => binding;
1898
1925
  class OnChangeBinding extends Binding {
1899
- constructor(evaluate, isVolatile) {
1900
- super();
1901
- this.evaluate = evaluate;
1902
- this.isVolatile = isVolatile;
1903
- }
1904
1926
  createObserver(_, subscriber) {
1905
1927
  return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1906
1928
  }
1907
1929
  }
1908
1930
  class OneTimeBinding extends Binding {
1909
- constructor(evaluate) {
1910
- super();
1911
- this.evaluate = evaluate;
1912
- }
1913
1931
  createObserver() {
1914
1932
  return this;
1915
1933
  }
1916
- observe(source, context) {
1917
- return this.evaluate(source, context);
1934
+ bind(controller) {
1935
+ return this.evaluate(controller.source, controller.context);
1918
1936
  }
1919
- dispose() { }
1920
1937
  }
1921
- function updateContentTarget(target, aspect, value, source, context) {
1938
+ function updateContent(target, aspect, value, controller) {
1922
1939
  // If there's no actual value, then this equates to the
1923
1940
  // empty string for the purposes of content bindings.
1924
1941
  if (value === null || value === undefined) {
@@ -1950,14 +1967,14 @@ function updateContentTarget(target, aspect, value, source, context) {
1950
1967
  // and that there's actually no need to compose it.
1951
1968
  if (!view.isComposed) {
1952
1969
  view.isComposed = true;
1953
- view.bind(source, context);
1970
+ view.bind(controller.source);
1954
1971
  view.insertBefore(target);
1955
1972
  target.$fastView = view;
1956
1973
  target.$fastTemplate = value;
1957
1974
  }
1958
1975
  else if (view.needsBindOnly) {
1959
1976
  view.needsBindOnly = false;
1960
- view.bind(source, context);
1977
+ view.bind(controller.source);
1961
1978
  }
1962
1979
  }
1963
1980
  else {
@@ -1977,10 +1994,9 @@ function updateContentTarget(target, aspect, value, source, context) {
1977
1994
  target.textContent = value;
1978
1995
  }
1979
1996
  }
1980
- function updateTokenListTarget(target, aspect, value) {
1997
+ function updateTokenList(target, aspect, value) {
1981
1998
  var _a;
1982
- const directive = this.directive;
1983
- const lookup = `${directive.id}-t`;
1999
+ const lookup = `${this.id}-t`;
1984
2000
  const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { c: 0, v: Object.create(null) });
1985
2001
  const versions = state.v;
1986
2002
  let currentVersion = state.c;
@@ -2010,154 +2026,8 @@ function updateTokenListTarget(target, aspect, value) {
2010
2026
  }
2011
2027
  }
2012
2028
  }
2013
- /**
2014
- * A binding behavior for bindings that change.
2015
- * @public
2016
- */
2017
- class BindingBehavior {
2018
- /**
2019
- * Creates an instance of ChangeBinding.
2020
- * @param directive - The directive that has the configuration for this behavior.
2021
- * @param updateTarget - The function used to update the target with the latest value.
2022
- */
2023
- constructor(directive, updateTarget) {
2024
- this.directive = directive;
2025
- this.updateTarget = updateTarget;
2026
- this.observerProperty = `${directive.id}-o`;
2027
- }
2028
- /**
2029
- * Bind this behavior to the source.
2030
- * @param source - The source to bind to.
2031
- * @param context - The execution context that the binding is operating within.
2032
- * @param targets - The targets that behaviors in a view can attach to.
2033
- */
2034
- bind(source, context, targets) {
2035
- const directive = this.directive;
2036
- const target = targets[directive.nodeId];
2037
- const observer = this.getObserver(target);
2038
- observer.target = target;
2039
- observer.source = source;
2040
- observer.context = context;
2041
- this.updateTarget(target, directive.targetAspect, observer.observe(source, context), source, context);
2042
- }
2043
- /**
2044
- * Unbinds this behavior from the source.
2045
- * @param source - The source to unbind from.
2046
- * @param context - The execution context that the binding is operating within.
2047
- * @param targets - The targets that behaviors in a view can attach to.
2048
- */
2049
- unbind(source, context, targets) {
2050
- const target = targets[this.directive.nodeId];
2051
- const observer = this.getObserver(target);
2052
- observer.dispose();
2053
- observer.target = null;
2054
- observer.source = null;
2055
- observer.context = null;
2056
- }
2057
- /** @internal */
2058
- handleChange(binding, observer) {
2059
- const target = observer.target;
2060
- const source = observer.source;
2061
- const context = observer.context;
2062
- this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
2063
- }
2064
- /**
2065
- * Returns the binding observer used to update the node.
2066
- * @param target - The target node.
2067
- * @returns A BindingObserver.
2068
- */
2069
- getObserver(target) {
2070
- var _a;
2071
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = this.directive.dataBinding.createObserver(this.directive, this)));
2072
- }
2073
- /**
2074
- * Creates a behavior.
2075
- * @param targets - The targets available for behaviors to be attached to.
2076
- */
2077
- createBehavior(targets) {
2078
- return this;
2079
- }
2080
- }
2081
- /**
2082
- * A special binding behavior that can bind node content.
2083
- * @public
2084
- */
2085
- class ContentBehavior extends BindingBehavior {
2086
- /**
2087
- * Unbinds this behavior from the source.
2088
- * @param source - The source to unbind from.
2089
- * @param context - The execution context that the binding is operating within.
2090
- * @param targets - The targets that behaviors in a view can attach to.
2091
- */
2092
- unbind(source, context, targets) {
2093
- super.unbind(source, context, targets);
2094
- const target = targets[this.directive.nodeId];
2095
- const view = target.$fastView;
2096
- if (view !== void 0 && view.isComposed) {
2097
- view.unbind();
2098
- view.needsBindOnly = true;
2099
- }
2100
- }
2101
- }
2102
- /**
2103
- * A binding behavior for handling events.
2104
- * @public
2105
- */
2106
- class EventBehavior {
2107
- /**
2108
- * Creates an instance of EventBinding.
2109
- * @param directive - The directive that has the configuration for this behavior.
2110
- */
2111
- constructor(directive) {
2112
- this.directive = directive;
2113
- this.sourceProperty = `${directive.id}-s`;
2114
- this.contextProperty = `${directive.id}-c`;
2115
- }
2116
- /**
2117
- * Bind this behavior to the source.
2118
- * @param source - The source to bind to.
2119
- * @param context - The execution context that the binding is operating within.
2120
- * @param targets - The targets that behaviors in a view can attach to.
2121
- */
2122
- bind(source, context, targets) {
2123
- const directive = this.directive;
2124
- const target = targets[directive.nodeId];
2125
- target[this.sourceProperty] = source;
2126
- target[this.contextProperty] = context;
2127
- target.addEventListener(directive.targetAspect, this, directive.dataBinding.options);
2128
- }
2129
- /**
2130
- * Unbinds this behavior from the source.
2131
- * @param source - The source to unbind from.
2132
- * @param context - The execution context that the binding is operating within.
2133
- * @param targets - The targets that behaviors in a view can attach to.
2134
- */
2135
- unbind(source, context, targets) {
2136
- const directive = this.directive;
2137
- const target = targets[directive.nodeId];
2138
- target[this.sourceProperty] = target[this.contextProperty] = null;
2139
- target.removeEventListener(directive.targetAspect, this, directive.dataBinding.options);
2140
- }
2141
- /**
2142
- * Creates a behavior.
2143
- * @param targets - The targets available for behaviors to be attached to.
2144
- */
2145
- createBehavior(targets) {
2146
- return this;
2147
- }
2148
- /**
2149
- * @internal
2150
- */
2151
- handleEvent(event) {
2152
- const target = event.currentTarget;
2153
- ExecutionContext.setEvent(event);
2154
- const result = this.directive.dataBinding.evaluate(target[this.sourceProperty], target[this.contextProperty]);
2155
- ExecutionContext.setEvent(null);
2156
- if (result !== true) {
2157
- event.preventDefault();
2158
- }
2159
- }
2160
- }
2029
+ const setProperty = (t, a, v) => (t[a] = v);
2030
+ const eventTarget = () => void 0;
2161
2031
  /**
2162
2032
  * A directive that applies bindings.
2163
2033
  * @public
@@ -2169,7 +2039,7 @@ class HTMLBindingDirective {
2169
2039
  */
2170
2040
  constructor(dataBinding) {
2171
2041
  this.dataBinding = dataBinding;
2172
- this.factory = null;
2042
+ this.updateTarget = null;
2173
2043
  /**
2174
2044
  * The unique id of the factory.
2175
2045
  */
@@ -2178,6 +2048,9 @@ class HTMLBindingDirective {
2178
2048
  * The type of aspect to target.
2179
2049
  */
2180
2050
  this.aspectType = Aspect.content;
2051
+ /** @internal */
2052
+ this.bind = this.bindDefault;
2053
+ this.data = `${this.id}-d`;
2181
2054
  }
2182
2055
  /**
2183
2056
  * Creates HTML to be used within a template.
@@ -2188,37 +2061,87 @@ class HTMLBindingDirective {
2188
2061
  }
2189
2062
  /**
2190
2063
  * Creates a behavior.
2191
- * @param targets - The targets available for behaviors to be attached to.
2192
2064
  */
2193
- createBehavior(targets) {
2194
- if (this.factory == null) {
2065
+ createBehavior() {
2066
+ if (this.updateTarget === null) {
2195
2067
  if (this.targetAspect === "innerHTML") {
2196
2068
  this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
2197
2069
  }
2198
2070
  switch (this.aspectType) {
2199
2071
  case 1:
2200
- this.factory = new BindingBehavior(this, DOM.setAttribute);
2072
+ this.updateTarget = DOM.setAttribute;
2201
2073
  break;
2202
2074
  case 2:
2203
- this.factory = new BindingBehavior(this, DOM.setBooleanAttribute);
2075
+ this.updateTarget = DOM.setBooleanAttribute;
2204
2076
  break;
2205
2077
  case 3:
2206
- this.factory = new BindingBehavior(this, (t, a, v) => (t[a] = v));
2078
+ this.updateTarget = setProperty;
2207
2079
  break;
2208
2080
  case 4:
2209
- this.factory = new ContentBehavior(this, updateContentTarget);
2081
+ this.bind = this.bindContent;
2082
+ this.updateTarget = updateContent;
2210
2083
  break;
2211
2084
  case 5:
2212
- this.factory = new BindingBehavior(this, updateTokenListTarget);
2085
+ this.updateTarget = updateTokenList;
2213
2086
  break;
2214
2087
  case 6:
2215
- this.factory = new EventBehavior(this);
2088
+ this.bind = this.bindEvent;
2089
+ this.updateTarget = eventTarget;
2216
2090
  break;
2217
2091
  default:
2218
2092
  throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2219
2093
  }
2220
2094
  }
2221
- return this.factory.createBehavior(targets);
2095
+ return this;
2096
+ }
2097
+ /** @internal */
2098
+ bindDefault(controller) {
2099
+ var _a;
2100
+ const target = controller.targets[this.nodeId];
2101
+ const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
2102
+ observer.target = target;
2103
+ observer.controller = controller;
2104
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2105
+ if (this.updateTarget === updateContent) {
2106
+ controller.onUnbind(this);
2107
+ }
2108
+ }
2109
+ /** @internal */
2110
+ bindContent(controller) {
2111
+ this.bindDefault(controller);
2112
+ controller.onUnbind(this);
2113
+ }
2114
+ /** @internal */
2115
+ bindEvent(controller) {
2116
+ const target = controller.targets[this.nodeId];
2117
+ target[this.data] = controller;
2118
+ target.addEventListener(this.targetAspect, this, this.dataBinding.options);
2119
+ }
2120
+ /** @internal */
2121
+ unbind(controller) {
2122
+ const target = controller.targets[this.nodeId];
2123
+ const view = target.$fastView;
2124
+ if (view !== void 0 && view.isComposed) {
2125
+ view.unbind();
2126
+ view.needsBindOnly = true;
2127
+ }
2128
+ }
2129
+ /** @internal */
2130
+ handleEvent(event) {
2131
+ const target = event.currentTarget;
2132
+ ExecutionContext.setEvent(event);
2133
+ const controller = target[this.data];
2134
+ const result = this.dataBinding.evaluate(controller.source, controller.context);
2135
+ ExecutionContext.setEvent(null);
2136
+ if (result !== true) {
2137
+ event.preventDefault();
2138
+ }
2139
+ }
2140
+ /** @internal */
2141
+ handleChange(binding, observer) {
2142
+ const target = observer.target;
2143
+ const controller = observer.controller;
2144
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2222
2145
  }
2223
2146
  }
2224
2147
  HTMLDirective.define(HTMLBindingDirective, { aspected: true });
@@ -2293,17 +2216,83 @@ class HTMLView {
2293
2216
  this.factories = factories;
2294
2217
  this.targets = targets;
2295
2218
  this.behaviors = null;
2219
+ this.unbindables = [];
2296
2220
  /**
2297
2221
  * The data that the view is bound to.
2298
2222
  */
2299
2223
  this.source = null;
2224
+ this.isBound = false;
2225
+ this.selfContained = false;
2300
2226
  /**
2301
- * The execution context the view is running within.
2227
+ * The index of the current item within a repeat context.
2228
+ */
2229
+ this.index = 0;
2230
+ /**
2231
+ * The length of the current collection within a repeat context.
2302
2232
  */
2303
- this.context = null;
2233
+ this.length = 0;
2304
2234
  this.firstChild = fragment.firstChild;
2305
2235
  this.lastChild = fragment.lastChild;
2306
2236
  }
2237
+ /**
2238
+ * The execution context the view is running within.
2239
+ */
2240
+ get context() {
2241
+ return this;
2242
+ }
2243
+ /**
2244
+ * The current event within an event handler.
2245
+ */
2246
+ get event() {
2247
+ return ExecutionContext.getEvent();
2248
+ }
2249
+ /**
2250
+ * Indicates whether the current item within a repeat context
2251
+ * has an even index.
2252
+ */
2253
+ get isEven() {
2254
+ return this.index % 2 === 0;
2255
+ }
2256
+ /**
2257
+ * Indicates whether the current item within a repeat context
2258
+ * has an odd index.
2259
+ */
2260
+ get isOdd() {
2261
+ return this.index % 2 !== 0;
2262
+ }
2263
+ /**
2264
+ * Indicates whether the current item within a repeat context
2265
+ * is the first item in the collection.
2266
+ */
2267
+ get isFirst() {
2268
+ return this.index === 0;
2269
+ }
2270
+ /**
2271
+ * Indicates whether the current item within a repeat context
2272
+ * is somewhere in the middle of the collection.
2273
+ */
2274
+ get isInMiddle() {
2275
+ return !this.isFirst && !this.isLast;
2276
+ }
2277
+ /**
2278
+ * Indicates whether the current item within a repeat context
2279
+ * is the last item in the collection.
2280
+ */
2281
+ get isLast() {
2282
+ return this.index === this.length - 1;
2283
+ }
2284
+ /**
2285
+ * Returns the typed event detail of a custom event.
2286
+ */
2287
+ eventDetail() {
2288
+ return this.event.detail;
2289
+ }
2290
+ /**
2291
+ * Returns the typed event target of the event.
2292
+ */
2293
+ eventTarget() {
2294
+ return this.event.target;
2295
+ }
2307
2296
  /**
2308
2297
  * Appends the view's DOM nodes to the referenced node.
2309
2298
  * @param node - The parent node to append the view's DOM nodes to.
@@ -2358,58 +2347,58 @@ class HTMLView {
2358
2347
  removeNodeSequence(this.firstChild, this.lastChild);
2359
2348
  this.unbind();
2360
2349
  }
2350
+ onUnbind(behavior) {
2351
+ this.unbindables.push(behavior);
2352
+ }
2361
2353
  /**
2362
2354
  * Binds a view's behaviors to its binding source.
2363
2355
  * @param source - The binding source for the view's binding behaviors.
2364
2356
  * @param context - The execution context to run the behaviors within.
2365
2357
  */
2366
- bind(source, context) {
2367
- let behaviors = this.behaviors;
2368
- const oldSource = this.source;
2369
- if (oldSource === source) {
2358
+ bind(source) {
2359
+ if (this.source === source) {
2370
2360
  return;
2371
2361
  }
2372
- this.source = source;
2373
- this.context = context;
2374
- const targets = this.targets;
2375
- if (oldSource !== null) {
2376
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2377
- const current = behaviors[i];
2378
- current.unbind(oldSource, context, targets);
2379
- current.bind(source, context, targets);
2380
- }
2381
- }
2382
- else if (behaviors === null) {
2362
+ let behaviors = this.behaviors;
2363
+ if (behaviors === null) {
2364
+ this.source = source;
2383
2365
  this.behaviors = behaviors = new Array(this.factories.length);
2384
2366
  const factories = this.factories;
2385
2367
  for (let i = 0, ii = factories.length; i < ii; ++i) {
2386
- const behavior = factories[i].createBehavior(targets);
2387
- behavior.bind(source, context, targets);
2368
+ const behavior = factories[i].createBehavior();
2369
+ behavior.bind(this);
2388
2370
  behaviors[i] = behavior;
2389
2371
  }
2390
2372
  }
2391
2373
  else {
2374
+ if (this.source !== null) {
2375
+ this.evaluateUnbindables();
2376
+ }
2377
+ this.isBound = false;
2378
+ this.source = source;
2392
2379
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2393
- behaviors[i].bind(source, context, targets);
2380
+ behaviors[i].bind(this);
2394
2381
  }
2395
2382
  }
2383
+ this.isBound = true;
2396
2384
  }
2397
2385
  /**
2398
2386
  * Unbinds a view's behaviors from its binding source.
2399
2387
  */
2400
2388
  unbind() {
2401
- const oldSource = this.source;
2402
- if (oldSource === null) {
2389
+ if (!this.isBound || this.source === null) {
2403
2390
  return;
2404
2391
  }
2405
- const targets = this.targets;
2406
- const context = this.context;
2407
- const behaviors = this.behaviors;
2408
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2409
- behaviors[i].unbind(oldSource, context, targets);
2410
- }
2392
+ this.evaluateUnbindables();
2411
2393
  this.source = null;
2412
- this.context = null;
2394
+ this.isBound = false;
2395
+ }
2396
+ evaluateUnbindables() {
2397
+ const unbindables = this.unbindables;
2398
+ for (let i = 0, ii = unbindables.length; i < ii; ++i) {
2399
+ unbindables[i].unbind(this);
2400
+ }
2401
+ unbindables.length = 0;
2413
2402
  }
2414
2403
  /**
2415
2404
  * Efficiently disposes of a contiguous range of synthetic view instances.
@@ -2425,6 +2414,8 @@ class HTMLView {
2425
2414
  }
2426
2415
  }
2427
2416
  }
2417
+ Observable.defineProperty(HTMLView.prototype, "index");
2418
+ Observable.defineProperty(HTMLView.prototype, "length");
2428
2419
 
2429
2420
  const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
2430
2421
  const descriptorCache = {};
@@ -2745,9 +2736,9 @@ class ViewTemplate {
2745
2736
  * @param hostBindingTarget - An HTML element to target the host bindings at if different from the
2746
2737
  * host that the template is being attached to.
2747
2738
  */
2748
- render(source, host, hostBindingTarget, context) {
2749
- const view = this.create(hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : host);
2750
- view.bind(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
2739
+ render(source, host, hostBindingTarget) {
2740
+ const view = this.create(hostBindingTarget);
2741
+ view.bind(source);
2751
2742
  view.appendTo(host);
2752
2743
  return view;
2753
2744
  }
@@ -2824,20 +2815,12 @@ function html(strings, ...values) {
2824
2815
  */
2825
2816
  class RefDirective extends StatelessAttachedAttributeDirective {
2826
2817
  /**
2827
- * Bind this behavior to the source.
2828
- * @param source - The source to bind to.
2829
- * @param context - The execution context that the binding is operating within.
2830
- * @param targets - The targets that behaviors in a view can attach to.
2818
+ * Bind this behavior.
2819
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2831
2820
  */
2832
- bind(source, context, targets) {
2833
- source[this.options] = targets[this.nodeId];
2821
+ bind(controller) {
2822
+ controller.source[this.options] = controller.targets[this.nodeId];
2834
2823
  }
2835
- /**
2836
- * Unbinds this behavior from the source.
2837
- * @param source - The source to unbind from.
2838
- */
2839
- /* eslint-disable-next-line @typescript-eslint/no-empty-function */
2840
- unbind() { }
2841
2824
  }
2842
2825
  HTMLDirective.define(RefDirective);
2843
2826
  /**
@@ -2866,11 +2849,17 @@ const defaultRepeatOptions = Object.freeze({
2866
2849
  positioning: false,
2867
2850
  recycle: true,
2868
2851
  });
2869
- function bindWithoutPositioning(view, items, index, context) {
2870
- view.bind(items[index], context);
2852
+ function bindWithoutPositioning(view, items, index, controller) {
2853
+ view.context.parent = controller.source;
2854
+ view.context.parentContext = controller.context;
2855
+ view.bind(items[index]);
2871
2856
  }
2872
- function bindWithPositioning(view, items, index, context) {
2873
- view.bind(items[index], context.createItemContext(index, items.length));
2857
+ function bindWithPositioning(view, items, index, controller) {
2858
+ view.context.parent = controller.source;
2859
+ view.context.parentContext = controller.context;
2860
+ view.context.length = items.length;
2861
+ view.context.index = index;
2862
+ view.bind(items[index]);
2874
2863
  }
2875
2864
  /**
2876
2865
  * A behavior that renders a template for each item in an array.
@@ -2886,15 +2875,11 @@ class RepeatBehavior {
2886
2875
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2887
2876
  * @param options - Options used to turn on special repeat features.
2888
2877
  */
2889
- constructor(directive, location) {
2878
+ constructor(directive) {
2890
2879
  this.directive = directive;
2891
- this.location = location;
2892
- this.source = null;
2893
2880
  this.views = [];
2894
2881
  this.items = null;
2895
2882
  this.itemsObserver = null;
2896
- this.context = void 0;
2897
- this.childContext = void 0;
2898
2883
  this.bindView = bindWithoutPositioning;
2899
2884
  this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2900
2885
  this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
@@ -2903,32 +2888,26 @@ class RepeatBehavior {
2903
2888
  }
2904
2889
  }
2905
2890
  /**
2906
- * Bind this behavior to the source.
2907
- * @param source - The source to bind to.
2908
- * @param context - The execution context that the binding is operating within.
2891
+ * Bind this behavior.
2892
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2909
2893
  */
2910
- bind(source, context) {
2911
- this.source = source;
2912
- this.context = context;
2913
- this.childContext = context.createChildContext(source);
2914
- this.items = this.itemsBindingObserver.observe(source, this.context);
2915
- this.template = this.templateBindingObserver.observe(source, this.context);
2894
+ bind(controller) {
2895
+ this.location = controller.targets[this.directive.nodeId];
2896
+ this.controller = controller;
2897
+ this.items = this.itemsBindingObserver.bind(controller);
2898
+ this.template = this.templateBindingObserver.bind(controller);
2916
2899
  this.observeItems(true);
2917
2900
  this.refreshAllViews();
2901
+ controller.onUnbind(this);
2918
2902
  }
2919
2903
  /**
2920
- * Unbinds this behavior from the source.
2921
- * @param source - The source to unbind from.
2904
+ * Unbinds this behavior.
2922
2905
  */
2923
2906
  unbind() {
2924
- this.source = null;
2925
- this.items = null;
2926
2907
  if (this.itemsObserver !== null) {
2927
2908
  this.itemsObserver.unsubscribe(this);
2928
2909
  }
2929
2910
  this.unbindAllViews();
2930
- this.itemsBindingObserver.dispose();
2931
- this.templateBindingObserver.dispose();
2932
2911
  }
2933
2912
  /**
2934
2913
  * Handles changes in the array, its items, and the repeat template.
@@ -2937,12 +2916,12 @@ class RepeatBehavior {
2937
2916
  */
2938
2917
  handleChange(source, args) {
2939
2918
  if (args === this.itemsBindingObserver) {
2940
- this.items = this.itemsBindingObserver.observe(this.source, this.context);
2919
+ this.items = this.itemsBindingObserver.bind(this.controller);
2941
2920
  this.observeItems();
2942
2921
  this.refreshAllViews();
2943
2922
  }
2944
2923
  else if (args === this.templateBindingObserver) {
2945
- this.template = this.templateBindingObserver.observe(this.source, this.context);
2924
+ this.template = this.templateBindingObserver.bind(this.controller);
2946
2925
  this.refreshAllViews(true);
2947
2926
  }
2948
2927
  else if (!args[0]) {
@@ -2972,10 +2951,10 @@ class RepeatBehavior {
2972
2951
  }
2973
2952
  updateViews(splices) {
2974
2953
  const views = this.views;
2975
- const childContext = this.childContext;
2976
2954
  const bindView = this.bindView;
2977
2955
  const items = this.items;
2978
2956
  const template = this.template;
2957
+ const controller = this.controller;
2979
2958
  const recycle = this.directive.options.recycle;
2980
2959
  const leftoverViews = [];
2981
2960
  let leftoverIndex = 0;
@@ -2987,13 +2966,14 @@ class RepeatBehavior {
2987
2966
  let addIndex = splice.index;
2988
2967
  const end = addIndex + splice.addedCount;
2989
2968
  const removedViews = views.splice(splice.index, removed.length);
2990
- availableViews = leftoverViews.length + removedViews.length;
2969
+ const totalAvailableViews = (availableViews =
2970
+ leftoverViews.length + removedViews.length);
2991
2971
  for (; addIndex < end; ++addIndex) {
2992
2972
  const neighbor = views[addIndex];
2993
2973
  const location = neighbor ? neighbor.firstChild : this.location;
2994
2974
  let view;
2995
2975
  if (recycle && availableViews > 0) {
2996
- if (removeIndex <= availableViews && removedViews.length > 0) {
2976
+ if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
2997
2977
  view = removedViews[removeIndex];
2998
2978
  removeIndex++;
2999
2979
  }
@@ -3007,7 +2987,7 @@ class RepeatBehavior {
3007
2987
  view = template.create();
3008
2988
  }
3009
2989
  views.splice(addIndex, 0, view);
3010
- bindView(view, items, addIndex, childContext);
2990
+ bindView(view, items, addIndex, controller);
3011
2991
  view.insertBefore(location);
3012
2992
  }
3013
2993
  if (removedViews[removeIndex]) {
@@ -3019,7 +2999,9 @@ class RepeatBehavior {
3019
2999
  }
3020
3000
  if (this.directive.options.positioning) {
3021
3001
  for (let i = 0, ii = views.length; i < ii; ++i) {
3022
- views[i].context.updatePosition(i, ii);
3002
+ const context = views[i].context;
3003
+ context.length = i;
3004
+ context.index = ii;
3023
3005
  }
3024
3006
  }
3025
3007
  }
@@ -3028,7 +3010,7 @@ class RepeatBehavior {
3028
3010
  const template = this.template;
3029
3011
  const location = this.location;
3030
3012
  const bindView = this.bindView;
3031
- const childContext = this.childContext;
3013
+ const controller = this.controller;
3032
3014
  let itemsLength = items.length;
3033
3015
  let views = this.views;
3034
3016
  let viewsLength = views.length;
@@ -3042,7 +3024,7 @@ class RepeatBehavior {
3042
3024
  this.views = views = new Array(itemsLength);
3043
3025
  for (let i = 0; i < itemsLength; ++i) {
3044
3026
  const view = template.create();
3045
- bindView(view, items, i, childContext);
3027
+ bindView(view, items, i, controller);
3046
3028
  views[i] = view;
3047
3029
  view.insertBefore(location);
3048
3030
  }
@@ -3053,11 +3035,11 @@ class RepeatBehavior {
3053
3035
  for (; i < itemsLength; ++i) {
3054
3036
  if (i < viewsLength) {
3055
3037
  const view = views[i];
3056
- bindView(view, items, i, childContext);
3038
+ bindView(view, items, i, controller);
3057
3039
  }
3058
3040
  else {
3059
3041
  const view = template.create();
3060
- bindView(view, items, i, childContext);
3042
+ bindView(view, items, i, controller);
3061
3043
  views.push(view);
3062
3044
  view.insertBefore(location);
3063
3045
  }
@@ -3107,8 +3089,8 @@ class RepeatDirective {
3107
3089
  * Creates a behavior for the provided target node.
3108
3090
  * @param target - The node instance to create the behavior for.
3109
3091
  */
3110
- createBehavior(targets) {
3111
- return new RepeatBehavior(this, targets[this.nodeId]);
3092
+ createBehavior() {
3093
+ return new RepeatBehavior(this);
3112
3094
  }
3113
3095
  }
3114
3096
  HTMLDirective.define(RepeatDirective);
@@ -3152,11 +3134,12 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3152
3134
  * @param context - The execution context that the binding is operating within.
3153
3135
  * @param targets - The targets that behaviors in a view can attach to.
3154
3136
  */
3155
- bind(source, context, targets) {
3156
- const target = targets[this.nodeId];
3157
- target[this.sourceProperty] = source;
3158
- this.updateTarget(source, this.computeNodes(target));
3137
+ bind(controller) {
3138
+ const target = controller.targets[this.nodeId];
3139
+ target[this.sourceProperty] = controller.source;
3140
+ this.updateTarget(controller.source, this.computeNodes(target));
3159
3141
  this.observe(target);
3142
+ controller.onUnbind(this);
3160
3143
  }
3161
3144
  /**
3162
3145
  * Unbinds this behavior from the source.
@@ -3164,9 +3147,9 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3164
3147
  * @param context - The execution context that the binding is operating within.
3165
3148
  * @param targets - The targets that behaviors in a view can attach to.
3166
3149
  */
3167
- unbind(source, context, targets) {
3168
- const target = targets[this.nodeId];
3169
- this.updateTarget(source, emptyArray);
3150
+ unbind(controller) {
3151
+ const target = controller.targets[this.nodeId];
3152
+ this.updateTarget(controller.source, emptyArray);
3170
3153
  this.disconnect(target);
3171
3154
  target[this.sourceProperty] = null;
3172
3155
  }
@@ -3315,6 +3298,16 @@ function children(propertyOrOptions) {
3315
3298
 
3316
3299
  const booleanMode = "boolean";
3317
3300
  const reflectMode = "reflect";
3301
+ /**
3302
+ * Metadata used to configure a custom attribute's behavior.
3303
+ * @public
3304
+ */
3305
+ const AttributeConfiguration = Object.freeze({
3306
+ /**
3307
+ * Locates all attribute configurations associated with a type.
3308
+ */
3309
+ locate: createMetadataLocator(),
3310
+ });
3318
3311
  /**
3319
3312
  * A {@link ValueConverter} that converts to and from `boolean` values.
3320
3313
  * @remarks
@@ -3452,7 +3445,7 @@ class AttributeDefinition {
3452
3445
  */
3453
3446
  static collect(Owner, ...attributeLists) {
3454
3447
  const attributes = [];
3455
- attributeLists.push(Owner.attributes);
3448
+ attributeLists.push(AttributeConfiguration.locate(Owner));
3456
3449
  for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
3457
3450
  const list = attributeLists[i];
3458
3451
  if (list === void 0) {
@@ -3482,9 +3475,7 @@ function attr(configOrTarget, prop) {
3482
3475
  // - @attr({...opts})
3483
3476
  config.property = $prop;
3484
3477
  }
3485
- const attributes = $target.constructor.attributes ||
3486
- ($target.constructor.attributes = []);
3487
- attributes.push(config);
3478
+ AttributeConfiguration.locate($target.constructor).push(config);
3488
3479
  }
3489
3480
  if (arguments.length > 1) {
3490
3481
  // Non invocation:
@@ -3509,6 +3500,7 @@ const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */
3509
3500
  */
3510
3501
  class FASTElementDefinition {
3511
3502
  constructor(type, nameOrConfig = type.definition) {
3503
+ var _a;
3512
3504
  this.platformDefined = false;
3513
3505
  if (isString(nameOrConfig)) {
3514
3506
  nameOrConfig = { name: nameOrConfig };
@@ -3516,6 +3508,7 @@ class FASTElementDefinition {
3516
3508
  this.type = type;
3517
3509
  this.name = nameOrConfig.name;
3518
3510
  this.template = nameOrConfig.template;
3511
+ this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
3519
3512
  const proto = type.prototype;
3520
3513
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3521
3514
  const observedAttributes = new Array(attributes.length);
@@ -3560,7 +3553,7 @@ class FASTElementDefinition {
3560
3553
  * @remarks
3561
3554
  * This operation is idempotent per registry.
3562
3555
  */
3563
- define(registry = customElements) {
3556
+ define(registry = this.registry) {
3564
3557
  const type = this.type;
3565
3558
  if (!registry.get(this.name)) {
3566
3559
  this.platformDefined = true;
@@ -3594,22 +3587,22 @@ FASTElementDefinition.getByType = fastElementRegistry.getByType;
3594
3587
  */
3595
3588
  FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
3596
3589
 
3597
- const shadowRoots = new WeakMap();
3598
3590
  const defaultEventOptions = {
3599
3591
  bubbles: true,
3600
3592
  composed: true,
3601
3593
  cancelable: true,
3602
3594
  };
3595
+ const isConnectedPropertyName = "isConnected";
3596
+ const shadowRoots = new WeakMap();
3603
3597
  function getShadowRoot(element) {
3604
3598
  var _a, _b;
3605
3599
  return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
3606
3600
  }
3607
- const isConnectedPropertyName = "isConnected";
3608
3601
  /**
3609
3602
  * Controls the lifecycle and rendering of a `FASTElement`.
3610
3603
  * @public
3611
3604
  */
3612
- class Controller extends PropertyChangeNotifier {
3605
+ class ElementController extends PropertyChangeNotifier {
3613
3606
  /**
3614
3607
  * Creates a Controller to control the specified element.
3615
3608
  * @param element - The element to be controlled by this controller.
@@ -3620,12 +3613,12 @@ class Controller extends PropertyChangeNotifier {
3620
3613
  constructor(element, definition) {
3621
3614
  super(element);
3622
3615
  this.boundObservables = null;
3623
- this.behaviors = null;
3624
3616
  this.needsInitialization = true;
3625
3617
  this.hasExistingShadowRoot = false;
3626
3618
  this._template = null;
3627
- this._styles = null;
3628
3619
  this._isConnected = false;
3620
+ this.behaviors = null;
3621
+ this._mainStyles = null;
3629
3622
  /**
3630
3623
  * This allows Observable.getNotifier(...) to return the Controller
3631
3624
  * when the notifier for the Controller itself is being requested. The
@@ -3641,7 +3634,7 @@ class Controller extends PropertyChangeNotifier {
3641
3634
  * If `null` then the element is managing its own rendering.
3642
3635
  */
3643
3636
  this.view = null;
3644
- this.element = element;
3637
+ this.source = element;
3645
3638
  this.definition = definition;
3646
3639
  const shadowOptions = definition.shadowOptions;
3647
3640
  if (shadowOptions !== void 0) {
@@ -3695,9 +3688,9 @@ class Controller extends PropertyChangeNotifier {
3695
3688
  // 1. Template overrides take top precedence.
3696
3689
  if (this._template === null) {
3697
3690
  const definition = this.definition;
3698
- if (this.element.resolveTemplate) {
3691
+ if (this.source.resolveTemplate) {
3699
3692
  // 2. Allow for element instance overrides next.
3700
- this._template = this.element.resolveTemplate();
3693
+ this._template = this.source.resolveTemplate();
3701
3694
  }
3702
3695
  else if (definition.template) {
3703
3696
  // 3. Default to the static definition.
@@ -3716,48 +3709,92 @@ class Controller extends PropertyChangeNotifier {
3716
3709
  }
3717
3710
  }
3718
3711
  /**
3719
- * Gets/sets the primary styles used for the component.
3720
- * @remarks
3721
- * This value can only be accurately read after connect but can be set at any time.
3712
+ * The main set of styles used for the component, independent
3713
+ * of any dynamically added styles.
3722
3714
  */
3723
- get styles() {
3715
+ get mainStyles() {
3724
3716
  var _a;
3725
3717
  // 1. Styles overrides take top precedence.
3726
- if (this._styles === null) {
3718
+ if (this._mainStyles === null) {
3727
3719
  const definition = this.definition;
3728
- if (this.element.resolveStyles) {
3720
+ if (this.source.resolveStyles) {
3729
3721
  // 2. Allow for element instance overrides next.
3730
- this._styles = this.element.resolveStyles();
3722
+ this._mainStyles = this.source.resolveStyles();
3731
3723
  }
3732
3724
  else if (definition.styles) {
3733
3725
  // 3. Default to the static definition.
3734
- this._styles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3726
+ this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3735
3727
  }
3736
3728
  }
3737
- return this._styles;
3729
+ return this._mainStyles;
3738
3730
  }
3739
- set styles(value) {
3740
- if (this._styles === value) {
3731
+ set mainStyles(value) {
3732
+ if (this._mainStyles === value) {
3741
3733
  return;
3742
3734
  }
3743
- if (this._styles !== null) {
3744
- this.removeStyles(this._styles);
3735
+ if (this._mainStyles !== null) {
3736
+ this.removeStyles(this._mainStyles);
3745
3737
  }
3746
- this._styles = value;
3738
+ this._mainStyles = value;
3747
3739
  if (!this.needsInitialization) {
3748
3740
  this.addStyles(value);
3749
3741
  }
3750
3742
  }
3743
+ /**
3744
+ * Adds the behavior to the component.
3745
+ * @param behavior - The behavior to add.
3746
+ */
3747
+ addBehavior(behavior) {
3748
+ var _a, _b;
3749
+ const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3750
+ const count = (_b = targetBehaviors.get(behavior)) !== null && _b !== void 0 ? _b : 0;
3751
+ if (count === 0) {
3752
+ targetBehaviors.set(behavior, 1);
3753
+ behavior.addedCallback && behavior.addedCallback(this);
3754
+ if (behavior.connectedCallback && this.isConnected) {
3755
+ behavior.connectedCallback(this);
3756
+ }
3757
+ }
3758
+ else {
3759
+ targetBehaviors.set(behavior, count + 1);
3760
+ }
3761
+ }
3762
+ /**
3763
+ * Removes the behavior from the component.
3764
+ * @param behavior - The behavior to remove.
3765
+ * @param force - Forces removal even if this behavior was added more than once.
3766
+ */
3767
+ removeBehavior(behavior, force = false) {
3768
+ const targetBehaviors = this.behaviors;
3769
+ if (targetBehaviors === null) {
3770
+ return;
3771
+ }
3772
+ const count = targetBehaviors.get(behavior);
3773
+ if (count === void 0) {
3774
+ return;
3775
+ }
3776
+ if (count === 1 || force) {
3777
+ targetBehaviors.delete(behavior);
3778
+ if (behavior.disconnectedCallback && this.isConnected) {
3779
+ behavior.disconnectedCallback(this);
3780
+ }
3781
+ behavior.removedCallback && behavior.removedCallback(this);
3782
+ }
3783
+ else {
3784
+ targetBehaviors.set(behavior, count - 1);
3785
+ }
3786
+ }
3751
3787
  /**
3752
3788
  * Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot.
3753
3789
  * @param styles - The styles to add.
3754
3790
  */
3755
3791
  addStyles(styles) {
3792
+ var _a;
3756
3793
  if (!styles) {
3757
3794
  return;
3758
3795
  }
3759
- const target = getShadowRoot(this.element) ||
3760
- this.element.getRootNode();
3796
+ const source = this.source;
3797
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
3761
3798
  if (styles instanceof HTMLElement) {
3762
3799
  target.append(styles);
3763
3800
  }
@@ -3765,7 +3802,9 @@ class Controller extends PropertyChangeNotifier {
3765
3802
  const sourceBehaviors = styles.behaviors;
3766
3803
  styles.addStylesTo(target);
3767
3804
  if (sourceBehaviors !== null) {
3768
- this.addBehaviors(sourceBehaviors);
3805
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3806
+ this.addBehavior(sourceBehaviors[i]);
3807
+ }
3769
3808
  }
3770
3809
  }
3771
3810
  }
@@ -3774,11 +3813,12 @@ class Controller extends PropertyChangeNotifier {
3774
3813
  * @param styles - the styles to remove.
3775
3814
  */
3776
3815
  removeStyles(styles) {
3816
+ var _a;
3777
3817
  if (!styles) {
3778
3818
  return;
3779
3819
  }
3780
- const target = getShadowRoot(this.element) ||
3781
- this.element.getRootNode();
3820
+ const source = this.source;
3821
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
3782
3822
  if (styles instanceof HTMLElement) {
3783
3823
  target.removeChild(styles);
3784
3824
  }
@@ -3786,85 +3826,29 @@ class Controller extends PropertyChangeNotifier {
3786
3826
  const sourceBehaviors = styles.behaviors;
3787
3827
  styles.removeStylesFrom(target);
3788
3828
  if (sourceBehaviors !== null) {
3789
- this.removeBehaviors(sourceBehaviors);
3790
- }
3791
- }
3792
- }
3793
- /**
3794
- * Adds behaviors to this element.
3795
- * @param behaviors - The behaviors to add.
3796
- */
3797
- addBehaviors(behaviors) {
3798
- var _a;
3799
- const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3800
- const length = behaviors.length;
3801
- const behaviorsToBind = [];
3802
- for (let i = 0; i < length; ++i) {
3803
- const behavior = behaviors[i];
3804
- if (targetBehaviors.has(behavior)) {
3805
- targetBehaviors.set(behavior, targetBehaviors.get(behavior) + 1);
3806
- }
3807
- else {
3808
- targetBehaviors.set(behavior, 1);
3809
- behaviorsToBind.push(behavior);
3810
- }
3811
- }
3812
- if (this._isConnected) {
3813
- const element = this.element;
3814
- const context = ExecutionContext.default;
3815
- for (let i = 0; i < behaviorsToBind.length; ++i) {
3816
- behaviorsToBind[i].bind(element, context);
3817
- }
3818
- }
3819
- }
3820
- /**
3821
- * Removes behaviors from this element.
3822
- * @param behaviors - The behaviors to remove.
3823
- * @param force - Forces unbinding of behaviors.
3824
- */
3825
- removeBehaviors(behaviors, force = false) {
3826
- const targetBehaviors = this.behaviors;
3827
- if (targetBehaviors === null) {
3828
- return;
3829
- }
3830
- const length = behaviors.length;
3831
- const behaviorsToUnbind = [];
3832
- for (let i = 0; i < length; ++i) {
3833
- const behavior = behaviors[i];
3834
- if (targetBehaviors.has(behavior)) {
3835
- const count = targetBehaviors.get(behavior) - 1;
3836
- count === 0 || force
3837
- ? targetBehaviors.delete(behavior) && behaviorsToUnbind.push(behavior)
3838
- : targetBehaviors.set(behavior, count);
3839
- }
3840
- }
3841
- if (this._isConnected) {
3842
- const element = this.element;
3843
- const context = ExecutionContext.default;
3844
- for (let i = 0; i < behaviorsToUnbind.length; ++i) {
3845
- behaviorsToUnbind[i].unbind(element, context);
3829
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3830
+ this.addBehavior(sourceBehaviors[i]);
3831
+ }
3846
3832
  }
3847
3833
  }
3848
3834
  }
3849
3835
  /**
3850
3836
  * Runs connected lifecycle behavior on the associated element.
3851
3837
  */
3852
- onConnectedCallback() {
3838
+ connect() {
3853
3839
  if (this._isConnected) {
3854
3840
  return;
3855
3841
  }
3856
- const element = this.element;
3857
- const context = ExecutionContext.default;
3858
3842
  if (this.needsInitialization) {
3859
3843
  this.finishInitialization();
3860
3844
  }
3861
3845
  else if (this.view !== null) {
3862
- this.view.bind(element, context);
3846
+ this.view.bind(this.source);
3863
3847
  }
3864
3848
  const behaviors = this.behaviors;
3865
3849
  if (behaviors !== null) {
3866
- for (const behavior of behaviors.keys()) {
3867
- behavior.bind(element, context);
3850
+ for (const key of behaviors.keys()) {
3851
+ key.connectedCallback && key.connectedCallback(this);
3868
3852
  }
3869
3853
  }
3870
3854
  this.setIsConnected(true);
@@ -3872,21 +3856,18 @@ class Controller extends PropertyChangeNotifier {
3872
3856
  /**
3873
3857
  * Runs disconnected lifecycle behavior on the associated element.
3874
3858
  */
3875
- onDisconnectedCallback() {
3859
+ disconnect() {
3876
3860
  if (!this._isConnected) {
3877
3861
  return;
3878
3862
  }
3879
3863
  this.setIsConnected(false);
3880
- const view = this.view;
3881
- if (view !== null) {
3882
- view.unbind();
3864
+ if (this.view !== null) {
3865
+ this.view.unbind();
3883
3866
  }
3884
3867
  const behaviors = this.behaviors;
3885
3868
  if (behaviors !== null) {
3886
- const element = this.element;
3887
- const context = ExecutionContext.default;
3888
- for (const behavior of behaviors.keys()) {
3889
- behavior.unbind(element, context);
3869
+ for (const key of behaviors.keys()) {
3870
+ key.disconnectedCallback && key.disconnectedCallback(this);
3890
3871
  }
3891
3872
  }
3892
3873
  }
@@ -3899,7 +3880,7 @@ class Controller extends PropertyChangeNotifier {
3899
3880
  onAttributeChangedCallback(name, oldValue, newValue) {
3900
3881
  const attrDef = this.definition.attributeLookup[name];
3901
3882
  if (attrDef !== void 0) {
3902
- attrDef.onAttributeChangedCallback(this.element, newValue);
3883
+ attrDef.onAttributeChangedCallback(this.source, newValue);
3903
3884
  }
3904
3885
  }
3905
3886
  /**
@@ -3912,12 +3893,12 @@ class Controller extends PropertyChangeNotifier {
3912
3893
  */
3913
3894
  emit(type, detail, options) {
3914
3895
  if (this._isConnected) {
3915
- return this.element.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3896
+ return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3916
3897
  }
3917
3898
  return false;
3918
3899
  }
3919
3900
  finishInitialization() {
3920
- const element = this.element;
3901
+ const element = this.source;
3921
3902
  const boundObservables = this.boundObservables;
3922
3903
  // If we have any observables that were bound, re-apply their values.
3923
3904
  if (boundObservables !== null) {
@@ -3929,15 +3910,15 @@ class Controller extends PropertyChangeNotifier {
3929
3910
  this.boundObservables = null;
3930
3911
  }
3931
3912
  this.renderTemplate(this.template);
3932
- this.addStyles(this.styles);
3913
+ this.addStyles(this.mainStyles);
3933
3914
  this.needsInitialization = false;
3934
3915
  }
3935
3916
  renderTemplate(template) {
3936
3917
  var _a;
3937
- const element = this.element;
3938
3918
  // When getting the host to render to, we start by looking
3939
3919
  // up the shadow root. If there isn't one, then that means
3940
3920
  // we're doing a Light DOM render to the element's direct children.
3921
+ const element = this.source;
3941
3922
  const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
3942
3923
  if (this.view !== null) {
3943
3924
  // If there's already a view, we need to unbind and remove through dispose.
@@ -3954,6 +3935,8 @@ class Controller extends PropertyChangeNotifier {
3954
3935
  if (template) {
3955
3936
  // If a new template was provided, render it.
3956
3937
  this.view = template.render(element, host, element);
3938
+ this.view.sourceLifetime =
3939
+ SourceLifetime.coupled;
3957
3940
  }
3958
3941
  }
3959
3942
  /**
@@ -3973,7 +3956,7 @@ class Controller extends PropertyChangeNotifier {
3973
3956
  if (definition === void 0) {
3974
3957
  throw FAST.error(1401 /* Message.missingElementDefinition */);
3975
3958
  }
3976
- return (element.$fastController = new Controller(element, definition));
3959
+ return (element.$fastController = new ElementController(element, definition));
3977
3960
  }
3978
3961
  }
3979
3962
 
@@ -3983,16 +3966,16 @@ function createFASTElement(BaseType) {
3983
3966
  constructor() {
3984
3967
  /* eslint-disable-next-line */
3985
3968
  super();
3986
- Controller.forCustomElement(this);
3969
+ ElementController.forCustomElement(this);
3987
3970
  }
3988
3971
  $emit(type, detail, options) {
3989
3972
  return this.$fastController.emit(type, detail, options);
3990
3973
  }
3991
3974
  connectedCallback() {
3992
- this.$fastController.onConnectedCallback();
3975
+ this.$fastController.connect();
3993
3976
  }
3994
3977
  disconnectedCallback() {
3995
- this.$fastController.onDisconnectedCallback();
3978
+ this.$fastController.disconnect();
3996
3979
  }
3997
3980
  attributeChangedCallback(name, oldValue, newValue) {
3998
3981
  this.$fastController.onAttributeChangedCallback(name, oldValue, newValue);
@@ -4052,4 +4035,4 @@ function customElement(nameOrDef) {
4052
4035
  };
4053
4036
  }
4054
4037
 
4055
- 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 };
4038
+ 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 };