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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.json +348 -0
  2. package/CHANGELOG.md +114 -1
  3. package/dist/dts/components/attributes.d.ts +10 -0
  4. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +49 -25
  5. package/dist/dts/components/fast-definitions.d.ts +43 -9
  6. package/dist/dts/components/fast-element.d.ts +15 -21
  7. package/dist/dts/context.d.ts +157 -0
  8. package/dist/dts/di/di.d.ts +899 -0
  9. package/dist/dts/index.d.ts +2 -2
  10. package/dist/dts/interfaces.d.ts +45 -14
  11. package/dist/dts/metadata.d.ts +25 -0
  12. package/dist/dts/observation/arrays.d.ts +1 -1
  13. package/dist/dts/observation/observable.d.ts +101 -75
  14. package/dist/dts/pending-task.d.ts +20 -0
  15. package/dist/dts/platform.d.ts +7 -0
  16. package/dist/dts/polyfills.d.ts +1 -8
  17. package/dist/dts/state/exports.d.ts +3 -0
  18. package/dist/dts/state/reactive.d.ts +8 -0
  19. package/dist/dts/state/state.d.ts +141 -0
  20. package/dist/dts/state/visitor.d.ts +6 -0
  21. package/dist/dts/state/watch.d.ts +10 -0
  22. package/dist/dts/styles/css-directive.d.ts +2 -2
  23. package/dist/dts/styles/element-styles.d.ts +10 -17
  24. package/dist/dts/styles/host.d.ts +68 -0
  25. package/dist/dts/templating/binding-signal.d.ts +21 -0
  26. package/dist/dts/templating/binding-two-way.d.ts +39 -0
  27. package/dist/dts/templating/binding.d.ts +69 -294
  28. package/dist/dts/templating/children.d.ts +1 -1
  29. package/dist/dts/templating/compiler.d.ts +1 -2
  30. package/dist/dts/templating/html-directive.d.ts +93 -35
  31. package/dist/dts/templating/node-observation.d.ts +4 -5
  32. package/dist/dts/templating/ref.d.ts +5 -13
  33. package/dist/dts/templating/render.d.ts +272 -0
  34. package/dist/dts/templating/repeat.d.ts +20 -75
  35. package/dist/dts/templating/slotted.d.ts +1 -1
  36. package/dist/dts/templating/template.d.ts +12 -61
  37. package/dist/dts/templating/view.d.ts +77 -12
  38. package/dist/dts/templating/when.d.ts +3 -3
  39. package/dist/dts/testing/exports.d.ts +3 -0
  40. package/dist/dts/testing/fakes.d.ts +4 -0
  41. package/dist/dts/testing/fixture.d.ts +84 -0
  42. package/dist/dts/testing/timeout.d.ts +7 -0
  43. package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +0 -0
  44. package/dist/dts/utilities.d.ts +0 -18
  45. package/dist/esm/components/attributes.js +13 -4
  46. package/dist/esm/components/{controller.js → element-controller.js} +188 -109
  47. package/dist/esm/components/fast-definitions.js +38 -28
  48. package/dist/esm/components/fast-element.js +31 -12
  49. package/dist/esm/context.js +163 -0
  50. package/dist/esm/debug.js +36 -4
  51. package/dist/esm/di/di.js +1435 -0
  52. package/dist/esm/index.js +2 -1
  53. package/dist/esm/interfaces.js +4 -0
  54. package/dist/esm/metadata.js +60 -0
  55. package/dist/esm/observation/arrays.js +304 -3
  56. package/dist/esm/observation/observable.js +81 -87
  57. package/dist/esm/pending-task.js +16 -0
  58. package/dist/esm/platform.js +26 -1
  59. package/dist/esm/polyfills.js +1 -55
  60. package/dist/esm/state/exports.js +3 -0
  61. package/dist/esm/state/reactive.js +34 -0
  62. package/dist/esm/state/state.js +148 -0
  63. package/dist/esm/state/visitor.js +28 -0
  64. package/dist/esm/state/watch.js +36 -0
  65. package/dist/esm/styles/css.js +4 -4
  66. package/dist/esm/styles/element-styles.js +14 -33
  67. package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
  68. package/dist/esm/templating/binding-signal.js +83 -0
  69. package/dist/esm/templating/binding-two-way.js +103 -0
  70. package/dist/esm/templating/binding.js +134 -414
  71. package/dist/esm/templating/compiler.js +30 -7
  72. package/dist/esm/templating/html-directive.js +100 -28
  73. package/dist/esm/templating/node-observation.js +9 -8
  74. package/dist/esm/templating/ref.js +4 -12
  75. package/dist/esm/templating/render.js +391 -0
  76. package/dist/esm/templating/repeat.js +96 -72
  77. package/dist/esm/templating/template.js +11 -29
  78. package/dist/esm/templating/view.js +107 -29
  79. package/dist/esm/templating/when.js +5 -4
  80. package/dist/esm/testing/exports.js +3 -0
  81. package/dist/esm/testing/fakes.js +76 -0
  82. package/dist/esm/testing/fixture.js +86 -0
  83. package/dist/esm/testing/timeout.js +24 -0
  84. package/dist/esm/utilities.js +0 -95
  85. package/dist/fast-element.api.json +9278 -10745
  86. package/dist/fast-element.d.ts +707 -813
  87. package/dist/fast-element.debug.js +1229 -944
  88. package/dist/fast-element.debug.min.js +1 -1
  89. package/dist/fast-element.js +1191 -938
  90. package/dist/fast-element.min.js +1 -1
  91. package/dist/fast-element.untrimmed.d.ts +716 -824
  92. package/docs/api-report.md +265 -319
  93. package/package.json +39 -14
  94. package/dist/dts/hooks.d.ts +0 -20
  95. package/dist/dts/observation/behavior.d.ts +0 -19
  96. package/dist/dts/observation/splice-strategies.d.ts +0 -13
  97. package/dist/esm/hooks.js +0 -32
  98. package/dist/esm/observation/splice-strategies.js +0 -400
@@ -28,61 +28,6 @@ if (!globalThis.trustedTypes) {
28
28
  createPolicy: (n, r) => r,
29
29
  };
30
30
  }
31
- // ensure FAST global - duplicated in platform.ts
32
- const propConfig$1 = {
33
- configurable: false,
34
- enumerable: false,
35
- writable: false,
36
- };
37
- if (globalThis.FAST === void 0) {
38
- Reflect.defineProperty(globalThis, "FAST", Object.assign({ value: Object.create(null) }, propConfig$1));
39
- }
40
- const FAST$2 = globalThis.FAST;
41
- if (FAST$2.getById === void 0) {
42
- const storage = Object.create(null);
43
- Reflect.defineProperty(FAST$2, "getById", Object.assign({ value(id, initialize) {
44
- let found = storage[id];
45
- if (found === void 0) {
46
- found = initialize ? (storage[id] = initialize()) : null;
47
- }
48
- return found;
49
- } }, propConfig$1));
50
- }
51
- // duplicated from DOM
52
- const supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
53
- "replace" in CSSStyleSheet.prototype;
54
- function usableStyleTarget(target) {
55
- return target === document ? document.body : target;
56
- }
57
- let id$1 = 0;
58
- const nextStyleId = () => `fast-${++id$1}`;
59
- class StyleElementStrategy {
60
- constructor(styles) {
61
- this.styles = styles;
62
- this.styleClass = nextStyleId();
63
- }
64
- addStylesTo(target) {
65
- target = usableStyleTarget(target);
66
- const styles = this.styles;
67
- const styleClass = this.styleClass;
68
- for (let i = 0; i < styles.length; i++) {
69
- const element = document.createElement("style");
70
- element.innerHTML = styles[i];
71
- element.className = styleClass;
72
- target.append(element);
73
- }
74
- }
75
- removeStylesFrom(target) {
76
- const styles = target.querySelectorAll(`.${this.styleClass}`);
77
- target = usableStyleTarget(target);
78
- for (let i = 0, ii = styles.length; i < ii; ++i) {
79
- target.removeChild(styles[i]);
80
- }
81
- }
82
- }
83
- if (!supportsAdoptedStyleSheets) {
84
- FAST$2.getById(/* KernelServiceId.styleSheetStrategy */ 5, () => StyleElementStrategy);
85
- }
86
31
 
87
32
  if (globalThis.FAST === void 0) {
88
33
  Reflect.defineProperty(globalThis, "FAST", {
@@ -97,19 +42,51 @@ const debugMessages = {
97
42
  [1101 /* needsArrayObservation */]: "Must call enableArrayObservation before observing arrays.",
98
43
  [1201 /* onlySetHTMLPolicyOnce */]: "The HTML policy can only be set once.",
99
44
  [1202 /* bindingInnerHTMLRequiresTrustedTypes */]: "To bind innerHTML, you must use a TrustedTypesPolicy.",
45
+ [1203 /* twoWayBindingRequiresObservables */]: "View=>Model update skipped. To use twoWay binding, the target property must be observable.",
46
+ [1204 /* hostBindingWithoutHost */]: "No host element is present. Cannot bind host with ${name}.",
47
+ [1205 /* unsupportedBindingBehavior */]: "The requested binding behavior is not supported by the binding engine.",
100
48
  [1401 /* missingElementDefinition */]: "Missing FASTElement definition.",
49
+ [1501 /* noRegistrationForContext */]: "No registration for Context/Interface '${name}'.",
50
+ [1502 /* noFactoryForResolver */]: "Dependency injection resolver for '${key}' returned a null factory.",
51
+ [1503 /* invalidResolverStrategy */]: "Invalid dependency injection resolver strategy specified '${strategy}'.",
52
+ [1504 /* cannotAutoregisterDependency */]: "Unable to autoregister dependency.",
53
+ [1505 /* cannotResolveKey */]: "Unable to resolve dependency injection key '${key}'.",
54
+ [1506 /* cannotConstructNativeFunction */]: "'${name}' is a native function and therefore cannot be safely constructed by DI. If this is intentional, please use a callback or cachedCallback resolver.",
55
+ [1507 /* cannotJITRegisterNonConstructor */]: "Attempted to jitRegister something that is not a constructor '${value}'. Did you forget to register this dependency?",
56
+ [1508 /* cannotJITRegisterIntrinsic */]: "Attempted to jitRegister an intrinsic type '${value}'. Did you forget to add @inject(Key)?",
57
+ [1509 /* cannotJITRegisterInterface */]: "Attempted to jitRegister an interface '${value}'.",
58
+ [1510 /* invalidResolver */]: "A valid resolver was not returned from the register method.",
59
+ [1511 /* invalidKey */]: "Key/value cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?",
60
+ [1512 /* noDefaultResolver */]: "'${key}' not registered. Did you forget to add @singleton()?",
61
+ [1513 /* cyclicDependency */]: "Cyclic dependency found '${name}'.",
62
+ [1514 /* connectUpdateRequiresController */]: "Injected properties that are updated on changes to DOM connectivity require the target object to be an instance of FASTElement.",
101
63
  };
64
+ const allPlaceholders = /(\$\{\w+?})/g;
65
+ const placeholder = /\$\{(\w+?)}/g;
66
+ const noValues = Object.freeze({});
67
+ function formatMessage(message, values) {
68
+ return message
69
+ .split(allPlaceholders)
70
+ .map(v => {
71
+ var _a;
72
+ const replaced = v.replace(placeholder, "$1");
73
+ return String((_a = values[replaced]) !== null && _a !== void 0 ? _a : v);
74
+ })
75
+ .join("");
76
+ }
102
77
  Object.assign(FAST$1, {
103
78
  addMessages(messages) {
104
79
  Object.assign(debugMessages, messages);
105
80
  },
106
- warn(code, ...args) {
81
+ warn(code, values = noValues) {
107
82
  var _a;
108
- console.warn((_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Warning");
83
+ const message = (_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Warning";
84
+ console.warn(formatMessage(message, values));
109
85
  },
110
- error(code, ...args) {
86
+ error(code, values = noValues) {
111
87
  var _a;
112
- return new Error((_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Error");
88
+ const message = (_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Error";
89
+ return new Error(formatMessage(message, values));
113
90
  },
114
91
  });
115
92
 
@@ -141,7 +118,7 @@ if (FAST.error === void 0) {
141
118
  Object.assign(FAST, {
142
119
  warn() { },
143
120
  error(code) {
144
- return new Error(`Code ${code}`);
121
+ return new Error(`Error ${code}`);
145
122
  },
146
123
  addMessages() { },
147
124
  });
@@ -172,10 +149,34 @@ function createTypeRegistry() {
172
149
  return typeToDefinition.get(key);
173
150
  },
174
151
  getForInstance(object) {
152
+ if (object === null || object === void 0) {
153
+ return void 0;
154
+ }
175
155
  return typeToDefinition.get(object.constructor);
176
156
  },
177
157
  });
178
158
  }
159
+ /**
160
+ * Creates a function capable of locating metadata associated with a type.
161
+ * @returns A metadata locator function.
162
+ * @internal
163
+ */
164
+ function createMetadataLocator() {
165
+ const metadataLookup = new WeakMap();
166
+ return function (target) {
167
+ let metadata = metadataLookup.get(target);
168
+ if (metadata === void 0) {
169
+ let currentTarget = Reflect.getPrototypeOf(target);
170
+ while (metadata === void 0 && currentTarget !== null) {
171
+ metadata = metadataLookup.get(currentTarget);
172
+ currentTarget = Reflect.getPrototypeOf(currentTarget);
173
+ }
174
+ metadata = metadata === void 0 ? [] : metadata.slice(0);
175
+ metadataLookup.set(target, metadata);
176
+ }
177
+ return metadata;
178
+ };
179
+ }
179
180
 
180
181
  /**
181
182
  * @internal
@@ -417,6 +418,21 @@ class PropertyChangeNotifier {
417
418
  }
418
419
  }
419
420
 
421
+ /**
422
+ * Describes how the source's lifetime relates to its controller's lifetime.
423
+ * @public
424
+ */
425
+ const SourceLifetime = Object.freeze({
426
+ /**
427
+ * The source to controller lifetime relationship is unknown.
428
+ */
429
+ unknown: void 0,
430
+ /**
431
+ * The source and controller lifetimes are coupled to one another.
432
+ * They can/will be GC'd together.
433
+ */
434
+ coupled: 1,
435
+ });
420
436
  /**
421
437
  * Common Observable APIs.
422
438
  * @public
@@ -425,7 +441,6 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
425
441
  const queueUpdate = Updates.enqueue;
426
442
  const volatileRegex = /(:|&&|\|\||if)/;
427
443
  const notifierLookup = new WeakMap();
428
- const accessorLookup = new WeakMap();
429
444
  let watcher = void 0;
430
445
  let createArrayObserver = (array) => {
431
446
  throw FAST.error(1101 /* Message.needsArrayObservation */);
@@ -440,19 +455,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
440
455
  }
441
456
  return found;
442
457
  }
443
- function getAccessors(target) {
444
- let accessors = accessorLookup.get(target);
445
- if (accessors === void 0) {
446
- let currentTarget = Reflect.getPrototypeOf(target);
447
- while (accessors === void 0 && currentTarget !== null) {
448
- accessors = accessorLookup.get(currentTarget);
449
- currentTarget = Reflect.getPrototypeOf(currentTarget);
450
- }
451
- accessors = accessors === void 0 ? [] : accessors.slice(0);
452
- accessorLookup.set(target, accessors);
453
- }
454
- return accessors;
455
- }
458
+ const getAccessors = createMetadataLocator();
456
459
  class DefaultObservableAccessor {
457
460
  constructor(name) {
458
461
  this.name = name;
@@ -478,10 +481,10 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
478
481
  }
479
482
  }
480
483
  }
481
- class BindingObserverImplementation extends SubscriberSet {
482
- constructor(binding, initialSubscriber, isVolatileBinding = false) {
483
- super(binding, initialSubscriber);
484
- this.binding = binding;
484
+ class ExpressionNotifierImplementation extends SubscriberSet {
485
+ constructor(expression, initialSubscriber, isVolatileBinding = false) {
486
+ super(expression, initialSubscriber);
487
+ this.expression = expression;
485
488
  this.isVolatileBinding = isVolatileBinding;
486
489
  this.needsRefresh = true;
487
490
  this.needsQueue = true;
@@ -496,6 +499,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
496
499
  setMode(isAsync) {
497
500
  this.isAsync = this.needsQueue = isAsync;
498
501
  }
502
+ bind(controller) {
503
+ this.controller = controller;
504
+ const value = this.observe(controller.source, controller.context);
505
+ if (!controller.isBound && this.requiresUnbind(controller)) {
506
+ controller.onUnbind(this);
507
+ }
508
+ return value;
509
+ }
510
+ requiresUnbind(controller) {
511
+ return (controller.sourceLifetime !== SourceLifetime.coupled ||
512
+ this.first !== this.last ||
513
+ this.first.propertySource !== controller.source);
514
+ }
515
+ unbind(controller) {
516
+ this.dispose();
517
+ }
499
518
  observe(source, context) {
500
519
  if (this.needsRefresh && this.last !== null) {
501
520
  this.dispose();
@@ -503,10 +522,19 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
503
522
  const previousWatcher = watcher;
504
523
  watcher = this.needsRefresh ? this : void 0;
505
524
  this.needsRefresh = this.isVolatileBinding;
506
- const result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
507
- watcher = previousWatcher;
525
+ let result;
526
+ try {
527
+ result = this.expression(source, context);
528
+ }
529
+ finally {
530
+ watcher = previousWatcher;
531
+ }
508
532
  return result;
509
533
  }
534
+ // backwards compat with v1 kernel
535
+ disconnect() {
536
+ this.dispose();
537
+ }
510
538
  dispose() {
511
539
  if (this.last !== null) {
512
540
  let current = this.first;
@@ -633,22 +661,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
633
661
  */
634
662
  getAccessors,
635
663
  /**
636
- * Creates a {@link BindingObserver} that can watch the
637
- * provided {@link Binding} for changes.
638
- * @param binding - The binding to observe.
664
+ * Creates a {@link ExpressionNotifier} that can watch the
665
+ * provided {@link Expression} for changes.
666
+ * @param expression - The binding to observe.
639
667
  * @param initialSubscriber - An initial subscriber to changes in the binding value.
640
668
  * @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
641
669
  */
642
- binding(binding, initialSubscriber, isVolatileBinding = this.isVolatileBinding(binding)) {
643
- return new BindingObserverImplementation(binding, initialSubscriber, isVolatileBinding);
670
+ binding(expression, initialSubscriber, isVolatileBinding = this.isVolatileBinding(expression)) {
671
+ return new ExpressionNotifierImplementation(expression, initialSubscriber, isVolatileBinding);
644
672
  },
645
673
  /**
646
674
  * Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
647
675
  * on every evaluation of the value.
648
- * @param binding - The binding to inspect.
676
+ * @param expression - The binding to inspect.
649
677
  */
650
- isVolatileBinding(binding) {
651
- return volatileRegex.test(binding.toString());
678
+ isVolatileBinding(expression) {
679
+ return volatileRegex.test(expression.toString());
652
680
  },
653
681
  });
654
682
  });
@@ -687,73 +715,40 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
687
715
  },
688
716
  };
689
717
  });
690
- class DefaultExecutionContext {
691
- constructor(parentSource = null, parentContext = null) {
692
- this.index = 0;
693
- this.length = 0;
694
- this.parent = parentSource;
695
- this.parentContext = parentContext;
696
- }
697
- get event() {
698
- return contextEvent.get();
699
- }
700
- get isEven() {
701
- return this.index % 2 === 0;
702
- }
703
- get isOdd() {
704
- return this.index % 2 !== 0;
705
- }
706
- get isFirst() {
707
- return this.index === 0;
708
- }
709
- get isInMiddle() {
710
- return !this.isFirst && !this.isLast;
711
- }
712
- get isLast() {
713
- return this.index === this.length - 1;
714
- }
715
- eventDetail() {
716
- return this.event.detail;
717
- }
718
- eventTarget() {
719
- return this.event.target;
720
- }
721
- updatePosition(index, length) {
722
- this.index = index;
723
- this.length = length;
724
- }
725
- createChildContext(parentSource) {
726
- return new DefaultExecutionContext(parentSource, this);
727
- }
728
- createItemContext(index, length) {
729
- const childContext = Object.create(this);
730
- childContext.index = index;
731
- childContext.length = length;
732
- return childContext;
733
- }
734
- }
735
- Observable.defineProperty(DefaultExecutionContext.prototype, "index");
736
- Observable.defineProperty(DefaultExecutionContext.prototype, "length");
737
718
  /**
738
- * The common execution context APIs.
719
+ * Provides additional contextual information available to behaviors and expressions.
739
720
  * @public
740
721
  */
741
722
  const ExecutionContext = Object.freeze({
742
- default: new DefaultExecutionContext(),
743
723
  /**
744
- * Sets the event for the current execution context.
745
- * @param event - The event to set.
746
- * @internal
724
+ * A default execution context.
747
725
  */
748
- setEvent(event) {
749
- contextEvent.set(event);
726
+ default: {
727
+ index: 0,
728
+ length: 0,
729
+ get event() {
730
+ return ExecutionContext.getEvent();
731
+ },
732
+ eventDetail() {
733
+ return this.event.detail;
734
+ },
735
+ eventTarget() {
736
+ return this.event.target;
737
+ },
750
738
  },
751
739
  /**
752
- * Creates a new root execution context.
753
- * @returns A new execution context.
740
+ * Gets the current event.
741
+ * @returns An event object.
754
742
  */
755
- create() {
756
- return new DefaultExecutionContext();
743
+ getEvent() {
744
+ return contextEvent.get();
745
+ },
746
+ /**
747
+ * Sets the current event.
748
+ * @param event - An event object.
749
+ */
750
+ setEvent(event) {
751
+ contextEvent.set(event);
757
752
  },
758
753
  });
759
754
 
@@ -822,10 +817,311 @@ const SpliceStrategySupport = Object.freeze({
822
817
  const reset = new Splice(0, emptyArray, 0);
823
818
  reset.reset = true;
824
819
  const resetSplices = [reset];
820
+ // Note: This function is *based* on the computation of the Levenshtein
821
+ // "edit" distance. The one change is that "updates" are treated as two
822
+ // edits - not one. With Array splices, an update is really a delete
823
+ // followed by an add. By retaining this, we optimize for "keeping" the
824
+ // maximum array items in the original array. For example:
825
+ //
826
+ // 'xxxx123' to '123yyyy'
827
+ //
828
+ // With 1-edit updates, the shortest path would be just to update all seven
829
+ // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
830
+ // leaves the substring '123' intact.
831
+ function calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd) {
832
+ // "Deletion" columns
833
+ const rowCount = oldEnd - oldStart + 1;
834
+ const columnCount = currentEnd - currentStart + 1;
835
+ const distances = new Array(rowCount);
836
+ let north;
837
+ let west;
838
+ // "Addition" rows. Initialize null column.
839
+ for (let i = 0; i < rowCount; ++i) {
840
+ distances[i] = new Array(columnCount);
841
+ distances[i][0] = i;
842
+ }
843
+ // Initialize null row
844
+ for (let j = 0; j < columnCount; ++j) {
845
+ distances[0][j] = j;
846
+ }
847
+ for (let i = 1; i < rowCount; ++i) {
848
+ for (let j = 1; j < columnCount; ++j) {
849
+ if (current[currentStart + j - 1] === old[oldStart + i - 1]) {
850
+ distances[i][j] = distances[i - 1][j - 1];
851
+ }
852
+ else {
853
+ north = distances[i - 1][j] + 1;
854
+ west = distances[i][j - 1] + 1;
855
+ distances[i][j] = north < west ? north : west;
856
+ }
857
+ }
858
+ }
859
+ return distances;
860
+ }
861
+ // This starts at the final weight, and walks "backward" by finding
862
+ // the minimum previous weight recursively until the origin of the weight
863
+ // matrix.
864
+ function spliceOperationsFromEditDistances(distances) {
865
+ let i = distances.length - 1;
866
+ let j = distances[0].length - 1;
867
+ let current = distances[i][j];
868
+ const edits = [];
869
+ while (i > 0 || j > 0) {
870
+ if (i === 0) {
871
+ edits.push(2 /* Edit.add */);
872
+ j--;
873
+ continue;
874
+ }
875
+ if (j === 0) {
876
+ edits.push(3 /* Edit.delete */);
877
+ i--;
878
+ continue;
879
+ }
880
+ const northWest = distances[i - 1][j - 1];
881
+ const west = distances[i - 1][j];
882
+ const north = distances[i][j - 1];
883
+ let min;
884
+ if (west < north) {
885
+ min = west < northWest ? west : northWest;
886
+ }
887
+ else {
888
+ min = north < northWest ? north : northWest;
889
+ }
890
+ if (min === northWest) {
891
+ if (northWest === current) {
892
+ edits.push(0 /* Edit.leave */);
893
+ }
894
+ else {
895
+ edits.push(1 /* Edit.update */);
896
+ current = northWest;
897
+ }
898
+ i--;
899
+ j--;
900
+ }
901
+ else if (min === west) {
902
+ edits.push(3 /* Edit.delete */);
903
+ i--;
904
+ current = west;
905
+ }
906
+ else {
907
+ edits.push(2 /* Edit.add */);
908
+ j--;
909
+ current = north;
910
+ }
911
+ }
912
+ return edits.reverse();
913
+ }
914
+ function sharedPrefix(current, old, searchLength) {
915
+ for (let i = 0; i < searchLength; ++i) {
916
+ if (current[i] !== old[i]) {
917
+ return i;
918
+ }
919
+ }
920
+ return searchLength;
921
+ }
922
+ function sharedSuffix(current, old, searchLength) {
923
+ let index1 = current.length;
924
+ let index2 = old.length;
925
+ let count = 0;
926
+ while (count < searchLength && current[--index1] === old[--index2]) {
927
+ count++;
928
+ }
929
+ return count;
930
+ }
931
+ function intersect(start1, end1, start2, end2) {
932
+ // Disjoint
933
+ if (end1 < start2 || end2 < start1) {
934
+ return -1;
935
+ }
936
+ // Adjacent
937
+ if (end1 === start2 || end2 === start1) {
938
+ return 0;
939
+ }
940
+ // Non-zero intersect, span1 first
941
+ if (start1 < start2) {
942
+ if (end1 < end2) {
943
+ return end1 - start2; // Overlap
944
+ }
945
+ return end2 - start2; // Contained
946
+ }
947
+ // Non-zero intersect, span2 first
948
+ if (end2 < end1) {
949
+ return end2 - start1; // Overlap
950
+ }
951
+ return end1 - start1; // Contained
952
+ }
953
+ /**
954
+ * @remarks
955
+ * Lacking individual splice mutation information, the minimal set of
956
+ * splices can be synthesized given the previous state and final state of an
957
+ * array. The basic approach is to calculate the edit distance matrix and
958
+ * choose the shortest path through it.
959
+ *
960
+ * Complexity: O(l * p)
961
+ * l: The length of the current array
962
+ * p: The length of the old array
963
+ */
964
+ function calc(current, currentStart, currentEnd, old, oldStart, oldEnd) {
965
+ let prefixCount = 0;
966
+ let suffixCount = 0;
967
+ const minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
968
+ if (currentStart === 0 && oldStart === 0) {
969
+ prefixCount = sharedPrefix(current, old, minLength);
970
+ }
971
+ if (currentEnd === current.length && oldEnd === old.length) {
972
+ suffixCount = sharedSuffix(current, old, minLength - prefixCount);
973
+ }
974
+ currentStart += prefixCount;
975
+ oldStart += prefixCount;
976
+ currentEnd -= suffixCount;
977
+ oldEnd -= suffixCount;
978
+ if (currentEnd - currentStart === 0 && oldEnd - oldStart === 0) {
979
+ return emptyArray;
980
+ }
981
+ if (currentStart === currentEnd) {
982
+ const splice = new Splice(currentStart, [], 0);
983
+ while (oldStart < oldEnd) {
984
+ splice.removed.push(old[oldStart++]);
985
+ }
986
+ return [splice];
987
+ }
988
+ else if (oldStart === oldEnd) {
989
+ return [new Splice(currentStart, [], currentEnd - currentStart)];
990
+ }
991
+ const ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
992
+ const splices = [];
993
+ let splice = void 0;
994
+ let index = currentStart;
995
+ let oldIndex = oldStart;
996
+ for (let i = 0; i < ops.length; ++i) {
997
+ switch (ops[i]) {
998
+ case 0 /* Edit.leave */:
999
+ if (splice !== void 0) {
1000
+ splices.push(splice);
1001
+ splice = void 0;
1002
+ }
1003
+ index++;
1004
+ oldIndex++;
1005
+ break;
1006
+ case 1 /* Edit.update */:
1007
+ if (splice === void 0) {
1008
+ splice = new Splice(index, [], 0);
1009
+ }
1010
+ splice.addedCount++;
1011
+ index++;
1012
+ splice.removed.push(old[oldIndex]);
1013
+ oldIndex++;
1014
+ break;
1015
+ case 2 /* Edit.add */:
1016
+ if (splice === void 0) {
1017
+ splice = new Splice(index, [], 0);
1018
+ }
1019
+ splice.addedCount++;
1020
+ index++;
1021
+ break;
1022
+ case 3 /* Edit.delete */:
1023
+ if (splice === void 0) {
1024
+ splice = new Splice(index, [], 0);
1025
+ }
1026
+ splice.removed.push(old[oldIndex]);
1027
+ oldIndex++;
1028
+ break;
1029
+ // no default
1030
+ }
1031
+ }
1032
+ if (splice !== void 0) {
1033
+ splices.push(splice);
1034
+ }
1035
+ return splices;
1036
+ }
1037
+ function merge(splice, splices) {
1038
+ let inserted = false;
1039
+ let insertionOffset = 0;
1040
+ for (let i = 0; i < splices.length; i++) {
1041
+ const current = splices[i];
1042
+ current.index += insertionOffset;
1043
+ if (inserted) {
1044
+ continue;
1045
+ }
1046
+ const intersectCount = intersect(splice.index, splice.index + splice.removed.length, current.index, current.index + current.addedCount);
1047
+ if (intersectCount >= 0) {
1048
+ // Merge the two splices
1049
+ splices.splice(i, 1);
1050
+ i--;
1051
+ insertionOffset -= current.addedCount - current.removed.length;
1052
+ splice.addedCount += current.addedCount - intersectCount;
1053
+ const deleteCount = splice.removed.length + current.removed.length - intersectCount;
1054
+ if (!splice.addedCount && !deleteCount) {
1055
+ // merged splice is a noop. discard.
1056
+ inserted = true;
1057
+ }
1058
+ else {
1059
+ let currentRemoved = current.removed;
1060
+ if (splice.index < current.index) {
1061
+ // some prefix of splice.removed is prepended to current.removed.
1062
+ const prepend = splice.removed.slice(0, current.index - splice.index);
1063
+ prepend.push(...currentRemoved);
1064
+ currentRemoved = prepend;
1065
+ }
1066
+ if (splice.index + splice.removed.length >
1067
+ current.index + current.addedCount) {
1068
+ // some suffix of splice.removed is appended to current.removed.
1069
+ const append = splice.removed.slice(current.index + current.addedCount - splice.index);
1070
+ currentRemoved.push(...append);
1071
+ }
1072
+ splice.removed = currentRemoved;
1073
+ if (current.index < splice.index) {
1074
+ splice.index = current.index;
1075
+ }
1076
+ }
1077
+ }
1078
+ else if (splice.index < current.index) {
1079
+ // Insert splice here.
1080
+ inserted = true;
1081
+ splices.splice(i, 0, splice);
1082
+ i++;
1083
+ const offset = splice.addedCount - splice.removed.length;
1084
+ current.index += offset;
1085
+ insertionOffset += offset;
1086
+ }
1087
+ }
1088
+ if (!inserted) {
1089
+ splices.push(splice);
1090
+ }
1091
+ }
1092
+ function project(array, changes) {
1093
+ let splices = [];
1094
+ const initialSplices = [];
1095
+ for (let i = 0, ii = changes.length; i < ii; i++) {
1096
+ merge(changes[i], initialSplices);
1097
+ }
1098
+ for (let i = 0, ii = initialSplices.length; i < ii; ++i) {
1099
+ const splice = initialSplices[i];
1100
+ if (splice.addedCount === 1 && splice.removed.length === 1) {
1101
+ if (splice.removed[0] !== array[splice.index]) {
1102
+ splices.push(splice);
1103
+ }
1104
+ continue;
1105
+ }
1106
+ splices = splices.concat(calc(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
1107
+ }
1108
+ return splices;
1109
+ }
1110
+ /**
1111
+ * A SpliceStrategy that attempts to merge all splices into the minimal set of
1112
+ * splices needed to represent the change from the old array to the new array.
1113
+ * @public
1114
+ */
825
1115
  let defaultSpliceStrategy = Object.freeze({
826
- support: SpliceStrategySupport.splice,
1116
+ support: SpliceStrategySupport.optimized,
827
1117
  normalize(previous, current, changes) {
828
- return previous === void 0 ? changes !== null && changes !== void 0 ? changes : emptyArray : resetSplices;
1118
+ if (previous === void 0) {
1119
+ if (changes === void 0) {
1120
+ return emptyArray;
1121
+ }
1122
+ return changes.length > 1 ? project(current, changes) : changes;
1123
+ }
1124
+ return resetSplices;
829
1125
  },
830
1126
  pop(array, observer, pop, args) {
831
1127
  const notEmpty = array.length > 0;
@@ -1010,7 +1306,7 @@ const ArrayObserver = Object.freeze({
1010
1306
  * @returns The length of the array.
1011
1307
  * @public
1012
1308
  */
1013
- function length(array) {
1309
+ function lengthOf(array) {
1014
1310
  if (!array) {
1015
1311
  return 0;
1016
1312
  }
@@ -1023,7 +1319,6 @@ function length(array) {
1023
1319
  return array.length;
1024
1320
  }
1025
1321
 
1026
- const styleSheetCache = new Map();
1027
1322
  let DefaultStyleStrategy;
1028
1323
  function reduceStyles(styles) {
1029
1324
  return styles
@@ -1094,42 +1389,26 @@ class ElementStyles {
1094
1389
  static setDefaultStrategy(Strategy) {
1095
1390
  DefaultStyleStrategy = Strategy;
1096
1391
  }
1392
+ /**
1393
+ * Normalizes a set of composable style options.
1394
+ * @param styles - The style options to normalize.
1395
+ * @returns A singular ElementStyles instance or undefined.
1396
+ */
1397
+ static normalize(styles) {
1398
+ return styles === void 0
1399
+ ? void 0
1400
+ : Array.isArray(styles)
1401
+ ? new ElementStyles(styles)
1402
+ : styles instanceof ElementStyles
1403
+ ? styles
1404
+ : new ElementStyles([styles]);
1405
+ }
1097
1406
  }
1098
1407
  /**
1099
1408
  * Indicates whether the DOM supports the adoptedStyleSheets feature.
1100
1409
  */
1101
1410
  ElementStyles.supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
1102
1411
  "replace" in CSSStyleSheet.prototype;
1103
- /**
1104
- * https://wicg.github.io/construct-stylesheets/
1105
- * https://developers.google.com/web/updates/2019/02/constructable-stylesheets
1106
- *
1107
- * @internal
1108
- */
1109
- class AdoptedStyleSheetsStrategy {
1110
- constructor(styles) {
1111
- this.sheets = styles.map((x) => {
1112
- if (x instanceof CSSStyleSheet) {
1113
- return x;
1114
- }
1115
- let sheet = styleSheetCache.get(x);
1116
- if (sheet === void 0) {
1117
- sheet = new CSSStyleSheet();
1118
- sheet.replaceSync(x);
1119
- styleSheetCache.set(x, sheet);
1120
- }
1121
- return sheet;
1122
- });
1123
- }
1124
- addStylesTo(target) {
1125
- target.adoptedStyleSheets = [...target.adoptedStyleSheets, ...this.sheets];
1126
- }
1127
- removeStylesFrom(target) {
1128
- const sheets = this.sheets;
1129
- target.adoptedStyleSheets = target.adoptedStyleSheets.filter((x) => sheets.indexOf(x) === -1);
1130
- }
1131
- }
1132
- ElementStyles.setDefaultStrategy(FAST.getById(5 /* KernelServiceId.styleSheetStrategy */, () => AdoptedStyleSheetsStrategy));
1133
1412
 
1134
1413
  const registry$1 = createTypeRegistry();
1135
1414
  /**
@@ -1238,11 +1517,11 @@ class CSSPartial {
1238
1517
  }
1239
1518
  return this.css;
1240
1519
  }
1241
- bind(el) {
1242
- el.$fastController.addStyles(this.styles);
1520
+ addedCallback(controller) {
1521
+ controller.addStyles(this.styles);
1243
1522
  }
1244
- unbind(el) {
1245
- el.$fastController.removeStyles(this.styles);
1523
+ removedCallback(controller) {
1524
+ controller.removeStyles(this.styles);
1246
1525
  }
1247
1526
  }
1248
1527
  CSSDirective.define(CSSPartial);
@@ -1309,9 +1588,9 @@ const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
1309
1588
  const interpolationStart = `${marker}{`;
1310
1589
  const interpolationEnd = `}${marker}`;
1311
1590
  const interpolationEndLength = interpolationEnd.length;
1312
- let id = 0;
1591
+ let id$1 = 0;
1313
1592
  /** @internal */
1314
- const nextId = () => `${marker}-${++id}`;
1593
+ const nextId = () => `${marker}-${++id$1}`;
1315
1594
  /**
1316
1595
  * Common APIs related to markup generation.
1317
1596
  * @public
@@ -1381,6 +1660,67 @@ const Parser = Object.freeze({
1381
1660
  },
1382
1661
  });
1383
1662
 
1663
+ /**
1664
+ * Bridges between ViewBehaviors and HostBehaviors, enabling a host to
1665
+ * control ViewBehaviors.
1666
+ * @public
1667
+ */
1668
+ const ViewBehaviorOrchestrator = Object.freeze({
1669
+ /**
1670
+ * Creates a ViewBehaviorOrchestrator.
1671
+ * @param source - The source to to associate behaviors with.
1672
+ * @returns A ViewBehaviorOrchestrator.
1673
+ */
1674
+ create(source) {
1675
+ const behaviors = [];
1676
+ const targets = {};
1677
+ let unbindables = null;
1678
+ let isConnected = false;
1679
+ return {
1680
+ source,
1681
+ context: ExecutionContext.default,
1682
+ targets,
1683
+ get isBound() {
1684
+ return isConnected;
1685
+ },
1686
+ addBehaviorFactory(factory, target) {
1687
+ const nodeId = factory.nodeId || (factory.nodeId = nextId());
1688
+ factory.id || (factory.id = nextId());
1689
+ this.addTarget(nodeId, target);
1690
+ this.addBehavior(factory.createBehavior());
1691
+ },
1692
+ addTarget(nodeId, target) {
1693
+ targets[nodeId] = target;
1694
+ },
1695
+ addBehavior(behavior) {
1696
+ behaviors.push(behavior);
1697
+ if (isConnected) {
1698
+ behavior.bind(this);
1699
+ }
1700
+ },
1701
+ onUnbind(unbindable) {
1702
+ if (unbindables === null) {
1703
+ unbindables = [];
1704
+ }
1705
+ unbindables.push(unbindable);
1706
+ },
1707
+ connectedCallback(controller) {
1708
+ if (!isConnected) {
1709
+ isConnected = true;
1710
+ behaviors.forEach(x => x.bind(this));
1711
+ }
1712
+ },
1713
+ disconnectedCallback(controller) {
1714
+ if (isConnected) {
1715
+ isConnected = false;
1716
+ if (unbindables !== null) {
1717
+ unbindables.forEach(x => x.unbind(this));
1718
+ }
1719
+ }
1720
+ },
1721
+ };
1722
+ },
1723
+ });
1384
1724
  const registry = createTypeRegistry();
1385
1725
  /**
1386
1726
  * Instructs the template engine to apply behavior to a node.
@@ -1420,6 +1760,22 @@ function htmlDirective(options) {
1420
1760
  HTMLDirective.define(type, options);
1421
1761
  };
1422
1762
  }
1763
+ /**
1764
+ * Captures a binding expression along with related information and capabilities.
1765
+ *
1766
+ * @public
1767
+ */
1768
+ class Binding {
1769
+ /**
1770
+ * Creates a binding.
1771
+ * @param evaluate - Evaluates the binding.
1772
+ * @param isVolatile - Indicates whether the binding is volatile.
1773
+ */
1774
+ constructor(evaluate, isVolatile = false) {
1775
+ this.evaluate = evaluate;
1776
+ this.isVolatile = isVolatile;
1777
+ }
1778
+ }
1423
1779
  /**
1424
1780
  * The type of HTML aspect to target.
1425
1781
  * @public
@@ -1457,26 +1813,22 @@ const Aspect = Object.freeze({
1457
1813
  *
1458
1814
  * @param directive - The directive to assign the aspect to.
1459
1815
  * @param value - The value to base the aspect determination on.
1816
+ * @remarks
1817
+ * If a falsy value is provided, then the content aspect will be assigned.
1460
1818
  */
1461
1819
  assign(directive, value) {
1462
- directive.sourceAspect = value;
1463
1820
  if (!value) {
1821
+ directive.aspectType = Aspect.content;
1464
1822
  return;
1465
1823
  }
1824
+ directive.sourceAspect = value;
1466
1825
  switch (value[0]) {
1467
1826
  case ":":
1468
1827
  directive.targetAspect = value.substring(1);
1469
- switch (directive.targetAspect) {
1470
- case "innerHTML":
1471
- directive.aspectType = Aspect.property;
1472
- break;
1473
- case "classList":
1474
- directive.aspectType = Aspect.tokenList;
1475
- break;
1476
- default:
1477
- directive.aspectType = Aspect.property;
1478
- break;
1479
- }
1828
+ directive.aspectType =
1829
+ directive.targetAspect === "classList"
1830
+ ? Aspect.tokenList
1831
+ : Aspect.property;
1480
1832
  break;
1481
1833
  case "?":
1482
1834
  directive.targetAspect = value.substring(1);
@@ -1487,14 +1839,8 @@ const Aspect = Object.freeze({
1487
1839
  directive.aspectType = Aspect.event;
1488
1840
  break;
1489
1841
  default:
1490
- if (value === "class") {
1491
- directive.targetAspect = "className";
1492
- directive.aspectType = Aspect.property;
1493
- }
1494
- else {
1495
- directive.targetAspect = value;
1496
- directive.aspectType = Aspect.attribute;
1497
- }
1842
+ directive.targetAspect = value;
1843
+ directive.aspectType = Aspect.attribute;
1498
1844
  break;
1499
1845
  }
1500
1846
  },
@@ -1510,13 +1856,10 @@ class StatelessAttachedAttributeDirective {
1510
1856
  */
1511
1857
  constructor(options) {
1512
1858
  this.options = options;
1513
- }
1514
- /**
1515
- * Creates a behavior.
1516
- * @param targets - The targets available for behaviors to be attached to.
1517
- */
1518
- createBehavior(targets) {
1519
- return this;
1859
+ /**
1860
+ * The unique id of the factory.
1861
+ */
1862
+ this.id = nextId();
1520
1863
  }
1521
1864
  /**
1522
1865
  * Creates a placeholder string based on the directive's index within the template.
@@ -1527,8 +1870,15 @@ class StatelessAttachedAttributeDirective {
1527
1870
  createHTML(add) {
1528
1871
  return Markup.attribute(add(this));
1529
1872
  }
1530
- }
1531
-
1873
+ /**
1874
+ * Creates a behavior.
1875
+ * @param targets - The targets available for behaviors to be attached to.
1876
+ */
1877
+ createBehavior() {
1878
+ return this;
1879
+ }
1880
+ }
1881
+
1532
1882
  const createInnerHTMLBinding = globalThis.TrustedHTML
1533
1883
  ? (binding) => (s, c) => {
1534
1884
  const value = binding(s, c);
@@ -1538,107 +1888,26 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1538
1888
  throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1539
1889
  }
1540
1890
  : (binding) => binding;
1541
- /**
1542
- * Describes how aspects of an HTML element will be affected by bindings.
1543
- * @public
1544
- */
1545
- const BindingMode = Object.freeze({
1546
- /**
1547
- * Creates a binding mode based on the supplied behavior types.
1548
- * @param UpdateType - The base behavior type used to update aspects.
1549
- * @param EventType - The base behavior type used to respond to events.
1550
- * @returns A new binding mode.
1551
- */
1552
- define(UpdateType, EventType = EventBinding) {
1553
- return Object.freeze({
1554
- [1]: d => new UpdateType(d, DOM.setAttribute),
1555
- [2]: d => new UpdateType(d, DOM.setBooleanAttribute),
1556
- [3]: d => new UpdateType(d, (t, a, v) => (t[a] = v)),
1557
- [4]: d => new (createContentBinding(UpdateType))(d, updateContentTarget),
1558
- [5]: d => new UpdateType(d, updateTokenListTarget),
1559
- [6]: d => new EventType(d),
1560
- });
1561
- },
1562
- });
1563
- /**
1564
- * Describes the configuration for a binding expression.
1565
- * @public
1566
- */
1567
- const BindingConfig = Object.freeze({
1568
- /**
1569
- * Creates a binding configuration based on the provided mode and options.
1570
- * @param mode - The mode to use for the configuration.
1571
- * @param defaultOptions - The default options to use for the configuration.
1572
- * @returns A new binding configuration.
1573
- */
1574
- define(mode, defaultOptions) {
1575
- const config = (options) => {
1576
- return {
1577
- mode: config.mode,
1578
- options: Object.assign({}, defaultOptions, options),
1579
- };
1580
- };
1581
- config.options = defaultOptions;
1582
- config.mode = mode;
1583
- return config;
1584
- },
1585
- });
1586
- /**
1587
- * A base binding behavior for DOM updates.
1588
- * @public
1589
- */
1590
- class UpdateBinding {
1591
- /**
1592
- * Creates an instance of UpdateBinding.
1593
- * @param directive - The directive that has the configuration for this behavior.
1594
- * @param updateTarget - The function used to update the target with the latest value.
1595
- */
1596
- constructor(directive, updateTarget) {
1597
- this.directive = directive;
1598
- this.updateTarget = updateTarget;
1891
+ class OnChangeBinding extends Binding {
1892
+ createObserver(_, subscriber) {
1893
+ return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1599
1894
  }
1600
- /**
1601
- * Bind this behavior to the source.
1602
- * @param source - The source to bind to.
1603
- * @param context - The execution context that the binding is operating within.
1604
- * @param targets - The targets that behaviors in a view can attach to.
1605
- */
1606
- bind(source, context, targets) { }
1607
- /**
1608
- * Unbinds this behavior from the source.
1609
- * @param source - The source to unbind from.
1610
- * @param context - The execution context that the binding is operating within.
1611
- * @param targets - The targets that behaviors in a view can attach to.
1612
- */
1613
- unbind(source, context, targets) { }
1614
- /**
1615
- * Creates a behavior.
1616
- * @param targets - The targets available for behaviors to be attached to.
1617
- */
1618
- createBehavior(targets) {
1895
+ }
1896
+ class OneTimeBinding extends Binding {
1897
+ createObserver() {
1619
1898
  return this;
1620
1899
  }
1900
+ bind(controller) {
1901
+ return this.evaluate(controller.source, controller.context);
1902
+ }
1621
1903
  }
1622
- function createContentBinding(Type) {
1623
- return class extends Type {
1624
- unbind(source, context, targets) {
1625
- super.unbind(source, context, targets);
1626
- const target = targets[this.directive.nodeId];
1627
- const view = target.$fastView;
1628
- if (view !== void 0 && view.isComposed) {
1629
- view.unbind();
1630
- view.needsBindOnly = true;
1631
- }
1632
- }
1633
- };
1634
- }
1635
- function updateContentTarget(target, aspect, value, source, context) {
1904
+ function updateContent(target, aspect, value, controller) {
1636
1905
  // If there's no actual value, then this equates to the
1637
1906
  // empty string for the purposes of content bindings.
1638
1907
  if (value === null || value === undefined) {
1639
1908
  value = "";
1640
1909
  }
1641
- // If the value has a "create" method, then it's a template-like.
1910
+ // If the value has a "create" method, then it's a ContentTemplate.
1642
1911
  if (value.create) {
1643
1912
  target.textContent = "";
1644
1913
  let view = target.$fastView;
@@ -1664,14 +1933,14 @@ function updateContentTarget(target, aspect, value, source, context) {
1664
1933
  // and that there's actually no need to compose it.
1665
1934
  if (!view.isComposed) {
1666
1935
  view.isComposed = true;
1667
- view.bind(source, context);
1936
+ view.bind(controller.source, controller.context);
1668
1937
  view.insertBefore(target);
1669
1938
  target.$fastView = view;
1670
1939
  target.$fastTemplate = value;
1671
1940
  }
1672
1941
  else if (view.needsBindOnly) {
1673
1942
  view.needsBindOnly = false;
1674
- view.bind(source, context);
1943
+ view.bind(controller.source, controller.context);
1675
1944
  }
1676
1945
  }
1677
1946
  else {
@@ -1691,10 +1960,9 @@ function updateContentTarget(target, aspect, value, source, context) {
1691
1960
  target.textContent = value;
1692
1961
  }
1693
1962
  }
1694
- function updateTokenListTarget(target, aspect, value) {
1963
+ function updateTokenList(target, aspect, value) {
1695
1964
  var _a;
1696
- const directive = this.directive;
1697
- const lookup = `${directive.id}-t`;
1965
+ const lookup = `${this.id}-t`;
1698
1966
  const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { c: 0, v: Object.create(null) });
1699
1967
  const versions = state.v;
1700
1968
  let currentVersion = state.c;
@@ -1724,366 +1992,168 @@ function updateTokenListTarget(target, aspect, value) {
1724
1992
  }
1725
1993
  }
1726
1994
  }
1995
+ const setProperty = (t, a, v) => (t[a] = v);
1996
+ const eventTarget = () => void 0;
1727
1997
  /**
1728
- * A binding behavior for one-time bindings.
1998
+ * A directive that applies bindings.
1729
1999
  * @public
1730
2000
  */
1731
- class OneTimeBinding extends UpdateBinding {
2001
+ class HTMLBindingDirective {
1732
2002
  /**
1733
- * Bind this behavior to the source.
1734
- * @param source - The source to bind to.
1735
- * @param context - The execution context that the binding is operating within.
1736
- * @param targets - The targets that behaviors in a view can attach to.
2003
+ * Creates an instance of HTMLBindingDirective.
2004
+ * @param dataBinding - The binding configuration to apply.
1737
2005
  */
1738
- bind(source, context, targets) {
1739
- const directive = this.directive;
1740
- this.updateTarget(targets[directive.nodeId], directive.targetAspect, directive.binding(source, context), source, context);
1741
- }
1742
- }
1743
- const signals = Object.create(null);
1744
- /**
1745
- * A binding behavior for signal bindings.
1746
- * @public
1747
- */
1748
- class SignalBinding extends UpdateBinding {
1749
- constructor() {
1750
- super(...arguments);
1751
- this.handlerProperty = `${this.directive.id}-h`;
2006
+ constructor(dataBinding) {
2007
+ this.dataBinding = dataBinding;
2008
+ this.updateTarget = null;
2009
+ /**
2010
+ * The unique id of the factory.
2011
+ */
2012
+ this.id = nextId();
2013
+ /**
2014
+ * The type of aspect to target.
2015
+ */
2016
+ this.aspectType = Aspect.content;
2017
+ /** @internal */
2018
+ this.bind = this.bindDefault;
2019
+ this.data = `${this.id}-d`;
1752
2020
  }
1753
2021
  /**
1754
- * Bind this behavior to the source.
1755
- * @param source - The source to bind to.
1756
- * @param context - The execution context that the binding is operating within.
1757
- * @param targets - The targets that behaviors in a view can attach to.
2022
+ * Creates HTML to be used within a template.
2023
+ * @param add - Can be used to add behavior factories to a template.
1758
2024
  */
1759
- bind(source, context, targets) {
1760
- const directive = this.directive;
1761
- const target = targets[directive.nodeId];
1762
- const signal = this.getSignal(source, context);
1763
- const handler = (target[this.handlerProperty] = () => {
1764
- this.updateTarget(target, directive.targetAspect, directive.binding(source, context), source, context);
1765
- });
1766
- handler();
1767
- const found = signals[signal];
1768
- if (found) {
1769
- Array.isArray(found)
1770
- ? found.push(handler)
1771
- : (signals[signal] = [found, handler]);
1772
- }
1773
- else {
1774
- signals[signal] = handler;
1775
- }
2025
+ createHTML(add) {
2026
+ return Markup.interpolation(add(this));
1776
2027
  }
1777
2028
  /**
1778
- * Unbinds this behavior from the source.
1779
- * @param source - The source to unbind from.
1780
- * @param context - The execution context that the binding is operating within.
1781
- * @param targets - The targets that behaviors in a view can attach to.
2029
+ * Creates a behavior.
1782
2030
  */
1783
- unbind(source, context, targets) {
1784
- const signal = this.getSignal(source, context);
1785
- const found = signals[signal];
1786
- if (found && Array.isArray(found)) {
1787
- const directive = this.directive;
1788
- const target = targets[directive.nodeId];
1789
- const handler = target[this.handlerProperty];
1790
- const index = found.indexOf(handler);
1791
- if (index !== -1) {
1792
- found.splice(index, 1);
2031
+ createBehavior() {
2032
+ if (this.updateTarget === null) {
2033
+ if (this.targetAspect === "innerHTML") {
2034
+ this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
2035
+ }
2036
+ switch (this.aspectType) {
2037
+ case 1:
2038
+ this.updateTarget = DOM.setAttribute;
2039
+ break;
2040
+ case 2:
2041
+ this.updateTarget = DOM.setBooleanAttribute;
2042
+ break;
2043
+ case 3:
2044
+ this.updateTarget = setProperty;
2045
+ break;
2046
+ case 4:
2047
+ this.bind = this.bindContent;
2048
+ this.updateTarget = updateContent;
2049
+ break;
2050
+ case 5:
2051
+ this.updateTarget = updateTokenList;
2052
+ break;
2053
+ case 6:
2054
+ this.bind = this.bindEvent;
2055
+ this.updateTarget = eventTarget;
2056
+ break;
2057
+ default:
2058
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
1793
2059
  }
1794
2060
  }
1795
- else {
1796
- signals[signal] = void 0;
1797
- }
1798
- }
1799
- getSignal(source, context) {
1800
- const options = this.directive.options;
1801
- return isString(options) ? options : options(source, context);
1802
- }
1803
- /**
1804
- * Sends the specified signal to signaled bindings.
1805
- * @param signal - The signal to send.
1806
- * @public
1807
- */
1808
- static send(signal) {
1809
- const found = signals[signal];
1810
- if (found) {
1811
- Array.isArray(found) ? found.forEach(x => x()) : found();
1812
- }
1813
- }
1814
- }
1815
- /**
1816
- * A binding behavior for bindings that change.
1817
- * @public
1818
- */
1819
- class ChangeBinding extends UpdateBinding {
1820
- /**
1821
- * Creates an instance of ChangeBinding.
1822
- * @param directive - The directive that has the configuration for this behavior.
1823
- * @param updateTarget - The function used to update the target with the latest value.
1824
- */
1825
- constructor(directive, updateTarget) {
1826
- super(directive, updateTarget);
1827
- this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
1828
- this.observerProperty = `${directive.id}-o`;
2061
+ return this;
1829
2062
  }
1830
- /**
1831
- * Returns the binding observer used to update the node.
1832
- * @param target - The target node.
1833
- * @returns A BindingObserver.
1834
- */
1835
- getObserver(target) {
2063
+ /** @internal */
2064
+ bindDefault(controller) {
1836
2065
  var _a;
1837
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = Observable.binding(this.directive.binding, this, this.isBindingVolatile)));
1838
- }
1839
- /**
1840
- * Bind this behavior to the source.
1841
- * @param source - The source to bind to.
1842
- * @param context - The execution context that the binding is operating within.
1843
- * @param targets - The targets that behaviors in a view can attach to.
1844
- */
1845
- bind(source, context, targets) {
1846
- const directive = this.directive;
1847
- const target = targets[directive.nodeId];
1848
- const observer = this.getObserver(target);
2066
+ const target = controller.targets[this.nodeId];
2067
+ const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
1849
2068
  observer.target = target;
1850
- observer.source = source;
1851
- observer.context = context;
1852
- this.updateTarget(target, directive.targetAspect, observer.observe(source, context), source, context);
1853
- }
1854
- /**
1855
- * Unbinds this behavior from the source.
1856
- * @param source - The source to unbind from.
1857
- * @param context - The execution context that the binding is operating within.
1858
- * @param targets - The targets that behaviors in a view can attach to.
1859
- */
1860
- unbind(source, context, targets) {
1861
- const target = targets[this.directive.nodeId];
1862
- const observer = this.getObserver(target);
1863
- observer.dispose();
1864
- observer.target = null;
1865
- observer.source = null;
1866
- observer.context = null;
2069
+ observer.controller = controller;
2070
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2071
+ if (this.updateTarget === updateContent) {
2072
+ controller.onUnbind(this);
2073
+ }
1867
2074
  }
1868
2075
  /** @internal */
1869
- handleChange(binding, observer) {
1870
- const target = observer.target;
1871
- const source = observer.source;
1872
- const context = observer.context;
1873
- this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
1874
- }
1875
- }
1876
- /**
1877
- * A binding behavior for handling events.
1878
- * @public
1879
- */
1880
- class EventBinding {
1881
- /**
1882
- * Creates an instance of EventBinding.
1883
- * @param directive - The directive that has the configuration for this behavior.
1884
- */
1885
- constructor(directive) {
1886
- this.directive = directive;
1887
- this.sourceProperty = `${directive.id}-s`;
1888
- this.contextProperty = `${directive.id}-c`;
2076
+ bindContent(controller) {
2077
+ this.bindDefault(controller);
2078
+ controller.onUnbind(this);
1889
2079
  }
1890
- /**
1891
- * Bind this behavior to the source.
1892
- * @param source - The source to bind to.
1893
- * @param context - The execution context that the binding is operating within.
1894
- * @param targets - The targets that behaviors in a view can attach to.
1895
- */
1896
- bind(source, context, targets) {
1897
- const directive = this.directive;
1898
- const target = targets[directive.nodeId];
1899
- target[this.sourceProperty] = source;
1900
- target[this.contextProperty] = context;
1901
- target.addEventListener(directive.targetAspect, this, directive.options);
1902
- }
1903
- /**
1904
- * Unbinds this behavior from the source.
1905
- * @param source - The source to unbind from.
1906
- * @param context - The execution context that the binding is operating within.
1907
- * @param targets - The targets that behaviors in a view can attach to.
1908
- */
1909
- unbind(source, context, targets) {
1910
- const directive = this.directive;
1911
- const target = targets[directive.nodeId];
1912
- target[this.sourceProperty] = target[this.contextProperty] = null;
1913
- target.removeEventListener(directive.targetAspect, this, directive.options);
2080
+ /** @internal */
2081
+ bindEvent(controller) {
2082
+ const target = controller.targets[this.nodeId];
2083
+ target[this.data] = controller;
2084
+ target.addEventListener(this.targetAspect, this, this.dataBinding.options);
1914
2085
  }
1915
- /**
1916
- * Creates a behavior.
1917
- * @param targets - The targets available for behaviors to be attached to.
1918
- */
1919
- createBehavior(targets) {
1920
- return this;
2086
+ /** @internal */
2087
+ unbind(controller) {
2088
+ const target = controller.targets[this.nodeId];
2089
+ const view = target.$fastView;
2090
+ if (view !== void 0 && view.isComposed) {
2091
+ view.unbind();
2092
+ view.needsBindOnly = true;
2093
+ }
1921
2094
  }
1922
- /**
1923
- * @internal
1924
- */
2095
+ /** @internal */
1925
2096
  handleEvent(event) {
1926
2097
  const target = event.currentTarget;
1927
2098
  ExecutionContext.setEvent(event);
1928
- const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
2099
+ const controller = target[this.data];
2100
+ const result = this.dataBinding.evaluate(controller.source, controller.context);
1929
2101
  ExecutionContext.setEvent(null);
1930
2102
  if (result !== true) {
1931
2103
  event.preventDefault();
1932
2104
  }
1933
2105
  }
1934
- }
1935
- let twoWaySettings = {
1936
- determineChangeEvent() {
1937
- return "change";
1938
- },
1939
- };
1940
- /**
1941
- * A binding behavior for bindings that update in two directions.
1942
- * @public
1943
- */
1944
- class TwoWayBinding extends ChangeBinding {
1945
- /**
1946
- * Bind this behavior to the source.
1947
- * @param source - The source to bind to.
1948
- * @param context - The execution context that the binding is operating within.
1949
- * @param targets - The targets that behaviors in a view can attach to.
1950
- */
1951
- bind(source, context, targets) {
1952
- var _a;
1953
- super.bind(source, context, targets);
1954
- const directive = this.directive;
1955
- const target = targets[directive.nodeId];
1956
- if (!this.changeEvent) {
1957
- this.changeEvent =
1958
- (_a = directive.options.changeEvent) !== null && _a !== void 0 ? _a : twoWaySettings.determineChangeEvent(directive, target);
1959
- }
1960
- target.addEventListener(this.changeEvent, this);
1961
- }
1962
- /**
1963
- * Unbinds this behavior from the source.
1964
- * @param source - The source to unbind from.
1965
- * @param context - The execution context that the binding is operating within.
1966
- * @param targets - The targets that behaviors in a view can attach to.
1967
- */
1968
- unbind(source, context, targets) {
1969
- super.unbind(source, context, targets);
1970
- targets[this.directive.nodeId].removeEventListener(this.changeEvent, this);
1971
- }
1972
2106
  /** @internal */
1973
- handleEvent(event) {
1974
- const directive = this.directive;
1975
- const target = event.currentTarget;
1976
- let value;
1977
- switch (directive.aspectType) {
1978
- case 1:
1979
- value = target.getAttribute(directive.targetAspect);
1980
- break;
1981
- case 2:
1982
- value = target.hasAttribute(directive.targetAspect);
1983
- break;
1984
- case 4:
1985
- value = target.innerText;
1986
- break;
1987
- default:
1988
- value = target[directive.targetAspect];
1989
- break;
1990
- }
1991
- const observer = this.getObserver(target);
1992
- const last = observer.last; // using internal API!!!
1993
- last.propertySource[last.propertyName] = directive.options.fromView(value);
1994
- }
1995
- /**
1996
- * Configures two-way binding.
1997
- * @param settings - The settings to use for the two-way binding system.
1998
- */
1999
- static configure(settings) {
2000
- twoWaySettings = settings;
2107
+ handleChange(binding, observer) {
2108
+ const target = observer.target;
2109
+ const controller = observer.controller;
2110
+ this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2001
2111
  }
2002
2112
  }
2113
+ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
2003
2114
  /**
2004
- * The default onChange binding configuration.
2005
- * @public
2006
- */
2007
- const onChange = BindingConfig.define(BindingMode.define(ChangeBinding), {});
2008
- /**
2009
- * The default twoWay binding configuration.
2010
- * @public
2011
- */
2012
- const twoWay = BindingConfig.define(BindingMode.define(TwoWayBinding), {
2013
- fromView: v => v,
2014
- });
2015
- /**
2016
- * The default onTime binding configuration.
2115
+ * Creates an standard binding.
2116
+ * @param binding - The binding to refresh when changed.
2117
+ * @param isVolatile - Indicates whether the binding is volatile or not.
2118
+ * @returns A binding configuration.
2017
2119
  * @public
2018
2120
  */
2019
- const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
2020
- once: true,
2021
- });
2022
- const signalMode = BindingMode.define(SignalBinding);
2121
+ function bind(binding, isVolatile = Observable.isVolatileBinding(binding)) {
2122
+ return new OnChangeBinding(binding, isVolatile);
2123
+ }
2023
2124
  /**
2024
- * Creates a signal binding configuration with the supplied options.
2025
- * @param options - The signal name or a binding to use to retrieve the signal name.
2125
+ * Creates a one time binding
2126
+ * @param binding - The binding to refresh when signaled.
2026
2127
  * @returns A binding configuration.
2027
2128
  * @public
2028
2129
  */
2029
- const signal = (options) => {
2030
- return { mode: signalMode, options };
2031
- };
2130
+ function oneTime(binding) {
2131
+ return new OneTimeBinding(binding);
2132
+ }
2032
2133
  /**
2033
- * A directive that applies bindings.
2134
+ * Creates an event listener binding.
2135
+ * @param binding - The binding to invoke when the event is raised.
2136
+ * @param options - Event listener options.
2137
+ * @returns A binding configuration.
2034
2138
  * @public
2035
2139
  */
2036
- class HTMLBindingDirective {
2037
- /**
2038
- * Creates an instance of HTMLBindingDirective.
2039
- * @param binding - The binding to apply.
2040
- * @param mode - The binding mode to use when applying the binding.
2041
- * @param options - The options to configure the binding with.
2042
- */
2043
- constructor(binding, mode, options) {
2044
- this.binding = binding;
2045
- this.mode = mode;
2046
- this.options = options;
2047
- this.factory = null;
2048
- /**
2049
- * The type of aspect to target.
2050
- */
2051
- this.aspectType = Aspect.content;
2052
- }
2053
- /**
2054
- * Creates HTML to be used within a template.
2055
- * @param add - Can be used to add behavior factories to a template.
2056
- */
2057
- createHTML(add) {
2058
- return Markup.interpolation(add(this));
2059
- }
2060
- /**
2061
- * Creates a behavior.
2062
- * @param targets - The targets available for behaviors to be attached to.
2063
- */
2064
- createBehavior(targets) {
2065
- if (this.factory == null) {
2066
- if (this.targetAspect === "innerHTML") {
2067
- this.binding = createInnerHTMLBinding(this.binding);
2068
- }
2069
- this.factory = this.mode[this.aspectType](this);
2070
- }
2071
- return this.factory.createBehavior(targets);
2072
- }
2140
+ function listener(binding, options) {
2141
+ const config = new OnChangeBinding(binding, false);
2142
+ config.options = options;
2143
+ return config;
2073
2144
  }
2074
- HTMLDirective.define(HTMLBindingDirective, { aspected: true });
2075
2145
  /**
2076
- * Creates a binding directive with the specified configuration.
2077
- * @param binding - The binding expression.
2078
- * @param config - The binding configuration.
2079
- * @returns A binding directive.
2146
+ * Normalizes the input value into a binding.
2147
+ * @param value - The value to create the default binding for.
2148
+ * @returns A binding configuration for the provided value.
2080
2149
  * @public
2081
2150
  */
2082
- function bind(binding, config = onChange) {
2083
- if (!("mode" in config)) {
2084
- config = onChange(config);
2085
- }
2086
- return new HTMLBindingDirective(binding, config.mode, config.options);
2151
+ function normalizeBinding(value) {
2152
+ return isFunction(value)
2153
+ ? bind(value)
2154
+ : value instanceof Binding
2155
+ ? value
2156
+ : oneTime(() => value);
2087
2157
  }
2088
2158
 
2089
2159
  function removeNodeSequence(firstNode, lastNode) {
@@ -2107,21 +2177,91 @@ class HTMLView {
2107
2177
  * @param fragment - The html fragment that contains the nodes for this view.
2108
2178
  * @param behaviors - The behaviors to be applied to this view.
2109
2179
  */
2110
- constructor(fragment, factories, targets) {
2111
- this.fragment = fragment;
2112
- this.factories = factories;
2113
- this.targets = targets;
2114
- this.behaviors = null;
2115
- /**
2116
- * The data that the view is bound to.
2117
- */
2118
- this.source = null;
2119
- /**
2120
- * The execution context the view is running within.
2121
- */
2122
- this.context = null;
2123
- this.firstChild = fragment.firstChild;
2124
- this.lastChild = fragment.lastChild;
2180
+ constructor(fragment, factories, targets) {
2181
+ this.fragment = fragment;
2182
+ this.factories = factories;
2183
+ this.targets = targets;
2184
+ this.behaviors = null;
2185
+ this.unbindables = [];
2186
+ /**
2187
+ * The data that the view is bound to.
2188
+ */
2189
+ this.source = null;
2190
+ /**
2191
+ * Indicates whether the controller is bound.
2192
+ */
2193
+ this.isBound = false;
2194
+ /**
2195
+ * Indicates how the source's lifetime relates to the controller's lifetime.
2196
+ */
2197
+ this.sourceLifetime = SourceLifetime.unknown;
2198
+ /**
2199
+ * The execution context the view is running within.
2200
+ */
2201
+ this.context = this;
2202
+ /**
2203
+ * The index of the current item within a repeat context.
2204
+ */
2205
+ this.index = 0;
2206
+ /**
2207
+ * The length of the current collection within a repeat context.
2208
+ */
2209
+ this.length = 0;
2210
+ this.firstChild = fragment.firstChild;
2211
+ this.lastChild = fragment.lastChild;
2212
+ }
2213
+ /**
2214
+ * The current event within an event handler.
2215
+ */
2216
+ get event() {
2217
+ return ExecutionContext.getEvent();
2218
+ }
2219
+ /**
2220
+ * Indicates whether the current item within a repeat context
2221
+ * has an even index.
2222
+ */
2223
+ get isEven() {
2224
+ return this.index % 2 === 0;
2225
+ }
2226
+ /**
2227
+ * Indicates whether the current item within a repeat context
2228
+ * has an odd index.
2229
+ */
2230
+ get isOdd() {
2231
+ return this.index % 2 !== 0;
2232
+ }
2233
+ /**
2234
+ * Indicates whether the current item within a repeat context
2235
+ * is the first item in the collection.
2236
+ */
2237
+ get isFirst() {
2238
+ return this.index === 0;
2239
+ }
2240
+ /**
2241
+ * Indicates whether the current item within a repeat context
2242
+ * is somewhere in the middle of the collection.
2243
+ */
2244
+ get isInMiddle() {
2245
+ return !this.isFirst && !this.isLast;
2246
+ }
2247
+ /**
2248
+ * Indicates whether the current item within a repeat context
2249
+ * is the last item in the collection.
2250
+ */
2251
+ get isLast() {
2252
+ return this.index === this.length - 1;
2253
+ }
2254
+ /**
2255
+ * Returns the typed event detail of a custom event.
2256
+ */
2257
+ eventDetail() {
2258
+ return this.event.detail;
2259
+ }
2260
+ /**
2261
+ * Returns the typed event target of the event.
2262
+ */
2263
+ eventTarget() {
2264
+ return this.event.target;
2125
2265
  }
2126
2266
  /**
2127
2267
  * Appends the view's DOM nodes to the referenced node.
@@ -2139,8 +2279,10 @@ class HTMLView {
2139
2279
  node.parentNode.insertBefore(this.fragment, node);
2140
2280
  }
2141
2281
  else {
2142
- const parentNode = node.parentNode;
2143
2282
  const end = this.lastChild;
2283
+ if (node.previousSibling === end)
2284
+ return;
2285
+ const parentNode = node.parentNode;
2144
2286
  let current = this.firstChild;
2145
2287
  let next;
2146
2288
  while (current !== end) {
@@ -2175,58 +2317,61 @@ class HTMLView {
2175
2317
  removeNodeSequence(this.firstChild, this.lastChild);
2176
2318
  this.unbind();
2177
2319
  }
2320
+ onUnbind(behavior) {
2321
+ this.unbindables.push(behavior);
2322
+ }
2178
2323
  /**
2179
2324
  * Binds a view's behaviors to its binding source.
2180
2325
  * @param source - The binding source for the view's binding behaviors.
2181
2326
  * @param context - The execution context to run the behaviors within.
2182
2327
  */
2183
- bind(source, context) {
2184
- let behaviors = this.behaviors;
2185
- const oldSource = this.source;
2186
- if (oldSource === source) {
2328
+ bind(source, context = this) {
2329
+ if (this.source === source) {
2187
2330
  return;
2188
2331
  }
2189
- this.source = source;
2190
- this.context = context;
2191
- const targets = this.targets;
2192
- if (oldSource !== null) {
2193
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2194
- const current = behaviors[i];
2195
- current.unbind(oldSource, context, targets);
2196
- current.bind(source, context, targets);
2197
- }
2198
- }
2199
- else if (behaviors === null) {
2332
+ let behaviors = this.behaviors;
2333
+ if (behaviors === null) {
2334
+ this.source = source;
2335
+ this.context = context;
2200
2336
  this.behaviors = behaviors = new Array(this.factories.length);
2201
2337
  const factories = this.factories;
2202
2338
  for (let i = 0, ii = factories.length; i < ii; ++i) {
2203
- const behavior = factories[i].createBehavior(targets);
2204
- behavior.bind(source, context, targets);
2339
+ const behavior = factories[i].createBehavior();
2340
+ behavior.bind(this);
2205
2341
  behaviors[i] = behavior;
2206
2342
  }
2207
2343
  }
2208
2344
  else {
2345
+ if (this.source !== null) {
2346
+ this.evaluateUnbindables();
2347
+ }
2348
+ this.isBound = false;
2349
+ this.source = source;
2350
+ this.context = context;
2209
2351
  for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2210
- behaviors[i].bind(source, context, targets);
2352
+ behaviors[i].bind(this);
2211
2353
  }
2212
2354
  }
2355
+ this.isBound = true;
2213
2356
  }
2214
2357
  /**
2215
2358
  * Unbinds a view's behaviors from its binding source.
2216
2359
  */
2217
2360
  unbind() {
2218
- const oldSource = this.source;
2219
- if (oldSource === null) {
2361
+ if (!this.isBound || this.source === null) {
2220
2362
  return;
2221
2363
  }
2222
- const targets = this.targets;
2223
- const context = this.context;
2224
- const behaviors = this.behaviors;
2225
- for (let i = 0, ii = behaviors.length; i < ii; ++i) {
2226
- behaviors[i].unbind(oldSource, context, targets);
2227
- }
2364
+ this.evaluateUnbindables();
2228
2365
  this.source = null;
2229
- this.context = null;
2366
+ this.context = this;
2367
+ this.isBound = false;
2368
+ }
2369
+ evaluateUnbindables() {
2370
+ const unbindables = this.unbindables;
2371
+ for (let i = 0, ii = unbindables.length; i < ii; ++i) {
2372
+ unbindables[i].unbind(this);
2373
+ }
2374
+ unbindables.length = 0;
2230
2375
  }
2231
2376
  /**
2232
2377
  * Efficiently disposes of a contiguous range of synthetic view instances.
@@ -2242,6 +2387,8 @@ class HTMLView {
2242
2387
  }
2243
2388
  }
2244
2389
  }
2390
+ Observable.defineProperty(HTMLView.prototype, "index");
2391
+ Observable.defineProperty(HTMLView.prototype, "length");
2245
2392
 
2246
2393
  const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
2247
2394
  const descriptorCache = {};
@@ -2250,6 +2397,22 @@ const next = {
2250
2397
  index: 0,
2251
2398
  node: null,
2252
2399
  };
2400
+ function tryWarn(name) {
2401
+ if (!name.startsWith("fast-")) {
2402
+ FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
2403
+ }
2404
+ }
2405
+ const warningHost = new Proxy(document.createElement("div"), {
2406
+ get(target, property) {
2407
+ tryWarn(property);
2408
+ const value = Reflect.get(target, property);
2409
+ return isFunction(value) ? value.bind(target) : value;
2410
+ },
2411
+ set(target, property, value) {
2412
+ tryWarn(property);
2413
+ return Reflect.set(target, property, value);
2414
+ },
2415
+ });
2253
2416
  class CompilationContext {
2254
2417
  constructor(fragment, directives) {
2255
2418
  this.fragment = fragment;
@@ -2300,7 +2463,7 @@ class CompilationContext {
2300
2463
  const fragment = this.fragment.cloneNode(true);
2301
2464
  const targets = Object.create(this.proto);
2302
2465
  targets.r = fragment;
2303
- targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : fragment;
2466
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
2304
2467
  for (const id of this.nodeIds) {
2305
2468
  targets[id]; // trigger locator
2306
2469
  }
@@ -2317,7 +2480,7 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2317
2480
  let result = null;
2318
2481
  if (parseResult === null) {
2319
2482
  if (includeBasicValues) {
2320
- result = bind(() => attrValue, oneTime);
2483
+ result = new HTMLBindingDirective(oneTime(() => attrValue));
2321
2484
  Aspect.assign(result, attr.name);
2322
2485
  }
2323
2486
  }
@@ -2354,6 +2517,7 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
2354
2517
  }
2355
2518
  else {
2356
2519
  currentNode.textContent = " ";
2520
+ Aspect.assign(currentPart);
2357
2521
  context.addFactory(currentPart, parentId, nodeId, nodeIndex);
2358
2522
  }
2359
2523
  lastNode = currentNode;
@@ -2486,22 +2650,28 @@ const Compiler = {
2486
2650
  return parts[0];
2487
2651
  }
2488
2652
  let sourceAspect;
2653
+ let binding;
2654
+ let isVolatile = false;
2489
2655
  const partCount = parts.length;
2490
2656
  const finalParts = parts.map((x) => {
2491
2657
  if (isString(x)) {
2492
2658
  return () => x;
2493
2659
  }
2494
2660
  sourceAspect = x.sourceAspect || sourceAspect;
2495
- return x.binding;
2661
+ binding = x.dataBinding || binding;
2662
+ isVolatile = isVolatile || x.dataBinding.isVolatile;
2663
+ return x.dataBinding.evaluate;
2496
2664
  });
2497
- const binding = (scope, context) => {
2665
+ const expression = (scope, context) => {
2498
2666
  let output = "";
2499
2667
  for (let i = 0; i < partCount; ++i) {
2500
2668
  output += finalParts[i](scope, context);
2501
2669
  }
2502
2670
  return output;
2503
2671
  };
2504
- const directive = bind(binding);
2672
+ binding.evaluate = expression;
2673
+ binding.isVolatile = isVolatile;
2674
+ const directive = new HTMLBindingDirective(binding);
2505
2675
  Aspect.assign(directive, sourceAspect);
2506
2676
  return directive;
2507
2677
  },
@@ -2539,9 +2709,9 @@ class ViewTemplate {
2539
2709
  * @param hostBindingTarget - An HTML element to target the host bindings at if different from the
2540
2710
  * host that the template is being attached to.
2541
2711
  */
2542
- render(source, host, hostBindingTarget, context) {
2543
- const view = this.create(hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : host);
2544
- view.bind(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
2712
+ render(source, host, hostBindingTarget) {
2713
+ const view = this.create(hostBindingTarget);
2714
+ view.bind(source);
2545
2715
  view.appendTo(host);
2546
2716
  return view;
2547
2717
  }
@@ -2581,12 +2751,12 @@ function html(strings, ...values) {
2581
2751
  let definition;
2582
2752
  html += currentString;
2583
2753
  if (isFunction(currentValue)) {
2584
- html += createAspectedHTML(bind(currentValue), currentString, add);
2754
+ html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2585
2755
  }
2586
2756
  else if (isString(currentValue)) {
2587
2757
  const match = lastAttributeNameRegex.exec(currentString);
2588
2758
  if (match !== null) {
2589
- const directive = bind(() => currentValue, oneTime);
2759
+ const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2590
2760
  Aspect.assign(directive, match[2]);
2591
2761
  html += directive.createHTML(add);
2592
2762
  }
@@ -2594,8 +2764,11 @@ function html(strings, ...values) {
2594
2764
  html += currentValue;
2595
2765
  }
2596
2766
  }
2767
+ else if (currentValue instanceof Binding) {
2768
+ html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2769
+ }
2597
2770
  else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2598
- html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
2771
+ html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2599
2772
  }
2600
2773
  else {
2601
2774
  if (definition.aspected) {
@@ -2608,26 +2781,6 @@ function html(strings, ...values) {
2608
2781
  }
2609
2782
  return new ViewTemplate(html + strings[strings.length - 1], factories);
2610
2783
  }
2611
- /**
2612
- * Transforms a template literal string into a ChildViewTemplate.
2613
- * @param strings - The string fragments that are interpolated with the values.
2614
- * @param values - The values that are interpolated with the string fragments.
2615
- * @remarks
2616
- * The html helper supports interpolation of strings, numbers, binding expressions,
2617
- * other template instances, and Directive instances.
2618
- * @public
2619
- */
2620
- const child = html;
2621
- /**
2622
- * Transforms a template literal string into an ItemViewTemplate.
2623
- * @param strings - The string fragments that are interpolated with the values.
2624
- * @param values - The values that are interpolated with the string fragments.
2625
- * @remarks
2626
- * The html helper supports interpolation of strings, numbers, binding expressions,
2627
- * other template instances, and Directive instances.
2628
- * @public
2629
- */
2630
- const item = html;
2631
2784
 
2632
2785
  /**
2633
2786
  * The runtime behavior for template references.
@@ -2635,20 +2788,12 @@ const item = html;
2635
2788
  */
2636
2789
  class RefDirective extends StatelessAttachedAttributeDirective {
2637
2790
  /**
2638
- * Bind this behavior to the source.
2639
- * @param source - The source to bind to.
2640
- * @param context - The execution context that the binding is operating within.
2641
- * @param targets - The targets that behaviors in a view can attach to.
2791
+ * Bind this behavior.
2792
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2642
2793
  */
2643
- bind(source, context, targets) {
2644
- source[this.options] = targets[this.nodeId];
2794
+ bind(controller) {
2795
+ controller.source[this.options] = controller.targets[this.nodeId];
2645
2796
  }
2646
- /**
2647
- * Unbinds this behavior from the source.
2648
- * @param source - The source to unbind from.
2649
- */
2650
- /* eslint-disable-next-line @typescript-eslint/no-empty-function */
2651
- unbind() { }
2652
2797
  }
2653
2798
  HTMLDirective.define(RefDirective);
2654
2799
  /**
@@ -2660,27 +2805,34 @@ const ref = (propertyName) => new RefDirective(propertyName);
2660
2805
 
2661
2806
  /**
2662
2807
  * A directive that enables basic conditional rendering in a template.
2663
- * @param binding - The condition to test for rendering.
2808
+ * @param condition - The condition to test for rendering.
2664
2809
  * @param templateOrTemplateBinding - The template or a binding that gets
2665
2810
  * the template to render when the condition is true.
2666
2811
  * @public
2667
2812
  */
2668
- function when(binding, templateOrTemplateBinding) {
2669
- const getTemplate = isFunction(templateOrTemplateBinding)
2813
+ function when(condition, templateOrTemplateBinding) {
2814
+ const dataBinding = isFunction(condition) ? condition : () => condition;
2815
+ const templateBinding = isFunction(templateOrTemplateBinding)
2670
2816
  ? templateOrTemplateBinding
2671
2817
  : () => templateOrTemplateBinding;
2672
- return (source, context) => binding(source, context) ? getTemplate(source, context) : null;
2818
+ return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
2673
2819
  }
2674
2820
 
2675
2821
  const defaultRepeatOptions = Object.freeze({
2676
2822
  positioning: false,
2677
2823
  recycle: true,
2678
2824
  });
2679
- function bindWithoutPositioning(view, items, index, context) {
2680
- view.bind(items[index], context);
2825
+ function bindWithoutPositioning(view, items, index, controller) {
2826
+ view.context.parent = controller.source;
2827
+ view.context.parentContext = controller.context;
2828
+ view.bind(items[index]);
2681
2829
  }
2682
- function bindWithPositioning(view, items, index, context) {
2683
- view.bind(items[index], context.createItemContext(index, items.length));
2830
+ function bindWithPositioning(view, items, index, controller) {
2831
+ view.context.parent = controller.source;
2832
+ view.context.parentContext = controller.context;
2833
+ view.context.length = items.length;
2834
+ view.context.index = index;
2835
+ view.bind(items[index]);
2684
2836
  }
2685
2837
  /**
2686
2838
  * A behavior that renders a template for each item in an array.
@@ -2690,57 +2842,45 @@ class RepeatBehavior {
2690
2842
  /**
2691
2843
  * Creates an instance of RepeatBehavior.
2692
2844
  * @param location - The location in the DOM to render the repeat.
2693
- * @param itemsBinding - The array to render.
2845
+ * @param dataBinding - The array to render.
2694
2846
  * @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
2695
2847
  * @param templateBinding - The template to render for each item.
2696
2848
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2697
2849
  * @param options - Options used to turn on special repeat features.
2698
2850
  */
2699
- constructor(location, itemsBinding, isItemsBindingVolatile, templateBinding, isTemplateBindingVolatile, options) {
2700
- this.location = location;
2701
- this.itemsBinding = itemsBinding;
2702
- this.templateBinding = templateBinding;
2703
- this.options = options;
2704
- this.source = null;
2851
+ constructor(directive) {
2852
+ this.directive = directive;
2705
2853
  this.views = [];
2706
2854
  this.items = null;
2707
2855
  this.itemsObserver = null;
2708
- this.context = void 0;
2709
- this.childContext = void 0;
2710
2856
  this.bindView = bindWithoutPositioning;
2711
- this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
2712
- this.templateBindingObserver = Observable.binding(templateBinding, this, isTemplateBindingVolatile);
2713
- if (options.positioning) {
2857
+ this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2858
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
2859
+ if (directive.options.positioning) {
2714
2860
  this.bindView = bindWithPositioning;
2715
2861
  }
2716
2862
  }
2717
2863
  /**
2718
- * Bind this behavior to the source.
2719
- * @param source - The source to bind to.
2720
- * @param context - The execution context that the binding is operating within.
2864
+ * Bind this behavior.
2865
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2721
2866
  */
2722
- bind(source, context) {
2723
- this.source = source;
2724
- this.context = context;
2725
- this.childContext = context.createChildContext(source);
2726
- this.items = this.itemsBindingObserver.observe(source, this.context);
2727
- this.template = this.templateBindingObserver.observe(source, this.context);
2867
+ bind(controller) {
2868
+ this.location = controller.targets[this.directive.nodeId];
2869
+ this.controller = controller;
2870
+ this.items = this.itemsBindingObserver.bind(controller);
2871
+ this.template = this.templateBindingObserver.bind(controller);
2728
2872
  this.observeItems(true);
2729
2873
  this.refreshAllViews();
2874
+ controller.onUnbind(this);
2730
2875
  }
2731
2876
  /**
2732
- * Unbinds this behavior from the source.
2733
- * @param source - The source to unbind from.
2877
+ * Unbinds this behavior.
2734
2878
  */
2735
2879
  unbind() {
2736
- this.source = null;
2737
- this.items = null;
2738
2880
  if (this.itemsObserver !== null) {
2739
2881
  this.itemsObserver.unsubscribe(this);
2740
2882
  }
2741
2883
  this.unbindAllViews();
2742
- this.itemsBindingObserver.dispose();
2743
- this.templateBindingObserver.dispose();
2744
2884
  }
2745
2885
  /**
2746
2886
  * Handles changes in the array, its items, and the repeat template.
@@ -2748,15 +2888,18 @@ class RepeatBehavior {
2748
2888
  * @param args - The details about what was changed.
2749
2889
  */
2750
2890
  handleChange(source, args) {
2751
- if (source === this.itemsBinding) {
2752
- this.items = this.itemsBindingObserver.observe(this.source, this.context);
2891
+ if (args === this.itemsBindingObserver) {
2892
+ this.items = this.itemsBindingObserver.bind(this.controller);
2753
2893
  this.observeItems();
2754
2894
  this.refreshAllViews();
2755
2895
  }
2756
- else if (source === this.templateBinding) {
2757
- this.template = this.templateBindingObserver.observe(this.source, this.context);
2896
+ else if (args === this.templateBindingObserver) {
2897
+ this.template = this.templateBindingObserver.bind(this.controller);
2758
2898
  this.refreshAllViews(true);
2759
2899
  }
2900
+ else if (!args[0]) {
2901
+ return;
2902
+ }
2760
2903
  else if (args[0].reset) {
2761
2904
  this.refreshAllViews();
2762
2905
  }
@@ -2781,39 +2924,57 @@ class RepeatBehavior {
2781
2924
  }
2782
2925
  updateViews(splices) {
2783
2926
  const views = this.views;
2784
- const childContext = this.childContext;
2785
- const totalRemoved = [];
2786
2927
  const bindView = this.bindView;
2787
- let removeDelta = 0;
2788
- for (let i = 0, ii = splices.length; i < ii; ++i) {
2789
- const splice = splices[i];
2790
- const removed = splice.removed;
2791
- totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
2792
- removeDelta -= splice.addedCount;
2793
- }
2794
2928
  const items = this.items;
2795
2929
  const template = this.template;
2930
+ const controller = this.controller;
2931
+ const recycle = this.directive.options.recycle;
2932
+ const leftoverViews = [];
2933
+ let leftoverIndex = 0;
2934
+ let availableViews = 0;
2796
2935
  for (let i = 0, ii = splices.length; i < ii; ++i) {
2797
2936
  const splice = splices[i];
2937
+ const removed = splice.removed;
2938
+ let removeIndex = 0;
2798
2939
  let addIndex = splice.index;
2799
2940
  const end = addIndex + splice.addedCount;
2941
+ const removedViews = views.splice(splice.index, removed.length);
2942
+ const totalAvailableViews = (availableViews =
2943
+ leftoverViews.length + removedViews.length);
2800
2944
  for (; addIndex < end; ++addIndex) {
2801
2945
  const neighbor = views[addIndex];
2802
2946
  const location = neighbor ? neighbor.firstChild : this.location;
2803
- const view = this.options.recycle && totalRemoved.length > 0
2804
- ? totalRemoved.shift()
2805
- : template.create();
2947
+ let view;
2948
+ if (recycle && availableViews > 0) {
2949
+ if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
2950
+ view = removedViews[removeIndex];
2951
+ removeIndex++;
2952
+ }
2953
+ else {
2954
+ view = leftoverViews[leftoverIndex];
2955
+ leftoverIndex++;
2956
+ }
2957
+ availableViews--;
2958
+ }
2959
+ else {
2960
+ view = template.create();
2961
+ }
2806
2962
  views.splice(addIndex, 0, view);
2807
- bindView(view, items, addIndex, childContext);
2963
+ bindView(view, items, addIndex, controller);
2808
2964
  view.insertBefore(location);
2809
2965
  }
2966
+ if (removedViews[removeIndex]) {
2967
+ leftoverViews.push(...removedViews.slice(removeIndex));
2968
+ }
2810
2969
  }
2811
- for (let i = 0, ii = totalRemoved.length; i < ii; ++i) {
2812
- totalRemoved[i].dispose();
2970
+ for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
2971
+ leftoverViews[i].dispose();
2813
2972
  }
2814
- if (this.options.positioning) {
2973
+ if (this.directive.options.positioning) {
2815
2974
  for (let i = 0, ii = views.length; i < ii; ++i) {
2816
- views[i].context.updatePosition(i, ii);
2975
+ const context = views[i].context;
2976
+ context.length = i;
2977
+ context.index = ii;
2817
2978
  }
2818
2979
  }
2819
2980
  }
@@ -2822,11 +2983,11 @@ class RepeatBehavior {
2822
2983
  const template = this.template;
2823
2984
  const location = this.location;
2824
2985
  const bindView = this.bindView;
2825
- const childContext = this.childContext;
2986
+ const controller = this.controller;
2826
2987
  let itemsLength = items.length;
2827
2988
  let views = this.views;
2828
2989
  let viewsLength = views.length;
2829
- if (itemsLength === 0 || templateChanged) {
2990
+ if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
2830
2991
  // all views need to be removed
2831
2992
  HTMLView.disposeContiguousBatch(views);
2832
2993
  viewsLength = 0;
@@ -2836,7 +2997,7 @@ class RepeatBehavior {
2836
2997
  this.views = views = new Array(itemsLength);
2837
2998
  for (let i = 0; i < itemsLength; ++i) {
2838
2999
  const view = template.create();
2839
- bindView(view, items, i, childContext);
3000
+ bindView(view, items, i, controller);
2840
3001
  views[i] = view;
2841
3002
  view.insertBefore(location);
2842
3003
  }
@@ -2847,11 +3008,11 @@ class RepeatBehavior {
2847
3008
  for (; i < itemsLength; ++i) {
2848
3009
  if (i < viewsLength) {
2849
3010
  const view = views[i];
2850
- bindView(view, items, i, childContext);
3011
+ bindView(view, items, i, controller);
2851
3012
  }
2852
3013
  else {
2853
3014
  const view = template.create();
2854
- bindView(view, items, i, childContext);
3015
+ bindView(view, items, i, controller);
2855
3016
  views.push(view);
2856
3017
  view.insertBefore(location);
2857
3018
  }
@@ -2876,17 +3037,19 @@ class RepeatBehavior {
2876
3037
  class RepeatDirective {
2877
3038
  /**
2878
3039
  * Creates an instance of RepeatDirective.
2879
- * @param itemsBinding - The binding that provides the array to render.
3040
+ * @param dataBinding - The binding that provides the array to render.
2880
3041
  * @param templateBinding - The template binding used to obtain a template to render for each item in the array.
2881
3042
  * @param options - Options used to turn on special repeat features.
2882
3043
  */
2883
- constructor(itemsBinding, templateBinding, options) {
2884
- this.itemsBinding = itemsBinding;
3044
+ constructor(dataBinding, templateBinding, options) {
3045
+ this.dataBinding = dataBinding;
2885
3046
  this.templateBinding = templateBinding;
2886
3047
  this.options = options;
3048
+ /**
3049
+ * The unique id of the factory.
3050
+ */
3051
+ this.id = nextId();
2887
3052
  ArrayObserver.enable();
2888
- this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
2889
- this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
2890
3053
  }
2891
3054
  /**
2892
3055
  * Creates a placeholder string based on the directive's index within the template.
@@ -2899,16 +3062,23 @@ class RepeatDirective {
2899
3062
  * Creates a behavior for the provided target node.
2900
3063
  * @param target - The node instance to create the behavior for.
2901
3064
  */
2902
- createBehavior(targets) {
2903
- return new RepeatBehavior(targets[this.nodeId], this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
3065
+ createBehavior() {
3066
+ return new RepeatBehavior(this);
2904
3067
  }
2905
3068
  }
2906
3069
  HTMLDirective.define(RepeatDirective);
2907
- function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
2908
- const templateBinding = isFunction(templateOrTemplateBinding)
2909
- ? templateOrTemplateBinding
2910
- : () => templateOrTemplateBinding;
2911
- return new RepeatDirective(itemsBinding, templateBinding, options);
3070
+ /**
3071
+ * A directive that enables list rendering.
3072
+ * @param items - The array to render.
3073
+ * @param template - The template or a template binding used obtain a template
3074
+ * to render for each item in the array.
3075
+ * @param options - Options used to turn on special repeat features.
3076
+ * @public
3077
+ */
3078
+ function repeat(items, template, options = defaultRepeatOptions) {
3079
+ const dataBinding = normalizeBinding(items);
3080
+ const templateBinding = normalizeBinding(template);
3081
+ return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
2912
3082
  }
2913
3083
 
2914
3084
  const selectElements = (value) => value.nodeType === 1;
@@ -2937,11 +3107,12 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
2937
3107
  * @param context - The execution context that the binding is operating within.
2938
3108
  * @param targets - The targets that behaviors in a view can attach to.
2939
3109
  */
2940
- bind(source, context, targets) {
2941
- const target = targets[this.nodeId];
2942
- target[this.sourceProperty] = source;
2943
- this.updateTarget(source, this.computeNodes(target));
3110
+ bind(controller) {
3111
+ const target = controller.targets[this.nodeId];
3112
+ target[this.sourceProperty] = controller.source;
3113
+ this.updateTarget(controller.source, this.computeNodes(target));
2944
3114
  this.observe(target);
3115
+ controller.onUnbind(this);
2945
3116
  }
2946
3117
  /**
2947
3118
  * Unbinds this behavior from the source.
@@ -2949,9 +3120,9 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
2949
3120
  * @param context - The execution context that the binding is operating within.
2950
3121
  * @param targets - The targets that behaviors in a view can attach to.
2951
3122
  */
2952
- unbind(source, context, targets) {
2953
- const target = targets[this.nodeId];
2954
- this.updateTarget(source, emptyArray);
3123
+ unbind(controller) {
3124
+ const target = controller.targets[this.nodeId];
3125
+ this.updateTarget(controller.source, emptyArray);
2955
3126
  this.disconnect(target);
2956
3127
  target[this.sourceProperty] = null;
2957
3128
  }
@@ -3100,6 +3271,16 @@ function children(propertyOrOptions) {
3100
3271
 
3101
3272
  const booleanMode = "boolean";
3102
3273
  const reflectMode = "reflect";
3274
+ /**
3275
+ * Metadata used to configure a custom attribute's behavior.
3276
+ * @public
3277
+ */
3278
+ const AttributeConfiguration = Object.freeze({
3279
+ /**
3280
+ * Locates all attribute configurations associated with a type.
3281
+ */
3282
+ locate: createMetadataLocator(),
3283
+ });
3103
3284
  /**
3104
3285
  * A {@link ValueConverter} that converts to and from `boolean` values.
3105
3286
  * @remarks
@@ -3237,7 +3418,7 @@ class AttributeDefinition {
3237
3418
  */
3238
3419
  static collect(Owner, ...attributeLists) {
3239
3420
  const attributes = [];
3240
- attributeLists.push(Owner.attributes);
3421
+ attributeLists.push(AttributeConfiguration.locate(Owner));
3241
3422
  for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
3242
3423
  const list = attributeLists[i];
3243
3424
  if (list === void 0) {
@@ -3267,9 +3448,7 @@ function attr(configOrTarget, prop) {
3267
3448
  // - @attr({...opts})
3268
3449
  config.property = $prop;
3269
3450
  }
3270
- const attributes = $target.constructor.attributes ||
3271
- ($target.constructor.attributes = []);
3272
- attributes.push(config);
3451
+ AttributeConfiguration.locate($target.constructor).push(config);
3273
3452
  }
3274
3453
  if (arguments.length > 1) {
3275
3454
  // Non invocation:
@@ -3287,25 +3466,24 @@ function attr(configOrTarget, prop) {
3287
3466
 
3288
3467
  const defaultShadowOptions = { mode: "open" };
3289
3468
  const defaultElementOptions = {};
3469
+ const fastElementBaseTypes = new Set();
3290
3470
  const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */, () => createTypeRegistry());
3291
3471
  /**
3292
3472
  * Defines metadata for a FASTElement.
3293
3473
  * @public
3294
3474
  */
3295
3475
  class FASTElementDefinition {
3296
- /**
3297
- * Creates an instance of FASTElementDefinition.
3298
- * @param type - The type this definition is being created for.
3299
- * @param nameOrConfig - The name of the element to define or a config object
3300
- * that describes the element to define.
3301
- */
3302
3476
  constructor(type, nameOrConfig = type.definition) {
3477
+ var _a;
3478
+ this.platformDefined = false;
3303
3479
  if (isString(nameOrConfig)) {
3304
3480
  nameOrConfig = { name: nameOrConfig };
3305
3481
  }
3306
3482
  this.type = type;
3307
3483
  this.name = nameOrConfig.name;
3308
3484
  this.template = nameOrConfig.template;
3485
+ this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
3486
+ const proto = type.prototype;
3309
3487
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3310
3488
  const observedAttributes = new Array(attributes.length);
3311
3489
  const propertyLookup = {};
@@ -3315,9 +3493,13 @@ class FASTElementDefinition {
3315
3493
  observedAttributes[i] = current.attribute;
3316
3494
  propertyLookup[current.name] = current;
3317
3495
  attributeLookup[current.attribute] = current;
3496
+ Observable.defineProperty(proto, current);
3318
3497
  }
3498
+ Reflect.defineProperty(type, "observedAttributes", {
3499
+ value: observedAttributes,
3500
+ enumerable: true,
3501
+ });
3319
3502
  this.attributes = attributes;
3320
- this.observedAttributes = observedAttributes;
3321
3503
  this.propertyLookup = propertyLookup;
3322
3504
  this.attributeLookup = attributeLookup;
3323
3505
  this.shadowOptions =
@@ -3330,43 +3512,50 @@ class FASTElementDefinition {
3330
3512
  nameOrConfig.elementOptions === void 0
3331
3513
  ? defaultElementOptions
3332
3514
  : Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
3333
- this.styles =
3334
- nameOrConfig.styles === void 0
3335
- ? void 0
3336
- : Array.isArray(nameOrConfig.styles)
3337
- ? new ElementStyles(nameOrConfig.styles)
3338
- : nameOrConfig.styles instanceof ElementStyles
3339
- ? nameOrConfig.styles
3340
- : new ElementStyles([nameOrConfig.styles]);
3515
+ this.styles = ElementStyles.normalize(nameOrConfig.styles);
3516
+ fastElementRegistry.register(this);
3341
3517
  }
3342
3518
  /**
3343
3519
  * Indicates if this element has been defined in at least one registry.
3344
3520
  */
3345
3521
  get isDefined() {
3346
- return !!fastElementRegistry.getByType(this.type);
3522
+ return this.platformDefined;
3347
3523
  }
3348
3524
  /**
3349
3525
  * Defines a custom element based on this definition.
3350
3526
  * @param registry - The element registry to define the element in.
3527
+ * @remarks
3528
+ * This operation is idempotent per registry.
3351
3529
  */
3352
- define(registry = customElements) {
3530
+ define(registry = this.registry) {
3353
3531
  const type = this.type;
3354
- if (fastElementRegistry.register(this)) {
3355
- const attributes = this.attributes;
3356
- const proto = type.prototype;
3357
- for (let i = 0, ii = attributes.length; i < ii; ++i) {
3358
- Observable.defineProperty(proto, attributes[i]);
3359
- }
3360
- Reflect.defineProperty(type, "observedAttributes", {
3361
- value: this.observedAttributes,
3362
- enumerable: true,
3363
- });
3364
- }
3365
3532
  if (!registry.get(this.name)) {
3533
+ this.platformDefined = true;
3366
3534
  registry.define(this.name, type, this.elementOptions);
3367
3535
  }
3368
3536
  return this;
3369
3537
  }
3538
+ /**
3539
+ * Creates an instance of FASTElementDefinition.
3540
+ * @param type - The type this definition is being created for.
3541
+ * @param nameOrDef - The name of the element to define or a config object
3542
+ * that describes the element to define.
3543
+ */
3544
+ static compose(type, nameOrDef) {
3545
+ if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
3546
+ return new FASTElementDefinition(class extends type {
3547
+ }, nameOrDef);
3548
+ }
3549
+ return new FASTElementDefinition(type, nameOrDef);
3550
+ }
3551
+ /**
3552
+ * Registers a FASTElement base type.
3553
+ * @param type - The type to register as a base type.
3554
+ * @internal
3555
+ */
3556
+ static registerBaseType(type) {
3557
+ fastElementBaseTypes.add(type);
3558
+ }
3370
3559
  }
3371
3560
  /**
3372
3561
  * Gets the element definition associated with the specified type.
@@ -3379,22 +3568,22 @@ FASTElementDefinition.getByType = fastElementRegistry.getByType;
3379
3568
  */
3380
3569
  FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
3381
3570
 
3382
- const shadowRoots = new WeakMap();
3383
3571
  const defaultEventOptions = {
3384
3572
  bubbles: true,
3385
3573
  composed: true,
3386
3574
  cancelable: true,
3387
3575
  };
3576
+ const isConnectedPropertyName = "isConnected";
3577
+ const shadowRoots = new WeakMap();
3388
3578
  function getShadowRoot(element) {
3389
3579
  var _a, _b;
3390
3580
  return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
3391
3581
  }
3392
- const isConnectedPropertyName = "isConnected";
3393
3582
  /**
3394
3583
  * Controls the lifecycle and rendering of a `FASTElement`.
3395
3584
  * @public
3396
3585
  */
3397
- class Controller extends PropertyChangeNotifier {
3586
+ class ElementController extends PropertyChangeNotifier {
3398
3587
  /**
3399
3588
  * Creates a Controller to control the specified element.
3400
3589
  * @param element - The element to be controlled by this controller.
@@ -3405,12 +3594,12 @@ class Controller extends PropertyChangeNotifier {
3405
3594
  constructor(element, definition) {
3406
3595
  super(element);
3407
3596
  this.boundObservables = null;
3408
- this.behaviors = null;
3409
3597
  this.needsInitialization = true;
3410
3598
  this.hasExistingShadowRoot = false;
3411
3599
  this._template = null;
3412
- this._styles = null;
3413
3600
  this._isConnected = false;
3601
+ this.behaviors = null;
3602
+ this._mainStyles = null;
3414
3603
  /**
3415
3604
  * This allows Observable.getNotifier(...) to return the Controller
3416
3605
  * when the notifier for the Controller itself is being requested. The
@@ -3426,7 +3615,7 @@ class Controller extends PropertyChangeNotifier {
3426
3615
  * If `null` then the element is managing its own rendering.
3427
3616
  */
3428
3617
  this.view = null;
3429
- this.element = element;
3618
+ this.source = element;
3430
3619
  this.definition = definition;
3431
3620
  const shadowOptions = definition.shadowOptions;
3432
3621
  if (shadowOptions !== void 0) {
@@ -3480,9 +3669,9 @@ class Controller extends PropertyChangeNotifier {
3480
3669
  // 1. Template overrides take top precedence.
3481
3670
  if (this._template === null) {
3482
3671
  const definition = this.definition;
3483
- if (this.element.resolveTemplate) {
3672
+ if (this.source.resolveTemplate) {
3484
3673
  // 2. Allow for element instance overrides next.
3485
- this._template = this.element.resolveTemplate();
3674
+ this._template = this.source.resolveTemplate();
3486
3675
  }
3487
3676
  else if (definition.template) {
3488
3677
  // 3. Default to the static definition.
@@ -3501,56 +3690,102 @@ class Controller extends PropertyChangeNotifier {
3501
3690
  }
3502
3691
  }
3503
3692
  /**
3504
- * Gets/sets the primary styles used for the component.
3505
- * @remarks
3506
- * This value can only be accurately read after connect but can be set at any time.
3693
+ * The main set of styles used for the component, independent
3694
+ * of any dynamically added styles.
3507
3695
  */
3508
- get styles() {
3696
+ get mainStyles() {
3509
3697
  var _a;
3510
3698
  // 1. Styles overrides take top precedence.
3511
- if (this._styles === null) {
3699
+ if (this._mainStyles === null) {
3512
3700
  const definition = this.definition;
3513
- if (this.element.resolveStyles) {
3701
+ if (this.source.resolveStyles) {
3514
3702
  // 2. Allow for element instance overrides next.
3515
- this._styles = this.element.resolveStyles();
3703
+ this._mainStyles = this.source.resolveStyles();
3516
3704
  }
3517
3705
  else if (definition.styles) {
3518
3706
  // 3. Default to the static definition.
3519
- this._styles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3707
+ this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3520
3708
  }
3521
3709
  }
3522
- return this._styles;
3710
+ return this._mainStyles;
3523
3711
  }
3524
- set styles(value) {
3525
- if (this._styles === value) {
3712
+ set mainStyles(value) {
3713
+ if (this._mainStyles === value) {
3526
3714
  return;
3527
3715
  }
3528
- if (this._styles !== null) {
3529
- this.removeStyles(this._styles);
3716
+ if (this._mainStyles !== null) {
3717
+ this.removeStyles(this._mainStyles);
3530
3718
  }
3531
- this._styles = value;
3719
+ this._mainStyles = value;
3532
3720
  if (!this.needsInitialization) {
3533
3721
  this.addStyles(value);
3534
3722
  }
3535
3723
  }
3724
+ /**
3725
+ * Adds the behavior to the component.
3726
+ * @param behavior - The behavior to add.
3727
+ */
3728
+ addBehavior(behavior) {
3729
+ var _a, _b;
3730
+ const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3731
+ const count = (_b = targetBehaviors.get(behavior)) !== null && _b !== void 0 ? _b : 0;
3732
+ if (count === 0) {
3733
+ targetBehaviors.set(behavior, 1);
3734
+ behavior.addedCallback && behavior.addedCallback(this);
3735
+ if (behavior.connectedCallback && this.isConnected) {
3736
+ behavior.connectedCallback(this);
3737
+ }
3738
+ }
3739
+ else {
3740
+ targetBehaviors.set(behavior, count + 1);
3741
+ }
3742
+ }
3743
+ /**
3744
+ * Removes the behavior from the component.
3745
+ * @param behavior - The behavior to remove.
3746
+ * @param force - Forces removal even if this behavior was added more than once.
3747
+ */
3748
+ removeBehavior(behavior, force = false) {
3749
+ const targetBehaviors = this.behaviors;
3750
+ if (targetBehaviors === null) {
3751
+ return;
3752
+ }
3753
+ const count = targetBehaviors.get(behavior);
3754
+ if (count === void 0) {
3755
+ return;
3756
+ }
3757
+ if (count === 1 || force) {
3758
+ targetBehaviors.delete(behavior);
3759
+ if (behavior.disconnectedCallback && this.isConnected) {
3760
+ behavior.disconnectedCallback(this);
3761
+ }
3762
+ behavior.removedCallback && behavior.removedCallback(this);
3763
+ }
3764
+ else {
3765
+ targetBehaviors.set(behavior, count - 1);
3766
+ }
3767
+ }
3536
3768
  /**
3537
3769
  * Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot.
3538
3770
  * @param styles - The styles to add.
3539
3771
  */
3540
3772
  addStyles(styles) {
3773
+ var _a;
3541
3774
  if (!styles) {
3542
3775
  return;
3543
3776
  }
3544
- const target = getShadowRoot(this.element) ||
3545
- this.element.getRootNode();
3777
+ const source = this.source;
3546
3778
  if (styles instanceof HTMLElement) {
3779
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : this.source;
3547
3780
  target.append(styles);
3548
3781
  }
3549
- else if (!styles.isAttachedTo(target)) {
3782
+ else if (!styles.isAttachedTo(source)) {
3550
3783
  const sourceBehaviors = styles.behaviors;
3551
- styles.addStylesTo(target);
3784
+ styles.addStylesTo(source);
3552
3785
  if (sourceBehaviors !== null) {
3553
- this.addBehaviors(sourceBehaviors);
3786
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3787
+ this.addBehavior(sourceBehaviors[i]);
3788
+ }
3554
3789
  }
3555
3790
  }
3556
3791
  }
@@ -3559,97 +3794,42 @@ class Controller extends PropertyChangeNotifier {
3559
3794
  * @param styles - the styles to remove.
3560
3795
  */
3561
3796
  removeStyles(styles) {
3797
+ var _a;
3562
3798
  if (!styles) {
3563
3799
  return;
3564
3800
  }
3565
- const target = getShadowRoot(this.element) ||
3566
- this.element.getRootNode();
3801
+ const source = this.source;
3567
3802
  if (styles instanceof HTMLElement) {
3803
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source;
3568
3804
  target.removeChild(styles);
3569
3805
  }
3570
- else if (styles.isAttachedTo(target)) {
3806
+ else if (styles.isAttachedTo(source)) {
3571
3807
  const sourceBehaviors = styles.behaviors;
3572
- styles.removeStylesFrom(target);
3808
+ styles.removeStylesFrom(source);
3573
3809
  if (sourceBehaviors !== null) {
3574
- this.removeBehaviors(sourceBehaviors);
3575
- }
3576
- }
3577
- }
3578
- /**
3579
- * Adds behaviors to this element.
3580
- * @param behaviors - The behaviors to add.
3581
- */
3582
- addBehaviors(behaviors) {
3583
- var _a;
3584
- const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
3585
- const length = behaviors.length;
3586
- const behaviorsToBind = [];
3587
- for (let i = 0; i < length; ++i) {
3588
- const behavior = behaviors[i];
3589
- if (targetBehaviors.has(behavior)) {
3590
- targetBehaviors.set(behavior, targetBehaviors.get(behavior) + 1);
3591
- }
3592
- else {
3593
- targetBehaviors.set(behavior, 1);
3594
- behaviorsToBind.push(behavior);
3595
- }
3596
- }
3597
- if (this._isConnected) {
3598
- const element = this.element;
3599
- const context = ExecutionContext.default;
3600
- for (let i = 0; i < behaviorsToBind.length; ++i) {
3601
- behaviorsToBind[i].bind(element, context);
3602
- }
3603
- }
3604
- }
3605
- /**
3606
- * Removes behaviors from this element.
3607
- * @param behaviors - The behaviors to remove.
3608
- * @param force - Forces unbinding of behaviors.
3609
- */
3610
- removeBehaviors(behaviors, force = false) {
3611
- const targetBehaviors = this.behaviors;
3612
- if (targetBehaviors === null) {
3613
- return;
3614
- }
3615
- const length = behaviors.length;
3616
- const behaviorsToUnbind = [];
3617
- for (let i = 0; i < length; ++i) {
3618
- const behavior = behaviors[i];
3619
- if (targetBehaviors.has(behavior)) {
3620
- const count = targetBehaviors.get(behavior) - 1;
3621
- count === 0 || force
3622
- ? targetBehaviors.delete(behavior) && behaviorsToUnbind.push(behavior)
3623
- : targetBehaviors.set(behavior, count);
3624
- }
3625
- }
3626
- if (this._isConnected) {
3627
- const element = this.element;
3628
- const context = ExecutionContext.default;
3629
- for (let i = 0; i < behaviorsToUnbind.length; ++i) {
3630
- behaviorsToUnbind[i].unbind(element, context);
3810
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
3811
+ this.addBehavior(sourceBehaviors[i]);
3812
+ }
3631
3813
  }
3632
3814
  }
3633
3815
  }
3634
3816
  /**
3635
3817
  * Runs connected lifecycle behavior on the associated element.
3636
3818
  */
3637
- onConnectedCallback() {
3819
+ connect() {
3638
3820
  if (this._isConnected) {
3639
3821
  return;
3640
3822
  }
3641
- const element = this.element;
3642
- const context = ExecutionContext.default;
3643
3823
  if (this.needsInitialization) {
3644
3824
  this.finishInitialization();
3645
3825
  }
3646
3826
  else if (this.view !== null) {
3647
- this.view.bind(element, context);
3827
+ this.view.bind(this.source);
3648
3828
  }
3649
3829
  const behaviors = this.behaviors;
3650
3830
  if (behaviors !== null) {
3651
- for (const behavior of behaviors.keys()) {
3652
- behavior.bind(element, context);
3831
+ for (const key of behaviors.keys()) {
3832
+ key.connectedCallback && key.connectedCallback(this);
3653
3833
  }
3654
3834
  }
3655
3835
  this.setIsConnected(true);
@@ -3657,21 +3837,18 @@ class Controller extends PropertyChangeNotifier {
3657
3837
  /**
3658
3838
  * Runs disconnected lifecycle behavior on the associated element.
3659
3839
  */
3660
- onDisconnectedCallback() {
3840
+ disconnect() {
3661
3841
  if (!this._isConnected) {
3662
3842
  return;
3663
3843
  }
3664
3844
  this.setIsConnected(false);
3665
- const view = this.view;
3666
- if (view !== null) {
3667
- view.unbind();
3845
+ if (this.view !== null) {
3846
+ this.view.unbind();
3668
3847
  }
3669
3848
  const behaviors = this.behaviors;
3670
3849
  if (behaviors !== null) {
3671
- const element = this.element;
3672
- const context = ExecutionContext.default;
3673
- for (const behavior of behaviors.keys()) {
3674
- behavior.unbind(element, context);
3850
+ for (const key of behaviors.keys()) {
3851
+ key.disconnectedCallback && key.disconnectedCallback(this);
3675
3852
  }
3676
3853
  }
3677
3854
  }
@@ -3684,7 +3861,7 @@ class Controller extends PropertyChangeNotifier {
3684
3861
  onAttributeChangedCallback(name, oldValue, newValue) {
3685
3862
  const attrDef = this.definition.attributeLookup[name];
3686
3863
  if (attrDef !== void 0) {
3687
- attrDef.onAttributeChangedCallback(this.element, newValue);
3864
+ attrDef.onAttributeChangedCallback(this.source, newValue);
3688
3865
  }
3689
3866
  }
3690
3867
  /**
@@ -3697,12 +3874,12 @@ class Controller extends PropertyChangeNotifier {
3697
3874
  */
3698
3875
  emit(type, detail, options) {
3699
3876
  if (this._isConnected) {
3700
- return this.element.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3877
+ return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3701
3878
  }
3702
3879
  return false;
3703
3880
  }
3704
3881
  finishInitialization() {
3705
- const element = this.element;
3882
+ const element = this.source;
3706
3883
  const boundObservables = this.boundObservables;
3707
3884
  // If we have any observables that were bound, re-apply their values.
3708
3885
  if (boundObservables !== null) {
@@ -3714,15 +3891,15 @@ class Controller extends PropertyChangeNotifier {
3714
3891
  this.boundObservables = null;
3715
3892
  }
3716
3893
  this.renderTemplate(this.template);
3717
- this.addStyles(this.styles);
3894
+ this.addStyles(this.mainStyles);
3718
3895
  this.needsInitialization = false;
3719
3896
  }
3720
3897
  renderTemplate(template) {
3721
3898
  var _a;
3722
- const element = this.element;
3723
3899
  // When getting the host to render to, we start by looking
3724
3900
  // up the shadow root. If there isn't one, then that means
3725
3901
  // we're doing a Light DOM render to the element's direct children.
3902
+ const element = this.source;
3726
3903
  const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
3727
3904
  if (this.view !== null) {
3728
3905
  // If there's already a view, we need to unbind and remove through dispose.
@@ -3739,6 +3916,8 @@ class Controller extends PropertyChangeNotifier {
3739
3916
  if (template) {
3740
3917
  // If a new template was provided, render it.
3741
3918
  this.view = template.render(element, host, element);
3919
+ this.view.sourceLifetime =
3920
+ SourceLifetime.coupled;
3742
3921
  }
3743
3922
  }
3744
3923
  /**
@@ -3758,31 +3937,136 @@ class Controller extends PropertyChangeNotifier {
3758
3937
  if (definition === void 0) {
3759
3938
  throw FAST.error(1401 /* Message.missingElementDefinition */);
3760
3939
  }
3761
- return (element.$fastController = new Controller(element, definition));
3940
+ return (element.$fastController = new ElementController(element, definition));
3941
+ }
3942
+ }
3943
+ /**
3944
+ * Converts a styleTarget into the operative target. When the provided target is an Element
3945
+ * that is a FASTElement, the function will return the ShadowRoot for that element. Otherwise,
3946
+ * it will return the root node for the element.
3947
+ * @param target
3948
+ * @returns
3949
+ */
3950
+ function normalizeStyleTarget(target) {
3951
+ var _a;
3952
+ if ("adoptedStyleSheets" in target) {
3953
+ return target;
3954
+ }
3955
+ else {
3956
+ return ((_a = getShadowRoot(target)) !== null && _a !== void 0 ? _a : target.getRootNode());
3957
+ }
3958
+ }
3959
+ // Default StyleStrategy implementations are defined in this module because they
3960
+ // require access to element shadowRoots, and we don't want to leak shadowRoot
3961
+ // objects out of this module.
3962
+ /**
3963
+ * https://wicg.github.io/construct-stylesheets/
3964
+ * https://developers.google.com/web/updates/2019/02/constructable-stylesheets
3965
+ *
3966
+ * @internal
3967
+ */
3968
+ class AdoptedStyleSheetsStrategy {
3969
+ constructor(styles) {
3970
+ const styleSheetCache = AdoptedStyleSheetsStrategy.styleSheetCache;
3971
+ this.sheets = styles.map((x) => {
3972
+ if (x instanceof CSSStyleSheet) {
3973
+ return x;
3974
+ }
3975
+ let sheet = styleSheetCache.get(x);
3976
+ if (sheet === void 0) {
3977
+ sheet = new CSSStyleSheet();
3978
+ sheet.replaceSync(x);
3979
+ styleSheetCache.set(x, sheet);
3980
+ }
3981
+ return sheet;
3982
+ });
3983
+ }
3984
+ addStylesTo(target) {
3985
+ const t = normalizeStyleTarget(target);
3986
+ t.adoptedStyleSheets = [...t.adoptedStyleSheets, ...this.sheets];
3987
+ }
3988
+ removeStylesFrom(target) {
3989
+ const t = normalizeStyleTarget(target);
3990
+ const sheets = this.sheets;
3991
+ t.adoptedStyleSheets = t.adoptedStyleSheets.filter((x) => sheets.indexOf(x) === -1);
3992
+ }
3993
+ }
3994
+ AdoptedStyleSheetsStrategy.styleSheetCache = new Map();
3995
+ let id = 0;
3996
+ const nextStyleId = () => `fast-${++id}`;
3997
+ function usableStyleTarget(target) {
3998
+ return target === document ? document.body : target;
3999
+ }
4000
+ /**
4001
+ * @internal
4002
+ */
4003
+ class StyleElementStrategy {
4004
+ constructor(styles) {
4005
+ this.styles = styles;
4006
+ this.styleClass = nextStyleId();
4007
+ }
4008
+ addStylesTo(target) {
4009
+ target = usableStyleTarget(normalizeStyleTarget(target));
4010
+ const styles = this.styles;
4011
+ const styleClass = this.styleClass;
4012
+ for (let i = 0; i < styles.length; i++) {
4013
+ const element = document.createElement("style");
4014
+ element.innerHTML = styles[i];
4015
+ element.className = styleClass;
4016
+ target.append(element);
4017
+ }
4018
+ }
4019
+ removeStylesFrom(target) {
4020
+ target = usableStyleTarget(normalizeStyleTarget(target));
4021
+ const styles = target.querySelectorAll(`.${this.styleClass}`);
4022
+ styles[0].parentNode;
4023
+ for (let i = 0, ii = styles.length; i < ii; ++i) {
4024
+ target.removeChild(styles[i]);
4025
+ }
3762
4026
  }
3763
4027
  }
4028
+ ElementStyles.setDefaultStrategy(ElementStyles.supportsAdoptedStyleSheets
4029
+ ? AdoptedStyleSheetsStrategy
4030
+ : StyleElementStrategy);
3764
4031
 
3765
4032
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
3766
4033
  function createFASTElement(BaseType) {
3767
- return class extends BaseType {
4034
+ const type = class extends BaseType {
3768
4035
  constructor() {
3769
4036
  /* eslint-disable-next-line */
3770
4037
  super();
3771
- Controller.forCustomElement(this);
4038
+ ElementController.forCustomElement(this);
3772
4039
  }
3773
4040
  $emit(type, detail, options) {
3774
4041
  return this.$fastController.emit(type, detail, options);
3775
4042
  }
3776
4043
  connectedCallback() {
3777
- this.$fastController.onConnectedCallback();
4044
+ this.$fastController.connect();
3778
4045
  }
3779
4046
  disconnectedCallback() {
3780
- this.$fastController.onDisconnectedCallback();
4047
+ this.$fastController.disconnect();
3781
4048
  }
3782
4049
  attributeChangedCallback(name, oldValue, newValue) {
3783
4050
  this.$fastController.onAttributeChangedCallback(name, oldValue, newValue);
3784
4051
  }
3785
4052
  };
4053
+ FASTElementDefinition.registerBaseType(type);
4054
+ return type;
4055
+ }
4056
+ function compose(type, nameOrDef) {
4057
+ if (isFunction(type)) {
4058
+ return FASTElementDefinition.compose(type, nameOrDef);
4059
+ }
4060
+ return FASTElementDefinition.compose(this, type);
4061
+ }
4062
+ function define(type, nameOrDef) {
4063
+ if (isFunction(type)) {
4064
+ return FASTElementDefinition.compose(type, nameOrDef).define().type;
4065
+ }
4066
+ return FASTElementDefinition.compose(this, type).define().type;
4067
+ }
4068
+ function from(BaseType) {
4069
+ return createFASTElement(BaseType);
3786
4070
  }
3787
4071
  /**
3788
4072
  * A minimal base class for FASTElements that also provides
@@ -3795,18 +4079,19 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3795
4079
  * provided base type.
3796
4080
  * @param BaseType - The base element type to inherit from.
3797
4081
  */
3798
- from(BaseType) {
3799
- return createFASTElement(BaseType);
3800
- },
4082
+ from,
3801
4083
  /**
3802
4084
  * Defines a platform custom element based on the provided type and definition.
3803
4085
  * @param type - The custom element type to define.
3804
4086
  * @param nameOrDef - The name of the element to define or a definition object
3805
4087
  * that describes the element to define.
3806
4088
  */
3807
- define(type, nameOrDef) {
3808
- return new FASTElementDefinition(type, nameOrDef).define().type;
3809
- },
4089
+ define,
4090
+ /**
4091
+ * Defines metadata for a FASTElement which can be used to later define the element.
4092
+ * @public
4093
+ */
4094
+ compose,
3810
4095
  });
3811
4096
  /**
3812
4097
  * Decorator: Defines a platform custom element based on `FASTElement`.
@@ -3817,8 +4102,8 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3817
4102
  function customElement(nameOrDef) {
3818
4103
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
3819
4104
  return function (type) {
3820
- new FASTElementDefinition(type, nameOrDef).define();
4105
+ define(type, nameOrDef);
3821
4106
  };
3822
4107
  }
3823
4108
 
3824
- export { AdoptedStyleSheetsStrategy, ArrayObserver, Aspect, AttributeDefinition, BindingConfig, BindingMode, CSSDirective, ChangeBinding, ChildrenDirective, Compiler, Controller, DOM, ElementStyles, EventBinding, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, OneTimeBinding, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SignalBinding, SlottedDirective, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, TwoWayBinding, UpdateBinding, Updates, ViewTemplate, attr, bind, booleanConverter, child, children, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, item, length, nullableNumberConverter, observable, onChange, oneTime, ref, repeat, signal, slotted, twoWay, volatile, when };
4109
+ export { ArrayObserver, Aspect, AttributeConfiguration, AttributeDefinition, Binding, CSSDirective, ChildrenDirective, Compiler, DOM, ElementController, ElementStyles, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, SourceLifetime, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewBehaviorOrchestrator, ViewTemplate, attr, bind, booleanConverter, children, createMetadataLocator, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };