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

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 (113) hide show
  1. package/CHANGELOG.json +488 -0
  2. package/CHANGELOG.md +180 -1
  3. package/dist/dts/components/attributes.d.ts +15 -0
  4. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +74 -28
  5. package/dist/dts/components/fast-definitions.d.ts +41 -9
  6. package/dist/dts/components/fast-element.d.ts +14 -26
  7. package/dist/dts/components/hydration.d.ts +14 -0
  8. package/dist/{esm/observation/behavior.js → dts/components/install-hydration.d.ts} +0 -0
  9. package/dist/dts/context.d.ts +1 -1
  10. package/dist/dts/di/di.d.ts +894 -0
  11. package/dist/dts/dom-policy.d.ts +68 -0
  12. package/dist/dts/dom.d.ts +100 -0
  13. package/dist/dts/index.d.ts +5 -4
  14. package/dist/dts/index.rollup.d.ts +0 -1
  15. package/dist/dts/index.rollup.debug.d.ts +0 -1
  16. package/dist/dts/interfaces.d.ts +60 -79
  17. package/dist/dts/observation/observable.d.ts +99 -54
  18. package/dist/dts/pending-task.d.ts +20 -0
  19. package/dist/dts/platform.d.ts +7 -0
  20. package/dist/dts/polyfills.d.ts +0 -8
  21. package/dist/dts/state/exports.d.ts +3 -0
  22. package/dist/dts/state/reactive.d.ts +8 -0
  23. package/dist/dts/state/state.d.ts +141 -0
  24. package/dist/dts/state/visitor.d.ts +6 -0
  25. package/dist/dts/state/watch.d.ts +10 -0
  26. package/dist/dts/styles/css-directive.d.ts +2 -2
  27. package/dist/dts/styles/css.d.ts +0 -5
  28. package/dist/dts/styles/element-styles.d.ts +10 -17
  29. package/dist/dts/styles/host.d.ts +68 -0
  30. package/dist/dts/styles/style-strategy.d.ts +42 -0
  31. package/dist/dts/templating/binding-signal.d.ts +12 -27
  32. package/dist/dts/templating/binding-two-way.d.ts +22 -37
  33. package/dist/dts/templating/binding.d.ts +76 -208
  34. package/dist/dts/templating/children.d.ts +1 -1
  35. package/dist/dts/templating/compiler.d.ts +11 -13
  36. package/dist/dts/templating/html-directive.d.ts +91 -97
  37. package/dist/dts/templating/node-observation.d.ts +15 -6
  38. package/dist/dts/templating/ref.d.ts +7 -11
  39. package/dist/dts/templating/render.d.ts +296 -0
  40. package/dist/dts/templating/repeat.d.ts +23 -34
  41. package/dist/dts/templating/slotted.d.ts +1 -1
  42. package/dist/dts/templating/template.d.ts +92 -14
  43. package/dist/dts/templating/view.d.ts +81 -11
  44. package/dist/dts/templating/when.d.ts +3 -3
  45. package/dist/dts/testing/exports.d.ts +3 -0
  46. package/dist/dts/testing/fakes.d.ts +14 -0
  47. package/dist/dts/testing/fixture.d.ts +84 -0
  48. package/dist/dts/testing/timeout.d.ts +7 -0
  49. package/dist/dts/utilities.d.ts +53 -18
  50. package/dist/esm/components/attributes.js +28 -5
  51. package/dist/esm/components/{controller.js → element-controller.js} +239 -137
  52. package/dist/esm/components/fast-definitions.js +38 -30
  53. package/dist/esm/components/fast-element.js +27 -16
  54. package/dist/esm/components/hydration.js +35 -0
  55. package/dist/esm/components/install-hydration.js +2 -0
  56. package/dist/esm/context.js +5 -1
  57. package/dist/esm/debug.js +40 -5
  58. package/dist/esm/di/di.js +1430 -0
  59. package/dist/esm/dom-policy.js +337 -0
  60. package/dist/esm/dom.js +101 -0
  61. package/dist/esm/index.js +4 -2
  62. package/dist/esm/index.rollup.debug.js +3 -1
  63. package/dist/esm/index.rollup.js +3 -1
  64. package/dist/esm/interfaces.js +52 -0
  65. package/dist/esm/observation/arrays.js +303 -2
  66. package/dist/esm/observation/observable.js +88 -142
  67. package/dist/esm/observation/update-queue.js +2 -2
  68. package/dist/esm/pending-task.js +16 -0
  69. package/dist/esm/platform.js +27 -2
  70. package/dist/esm/polyfills.js +3 -61
  71. package/dist/esm/state/exports.js +3 -0
  72. package/dist/esm/state/reactive.js +34 -0
  73. package/dist/esm/state/state.js +148 -0
  74. package/dist/esm/state/visitor.js +28 -0
  75. package/dist/esm/state/watch.js +36 -0
  76. package/dist/esm/styles/css.js +4 -9
  77. package/dist/esm/styles/element-styles.js +14 -33
  78. package/dist/esm/styles/host.js +1 -0
  79. package/dist/esm/styles/style-strategy.js +1 -0
  80. package/dist/esm/templating/binding-signal.js +67 -62
  81. package/dist/esm/templating/binding-two-way.js +72 -39
  82. package/dist/esm/templating/binding.js +142 -286
  83. package/dist/esm/templating/children.js +8 -4
  84. package/dist/esm/templating/compiler.js +59 -43
  85. package/dist/esm/templating/html-directive.js +56 -75
  86. package/dist/esm/templating/node-observation.js +20 -13
  87. package/dist/esm/templating/ref.js +4 -12
  88. package/dist/esm/templating/render.js +402 -0
  89. package/dist/esm/templating/repeat.js +88 -75
  90. package/dist/esm/templating/template.js +132 -60
  91. package/dist/esm/templating/view.js +113 -29
  92. package/dist/esm/templating/when.js +5 -4
  93. package/dist/esm/testing/exports.js +3 -0
  94. package/dist/esm/testing/fakes.js +107 -0
  95. package/dist/esm/testing/fixture.js +86 -0
  96. package/dist/esm/testing/timeout.js +24 -0
  97. package/dist/esm/utilities.js +95 -95
  98. package/dist/fast-element.api.json +9487 -8326
  99. package/dist/fast-element.d.ts +847 -644
  100. package/dist/fast-element.debug.js +1993 -1166
  101. package/dist/fast-element.debug.min.js +1 -1
  102. package/dist/fast-element.js +1903 -1111
  103. package/dist/fast-element.min.js +1 -1
  104. package/dist/fast-element.untrimmed.d.ts +911 -701
  105. package/docs/api-report.md +329 -252
  106. package/package.json +38 -16
  107. package/dist/dts/hooks.d.ts +0 -20
  108. package/dist/dts/observation/behavior.d.ts +0 -19
  109. package/dist/dts/observation/splice-strategies.d.ts +0 -13
  110. package/dist/dts/templating/dom.d.ts +0 -41
  111. package/dist/esm/hooks.js +0 -32
  112. package/dist/esm/observation/splice-strategies.js +0 -400
  113. package/dist/esm/templating/dom.js +0 -49
@@ -1,19 +1,25 @@
1
- import { isFunction } from "../interfaces.js";
2
- import { Observable, } from "../observation/observable.js";
1
+ import { Observable } from "../observation/observable.js";
3
2
  import { emptyArray } from "../platform.js";
4
3
  import { ArrayObserver } from "../observation/arrays.js";
5
4
  import { Markup } from "./markup.js";
6
5
  import { HTMLDirective, } from "./html-directive.js";
7
6
  import { HTMLView } from "./view.js";
7
+ import { normalizeBinding } from "./binding.js";
8
8
  const defaultRepeatOptions = Object.freeze({
9
9
  positioning: false,
10
10
  recycle: true,
11
11
  });
12
- function bindWithoutPositioning(view, items, index, context) {
13
- view.bind(items[index], context);
12
+ function bindWithoutPositioning(view, items, index, controller) {
13
+ view.context.parent = controller.source;
14
+ view.context.parentContext = controller.context;
15
+ view.bind(items[index]);
14
16
  }
15
- function bindWithPositioning(view, items, index, context) {
16
- view.bind(items[index], context.createItemContext(index, items.length));
17
+ function bindWithPositioning(view, items, index, controller) {
18
+ view.context.parent = controller.source;
19
+ view.context.parentContext = controller.context;
20
+ view.context.length = items.length;
21
+ view.context.index = index;
22
+ view.bind(items[index]);
17
23
  }
18
24
  /**
19
25
  * A behavior that renders a template for each item in an array.
@@ -23,57 +29,46 @@ export class RepeatBehavior {
23
29
  /**
24
30
  * Creates an instance of RepeatBehavior.
25
31
  * @param location - The location in the DOM to render the repeat.
26
- * @param itemsBinding - The array to render.
32
+ * @param dataBinding - The array to render.
27
33
  * @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
28
34
  * @param templateBinding - The template to render for each item.
29
35
  * @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
30
36
  * @param options - Options used to turn on special repeat features.
31
37
  */
32
- constructor(location, itemsBinding, isItemsBindingVolatile, templateBinding, isTemplateBindingVolatile, options) {
33
- this.location = location;
34
- this.itemsBinding = itemsBinding;
35
- this.templateBinding = templateBinding;
36
- this.options = options;
37
- this.source = null;
38
- this.views = [];
38
+ constructor(directive) {
39
+ this.directive = directive;
39
40
  this.items = null;
40
41
  this.itemsObserver = null;
41
- this.context = void 0;
42
- this.childContext = void 0;
43
42
  this.bindView = bindWithoutPositioning;
44
- this.itemsBindingObserver = Observable.binding(itemsBinding, this, isItemsBindingVolatile);
45
- this.templateBindingObserver = Observable.binding(templateBinding, this, isTemplateBindingVolatile);
46
- if (options.positioning) {
43
+ /** @internal */
44
+ this.views = [];
45
+ this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
46
+ this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
47
+ if (directive.options.positioning) {
47
48
  this.bindView = bindWithPositioning;
48
49
  }
49
50
  }
50
51
  /**
51
- * Bind this behavior to the source.
52
- * @param source - The source to bind to.
53
- * @param context - The execution context that the binding is operating within.
52
+ * Bind this behavior.
53
+ * @param controller - The view controller that manages the lifecycle of this behavior.
54
54
  */
55
- bind(source, context) {
56
- this.source = source;
57
- this.context = context;
58
- this.childContext = context.createChildContext(source);
59
- this.items = this.itemsBindingObserver.observe(source, this.context);
60
- this.template = this.templateBindingObserver.observe(source, this.context);
55
+ bind(controller) {
56
+ this.location = controller.targets[this.directive.targetNodeId];
57
+ this.controller = controller;
58
+ this.items = this.itemsBindingObserver.bind(controller);
59
+ this.template = this.templateBindingObserver.bind(controller);
61
60
  this.observeItems(true);
62
61
  this.refreshAllViews();
62
+ controller.onUnbind(this);
63
63
  }
64
64
  /**
65
- * Unbinds this behavior from the source.
66
- * @param source - The source to unbind from.
65
+ * Unbinds this behavior.
67
66
  */
68
67
  unbind() {
69
- this.source = null;
70
- this.items = null;
71
68
  if (this.itemsObserver !== null) {
72
69
  this.itemsObserver.unsubscribe(this);
73
70
  }
74
71
  this.unbindAllViews();
75
- this.itemsBindingObserver.dispose();
76
- this.templateBindingObserver.dispose();
77
72
  }
78
73
  /**
79
74
  * Handles changes in the array, its items, and the repeat template.
@@ -81,15 +76,18 @@ export class RepeatBehavior {
81
76
  * @param args - The details about what was changed.
82
77
  */
83
78
  handleChange(source, args) {
84
- if (source === this.itemsBinding) {
85
- this.items = this.itemsBindingObserver.observe(this.source, this.context);
79
+ if (args === this.itemsBindingObserver) {
80
+ this.items = this.itemsBindingObserver.bind(this.controller);
86
81
  this.observeItems();
87
82
  this.refreshAllViews();
88
83
  }
89
- else if (source === this.templateBinding) {
90
- this.template = this.templateBindingObserver.observe(this.source, this.context);
84
+ else if (args === this.templateBindingObserver) {
85
+ this.template = this.templateBindingObserver.bind(this.controller);
91
86
  this.refreshAllViews(true);
92
87
  }
88
+ else if (!args[0]) {
89
+ return;
90
+ }
93
91
  else if (args[0].reset) {
94
92
  this.refreshAllViews();
95
93
  }
@@ -114,39 +112,57 @@ export class RepeatBehavior {
114
112
  }
115
113
  updateViews(splices) {
116
114
  const views = this.views;
117
- const childContext = this.childContext;
118
- const totalRemoved = [];
119
115
  const bindView = this.bindView;
120
- let removeDelta = 0;
121
- for (let i = 0, ii = splices.length; i < ii; ++i) {
122
- const splice = splices[i];
123
- const removed = splice.removed;
124
- totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
125
- removeDelta -= splice.addedCount;
126
- }
127
116
  const items = this.items;
128
117
  const template = this.template;
118
+ const controller = this.controller;
119
+ const recycle = this.directive.options.recycle;
120
+ const leftoverViews = [];
121
+ let leftoverIndex = 0;
122
+ let availableViews = 0;
129
123
  for (let i = 0, ii = splices.length; i < ii; ++i) {
130
124
  const splice = splices[i];
125
+ const removed = splice.removed;
126
+ let removeIndex = 0;
131
127
  let addIndex = splice.index;
132
128
  const end = addIndex + splice.addedCount;
129
+ const removedViews = views.splice(splice.index, removed.length);
130
+ const totalAvailableViews = (availableViews =
131
+ leftoverViews.length + removedViews.length);
133
132
  for (; addIndex < end; ++addIndex) {
134
133
  const neighbor = views[addIndex];
135
134
  const location = neighbor ? neighbor.firstChild : this.location;
136
- const view = this.options.recycle && totalRemoved.length > 0
137
- ? totalRemoved.shift()
138
- : template.create();
135
+ let view;
136
+ if (recycle && availableViews > 0) {
137
+ if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
138
+ view = removedViews[removeIndex];
139
+ removeIndex++;
140
+ }
141
+ else {
142
+ view = leftoverViews[leftoverIndex];
143
+ leftoverIndex++;
144
+ }
145
+ availableViews--;
146
+ }
147
+ else {
148
+ view = template.create();
149
+ }
139
150
  views.splice(addIndex, 0, view);
140
- bindView(view, items, addIndex, childContext);
151
+ bindView(view, items, addIndex, controller);
141
152
  view.insertBefore(location);
142
153
  }
154
+ if (removedViews[removeIndex]) {
155
+ leftoverViews.push(...removedViews.slice(removeIndex));
156
+ }
143
157
  }
144
- for (let i = 0, ii = totalRemoved.length; i < ii; ++i) {
145
- totalRemoved[i].dispose();
158
+ for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
159
+ leftoverViews[i].dispose();
146
160
  }
147
- if (this.options.positioning) {
148
- for (let i = 0, ii = views.length; i < ii; ++i) {
149
- views[i].context.updatePosition(i, ii);
161
+ if (this.directive.options.positioning) {
162
+ for (let i = 0, viewsLength = views.length; i < viewsLength; ++i) {
163
+ const context = views[i].context;
164
+ context.length = viewsLength;
165
+ context.index = i;
150
166
  }
151
167
  }
152
168
  }
@@ -155,11 +171,11 @@ export class RepeatBehavior {
155
171
  const template = this.template;
156
172
  const location = this.location;
157
173
  const bindView = this.bindView;
158
- const childContext = this.childContext;
174
+ const controller = this.controller;
159
175
  let itemsLength = items.length;
160
176
  let views = this.views;
161
177
  let viewsLength = views.length;
162
- if (itemsLength === 0 || templateChanged || !this.options.recycle) {
178
+ if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
163
179
  // all views need to be removed
164
180
  HTMLView.disposeContiguousBatch(views);
165
181
  viewsLength = 0;
@@ -169,7 +185,7 @@ export class RepeatBehavior {
169
185
  this.views = views = new Array(itemsLength);
170
186
  for (let i = 0; i < itemsLength; ++i) {
171
187
  const view = template.create();
172
- bindView(view, items, i, childContext);
188
+ bindView(view, items, i, controller);
173
189
  views[i] = view;
174
190
  view.insertBefore(location);
175
191
  }
@@ -180,11 +196,11 @@ export class RepeatBehavior {
180
196
  for (; i < itemsLength; ++i) {
181
197
  if (i < viewsLength) {
182
198
  const view = views[i];
183
- bindView(view, items, i, childContext);
199
+ bindView(view, items, i, controller);
184
200
  }
185
201
  else {
186
202
  const view = template.create();
187
- bindView(view, items, i, childContext);
203
+ bindView(view, items, i, controller);
188
204
  views.push(view);
189
205
  view.insertBefore(location);
190
206
  }
@@ -209,17 +225,15 @@ export class RepeatBehavior {
209
225
  export class RepeatDirective {
210
226
  /**
211
227
  * Creates an instance of RepeatDirective.
212
- * @param itemsBinding - The binding that provides the array to render.
228
+ * @param dataBinding - The binding that provides the array to render.
213
229
  * @param templateBinding - The template binding used to obtain a template to render for each item in the array.
214
230
  * @param options - Options used to turn on special repeat features.
215
231
  */
216
- constructor(itemsBinding, templateBinding, options) {
217
- this.itemsBinding = itemsBinding;
232
+ constructor(dataBinding, templateBinding, options) {
233
+ this.dataBinding = dataBinding;
218
234
  this.templateBinding = templateBinding;
219
235
  this.options = options;
220
236
  ArrayObserver.enable();
221
- this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
222
- this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
223
237
  }
224
238
  /**
225
239
  * Creates a placeholder string based on the directive's index within the template.
@@ -232,22 +246,21 @@ export class RepeatDirective {
232
246
  * Creates a behavior for the provided target node.
233
247
  * @param target - The node instance to create the behavior for.
234
248
  */
235
- createBehavior(targets) {
236
- return new RepeatBehavior(targets[this.nodeId], this.itemsBinding, this.isItemsBindingVolatile, this.templateBinding, this.isTemplateBindingVolatile, this.options);
249
+ createBehavior() {
250
+ return new RepeatBehavior(this);
237
251
  }
238
252
  }
239
253
  HTMLDirective.define(RepeatDirective);
240
254
  /**
241
255
  * A directive that enables list rendering.
242
- * @param itemsBinding - The array to render.
243
- * @param templateOrTemplateBinding - The template or a template binding used obtain a template
256
+ * @param items - The array to render.
257
+ * @param template - The template or a template binding used obtain a template
244
258
  * to render for each item in the array.
245
259
  * @param options - Options used to turn on special repeat features.
246
260
  * @public
247
261
  */
248
- export function repeat(itemsBinding, templateOrTemplateBinding, options = defaultRepeatOptions) {
249
- const templateBinding = isFunction(templateOrTemplateBinding)
250
- ? templateOrTemplateBinding
251
- : () => templateOrTemplateBinding;
252
- return new RepeatDirective(itemsBinding, templateBinding, options);
262
+ export function repeat(items, template, options = defaultRepeatOptions) {
263
+ const dataBinding = normalizeBinding(items);
264
+ const templateBinding = normalizeBinding(template);
265
+ return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
253
266
  }
@@ -1,9 +1,53 @@
1
- import { isFunction, isString } from "../interfaces.js";
2
- import { ExecutionContext } from "../observation/observable.js";
3
- import { bind, oneTime } from "./binding.js";
1
+ import { isFunction, isString, noop } from "../interfaces.js";
2
+ import { FAST } from "../platform.js";
3
+ import { bind, HTMLBindingDirective, oneTime } from "./binding.js";
4
4
  import { Compiler } from "./compiler.js";
5
- import { Aspect, HTMLDirective, } from "./html-directive.js";
5
+ import { Binding, HTMLDirective, } from "./html-directive.js";
6
6
  import { nextId } from "./markup.js";
7
+ // Much thanks to LitHTML for working this out!
8
+ const lastAttributeNameRegex =
9
+ /* eslint-disable-next-line no-control-regex */
10
+ /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
11
+ const noFactories = Object.create(null);
12
+ /**
13
+ * Inlines a template into another template.
14
+ * @public
15
+ */
16
+ export class InlineTemplateDirective {
17
+ /**
18
+ * Creates an instance of InlineTemplateDirective.
19
+ * @param template - The template to inline.
20
+ */
21
+ constructor(html, factories = noFactories) {
22
+ this.html = html;
23
+ this.factories = factories;
24
+ }
25
+ /**
26
+ * Creates HTML to be used within a template.
27
+ * @param add - Can be used to add behavior factories to a template.
28
+ */
29
+ createHTML(add) {
30
+ const factories = this.factories;
31
+ for (const key in factories) {
32
+ add(factories[key]);
33
+ }
34
+ return this.html;
35
+ }
36
+ }
37
+ /**
38
+ * An empty template partial.
39
+ */
40
+ InlineTemplateDirective.empty = new InlineTemplateDirective("");
41
+ HTMLDirective.define(InlineTemplateDirective);
42
+ function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
43
+ if (definition.aspected) {
44
+ const match = lastAttributeNameRegex.exec(prevString);
45
+ if (match !== null) {
46
+ HTMLDirective.assignAspect(value, match[2]);
47
+ }
48
+ }
49
+ return value.createHTML(add);
50
+ }
7
51
  /**
8
52
  * A template capable of creating HTMLView instances or rendering directly to DOM.
9
53
  * @public
@@ -13,9 +57,16 @@ export class ViewTemplate {
13
57
  * Creates an instance of ViewTemplate.
14
58
  * @param html - The html representing what this template will instantiate, including placeholders for directives.
15
59
  * @param factories - The directives that will be connected to placeholders in the html.
60
+ * @param policy - The security policy to use when compiling this template.
16
61
  */
17
- constructor(html, factories) {
62
+ constructor(html, factories = {}, policy) {
63
+ this.policy = policy;
18
64
  this.result = null;
65
+ /**
66
+ * Opts out of JSON stringification.
67
+ * @internal
68
+ */
69
+ this.toJSON = noop;
19
70
  this.html = html;
20
71
  this.factories = factories;
21
72
  }
@@ -25,10 +76,34 @@ export class ViewTemplate {
25
76
  */
26
77
  create(hostBindingTarget) {
27
78
  if (this.result === null) {
28
- this.result = Compiler.compile(this.html, this.factories);
79
+ this.result = Compiler.compile(this.html, this.factories, this.policy);
29
80
  }
30
81
  return this.result.createView(hostBindingTarget);
31
82
  }
83
+ /**
84
+ * Returns a directive that can inline the template.
85
+ */
86
+ inline() {
87
+ return new InlineTemplateDirective(isString(this.html) ? this.html : this.html.innerHTML, this.factories);
88
+ }
89
+ /**
90
+ * Sets the DOMPolicy for this template.
91
+ * @param policy - The policy to associated with this template.
92
+ * @returns The modified template instance.
93
+ * @remarks
94
+ * The DOMPolicy can only be set once for a template and cannot be
95
+ * set after the template is compiled.
96
+ */
97
+ withPolicy(policy) {
98
+ if (this.result) {
99
+ throw FAST.error(1208 /* Message.cannotSetTemplatePolicyAfterCompilation */);
100
+ }
101
+ if (this.policy) {
102
+ throw FAST.error(1207 /* Message.onlySetTemplatePolicyOnce */);
103
+ }
104
+ this.policy = policy;
105
+ return this;
106
+ }
32
107
  /**
33
108
  * Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
34
109
  * @param source - The data source to bind the template to.
@@ -36,23 +111,53 @@ export class ViewTemplate {
36
111
  * @param hostBindingTarget - An HTML element to target the host bindings at if different from the
37
112
  * host that the template is being attached to.
38
113
  */
39
- render(source, host, hostBindingTarget, context) {
40
- const view = this.create(hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : host);
41
- view.bind(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
114
+ render(source, host, hostBindingTarget) {
115
+ const view = this.create(hostBindingTarget);
116
+ view.bind(source);
42
117
  view.appendTo(host);
43
118
  return view;
44
119
  }
45
- }
46
- // Much thanks to LitHTML for working this out!
47
- const lastAttributeNameRegex =
48
- /* eslint-disable-next-line no-control-regex */
49
- /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
50
- function createAspectedHTML(value, prevString, add) {
51
- const match = lastAttributeNameRegex.exec(prevString);
52
- if (match !== null) {
53
- Aspect.assign(value, match[2]);
120
+ /**
121
+ * Creates a template based on a set of static strings and dynamic values.
122
+ * @param strings - The static strings to create the template with.
123
+ * @param values - The dynamic values to create the template with.
124
+ * @param policy - The DOMPolicy to associated with the template.
125
+ * @returns A ViewTemplate.
126
+ * @remarks
127
+ * This API should not be used directly under normal circumstances because constructing
128
+ * a template in this way, if not done properly, can open up the application to XSS
129
+ * attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
130
+ * and also be sure to manually sanitize all static strings particularly if they can
131
+ * come from user input.
132
+ */
133
+ static create(strings, values, policy) {
134
+ let html = "";
135
+ const factories = Object.create(null);
136
+ const add = (factory) => {
137
+ var _a;
138
+ const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
139
+ factories[id] = factory;
140
+ return id;
141
+ };
142
+ for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
143
+ const currentString = strings[i];
144
+ let currentValue = values[i];
145
+ let definition;
146
+ html += currentString;
147
+ if (isFunction(currentValue)) {
148
+ currentValue = new HTMLBindingDirective(bind(currentValue));
149
+ }
150
+ else if (currentValue instanceof Binding) {
151
+ currentValue = new HTMLBindingDirective(currentValue);
152
+ }
153
+ else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
154
+ const staticValue = currentValue;
155
+ currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
156
+ }
157
+ html += createHTML(currentValue, currentString, add, definition);
158
+ }
159
+ return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
54
160
  }
55
- return value.createHTML(add);
56
161
  }
57
162
  /**
58
163
  * Transforms a template literal string into a ViewTemplate.
@@ -63,45 +168,12 @@ function createAspectedHTML(value, prevString, add) {
63
168
  * other template instances, and Directive instances.
64
169
  * @public
65
170
  */
66
- export function html(strings, ...values) {
67
- let html = "";
68
- const factories = Object.create(null);
69
- const add = (factory) => {
70
- var _a;
71
- const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
72
- factories[id] = factory;
73
- return id;
74
- };
75
- for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
76
- const currentString = strings[i];
77
- const currentValue = values[i];
78
- let definition;
79
- html += currentString;
80
- if (isFunction(currentValue)) {
81
- html += createAspectedHTML(bind(currentValue), currentString, add);
82
- }
83
- else if (isString(currentValue)) {
84
- const match = lastAttributeNameRegex.exec(currentString);
85
- if (match !== null) {
86
- const directive = bind(() => currentValue, oneTime);
87
- Aspect.assign(directive, match[2]);
88
- html += directive.createHTML(add);
89
- }
90
- else {
91
- html += currentValue;
92
- }
93
- }
94
- else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
95
- html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
96
- }
97
- else {
98
- if (definition.aspected) {
99
- html += createAspectedHTML(currentValue, currentString, add);
100
- }
101
- else {
102
- html += currentValue.createHTML(add);
103
- }
104
- }
171
+ export const html = ((strings, ...values) => {
172
+ if (Array.isArray(strings) && Array.isArray(strings.raw)) {
173
+ return ViewTemplate.create(strings, values);
105
174
  }
106
- return new ViewTemplate(html + strings[strings.length - 1], factories);
107
- }
175
+ throw FAST.error(1206 /* Message.directCallToHTMLTagNotAllowed */);
176
+ });
177
+ html.partial = (html) => {
178
+ return new InlineTemplateDirective(html);
179
+ };