@microsoft/fast-element 2.0.0-beta.2 → 2.0.0-beta.21
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.
- package/CHANGELOG.json +509 -0
- package/CHANGELOG.md +189 -1
- package/dist/dts/components/attributes.d.ts +15 -0
- package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +74 -28
- package/dist/dts/components/fast-definitions.d.ts +41 -9
- package/dist/dts/components/fast-element.d.ts +14 -26
- package/dist/dts/components/hydration.d.ts +14 -0
- package/dist/{esm/observation/behavior.js → dts/components/install-hydration.d.ts} +0 -0
- package/dist/dts/context.d.ts +7 -7
- package/dist/dts/di/di.d.ts +894 -0
- package/dist/dts/dom-policy.d.ts +83 -0
- package/dist/dts/dom.d.ts +100 -0
- package/dist/dts/index.d.ts +5 -4
- package/dist/dts/index.rollup.d.ts +0 -1
- package/dist/dts/index.rollup.debug.d.ts +0 -1
- package/dist/dts/interfaces.d.ts +62 -80
- package/dist/dts/metadata.d.ts +5 -5
- package/dist/dts/observation/observable.d.ts +99 -54
- package/dist/dts/pending-task.d.ts +32 -0
- package/dist/dts/platform.d.ts +8 -1
- package/dist/dts/polyfills.d.ts +0 -8
- package/dist/dts/state/exports.d.ts +3 -0
- package/dist/dts/state/reactive.d.ts +8 -0
- package/dist/dts/state/state.d.ts +141 -0
- package/dist/dts/state/visitor.d.ts +6 -0
- package/dist/dts/state/watch.d.ts +10 -0
- package/dist/dts/styles/css-directive.d.ts +2 -2
- package/dist/dts/styles/css.d.ts +0 -5
- package/dist/dts/styles/element-styles.d.ts +10 -17
- package/dist/dts/styles/host.d.ts +68 -0
- package/dist/dts/styles/style-strategy.d.ts +42 -0
- package/dist/dts/templating/binding-signal.d.ts +12 -27
- package/dist/dts/templating/binding-two-way.d.ts +22 -37
- package/dist/dts/templating/binding.d.ts +76 -208
- package/dist/dts/templating/children.d.ts +1 -1
- package/dist/dts/templating/compiler.d.ts +11 -13
- package/dist/dts/templating/html-directive.d.ts +91 -97
- package/dist/dts/templating/node-observation.d.ts +15 -6
- package/dist/dts/templating/ref.d.ts +7 -11
- package/dist/dts/templating/render.d.ts +296 -0
- package/dist/dts/templating/repeat.d.ts +23 -34
- package/dist/dts/templating/slotted.d.ts +1 -1
- package/dist/dts/templating/template.d.ts +92 -14
- package/dist/dts/templating/view.d.ts +81 -11
- package/dist/dts/templating/when.d.ts +3 -3
- package/dist/dts/testing/exports.d.ts +3 -0
- package/dist/dts/testing/fakes.d.ts +14 -0
- package/dist/dts/testing/fixture.d.ts +84 -0
- package/dist/dts/testing/timeout.d.ts +7 -0
- package/dist/dts/utilities.d.ts +55 -19
- package/dist/esm/components/attributes.js +28 -5
- package/dist/esm/components/{controller.js → element-controller.js} +238 -137
- package/dist/esm/components/fast-definitions.js +38 -30
- package/dist/esm/components/fast-element.js +27 -16
- package/dist/esm/components/hydration.js +35 -0
- package/dist/esm/components/install-hydration.js +2 -0
- package/dist/esm/context.js +7 -3
- package/dist/esm/debug.js +41 -5
- package/dist/esm/di/di.js +1430 -0
- package/dist/esm/dom-policy.js +345 -0
- package/dist/esm/dom.js +101 -0
- package/dist/esm/index.js +4 -2
- package/dist/esm/index.rollup.debug.js +3 -1
- package/dist/esm/index.rollup.js +3 -1
- package/dist/esm/interfaces.js +52 -0
- package/dist/esm/metadata.js +9 -8
- package/dist/esm/observation/arrays.js +303 -2
- package/dist/esm/observation/observable.js +88 -142
- package/dist/esm/observation/update-queue.js +2 -2
- package/dist/esm/pending-task.js +28 -0
- package/dist/esm/platform.js +28 -3
- package/dist/esm/polyfills.js +3 -61
- package/dist/esm/state/exports.js +3 -0
- package/dist/esm/state/reactive.js +34 -0
- package/dist/esm/state/state.js +148 -0
- package/dist/esm/state/visitor.js +28 -0
- package/dist/esm/state/watch.js +36 -0
- package/dist/esm/styles/css.js +4 -9
- package/dist/esm/styles/element-styles.js +14 -33
- package/dist/esm/styles/host.js +1 -0
- package/dist/esm/styles/style-strategy.js +1 -0
- package/dist/esm/templating/binding-signal.js +67 -62
- package/dist/esm/templating/binding-two-way.js +72 -39
- package/dist/esm/templating/binding.js +142 -286
- package/dist/esm/templating/children.js +8 -4
- package/dist/esm/templating/compiler.js +59 -43
- package/dist/esm/templating/html-directive.js +56 -75
- package/dist/esm/templating/node-observation.js +20 -13
- package/dist/esm/templating/ref.js +4 -12
- package/dist/esm/templating/render.js +402 -0
- package/dist/esm/templating/repeat.js +88 -75
- package/dist/esm/templating/template.js +132 -60
- package/dist/esm/templating/view.js +113 -29
- package/dist/esm/templating/when.js +5 -4
- package/dist/esm/testing/exports.js +3 -0
- package/dist/esm/testing/fakes.js +107 -0
- package/dist/esm/testing/fixture.js +86 -0
- package/dist/esm/testing/timeout.js +24 -0
- package/dist/esm/utilities.js +97 -96
- package/dist/fast-element.api.json +9741 -8201
- package/dist/fast-element.d.ts +889 -646
- package/dist/fast-element.debug.js +2001 -1167
- package/dist/fast-element.debug.min.js +1 -1
- package/dist/fast-element.js +1907 -1109
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +913 -703
- package/docs/api-report.md +331 -258
- package/package.json +38 -16
- package/dist/dts/hooks.d.ts +0 -20
- package/dist/dts/observation/behavior.d.ts +0 -19
- package/dist/dts/observation/splice-strategies.d.ts +0 -13
- package/dist/dts/templating/dom.d.ts +0 -41
- package/dist/esm/hooks.js +0 -32
- package/dist/esm/observation/splice-strategies.js +0 -400
- package/dist/esm/templating/dom.js +0 -49
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
import {
|
|
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,
|
|
13
|
-
view.
|
|
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,
|
|
16
|
-
view.
|
|
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
|
|
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(
|
|
33
|
-
this.
|
|
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
|
-
|
|
45
|
-
this.
|
|
46
|
-
|
|
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
|
|
52
|
-
* @param
|
|
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(
|
|
56
|
-
this.
|
|
57
|
-
this.
|
|
58
|
-
this.
|
|
59
|
-
this.
|
|
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
|
|
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 (
|
|
85
|
-
this.items = this.itemsBindingObserver.
|
|
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 (
|
|
90
|
-
this.template = this.templateBindingObserver.
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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,
|
|
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 =
|
|
145
|
-
|
|
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,
|
|
149
|
-
views[i].context
|
|
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
|
|
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,
|
|
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,
|
|
199
|
+
bindView(view, items, i, controller);
|
|
184
200
|
}
|
|
185
201
|
else {
|
|
186
202
|
const view = template.create();
|
|
187
|
-
bindView(view, items, i,
|
|
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
|
|
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(
|
|
217
|
-
this.
|
|
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(
|
|
236
|
-
return new RepeatBehavior(
|
|
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
|
|
243
|
-
* @param
|
|
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(
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
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 {
|
|
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 {
|
|
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
|
|
40
|
-
const view = this.create(hostBindingTarget
|
|
41
|
-
view.bind(source
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
107
|
-
}
|
|
175
|
+
throw FAST.error(1206 /* Message.directCallToHTMLTagNotAllowed */);
|
|
176
|
+
});
|
|
177
|
+
html.partial = (html) => {
|
|
178
|
+
return new InlineTemplateDirective(html);
|
|
179
|
+
};
|