@microsoft/fast-element 2.0.0-beta.2 → 2.0.0-beta.5

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 (54) hide show
  1. package/CHANGELOG.json +147 -0
  2. package/CHANGELOG.md +42 -1
  3. package/dist/dts/components/fast-definitions.d.ts +9 -8
  4. package/dist/dts/components/fast-element.d.ts +12 -24
  5. package/dist/dts/context.d.ts +1 -1
  6. package/dist/dts/di/di.d.ts +858 -0
  7. package/dist/dts/hooks.d.ts +2 -2
  8. package/dist/dts/interfaces.d.ts +40 -7
  9. package/dist/dts/observation/observable.d.ts +19 -13
  10. package/dist/dts/styles/element-styles.d.ts +6 -0
  11. package/dist/dts/templating/binding-signal.d.ts +10 -27
  12. package/dist/dts/templating/binding-two-way.d.ts +16 -41
  13. package/dist/dts/templating/binding.d.ts +79 -118
  14. package/dist/dts/templating/html-directive.d.ts +31 -3
  15. package/dist/dts/templating/render.d.ts +277 -0
  16. package/dist/dts/templating/repeat.d.ts +12 -16
  17. package/dist/dts/templating/template.d.ts +3 -3
  18. package/dist/dts/templating/when.d.ts +3 -3
  19. package/dist/dts/testing/exports.d.ts +2 -0
  20. package/dist/dts/testing/fixture.d.ts +90 -0
  21. package/dist/dts/testing/timeout.d.ts +7 -0
  22. package/dist/esm/components/fast-definitions.js +25 -27
  23. package/dist/esm/components/fast-element.js +20 -11
  24. package/dist/esm/context.js +5 -1
  25. package/dist/esm/debug.js +36 -4
  26. package/dist/esm/di/di.js +1351 -0
  27. package/dist/esm/observation/arrays.js +303 -2
  28. package/dist/esm/observation/observable.js +11 -6
  29. package/dist/esm/platform.js +1 -1
  30. package/dist/esm/styles/element-styles.js +14 -0
  31. package/dist/esm/templating/binding-signal.js +56 -61
  32. package/dist/esm/templating/binding-two-way.js +56 -34
  33. package/dist/esm/templating/binding.js +137 -156
  34. package/dist/esm/templating/compiler.js +30 -7
  35. package/dist/esm/templating/html-directive.js +16 -2
  36. package/dist/esm/templating/render.js +392 -0
  37. package/dist/esm/templating/repeat.js +57 -40
  38. package/dist/esm/templating/template.js +8 -5
  39. package/dist/esm/templating/view.js +3 -1
  40. package/dist/esm/templating/when.js +5 -4
  41. package/dist/esm/testing/exports.js +2 -0
  42. package/dist/esm/testing/fixture.js +88 -0
  43. package/dist/esm/testing/timeout.js +24 -0
  44. package/dist/fast-element.api.json +2828 -2758
  45. package/dist/fast-element.d.ts +218 -230
  46. package/dist/fast-element.debug.js +656 -257
  47. package/dist/fast-element.debug.min.js +1 -1
  48. package/dist/fast-element.js +620 -253
  49. package/dist/fast-element.min.js +1 -1
  50. package/dist/fast-element.untrimmed.d.ts +226 -235
  51. package/docs/api-report.md +88 -91
  52. package/package.json +15 -6
  53. package/dist/dts/observation/splice-strategies.d.ts +0 -13
  54. package/dist/esm/observation/splice-strategies.js +0 -400
@@ -112,7 +112,7 @@ if (FAST.error === void 0) {
112
112
  Object.assign(FAST, {
113
113
  warn() { },
114
114
  error(code) {
115
- return new Error(`Code ${code}`);
115
+ return new Error(`Error ${code}`);
116
116
  },
117
117
  addMessages() { },
118
118
  });
@@ -449,7 +449,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
449
449
  }
450
450
  }
451
451
  }
452
- class BindingObserverImplementation extends SubscriberSet {
452
+ class ExpressionNotifierImplementation extends SubscriberSet {
453
453
  constructor(binding, initialSubscriber, isVolatileBinding = false) {
454
454
  super(binding, initialSubscriber);
455
455
  this.binding = binding;
@@ -474,8 +474,13 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
474
474
  const previousWatcher = watcher;
475
475
  watcher = this.needsRefresh ? this : void 0;
476
476
  this.needsRefresh = this.isVolatileBinding;
477
- const result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
478
- watcher = previousWatcher;
477
+ let result;
478
+ try {
479
+ result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
480
+ }
481
+ finally {
482
+ watcher = previousWatcher;
483
+ }
479
484
  return result;
480
485
  }
481
486
  dispose() {
@@ -604,14 +609,14 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
604
609
  */
605
610
  getAccessors,
606
611
  /**
607
- * Creates a {@link BindingObserver} that can watch the
608
- * provided {@link Binding} for changes.
612
+ * Creates a {@link ExpressionNotifier} that can watch the
613
+ * provided {@link Expression} for changes.
609
614
  * @param binding - The binding to observe.
610
615
  * @param initialSubscriber - An initial subscriber to changes in the binding value.
611
616
  * @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
612
617
  */
613
618
  binding(binding, initialSubscriber, isVolatileBinding = this.isVolatileBinding(binding)) {
614
- return new BindingObserverImplementation(binding, initialSubscriber, isVolatileBinding);
619
+ return new ExpressionNotifierImplementation(binding, initialSubscriber, isVolatileBinding);
615
620
  },
616
621
  /**
617
622
  * Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
@@ -845,10 +850,311 @@ const SpliceStrategySupport = Object.freeze({
845
850
  const reset = new Splice(0, emptyArray, 0);
846
851
  reset.reset = true;
847
852
  const resetSplices = [reset];
853
+ // Note: This function is *based* on the computation of the Levenshtein
854
+ // "edit" distance. The one change is that "updates" are treated as two
855
+ // edits - not one. With Array splices, an update is really a delete
856
+ // followed by an add. By retaining this, we optimize for "keeping" the
857
+ // maximum array items in the original array. For example:
858
+ //
859
+ // 'xxxx123' to '123yyyy'
860
+ //
861
+ // With 1-edit updates, the shortest path would be just to update all seven
862
+ // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
863
+ // leaves the substring '123' intact.
864
+ function calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd) {
865
+ // "Deletion" columns
866
+ const rowCount = oldEnd - oldStart + 1;
867
+ const columnCount = currentEnd - currentStart + 1;
868
+ const distances = new Array(rowCount);
869
+ let north;
870
+ let west;
871
+ // "Addition" rows. Initialize null column.
872
+ for (let i = 0; i < rowCount; ++i) {
873
+ distances[i] = new Array(columnCount);
874
+ distances[i][0] = i;
875
+ }
876
+ // Initialize null row
877
+ for (let j = 0; j < columnCount; ++j) {
878
+ distances[0][j] = j;
879
+ }
880
+ for (let i = 1; i < rowCount; ++i) {
881
+ for (let j = 1; j < columnCount; ++j) {
882
+ if (current[currentStart + j - 1] === old[oldStart + i - 1]) {
883
+ distances[i][j] = distances[i - 1][j - 1];
884
+ }
885
+ else {
886
+ north = distances[i - 1][j] + 1;
887
+ west = distances[i][j - 1] + 1;
888
+ distances[i][j] = north < west ? north : west;
889
+ }
890
+ }
891
+ }
892
+ return distances;
893
+ }
894
+ // This starts at the final weight, and walks "backward" by finding
895
+ // the minimum previous weight recursively until the origin of the weight
896
+ // matrix.
897
+ function spliceOperationsFromEditDistances(distances) {
898
+ let i = distances.length - 1;
899
+ let j = distances[0].length - 1;
900
+ let current = distances[i][j];
901
+ const edits = [];
902
+ while (i > 0 || j > 0) {
903
+ if (i === 0) {
904
+ edits.push(2 /* Edit.add */);
905
+ j--;
906
+ continue;
907
+ }
908
+ if (j === 0) {
909
+ edits.push(3 /* Edit.delete */);
910
+ i--;
911
+ continue;
912
+ }
913
+ const northWest = distances[i - 1][j - 1];
914
+ const west = distances[i - 1][j];
915
+ const north = distances[i][j - 1];
916
+ let min;
917
+ if (west < north) {
918
+ min = west < northWest ? west : northWest;
919
+ }
920
+ else {
921
+ min = north < northWest ? north : northWest;
922
+ }
923
+ if (min === northWest) {
924
+ if (northWest === current) {
925
+ edits.push(0 /* Edit.leave */);
926
+ }
927
+ else {
928
+ edits.push(1 /* Edit.update */);
929
+ current = northWest;
930
+ }
931
+ i--;
932
+ j--;
933
+ }
934
+ else if (min === west) {
935
+ edits.push(3 /* Edit.delete */);
936
+ i--;
937
+ current = west;
938
+ }
939
+ else {
940
+ edits.push(2 /* Edit.add */);
941
+ j--;
942
+ current = north;
943
+ }
944
+ }
945
+ return edits.reverse();
946
+ }
947
+ function sharedPrefix(current, old, searchLength) {
948
+ for (let i = 0; i < searchLength; ++i) {
949
+ if (current[i] !== old[i]) {
950
+ return i;
951
+ }
952
+ }
953
+ return searchLength;
954
+ }
955
+ function sharedSuffix(current, old, searchLength) {
956
+ let index1 = current.length;
957
+ let index2 = old.length;
958
+ let count = 0;
959
+ while (count < searchLength && current[--index1] === old[--index2]) {
960
+ count++;
961
+ }
962
+ return count;
963
+ }
964
+ function intersect(start1, end1, start2, end2) {
965
+ // Disjoint
966
+ if (end1 < start2 || end2 < start1) {
967
+ return -1;
968
+ }
969
+ // Adjacent
970
+ if (end1 === start2 || end2 === start1) {
971
+ return 0;
972
+ }
973
+ // Non-zero intersect, span1 first
974
+ if (start1 < start2) {
975
+ if (end1 < end2) {
976
+ return end1 - start2; // Overlap
977
+ }
978
+ return end2 - start2; // Contained
979
+ }
980
+ // Non-zero intersect, span2 first
981
+ if (end2 < end1) {
982
+ return end2 - start1; // Overlap
983
+ }
984
+ return end1 - start1; // Contained
985
+ }
986
+ /**
987
+ * @remarks
988
+ * Lacking individual splice mutation information, the minimal set of
989
+ * splices can be synthesized given the previous state and final state of an
990
+ * array. The basic approach is to calculate the edit distance matrix and
991
+ * choose the shortest path through it.
992
+ *
993
+ * Complexity: O(l * p)
994
+ * l: The length of the current array
995
+ * p: The length of the old array
996
+ */
997
+ function calc(current, currentStart, currentEnd, old, oldStart, oldEnd) {
998
+ let prefixCount = 0;
999
+ let suffixCount = 0;
1000
+ const minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
1001
+ if (currentStart === 0 && oldStart === 0) {
1002
+ prefixCount = sharedPrefix(current, old, minLength);
1003
+ }
1004
+ if (currentEnd === current.length && oldEnd === old.length) {
1005
+ suffixCount = sharedSuffix(current, old, minLength - prefixCount);
1006
+ }
1007
+ currentStart += prefixCount;
1008
+ oldStart += prefixCount;
1009
+ currentEnd -= suffixCount;
1010
+ oldEnd -= suffixCount;
1011
+ if (currentEnd - currentStart === 0 && oldEnd - oldStart === 0) {
1012
+ return emptyArray;
1013
+ }
1014
+ if (currentStart === currentEnd) {
1015
+ const splice = new Splice(currentStart, [], 0);
1016
+ while (oldStart < oldEnd) {
1017
+ splice.removed.push(old[oldStart++]);
1018
+ }
1019
+ return [splice];
1020
+ }
1021
+ else if (oldStart === oldEnd) {
1022
+ return [new Splice(currentStart, [], currentEnd - currentStart)];
1023
+ }
1024
+ const ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
1025
+ const splices = [];
1026
+ let splice = void 0;
1027
+ let index = currentStart;
1028
+ let oldIndex = oldStart;
1029
+ for (let i = 0; i < ops.length; ++i) {
1030
+ switch (ops[i]) {
1031
+ case 0 /* Edit.leave */:
1032
+ if (splice !== void 0) {
1033
+ splices.push(splice);
1034
+ splice = void 0;
1035
+ }
1036
+ index++;
1037
+ oldIndex++;
1038
+ break;
1039
+ case 1 /* Edit.update */:
1040
+ if (splice === void 0) {
1041
+ splice = new Splice(index, [], 0);
1042
+ }
1043
+ splice.addedCount++;
1044
+ index++;
1045
+ splice.removed.push(old[oldIndex]);
1046
+ oldIndex++;
1047
+ break;
1048
+ case 2 /* Edit.add */:
1049
+ if (splice === void 0) {
1050
+ splice = new Splice(index, [], 0);
1051
+ }
1052
+ splice.addedCount++;
1053
+ index++;
1054
+ break;
1055
+ case 3 /* Edit.delete */:
1056
+ if (splice === void 0) {
1057
+ splice = new Splice(index, [], 0);
1058
+ }
1059
+ splice.removed.push(old[oldIndex]);
1060
+ oldIndex++;
1061
+ break;
1062
+ // no default
1063
+ }
1064
+ }
1065
+ if (splice !== void 0) {
1066
+ splices.push(splice);
1067
+ }
1068
+ return splices;
1069
+ }
1070
+ function merge(splice, splices) {
1071
+ let inserted = false;
1072
+ let insertionOffset = 0;
1073
+ for (let i = 0; i < splices.length; i++) {
1074
+ const current = splices[i];
1075
+ current.index += insertionOffset;
1076
+ if (inserted) {
1077
+ continue;
1078
+ }
1079
+ const intersectCount = intersect(splice.index, splice.index + splice.removed.length, current.index, current.index + current.addedCount);
1080
+ if (intersectCount >= 0) {
1081
+ // Merge the two splices
1082
+ splices.splice(i, 1);
1083
+ i--;
1084
+ insertionOffset -= current.addedCount - current.removed.length;
1085
+ splice.addedCount += current.addedCount - intersectCount;
1086
+ const deleteCount = splice.removed.length + current.removed.length - intersectCount;
1087
+ if (!splice.addedCount && !deleteCount) {
1088
+ // merged splice is a noop. discard.
1089
+ inserted = true;
1090
+ }
1091
+ else {
1092
+ let currentRemoved = current.removed;
1093
+ if (splice.index < current.index) {
1094
+ // some prefix of splice.removed is prepended to current.removed.
1095
+ const prepend = splice.removed.slice(0, current.index - splice.index);
1096
+ prepend.push(...currentRemoved);
1097
+ currentRemoved = prepend;
1098
+ }
1099
+ if (splice.index + splice.removed.length >
1100
+ current.index + current.addedCount) {
1101
+ // some suffix of splice.removed is appended to current.removed.
1102
+ const append = splice.removed.slice(current.index + current.addedCount - splice.index);
1103
+ currentRemoved.push(...append);
1104
+ }
1105
+ splice.removed = currentRemoved;
1106
+ if (current.index < splice.index) {
1107
+ splice.index = current.index;
1108
+ }
1109
+ }
1110
+ }
1111
+ else if (splice.index < current.index) {
1112
+ // Insert splice here.
1113
+ inserted = true;
1114
+ splices.splice(i, 0, splice);
1115
+ i++;
1116
+ const offset = splice.addedCount - splice.removed.length;
1117
+ current.index += offset;
1118
+ insertionOffset += offset;
1119
+ }
1120
+ }
1121
+ if (!inserted) {
1122
+ splices.push(splice);
1123
+ }
1124
+ }
1125
+ function project(array, changes) {
1126
+ let splices = [];
1127
+ const initialSplices = [];
1128
+ for (let i = 0, ii = changes.length; i < ii; i++) {
1129
+ merge(changes[i], initialSplices);
1130
+ }
1131
+ for (let i = 0, ii = initialSplices.length; i < ii; ++i) {
1132
+ const splice = initialSplices[i];
1133
+ if (splice.addedCount === 1 && splice.removed.length === 1) {
1134
+ if (splice.removed[0] !== array[splice.index]) {
1135
+ splices.push(splice);
1136
+ }
1137
+ continue;
1138
+ }
1139
+ splices = splices.concat(calc(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
1140
+ }
1141
+ return splices;
1142
+ }
1143
+ /**
1144
+ * A SpliceStrategy that attempts to merge all splices into the minimal set of
1145
+ * splices needed to represent the change from the old array to the new array.
1146
+ * @public
1147
+ */
848
1148
  let defaultSpliceStrategy = Object.freeze({
849
- support: SpliceStrategySupport.splice,
1149
+ support: SpliceStrategySupport.optimized,
850
1150
  normalize(previous, current, changes) {
851
- return previous === void 0 ? changes !== null && changes !== void 0 ? changes : emptyArray : resetSplices;
1151
+ if (previous === void 0) {
1152
+ if (changes === void 0) {
1153
+ return emptyArray;
1154
+ }
1155
+ return changes.length > 1 ? project(current, changes) : changes;
1156
+ }
1157
+ return resetSplices;
852
1158
  },
853
1159
  pop(array, observer, pop, args) {
854
1160
  const notEmpty = array.length > 0;
@@ -1117,6 +1423,20 @@ class ElementStyles {
1117
1423
  static setDefaultStrategy(Strategy) {
1118
1424
  DefaultStyleStrategy = Strategy;
1119
1425
  }
1426
+ /**
1427
+ * Normalizes a set of composable style options.
1428
+ * @param styles - The style options to normalize.
1429
+ * @returns A singular ElementStyles instance or undefined.
1430
+ */
1431
+ static normalize(styles) {
1432
+ return styles === void 0
1433
+ ? void 0
1434
+ : Array.isArray(styles)
1435
+ ? new ElementStyles(styles)
1436
+ : styles instanceof ElementStyles
1437
+ ? styles
1438
+ : new ElementStyles([styles]);
1439
+ }
1120
1440
  }
1121
1441
  /**
1122
1442
  * Indicates whether the DOM supports the adoptedStyleSheets feature.
@@ -1443,6 +1763,13 @@ function htmlDirective(options) {
1443
1763
  HTMLDirective.define(type, options);
1444
1764
  };
1445
1765
  }
1766
+ /**
1767
+ * Captures a binding expression along with related information and capabilities.
1768
+ *
1769
+ * @public
1770
+ */
1771
+ class Binding {
1772
+ }
1446
1773
  /**
1447
1774
  * The type of HTML aspect to target.
1448
1775
  * @public
@@ -1480,12 +1807,15 @@ const Aspect = Object.freeze({
1480
1807
  *
1481
1808
  * @param directive - The directive to assign the aspect to.
1482
1809
  * @param value - The value to base the aspect determination on.
1810
+ * @remarks
1811
+ * If a falsy value is provided, then the content aspect will be assigned.
1483
1812
  */
1484
1813
  assign(directive, value) {
1485
- directive.sourceAspect = value;
1486
1814
  if (!value) {
1815
+ directive.aspectType = Aspect.content;
1487
1816
  return;
1488
1817
  }
1818
+ directive.sourceAspect = value;
1489
1819
  switch (value[0]) {
1490
1820
  case ":":
1491
1821
  directive.targetAspect = value.substring(1);
@@ -1533,6 +1863,10 @@ class StatelessAttachedAttributeDirective {
1533
1863
  */
1534
1864
  constructor(options) {
1535
1865
  this.options = options;
1866
+ /**
1867
+ * The unique id of the factory.
1868
+ */
1869
+ this.id = nextId();
1536
1870
  }
1537
1871
  /**
1538
1872
  * Creates a behavior.
@@ -1561,99 +1895,28 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1561
1895
  throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1562
1896
  }
1563
1897
  : (binding) => binding;
1564
- /**
1565
- * Describes how aspects of an HTML element will be affected by bindings.
1566
- * @public
1567
- */
1568
- const BindingMode = Object.freeze({
1569
- /**
1570
- * Creates a binding mode based on the supplied behavior types.
1571
- * @param UpdateType - The base behavior type used to update aspects.
1572
- * @param EventType - The base behavior type used to respond to events.
1573
- * @returns A new binding mode.
1574
- */
1575
- define(UpdateType, EventType = EventBinding) {
1576
- return Object.freeze({
1577
- [1]: d => new UpdateType(d, DOM.setAttribute),
1578
- [2]: d => new UpdateType(d, DOM.setBooleanAttribute),
1579
- [3]: d => new UpdateType(d, (t, a, v) => (t[a] = v)),
1580
- [4]: d => new (createContentBinding(UpdateType))(d, updateContentTarget),
1581
- [5]: d => new UpdateType(d, updateTokenListTarget),
1582
- [6]: d => new EventType(d),
1583
- });
1584
- },
1585
- });
1586
- /**
1587
- * Describes the configuration for a binding expression.
1588
- * @public
1589
- */
1590
- const BindingConfig = Object.freeze({
1591
- /**
1592
- * Creates a binding configuration based on the provided mode and options.
1593
- * @param mode - The mode to use for the configuration.
1594
- * @param defaultOptions - The default options to use for the configuration.
1595
- * @returns A new binding configuration.
1596
- */
1597
- define(mode, defaultOptions) {
1598
- const config = (options) => {
1599
- return {
1600
- mode: config.mode,
1601
- options: Object.assign({}, defaultOptions, options),
1602
- };
1603
- };
1604
- config.options = defaultOptions;
1605
- config.mode = mode;
1606
- return config;
1607
- },
1608
- });
1609
- /**
1610
- * A base binding behavior for DOM updates.
1611
- * @public
1612
- */
1613
- class UpdateBinding {
1614
- /**
1615
- * Creates an instance of UpdateBinding.
1616
- * @param directive - The directive that has the configuration for this behavior.
1617
- * @param updateTarget - The function used to update the target with the latest value.
1618
- */
1619
- constructor(directive, updateTarget) {
1620
- this.directive = directive;
1621
- this.updateTarget = updateTarget;
1898
+ class OnChangeBinding extends Binding {
1899
+ constructor(evaluate, isVolatile) {
1900
+ super();
1901
+ this.evaluate = evaluate;
1902
+ this.isVolatile = isVolatile;
1622
1903
  }
1623
- /**
1624
- * Bind this behavior to the source.
1625
- * @param source - The source to bind to.
1626
- * @param context - The execution context that the binding is operating within.
1627
- * @param targets - The targets that behaviors in a view can attach to.
1628
- */
1629
- bind(source, context, targets) { }
1630
- /**
1631
- * Unbinds this behavior from the source.
1632
- * @param source - The source to unbind from.
1633
- * @param context - The execution context that the binding is operating within.
1634
- * @param targets - The targets that behaviors in a view can attach to.
1635
- */
1636
- unbind(source, context, targets) { }
1637
- /**
1638
- * Creates a behavior.
1639
- * @param targets - The targets available for behaviors to be attached to.
1640
- */
1641
- createBehavior(targets) {
1642
- return this;
1904
+ createObserver(_, subscriber) {
1905
+ return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1643
1906
  }
1644
1907
  }
1645
- function createContentBinding(Type) {
1646
- return class extends Type {
1647
- unbind(source, context, targets) {
1648
- super.unbind(source, context, targets);
1649
- const target = targets[this.directive.nodeId];
1650
- const view = target.$fastView;
1651
- if (view !== void 0 && view.isComposed) {
1652
- view.unbind();
1653
- view.needsBindOnly = true;
1654
- }
1655
- }
1656
- };
1908
+ class OneTimeBinding extends Binding {
1909
+ constructor(evaluate) {
1910
+ super();
1911
+ this.evaluate = evaluate;
1912
+ }
1913
+ createObserver() {
1914
+ return this;
1915
+ }
1916
+ observe(source, context) {
1917
+ return this.evaluate(source, context);
1918
+ }
1919
+ dispose() { }
1657
1920
  }
1658
1921
  function updateContentTarget(target, aspect, value, source, context) {
1659
1922
  // If there's no actual value, then this equates to the
@@ -1661,7 +1924,7 @@ function updateContentTarget(target, aspect, value, source, context) {
1661
1924
  if (value === null || value === undefined) {
1662
1925
  value = "";
1663
1926
  }
1664
- // If the value has a "create" method, then it's a template-like.
1927
+ // If the value has a "create" method, then it's a ContentTemplate.
1665
1928
  if (value.create) {
1666
1929
  target.textContent = "";
1667
1930
  let view = target.$fastView;
@@ -1747,46 +2010,21 @@ function updateTokenListTarget(target, aspect, value) {
1747
2010
  }
1748
2011
  }
1749
2012
  }
1750
- /**
1751
- * A binding behavior for one-time bindings.
1752
- * @public
1753
- */
1754
- class OneTimeBinding extends UpdateBinding {
1755
- /**
1756
- * Bind this behavior to the source.
1757
- * @param source - The source to bind to.
1758
- * @param context - The execution context that the binding is operating within.
1759
- * @param targets - The targets that behaviors in a view can attach to.
1760
- */
1761
- bind(source, context, targets) {
1762
- const directive = this.directive;
1763
- this.updateTarget(targets[directive.nodeId], directive.targetAspect, directive.binding(source, context), source, context);
1764
- }
1765
- }
1766
2013
  /**
1767
2014
  * A binding behavior for bindings that change.
1768
2015
  * @public
1769
2016
  */
1770
- class ChangeBinding extends UpdateBinding {
2017
+ class BindingBehavior {
1771
2018
  /**
1772
2019
  * Creates an instance of ChangeBinding.
1773
2020
  * @param directive - The directive that has the configuration for this behavior.
1774
2021
  * @param updateTarget - The function used to update the target with the latest value.
1775
2022
  */
1776
2023
  constructor(directive, updateTarget) {
1777
- super(directive, updateTarget);
1778
- this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
2024
+ this.directive = directive;
2025
+ this.updateTarget = updateTarget;
1779
2026
  this.observerProperty = `${directive.id}-o`;
1780
2027
  }
1781
- /**
1782
- * Returns the binding observer used to update the node.
1783
- * @param target - The target node.
1784
- * @returns A BindingObserver.
1785
- */
1786
- getObserver(target) {
1787
- var _a;
1788
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = Observable.binding(this.directive.binding, this, this.isBindingVolatile)));
1789
- }
1790
2028
  /**
1791
2029
  * Bind this behavior to the source.
1792
2030
  * @param source - The source to bind to.
@@ -1823,12 +2061,49 @@ class ChangeBinding extends UpdateBinding {
1823
2061
  const context = observer.context;
1824
2062
  this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
1825
2063
  }
2064
+ /**
2065
+ * Returns the binding observer used to update the node.
2066
+ * @param target - The target node.
2067
+ * @returns A BindingObserver.
2068
+ */
2069
+ getObserver(target) {
2070
+ var _a;
2071
+ return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = this.directive.dataBinding.createObserver(this.directive, this)));
2072
+ }
2073
+ /**
2074
+ * Creates a behavior.
2075
+ * @param targets - The targets available for behaviors to be attached to.
2076
+ */
2077
+ createBehavior(targets) {
2078
+ return this;
2079
+ }
2080
+ }
2081
+ /**
2082
+ * A special binding behavior that can bind node content.
2083
+ * @public
2084
+ */
2085
+ class ContentBehavior extends BindingBehavior {
2086
+ /**
2087
+ * Unbinds this behavior from the source.
2088
+ * @param source - The source to unbind from.
2089
+ * @param context - The execution context that the binding is operating within.
2090
+ * @param targets - The targets that behaviors in a view can attach to.
2091
+ */
2092
+ unbind(source, context, targets) {
2093
+ super.unbind(source, context, targets);
2094
+ const target = targets[this.directive.nodeId];
2095
+ const view = target.$fastView;
2096
+ if (view !== void 0 && view.isComposed) {
2097
+ view.unbind();
2098
+ view.needsBindOnly = true;
2099
+ }
2100
+ }
1826
2101
  }
1827
2102
  /**
1828
2103
  * A binding behavior for handling events.
1829
2104
  * @public
1830
2105
  */
1831
- class EventBinding {
2106
+ class EventBehavior {
1832
2107
  /**
1833
2108
  * Creates an instance of EventBinding.
1834
2109
  * @param directive - The directive that has the configuration for this behavior.
@@ -1849,7 +2124,7 @@ class EventBinding {
1849
2124
  const target = targets[directive.nodeId];
1850
2125
  target[this.sourceProperty] = source;
1851
2126
  target[this.contextProperty] = context;
1852
- target.addEventListener(directive.targetAspect, this, directive.options);
2127
+ target.addEventListener(directive.targetAspect, this, directive.dataBinding.options);
1853
2128
  }
1854
2129
  /**
1855
2130
  * Unbinds this behavior from the source.
@@ -1861,7 +2136,7 @@ class EventBinding {
1861
2136
  const directive = this.directive;
1862
2137
  const target = targets[directive.nodeId];
1863
2138
  target[this.sourceProperty] = target[this.contextProperty] = null;
1864
- target.removeEventListener(directive.targetAspect, this, directive.options);
2139
+ target.removeEventListener(directive.targetAspect, this, directive.dataBinding.options);
1865
2140
  }
1866
2141
  /**
1867
2142
  * Creates a behavior.
@@ -1876,25 +2151,13 @@ class EventBinding {
1876
2151
  handleEvent(event) {
1877
2152
  const target = event.currentTarget;
1878
2153
  ExecutionContext.setEvent(event);
1879
- const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
2154
+ const result = this.directive.dataBinding.evaluate(target[this.sourceProperty], target[this.contextProperty]);
1880
2155
  ExecutionContext.setEvent(null);
1881
2156
  if (result !== true) {
1882
2157
  event.preventDefault();
1883
2158
  }
1884
2159
  }
1885
2160
  }
1886
- /**
1887
- * The default onChange binding configuration.
1888
- * @public
1889
- */
1890
- const onChange = BindingConfig.define(BindingMode.define(ChangeBinding), {});
1891
- /**
1892
- * The default onTime binding configuration.
1893
- * @public
1894
- */
1895
- const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
1896
- once: true,
1897
- });
1898
2161
  /**
1899
2162
  * A directive that applies bindings.
1900
2163
  * @public
@@ -1902,15 +2165,15 @@ const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
1902
2165
  class HTMLBindingDirective {
1903
2166
  /**
1904
2167
  * Creates an instance of HTMLBindingDirective.
1905
- * @param binding - The binding to apply.
1906
- * @param mode - The binding mode to use when applying the binding.
1907
- * @param options - The options to configure the binding with.
2168
+ * @param dataBinding - The binding configuration to apply.
1908
2169
  */
1909
- constructor(binding, mode, options) {
1910
- this.binding = binding;
1911
- this.mode = mode;
1912
- this.options = options;
2170
+ constructor(dataBinding) {
2171
+ this.dataBinding = dataBinding;
1913
2172
  this.factory = null;
2173
+ /**
2174
+ * The unique id of the factory.
2175
+ */
2176
+ this.id = nextId();
1914
2177
  /**
1915
2178
  * The type of aspect to target.
1916
2179
  */
@@ -1930,26 +2193,78 @@ class HTMLBindingDirective {
1930
2193
  createBehavior(targets) {
1931
2194
  if (this.factory == null) {
1932
2195
  if (this.targetAspect === "innerHTML") {
1933
- this.binding = createInnerHTMLBinding(this.binding);
2196
+ this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
2197
+ }
2198
+ switch (this.aspectType) {
2199
+ case 1:
2200
+ this.factory = new BindingBehavior(this, DOM.setAttribute);
2201
+ break;
2202
+ case 2:
2203
+ this.factory = new BindingBehavior(this, DOM.setBooleanAttribute);
2204
+ break;
2205
+ case 3:
2206
+ this.factory = new BindingBehavior(this, (t, a, v) => (t[a] = v));
2207
+ break;
2208
+ case 4:
2209
+ this.factory = new ContentBehavior(this, updateContentTarget);
2210
+ break;
2211
+ case 5:
2212
+ this.factory = new BindingBehavior(this, updateTokenListTarget);
2213
+ break;
2214
+ case 6:
2215
+ this.factory = new EventBehavior(this);
2216
+ break;
2217
+ default:
2218
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
1934
2219
  }
1935
- this.factory = this.mode[this.aspectType](this);
1936
2220
  }
1937
2221
  return this.factory.createBehavior(targets);
1938
2222
  }
1939
2223
  }
1940
2224
  HTMLDirective.define(HTMLBindingDirective, { aspected: true });
1941
2225
  /**
1942
- * Creates a binding directive with the specified configuration.
1943
- * @param binding - The binding expression.
1944
- * @param config - The binding configuration.
1945
- * @returns A binding directive.
2226
+ * Creates an standard binding.
2227
+ * @param binding - The binding to refresh when changed.
2228
+ * @param isVolatile - Indicates whether the binding is volatile or not.
2229
+ * @returns A binding configuration.
1946
2230
  * @public
1947
2231
  */
1948
- function bind(binding, config = onChange) {
1949
- if (!("mode" in config)) {
1950
- config = onChange(config);
1951
- }
1952
- return new HTMLBindingDirective(binding, config.mode, config.options);
2232
+ function bind(binding, isVolatile = Observable.isVolatileBinding(binding)) {
2233
+ return new OnChangeBinding(binding, isVolatile);
2234
+ }
2235
+ /**
2236
+ * Creates a one time binding
2237
+ * @param binding - The binding to refresh when signaled.
2238
+ * @returns A binding configuration.
2239
+ * @public
2240
+ */
2241
+ function oneTime(binding) {
2242
+ return new OneTimeBinding(binding);
2243
+ }
2244
+ /**
2245
+ * Creates an event listener binding.
2246
+ * @param binding - The binding to invoke when the event is raised.
2247
+ * @param options - Event listener options.
2248
+ * @returns A binding configuration.
2249
+ * @public
2250
+ */
2251
+ function listener(binding, options) {
2252
+ const config = new OnChangeBinding(binding, false);
2253
+ config.options = options;
2254
+ return config;
2255
+ }
2256
+ /**
2257
+ * Normalizes the input value into a binding.
2258
+ * @param value - The value to create the default binding for.
2259
+ * @returns A binding configuration for the provided value.
2260
+ * @public
2261
+ */
2262
+ function normalizeBinding(value) {
2263
+ return isFunction(value)
2264
+ ? bind(value)
2265
+ : value instanceof Binding
2266
+ ? value
2267
+ : oneTime(() => value);
1953
2268
  }
1954
2269
 
1955
2270
  function removeNodeSequence(firstNode, lastNode) {
@@ -2005,8 +2320,10 @@ class HTMLView {
2005
2320
  node.parentNode.insertBefore(this.fragment, node);
2006
2321
  }
2007
2322
  else {
2008
- const parentNode = node.parentNode;
2009
2323
  const end = this.lastChild;
2324
+ if (node.previousSibling === end)
2325
+ return;
2326
+ const parentNode = node.parentNode;
2010
2327
  let current = this.firstChild;
2011
2328
  let next;
2012
2329
  while (current !== end) {
@@ -2116,6 +2433,22 @@ const next = {
2116
2433
  index: 0,
2117
2434
  node: null,
2118
2435
  };
2436
+ function tryWarn(name) {
2437
+ if (!name.startsWith("fast-")) {
2438
+ FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
2439
+ }
2440
+ }
2441
+ const warningHost = new Proxy(document.createElement("div"), {
2442
+ get(target, property) {
2443
+ tryWarn(property);
2444
+ const value = Reflect.get(target, property);
2445
+ return isFunction(value) ? value.bind(target) : value;
2446
+ },
2447
+ set(target, property, value) {
2448
+ tryWarn(property);
2449
+ return Reflect.set(target, property, value);
2450
+ },
2451
+ });
2119
2452
  class CompilationContext {
2120
2453
  constructor(fragment, directives) {
2121
2454
  this.fragment = fragment;
@@ -2166,7 +2499,7 @@ class CompilationContext {
2166
2499
  const fragment = this.fragment.cloneNode(true);
2167
2500
  const targets = Object.create(this.proto);
2168
2501
  targets.r = fragment;
2169
- targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : fragment;
2502
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
2170
2503
  for (const id of this.nodeIds) {
2171
2504
  targets[id]; // trigger locator
2172
2505
  }
@@ -2183,7 +2516,7 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2183
2516
  let result = null;
2184
2517
  if (parseResult === null) {
2185
2518
  if (includeBasicValues) {
2186
- result = bind(() => attrValue, oneTime);
2519
+ result = new HTMLBindingDirective(oneTime(() => attrValue));
2187
2520
  Aspect.assign(result, attr.name);
2188
2521
  }
2189
2522
  }
@@ -2220,6 +2553,7 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
2220
2553
  }
2221
2554
  else {
2222
2555
  currentNode.textContent = " ";
2556
+ Aspect.assign(currentPart);
2223
2557
  context.addFactory(currentPart, parentId, nodeId, nodeIndex);
2224
2558
  }
2225
2559
  lastNode = currentNode;
@@ -2352,22 +2686,28 @@ const Compiler = {
2352
2686
  return parts[0];
2353
2687
  }
2354
2688
  let sourceAspect;
2689
+ let binding;
2690
+ let isVolatile = false;
2355
2691
  const partCount = parts.length;
2356
2692
  const finalParts = parts.map((x) => {
2357
2693
  if (isString(x)) {
2358
2694
  return () => x;
2359
2695
  }
2360
2696
  sourceAspect = x.sourceAspect || sourceAspect;
2361
- return x.binding;
2697
+ binding = x.dataBinding || binding;
2698
+ isVolatile = isVolatile || x.dataBinding.isVolatile;
2699
+ return x.dataBinding.evaluate;
2362
2700
  });
2363
- const binding = (scope, context) => {
2701
+ const expression = (scope, context) => {
2364
2702
  let output = "";
2365
2703
  for (let i = 0; i < partCount; ++i) {
2366
2704
  output += finalParts[i](scope, context);
2367
2705
  }
2368
2706
  return output;
2369
2707
  };
2370
- const directive = bind(binding);
2708
+ binding.evaluate = expression;
2709
+ binding.isVolatile = isVolatile;
2710
+ const directive = new HTMLBindingDirective(binding);
2371
2711
  Aspect.assign(directive, sourceAspect);
2372
2712
  return directive;
2373
2713
  },
@@ -2447,12 +2787,12 @@ function html(strings, ...values) {
2447
2787
  let definition;
2448
2788
  html += currentString;
2449
2789
  if (isFunction(currentValue)) {
2450
- html += createAspectedHTML(bind(currentValue), currentString, add);
2790
+ html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2451
2791
  }
2452
2792
  else if (isString(currentValue)) {
2453
2793
  const match = lastAttributeNameRegex.exec(currentString);
2454
2794
  if (match !== null) {
2455
- const directive = bind(() => currentValue, oneTime);
2795
+ const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2456
2796
  Aspect.assign(directive, match[2]);
2457
2797
  html += directive.createHTML(add);
2458
2798
  }
@@ -2460,8 +2800,11 @@ function html(strings, ...values) {
2460
2800
  html += currentValue;
2461
2801
  }
2462
2802
  }
2803
+ else if (currentValue instanceof Binding) {
2804
+ html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2805
+ }
2463
2806
  else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2464
- html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
2807
+ html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2465
2808
  }
2466
2809
  else {
2467
2810
  if (definition.aspected) {
@@ -2506,16 +2849,17 @@ const ref = (propertyName) => new RefDirective(propertyName);
2506
2849
 
2507
2850
  /**
2508
2851
  * A directive that enables basic conditional rendering in a template.
2509
- * @param binding - The condition to test for rendering.
2852
+ * @param condition - The condition to test for rendering.
2510
2853
  * @param templateOrTemplateBinding - The template or a binding that gets
2511
2854
  * the template to render when the condition is true.
2512
2855
  * @public
2513
2856
  */
2514
- function when(binding, templateOrTemplateBinding) {
2515
- const getTemplate = isFunction(templateOrTemplateBinding)
2857
+ function when(condition, templateOrTemplateBinding) {
2858
+ const dataBinding = isFunction(condition) ? condition : () => condition;
2859
+ const templateBinding = isFunction(templateOrTemplateBinding)
2516
2860
  ? templateOrTemplateBinding
2517
2861
  : () => templateOrTemplateBinding;
2518
- return (source, context) => binding(source, context) ? getTemplate(source, context) : null;
2862
+ return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
2519
2863
  }
2520
2864
 
2521
2865
  const defaultRepeatOptions = Object.freeze({
@@ -2536,17 +2880,15 @@ class RepeatBehavior {
2536
2880
  /**
2537
2881
  * Creates an instance of RepeatBehavior.
2538
2882
  * @param location - The location in the DOM to render the repeat.
2539
- * @param itemsBinding - The array to render.
2883
+ * @param dataBinding - The array to render.
2540
2884
  * @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
2541
2885
  * @param templateBinding - The template to render for each item.
2542
2886
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2543
2887
  * @param options - Options used to turn on special repeat features.
2544
2888
  */
2545
- constructor(location, itemsBinding, isItemsBindingVolatile, templateBinding, isTemplateBindingVolatile, options) {
2889
+ constructor(directive, location) {
2890
+ this.directive = directive;
2546
2891
  this.location = location;
2547
- this.itemsBinding = itemsBinding;
2548
- this.templateBinding = templateBinding;
2549
- this.options = options;
2550
2892
  this.source = null;
2551
2893
  this.views = [];
2552
2894
  this.items = null;
@@ -2554,9 +2896,9 @@ class RepeatBehavior {
2554
2896
  this.context = void 0;
2555
2897
  this.childContext = void 0;
2556
2898
  this.bindView = bindWithoutPositioning;
2557
- this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
2558
- this.templateBindingObserver = Observable.binding(templateBinding, this, isTemplateBindingVolatile);
2559
- if (options.positioning) {
2899
+ this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2900
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
2901
+ if (directive.options.positioning) {
2560
2902
  this.bindView = bindWithPositioning;
2561
2903
  }
2562
2904
  }
@@ -2594,15 +2936,18 @@ class RepeatBehavior {
2594
2936
  * @param args - The details about what was changed.
2595
2937
  */
2596
2938
  handleChange(source, args) {
2597
- if (source === this.itemsBinding) {
2939
+ if (args === this.itemsBindingObserver) {
2598
2940
  this.items = this.itemsBindingObserver.observe(this.source, this.context);
2599
2941
  this.observeItems();
2600
2942
  this.refreshAllViews();
2601
2943
  }
2602
- else if (source === this.templateBinding) {
2944
+ else if (args === this.templateBindingObserver) {
2603
2945
  this.template = this.templateBindingObserver.observe(this.source, this.context);
2604
2946
  this.refreshAllViews(true);
2605
2947
  }
2948
+ else if (!args[0]) {
2949
+ return;
2950
+ }
2606
2951
  else if (args[0].reset) {
2607
2952
  this.refreshAllViews();
2608
2953
  }
@@ -2628,36 +2973,51 @@ class RepeatBehavior {
2628
2973
  updateViews(splices) {
2629
2974
  const views = this.views;
2630
2975
  const childContext = this.childContext;
2631
- const totalRemoved = [];
2632
2976
  const bindView = this.bindView;
2633
- let removeDelta = 0;
2634
- for (let i = 0, ii = splices.length; i < ii; ++i) {
2635
- const splice = splices[i];
2636
- const removed = splice.removed;
2637
- totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
2638
- removeDelta -= splice.addedCount;
2639
- }
2640
2977
  const items = this.items;
2641
2978
  const template = this.template;
2979
+ const recycle = this.directive.options.recycle;
2980
+ const leftoverViews = [];
2981
+ let leftoverIndex = 0;
2982
+ let availableViews = 0;
2642
2983
  for (let i = 0, ii = splices.length; i < ii; ++i) {
2643
2984
  const splice = splices[i];
2985
+ const removed = splice.removed;
2986
+ let removeIndex = 0;
2644
2987
  let addIndex = splice.index;
2645
2988
  const end = addIndex + splice.addedCount;
2989
+ const removedViews = views.splice(splice.index, removed.length);
2990
+ availableViews = leftoverViews.length + removedViews.length;
2646
2991
  for (; addIndex < end; ++addIndex) {
2647
2992
  const neighbor = views[addIndex];
2648
2993
  const location = neighbor ? neighbor.firstChild : this.location;
2649
- const view = this.options.recycle && totalRemoved.length > 0
2650
- ? totalRemoved.shift()
2651
- : template.create();
2994
+ let view;
2995
+ if (recycle && availableViews > 0) {
2996
+ if (removeIndex <= availableViews && removedViews.length > 0) {
2997
+ view = removedViews[removeIndex];
2998
+ removeIndex++;
2999
+ }
3000
+ else {
3001
+ view = leftoverViews[leftoverIndex];
3002
+ leftoverIndex++;
3003
+ }
3004
+ availableViews--;
3005
+ }
3006
+ else {
3007
+ view = template.create();
3008
+ }
2652
3009
  views.splice(addIndex, 0, view);
2653
3010
  bindView(view, items, addIndex, childContext);
2654
3011
  view.insertBefore(location);
2655
3012
  }
3013
+ if (removedViews[removeIndex]) {
3014
+ leftoverViews.push(...removedViews.slice(removeIndex));
3015
+ }
2656
3016
  }
2657
- for (let i = 0, ii = totalRemoved.length; i < ii; ++i) {
2658
- totalRemoved[i].dispose();
3017
+ for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
3018
+ leftoverViews[i].dispose();
2659
3019
  }
2660
- if (this.options.positioning) {
3020
+ if (this.directive.options.positioning) {
2661
3021
  for (let i = 0, ii = views.length; i < ii; ++i) {
2662
3022
  views[i].context.updatePosition(i, ii);
2663
3023
  }
@@ -2672,7 +3032,7 @@ class RepeatBehavior {
2672
3032
  let itemsLength = items.length;
2673
3033
  let views = this.views;
2674
3034
  let viewsLength = views.length;
2675
- if (itemsLength === 0 || templateChanged || !this.options.recycle) {
3035
+ if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
2676
3036
  // all views need to be removed
2677
3037
  HTMLView.disposeContiguousBatch(views);
2678
3038
  viewsLength = 0;
@@ -2722,17 +3082,19 @@ class RepeatBehavior {
2722
3082
  class RepeatDirective {
2723
3083
  /**
2724
3084
  * Creates an instance of RepeatDirective.
2725
- * @param itemsBinding - The binding that provides the array to render.
3085
+ * @param dataBinding - The binding that provides the array to render.
2726
3086
  * @param templateBinding - The template binding used to obtain a template to render for each item in the array.
2727
3087
  * @param options - Options used to turn on special repeat features.
2728
3088
  */
2729
- constructor(itemsBinding, templateBinding, options) {
2730
- this.itemsBinding = itemsBinding;
3089
+ constructor(dataBinding, templateBinding, options) {
3090
+ this.dataBinding = dataBinding;
2731
3091
  this.templateBinding = templateBinding;
2732
3092
  this.options = options;
3093
+ /**
3094
+ * The unique id of the factory.
3095
+ */
3096
+ this.id = nextId();
2733
3097
  ArrayObserver.enable();
2734
- this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
2735
- this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
2736
3098
  }
2737
3099
  /**
2738
3100
  * Creates a placeholder string based on the directive's index within the template.
@@ -2746,23 +3108,22 @@ class RepeatDirective {
2746
3108
  * @param target - The node instance to create the behavior for.
2747
3109
  */
2748
3110
  createBehavior(targets) {
2749
- return new RepeatBehavior(targets[this.nodeId], this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
3111
+ return new RepeatBehavior(this, targets[this.nodeId]);
2750
3112
  }
2751
3113
  }
2752
3114
  HTMLDirective.define(RepeatDirective);
2753
3115
  /**
2754
3116
  * A directive that enables list rendering.
2755
- * @param itemsBinding - The array to render.
2756
- * @param templateOrTemplateBinding - The template or a template binding used obtain a template
3117
+ * @param items - The array to render.
3118
+ * @param template - The template or a template binding used obtain a template
2757
3119
  * to render for each item in the array.
2758
3120
  * @param options - Options used to turn on special repeat features.
2759
3121
  * @public
2760
3122
  */
2761
- function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
2762
- const templateBinding = isFunction(templateOrTemplateBinding)
2763
- ? templateOrTemplateBinding
2764
- : () => templateOrTemplateBinding;
2765
- return new RepeatDirective(itemsBinding, templateBinding, options);
3123
+ function repeat(items, template, options = defaultRepeatOptions) {
3124
+ const dataBinding = normalizeBinding(items);
3125
+ const templateBinding = normalizeBinding(template);
3126
+ return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
2766
3127
  }
2767
3128
 
2768
3129
  const selectElements = (value) => value.nodeType === 1;
@@ -3147,19 +3508,15 @@ const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */
3147
3508
  * @public
3148
3509
  */
3149
3510
  class FASTElementDefinition {
3150
- /**
3151
- * Creates an instance of FASTElementDefinition.
3152
- * @param type - The type this definition is being created for.
3153
- * @param nameOrConfig - The name of the element to define or a config object
3154
- * that describes the element to define.
3155
- */
3156
3511
  constructor(type, nameOrConfig = type.definition) {
3512
+ this.platformDefined = false;
3157
3513
  if (isString(nameOrConfig)) {
3158
3514
  nameOrConfig = { name: nameOrConfig };
3159
3515
  }
3160
3516
  this.type = type;
3161
3517
  this.name = nameOrConfig.name;
3162
3518
  this.template = nameOrConfig.template;
3519
+ const proto = type.prototype;
3163
3520
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3164
3521
  const observedAttributes = new Array(attributes.length);
3165
3522
  const propertyLookup = {};
@@ -3169,9 +3526,13 @@ class FASTElementDefinition {
3169
3526
  observedAttributes[i] = current.attribute;
3170
3527
  propertyLookup[current.name] = current;
3171
3528
  attributeLookup[current.attribute] = current;
3529
+ Observable.defineProperty(proto, current);
3172
3530
  }
3531
+ Reflect.defineProperty(type, "observedAttributes", {
3532
+ value: observedAttributes,
3533
+ enumerable: true,
3534
+ });
3173
3535
  this.attributes = attributes;
3174
- this.observedAttributes = observedAttributes;
3175
3536
  this.propertyLookup = propertyLookup;
3176
3537
  this.attributeLookup = attributeLookup;
3177
3538
  this.shadowOptions =
@@ -3184,20 +3545,14 @@ class FASTElementDefinition {
3184
3545
  nameOrConfig.elementOptions === void 0
3185
3546
  ? defaultElementOptions
3186
3547
  : Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
3187
- this.styles =
3188
- nameOrConfig.styles === void 0
3189
- ? void 0
3190
- : Array.isArray(nameOrConfig.styles)
3191
- ? new ElementStyles(nameOrConfig.styles)
3192
- : nameOrConfig.styles instanceof ElementStyles
3193
- ? nameOrConfig.styles
3194
- : new ElementStyles([nameOrConfig.styles]);
3548
+ this.styles = ElementStyles.normalize(nameOrConfig.styles);
3549
+ fastElementRegistry.register(this);
3195
3550
  }
3196
3551
  /**
3197
3552
  * Indicates if this element has been defined in at least one registry.
3198
3553
  */
3199
3554
  get isDefined() {
3200
- return !!fastElementRegistry.getByType(this.type);
3555
+ return this.platformDefined;
3201
3556
  }
3202
3557
  /**
3203
3558
  * Defines a custom element based on this definition.
@@ -3207,22 +3562,26 @@ class FASTElementDefinition {
3207
3562
  */
3208
3563
  define(registry = customElements) {
3209
3564
  const type = this.type;
3210
- if (fastElementRegistry.register(this)) {
3211
- const attributes = this.attributes;
3212
- const proto = type.prototype;
3213
- for (let i = 0, ii = attributes.length; i < ii; ++i) {
3214
- Observable.defineProperty(proto, attributes[i]);
3215
- }
3216
- Reflect.defineProperty(type, "observedAttributes", {
3217
- value: this.observedAttributes,
3218
- enumerable: true,
3219
- });
3220
- }
3221
3565
  if (!registry.get(this.name)) {
3566
+ this.platformDefined = true;
3222
3567
  registry.define(this.name, type, this.elementOptions);
3223
3568
  }
3224
3569
  return this;
3225
3570
  }
3571
+ /**
3572
+ * Creates an instance of FASTElementDefinition.
3573
+ * @param type - The type this definition is being created for.
3574
+ * @param nameOrDef - The name of the element to define or a config object
3575
+ * that describes the element to define.
3576
+ */
3577
+ static compose(type, nameOrDef) {
3578
+ const found = fastElementRegistry.getByType(type);
3579
+ if (found) {
3580
+ return new FASTElementDefinition(class extends type {
3581
+ }, nameOrDef);
3582
+ }
3583
+ return new FASTElementDefinition(type, nameOrDef);
3584
+ }
3226
3585
  }
3227
3586
  /**
3228
3587
  * Gets the element definition associated with the specified type.
@@ -3640,6 +3999,21 @@ function createFASTElement(BaseType) {
3640
3999
  }
3641
4000
  };
3642
4001
  }
4002
+ function compose(type, nameOrDef) {
4003
+ if (isFunction(type)) {
4004
+ return FASTElementDefinition.compose(type, nameOrDef);
4005
+ }
4006
+ return FASTElementDefinition.compose(this, type);
4007
+ }
4008
+ function define(type, nameOrDef) {
4009
+ if (isFunction(type)) {
4010
+ return FASTElementDefinition.compose(type, nameOrDef).define().type;
4011
+ }
4012
+ return FASTElementDefinition.compose(this, type).define().type;
4013
+ }
4014
+ function from(BaseType) {
4015
+ return createFASTElement(BaseType);
4016
+ }
3643
4017
  /**
3644
4018
  * A minimal base class for FASTElements that also provides
3645
4019
  * static helpers for working with FASTElements.
@@ -3651,26 +4025,19 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3651
4025
  * provided base type.
3652
4026
  * @param BaseType - The base element type to inherit from.
3653
4027
  */
3654
- from(BaseType) {
3655
- return createFASTElement(BaseType);
3656
- },
4028
+ from,
3657
4029
  /**
3658
4030
  * Defines a platform custom element based on the provided type and definition.
3659
4031
  * @param type - The custom element type to define.
3660
4032
  * @param nameOrDef - The name of the element to define or a definition object
3661
4033
  * that describes the element to define.
3662
4034
  */
3663
- define(type, nameOrDef) {
3664
- return this.metadata(type, nameOrDef).define().type;
3665
- },
4035
+ define,
3666
4036
  /**
3667
4037
  * Defines metadata for a FASTElement which can be used to later define the element.
3668
- * IMPORTANT: This API will be renamed to "compose" in a future beta.
3669
4038
  * @public
3670
4039
  */
3671
- metadata(type, nameOrDef) {
3672
- return new FASTElementDefinition(type, nameOrDef);
3673
- },
4040
+ compose,
3674
4041
  });
3675
4042
  /**
3676
4043
  * Decorator: Defines a platform custom element based on `FASTElement`.
@@ -3681,8 +4048,8 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3681
4048
  function customElement(nameOrDef) {
3682
4049
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
3683
4050
  return function (type) {
3684
- FASTElement.define(type, nameOrDef);
4051
+ define(type, nameOrDef);
3685
4052
  };
3686
4053
  }
3687
4054
 
3688
- 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, SlottedDirective, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, UpdateBinding, Updates, ViewTemplate, attr, bind, booleanConverter, children, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, nullableNumberConverter, observable, onChange, oneTime, ref, repeat, slotted, volatile, when };
4055
+ export { AdoptedStyleSheetsStrategy, ArrayObserver, Aspect, AttributeDefinition, Binding, BindingBehavior, CSSDirective, ChildrenDirective, Compiler, ContentBehavior, Controller, DOM, ElementStyles, EventBehavior, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewTemplate, attr, bind, booleanConverter, children, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };