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

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 (68) 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/interfaces.d.ts +43 -7
  8. package/dist/dts/observation/observable.d.ts +19 -13
  9. package/dist/dts/state/exports.d.ts +3 -0
  10. package/dist/dts/state/reactive.d.ts +8 -0
  11. package/dist/dts/state/state.d.ts +141 -0
  12. package/dist/dts/state/visitor.d.ts +6 -0
  13. package/dist/dts/state/watch.d.ts +10 -0
  14. package/dist/dts/styles/element-styles.d.ts +6 -0
  15. package/dist/dts/templating/binding-signal.d.ts +10 -27
  16. package/dist/dts/templating/binding-two-way.d.ts +16 -41
  17. package/dist/dts/templating/binding.d.ts +79 -118
  18. package/dist/dts/templating/html-directive.d.ts +28 -2
  19. package/dist/dts/templating/render.d.ts +277 -0
  20. package/dist/dts/templating/repeat.d.ts +12 -16
  21. package/dist/dts/templating/template.d.ts +3 -3
  22. package/dist/dts/templating/when.d.ts +3 -3
  23. package/dist/dts/testing/exports.d.ts +2 -0
  24. package/dist/dts/testing/fixture.d.ts +90 -0
  25. package/dist/dts/testing/timeout.d.ts +7 -0
  26. package/dist/dts/utilities.d.ts +0 -18
  27. package/dist/esm/components/fast-definitions.js +25 -27
  28. package/dist/esm/components/fast-element.js +20 -11
  29. package/dist/esm/context.js +5 -1
  30. package/dist/esm/debug.js +35 -4
  31. package/dist/esm/di/di.js +1351 -0
  32. package/dist/esm/interfaces.js +4 -0
  33. package/dist/esm/observation/arrays.js +303 -2
  34. package/dist/esm/observation/observable.js +11 -6
  35. package/dist/esm/platform.js +1 -1
  36. package/dist/esm/state/exports.js +3 -0
  37. package/dist/esm/state/reactive.js +34 -0
  38. package/dist/esm/state/state.js +148 -0
  39. package/dist/esm/state/visitor.js +28 -0
  40. package/dist/esm/state/watch.js +36 -0
  41. package/dist/esm/styles/element-styles.js +14 -0
  42. package/dist/esm/templating/binding-signal.js +56 -61
  43. package/dist/esm/templating/binding-two-way.js +51 -35
  44. package/dist/esm/templating/binding.js +137 -156
  45. package/dist/esm/templating/compiler.js +29 -7
  46. package/dist/esm/templating/html-directive.js +12 -1
  47. package/dist/esm/templating/render.js +392 -0
  48. package/dist/esm/templating/repeat.js +57 -40
  49. package/dist/esm/templating/template.js +8 -5
  50. package/dist/esm/templating/view.js +3 -1
  51. package/dist/esm/templating/when.js +5 -4
  52. package/dist/esm/testing/exports.js +2 -0
  53. package/dist/esm/testing/fixture.js +88 -0
  54. package/dist/esm/testing/timeout.js +24 -0
  55. package/dist/esm/utilities.js +0 -95
  56. package/dist/fast-element.api.json +2827 -2757
  57. package/dist/fast-element.d.ts +215 -229
  58. package/dist/fast-element.debug.js +650 -256
  59. package/dist/fast-element.debug.min.js +1 -1
  60. package/dist/fast-element.js +615 -252
  61. package/dist/fast-element.min.js +1 -1
  62. package/dist/fast-element.untrimmed.d.ts +223 -234
  63. package/docs/api-report.md +87 -90
  64. package/package.json +18 -9
  65. package/dist/dts/hooks.d.ts +0 -20
  66. package/dist/dts/observation/splice-strategies.d.ts +0 -13
  67. package/dist/esm/hooks.js +0 -32
  68. package/dist/esm/observation/splice-strategies.js +0 -400
@@ -98,19 +98,50 @@ const debugMessages = {
98
98
  [1201 /* onlySetHTMLPolicyOnce */]: "The HTML policy can only be set once.",
99
99
  [1202 /* bindingInnerHTMLRequiresTrustedTypes */]: "To bind innerHTML, you must use a TrustedTypesPolicy.",
100
100
  [1203 /* twoWayBindingRequiresObservables */]: "View=>Model update skipped. To use twoWay binding, the target property must be observable.",
101
+ [1204 /* hostBindingWithoutHost */]: "No host element is present. Cannot bind host with ${name}.",
102
+ [1205 /* unsupportedBindingBehavior */]: "The requested binding behavior is not supported by the binding engine.",
101
103
  [1401 /* missingElementDefinition */]: "Missing FASTElement definition.",
104
+ [1501 /* noRegistrationForContext */]: "No registration for Context/Interface '${name}'.",
105
+ [1502 /* noFactoryForResolver */]: "Dependency injection resolver for '${key}' returned a null factory.",
106
+ [1503 /* invalidResolverStrategy */]: "Invalid dependency injection resolver strategy specified '${strategy}'.",
107
+ [1504 /* cannotAutoregisterDependency */]: "Unable to autoregister dependency.",
108
+ [1505 /* cannotResolveKey */]: "Unable to resolve dependency injection key '${key}'.",
109
+ [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.",
110
+ [1507 /* cannotJITRegisterNonConstructor */]: "Attempted to jitRegister something that is not a constructor '${value}'. Did you forget to register this dependency?",
111
+ [1508 /* cannotJITRegisterIntrinsic */]: "Attempted to jitRegister an intrinsic type '${value}'. Did you forget to add @inject(Key)?",
112
+ [1509 /* cannotJITRegisterInterface */]: "Attempted to jitRegister an interface '${value}'.",
113
+ [1510 /* invalidResolver */]: "A valid resolver was not returned from the register method.",
114
+ [1511 /* invalidKey */]: "Key/value cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?",
115
+ [1512 /* noDefaultResolver */]: "'${key}' not registered. Did you forget to add @singleton()?",
116
+ [1513 /* cyclicDependency */]: "Cyclic dependency found '${name}'.",
117
+ [1514 /* connectUpdateRequiresController */]: "Injected properties that are updated on changes to DOM connectivity require the target object to be an instance of FASTElement.",
102
118
  };
119
+ const allPlaceholders = /(\$\{\w+?})/g;
120
+ const placeholder = /\$\{(\w+?)}/g;
121
+ const noValues = Object.freeze({});
122
+ function formatMessage(message, values) {
123
+ return message
124
+ .split(allPlaceholders)
125
+ .map(v => {
126
+ var _a;
127
+ const replaced = v.replace(placeholder, "$1");
128
+ return String((_a = values[replaced]) !== null && _a !== void 0 ? _a : v);
129
+ })
130
+ .join("");
131
+ }
103
132
  Object.assign(FAST$1, {
104
133
  addMessages(messages) {
105
134
  Object.assign(debugMessages, messages);
106
135
  },
107
- warn(code, ...args) {
136
+ warn(code, values = noValues) {
108
137
  var _a;
109
- console.warn((_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Warning");
138
+ const message = (_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Warning";
139
+ console.warn(formatMessage(message, values));
110
140
  },
111
- error(code, ...args) {
141
+ error(code, values = noValues) {
112
142
  var _a;
113
- return new Error((_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Error");
143
+ const message = (_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Error";
144
+ return new Error(formatMessage(message, values));
114
145
  },
115
146
  });
116
147
 
@@ -142,7 +173,7 @@ if (FAST.error === void 0) {
142
173
  Object.assign(FAST, {
143
174
  warn() { },
144
175
  error(code) {
145
- return new Error(`Code ${code}`);
176
+ return new Error(`Error ${code}`);
146
177
  },
147
178
  addMessages() { },
148
179
  });
@@ -479,7 +510,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
479
510
  }
480
511
  }
481
512
  }
482
- class BindingObserverImplementation extends SubscriberSet {
513
+ class ExpressionNotifierImplementation extends SubscriberSet {
483
514
  constructor(binding, initialSubscriber, isVolatileBinding = false) {
484
515
  super(binding, initialSubscriber);
485
516
  this.binding = binding;
@@ -504,8 +535,13 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
504
535
  const previousWatcher = watcher;
505
536
  watcher = this.needsRefresh ? this : void 0;
506
537
  this.needsRefresh = this.isVolatileBinding;
507
- const result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
508
- watcher = previousWatcher;
538
+ let result;
539
+ try {
540
+ result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
541
+ }
542
+ finally {
543
+ watcher = previousWatcher;
544
+ }
509
545
  return result;
510
546
  }
511
547
  dispose() {
@@ -634,14 +670,14 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
634
670
  */
635
671
  getAccessors,
636
672
  /**
637
- * Creates a {@link BindingObserver} that can watch the
638
- * provided {@link Binding} for changes.
673
+ * Creates a {@link ExpressionNotifier} that can watch the
674
+ * provided {@link Expression} for changes.
639
675
  * @param binding - The binding to observe.
640
676
  * @param initialSubscriber - An initial subscriber to changes in the binding value.
641
677
  * @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
642
678
  */
643
679
  binding(binding, initialSubscriber, isVolatileBinding = this.isVolatileBinding(binding)) {
644
- return new BindingObserverImplementation(binding, initialSubscriber, isVolatileBinding);
680
+ return new ExpressionNotifierImplementation(binding, initialSubscriber, isVolatileBinding);
645
681
  },
646
682
  /**
647
683
  * Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
@@ -875,10 +911,311 @@ const SpliceStrategySupport = Object.freeze({
875
911
  const reset = new Splice(0, emptyArray, 0);
876
912
  reset.reset = true;
877
913
  const resetSplices = [reset];
914
+ // Note: This function is *based* on the computation of the Levenshtein
915
+ // "edit" distance. The one change is that "updates" are treated as two
916
+ // edits - not one. With Array splices, an update is really a delete
917
+ // followed by an add. By retaining this, we optimize for "keeping" the
918
+ // maximum array items in the original array. For example:
919
+ //
920
+ // 'xxxx123' to '123yyyy'
921
+ //
922
+ // With 1-edit updates, the shortest path would be just to update all seven
923
+ // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
924
+ // leaves the substring '123' intact.
925
+ function calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd) {
926
+ // "Deletion" columns
927
+ const rowCount = oldEnd - oldStart + 1;
928
+ const columnCount = currentEnd - currentStart + 1;
929
+ const distances = new Array(rowCount);
930
+ let north;
931
+ let west;
932
+ // "Addition" rows. Initialize null column.
933
+ for (let i = 0; i < rowCount; ++i) {
934
+ distances[i] = new Array(columnCount);
935
+ distances[i][0] = i;
936
+ }
937
+ // Initialize null row
938
+ for (let j = 0; j < columnCount; ++j) {
939
+ distances[0][j] = j;
940
+ }
941
+ for (let i = 1; i < rowCount; ++i) {
942
+ for (let j = 1; j < columnCount; ++j) {
943
+ if (current[currentStart + j - 1] === old[oldStart + i - 1]) {
944
+ distances[i][j] = distances[i - 1][j - 1];
945
+ }
946
+ else {
947
+ north = distances[i - 1][j] + 1;
948
+ west = distances[i][j - 1] + 1;
949
+ distances[i][j] = north < west ? north : west;
950
+ }
951
+ }
952
+ }
953
+ return distances;
954
+ }
955
+ // This starts at the final weight, and walks "backward" by finding
956
+ // the minimum previous weight recursively until the origin of the weight
957
+ // matrix.
958
+ function spliceOperationsFromEditDistances(distances) {
959
+ let i = distances.length - 1;
960
+ let j = distances[0].length - 1;
961
+ let current = distances[i][j];
962
+ const edits = [];
963
+ while (i > 0 || j > 0) {
964
+ if (i === 0) {
965
+ edits.push(2 /* Edit.add */);
966
+ j--;
967
+ continue;
968
+ }
969
+ if (j === 0) {
970
+ edits.push(3 /* Edit.delete */);
971
+ i--;
972
+ continue;
973
+ }
974
+ const northWest = distances[i - 1][j - 1];
975
+ const west = distances[i - 1][j];
976
+ const north = distances[i][j - 1];
977
+ let min;
978
+ if (west < north) {
979
+ min = west < northWest ? west : northWest;
980
+ }
981
+ else {
982
+ min = north < northWest ? north : northWest;
983
+ }
984
+ if (min === northWest) {
985
+ if (northWest === current) {
986
+ edits.push(0 /* Edit.leave */);
987
+ }
988
+ else {
989
+ edits.push(1 /* Edit.update */);
990
+ current = northWest;
991
+ }
992
+ i--;
993
+ j--;
994
+ }
995
+ else if (min === west) {
996
+ edits.push(3 /* Edit.delete */);
997
+ i--;
998
+ current = west;
999
+ }
1000
+ else {
1001
+ edits.push(2 /* Edit.add */);
1002
+ j--;
1003
+ current = north;
1004
+ }
1005
+ }
1006
+ return edits.reverse();
1007
+ }
1008
+ function sharedPrefix(current, old, searchLength) {
1009
+ for (let i = 0; i < searchLength; ++i) {
1010
+ if (current[i] !== old[i]) {
1011
+ return i;
1012
+ }
1013
+ }
1014
+ return searchLength;
1015
+ }
1016
+ function sharedSuffix(current, old, searchLength) {
1017
+ let index1 = current.length;
1018
+ let index2 = old.length;
1019
+ let count = 0;
1020
+ while (count < searchLength && current[--index1] === old[--index2]) {
1021
+ count++;
1022
+ }
1023
+ return count;
1024
+ }
1025
+ function intersect(start1, end1, start2, end2) {
1026
+ // Disjoint
1027
+ if (end1 < start2 || end2 < start1) {
1028
+ return -1;
1029
+ }
1030
+ // Adjacent
1031
+ if (end1 === start2 || end2 === start1) {
1032
+ return 0;
1033
+ }
1034
+ // Non-zero intersect, span1 first
1035
+ if (start1 < start2) {
1036
+ if (end1 < end2) {
1037
+ return end1 - start2; // Overlap
1038
+ }
1039
+ return end2 - start2; // Contained
1040
+ }
1041
+ // Non-zero intersect, span2 first
1042
+ if (end2 < end1) {
1043
+ return end2 - start1; // Overlap
1044
+ }
1045
+ return end1 - start1; // Contained
1046
+ }
1047
+ /**
1048
+ * @remarks
1049
+ * Lacking individual splice mutation information, the minimal set of
1050
+ * splices can be synthesized given the previous state and final state of an
1051
+ * array. The basic approach is to calculate the edit distance matrix and
1052
+ * choose the shortest path through it.
1053
+ *
1054
+ * Complexity: O(l * p)
1055
+ * l: The length of the current array
1056
+ * p: The length of the old array
1057
+ */
1058
+ function calc(current, currentStart, currentEnd, old, oldStart, oldEnd) {
1059
+ let prefixCount = 0;
1060
+ let suffixCount = 0;
1061
+ const minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
1062
+ if (currentStart === 0 && oldStart === 0) {
1063
+ prefixCount = sharedPrefix(current, old, minLength);
1064
+ }
1065
+ if (currentEnd === current.length && oldEnd === old.length) {
1066
+ suffixCount = sharedSuffix(current, old, minLength - prefixCount);
1067
+ }
1068
+ currentStart += prefixCount;
1069
+ oldStart += prefixCount;
1070
+ currentEnd -= suffixCount;
1071
+ oldEnd -= suffixCount;
1072
+ if (currentEnd - currentStart === 0 && oldEnd - oldStart === 0) {
1073
+ return emptyArray;
1074
+ }
1075
+ if (currentStart === currentEnd) {
1076
+ const splice = new Splice(currentStart, [], 0);
1077
+ while (oldStart < oldEnd) {
1078
+ splice.removed.push(old[oldStart++]);
1079
+ }
1080
+ return [splice];
1081
+ }
1082
+ else if (oldStart === oldEnd) {
1083
+ return [new Splice(currentStart, [], currentEnd - currentStart)];
1084
+ }
1085
+ const ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
1086
+ const splices = [];
1087
+ let splice = void 0;
1088
+ let index = currentStart;
1089
+ let oldIndex = oldStart;
1090
+ for (let i = 0; i < ops.length; ++i) {
1091
+ switch (ops[i]) {
1092
+ case 0 /* Edit.leave */:
1093
+ if (splice !== void 0) {
1094
+ splices.push(splice);
1095
+ splice = void 0;
1096
+ }
1097
+ index++;
1098
+ oldIndex++;
1099
+ break;
1100
+ case 1 /* Edit.update */:
1101
+ if (splice === void 0) {
1102
+ splice = new Splice(index, [], 0);
1103
+ }
1104
+ splice.addedCount++;
1105
+ index++;
1106
+ splice.removed.push(old[oldIndex]);
1107
+ oldIndex++;
1108
+ break;
1109
+ case 2 /* Edit.add */:
1110
+ if (splice === void 0) {
1111
+ splice = new Splice(index, [], 0);
1112
+ }
1113
+ splice.addedCount++;
1114
+ index++;
1115
+ break;
1116
+ case 3 /* Edit.delete */:
1117
+ if (splice === void 0) {
1118
+ splice = new Splice(index, [], 0);
1119
+ }
1120
+ splice.removed.push(old[oldIndex]);
1121
+ oldIndex++;
1122
+ break;
1123
+ // no default
1124
+ }
1125
+ }
1126
+ if (splice !== void 0) {
1127
+ splices.push(splice);
1128
+ }
1129
+ return splices;
1130
+ }
1131
+ function merge(splice, splices) {
1132
+ let inserted = false;
1133
+ let insertionOffset = 0;
1134
+ for (let i = 0; i < splices.length; i++) {
1135
+ const current = splices[i];
1136
+ current.index += insertionOffset;
1137
+ if (inserted) {
1138
+ continue;
1139
+ }
1140
+ const intersectCount = intersect(splice.index, splice.index + splice.removed.length, current.index, current.index + current.addedCount);
1141
+ if (intersectCount >= 0) {
1142
+ // Merge the two splices
1143
+ splices.splice(i, 1);
1144
+ i--;
1145
+ insertionOffset -= current.addedCount - current.removed.length;
1146
+ splice.addedCount += current.addedCount - intersectCount;
1147
+ const deleteCount = splice.removed.length + current.removed.length - intersectCount;
1148
+ if (!splice.addedCount && !deleteCount) {
1149
+ // merged splice is a noop. discard.
1150
+ inserted = true;
1151
+ }
1152
+ else {
1153
+ let currentRemoved = current.removed;
1154
+ if (splice.index < current.index) {
1155
+ // some prefix of splice.removed is prepended to current.removed.
1156
+ const prepend = splice.removed.slice(0, current.index - splice.index);
1157
+ prepend.push(...currentRemoved);
1158
+ currentRemoved = prepend;
1159
+ }
1160
+ if (splice.index + splice.removed.length >
1161
+ current.index + current.addedCount) {
1162
+ // some suffix of splice.removed is appended to current.removed.
1163
+ const append = splice.removed.slice(current.index + current.addedCount - splice.index);
1164
+ currentRemoved.push(...append);
1165
+ }
1166
+ splice.removed = currentRemoved;
1167
+ if (current.index < splice.index) {
1168
+ splice.index = current.index;
1169
+ }
1170
+ }
1171
+ }
1172
+ else if (splice.index < current.index) {
1173
+ // Insert splice here.
1174
+ inserted = true;
1175
+ splices.splice(i, 0, splice);
1176
+ i++;
1177
+ const offset = splice.addedCount - splice.removed.length;
1178
+ current.index += offset;
1179
+ insertionOffset += offset;
1180
+ }
1181
+ }
1182
+ if (!inserted) {
1183
+ splices.push(splice);
1184
+ }
1185
+ }
1186
+ function project(array, changes) {
1187
+ let splices = [];
1188
+ const initialSplices = [];
1189
+ for (let i = 0, ii = changes.length; i < ii; i++) {
1190
+ merge(changes[i], initialSplices);
1191
+ }
1192
+ for (let i = 0, ii = initialSplices.length; i < ii; ++i) {
1193
+ const splice = initialSplices[i];
1194
+ if (splice.addedCount === 1 && splice.removed.length === 1) {
1195
+ if (splice.removed[0] !== array[splice.index]) {
1196
+ splices.push(splice);
1197
+ }
1198
+ continue;
1199
+ }
1200
+ splices = splices.concat(calc(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
1201
+ }
1202
+ return splices;
1203
+ }
1204
+ /**
1205
+ * A SpliceStrategy that attempts to merge all splices into the minimal set of
1206
+ * splices needed to represent the change from the old array to the new array.
1207
+ * @public
1208
+ */
878
1209
  let defaultSpliceStrategy = Object.freeze({
879
- support: SpliceStrategySupport.splice,
1210
+ support: SpliceStrategySupport.optimized,
880
1211
  normalize(previous, current, changes) {
881
- return previous === void 0 ? changes !== null && changes !== void 0 ? changes : emptyArray : resetSplices;
1212
+ if (previous === void 0) {
1213
+ if (changes === void 0) {
1214
+ return emptyArray;
1215
+ }
1216
+ return changes.length > 1 ? project(current, changes) : changes;
1217
+ }
1218
+ return resetSplices;
882
1219
  },
883
1220
  pop(array, observer, pop, args) {
884
1221
  const notEmpty = array.length > 0;
@@ -1147,6 +1484,20 @@ class ElementStyles {
1147
1484
  static setDefaultStrategy(Strategy) {
1148
1485
  DefaultStyleStrategy = Strategy;
1149
1486
  }
1487
+ /**
1488
+ * Normalizes a set of composable style options.
1489
+ * @param styles - The style options to normalize.
1490
+ * @returns A singular ElementStyles instance or undefined.
1491
+ */
1492
+ static normalize(styles) {
1493
+ return styles === void 0
1494
+ ? void 0
1495
+ : Array.isArray(styles)
1496
+ ? new ElementStyles(styles)
1497
+ : styles instanceof ElementStyles
1498
+ ? styles
1499
+ : new ElementStyles([styles]);
1500
+ }
1150
1501
  }
1151
1502
  /**
1152
1503
  * Indicates whether the DOM supports the adoptedStyleSheets feature.
@@ -1473,6 +1824,13 @@ function htmlDirective(options) {
1473
1824
  HTMLDirective.define(type, options);
1474
1825
  };
1475
1826
  }
1827
+ /**
1828
+ * Captures a binding expression along with related information and capabilities.
1829
+ *
1830
+ * @public
1831
+ */
1832
+ class Binding {
1833
+ }
1476
1834
  /**
1477
1835
  * The type of HTML aspect to target.
1478
1836
  * @public
@@ -1566,6 +1924,10 @@ class StatelessAttachedAttributeDirective {
1566
1924
  */
1567
1925
  constructor(options) {
1568
1926
  this.options = options;
1927
+ /**
1928
+ * The unique id of the factory.
1929
+ */
1930
+ this.id = nextId();
1569
1931
  }
1570
1932
  /**
1571
1933
  * Creates a behavior.
@@ -1594,99 +1956,28 @@ const createInnerHTMLBinding = globalThis.TrustedHTML
1594
1956
  throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
1595
1957
  }
1596
1958
  : (binding) => binding;
1597
- /**
1598
- * Describes how aspects of an HTML element will be affected by bindings.
1599
- * @public
1600
- */
1601
- const BindingMode = Object.freeze({
1602
- /**
1603
- * Creates a binding mode based on the supplied behavior types.
1604
- * @param UpdateType - The base behavior type used to update aspects.
1605
- * @param EventType - The base behavior type used to respond to events.
1606
- * @returns A new binding mode.
1607
- */
1608
- define(UpdateType, EventType = EventBinding) {
1609
- return Object.freeze({
1610
- [1]: d => new UpdateType(d, DOM.setAttribute),
1611
- [2]: d => new UpdateType(d, DOM.setBooleanAttribute),
1612
- [3]: d => new UpdateType(d, (t, a, v) => (t[a] = v)),
1613
- [4]: d => new (createContentBinding(UpdateType))(d, updateContentTarget),
1614
- [5]: d => new UpdateType(d, updateTokenListTarget),
1615
- [6]: d => new EventType(d),
1616
- });
1617
- },
1618
- });
1619
- /**
1620
- * Describes the configuration for a binding expression.
1621
- * @public
1622
- */
1623
- const BindingConfig = Object.freeze({
1624
- /**
1625
- * Creates a binding configuration based on the provided mode and options.
1626
- * @param mode - The mode to use for the configuration.
1627
- * @param defaultOptions - The default options to use for the configuration.
1628
- * @returns A new binding configuration.
1629
- */
1630
- define(mode, defaultOptions) {
1631
- const config = (options) => {
1632
- return {
1633
- mode: config.mode,
1634
- options: Object.assign({}, defaultOptions, options),
1635
- };
1636
- };
1637
- config.options = defaultOptions;
1638
- config.mode = mode;
1639
- return config;
1640
- },
1641
- });
1642
- /**
1643
- * A base binding behavior for DOM updates.
1644
- * @public
1645
- */
1646
- class UpdateBinding {
1647
- /**
1648
- * Creates an instance of UpdateBinding.
1649
- * @param directive - The directive that has the configuration for this behavior.
1650
- * @param updateTarget - The function used to update the target with the latest value.
1651
- */
1652
- constructor(directive, updateTarget) {
1653
- this.directive = directive;
1654
- this.updateTarget = updateTarget;
1959
+ class OnChangeBinding extends Binding {
1960
+ constructor(evaluate, isVolatile) {
1961
+ super();
1962
+ this.evaluate = evaluate;
1963
+ this.isVolatile = isVolatile;
1655
1964
  }
1656
- /**
1657
- * Bind this behavior to the source.
1658
- * @param source - The source to bind to.
1659
- * @param context - The execution context that the binding is operating within.
1660
- * @param targets - The targets that behaviors in a view can attach to.
1661
- */
1662
- bind(source, context, targets) { }
1663
- /**
1664
- * Unbinds this behavior from the source.
1665
- * @param source - The source to unbind from.
1666
- * @param context - The execution context that the binding is operating within.
1667
- * @param targets - The targets that behaviors in a view can attach to.
1668
- */
1669
- unbind(source, context, targets) { }
1670
- /**
1671
- * Creates a behavior.
1672
- * @param targets - The targets available for behaviors to be attached to.
1673
- */
1674
- createBehavior(targets) {
1675
- return this;
1965
+ createObserver(_, subscriber) {
1966
+ return Observable.binding(this.evaluate, subscriber, this.isVolatile);
1676
1967
  }
1677
1968
  }
1678
- function createContentBinding(Type) {
1679
- return class extends Type {
1680
- unbind(source, context, targets) {
1681
- super.unbind(source, context, targets);
1682
- const target = targets[this.directive.nodeId];
1683
- const view = target.$fastView;
1684
- if (view !== void 0 && view.isComposed) {
1685
- view.unbind();
1686
- view.needsBindOnly = true;
1687
- }
1688
- }
1689
- };
1969
+ class OneTimeBinding extends Binding {
1970
+ constructor(evaluate) {
1971
+ super();
1972
+ this.evaluate = evaluate;
1973
+ }
1974
+ createObserver() {
1975
+ return this;
1976
+ }
1977
+ observe(source, context) {
1978
+ return this.evaluate(source, context);
1979
+ }
1980
+ dispose() { }
1690
1981
  }
1691
1982
  function updateContentTarget(target, aspect, value, source, context) {
1692
1983
  // If there's no actual value, then this equates to the
@@ -1694,7 +1985,7 @@ function updateContentTarget(target, aspect, value, source, context) {
1694
1985
  if (value === null || value === undefined) {
1695
1986
  value = "";
1696
1987
  }
1697
- // If the value has a "create" method, then it's a template-like.
1988
+ // If the value has a "create" method, then it's a ContentTemplate.
1698
1989
  if (value.create) {
1699
1990
  target.textContent = "";
1700
1991
  let view = target.$fastView;
@@ -1780,46 +2071,21 @@ function updateTokenListTarget(target, aspect, value) {
1780
2071
  }
1781
2072
  }
1782
2073
  }
1783
- /**
1784
- * A binding behavior for one-time bindings.
1785
- * @public
1786
- */
1787
- class OneTimeBinding extends UpdateBinding {
1788
- /**
1789
- * Bind this behavior to the source.
1790
- * @param source - The source to bind to.
1791
- * @param context - The execution context that the binding is operating within.
1792
- * @param targets - The targets that behaviors in a view can attach to.
1793
- */
1794
- bind(source, context, targets) {
1795
- const directive = this.directive;
1796
- this.updateTarget(targets[directive.nodeId], directive.targetAspect, directive.binding(source, context), source, context);
1797
- }
1798
- }
1799
2074
  /**
1800
2075
  * A binding behavior for bindings that change.
1801
2076
  * @public
1802
2077
  */
1803
- class ChangeBinding extends UpdateBinding {
2078
+ class BindingBehavior {
1804
2079
  /**
1805
2080
  * Creates an instance of ChangeBinding.
1806
2081
  * @param directive - The directive that has the configuration for this behavior.
1807
2082
  * @param updateTarget - The function used to update the target with the latest value.
1808
2083
  */
1809
2084
  constructor(directive, updateTarget) {
1810
- super(directive, updateTarget);
1811
- this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
2085
+ this.directive = directive;
2086
+ this.updateTarget = updateTarget;
1812
2087
  this.observerProperty = `${directive.id}-o`;
1813
2088
  }
1814
- /**
1815
- * Returns the binding observer used to update the node.
1816
- * @param target - The target node.
1817
- * @returns A BindingObserver.
1818
- */
1819
- getObserver(target) {
1820
- var _a;
1821
- return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = Observable.binding(this.directive.binding, this, this.isBindingVolatile)));
1822
- }
1823
2089
  /**
1824
2090
  * Bind this behavior to the source.
1825
2091
  * @param source - The source to bind to.
@@ -1856,12 +2122,49 @@ class ChangeBinding extends UpdateBinding {
1856
2122
  const context = observer.context;
1857
2123
  this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
1858
2124
  }
2125
+ /**
2126
+ * Returns the binding observer used to update the node.
2127
+ * @param target - The target node.
2128
+ * @returns A BindingObserver.
2129
+ */
2130
+ getObserver(target) {
2131
+ var _a;
2132
+ return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = this.directive.dataBinding.createObserver(this.directive, this)));
2133
+ }
2134
+ /**
2135
+ * Creates a behavior.
2136
+ * @param targets - The targets available for behaviors to be attached to.
2137
+ */
2138
+ createBehavior(targets) {
2139
+ return this;
2140
+ }
2141
+ }
2142
+ /**
2143
+ * A special binding behavior that can bind node content.
2144
+ * @public
2145
+ */
2146
+ class ContentBehavior extends BindingBehavior {
2147
+ /**
2148
+ * Unbinds this behavior from the source.
2149
+ * @param source - The source to unbind from.
2150
+ * @param context - The execution context that the binding is operating within.
2151
+ * @param targets - The targets that behaviors in a view can attach to.
2152
+ */
2153
+ unbind(source, context, targets) {
2154
+ super.unbind(source, context, targets);
2155
+ const target = targets[this.directive.nodeId];
2156
+ const view = target.$fastView;
2157
+ if (view !== void 0 && view.isComposed) {
2158
+ view.unbind();
2159
+ view.needsBindOnly = true;
2160
+ }
2161
+ }
1859
2162
  }
1860
2163
  /**
1861
2164
  * A binding behavior for handling events.
1862
2165
  * @public
1863
2166
  */
1864
- class EventBinding {
2167
+ class EventBehavior {
1865
2168
  /**
1866
2169
  * Creates an instance of EventBinding.
1867
2170
  * @param directive - The directive that has the configuration for this behavior.
@@ -1882,7 +2185,7 @@ class EventBinding {
1882
2185
  const target = targets[directive.nodeId];
1883
2186
  target[this.sourceProperty] = source;
1884
2187
  target[this.contextProperty] = context;
1885
- target.addEventListener(directive.targetAspect, this, directive.options);
2188
+ target.addEventListener(directive.targetAspect, this, directive.dataBinding.options);
1886
2189
  }
1887
2190
  /**
1888
2191
  * Unbinds this behavior from the source.
@@ -1894,7 +2197,7 @@ class EventBinding {
1894
2197
  const directive = this.directive;
1895
2198
  const target = targets[directive.nodeId];
1896
2199
  target[this.sourceProperty] = target[this.contextProperty] = null;
1897
- target.removeEventListener(directive.targetAspect, this, directive.options);
2200
+ target.removeEventListener(directive.targetAspect, this, directive.dataBinding.options);
1898
2201
  }
1899
2202
  /**
1900
2203
  * Creates a behavior.
@@ -1909,25 +2212,13 @@ class EventBinding {
1909
2212
  handleEvent(event) {
1910
2213
  const target = event.currentTarget;
1911
2214
  ExecutionContext.setEvent(event);
1912
- const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
2215
+ const result = this.directive.dataBinding.evaluate(target[this.sourceProperty], target[this.contextProperty]);
1913
2216
  ExecutionContext.setEvent(null);
1914
2217
  if (result !== true) {
1915
2218
  event.preventDefault();
1916
2219
  }
1917
2220
  }
1918
2221
  }
1919
- /**
1920
- * The default onChange binding configuration.
1921
- * @public
1922
- */
1923
- const onChange = BindingConfig.define(BindingMode.define(ChangeBinding), {});
1924
- /**
1925
- * The default onTime binding configuration.
1926
- * @public
1927
- */
1928
- const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
1929
- once: true,
1930
- });
1931
2222
  /**
1932
2223
  * A directive that applies bindings.
1933
2224
  * @public
@@ -1935,15 +2226,15 @@ const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
1935
2226
  class HTMLBindingDirective {
1936
2227
  /**
1937
2228
  * Creates an instance of HTMLBindingDirective.
1938
- * @param binding - The binding to apply.
1939
- * @param mode - The binding mode to use when applying the binding.
1940
- * @param options - The options to configure the binding with.
2229
+ * @param dataBinding - The binding configuration to apply.
1941
2230
  */
1942
- constructor(binding, mode, options) {
1943
- this.binding = binding;
1944
- this.mode = mode;
1945
- this.options = options;
2231
+ constructor(dataBinding) {
2232
+ this.dataBinding = dataBinding;
1946
2233
  this.factory = null;
2234
+ /**
2235
+ * The unique id of the factory.
2236
+ */
2237
+ this.id = nextId();
1947
2238
  /**
1948
2239
  * The type of aspect to target.
1949
2240
  */
@@ -1963,26 +2254,78 @@ class HTMLBindingDirective {
1963
2254
  createBehavior(targets) {
1964
2255
  if (this.factory == null) {
1965
2256
  if (this.targetAspect === "innerHTML") {
1966
- this.binding = createInnerHTMLBinding(this.binding);
2257
+ this.dataBinding.evaluate = createInnerHTMLBinding(this.dataBinding.evaluate);
2258
+ }
2259
+ switch (this.aspectType) {
2260
+ case 1:
2261
+ this.factory = new BindingBehavior(this, DOM.setAttribute);
2262
+ break;
2263
+ case 2:
2264
+ this.factory = new BindingBehavior(this, DOM.setBooleanAttribute);
2265
+ break;
2266
+ case 3:
2267
+ this.factory = new BindingBehavior(this, (t, a, v) => (t[a] = v));
2268
+ break;
2269
+ case 4:
2270
+ this.factory = new ContentBehavior(this, updateContentTarget);
2271
+ break;
2272
+ case 5:
2273
+ this.factory = new BindingBehavior(this, updateTokenListTarget);
2274
+ break;
2275
+ case 6:
2276
+ this.factory = new EventBehavior(this);
2277
+ break;
2278
+ default:
2279
+ throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
1967
2280
  }
1968
- this.factory = this.mode[this.aspectType](this);
1969
2281
  }
1970
2282
  return this.factory.createBehavior(targets);
1971
2283
  }
1972
2284
  }
1973
2285
  HTMLDirective.define(HTMLBindingDirective, { aspected: true });
1974
2286
  /**
1975
- * Creates a binding directive with the specified configuration.
1976
- * @param binding - The binding expression.
1977
- * @param config - The binding configuration.
1978
- * @returns A binding directive.
2287
+ * Creates an standard binding.
2288
+ * @param binding - The binding to refresh when changed.
2289
+ * @param isVolatile - Indicates whether the binding is volatile or not.
2290
+ * @returns A binding configuration.
1979
2291
  * @public
1980
2292
  */
1981
- function bind(binding, config = onChange) {
1982
- if (!("mode" in config)) {
1983
- config = onChange(config);
1984
- }
1985
- return new HTMLBindingDirective(binding, config.mode, config.options);
2293
+ function bind(binding, isVolatile = Observable.isVolatileBinding(binding)) {
2294
+ return new OnChangeBinding(binding, isVolatile);
2295
+ }
2296
+ /**
2297
+ * Creates a one time binding
2298
+ * @param binding - The binding to refresh when signaled.
2299
+ * @returns A binding configuration.
2300
+ * @public
2301
+ */
2302
+ function oneTime(binding) {
2303
+ return new OneTimeBinding(binding);
2304
+ }
2305
+ /**
2306
+ * Creates an event listener binding.
2307
+ * @param binding - The binding to invoke when the event is raised.
2308
+ * @param options - Event listener options.
2309
+ * @returns A binding configuration.
2310
+ * @public
2311
+ */
2312
+ function listener(binding, options) {
2313
+ const config = new OnChangeBinding(binding, false);
2314
+ config.options = options;
2315
+ return config;
2316
+ }
2317
+ /**
2318
+ * Normalizes the input value into a binding.
2319
+ * @param value - The value to create the default binding for.
2320
+ * @returns A binding configuration for the provided value.
2321
+ * @public
2322
+ */
2323
+ function normalizeBinding(value) {
2324
+ return isFunction(value)
2325
+ ? bind(value)
2326
+ : value instanceof Binding
2327
+ ? value
2328
+ : oneTime(() => value);
1986
2329
  }
1987
2330
 
1988
2331
  function removeNodeSequence(firstNode, lastNode) {
@@ -2038,8 +2381,10 @@ class HTMLView {
2038
2381
  node.parentNode.insertBefore(this.fragment, node);
2039
2382
  }
2040
2383
  else {
2041
- const parentNode = node.parentNode;
2042
2384
  const end = this.lastChild;
2385
+ if (node.previousSibling === end)
2386
+ return;
2387
+ const parentNode = node.parentNode;
2043
2388
  let current = this.firstChild;
2044
2389
  let next;
2045
2390
  while (current !== end) {
@@ -2149,6 +2494,22 @@ const next = {
2149
2494
  index: 0,
2150
2495
  node: null,
2151
2496
  };
2497
+ function tryWarn(name) {
2498
+ if (!name.startsWith("fast-")) {
2499
+ FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
2500
+ }
2501
+ }
2502
+ const warningHost = new Proxy(document.createElement("div"), {
2503
+ get(target, property) {
2504
+ tryWarn(property);
2505
+ const value = Reflect.get(target, property);
2506
+ return isFunction(value) ? value.bind(target) : value;
2507
+ },
2508
+ set(target, property, value) {
2509
+ tryWarn(property);
2510
+ return Reflect.set(target, property, value);
2511
+ },
2512
+ });
2152
2513
  class CompilationContext {
2153
2514
  constructor(fragment, directives) {
2154
2515
  this.fragment = fragment;
@@ -2199,7 +2560,7 @@ class CompilationContext {
2199
2560
  const fragment = this.fragment.cloneNode(true);
2200
2561
  const targets = Object.create(this.proto);
2201
2562
  targets.r = fragment;
2202
- targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : fragment;
2563
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
2203
2564
  for (const id of this.nodeIds) {
2204
2565
  targets[id]; // trigger locator
2205
2566
  }
@@ -2216,7 +2577,7 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2216
2577
  let result = null;
2217
2578
  if (parseResult === null) {
2218
2579
  if (includeBasicValues) {
2219
- result = bind(() => attrValue, oneTime);
2580
+ result = new HTMLBindingDirective(oneTime(() => attrValue));
2220
2581
  Aspect.assign(result, attr.name);
2221
2582
  }
2222
2583
  }
@@ -2386,22 +2747,28 @@ const Compiler = {
2386
2747
  return parts[0];
2387
2748
  }
2388
2749
  let sourceAspect;
2750
+ let binding;
2751
+ let isVolatile = false;
2389
2752
  const partCount = parts.length;
2390
2753
  const finalParts = parts.map((x) => {
2391
2754
  if (isString(x)) {
2392
2755
  return () => x;
2393
2756
  }
2394
2757
  sourceAspect = x.sourceAspect || sourceAspect;
2395
- return x.binding;
2758
+ binding = x.dataBinding || binding;
2759
+ isVolatile = isVolatile || x.dataBinding.isVolatile;
2760
+ return x.dataBinding.evaluate;
2396
2761
  });
2397
- const binding = (scope, context) => {
2762
+ const expression = (scope, context) => {
2398
2763
  let output = "";
2399
2764
  for (let i = 0; i < partCount; ++i) {
2400
2765
  output += finalParts[i](scope, context);
2401
2766
  }
2402
2767
  return output;
2403
2768
  };
2404
- const directive = bind(binding);
2769
+ binding.evaluate = expression;
2770
+ binding.isVolatile = isVolatile;
2771
+ const directive = new HTMLBindingDirective(binding);
2405
2772
  Aspect.assign(directive, sourceAspect);
2406
2773
  return directive;
2407
2774
  },
@@ -2481,12 +2848,12 @@ function html(strings, ...values) {
2481
2848
  let definition;
2482
2849
  html += currentString;
2483
2850
  if (isFunction(currentValue)) {
2484
- html += createAspectedHTML(bind(currentValue), currentString, add);
2851
+ html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
2485
2852
  }
2486
2853
  else if (isString(currentValue)) {
2487
2854
  const match = lastAttributeNameRegex.exec(currentString);
2488
2855
  if (match !== null) {
2489
- const directive = bind(() => currentValue, oneTime);
2856
+ const directive = new HTMLBindingDirective(oneTime(() => currentValue));
2490
2857
  Aspect.assign(directive, match[2]);
2491
2858
  html += directive.createHTML(add);
2492
2859
  }
@@ -2494,8 +2861,11 @@ function html(strings, ...values) {
2494
2861
  html += currentValue;
2495
2862
  }
2496
2863
  }
2864
+ else if (currentValue instanceof Binding) {
2865
+ html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
2866
+ }
2497
2867
  else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
2498
- html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
2868
+ html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
2499
2869
  }
2500
2870
  else {
2501
2871
  if (definition.aspected) {
@@ -2540,16 +2910,17 @@ const ref = (propertyName) => new RefDirective(propertyName);
2540
2910
 
2541
2911
  /**
2542
2912
  * A directive that enables basic conditional rendering in a template.
2543
- * @param binding - The condition to test for rendering.
2913
+ * @param condition - The condition to test for rendering.
2544
2914
  * @param templateOrTemplateBinding - The template or a binding that gets
2545
2915
  * the template to render when the condition is true.
2546
2916
  * @public
2547
2917
  */
2548
- function when(binding, templateOrTemplateBinding) {
2549
- const getTemplate = isFunction(templateOrTemplateBinding)
2918
+ function when(condition, templateOrTemplateBinding) {
2919
+ const dataBinding = isFunction(condition) ? condition : () => condition;
2920
+ const templateBinding = isFunction(templateOrTemplateBinding)
2550
2921
  ? templateOrTemplateBinding
2551
2922
  : () => templateOrTemplateBinding;
2552
- return (source, context) => binding(source, context) ? getTemplate(source, context) : null;
2923
+ return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
2553
2924
  }
2554
2925
 
2555
2926
  const defaultRepeatOptions = Object.freeze({
@@ -2570,17 +2941,15 @@ class RepeatBehavior {
2570
2941
  /**
2571
2942
  * Creates an instance of RepeatBehavior.
2572
2943
  * @param location - The location in the DOM to render the repeat.
2573
- * @param itemsBinding - The array to render.
2944
+ * @param dataBinding - The array to render.
2574
2945
  * @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
2575
2946
  * @param templateBinding - The template to render for each item.
2576
2947
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
2577
2948
  * @param options - Options used to turn on special repeat features.
2578
2949
  */
2579
- constructor(location, itemsBinding, isItemsBindingVolatile, templateBinding, isTemplateBindingVolatile, options) {
2950
+ constructor(directive, location) {
2951
+ this.directive = directive;
2580
2952
  this.location = location;
2581
- this.itemsBinding = itemsBinding;
2582
- this.templateBinding = templateBinding;
2583
- this.options = options;
2584
2953
  this.source = null;
2585
2954
  this.views = [];
2586
2955
  this.items = null;
@@ -2588,9 +2957,9 @@ class RepeatBehavior {
2588
2957
  this.context = void 0;
2589
2958
  this.childContext = void 0;
2590
2959
  this.bindView = bindWithoutPositioning;
2591
- this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
2592
- this.templateBindingObserver = Observable.binding(templateBinding, this, isTemplateBindingVolatile);
2593
- if (options.positioning) {
2960
+ this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
2961
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
2962
+ if (directive.options.positioning) {
2594
2963
  this.bindView = bindWithPositioning;
2595
2964
  }
2596
2965
  }
@@ -2628,15 +2997,18 @@ class RepeatBehavior {
2628
2997
  * @param args - The details about what was changed.
2629
2998
  */
2630
2999
  handleChange(source, args) {
2631
- if (source === this.itemsBinding) {
3000
+ if (args === this.itemsBindingObserver) {
2632
3001
  this.items = this.itemsBindingObserver.observe(this.source, this.context);
2633
3002
  this.observeItems();
2634
3003
  this.refreshAllViews();
2635
3004
  }
2636
- else if (source === this.templateBinding) {
3005
+ else if (args === this.templateBindingObserver) {
2637
3006
  this.template = this.templateBindingObserver.observe(this.source, this.context);
2638
3007
  this.refreshAllViews(true);
2639
3008
  }
3009
+ else if (!args[0]) {
3010
+ return;
3011
+ }
2640
3012
  else if (args[0].reset) {
2641
3013
  this.refreshAllViews();
2642
3014
  }
@@ -2662,36 +3034,51 @@ class RepeatBehavior {
2662
3034
  updateViews(splices) {
2663
3035
  const views = this.views;
2664
3036
  const childContext = this.childContext;
2665
- const totalRemoved = [];
2666
3037
  const bindView = this.bindView;
2667
- let removeDelta = 0;
2668
- for (let i = 0, ii = splices.length; i < ii; ++i) {
2669
- const splice = splices[i];
2670
- const removed = splice.removed;
2671
- totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
2672
- removeDelta -= splice.addedCount;
2673
- }
2674
3038
  const items = this.items;
2675
3039
  const template = this.template;
3040
+ const recycle = this.directive.options.recycle;
3041
+ const leftoverViews = [];
3042
+ let leftoverIndex = 0;
3043
+ let availableViews = 0;
2676
3044
  for (let i = 0, ii = splices.length; i < ii; ++i) {
2677
3045
  const splice = splices[i];
3046
+ const removed = splice.removed;
3047
+ let removeIndex = 0;
2678
3048
  let addIndex = splice.index;
2679
3049
  const end = addIndex + splice.addedCount;
3050
+ const removedViews = views.splice(splice.index, removed.length);
3051
+ availableViews = leftoverViews.length + removedViews.length;
2680
3052
  for (; addIndex < end; ++addIndex) {
2681
3053
  const neighbor = views[addIndex];
2682
3054
  const location = neighbor ? neighbor.firstChild : this.location;
2683
- const view = this.options.recycle && totalRemoved.length > 0
2684
- ? totalRemoved.shift()
2685
- : template.create();
3055
+ let view;
3056
+ if (recycle && availableViews > 0) {
3057
+ if (removeIndex <= availableViews && removedViews.length > 0) {
3058
+ view = removedViews[removeIndex];
3059
+ removeIndex++;
3060
+ }
3061
+ else {
3062
+ view = leftoverViews[leftoverIndex];
3063
+ leftoverIndex++;
3064
+ }
3065
+ availableViews--;
3066
+ }
3067
+ else {
3068
+ view = template.create();
3069
+ }
2686
3070
  views.splice(addIndex, 0, view);
2687
3071
  bindView(view, items, addIndex, childContext);
2688
3072
  view.insertBefore(location);
2689
3073
  }
3074
+ if (removedViews[removeIndex]) {
3075
+ leftoverViews.push(...removedViews.slice(removeIndex));
3076
+ }
2690
3077
  }
2691
- for (let i = 0, ii = totalRemoved.length; i < ii; ++i) {
2692
- totalRemoved[i].dispose();
3078
+ for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
3079
+ leftoverViews[i].dispose();
2693
3080
  }
2694
- if (this.options.positioning) {
3081
+ if (this.directive.options.positioning) {
2695
3082
  for (let i = 0, ii = views.length; i < ii; ++i) {
2696
3083
  views[i].context.updatePosition(i, ii);
2697
3084
  }
@@ -2706,7 +3093,7 @@ class RepeatBehavior {
2706
3093
  let itemsLength = items.length;
2707
3094
  let views = this.views;
2708
3095
  let viewsLength = views.length;
2709
- if (itemsLength === 0 || templateChanged || !this.options.recycle) {
3096
+ if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
2710
3097
  // all views need to be removed
2711
3098
  HTMLView.disposeContiguousBatch(views);
2712
3099
  viewsLength = 0;
@@ -2756,17 +3143,19 @@ class RepeatBehavior {
2756
3143
  class RepeatDirective {
2757
3144
  /**
2758
3145
  * Creates an instance of RepeatDirective.
2759
- * @param itemsBinding - The binding that provides the array to render.
3146
+ * @param dataBinding - The binding that provides the array to render.
2760
3147
  * @param templateBinding - The template binding used to obtain a template to render for each item in the array.
2761
3148
  * @param options - Options used to turn on special repeat features.
2762
3149
  */
2763
- constructor(itemsBinding, templateBinding, options) {
2764
- this.itemsBinding = itemsBinding;
3150
+ constructor(dataBinding, templateBinding, options) {
3151
+ this.dataBinding = dataBinding;
2765
3152
  this.templateBinding = templateBinding;
2766
3153
  this.options = options;
3154
+ /**
3155
+ * The unique id of the factory.
3156
+ */
3157
+ this.id = nextId();
2767
3158
  ArrayObserver.enable();
2768
- this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
2769
- this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
2770
3159
  }
2771
3160
  /**
2772
3161
  * Creates a placeholder string based on the directive's index within the template.
@@ -2780,23 +3169,22 @@ class RepeatDirective {
2780
3169
  * @param target - The node instance to create the behavior for.
2781
3170
  */
2782
3171
  createBehavior(targets) {
2783
- return new RepeatBehavior(targets[this.nodeId], this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
3172
+ return new RepeatBehavior(this, targets[this.nodeId]);
2784
3173
  }
2785
3174
  }
2786
3175
  HTMLDirective.define(RepeatDirective);
2787
3176
  /**
2788
3177
  * A directive that enables list rendering.
2789
- * @param itemsBinding - The array to render.
2790
- * @param templateOrTemplateBinding - The template or a template binding used obtain a template
3178
+ * @param items - The array to render.
3179
+ * @param template - The template or a template binding used obtain a template
2791
3180
  * to render for each item in the array.
2792
3181
  * @param options - Options used to turn on special repeat features.
2793
3182
  * @public
2794
3183
  */
2795
- function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
2796
- const templateBinding = isFunction(templateOrTemplateBinding)
2797
- ? templateOrTemplateBinding
2798
- : () => templateOrTemplateBinding;
2799
- return new RepeatDirective(itemsBinding, templateBinding, options);
3184
+ function repeat(items, template, options = defaultRepeatOptions) {
3185
+ const dataBinding = normalizeBinding(items);
3186
+ const templateBinding = normalizeBinding(template);
3187
+ return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
2800
3188
  }
2801
3189
 
2802
3190
  const selectElements = (value) => value.nodeType === 1;
@@ -3181,19 +3569,15 @@ const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */
3181
3569
  * @public
3182
3570
  */
3183
3571
  class FASTElementDefinition {
3184
- /**
3185
- * Creates an instance of FASTElementDefinition.
3186
- * @param type - The type this definition is being created for.
3187
- * @param nameOrConfig - The name of the element to define or a config object
3188
- * that describes the element to define.
3189
- */
3190
3572
  constructor(type, nameOrConfig = type.definition) {
3573
+ this.platformDefined = false;
3191
3574
  if (isString(nameOrConfig)) {
3192
3575
  nameOrConfig = { name: nameOrConfig };
3193
3576
  }
3194
3577
  this.type = type;
3195
3578
  this.name = nameOrConfig.name;
3196
3579
  this.template = nameOrConfig.template;
3580
+ const proto = type.prototype;
3197
3581
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3198
3582
  const observedAttributes = new Array(attributes.length);
3199
3583
  const propertyLookup = {};
@@ -3203,9 +3587,13 @@ class FASTElementDefinition {
3203
3587
  observedAttributes[i] = current.attribute;
3204
3588
  propertyLookup[current.name] = current;
3205
3589
  attributeLookup[current.attribute] = current;
3590
+ Observable.defineProperty(proto, current);
3206
3591
  }
3592
+ Reflect.defineProperty(type, "observedAttributes", {
3593
+ value: observedAttributes,
3594
+ enumerable: true,
3595
+ });
3207
3596
  this.attributes = attributes;
3208
- this.observedAttributes = observedAttributes;
3209
3597
  this.propertyLookup = propertyLookup;
3210
3598
  this.attributeLookup = attributeLookup;
3211
3599
  this.shadowOptions =
@@ -3218,20 +3606,14 @@ class FASTElementDefinition {
3218
3606
  nameOrConfig.elementOptions === void 0
3219
3607
  ? defaultElementOptions
3220
3608
  : Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
3221
- this.styles =
3222
- nameOrConfig.styles === void 0
3223
- ? void 0
3224
- : Array.isArray(nameOrConfig.styles)
3225
- ? new ElementStyles(nameOrConfig.styles)
3226
- : nameOrConfig.styles instanceof ElementStyles
3227
- ? nameOrConfig.styles
3228
- : new ElementStyles([nameOrConfig.styles]);
3609
+ this.styles = ElementStyles.normalize(nameOrConfig.styles);
3610
+ fastElementRegistry.register(this);
3229
3611
  }
3230
3612
  /**
3231
3613
  * Indicates if this element has been defined in at least one registry.
3232
3614
  */
3233
3615
  get isDefined() {
3234
- return !!fastElementRegistry.getByType(this.type);
3616
+ return this.platformDefined;
3235
3617
  }
3236
3618
  /**
3237
3619
  * Defines a custom element based on this definition.
@@ -3241,22 +3623,26 @@ class FASTElementDefinition {
3241
3623
  */
3242
3624
  define(registry = customElements) {
3243
3625
  const type = this.type;
3244
- if (fastElementRegistry.register(this)) {
3245
- const attributes = this.attributes;
3246
- const proto = type.prototype;
3247
- for (let i = 0, ii = attributes.length; i < ii; ++i) {
3248
- Observable.defineProperty(proto, attributes[i]);
3249
- }
3250
- Reflect.defineProperty(type, "observedAttributes", {
3251
- value: this.observedAttributes,
3252
- enumerable: true,
3253
- });
3254
- }
3255
3626
  if (!registry.get(this.name)) {
3627
+ this.platformDefined = true;
3256
3628
  registry.define(this.name, type, this.elementOptions);
3257
3629
  }
3258
3630
  return this;
3259
3631
  }
3632
+ /**
3633
+ * Creates an instance of FASTElementDefinition.
3634
+ * @param type - The type this definition is being created for.
3635
+ * @param nameOrDef - The name of the element to define or a config object
3636
+ * that describes the element to define.
3637
+ */
3638
+ static compose(type, nameOrDef) {
3639
+ const found = fastElementRegistry.getByType(type);
3640
+ if (found) {
3641
+ return new FASTElementDefinition(class extends type {
3642
+ }, nameOrDef);
3643
+ }
3644
+ return new FASTElementDefinition(type, nameOrDef);
3645
+ }
3260
3646
  }
3261
3647
  /**
3262
3648
  * Gets the element definition associated with the specified type.
@@ -3674,6 +4060,21 @@ function createFASTElement(BaseType) {
3674
4060
  }
3675
4061
  };
3676
4062
  }
4063
+ function compose(type, nameOrDef) {
4064
+ if (isFunction(type)) {
4065
+ return FASTElementDefinition.compose(type, nameOrDef);
4066
+ }
4067
+ return FASTElementDefinition.compose(this, type);
4068
+ }
4069
+ function define(type, nameOrDef) {
4070
+ if (isFunction(type)) {
4071
+ return FASTElementDefinition.compose(type, nameOrDef).define().type;
4072
+ }
4073
+ return FASTElementDefinition.compose(this, type).define().type;
4074
+ }
4075
+ function from(BaseType) {
4076
+ return createFASTElement(BaseType);
4077
+ }
3677
4078
  /**
3678
4079
  * A minimal base class for FASTElements that also provides
3679
4080
  * static helpers for working with FASTElements.
@@ -3685,26 +4086,19 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3685
4086
  * provided base type.
3686
4087
  * @param BaseType - The base element type to inherit from.
3687
4088
  */
3688
- from(BaseType) {
3689
- return createFASTElement(BaseType);
3690
- },
4089
+ from,
3691
4090
  /**
3692
4091
  * Defines a platform custom element based on the provided type and definition.
3693
4092
  * @param type - The custom element type to define.
3694
4093
  * @param nameOrDef - The name of the element to define or a definition object
3695
4094
  * that describes the element to define.
3696
4095
  */
3697
- define(type, nameOrDef) {
3698
- return this.metadata(type, nameOrDef).define().type;
3699
- },
4096
+ define,
3700
4097
  /**
3701
4098
  * Defines metadata for a FASTElement which can be used to later define the element.
3702
- * IMPORTANT: This API will be renamed to "compose" in a future beta.
3703
4099
  * @public
3704
4100
  */
3705
- metadata(type, nameOrDef) {
3706
- return new FASTElementDefinition(type, nameOrDef);
3707
- },
4101
+ compose,
3708
4102
  });
3709
4103
  /**
3710
4104
  * Decorator: Defines a platform custom element based on `FASTElement`.
@@ -3715,8 +4109,8 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
3715
4109
  function customElement(nameOrDef) {
3716
4110
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
3717
4111
  return function (type) {
3718
- FASTElement.define(type, nameOrDef);
4112
+ define(type, nameOrDef);
3719
4113
  };
3720
4114
  }
3721
4115
 
3722
- 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 };
4116
+ export { AdoptedStyleSheetsStrategy, ArrayObserver, Aspect, AttributeDefinition, Binding, BindingBehavior, CSSDirective, ChildrenDirective, Compiler, ContentBehavior, Controller, DOM, ElementStyles, EventBehavior, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewTemplate, attr, bind, booleanConverter, children, createTypeRegistry, css, cssDirective, cssPartial, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };