@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
package/dist/fast-element.js
CHANGED
|
@@ -1,10 +1,73 @@
|
|
|
1
|
+
let kernelMode;
|
|
2
|
+
const kernelAttr = "fast-kernel";
|
|
3
|
+
try {
|
|
4
|
+
if (document.currentScript) {
|
|
5
|
+
kernelMode = document.currentScript.getAttribute(kernelAttr);
|
|
6
|
+
}
|
|
7
|
+
else {
|
|
8
|
+
const scripts = document.getElementsByTagName("script");
|
|
9
|
+
const currentScript = scripts[scripts.length - 1];
|
|
10
|
+
kernelMode = currentScript.getAttribute(kernelAttr);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
catch (e) {
|
|
14
|
+
kernelMode = "isolate";
|
|
15
|
+
}
|
|
16
|
+
let KernelServiceId;
|
|
17
|
+
switch (kernelMode) {
|
|
18
|
+
case "share": // share the kernel across major versions
|
|
19
|
+
KernelServiceId = Object.freeze({
|
|
20
|
+
updateQueue: 1,
|
|
21
|
+
observable: 2,
|
|
22
|
+
contextEvent: 3,
|
|
23
|
+
elementRegistry: 4,
|
|
24
|
+
});
|
|
25
|
+
break;
|
|
26
|
+
case "share-v2": // only share the kernel with other v2 instances
|
|
27
|
+
KernelServiceId = Object.freeze({
|
|
28
|
+
updateQueue: 1.2,
|
|
29
|
+
observable: 2.2,
|
|
30
|
+
contextEvent: 3.2,
|
|
31
|
+
elementRegistry: 4.2,
|
|
32
|
+
});
|
|
33
|
+
break;
|
|
34
|
+
default:
|
|
35
|
+
// fully isolate the kernel from all other FAST instances
|
|
36
|
+
const postfix = `-${Math.random().toString(36).substring(2, 8)}`;
|
|
37
|
+
KernelServiceId = Object.freeze({
|
|
38
|
+
updateQueue: `1.2${postfix}`,
|
|
39
|
+
observable: `2.2${postfix}`,
|
|
40
|
+
contextEvent: `3.2${postfix}`,
|
|
41
|
+
elementRegistry: `4.2${postfix}`,
|
|
42
|
+
});
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Determines whether or not an object is a function.
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
const isFunction = (object) => typeof object === "function";
|
|
50
|
+
/**
|
|
51
|
+
* Determines whether or not an object is a string.
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
const isString = (object) => typeof object === "string";
|
|
55
|
+
/**
|
|
56
|
+
* A function which does nothing.
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
const noop = () => void 0;
|
|
60
|
+
|
|
61
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
1
62
|
(function ensureGlobalThis() {
|
|
2
63
|
if (typeof globalThis !== "undefined") {
|
|
3
64
|
// We're running in a modern environment.
|
|
4
65
|
return;
|
|
5
66
|
}
|
|
67
|
+
// @ts-ignore
|
|
6
68
|
if (typeof global !== "undefined") {
|
|
7
69
|
// We're running in NodeJS
|
|
70
|
+
// @ts-ignore
|
|
8
71
|
global.globalThis = global;
|
|
9
72
|
}
|
|
10
73
|
else if (typeof self !== "undefined") {
|
|
@@ -22,69 +85,8 @@
|
|
|
22
85
|
result.globalThis = result;
|
|
23
86
|
}
|
|
24
87
|
})();
|
|
25
|
-
// API-only Polyfill for trustedTypes
|
|
26
|
-
if (!globalThis.trustedTypes) {
|
|
27
|
-
globalThis.trustedTypes = {
|
|
28
|
-
createPolicy: (n, r) => r,
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
// ensure FAST global - duplicated in platform.ts
|
|
32
|
-
const propConfig$1 = {
|
|
33
|
-
configurable: false,
|
|
34
|
-
enumerable: false,
|
|
35
|
-
writable: false,
|
|
36
|
-
};
|
|
37
|
-
if (globalThis.FAST === void 0) {
|
|
38
|
-
Reflect.defineProperty(globalThis, "FAST", Object.assign({ value: Object.create(null) }, propConfig$1));
|
|
39
|
-
}
|
|
40
|
-
const FAST$1 = globalThis.FAST;
|
|
41
|
-
if (FAST$1.getById === void 0) {
|
|
42
|
-
const storage = Object.create(null);
|
|
43
|
-
Reflect.defineProperty(FAST$1, "getById", Object.assign({ value(id, initialize) {
|
|
44
|
-
let found = storage[id];
|
|
45
|
-
if (found === void 0) {
|
|
46
|
-
found = initialize ? (storage[id] = initialize()) : null;
|
|
47
|
-
}
|
|
48
|
-
return found;
|
|
49
|
-
} }, propConfig$1));
|
|
50
|
-
}
|
|
51
|
-
// duplicated from DOM
|
|
52
|
-
const supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
|
|
53
|
-
"replace" in CSSStyleSheet.prototype;
|
|
54
|
-
function usableStyleTarget(target) {
|
|
55
|
-
return target === document ? document.body : target;
|
|
56
|
-
}
|
|
57
|
-
let id$1 = 0;
|
|
58
|
-
const nextStyleId = () => `fast-${++id$1}`;
|
|
59
|
-
class StyleElementStrategy {
|
|
60
|
-
constructor(styles) {
|
|
61
|
-
this.styles = styles;
|
|
62
|
-
this.styleClass = nextStyleId();
|
|
63
|
-
}
|
|
64
|
-
addStylesTo(target) {
|
|
65
|
-
target = usableStyleTarget(target);
|
|
66
|
-
const styles = this.styles;
|
|
67
|
-
const styleClass = this.styleClass;
|
|
68
|
-
for (let i = 0; i < styles.length; i++) {
|
|
69
|
-
const element = document.createElement("style");
|
|
70
|
-
element.innerHTML = styles[i];
|
|
71
|
-
element.className = styleClass;
|
|
72
|
-
target.append(element);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
removeStylesFrom(target) {
|
|
76
|
-
const styles = target.querySelectorAll(`.${this.styleClass}`);
|
|
77
|
-
target = usableStyleTarget(target);
|
|
78
|
-
for (let i = 0, ii = styles.length; i < ii; ++i) {
|
|
79
|
-
target.removeChild(styles[i]);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (!supportsAdoptedStyleSheets) {
|
|
84
|
-
FAST$1.getById(/* KernelServiceId.styleSheetStrategy */ 5, () => StyleElementStrategy);
|
|
85
|
-
}
|
|
86
88
|
|
|
87
|
-
// ensure FAST global - duplicated
|
|
89
|
+
// ensure FAST global - duplicated debug.ts
|
|
88
90
|
const propConfig = {
|
|
89
91
|
configurable: false,
|
|
90
92
|
enumerable: false,
|
|
@@ -95,7 +97,7 @@ if (globalThis.FAST === void 0) {
|
|
|
95
97
|
}
|
|
96
98
|
/**
|
|
97
99
|
* The FAST global.
|
|
98
|
-
* @
|
|
100
|
+
* @public
|
|
99
101
|
*/
|
|
100
102
|
const FAST = globalThis.FAST;
|
|
101
103
|
if (FAST.getById === void 0) {
|
|
@@ -112,7 +114,7 @@ if (FAST.error === void 0) {
|
|
|
112
114
|
Object.assign(FAST, {
|
|
113
115
|
warn() { },
|
|
114
116
|
error(code) {
|
|
115
|
-
return new Error(`
|
|
117
|
+
return new Error(`Error ${code}`);
|
|
116
118
|
},
|
|
117
119
|
addMessages() { },
|
|
118
120
|
});
|
|
@@ -143,25 +145,482 @@ function createTypeRegistry() {
|
|
|
143
145
|
return typeToDefinition.get(key);
|
|
144
146
|
},
|
|
145
147
|
getForInstance(object) {
|
|
148
|
+
if (object === null || object === void 0) {
|
|
149
|
+
return void 0;
|
|
150
|
+
}
|
|
146
151
|
return typeToDefinition.get(object.constructor);
|
|
147
152
|
},
|
|
148
153
|
});
|
|
149
154
|
}
|
|
150
|
-
|
|
151
155
|
/**
|
|
156
|
+
* Creates a function capable of locating metadata associated with a type.
|
|
157
|
+
* @returns A metadata locator function.
|
|
152
158
|
* @internal
|
|
153
159
|
*/
|
|
154
|
-
|
|
160
|
+
function createMetadataLocator() {
|
|
161
|
+
const metadataLookup = new WeakMap();
|
|
162
|
+
return function (target) {
|
|
163
|
+
let metadata = metadataLookup.get(target);
|
|
164
|
+
if (metadata === void 0) {
|
|
165
|
+
let currentTarget = Reflect.getPrototypeOf(target);
|
|
166
|
+
while (metadata === void 0 && currentTarget !== null) {
|
|
167
|
+
metadata = metadataLookup.get(currentTarget);
|
|
168
|
+
currentTarget = Reflect.getPrototypeOf(currentTarget);
|
|
169
|
+
}
|
|
170
|
+
metadata = metadata === void 0 ? [] : metadata.slice(0);
|
|
171
|
+
metadataLookup.set(target, metadata);
|
|
172
|
+
}
|
|
173
|
+
return metadata;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
155
177
|
/**
|
|
156
|
-
*
|
|
178
|
+
* The type of HTML aspect to target.
|
|
179
|
+
* @public
|
|
157
180
|
*/
|
|
158
|
-
const
|
|
181
|
+
const DOMAspect = Object.freeze({
|
|
182
|
+
/**
|
|
183
|
+
* Not aspected.
|
|
184
|
+
*/
|
|
185
|
+
none: 0,
|
|
186
|
+
/**
|
|
187
|
+
* An attribute.
|
|
188
|
+
*/
|
|
189
|
+
attribute: 1,
|
|
190
|
+
/**
|
|
191
|
+
* A boolean attribute.
|
|
192
|
+
*/
|
|
193
|
+
booleanAttribute: 2,
|
|
194
|
+
/**
|
|
195
|
+
* A property.
|
|
196
|
+
*/
|
|
197
|
+
property: 3,
|
|
198
|
+
/**
|
|
199
|
+
* Content
|
|
200
|
+
*/
|
|
201
|
+
content: 4,
|
|
202
|
+
/**
|
|
203
|
+
* A token list.
|
|
204
|
+
*/
|
|
205
|
+
tokenList: 5,
|
|
206
|
+
/**
|
|
207
|
+
* An event.
|
|
208
|
+
*/
|
|
209
|
+
event: 6,
|
|
210
|
+
});
|
|
211
|
+
const createHTML$1 = html => html;
|
|
212
|
+
const fastTrustedType = globalThis.trustedTypes
|
|
213
|
+
? globalThis.trustedTypes.createPolicy("fast-html", { createHTML: createHTML$1 })
|
|
214
|
+
: { createHTML: createHTML$1 };
|
|
215
|
+
let defaultPolicy = Object.freeze({
|
|
216
|
+
createHTML(value) {
|
|
217
|
+
return fastTrustedType.createHTML(value);
|
|
218
|
+
},
|
|
219
|
+
protect(tagName, aspect, aspectName, sink) {
|
|
220
|
+
return sink;
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
const fastPolicy = defaultPolicy;
|
|
224
|
+
/**
|
|
225
|
+
* Common DOM APIs.
|
|
226
|
+
* @public
|
|
227
|
+
*/
|
|
228
|
+
const DOM = Object.freeze({
|
|
229
|
+
/**
|
|
230
|
+
* Gets the dom policy used by the templating system.
|
|
231
|
+
*/
|
|
232
|
+
get policy() {
|
|
233
|
+
return defaultPolicy;
|
|
234
|
+
},
|
|
235
|
+
/**
|
|
236
|
+
* Sets the dom policy used by the templating system.
|
|
237
|
+
* @param policy - The policy to set.
|
|
238
|
+
* @remarks
|
|
239
|
+
* This API can only be called once, for security reasons. It should be
|
|
240
|
+
* called by the application developer at the start of their program.
|
|
241
|
+
*/
|
|
242
|
+
setPolicy(value) {
|
|
243
|
+
if (defaultPolicy !== fastPolicy) {
|
|
244
|
+
throw FAST.error(1201 /* Message.onlySetDOMPolicyOnce */);
|
|
245
|
+
}
|
|
246
|
+
defaultPolicy = value;
|
|
247
|
+
},
|
|
248
|
+
/**
|
|
249
|
+
* Sets an attribute value on an element.
|
|
250
|
+
* @param element - The element to set the attribute value on.
|
|
251
|
+
* @param attributeName - The attribute name to set.
|
|
252
|
+
* @param value - The value of the attribute to set.
|
|
253
|
+
* @remarks
|
|
254
|
+
* If the value is `null` or `undefined`, the attribute is removed, otherwise
|
|
255
|
+
* it is set to the provided value using the standard `setAttribute` API.
|
|
256
|
+
*/
|
|
257
|
+
setAttribute(element, attributeName, value) {
|
|
258
|
+
value === null || value === undefined
|
|
259
|
+
? element.removeAttribute(attributeName)
|
|
260
|
+
: element.setAttribute(attributeName, value);
|
|
261
|
+
},
|
|
262
|
+
/**
|
|
263
|
+
* Sets a boolean attribute value.
|
|
264
|
+
* @param element - The element to set the boolean attribute value on.
|
|
265
|
+
* @param attributeName - The attribute name to set.
|
|
266
|
+
* @param value - The value of the attribute to set.
|
|
267
|
+
* @remarks
|
|
268
|
+
* If the value is true, the attribute is added; otherwise it is removed.
|
|
269
|
+
*/
|
|
270
|
+
setBooleanAttribute(element, attributeName, value) {
|
|
271
|
+
value
|
|
272
|
+
? element.setAttribute(attributeName, "")
|
|
273
|
+
: element.removeAttribute(attributeName);
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
function safeURL(tagName, aspect, aspectName, sink) {
|
|
278
|
+
return (target, name, value, ...rest) => {
|
|
279
|
+
if (isString(value)) {
|
|
280
|
+
value = value.replace("javascript:", "");
|
|
281
|
+
}
|
|
282
|
+
sink(target, name, value, ...rest);
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
function block(tagName, aspect, aspectName, sink) {
|
|
286
|
+
throw FAST.error(1209 /* Message.blockedByDOMPolicy */, {
|
|
287
|
+
aspectName,
|
|
288
|
+
tagName: tagName !== null && tagName !== void 0 ? tagName : "text",
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
const defaultDOMElementGuards = {
|
|
292
|
+
a: {
|
|
293
|
+
[DOMAspect.attribute]: {
|
|
294
|
+
href: safeURL,
|
|
295
|
+
},
|
|
296
|
+
[DOMAspect.property]: {
|
|
297
|
+
href: safeURL,
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
area: {
|
|
301
|
+
[DOMAspect.attribute]: {
|
|
302
|
+
href: safeURL,
|
|
303
|
+
},
|
|
304
|
+
[DOMAspect.property]: {
|
|
305
|
+
href: safeURL,
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
button: {
|
|
309
|
+
[DOMAspect.attribute]: {
|
|
310
|
+
formaction: safeURL,
|
|
311
|
+
},
|
|
312
|
+
[DOMAspect.property]: {
|
|
313
|
+
formAction: safeURL,
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
embed: {
|
|
317
|
+
[DOMAspect.attribute]: {
|
|
318
|
+
src: block,
|
|
319
|
+
},
|
|
320
|
+
[DOMAspect.property]: {
|
|
321
|
+
src: block,
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
form: {
|
|
325
|
+
[DOMAspect.attribute]: {
|
|
326
|
+
action: safeURL,
|
|
327
|
+
},
|
|
328
|
+
[DOMAspect.property]: {
|
|
329
|
+
action: safeURL,
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
frame: {
|
|
333
|
+
[DOMAspect.attribute]: {
|
|
334
|
+
src: safeURL,
|
|
335
|
+
},
|
|
336
|
+
[DOMAspect.property]: {
|
|
337
|
+
src: safeURL,
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
iframe: {
|
|
341
|
+
[DOMAspect.attribute]: {
|
|
342
|
+
src: safeURL,
|
|
343
|
+
},
|
|
344
|
+
[DOMAspect.property]: {
|
|
345
|
+
src: safeURL,
|
|
346
|
+
srcdoc: block,
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
input: {
|
|
350
|
+
[DOMAspect.attribute]: {
|
|
351
|
+
formaction: safeURL,
|
|
352
|
+
},
|
|
353
|
+
[DOMAspect.property]: {
|
|
354
|
+
formAction: safeURL,
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
link: {
|
|
358
|
+
[DOMAspect.attribute]: {
|
|
359
|
+
href: block,
|
|
360
|
+
},
|
|
361
|
+
[DOMAspect.property]: {
|
|
362
|
+
href: block,
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
object: {
|
|
366
|
+
[DOMAspect.attribute]: {
|
|
367
|
+
codebase: block,
|
|
368
|
+
data: block,
|
|
369
|
+
},
|
|
370
|
+
[DOMAspect.property]: {
|
|
371
|
+
codeBase: block,
|
|
372
|
+
data: block,
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
script: {
|
|
376
|
+
[DOMAspect.attribute]: {
|
|
377
|
+
src: block,
|
|
378
|
+
text: block,
|
|
379
|
+
},
|
|
380
|
+
[DOMAspect.property]: {
|
|
381
|
+
src: block,
|
|
382
|
+
text: block,
|
|
383
|
+
innerText: block,
|
|
384
|
+
textContent: block,
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
style: {
|
|
388
|
+
[DOMAspect.property]: {
|
|
389
|
+
innerText: block,
|
|
390
|
+
textContent: block,
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
const blockedEvents = {
|
|
395
|
+
onabort: block,
|
|
396
|
+
onauxclick: block,
|
|
397
|
+
onbeforeinput: block,
|
|
398
|
+
onbeforematch: block,
|
|
399
|
+
onblur: block,
|
|
400
|
+
oncancel: block,
|
|
401
|
+
oncanplay: block,
|
|
402
|
+
oncanplaythrough: block,
|
|
403
|
+
onchange: block,
|
|
404
|
+
onclick: block,
|
|
405
|
+
onclose: block,
|
|
406
|
+
oncontextlost: block,
|
|
407
|
+
oncontextmenu: block,
|
|
408
|
+
oncontextrestored: block,
|
|
409
|
+
oncopy: block,
|
|
410
|
+
oncuechange: block,
|
|
411
|
+
oncut: block,
|
|
412
|
+
ondblclick: block,
|
|
413
|
+
ondrag: block,
|
|
414
|
+
ondragend: block,
|
|
415
|
+
ondragenter: block,
|
|
416
|
+
ondragleave: block,
|
|
417
|
+
ondragover: block,
|
|
418
|
+
ondragstart: block,
|
|
419
|
+
ondrop: block,
|
|
420
|
+
ondurationchange: block,
|
|
421
|
+
onemptied: block,
|
|
422
|
+
onended: block,
|
|
423
|
+
onerror: block,
|
|
424
|
+
onfocus: block,
|
|
425
|
+
onformdata: block,
|
|
426
|
+
oninput: block,
|
|
427
|
+
oninvalid: block,
|
|
428
|
+
onkeydown: block,
|
|
429
|
+
onkeypress: block,
|
|
430
|
+
onkeyup: block,
|
|
431
|
+
onload: block,
|
|
432
|
+
onloadeddata: block,
|
|
433
|
+
onloadedmetadata: block,
|
|
434
|
+
onloadstart: block,
|
|
435
|
+
onmousedown: block,
|
|
436
|
+
onmouseenter: block,
|
|
437
|
+
onmouseleave: block,
|
|
438
|
+
onmousemove: block,
|
|
439
|
+
onmouseout: block,
|
|
440
|
+
onmouseover: block,
|
|
441
|
+
onmouseup: block,
|
|
442
|
+
onpaste: block,
|
|
443
|
+
onpause: block,
|
|
444
|
+
onplay: block,
|
|
445
|
+
onplaying: block,
|
|
446
|
+
onprogress: block,
|
|
447
|
+
onratechange: block,
|
|
448
|
+
onreset: block,
|
|
449
|
+
onresize: block,
|
|
450
|
+
onscroll: block,
|
|
451
|
+
onsecuritypolicyviolation: block,
|
|
452
|
+
onseeked: block,
|
|
453
|
+
onseeking: block,
|
|
454
|
+
onselect: block,
|
|
455
|
+
onslotchange: block,
|
|
456
|
+
onstalled: block,
|
|
457
|
+
onsubmit: block,
|
|
458
|
+
onsuspend: block,
|
|
459
|
+
ontimeupdate: block,
|
|
460
|
+
ontoggle: block,
|
|
461
|
+
onvolumechange: block,
|
|
462
|
+
onwaiting: block,
|
|
463
|
+
onwebkitanimationend: block,
|
|
464
|
+
onwebkitanimationiteration: block,
|
|
465
|
+
onwebkitanimationstart: block,
|
|
466
|
+
onwebkittransitionend: block,
|
|
467
|
+
onwheel: block,
|
|
468
|
+
};
|
|
469
|
+
const defaultDOMGuards = {
|
|
470
|
+
elements: defaultDOMElementGuards,
|
|
471
|
+
aspects: {
|
|
472
|
+
[DOMAspect.attribute]: Object.assign({}, blockedEvents),
|
|
473
|
+
[DOMAspect.property]: Object.assign({ innerHTML: block }, blockedEvents),
|
|
474
|
+
[DOMAspect.event]: Object.assign({}, blockedEvents),
|
|
475
|
+
},
|
|
476
|
+
};
|
|
477
|
+
function createDomSinkGuards(config, defaults) {
|
|
478
|
+
const result = {};
|
|
479
|
+
for (const name in defaults) {
|
|
480
|
+
const overrideValue = config[name];
|
|
481
|
+
const defaultValue = defaults[name];
|
|
482
|
+
switch (overrideValue) {
|
|
483
|
+
case null:
|
|
484
|
+
// remove the default
|
|
485
|
+
break;
|
|
486
|
+
case undefined:
|
|
487
|
+
// keep the default
|
|
488
|
+
result[name] = defaultValue;
|
|
489
|
+
break;
|
|
490
|
+
default:
|
|
491
|
+
// override the default
|
|
492
|
+
result[name] = overrideValue;
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
// add any new sinks that were not overrides
|
|
497
|
+
for (const name in config) {
|
|
498
|
+
if (!(name in result)) {
|
|
499
|
+
result[name] = config[name];
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return Object.freeze(result);
|
|
503
|
+
}
|
|
504
|
+
function createDOMAspectGuards(config, defaults) {
|
|
505
|
+
const result = {};
|
|
506
|
+
for (const aspect in defaults) {
|
|
507
|
+
const overrideValue = config[aspect];
|
|
508
|
+
const defaultValue = defaults[aspect];
|
|
509
|
+
switch (overrideValue) {
|
|
510
|
+
case null:
|
|
511
|
+
// remove the default
|
|
512
|
+
break;
|
|
513
|
+
case undefined:
|
|
514
|
+
// keep the default
|
|
515
|
+
result[aspect] = createDomSinkGuards(defaultValue, {});
|
|
516
|
+
break;
|
|
517
|
+
default:
|
|
518
|
+
// override the default
|
|
519
|
+
result[aspect] = createDomSinkGuards(overrideValue, defaultValue);
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
// add any new aspect guards that were not overrides
|
|
524
|
+
for (const aspect in config) {
|
|
525
|
+
if (!(aspect in result)) {
|
|
526
|
+
result[aspect] = createDomSinkGuards(config[aspect], {});
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return Object.freeze(result);
|
|
530
|
+
}
|
|
531
|
+
function createElementGuards(config, defaults) {
|
|
532
|
+
const result = {};
|
|
533
|
+
for (const tag in defaults) {
|
|
534
|
+
const overrideValue = config[tag];
|
|
535
|
+
const defaultValue = defaults[tag];
|
|
536
|
+
switch (overrideValue) {
|
|
537
|
+
case null:
|
|
538
|
+
// remove the default
|
|
539
|
+
break;
|
|
540
|
+
case undefined:
|
|
541
|
+
// keep the default
|
|
542
|
+
result[tag] = createDOMAspectGuards(overrideValue, {});
|
|
543
|
+
break;
|
|
544
|
+
default:
|
|
545
|
+
// override the default aspects
|
|
546
|
+
result[tag] = createDOMAspectGuards(overrideValue, defaultValue);
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
// Add any new element guards that were not overrides
|
|
551
|
+
for (const tag in config) {
|
|
552
|
+
if (!(tag in result)) {
|
|
553
|
+
result[tag] = createDOMAspectGuards(config[tag], {});
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return Object.freeze(result);
|
|
557
|
+
}
|
|
558
|
+
function createDOMGuards(config, defaults) {
|
|
559
|
+
return Object.freeze({
|
|
560
|
+
elements: config.elements
|
|
561
|
+
? createElementGuards(config.elements, defaults.elements)
|
|
562
|
+
: defaults.elements,
|
|
563
|
+
aspects: config.aspects
|
|
564
|
+
? createDOMAspectGuards(config.aspects, defaults.aspects)
|
|
565
|
+
: defaults.aspects,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
function createTrustedType() {
|
|
569
|
+
const createHTML = html => html;
|
|
570
|
+
return globalThis.trustedTypes
|
|
571
|
+
? globalThis.trustedTypes.createPolicy("fast-html", { createHTML })
|
|
572
|
+
: { createHTML };
|
|
573
|
+
}
|
|
574
|
+
function tryGuard(aspectGuards, tagName, aspect, aspectName, sink) {
|
|
575
|
+
const sinkGuards = aspectGuards[aspect];
|
|
576
|
+
if (sinkGuards) {
|
|
577
|
+
const guard = sinkGuards[aspectName];
|
|
578
|
+
if (guard) {
|
|
579
|
+
return guard(tagName, aspect, aspectName, sink);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* A helper for creating DOM policies.
|
|
585
|
+
* @public
|
|
586
|
+
*/
|
|
587
|
+
const DOMPolicy = Object.freeze({
|
|
588
|
+
/**
|
|
589
|
+
* Creates a new DOM Policy object.
|
|
590
|
+
* @param options The options to use in creating the policy.
|
|
591
|
+
* @returns The newly created DOMPolicy.
|
|
592
|
+
*/
|
|
593
|
+
create(options = {}) {
|
|
594
|
+
var _a, _b;
|
|
595
|
+
const trustedType = (_a = options.trustedType) !== null && _a !== void 0 ? _a : createTrustedType();
|
|
596
|
+
const guards = createDOMGuards((_b = options.guards) !== null && _b !== void 0 ? _b : {}, defaultDOMGuards);
|
|
597
|
+
return Object.freeze({
|
|
598
|
+
createHTML(value) {
|
|
599
|
+
return trustedType.createHTML(value);
|
|
600
|
+
},
|
|
601
|
+
protect(tagName, aspect, aspectName, sink) {
|
|
602
|
+
var _a;
|
|
603
|
+
// Check for element-specific guards.
|
|
604
|
+
const key = (tagName !== null && tagName !== void 0 ? tagName : "").toLowerCase();
|
|
605
|
+
const elementGuards = guards.elements[key];
|
|
606
|
+
if (elementGuards) {
|
|
607
|
+
const guard = tryGuard(elementGuards, tagName, aspect, aspectName, sink);
|
|
608
|
+
if (guard) {
|
|
609
|
+
return guard;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
// Check for guards applicable to all nodes.
|
|
613
|
+
return ((_a = tryGuard(guards.aspects, tagName, aspect, aspectName, sink)) !== null && _a !== void 0 ? _a : sink);
|
|
614
|
+
},
|
|
615
|
+
});
|
|
616
|
+
},
|
|
617
|
+
});
|
|
159
618
|
|
|
160
619
|
/**
|
|
161
620
|
* The default UpdateQueue.
|
|
162
621
|
* @public
|
|
163
622
|
*/
|
|
164
|
-
const Updates = FAST.getById(
|
|
623
|
+
const Updates = FAST.getById(KernelServiceId.updateQueue, () => {
|
|
165
624
|
const tasks = [];
|
|
166
625
|
const pendingErrors = [];
|
|
167
626
|
const rAF = globalThis.requestAnimationFrame;
|
|
@@ -388,15 +847,29 @@ class PropertyChangeNotifier {
|
|
|
388
847
|
}
|
|
389
848
|
}
|
|
390
849
|
|
|
850
|
+
/**
|
|
851
|
+
* Describes how the source's lifetime relates to its controller's lifetime.
|
|
852
|
+
* @public
|
|
853
|
+
*/
|
|
854
|
+
const SourceLifetime = Object.freeze({
|
|
855
|
+
/**
|
|
856
|
+
* The source to controller lifetime relationship is unknown.
|
|
857
|
+
*/
|
|
858
|
+
unknown: void 0,
|
|
859
|
+
/**
|
|
860
|
+
* The source and controller lifetimes are coupled to one another.
|
|
861
|
+
* They can/will be GC'd together.
|
|
862
|
+
*/
|
|
863
|
+
coupled: 1,
|
|
864
|
+
});
|
|
391
865
|
/**
|
|
392
866
|
* Common Observable APIs.
|
|
393
867
|
* @public
|
|
394
868
|
*/
|
|
395
|
-
const Observable = FAST.getById(
|
|
869
|
+
const Observable = FAST.getById(KernelServiceId.observable, () => {
|
|
396
870
|
const queueUpdate = Updates.enqueue;
|
|
397
871
|
const volatileRegex = /(:|&&|\|\||if)/;
|
|
398
872
|
const notifierLookup = new WeakMap();
|
|
399
|
-
const accessorLookup = new WeakMap();
|
|
400
873
|
let watcher = void 0;
|
|
401
874
|
let createArrayObserver = (array) => {
|
|
402
875
|
throw FAST.error(1101 /* Message.needsArrayObservation */);
|
|
@@ -411,19 +884,7 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
411
884
|
}
|
|
412
885
|
return found;
|
|
413
886
|
}
|
|
414
|
-
|
|
415
|
-
let accessors = accessorLookup.get(target);
|
|
416
|
-
if (accessors === void 0) {
|
|
417
|
-
let currentTarget = Reflect.getPrototypeOf(target);
|
|
418
|
-
while (accessors === void 0 && currentTarget !== null) {
|
|
419
|
-
accessors = accessorLookup.get(currentTarget);
|
|
420
|
-
currentTarget = Reflect.getPrototypeOf(currentTarget);
|
|
421
|
-
}
|
|
422
|
-
accessors = accessors === void 0 ? [] : accessors.slice(0);
|
|
423
|
-
accessorLookup.set(target, accessors);
|
|
424
|
-
}
|
|
425
|
-
return accessors;
|
|
426
|
-
}
|
|
887
|
+
const getAccessors = createMetadataLocator();
|
|
427
888
|
class DefaultObservableAccessor {
|
|
428
889
|
constructor(name) {
|
|
429
890
|
this.name = name;
|
|
@@ -449,10 +910,10 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
449
910
|
}
|
|
450
911
|
}
|
|
451
912
|
}
|
|
452
|
-
class
|
|
453
|
-
constructor(
|
|
454
|
-
super(
|
|
455
|
-
this.
|
|
913
|
+
class ExpressionNotifierImplementation extends SubscriberSet {
|
|
914
|
+
constructor(expression, initialSubscriber, isVolatileBinding = false) {
|
|
915
|
+
super(expression, initialSubscriber);
|
|
916
|
+
this.expression = expression;
|
|
456
917
|
this.isVolatileBinding = isVolatileBinding;
|
|
457
918
|
this.needsRefresh = true;
|
|
458
919
|
this.needsQueue = true;
|
|
@@ -463,10 +924,30 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
463
924
|
this.propertyName = void 0;
|
|
464
925
|
this.notifier = void 0;
|
|
465
926
|
this.next = void 0;
|
|
927
|
+
/**
|
|
928
|
+
* Opts out of JSON stringification.
|
|
929
|
+
*/
|
|
930
|
+
this.toJSON = noop;
|
|
466
931
|
}
|
|
467
932
|
setMode(isAsync) {
|
|
468
933
|
this.isAsync = this.needsQueue = isAsync;
|
|
469
934
|
}
|
|
935
|
+
bind(controller) {
|
|
936
|
+
this.controller = controller;
|
|
937
|
+
const value = this.observe(controller.source, controller.context);
|
|
938
|
+
if (!controller.isBound && this.requiresUnbind(controller)) {
|
|
939
|
+
controller.onUnbind(this);
|
|
940
|
+
}
|
|
941
|
+
return value;
|
|
942
|
+
}
|
|
943
|
+
requiresUnbind(controller) {
|
|
944
|
+
return (controller.sourceLifetime !== SourceLifetime.coupled ||
|
|
945
|
+
this.first !== this.last ||
|
|
946
|
+
this.first.propertySource !== controller.source);
|
|
947
|
+
}
|
|
948
|
+
unbind(controller) {
|
|
949
|
+
this.dispose();
|
|
950
|
+
}
|
|
470
951
|
observe(source, context) {
|
|
471
952
|
if (this.needsRefresh && this.last !== null) {
|
|
472
953
|
this.dispose();
|
|
@@ -474,10 +955,19 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
474
955
|
const previousWatcher = watcher;
|
|
475
956
|
watcher = this.needsRefresh ? this : void 0;
|
|
476
957
|
this.needsRefresh = this.isVolatileBinding;
|
|
477
|
-
|
|
478
|
-
|
|
958
|
+
let result;
|
|
959
|
+
try {
|
|
960
|
+
result = this.expression(source, context);
|
|
961
|
+
}
|
|
962
|
+
finally {
|
|
963
|
+
watcher = previousWatcher;
|
|
964
|
+
}
|
|
479
965
|
return result;
|
|
480
966
|
}
|
|
967
|
+
// backwards compat with v1 kernel
|
|
968
|
+
disconnect() {
|
|
969
|
+
this.dispose();
|
|
970
|
+
}
|
|
481
971
|
dispose() {
|
|
482
972
|
if (this.last !== null) {
|
|
483
973
|
let current = this.first;
|
|
@@ -604,22 +1094,22 @@ const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
|
|
|
604
1094
|
*/
|
|
605
1095
|
getAccessors,
|
|
606
1096
|
/**
|
|
607
|
-
* Creates a {@link
|
|
608
|
-
* provided {@link
|
|
609
|
-
* @param
|
|
1097
|
+
* Creates a {@link ExpressionNotifier} that can watch the
|
|
1098
|
+
* provided {@link Expression} for changes.
|
|
1099
|
+
* @param expression - The binding to observe.
|
|
610
1100
|
* @param initialSubscriber - An initial subscriber to changes in the binding value.
|
|
611
1101
|
* @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
|
|
612
1102
|
*/
|
|
613
|
-
binding(
|
|
614
|
-
return new
|
|
1103
|
+
binding(expression, initialSubscriber, isVolatileBinding = this.isVolatileBinding(expression)) {
|
|
1104
|
+
return new ExpressionNotifierImplementation(expression, initialSubscriber, isVolatileBinding);
|
|
615
1105
|
},
|
|
616
1106
|
/**
|
|
617
1107
|
* Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
|
|
618
1108
|
* on every evaluation of the value.
|
|
619
|
-
* @param
|
|
1109
|
+
* @param expression - The binding to inspect.
|
|
620
1110
|
*/
|
|
621
|
-
isVolatileBinding(
|
|
622
|
-
return volatileRegex.test(
|
|
1111
|
+
isVolatileBinding(expression) {
|
|
1112
|
+
return volatileRegex.test(expression.toString());
|
|
623
1113
|
},
|
|
624
1114
|
});
|
|
625
1115
|
});
|
|
@@ -647,7 +1137,7 @@ function volatile(target, name, descriptor) {
|
|
|
647
1137
|
},
|
|
648
1138
|
});
|
|
649
1139
|
}
|
|
650
|
-
const contextEvent = FAST.getById(
|
|
1140
|
+
const contextEvent = FAST.getById(KernelServiceId.contextEvent, () => {
|
|
651
1141
|
let current = null;
|
|
652
1142
|
return {
|
|
653
1143
|
get() {
|
|
@@ -662,123 +1152,38 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
|
|
|
662
1152
|
* Provides additional contextual information available to behaviors and expressions.
|
|
663
1153
|
* @public
|
|
664
1154
|
*/
|
|
665
|
-
|
|
666
|
-
constructor(parentSource = null, parentContext = null) {
|
|
667
|
-
/**
|
|
668
|
-
* The index of the current item within a repeat context.
|
|
669
|
-
*/
|
|
670
|
-
this.index = 0;
|
|
671
|
-
/**
|
|
672
|
-
* The length of the current collection within a repeat context.
|
|
673
|
-
*/
|
|
674
|
-
this.length = 0;
|
|
675
|
-
this.parent = parentSource;
|
|
676
|
-
this.parentContext = parentContext;
|
|
677
|
-
}
|
|
1155
|
+
const ExecutionContext = Object.freeze({
|
|
678
1156
|
/**
|
|
679
|
-
*
|
|
1157
|
+
* A default execution context.
|
|
680
1158
|
*/
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
*/
|
|
695
|
-
get isOdd() {
|
|
696
|
-
return this.index % 2 !== 0;
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* Indicates whether the current item within a repeat context
|
|
700
|
-
* is the first item in the collection.
|
|
701
|
-
*/
|
|
702
|
-
get isFirst() {
|
|
703
|
-
return this.index === 0;
|
|
704
|
-
}
|
|
705
|
-
/**
|
|
706
|
-
* Indicates whether the current item within a repeat context
|
|
707
|
-
* is somewhere in the middle of the collection.
|
|
708
|
-
*/
|
|
709
|
-
get isInMiddle() {
|
|
710
|
-
return !this.isFirst && !this.isLast;
|
|
711
|
-
}
|
|
712
|
-
/**
|
|
713
|
-
* Indicates whether the current item within a repeat context
|
|
714
|
-
* is the last item in the collection.
|
|
715
|
-
*/
|
|
716
|
-
get isLast() {
|
|
717
|
-
return this.index === this.length - 1;
|
|
718
|
-
}
|
|
719
|
-
/**
|
|
720
|
-
* Returns the typed event detail of a custom event.
|
|
721
|
-
*/
|
|
722
|
-
eventDetail() {
|
|
723
|
-
return this.event.detail;
|
|
724
|
-
}
|
|
725
|
-
/**
|
|
726
|
-
* Returns the typed event target of the event.
|
|
727
|
-
*/
|
|
728
|
-
eventTarget() {
|
|
729
|
-
return this.event.target;
|
|
730
|
-
}
|
|
731
|
-
/**
|
|
732
|
-
* Updates the position/size on a context associated with a list item.
|
|
733
|
-
* @param index - The new index of the item.
|
|
734
|
-
* @param length - The new length of the list.
|
|
735
|
-
*/
|
|
736
|
-
updatePosition(index, length) {
|
|
737
|
-
this.index = index;
|
|
738
|
-
this.length = length;
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* Creates a new execution context descendent from the current context.
|
|
742
|
-
* @param source - The source for the context if different than the parent.
|
|
743
|
-
* @returns A child execution context.
|
|
744
|
-
*/
|
|
745
|
-
createChildContext(parentSource) {
|
|
746
|
-
return new ExecutionContext(parentSource, this);
|
|
747
|
-
}
|
|
1159
|
+
default: {
|
|
1160
|
+
index: 0,
|
|
1161
|
+
length: 0,
|
|
1162
|
+
get event() {
|
|
1163
|
+
return ExecutionContext.getEvent();
|
|
1164
|
+
},
|
|
1165
|
+
eventDetail() {
|
|
1166
|
+
return this.event.detail;
|
|
1167
|
+
},
|
|
1168
|
+
eventTarget() {
|
|
1169
|
+
return this.event.target;
|
|
1170
|
+
},
|
|
1171
|
+
},
|
|
748
1172
|
/**
|
|
749
|
-
*
|
|
750
|
-
* @
|
|
751
|
-
* @param index - The index of the item in the list.
|
|
752
|
-
* @param length - The length of the list.
|
|
1173
|
+
* Gets the current event.
|
|
1174
|
+
* @returns An event object.
|
|
753
1175
|
*/
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
childContext.length = length;
|
|
758
|
-
return childContext;
|
|
759
|
-
}
|
|
1176
|
+
getEvent() {
|
|
1177
|
+
return contextEvent.get();
|
|
1178
|
+
},
|
|
760
1179
|
/**
|
|
761
|
-
* Sets the
|
|
762
|
-
* @param event -
|
|
763
|
-
* @internal
|
|
1180
|
+
* Sets the current event.
|
|
1181
|
+
* @param event - An event object.
|
|
764
1182
|
*/
|
|
765
|
-
|
|
1183
|
+
setEvent(event) {
|
|
766
1184
|
contextEvent.set(event);
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
* Creates a new root execution context.
|
|
770
|
-
* @returns A new execution context.
|
|
771
|
-
*/
|
|
772
|
-
static create() {
|
|
773
|
-
return new ExecutionContext();
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
/**
|
|
777
|
-
* The default execution context.
|
|
778
|
-
*/
|
|
779
|
-
ExecutionContext.default = new ExecutionContext();
|
|
780
|
-
Observable.defineProperty(ExecutionContext.prototype, "index");
|
|
781
|
-
Observable.defineProperty(ExecutionContext.prototype, "length");
|
|
1185
|
+
},
|
|
1186
|
+
});
|
|
782
1187
|
|
|
783
1188
|
/**
|
|
784
1189
|
* A splice map is a representation of how a previous array of items
|
|
@@ -845,10 +1250,311 @@ const SpliceStrategySupport = Object.freeze({
|
|
|
845
1250
|
const reset = new Splice(0, emptyArray, 0);
|
|
846
1251
|
reset.reset = true;
|
|
847
1252
|
const resetSplices = [reset];
|
|
1253
|
+
// Note: This function is *based* on the computation of the Levenshtein
|
|
1254
|
+
// "edit" distance. The one change is that "updates" are treated as two
|
|
1255
|
+
// edits - not one. With Array splices, an update is really a delete
|
|
1256
|
+
// followed by an add. By retaining this, we optimize for "keeping" the
|
|
1257
|
+
// maximum array items in the original array. For example:
|
|
1258
|
+
//
|
|
1259
|
+
// 'xxxx123' to '123yyyy'
|
|
1260
|
+
//
|
|
1261
|
+
// With 1-edit updates, the shortest path would be just to update all seven
|
|
1262
|
+
// characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
|
|
1263
|
+
// leaves the substring '123' intact.
|
|
1264
|
+
function calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd) {
|
|
1265
|
+
// "Deletion" columns
|
|
1266
|
+
const rowCount = oldEnd - oldStart + 1;
|
|
1267
|
+
const columnCount = currentEnd - currentStart + 1;
|
|
1268
|
+
const distances = new Array(rowCount);
|
|
1269
|
+
let north;
|
|
1270
|
+
let west;
|
|
1271
|
+
// "Addition" rows. Initialize null column.
|
|
1272
|
+
for (let i = 0; i < rowCount; ++i) {
|
|
1273
|
+
distances[i] = new Array(columnCount);
|
|
1274
|
+
distances[i][0] = i;
|
|
1275
|
+
}
|
|
1276
|
+
// Initialize null row
|
|
1277
|
+
for (let j = 0; j < columnCount; ++j) {
|
|
1278
|
+
distances[0][j] = j;
|
|
1279
|
+
}
|
|
1280
|
+
for (let i = 1; i < rowCount; ++i) {
|
|
1281
|
+
for (let j = 1; j < columnCount; ++j) {
|
|
1282
|
+
if (current[currentStart + j - 1] === old[oldStart + i - 1]) {
|
|
1283
|
+
distances[i][j] = distances[i - 1][j - 1];
|
|
1284
|
+
}
|
|
1285
|
+
else {
|
|
1286
|
+
north = distances[i - 1][j] + 1;
|
|
1287
|
+
west = distances[i][j - 1] + 1;
|
|
1288
|
+
distances[i][j] = north < west ? north : west;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
return distances;
|
|
1293
|
+
}
|
|
1294
|
+
// This starts at the final weight, and walks "backward" by finding
|
|
1295
|
+
// the minimum previous weight recursively until the origin of the weight
|
|
1296
|
+
// matrix.
|
|
1297
|
+
function spliceOperationsFromEditDistances(distances) {
|
|
1298
|
+
let i = distances.length - 1;
|
|
1299
|
+
let j = distances[0].length - 1;
|
|
1300
|
+
let current = distances[i][j];
|
|
1301
|
+
const edits = [];
|
|
1302
|
+
while (i > 0 || j > 0) {
|
|
1303
|
+
if (i === 0) {
|
|
1304
|
+
edits.push(2 /* Edit.add */);
|
|
1305
|
+
j--;
|
|
1306
|
+
continue;
|
|
1307
|
+
}
|
|
1308
|
+
if (j === 0) {
|
|
1309
|
+
edits.push(3 /* Edit.delete */);
|
|
1310
|
+
i--;
|
|
1311
|
+
continue;
|
|
1312
|
+
}
|
|
1313
|
+
const northWest = distances[i - 1][j - 1];
|
|
1314
|
+
const west = distances[i - 1][j];
|
|
1315
|
+
const north = distances[i][j - 1];
|
|
1316
|
+
let min;
|
|
1317
|
+
if (west < north) {
|
|
1318
|
+
min = west < northWest ? west : northWest;
|
|
1319
|
+
}
|
|
1320
|
+
else {
|
|
1321
|
+
min = north < northWest ? north : northWest;
|
|
1322
|
+
}
|
|
1323
|
+
if (min === northWest) {
|
|
1324
|
+
if (northWest === current) {
|
|
1325
|
+
edits.push(0 /* Edit.leave */);
|
|
1326
|
+
}
|
|
1327
|
+
else {
|
|
1328
|
+
edits.push(1 /* Edit.update */);
|
|
1329
|
+
current = northWest;
|
|
1330
|
+
}
|
|
1331
|
+
i--;
|
|
1332
|
+
j--;
|
|
1333
|
+
}
|
|
1334
|
+
else if (min === west) {
|
|
1335
|
+
edits.push(3 /* Edit.delete */);
|
|
1336
|
+
i--;
|
|
1337
|
+
current = west;
|
|
1338
|
+
}
|
|
1339
|
+
else {
|
|
1340
|
+
edits.push(2 /* Edit.add */);
|
|
1341
|
+
j--;
|
|
1342
|
+
current = north;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
return edits.reverse();
|
|
1346
|
+
}
|
|
1347
|
+
function sharedPrefix(current, old, searchLength) {
|
|
1348
|
+
for (let i = 0; i < searchLength; ++i) {
|
|
1349
|
+
if (current[i] !== old[i]) {
|
|
1350
|
+
return i;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
return searchLength;
|
|
1354
|
+
}
|
|
1355
|
+
function sharedSuffix(current, old, searchLength) {
|
|
1356
|
+
let index1 = current.length;
|
|
1357
|
+
let index2 = old.length;
|
|
1358
|
+
let count = 0;
|
|
1359
|
+
while (count < searchLength && current[--index1] === old[--index2]) {
|
|
1360
|
+
count++;
|
|
1361
|
+
}
|
|
1362
|
+
return count;
|
|
1363
|
+
}
|
|
1364
|
+
function intersect(start1, end1, start2, end2) {
|
|
1365
|
+
// Disjoint
|
|
1366
|
+
if (end1 < start2 || end2 < start1) {
|
|
1367
|
+
return -1;
|
|
1368
|
+
}
|
|
1369
|
+
// Adjacent
|
|
1370
|
+
if (end1 === start2 || end2 === start1) {
|
|
1371
|
+
return 0;
|
|
1372
|
+
}
|
|
1373
|
+
// Non-zero intersect, span1 first
|
|
1374
|
+
if (start1 < start2) {
|
|
1375
|
+
if (end1 < end2) {
|
|
1376
|
+
return end1 - start2; // Overlap
|
|
1377
|
+
}
|
|
1378
|
+
return end2 - start2; // Contained
|
|
1379
|
+
}
|
|
1380
|
+
// Non-zero intersect, span2 first
|
|
1381
|
+
if (end2 < end1) {
|
|
1382
|
+
return end2 - start1; // Overlap
|
|
1383
|
+
}
|
|
1384
|
+
return end1 - start1; // Contained
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* @remarks
|
|
1388
|
+
* Lacking individual splice mutation information, the minimal set of
|
|
1389
|
+
* splices can be synthesized given the previous state and final state of an
|
|
1390
|
+
* array. The basic approach is to calculate the edit distance matrix and
|
|
1391
|
+
* choose the shortest path through it.
|
|
1392
|
+
*
|
|
1393
|
+
* Complexity: O(l * p)
|
|
1394
|
+
* l: The length of the current array
|
|
1395
|
+
* p: The length of the old array
|
|
1396
|
+
*/
|
|
1397
|
+
function calc(current, currentStart, currentEnd, old, oldStart, oldEnd) {
|
|
1398
|
+
let prefixCount = 0;
|
|
1399
|
+
let suffixCount = 0;
|
|
1400
|
+
const minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
|
|
1401
|
+
if (currentStart === 0 && oldStart === 0) {
|
|
1402
|
+
prefixCount = sharedPrefix(current, old, minLength);
|
|
1403
|
+
}
|
|
1404
|
+
if (currentEnd === current.length && oldEnd === old.length) {
|
|
1405
|
+
suffixCount = sharedSuffix(current, old, minLength - prefixCount);
|
|
1406
|
+
}
|
|
1407
|
+
currentStart += prefixCount;
|
|
1408
|
+
oldStart += prefixCount;
|
|
1409
|
+
currentEnd -= suffixCount;
|
|
1410
|
+
oldEnd -= suffixCount;
|
|
1411
|
+
if (currentEnd - currentStart === 0 && oldEnd - oldStart === 0) {
|
|
1412
|
+
return emptyArray;
|
|
1413
|
+
}
|
|
1414
|
+
if (currentStart === currentEnd) {
|
|
1415
|
+
const splice = new Splice(currentStart, [], 0);
|
|
1416
|
+
while (oldStart < oldEnd) {
|
|
1417
|
+
splice.removed.push(old[oldStart++]);
|
|
1418
|
+
}
|
|
1419
|
+
return [splice];
|
|
1420
|
+
}
|
|
1421
|
+
else if (oldStart === oldEnd) {
|
|
1422
|
+
return [new Splice(currentStart, [], currentEnd - currentStart)];
|
|
1423
|
+
}
|
|
1424
|
+
const ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
|
|
1425
|
+
const splices = [];
|
|
1426
|
+
let splice = void 0;
|
|
1427
|
+
let index = currentStart;
|
|
1428
|
+
let oldIndex = oldStart;
|
|
1429
|
+
for (let i = 0; i < ops.length; ++i) {
|
|
1430
|
+
switch (ops[i]) {
|
|
1431
|
+
case 0 /* Edit.leave */:
|
|
1432
|
+
if (splice !== void 0) {
|
|
1433
|
+
splices.push(splice);
|
|
1434
|
+
splice = void 0;
|
|
1435
|
+
}
|
|
1436
|
+
index++;
|
|
1437
|
+
oldIndex++;
|
|
1438
|
+
break;
|
|
1439
|
+
case 1 /* Edit.update */:
|
|
1440
|
+
if (splice === void 0) {
|
|
1441
|
+
splice = new Splice(index, [], 0);
|
|
1442
|
+
}
|
|
1443
|
+
splice.addedCount++;
|
|
1444
|
+
index++;
|
|
1445
|
+
splice.removed.push(old[oldIndex]);
|
|
1446
|
+
oldIndex++;
|
|
1447
|
+
break;
|
|
1448
|
+
case 2 /* Edit.add */:
|
|
1449
|
+
if (splice === void 0) {
|
|
1450
|
+
splice = new Splice(index, [], 0);
|
|
1451
|
+
}
|
|
1452
|
+
splice.addedCount++;
|
|
1453
|
+
index++;
|
|
1454
|
+
break;
|
|
1455
|
+
case 3 /* Edit.delete */:
|
|
1456
|
+
if (splice === void 0) {
|
|
1457
|
+
splice = new Splice(index, [], 0);
|
|
1458
|
+
}
|
|
1459
|
+
splice.removed.push(old[oldIndex]);
|
|
1460
|
+
oldIndex++;
|
|
1461
|
+
break;
|
|
1462
|
+
// no default
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
if (splice !== void 0) {
|
|
1466
|
+
splices.push(splice);
|
|
1467
|
+
}
|
|
1468
|
+
return splices;
|
|
1469
|
+
}
|
|
1470
|
+
function merge(splice, splices) {
|
|
1471
|
+
let inserted = false;
|
|
1472
|
+
let insertionOffset = 0;
|
|
1473
|
+
for (let i = 0; i < splices.length; i++) {
|
|
1474
|
+
const current = splices[i];
|
|
1475
|
+
current.index += insertionOffset;
|
|
1476
|
+
if (inserted) {
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
const intersectCount = intersect(splice.index, splice.index + splice.removed.length, current.index, current.index + current.addedCount);
|
|
1480
|
+
if (intersectCount >= 0) {
|
|
1481
|
+
// Merge the two splices
|
|
1482
|
+
splices.splice(i, 1);
|
|
1483
|
+
i--;
|
|
1484
|
+
insertionOffset -= current.addedCount - current.removed.length;
|
|
1485
|
+
splice.addedCount += current.addedCount - intersectCount;
|
|
1486
|
+
const deleteCount = splice.removed.length + current.removed.length - intersectCount;
|
|
1487
|
+
if (!splice.addedCount && !deleteCount) {
|
|
1488
|
+
// merged splice is a noop. discard.
|
|
1489
|
+
inserted = true;
|
|
1490
|
+
}
|
|
1491
|
+
else {
|
|
1492
|
+
let currentRemoved = current.removed;
|
|
1493
|
+
if (splice.index < current.index) {
|
|
1494
|
+
// some prefix of splice.removed is prepended to current.removed.
|
|
1495
|
+
const prepend = splice.removed.slice(0, current.index - splice.index);
|
|
1496
|
+
prepend.push(...currentRemoved);
|
|
1497
|
+
currentRemoved = prepend;
|
|
1498
|
+
}
|
|
1499
|
+
if (splice.index + splice.removed.length >
|
|
1500
|
+
current.index + current.addedCount) {
|
|
1501
|
+
// some suffix of splice.removed is appended to current.removed.
|
|
1502
|
+
const append = splice.removed.slice(current.index + current.addedCount - splice.index);
|
|
1503
|
+
currentRemoved.push(...append);
|
|
1504
|
+
}
|
|
1505
|
+
splice.removed = currentRemoved;
|
|
1506
|
+
if (current.index < splice.index) {
|
|
1507
|
+
splice.index = current.index;
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
else if (splice.index < current.index) {
|
|
1512
|
+
// Insert splice here.
|
|
1513
|
+
inserted = true;
|
|
1514
|
+
splices.splice(i, 0, splice);
|
|
1515
|
+
i++;
|
|
1516
|
+
const offset = splice.addedCount - splice.removed.length;
|
|
1517
|
+
current.index += offset;
|
|
1518
|
+
insertionOffset += offset;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
if (!inserted) {
|
|
1522
|
+
splices.push(splice);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
function project(array, changes) {
|
|
1526
|
+
let splices = [];
|
|
1527
|
+
const initialSplices = [];
|
|
1528
|
+
for (let i = 0, ii = changes.length; i < ii; i++) {
|
|
1529
|
+
merge(changes[i], initialSplices);
|
|
1530
|
+
}
|
|
1531
|
+
for (let i = 0, ii = initialSplices.length; i < ii; ++i) {
|
|
1532
|
+
const splice = initialSplices[i];
|
|
1533
|
+
if (splice.addedCount === 1 && splice.removed.length === 1) {
|
|
1534
|
+
if (splice.removed[0] !== array[splice.index]) {
|
|
1535
|
+
splices.push(splice);
|
|
1536
|
+
}
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
splices = splices.concat(calc(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
|
|
1540
|
+
}
|
|
1541
|
+
return splices;
|
|
1542
|
+
}
|
|
1543
|
+
/**
|
|
1544
|
+
* A SpliceStrategy that attempts to merge all splices into the minimal set of
|
|
1545
|
+
* splices needed to represent the change from the old array to the new array.
|
|
1546
|
+
* @public
|
|
1547
|
+
*/
|
|
848
1548
|
let defaultSpliceStrategy = Object.freeze({
|
|
849
|
-
support: SpliceStrategySupport.
|
|
1549
|
+
support: SpliceStrategySupport.optimized,
|
|
850
1550
|
normalize(previous, current, changes) {
|
|
851
|
-
|
|
1551
|
+
if (previous === void 0) {
|
|
1552
|
+
if (changes === void 0) {
|
|
1553
|
+
return emptyArray;
|
|
1554
|
+
}
|
|
1555
|
+
return changes.length > 1 ? project(current, changes) : changes;
|
|
1556
|
+
}
|
|
1557
|
+
return resetSplices;
|
|
852
1558
|
},
|
|
853
1559
|
pop(array, observer, pop, args) {
|
|
854
1560
|
const notEmpty = array.length > 0;
|
|
@@ -1046,7 +1752,6 @@ function lengthOf(array) {
|
|
|
1046
1752
|
return array.length;
|
|
1047
1753
|
}
|
|
1048
1754
|
|
|
1049
|
-
const styleSheetCache = new Map();
|
|
1050
1755
|
let DefaultStyleStrategy;
|
|
1051
1756
|
function reduceStyles(styles) {
|
|
1052
1757
|
return styles
|
|
@@ -1117,42 +1822,26 @@ class ElementStyles {
|
|
|
1117
1822
|
static setDefaultStrategy(Strategy) {
|
|
1118
1823
|
DefaultStyleStrategy = Strategy;
|
|
1119
1824
|
}
|
|
1825
|
+
/**
|
|
1826
|
+
* Normalizes a set of composable style options.
|
|
1827
|
+
* @param styles - The style options to normalize.
|
|
1828
|
+
* @returns A singular ElementStyles instance or undefined.
|
|
1829
|
+
*/
|
|
1830
|
+
static normalize(styles) {
|
|
1831
|
+
return styles === void 0
|
|
1832
|
+
? void 0
|
|
1833
|
+
: Array.isArray(styles)
|
|
1834
|
+
? new ElementStyles(styles)
|
|
1835
|
+
: styles instanceof ElementStyles
|
|
1836
|
+
? styles
|
|
1837
|
+
: new ElementStyles([styles]);
|
|
1838
|
+
}
|
|
1120
1839
|
}
|
|
1121
1840
|
/**
|
|
1122
1841
|
* Indicates whether the DOM supports the adoptedStyleSheets feature.
|
|
1123
1842
|
*/
|
|
1124
1843
|
ElementStyles.supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
|
|
1125
1844
|
"replace" in CSSStyleSheet.prototype;
|
|
1126
|
-
/**
|
|
1127
|
-
* https://wicg.github.io/construct-stylesheets/
|
|
1128
|
-
* https://developers.google.com/web/updates/2019/02/constructable-stylesheets
|
|
1129
|
-
*
|
|
1130
|
-
* @internal
|
|
1131
|
-
*/
|
|
1132
|
-
class AdoptedStyleSheetsStrategy {
|
|
1133
|
-
constructor(styles) {
|
|
1134
|
-
this.sheets = styles.map((x) => {
|
|
1135
|
-
if (x instanceof CSSStyleSheet) {
|
|
1136
|
-
return x;
|
|
1137
|
-
}
|
|
1138
|
-
let sheet = styleSheetCache.get(x);
|
|
1139
|
-
if (sheet === void 0) {
|
|
1140
|
-
sheet = new CSSStyleSheet();
|
|
1141
|
-
sheet.replaceSync(x);
|
|
1142
|
-
styleSheetCache.set(x, sheet);
|
|
1143
|
-
}
|
|
1144
|
-
return sheet;
|
|
1145
|
-
});
|
|
1146
|
-
}
|
|
1147
|
-
addStylesTo(target) {
|
|
1148
|
-
target.adoptedStyleSheets = [...target.adoptedStyleSheets, ...this.sheets];
|
|
1149
|
-
}
|
|
1150
|
-
removeStylesFrom(target) {
|
|
1151
|
-
const sheets = this.sheets;
|
|
1152
|
-
target.adoptedStyleSheets = target.adoptedStyleSheets.filter((x) => sheets.indexOf(x) === -1);
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
ElementStyles.setDefaultStrategy(FAST.getById(5 /* KernelServiceId.styleSheetStrategy */, () => AdoptedStyleSheetsStrategy));
|
|
1156
1845
|
|
|
1157
1846
|
const registry$1 = createTypeRegistry();
|
|
1158
1847
|
/**
|
|
@@ -1261,11 +1950,11 @@ class CSSPartial {
|
|
|
1261
1950
|
}
|
|
1262
1951
|
return this.css;
|
|
1263
1952
|
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1953
|
+
addedCallback(controller) {
|
|
1954
|
+
controller.addStyles(this.styles);
|
|
1266
1955
|
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1956
|
+
removedCallback(controller) {
|
|
1957
|
+
controller.removeStyles(this.styles);
|
|
1269
1958
|
}
|
|
1270
1959
|
}
|
|
1271
1960
|
CSSDirective.define(CSSPartial);
|
|
@@ -1273,68 +1962,14 @@ css.partial = (strings, ...values) => {
|
|
|
1273
1962
|
const { styles, behaviors } = collectStyles(strings, values);
|
|
1274
1963
|
return new CSSPartial(styles, behaviors);
|
|
1275
1964
|
};
|
|
1276
|
-
/**
|
|
1277
|
-
* @deprecated Use css.partial instead.
|
|
1278
|
-
* @public
|
|
1279
|
-
*/
|
|
1280
|
-
const cssPartial = css.partial;
|
|
1281
|
-
|
|
1282
|
-
/**
|
|
1283
|
-
* Common DOM APIs.
|
|
1284
|
-
* @public
|
|
1285
|
-
*/
|
|
1286
|
-
const DOM = Object.freeze({
|
|
1287
|
-
/**
|
|
1288
|
-
* @deprecated
|
|
1289
|
-
* Use Updates.enqueue().
|
|
1290
|
-
*/
|
|
1291
|
-
queueUpdate: Updates.enqueue,
|
|
1292
|
-
/**
|
|
1293
|
-
* @deprecated
|
|
1294
|
-
* Use Updates.next()
|
|
1295
|
-
*/
|
|
1296
|
-
nextUpdate: Updates.next,
|
|
1297
|
-
/**
|
|
1298
|
-
* @deprecated
|
|
1299
|
-
* Use Updates.process()
|
|
1300
|
-
*/
|
|
1301
|
-
processUpdates: Updates.process,
|
|
1302
|
-
/**
|
|
1303
|
-
* Sets an attribute value on an element.
|
|
1304
|
-
* @param element - The element to set the attribute value on.
|
|
1305
|
-
* @param attributeName - The attribute name to set.
|
|
1306
|
-
* @param value - The value of the attribute to set.
|
|
1307
|
-
* @remarks
|
|
1308
|
-
* If the value is `null` or `undefined`, the attribute is removed, otherwise
|
|
1309
|
-
* it is set to the provided value using the standard `setAttribute` API.
|
|
1310
|
-
*/
|
|
1311
|
-
setAttribute(element, attributeName, value) {
|
|
1312
|
-
value === null || value === undefined
|
|
1313
|
-
? element.removeAttribute(attributeName)
|
|
1314
|
-
: element.setAttribute(attributeName, value);
|
|
1315
|
-
},
|
|
1316
|
-
/**
|
|
1317
|
-
* Sets a boolean attribute value.
|
|
1318
|
-
* @param element - The element to set the boolean attribute value on.
|
|
1319
|
-
* @param attributeName - The attribute name to set.
|
|
1320
|
-
* @param value - The value of the attribute to set.
|
|
1321
|
-
* @remarks
|
|
1322
|
-
* If the value is true, the attribute is added; otherwise it is removed.
|
|
1323
|
-
*/
|
|
1324
|
-
setBooleanAttribute(element, attributeName, value) {
|
|
1325
|
-
value
|
|
1326
|
-
? element.setAttribute(attributeName, "")
|
|
1327
|
-
: element.removeAttribute(attributeName);
|
|
1328
|
-
},
|
|
1329
|
-
});
|
|
1330
1965
|
|
|
1331
1966
|
const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
|
|
1332
1967
|
const interpolationStart = `${marker}{`;
|
|
1333
1968
|
const interpolationEnd = `}${marker}`;
|
|
1334
1969
|
const interpolationEndLength = interpolationEnd.length;
|
|
1335
|
-
let id = 0;
|
|
1970
|
+
let id$1 = 0;
|
|
1336
1971
|
/** @internal */
|
|
1337
|
-
const nextId = () => `${marker}-${++id}`;
|
|
1972
|
+
const nextId = () => `${marker}-${++id$1}`;
|
|
1338
1973
|
/**
|
|
1339
1974
|
* Common APIs related to markup generation.
|
|
1340
1975
|
* @public
|
|
@@ -1431,97 +2066,71 @@ const HTMLDirective = Object.freeze({
|
|
|
1431
2066
|
registry.register(options);
|
|
1432
2067
|
return type;
|
|
1433
2068
|
},
|
|
1434
|
-
});
|
|
1435
|
-
/**
|
|
1436
|
-
* Decorator: Defines an HTMLDirective.
|
|
1437
|
-
* @param options - Provides options that specify the directive's application.
|
|
1438
|
-
* @public
|
|
1439
|
-
*/
|
|
1440
|
-
function htmlDirective(options) {
|
|
1441
|
-
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
1442
|
-
return function (type) {
|
|
1443
|
-
HTMLDirective.define(type, options);
|
|
1444
|
-
};
|
|
1445
|
-
}
|
|
1446
|
-
/**
|
|
1447
|
-
* The type of HTML aspect to target.
|
|
1448
|
-
* @public
|
|
1449
|
-
*/
|
|
1450
|
-
const Aspect = Object.freeze({
|
|
1451
|
-
/**
|
|
1452
|
-
* Not aspected.
|
|
1453
|
-
*/
|
|
1454
|
-
none: 0,
|
|
1455
|
-
/**
|
|
1456
|
-
* An attribute.
|
|
1457
|
-
*/
|
|
1458
|
-
attribute: 1,
|
|
1459
|
-
/**
|
|
1460
|
-
* A boolean attribute.
|
|
1461
|
-
*/
|
|
1462
|
-
booleanAttribute: 2,
|
|
1463
|
-
/**
|
|
1464
|
-
* A property.
|
|
1465
|
-
*/
|
|
1466
|
-
property: 3,
|
|
1467
|
-
/**
|
|
1468
|
-
* Content
|
|
1469
|
-
*/
|
|
1470
|
-
content: 4,
|
|
1471
|
-
/**
|
|
1472
|
-
* A token list.
|
|
1473
|
-
*/
|
|
1474
|
-
tokenList: 5,
|
|
1475
|
-
/**
|
|
1476
|
-
* An event.
|
|
1477
|
-
*/
|
|
1478
|
-
event: 6,
|
|
1479
2069
|
/**
|
|
1480
2070
|
*
|
|
1481
2071
|
* @param directive - The directive to assign the aspect to.
|
|
1482
2072
|
* @param value - The value to base the aspect determination on.
|
|
2073
|
+
* @remarks
|
|
2074
|
+
* If a falsy value is provided, then the content aspect will be assigned.
|
|
1483
2075
|
*/
|
|
1484
|
-
|
|
1485
|
-
directive.sourceAspect = value;
|
|
2076
|
+
assignAspect(directive, value) {
|
|
1486
2077
|
if (!value) {
|
|
2078
|
+
directive.aspectType = DOMAspect.content;
|
|
1487
2079
|
return;
|
|
1488
2080
|
}
|
|
2081
|
+
directive.sourceAspect = value;
|
|
1489
2082
|
switch (value[0]) {
|
|
1490
2083
|
case ":":
|
|
1491
2084
|
directive.targetAspect = value.substring(1);
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
case "classList":
|
|
1497
|
-
directive.aspectType = Aspect.tokenList;
|
|
1498
|
-
break;
|
|
1499
|
-
default:
|
|
1500
|
-
directive.aspectType = Aspect.property;
|
|
1501
|
-
break;
|
|
1502
|
-
}
|
|
2085
|
+
directive.aspectType =
|
|
2086
|
+
directive.targetAspect === "classList"
|
|
2087
|
+
? DOMAspect.tokenList
|
|
2088
|
+
: DOMAspect.property;
|
|
1503
2089
|
break;
|
|
1504
2090
|
case "?":
|
|
1505
2091
|
directive.targetAspect = value.substring(1);
|
|
1506
|
-
directive.aspectType =
|
|
2092
|
+
directive.aspectType = DOMAspect.booleanAttribute;
|
|
1507
2093
|
break;
|
|
1508
2094
|
case "@":
|
|
1509
2095
|
directive.targetAspect = value.substring(1);
|
|
1510
|
-
directive.aspectType =
|
|
2096
|
+
directive.aspectType = DOMAspect.event;
|
|
1511
2097
|
break;
|
|
1512
2098
|
default:
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
directive.aspectType = Aspect.property;
|
|
1516
|
-
}
|
|
1517
|
-
else {
|
|
1518
|
-
directive.targetAspect = value;
|
|
1519
|
-
directive.aspectType = Aspect.attribute;
|
|
1520
|
-
}
|
|
2099
|
+
directive.targetAspect = value;
|
|
2100
|
+
directive.aspectType = DOMAspect.attribute;
|
|
1521
2101
|
break;
|
|
1522
2102
|
}
|
|
1523
2103
|
},
|
|
1524
2104
|
});
|
|
2105
|
+
/**
|
|
2106
|
+
* Decorator: Defines an HTMLDirective.
|
|
2107
|
+
* @param options - Provides options that specify the directive's application.
|
|
2108
|
+
* @public
|
|
2109
|
+
*/
|
|
2110
|
+
function htmlDirective(options) {
|
|
2111
|
+
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
2112
|
+
return function (type) {
|
|
2113
|
+
HTMLDirective.define(type, options);
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
/**
|
|
2117
|
+
* Captures a binding expression along with related information and capabilities.
|
|
2118
|
+
*
|
|
2119
|
+
* @public
|
|
2120
|
+
*/
|
|
2121
|
+
class Binding {
|
|
2122
|
+
/**
|
|
2123
|
+
* Creates a binding.
|
|
2124
|
+
* @param evaluate - Evaluates the binding.
|
|
2125
|
+
* @param policy - The security policy to associate with this binding.
|
|
2126
|
+
* @param isVolatile - Indicates whether the binding is volatile.
|
|
2127
|
+
*/
|
|
2128
|
+
constructor(evaluate, policy, isVolatile = false) {
|
|
2129
|
+
this.evaluate = evaluate;
|
|
2130
|
+
this.policy = policy;
|
|
2131
|
+
this.isVolatile = isVolatile;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
1525
2134
|
/**
|
|
1526
2135
|
* A base class used for attribute directives that don't need internal state.
|
|
1527
2136
|
* @public
|
|
@@ -1533,13 +2142,11 @@ class StatelessAttachedAttributeDirective {
|
|
|
1533
2142
|
*/
|
|
1534
2143
|
constructor(options) {
|
|
1535
2144
|
this.options = options;
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
createBehavior(targets) {
|
|
1542
|
-
return this;
|
|
2145
|
+
/**
|
|
2146
|
+
* Opts out of JSON stringification.
|
|
2147
|
+
* @internal
|
|
2148
|
+
*/
|
|
2149
|
+
this.toJSON = noop;
|
|
1543
2150
|
}
|
|
1544
2151
|
/**
|
|
1545
2152
|
* Creates a placeholder string based on the directive's index within the template.
|
|
@@ -1550,118 +2157,43 @@ class StatelessAttachedAttributeDirective {
|
|
|
1550
2157
|
createHTML(add) {
|
|
1551
2158
|
return Markup.attribute(add(this));
|
|
1552
2159
|
}
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
const createInnerHTMLBinding = globalThis.TrustedHTML
|
|
1556
|
-
? (binding) => (s, c) => {
|
|
1557
|
-
const value = binding(s, c);
|
|
1558
|
-
if (value instanceof TrustedHTML) {
|
|
1559
|
-
return value;
|
|
1560
|
-
}
|
|
1561
|
-
throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
|
|
1562
|
-
}
|
|
1563
|
-
: (binding) => binding;
|
|
1564
|
-
/**
|
|
1565
|
-
* Describes how aspects of an HTML element will be affected by bindings.
|
|
1566
|
-
* @public
|
|
1567
|
-
*/
|
|
1568
|
-
const BindingMode = Object.freeze({
|
|
1569
|
-
/**
|
|
1570
|
-
* Creates a binding mode based on the supplied behavior types.
|
|
1571
|
-
* @param UpdateType - The base behavior type used to update aspects.
|
|
1572
|
-
* @param EventType - The base behavior type used to respond to events.
|
|
1573
|
-
* @returns A new binding mode.
|
|
1574
|
-
*/
|
|
1575
|
-
define(UpdateType, EventType = EventBinding) {
|
|
1576
|
-
return Object.freeze({
|
|
1577
|
-
[1]: d => new UpdateType(d, DOM.setAttribute),
|
|
1578
|
-
[2]: d => new UpdateType(d, DOM.setBooleanAttribute),
|
|
1579
|
-
[3]: d => new UpdateType(d, (t, a, v) => (t[a] = v)),
|
|
1580
|
-
[4]: d => new (createContentBinding(UpdateType))(d, updateContentTarget),
|
|
1581
|
-
[5]: d => new UpdateType(d, updateTokenListTarget),
|
|
1582
|
-
[6]: d => new EventType(d),
|
|
1583
|
-
});
|
|
1584
|
-
},
|
|
1585
|
-
});
|
|
1586
|
-
/**
|
|
1587
|
-
* Describes the configuration for a binding expression.
|
|
1588
|
-
* @public
|
|
1589
|
-
*/
|
|
1590
|
-
const BindingConfig = Object.freeze({
|
|
1591
|
-
/**
|
|
1592
|
-
* Creates a binding configuration based on the provided mode and options.
|
|
1593
|
-
* @param mode - The mode to use for the configuration.
|
|
1594
|
-
* @param defaultOptions - The default options to use for the configuration.
|
|
1595
|
-
* @returns A new binding configuration.
|
|
1596
|
-
*/
|
|
1597
|
-
define(mode, defaultOptions) {
|
|
1598
|
-
const config = (options) => {
|
|
1599
|
-
return {
|
|
1600
|
-
mode: config.mode,
|
|
1601
|
-
options: Object.assign({}, defaultOptions, options),
|
|
1602
|
-
};
|
|
1603
|
-
};
|
|
1604
|
-
config.options = defaultOptions;
|
|
1605
|
-
config.mode = mode;
|
|
1606
|
-
return config;
|
|
1607
|
-
},
|
|
1608
|
-
});
|
|
1609
|
-
/**
|
|
1610
|
-
* A base binding behavior for DOM updates.
|
|
1611
|
-
* @public
|
|
1612
|
-
*/
|
|
1613
|
-
class UpdateBinding {
|
|
1614
|
-
/**
|
|
1615
|
-
* Creates an instance of UpdateBinding.
|
|
1616
|
-
* @param directive - The directive that has the configuration for this behavior.
|
|
1617
|
-
* @param updateTarget - The function used to update the target with the latest value.
|
|
1618
|
-
*/
|
|
1619
|
-
constructor(directive, updateTarget) {
|
|
1620
|
-
this.directive = directive;
|
|
1621
|
-
this.updateTarget = updateTarget;
|
|
1622
|
-
}
|
|
1623
|
-
/**
|
|
1624
|
-
* Bind this behavior to the source.
|
|
1625
|
-
* @param source - The source to bind to.
|
|
1626
|
-
* @param context - The execution context that the binding is operating within.
|
|
1627
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1628
|
-
*/
|
|
1629
|
-
bind(source, context, targets) { }
|
|
1630
|
-
/**
|
|
1631
|
-
* Unbinds this behavior from the source.
|
|
1632
|
-
* @param source - The source to unbind from.
|
|
1633
|
-
* @param context - The execution context that the binding is operating within.
|
|
1634
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1635
|
-
*/
|
|
1636
|
-
unbind(source, context, targets) { }
|
|
1637
2160
|
/**
|
|
1638
2161
|
* Creates a behavior.
|
|
1639
2162
|
* @param targets - The targets available for behaviors to be attached to.
|
|
1640
2163
|
*/
|
|
1641
|
-
createBehavior(
|
|
2164
|
+
createBehavior() {
|
|
1642
2165
|
return this;
|
|
1643
2166
|
}
|
|
1644
2167
|
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
2168
|
+
|
|
2169
|
+
class OnChangeBinding extends Binding {
|
|
2170
|
+
createObserver(_, subscriber) {
|
|
2171
|
+
return Observable.binding(this.evaluate, subscriber, this.isVolatile);
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
class OneTimeBinding extends Binding {
|
|
2175
|
+
constructor() {
|
|
2176
|
+
super(...arguments);
|
|
2177
|
+
/**
|
|
2178
|
+
* Opts out of JSON stringification.
|
|
2179
|
+
* @internal
|
|
2180
|
+
*/
|
|
2181
|
+
this.toJSON = noop;
|
|
2182
|
+
}
|
|
2183
|
+
createObserver() {
|
|
2184
|
+
return this;
|
|
2185
|
+
}
|
|
2186
|
+
bind(controller) {
|
|
2187
|
+
return this.evaluate(controller.source, controller.context);
|
|
2188
|
+
}
|
|
1657
2189
|
}
|
|
1658
|
-
function
|
|
2190
|
+
function updateContent(target, aspect, value, controller) {
|
|
1659
2191
|
// If there's no actual value, then this equates to the
|
|
1660
2192
|
// empty string for the purposes of content bindings.
|
|
1661
2193
|
if (value === null || value === undefined) {
|
|
1662
2194
|
value = "";
|
|
1663
2195
|
}
|
|
1664
|
-
// If the value has a "create" method, then it's a
|
|
2196
|
+
// If the value has a "create" method, then it's a ContentTemplate.
|
|
1665
2197
|
if (value.create) {
|
|
1666
2198
|
target.textContent = "";
|
|
1667
2199
|
let view = target.$fastView;
|
|
@@ -1687,14 +2219,14 @@ function updateContentTarget(target, aspect, value, source, context) {
|
|
|
1687
2219
|
// and that there's actually no need to compose it.
|
|
1688
2220
|
if (!view.isComposed) {
|
|
1689
2221
|
view.isComposed = true;
|
|
1690
|
-
view.bind(source, context);
|
|
2222
|
+
view.bind(controller.source, controller.context);
|
|
1691
2223
|
view.insertBefore(target);
|
|
1692
2224
|
target.$fastView = view;
|
|
1693
2225
|
target.$fastTemplate = value;
|
|
1694
2226
|
}
|
|
1695
2227
|
else if (view.needsBindOnly) {
|
|
1696
2228
|
view.needsBindOnly = false;
|
|
1697
|
-
view.bind(source, context);
|
|
2229
|
+
view.bind(controller.source, controller.context);
|
|
1698
2230
|
}
|
|
1699
2231
|
}
|
|
1700
2232
|
else {
|
|
@@ -1702,199 +2234,58 @@ function updateContentTarget(target, aspect, value, source, context) {
|
|
|
1702
2234
|
// If there is a view and it's currently composed into
|
|
1703
2235
|
// the DOM, then we need to remove it.
|
|
1704
2236
|
if (view !== void 0 && view.isComposed) {
|
|
1705
|
-
view.isComposed = false;
|
|
1706
|
-
view.remove();
|
|
1707
|
-
if (view.needsBindOnly) {
|
|
1708
|
-
view.needsBindOnly = false;
|
|
1709
|
-
}
|
|
1710
|
-
else {
|
|
1711
|
-
view.unbind();
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
target.textContent = value;
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
function updateTokenListTarget(target, aspect, value) {
|
|
1718
|
-
var _a;
|
|
1719
|
-
const directive = this.directive;
|
|
1720
|
-
const lookup = `${directive.id}-t`;
|
|
1721
|
-
const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { c: 0, v: Object.create(null) });
|
|
1722
|
-
const versions = state.v;
|
|
1723
|
-
let currentVersion = state.c;
|
|
1724
|
-
const tokenList = target[aspect];
|
|
1725
|
-
// Add the classes, tracking the version at which they were added.
|
|
1726
|
-
if (value !== null && value !== undefined && value.length) {
|
|
1727
|
-
const names = value.split(/\s+/);
|
|
1728
|
-
for (let i = 0, ii = names.length; i < ii; ++i) {
|
|
1729
|
-
const currentName = names[i];
|
|
1730
|
-
if (currentName === "") {
|
|
1731
|
-
continue;
|
|
1732
|
-
}
|
|
1733
|
-
versions[currentName] = currentVersion;
|
|
1734
|
-
tokenList.add(currentName);
|
|
1735
|
-
}
|
|
1736
|
-
}
|
|
1737
|
-
state.v = currentVersion + 1;
|
|
1738
|
-
// If this is the first call to add classes, there's no need to remove old ones.
|
|
1739
|
-
if (currentVersion === 0) {
|
|
1740
|
-
return;
|
|
1741
|
-
}
|
|
1742
|
-
// Remove classes from the previous version.
|
|
1743
|
-
currentVersion -= 1;
|
|
1744
|
-
for (const name in versions) {
|
|
1745
|
-
if (versions[name] === currentVersion) {
|
|
1746
|
-
tokenList.remove(name);
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
/**
|
|
1751
|
-
* A binding behavior for one-time bindings.
|
|
1752
|
-
* @public
|
|
1753
|
-
*/
|
|
1754
|
-
class OneTimeBinding extends UpdateBinding {
|
|
1755
|
-
/**
|
|
1756
|
-
* Bind this behavior to the source.
|
|
1757
|
-
* @param source - The source to bind to.
|
|
1758
|
-
* @param context - The execution context that the binding is operating within.
|
|
1759
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1760
|
-
*/
|
|
1761
|
-
bind(source, context, targets) {
|
|
1762
|
-
const directive = this.directive;
|
|
1763
|
-
this.updateTarget(targets[directive.nodeId], directive.targetAspect, directive.binding(source, context), source, context);
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
/**
|
|
1767
|
-
* A binding behavior for bindings that change.
|
|
1768
|
-
* @public
|
|
1769
|
-
*/
|
|
1770
|
-
class ChangeBinding extends UpdateBinding {
|
|
1771
|
-
/**
|
|
1772
|
-
* Creates an instance of ChangeBinding.
|
|
1773
|
-
* @param directive - The directive that has the configuration for this behavior.
|
|
1774
|
-
* @param updateTarget - The function used to update the target with the latest value.
|
|
1775
|
-
*/
|
|
1776
|
-
constructor(directive, updateTarget) {
|
|
1777
|
-
super(directive, updateTarget);
|
|
1778
|
-
this.isBindingVolatile = Observable.isVolatileBinding(directive.binding);
|
|
1779
|
-
this.observerProperty = `${directive.id}-o`;
|
|
1780
|
-
}
|
|
1781
|
-
/**
|
|
1782
|
-
* Returns the binding observer used to update the node.
|
|
1783
|
-
* @param target - The target node.
|
|
1784
|
-
* @returns A BindingObserver.
|
|
1785
|
-
*/
|
|
1786
|
-
getObserver(target) {
|
|
1787
|
-
var _a;
|
|
1788
|
-
return ((_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = Observable.binding(this.directive.binding, this, this.isBindingVolatile)));
|
|
1789
|
-
}
|
|
1790
|
-
/**
|
|
1791
|
-
* Bind this behavior to the source.
|
|
1792
|
-
* @param source - The source to bind to.
|
|
1793
|
-
* @param context - The execution context that the binding is operating within.
|
|
1794
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1795
|
-
*/
|
|
1796
|
-
bind(source, context, targets) {
|
|
1797
|
-
const directive = this.directive;
|
|
1798
|
-
const target = targets[directive.nodeId];
|
|
1799
|
-
const observer = this.getObserver(target);
|
|
1800
|
-
observer.target = target;
|
|
1801
|
-
observer.source = source;
|
|
1802
|
-
observer.context = context;
|
|
1803
|
-
this.updateTarget(target, directive.targetAspect, observer.observe(source, context), source, context);
|
|
1804
|
-
}
|
|
1805
|
-
/**
|
|
1806
|
-
* Unbinds this behavior from the source.
|
|
1807
|
-
* @param source - The source to unbind from.
|
|
1808
|
-
* @param context - The execution context that the binding is operating within.
|
|
1809
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1810
|
-
*/
|
|
1811
|
-
unbind(source, context, targets) {
|
|
1812
|
-
const target = targets[this.directive.nodeId];
|
|
1813
|
-
const observer = this.getObserver(target);
|
|
1814
|
-
observer.dispose();
|
|
1815
|
-
observer.target = null;
|
|
1816
|
-
observer.source = null;
|
|
1817
|
-
observer.context = null;
|
|
1818
|
-
}
|
|
1819
|
-
/** @internal */
|
|
1820
|
-
handleChange(binding, observer) {
|
|
1821
|
-
const target = observer.target;
|
|
1822
|
-
const source = observer.source;
|
|
1823
|
-
const context = observer.context;
|
|
1824
|
-
this.updateTarget(target, this.directive.targetAspect, observer.observe(source, context), source, context);
|
|
1825
|
-
}
|
|
1826
|
-
}
|
|
1827
|
-
/**
|
|
1828
|
-
* A binding behavior for handling events.
|
|
1829
|
-
* @public
|
|
1830
|
-
*/
|
|
1831
|
-
class EventBinding {
|
|
1832
|
-
/**
|
|
1833
|
-
* Creates an instance of EventBinding.
|
|
1834
|
-
* @param directive - The directive that has the configuration for this behavior.
|
|
1835
|
-
*/
|
|
1836
|
-
constructor(directive) {
|
|
1837
|
-
this.directive = directive;
|
|
1838
|
-
this.sourceProperty = `${directive.id}-s`;
|
|
1839
|
-
this.contextProperty = `${directive.id}-c`;
|
|
1840
|
-
}
|
|
1841
|
-
/**
|
|
1842
|
-
* Bind this behavior to the source.
|
|
1843
|
-
* @param source - The source to bind to.
|
|
1844
|
-
* @param context - The execution context that the binding is operating within.
|
|
1845
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
1846
|
-
*/
|
|
1847
|
-
bind(source, context, targets) {
|
|
1848
|
-
const directive = this.directive;
|
|
1849
|
-
const target = targets[directive.nodeId];
|
|
1850
|
-
target[this.sourceProperty] = source;
|
|
1851
|
-
target[this.contextProperty] = context;
|
|
1852
|
-
target.addEventListener(directive.targetAspect, this, directive.options);
|
|
2237
|
+
view.isComposed = false;
|
|
2238
|
+
view.remove();
|
|
2239
|
+
if (view.needsBindOnly) {
|
|
2240
|
+
view.needsBindOnly = false;
|
|
2241
|
+
}
|
|
2242
|
+
else {
|
|
2243
|
+
view.unbind();
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
target.textContent = value;
|
|
1853
2247
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
2248
|
+
}
|
|
2249
|
+
function updateTokenList(target, aspect, value) {
|
|
2250
|
+
var _a;
|
|
2251
|
+
const lookup = `${this.id}-t`;
|
|
2252
|
+
const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { v: 0, cv: Object.create(null) });
|
|
2253
|
+
const classVersions = state.cv;
|
|
2254
|
+
let version = state.v;
|
|
2255
|
+
const tokenList = target[aspect];
|
|
2256
|
+
// Add the classes, tracking the version at which they were added.
|
|
2257
|
+
if (value !== null && value !== undefined && value.length) {
|
|
2258
|
+
const names = value.split(/\s+/);
|
|
2259
|
+
for (let i = 0, ii = names.length; i < ii; ++i) {
|
|
2260
|
+
const currentName = names[i];
|
|
2261
|
+
if (currentName === "") {
|
|
2262
|
+
continue;
|
|
2263
|
+
}
|
|
2264
|
+
classVersions[currentName] = version;
|
|
2265
|
+
tokenList.add(currentName);
|
|
2266
|
+
}
|
|
1865
2267
|
}
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
createBehavior(targets) {
|
|
1871
|
-
return this;
|
|
2268
|
+
state.v = version + 1;
|
|
2269
|
+
// If this is the first call to add classes, there's no need to remove old ones.
|
|
2270
|
+
if (version === 0) {
|
|
2271
|
+
return;
|
|
1872
2272
|
}
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
ExecutionContext.setEvent(event);
|
|
1879
|
-
const result = this.directive.binding(target[this.sourceProperty], target[this.contextProperty]);
|
|
1880
|
-
ExecutionContext.setEvent(null);
|
|
1881
|
-
if (result !== true) {
|
|
1882
|
-
event.preventDefault();
|
|
2273
|
+
// Remove classes from the previous version.
|
|
2274
|
+
version -= 1;
|
|
2275
|
+
for (const name in classVersions) {
|
|
2276
|
+
if (classVersions[name] === version) {
|
|
2277
|
+
tokenList.remove(name);
|
|
1883
2278
|
}
|
|
1884
2279
|
}
|
|
1885
2280
|
}
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
*/
|
|
1895
|
-
const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
|
|
1896
|
-
once: true,
|
|
1897
|
-
});
|
|
2281
|
+
const sinkLookup = {
|
|
2282
|
+
[DOMAspect.attribute]: DOM.setAttribute,
|
|
2283
|
+
[DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
|
|
2284
|
+
[DOMAspect.property]: (t, a, v) => (t[a] = v),
|
|
2285
|
+
[DOMAspect.content]: updateContent,
|
|
2286
|
+
[DOMAspect.tokenList]: updateTokenList,
|
|
2287
|
+
[DOMAspect.event]: () => void 0,
|
|
2288
|
+
};
|
|
1898
2289
|
/**
|
|
1899
2290
|
* A directive that applies bindings.
|
|
1900
2291
|
* @public
|
|
@@ -1902,19 +2293,15 @@ const oneTime = BindingConfig.define(BindingMode.define(OneTimeBinding), {
|
|
|
1902
2293
|
class HTMLBindingDirective {
|
|
1903
2294
|
/**
|
|
1904
2295
|
* Creates an instance of HTMLBindingDirective.
|
|
1905
|
-
* @param
|
|
1906
|
-
* @param mode - The binding mode to use when applying the binding.
|
|
1907
|
-
* @param options - The options to configure the binding with.
|
|
2296
|
+
* @param dataBinding - The binding configuration to apply.
|
|
1908
2297
|
*/
|
|
1909
|
-
constructor(
|
|
1910
|
-
this.
|
|
1911
|
-
this.
|
|
1912
|
-
this.options = options;
|
|
1913
|
-
this.factory = null;
|
|
2298
|
+
constructor(dataBinding) {
|
|
2299
|
+
this.dataBinding = dataBinding;
|
|
2300
|
+
this.updateTarget = null;
|
|
1914
2301
|
/**
|
|
1915
2302
|
* The type of aspect to target.
|
|
1916
2303
|
*/
|
|
1917
|
-
this.aspectType =
|
|
2304
|
+
this.aspectType = DOMAspect.content;
|
|
1918
2305
|
}
|
|
1919
2306
|
/**
|
|
1920
2307
|
* Creates HTML to be used within a template.
|
|
@@ -1925,31 +2312,114 @@ class HTMLBindingDirective {
|
|
|
1925
2312
|
}
|
|
1926
2313
|
/**
|
|
1927
2314
|
* Creates a behavior.
|
|
1928
|
-
* @param targets - The targets available for behaviors to be attached to.
|
|
1929
2315
|
*/
|
|
1930
|
-
createBehavior(
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
2316
|
+
createBehavior() {
|
|
2317
|
+
var _a;
|
|
2318
|
+
if (this.updateTarget === null) {
|
|
2319
|
+
const sink = sinkLookup[this.aspectType];
|
|
2320
|
+
const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
|
|
2321
|
+
if (!sink) {
|
|
2322
|
+
throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
|
|
2323
|
+
}
|
|
2324
|
+
this.data = `${this.id}-d`;
|
|
2325
|
+
this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
|
|
2326
|
+
}
|
|
2327
|
+
return this;
|
|
2328
|
+
}
|
|
2329
|
+
/** @internal */
|
|
2330
|
+
bind(controller) {
|
|
2331
|
+
var _a;
|
|
2332
|
+
const target = controller.targets[this.targetNodeId];
|
|
2333
|
+
switch (this.aspectType) {
|
|
2334
|
+
case DOMAspect.event:
|
|
2335
|
+
target[this.data] = controller;
|
|
2336
|
+
target.addEventListener(this.targetAspect, this, this.dataBinding.options);
|
|
2337
|
+
break;
|
|
2338
|
+
case DOMAspect.content:
|
|
2339
|
+
controller.onUnbind(this);
|
|
2340
|
+
// intentional fall through
|
|
2341
|
+
default:
|
|
2342
|
+
const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
|
|
2343
|
+
observer.target = target;
|
|
2344
|
+
observer.controller = controller;
|
|
2345
|
+
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
2346
|
+
break;
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
/** @internal */
|
|
2350
|
+
unbind(controller) {
|
|
2351
|
+
const target = controller.targets[this.targetNodeId];
|
|
2352
|
+
const view = target.$fastView;
|
|
2353
|
+
if (view !== void 0 && view.isComposed) {
|
|
2354
|
+
view.unbind();
|
|
2355
|
+
view.needsBindOnly = true;
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
/** @internal */
|
|
2359
|
+
handleEvent(event) {
|
|
2360
|
+
const controller = event.currentTarget[this.data];
|
|
2361
|
+
if (controller.isBound) {
|
|
2362
|
+
ExecutionContext.setEvent(event);
|
|
2363
|
+
const result = this.dataBinding.evaluate(controller.source, controller.context);
|
|
2364
|
+
ExecutionContext.setEvent(null);
|
|
2365
|
+
if (result !== true) {
|
|
2366
|
+
event.preventDefault();
|
|
1934
2367
|
}
|
|
1935
|
-
this.factory = this.mode[this.aspectType](this);
|
|
1936
2368
|
}
|
|
1937
|
-
|
|
2369
|
+
}
|
|
2370
|
+
/** @internal */
|
|
2371
|
+
handleChange(binding, observer) {
|
|
2372
|
+
const target = observer.target;
|
|
2373
|
+
const controller = observer.controller;
|
|
2374
|
+
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
1938
2375
|
}
|
|
1939
2376
|
}
|
|
1940
2377
|
HTMLDirective.define(HTMLBindingDirective, { aspected: true });
|
|
1941
2378
|
/**
|
|
1942
|
-
* Creates
|
|
1943
|
-
* @param
|
|
1944
|
-
* @param
|
|
1945
|
-
* @
|
|
2379
|
+
* Creates an standard binding.
|
|
2380
|
+
* @param expression - The binding to refresh when changed.
|
|
2381
|
+
* @param policy - The security policy to associate with th binding.
|
|
2382
|
+
* @param isVolatile - Indicates whether the binding is volatile or not.
|
|
2383
|
+
* @returns A binding configuration.
|
|
1946
2384
|
* @public
|
|
1947
2385
|
*/
|
|
1948
|
-
function bind(
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
2386
|
+
function bind(expression, policy, isVolatile = Observable.isVolatileBinding(expression)) {
|
|
2387
|
+
return new OnChangeBinding(expression, policy, isVolatile);
|
|
2388
|
+
}
|
|
2389
|
+
/**
|
|
2390
|
+
* Creates a one time binding
|
|
2391
|
+
* @param expression - The binding to refresh when signaled.
|
|
2392
|
+
* @param policy - The security policy to associate with th binding.
|
|
2393
|
+
* @returns A binding configuration.
|
|
2394
|
+
* @public
|
|
2395
|
+
*/
|
|
2396
|
+
function oneTime(expression, policy) {
|
|
2397
|
+
return new OneTimeBinding(expression, policy);
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2400
|
+
* Creates an event listener binding.
|
|
2401
|
+
* @param expression - The binding to invoke when the event is raised.
|
|
2402
|
+
* @param options - Event listener options.
|
|
2403
|
+
* @returns A binding configuration.
|
|
2404
|
+
* @public
|
|
2405
|
+
*/
|
|
2406
|
+
function listener(expression, options) {
|
|
2407
|
+
const config = new OnChangeBinding(expression);
|
|
2408
|
+
config.options = options;
|
|
2409
|
+
return config;
|
|
2410
|
+
}
|
|
2411
|
+
/**
|
|
2412
|
+
* Normalizes the input value into a binding.
|
|
2413
|
+
* @param value - The value to create the default binding for.
|
|
2414
|
+
* @returns A binding configuration for the provided value.
|
|
2415
|
+
* @public
|
|
2416
|
+
*/
|
|
2417
|
+
function normalizeBinding(value) {
|
|
2418
|
+
return isFunction(value)
|
|
2419
|
+
? bind(value)
|
|
2420
|
+
: value instanceof Binding
|
|
2421
|
+
? value
|
|
2422
|
+
: oneTime(() => value);
|
|
1953
2423
|
}
|
|
1954
2424
|
|
|
1955
2425
|
function removeNodeSequence(firstNode, lastNode) {
|
|
@@ -1978,17 +2448,92 @@ class HTMLView {
|
|
|
1978
2448
|
this.factories = factories;
|
|
1979
2449
|
this.targets = targets;
|
|
1980
2450
|
this.behaviors = null;
|
|
2451
|
+
this.unbindables = [];
|
|
1981
2452
|
/**
|
|
1982
2453
|
* The data that the view is bound to.
|
|
1983
2454
|
*/
|
|
1984
2455
|
this.source = null;
|
|
2456
|
+
/**
|
|
2457
|
+
* Indicates whether the controller is bound.
|
|
2458
|
+
*/
|
|
2459
|
+
this.isBound = false;
|
|
2460
|
+
/**
|
|
2461
|
+
* Indicates how the source's lifetime relates to the controller's lifetime.
|
|
2462
|
+
*/
|
|
2463
|
+
this.sourceLifetime = SourceLifetime.unknown;
|
|
1985
2464
|
/**
|
|
1986
2465
|
* The execution context the view is running within.
|
|
1987
2466
|
*/
|
|
1988
|
-
this.context =
|
|
2467
|
+
this.context = this;
|
|
2468
|
+
/**
|
|
2469
|
+
* The index of the current item within a repeat context.
|
|
2470
|
+
*/
|
|
2471
|
+
this.index = 0;
|
|
2472
|
+
/**
|
|
2473
|
+
* The length of the current collection within a repeat context.
|
|
2474
|
+
*/
|
|
2475
|
+
this.length = 0;
|
|
2476
|
+
/**
|
|
2477
|
+
* Opts out of JSON stringification.
|
|
2478
|
+
* @internal
|
|
2479
|
+
*/
|
|
2480
|
+
this.toJSON = noop;
|
|
1989
2481
|
this.firstChild = fragment.firstChild;
|
|
1990
2482
|
this.lastChild = fragment.lastChild;
|
|
1991
2483
|
}
|
|
2484
|
+
/**
|
|
2485
|
+
* The current event within an event handler.
|
|
2486
|
+
*/
|
|
2487
|
+
get event() {
|
|
2488
|
+
return ExecutionContext.getEvent();
|
|
2489
|
+
}
|
|
2490
|
+
/**
|
|
2491
|
+
* Indicates whether the current item within a repeat context
|
|
2492
|
+
* has an even index.
|
|
2493
|
+
*/
|
|
2494
|
+
get isEven() {
|
|
2495
|
+
return this.index % 2 === 0;
|
|
2496
|
+
}
|
|
2497
|
+
/**
|
|
2498
|
+
* Indicates whether the current item within a repeat context
|
|
2499
|
+
* has an odd index.
|
|
2500
|
+
*/
|
|
2501
|
+
get isOdd() {
|
|
2502
|
+
return this.index % 2 !== 0;
|
|
2503
|
+
}
|
|
2504
|
+
/**
|
|
2505
|
+
* Indicates whether the current item within a repeat context
|
|
2506
|
+
* is the first item in the collection.
|
|
2507
|
+
*/
|
|
2508
|
+
get isFirst() {
|
|
2509
|
+
return this.index === 0;
|
|
2510
|
+
}
|
|
2511
|
+
/**
|
|
2512
|
+
* Indicates whether the current item within a repeat context
|
|
2513
|
+
* is somewhere in the middle of the collection.
|
|
2514
|
+
*/
|
|
2515
|
+
get isInMiddle() {
|
|
2516
|
+
return !this.isFirst && !this.isLast;
|
|
2517
|
+
}
|
|
2518
|
+
/**
|
|
2519
|
+
* Indicates whether the current item within a repeat context
|
|
2520
|
+
* is the last item in the collection.
|
|
2521
|
+
*/
|
|
2522
|
+
get isLast() {
|
|
2523
|
+
return this.index === this.length - 1;
|
|
2524
|
+
}
|
|
2525
|
+
/**
|
|
2526
|
+
* Returns the typed event detail of a custom event.
|
|
2527
|
+
*/
|
|
2528
|
+
eventDetail() {
|
|
2529
|
+
return this.event.detail;
|
|
2530
|
+
}
|
|
2531
|
+
/**
|
|
2532
|
+
* Returns the typed event target of the event.
|
|
2533
|
+
*/
|
|
2534
|
+
eventTarget() {
|
|
2535
|
+
return this.event.target;
|
|
2536
|
+
}
|
|
1992
2537
|
/**
|
|
1993
2538
|
* Appends the view's DOM nodes to the referenced node.
|
|
1994
2539
|
* @param node - The parent node to append the view's DOM nodes to.
|
|
@@ -2005,8 +2550,10 @@ class HTMLView {
|
|
|
2005
2550
|
node.parentNode.insertBefore(this.fragment, node);
|
|
2006
2551
|
}
|
|
2007
2552
|
else {
|
|
2008
|
-
const parentNode = node.parentNode;
|
|
2009
2553
|
const end = this.lastChild;
|
|
2554
|
+
if (node.previousSibling === end)
|
|
2555
|
+
return;
|
|
2556
|
+
const parentNode = node.parentNode;
|
|
2010
2557
|
let current = this.firstChild;
|
|
2011
2558
|
let next;
|
|
2012
2559
|
while (current !== end) {
|
|
@@ -2041,58 +2588,61 @@ class HTMLView {
|
|
|
2041
2588
|
removeNodeSequence(this.firstChild, this.lastChild);
|
|
2042
2589
|
this.unbind();
|
|
2043
2590
|
}
|
|
2591
|
+
onUnbind(behavior) {
|
|
2592
|
+
this.unbindables.push(behavior);
|
|
2593
|
+
}
|
|
2044
2594
|
/**
|
|
2045
2595
|
* Binds a view's behaviors to its binding source.
|
|
2046
2596
|
* @param source - The binding source for the view's binding behaviors.
|
|
2047
2597
|
* @param context - The execution context to run the behaviors within.
|
|
2048
2598
|
*/
|
|
2049
|
-
bind(source, context) {
|
|
2050
|
-
|
|
2051
|
-
const oldSource = this.source;
|
|
2052
|
-
if (oldSource === source) {
|
|
2599
|
+
bind(source, context = this) {
|
|
2600
|
+
if (this.source === source) {
|
|
2053
2601
|
return;
|
|
2054
2602
|
}
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
2060
|
-
const current = behaviors[i];
|
|
2061
|
-
current.unbind(oldSource, context, targets);
|
|
2062
|
-
current.bind(source, context, targets);
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
else if (behaviors === null) {
|
|
2603
|
+
let behaviors = this.behaviors;
|
|
2604
|
+
if (behaviors === null) {
|
|
2605
|
+
this.source = source;
|
|
2606
|
+
this.context = context;
|
|
2066
2607
|
this.behaviors = behaviors = new Array(this.factories.length);
|
|
2067
2608
|
const factories = this.factories;
|
|
2068
2609
|
for (let i = 0, ii = factories.length; i < ii; ++i) {
|
|
2069
|
-
const behavior = factories[i].createBehavior(
|
|
2070
|
-
behavior.bind(
|
|
2610
|
+
const behavior = factories[i].createBehavior();
|
|
2611
|
+
behavior.bind(this);
|
|
2071
2612
|
behaviors[i] = behavior;
|
|
2072
2613
|
}
|
|
2073
2614
|
}
|
|
2074
2615
|
else {
|
|
2616
|
+
if (this.source !== null) {
|
|
2617
|
+
this.evaluateUnbindables();
|
|
2618
|
+
}
|
|
2619
|
+
this.isBound = false;
|
|
2620
|
+
this.source = source;
|
|
2621
|
+
this.context = context;
|
|
2075
2622
|
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
2076
|
-
behaviors[i].bind(
|
|
2623
|
+
behaviors[i].bind(this);
|
|
2077
2624
|
}
|
|
2078
2625
|
}
|
|
2626
|
+
this.isBound = true;
|
|
2079
2627
|
}
|
|
2080
2628
|
/**
|
|
2081
2629
|
* Unbinds a view's behaviors from its binding source.
|
|
2082
2630
|
*/
|
|
2083
2631
|
unbind() {
|
|
2084
|
-
|
|
2085
|
-
if (oldSource === null) {
|
|
2632
|
+
if (!this.isBound || this.source === null) {
|
|
2086
2633
|
return;
|
|
2087
2634
|
}
|
|
2088
|
-
|
|
2089
|
-
const context = this.context;
|
|
2090
|
-
const behaviors = this.behaviors;
|
|
2091
|
-
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
2092
|
-
behaviors[i].unbind(oldSource, context, targets);
|
|
2093
|
-
}
|
|
2635
|
+
this.evaluateUnbindables();
|
|
2094
2636
|
this.source = null;
|
|
2095
|
-
this.context =
|
|
2637
|
+
this.context = this;
|
|
2638
|
+
this.isBound = false;
|
|
2639
|
+
}
|
|
2640
|
+
evaluateUnbindables() {
|
|
2641
|
+
const unbindables = this.unbindables;
|
|
2642
|
+
for (let i = 0, ii = unbindables.length; i < ii; ++i) {
|
|
2643
|
+
unbindables[i].unbind(this);
|
|
2644
|
+
}
|
|
2645
|
+
unbindables.length = 0;
|
|
2096
2646
|
}
|
|
2097
2647
|
/**
|
|
2098
2648
|
* Efficiently disposes of a contiguous range of synthetic view instances.
|
|
@@ -2108,6 +2658,8 @@ class HTMLView {
|
|
|
2108
2658
|
}
|
|
2109
2659
|
}
|
|
2110
2660
|
}
|
|
2661
|
+
Observable.defineProperty(HTMLView.prototype, "index");
|
|
2662
|
+
Observable.defineProperty(HTMLView.prototype, "length");
|
|
2111
2663
|
|
|
2112
2664
|
const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
|
|
2113
2665
|
const descriptorCache = {};
|
|
@@ -2116,21 +2668,42 @@ const next = {
|
|
|
2116
2668
|
index: 0,
|
|
2117
2669
|
node: null,
|
|
2118
2670
|
};
|
|
2671
|
+
function tryWarn(name) {
|
|
2672
|
+
if (!name.startsWith("fast-")) {
|
|
2673
|
+
FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
const warningHost = new Proxy(document.createElement("div"), {
|
|
2677
|
+
get(target, property) {
|
|
2678
|
+
tryWarn(property);
|
|
2679
|
+
const value = Reflect.get(target, property);
|
|
2680
|
+
return isFunction(value) ? value.bind(target) : value;
|
|
2681
|
+
},
|
|
2682
|
+
set(target, property, value) {
|
|
2683
|
+
tryWarn(property);
|
|
2684
|
+
return Reflect.set(target, property, value);
|
|
2685
|
+
},
|
|
2686
|
+
});
|
|
2119
2687
|
class CompilationContext {
|
|
2120
|
-
constructor(fragment, directives) {
|
|
2688
|
+
constructor(fragment, directives, policy) {
|
|
2121
2689
|
this.fragment = fragment;
|
|
2122
2690
|
this.directives = directives;
|
|
2691
|
+
this.policy = policy;
|
|
2123
2692
|
this.proto = null;
|
|
2124
2693
|
this.nodeIds = new Set();
|
|
2125
2694
|
this.descriptors = {};
|
|
2126
2695
|
this.factories = [];
|
|
2127
2696
|
}
|
|
2128
|
-
addFactory(factory, parentId, nodeId, targetIndex) {
|
|
2697
|
+
addFactory(factory, parentId, nodeId, targetIndex, tagName) {
|
|
2698
|
+
var _a, _b;
|
|
2129
2699
|
if (!this.nodeIds.has(nodeId)) {
|
|
2130
2700
|
this.nodeIds.add(nodeId);
|
|
2131
2701
|
this.addTargetDescriptor(parentId, nodeId, targetIndex);
|
|
2132
2702
|
}
|
|
2133
|
-
factory.
|
|
2703
|
+
factory.id = (_a = factory.id) !== null && _a !== void 0 ? _a : nextId();
|
|
2704
|
+
factory.targetNodeId = nodeId;
|
|
2705
|
+
factory.targetTagName = tagName;
|
|
2706
|
+
factory.policy = (_b = factory.policy) !== null && _b !== void 0 ? _b : this.policy;
|
|
2134
2707
|
this.factories.push(factory);
|
|
2135
2708
|
}
|
|
2136
2709
|
freeze() {
|
|
@@ -2166,7 +2739,7 @@ class CompilationContext {
|
|
|
2166
2739
|
const fragment = this.fragment.cloneNode(true);
|
|
2167
2740
|
const targets = Object.create(this.proto);
|
|
2168
2741
|
targets.r = fragment;
|
|
2169
|
-
targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget :
|
|
2742
|
+
targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
|
|
2170
2743
|
for (const id of this.nodeIds) {
|
|
2171
2744
|
targets[id]; // trigger locator
|
|
2172
2745
|
}
|
|
@@ -2183,19 +2756,19 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
|
|
|
2183
2756
|
let result = null;
|
|
2184
2757
|
if (parseResult === null) {
|
|
2185
2758
|
if (includeBasicValues) {
|
|
2186
|
-
result =
|
|
2187
|
-
|
|
2759
|
+
result = new HTMLBindingDirective(oneTime(() => attrValue, context.policy));
|
|
2760
|
+
HTMLDirective.assignAspect(result, attr.name);
|
|
2188
2761
|
}
|
|
2189
2762
|
}
|
|
2190
2763
|
else {
|
|
2191
2764
|
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
2192
|
-
result = Compiler.aggregate(parseResult);
|
|
2765
|
+
result = Compiler.aggregate(parseResult, context.policy);
|
|
2193
2766
|
}
|
|
2194
2767
|
if (result !== null) {
|
|
2195
2768
|
node.removeAttributeNode(attr);
|
|
2196
2769
|
i--;
|
|
2197
2770
|
ii--;
|
|
2198
|
-
context.addFactory(result, parentId, nodeId, nodeIndex);
|
|
2771
|
+
context.addFactory(result, parentId, nodeId, nodeIndex, node.tagName);
|
|
2199
2772
|
}
|
|
2200
2773
|
}
|
|
2201
2774
|
}
|
|
@@ -2220,7 +2793,8 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
|
|
|
2220
2793
|
}
|
|
2221
2794
|
else {
|
|
2222
2795
|
currentNode.textContent = " ";
|
|
2223
|
-
|
|
2796
|
+
HTMLDirective.assignAspect(currentPart);
|
|
2797
|
+
context.addFactory(currentPart, parentId, nodeId, nodeIndex, null);
|
|
2224
2798
|
}
|
|
2225
2799
|
lastNode = currentNode;
|
|
2226
2800
|
}
|
|
@@ -2252,7 +2826,7 @@ function compileNode(context, parentId, node, nodeIndex) {
|
|
|
2252
2826
|
if (parts !== null) {
|
|
2253
2827
|
context.addFactory(
|
|
2254
2828
|
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
2255
|
-
Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
|
|
2829
|
+
Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
|
|
2256
2830
|
}
|
|
2257
2831
|
break;
|
|
2258
2832
|
}
|
|
@@ -2266,45 +2840,28 @@ function isMarker(node, directives) {
|
|
|
2266
2840
|
Parser.parse(node.data, directives) !== null);
|
|
2267
2841
|
}
|
|
2268
2842
|
const templateTag = "TEMPLATE";
|
|
2269
|
-
const policyOptions = { createHTML: html => html };
|
|
2270
|
-
let htmlPolicy = globalThis.trustedTypes
|
|
2271
|
-
? globalThis.trustedTypes.createPolicy("fast-html", policyOptions)
|
|
2272
|
-
: policyOptions;
|
|
2273
|
-
const fastHTMLPolicy = htmlPolicy;
|
|
2274
2843
|
/**
|
|
2275
2844
|
* Common APIs related to compilation.
|
|
2276
2845
|
* @public
|
|
2277
2846
|
*/
|
|
2278
2847
|
const Compiler = {
|
|
2279
|
-
/**
|
|
2280
|
-
* Sets the HTML trusted types policy used by the compiler.
|
|
2281
|
-
* @param policy - The policy to set for HTML.
|
|
2282
|
-
* @remarks
|
|
2283
|
-
* This API can only be called once, for security reasons. It should be
|
|
2284
|
-
* called by the application developer at the start of their program.
|
|
2285
|
-
*/
|
|
2286
|
-
setHTMLPolicy(policy) {
|
|
2287
|
-
if (htmlPolicy !== fastHTMLPolicy) {
|
|
2288
|
-
throw FAST.error(1201 /* Message.onlySetHTMLPolicyOnce */);
|
|
2289
|
-
}
|
|
2290
|
-
htmlPolicy = policy;
|
|
2291
|
-
},
|
|
2292
2848
|
/**
|
|
2293
2849
|
* Compiles a template and associated directives into a compilation
|
|
2294
2850
|
* result which can be used to create views.
|
|
2295
2851
|
* @param html - The html string or template element to compile.
|
|
2296
|
-
* @param
|
|
2852
|
+
* @param factories - The behavior factories referenced by the template.
|
|
2853
|
+
* @param policy - The security policy to compile the html with.
|
|
2297
2854
|
* @remarks
|
|
2298
2855
|
* The template that is provided for compilation is altered in-place
|
|
2299
2856
|
* and cannot be compiled again. If the original template must be preserved,
|
|
2300
2857
|
* it is recommended that you clone the original and pass the clone to this API.
|
|
2301
2858
|
* @public
|
|
2302
2859
|
*/
|
|
2303
|
-
compile(html,
|
|
2860
|
+
compile(html, factories, policy = DOM.policy) {
|
|
2304
2861
|
let template;
|
|
2305
2862
|
if (isString(html)) {
|
|
2306
2863
|
template = document.createElement(templateTag);
|
|
2307
|
-
template.innerHTML =
|
|
2864
|
+
template.innerHTML = policy.createHTML(html);
|
|
2308
2865
|
const fec = template.content.firstElementChild;
|
|
2309
2866
|
if (fec !== null && fec.tagName === templateTag) {
|
|
2310
2867
|
template = fec;
|
|
@@ -2315,18 +2872,18 @@ const Compiler = {
|
|
|
2315
2872
|
}
|
|
2316
2873
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
|
|
2317
2874
|
const fragment = document.adoptNode(template.content);
|
|
2318
|
-
const context = new CompilationContext(fragment,
|
|
2875
|
+
const context = new CompilationContext(fragment, factories, policy);
|
|
2319
2876
|
compileAttributes(context, "", template, /* host */ "h", 0, true);
|
|
2320
2877
|
if (
|
|
2321
2878
|
// If the first node in a fragment is a marker, that means it's an unstable first node,
|
|
2322
2879
|
// because something like a when, repeat, etc. could add nodes before the marker.
|
|
2323
2880
|
// To mitigate this, we insert a stable first node. However, if we insert a node,
|
|
2324
2881
|
// that will alter the result of the TreeWalker. So, we also need to offset the target index.
|
|
2325
|
-
isMarker(fragment.firstChild,
|
|
2882
|
+
isMarker(fragment.firstChild, factories) ||
|
|
2326
2883
|
// Or if there is only one node and a directive, it means the template's content
|
|
2327
2884
|
// is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
|
|
2328
2885
|
// the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
|
|
2329
|
-
(fragment.childNodes.length === 1 && Object.keys(
|
|
2886
|
+
(fragment.childNodes.length === 1 && Object.keys(factories).length > 0)) {
|
|
2330
2887
|
fragment.insertBefore(document.createComment(""), fragment.firstChild);
|
|
2331
2888
|
}
|
|
2332
2889
|
compileChildren(context, fragment, /* root */ "r");
|
|
@@ -2345,34 +2902,88 @@ const Compiler = {
|
|
|
2345
2902
|
* Aggregates an array of strings and directives into a single directive.
|
|
2346
2903
|
* @param parts - A heterogeneous array of static strings interspersed with
|
|
2347
2904
|
* directives.
|
|
2905
|
+
* @param policy - The security policy to use with the aggregated bindings.
|
|
2348
2906
|
* @returns A single inline directive that aggregates the behavior of all the parts.
|
|
2349
2907
|
*/
|
|
2350
|
-
aggregate(parts) {
|
|
2908
|
+
aggregate(parts, policy = DOM.policy) {
|
|
2351
2909
|
if (parts.length === 1) {
|
|
2352
2910
|
return parts[0];
|
|
2353
2911
|
}
|
|
2354
2912
|
let sourceAspect;
|
|
2913
|
+
let binding;
|
|
2914
|
+
let isVolatile = false;
|
|
2915
|
+
let bindingPolicy = void 0;
|
|
2355
2916
|
const partCount = parts.length;
|
|
2356
2917
|
const finalParts = parts.map((x) => {
|
|
2357
2918
|
if (isString(x)) {
|
|
2358
2919
|
return () => x;
|
|
2359
2920
|
}
|
|
2360
2921
|
sourceAspect = x.sourceAspect || sourceAspect;
|
|
2361
|
-
|
|
2922
|
+
binding = x.dataBinding || binding;
|
|
2923
|
+
isVolatile = isVolatile || x.dataBinding.isVolatile;
|
|
2924
|
+
bindingPolicy = bindingPolicy || x.dataBinding.policy;
|
|
2925
|
+
return x.dataBinding.evaluate;
|
|
2362
2926
|
});
|
|
2363
|
-
const
|
|
2927
|
+
const expression = (scope, context) => {
|
|
2364
2928
|
let output = "";
|
|
2365
2929
|
for (let i = 0; i < partCount; ++i) {
|
|
2366
2930
|
output += finalParts[i](scope, context);
|
|
2367
2931
|
}
|
|
2368
2932
|
return output;
|
|
2369
2933
|
};
|
|
2370
|
-
|
|
2371
|
-
|
|
2934
|
+
binding.evaluate = expression;
|
|
2935
|
+
binding.isVolatile = isVolatile;
|
|
2936
|
+
binding.policy = bindingPolicy !== null && bindingPolicy !== void 0 ? bindingPolicy : policy;
|
|
2937
|
+
const directive = new HTMLBindingDirective(binding);
|
|
2938
|
+
HTMLDirective.assignAspect(directive, sourceAspect);
|
|
2372
2939
|
return directive;
|
|
2373
2940
|
},
|
|
2374
2941
|
};
|
|
2375
2942
|
|
|
2943
|
+
// Much thanks to LitHTML for working this out!
|
|
2944
|
+
const lastAttributeNameRegex =
|
|
2945
|
+
/* eslint-disable-next-line no-control-regex */
|
|
2946
|
+
/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
|
|
2947
|
+
const noFactories = Object.create(null);
|
|
2948
|
+
/**
|
|
2949
|
+
* Inlines a template into another template.
|
|
2950
|
+
* @public
|
|
2951
|
+
*/
|
|
2952
|
+
class InlineTemplateDirective {
|
|
2953
|
+
/**
|
|
2954
|
+
* Creates an instance of InlineTemplateDirective.
|
|
2955
|
+
* @param template - The template to inline.
|
|
2956
|
+
*/
|
|
2957
|
+
constructor(html, factories = noFactories) {
|
|
2958
|
+
this.html = html;
|
|
2959
|
+
this.factories = factories;
|
|
2960
|
+
}
|
|
2961
|
+
/**
|
|
2962
|
+
* Creates HTML to be used within a template.
|
|
2963
|
+
* @param add - Can be used to add behavior factories to a template.
|
|
2964
|
+
*/
|
|
2965
|
+
createHTML(add) {
|
|
2966
|
+
const factories = this.factories;
|
|
2967
|
+
for (const key in factories) {
|
|
2968
|
+
add(factories[key]);
|
|
2969
|
+
}
|
|
2970
|
+
return this.html;
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
/**
|
|
2974
|
+
* An empty template partial.
|
|
2975
|
+
*/
|
|
2976
|
+
InlineTemplateDirective.empty = new InlineTemplateDirective("");
|
|
2977
|
+
HTMLDirective.define(InlineTemplateDirective);
|
|
2978
|
+
function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
|
|
2979
|
+
if (definition.aspected) {
|
|
2980
|
+
const match = lastAttributeNameRegex.exec(prevString);
|
|
2981
|
+
if (match !== null) {
|
|
2982
|
+
HTMLDirective.assignAspect(value, match[2]);
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
return value.createHTML(add);
|
|
2986
|
+
}
|
|
2376
2987
|
/**
|
|
2377
2988
|
* A template capable of creating HTMLView instances or rendering directly to DOM.
|
|
2378
2989
|
* @public
|
|
@@ -2382,9 +2993,16 @@ class ViewTemplate {
|
|
|
2382
2993
|
* Creates an instance of ViewTemplate.
|
|
2383
2994
|
* @param html - The html representing what this template will instantiate, including placeholders for directives.
|
|
2384
2995
|
* @param factories - The directives that will be connected to placeholders in the html.
|
|
2996
|
+
* @param policy - The security policy to use when compiling this template.
|
|
2385
2997
|
*/
|
|
2386
|
-
constructor(html, factories) {
|
|
2998
|
+
constructor(html, factories = {}, policy) {
|
|
2999
|
+
this.policy = policy;
|
|
2387
3000
|
this.result = null;
|
|
3001
|
+
/**
|
|
3002
|
+
* Opts out of JSON stringification.
|
|
3003
|
+
* @internal
|
|
3004
|
+
*/
|
|
3005
|
+
this.toJSON = noop;
|
|
2388
3006
|
this.html = html;
|
|
2389
3007
|
this.factories = factories;
|
|
2390
3008
|
}
|
|
@@ -2394,10 +3012,34 @@ class ViewTemplate {
|
|
|
2394
3012
|
*/
|
|
2395
3013
|
create(hostBindingTarget) {
|
|
2396
3014
|
if (this.result === null) {
|
|
2397
|
-
this.result = Compiler.compile(this.html, this.factories);
|
|
3015
|
+
this.result = Compiler.compile(this.html, this.factories, this.policy);
|
|
2398
3016
|
}
|
|
2399
3017
|
return this.result.createView(hostBindingTarget);
|
|
2400
3018
|
}
|
|
3019
|
+
/**
|
|
3020
|
+
* Returns a directive that can inline the template.
|
|
3021
|
+
*/
|
|
3022
|
+
inline() {
|
|
3023
|
+
return new InlineTemplateDirective(isString(this.html) ? this.html : this.html.innerHTML, this.factories);
|
|
3024
|
+
}
|
|
3025
|
+
/**
|
|
3026
|
+
* Sets the DOMPolicy for this template.
|
|
3027
|
+
* @param policy - The policy to associated with this template.
|
|
3028
|
+
* @returns The modified template instance.
|
|
3029
|
+
* @remarks
|
|
3030
|
+
* The DOMPolicy can only be set once for a template and cannot be
|
|
3031
|
+
* set after the template is compiled.
|
|
3032
|
+
*/
|
|
3033
|
+
withPolicy(policy) {
|
|
3034
|
+
if (this.result) {
|
|
3035
|
+
throw FAST.error(1208 /* Message.cannotSetTemplatePolicyAfterCompilation */);
|
|
3036
|
+
}
|
|
3037
|
+
if (this.policy) {
|
|
3038
|
+
throw FAST.error(1207 /* Message.onlySetTemplatePolicyOnce */);
|
|
3039
|
+
}
|
|
3040
|
+
this.policy = policy;
|
|
3041
|
+
return this;
|
|
3042
|
+
}
|
|
2401
3043
|
/**
|
|
2402
3044
|
* Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
|
|
2403
3045
|
* @param source - The data source to bind the template to.
|
|
@@ -2405,75 +3047,72 @@ class ViewTemplate {
|
|
|
2405
3047
|
* @param hostBindingTarget - An HTML element to target the host bindings at if different from the
|
|
2406
3048
|
* host that the template is being attached to.
|
|
2407
3049
|
*/
|
|
2408
|
-
render(source, host, hostBindingTarget
|
|
2409
|
-
const view = this.create(hostBindingTarget
|
|
2410
|
-
view.bind(source
|
|
3050
|
+
render(source, host, hostBindingTarget) {
|
|
3051
|
+
const view = this.create(hostBindingTarget);
|
|
3052
|
+
view.bind(source);
|
|
2411
3053
|
view.appendTo(host);
|
|
2412
3054
|
return view;
|
|
2413
3055
|
}
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
};
|
|
2444
|
-
for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
|
|
2445
|
-
const currentString = strings[i];
|
|
2446
|
-
const currentValue = values[i];
|
|
2447
|
-
let definition;
|
|
2448
|
-
html += currentString;
|
|
2449
|
-
if (isFunction(currentValue)) {
|
|
2450
|
-
html += createAspectedHTML(bind(currentValue), currentString, add);
|
|
2451
|
-
}
|
|
2452
|
-
else if (isString(currentValue)) {
|
|
2453
|
-
const match = lastAttributeNameRegex.exec(currentString);
|
|
2454
|
-
if (match !== null) {
|
|
2455
|
-
const directive = bind(() => currentValue, oneTime);
|
|
2456
|
-
Aspect.assign(directive, match[2]);
|
|
2457
|
-
html += directive.createHTML(add);
|
|
2458
|
-
}
|
|
2459
|
-
else {
|
|
2460
|
-
html += currentValue;
|
|
3056
|
+
/**
|
|
3057
|
+
* Creates a template based on a set of static strings and dynamic values.
|
|
3058
|
+
* @param strings - The static strings to create the template with.
|
|
3059
|
+
* @param values - The dynamic values to create the template with.
|
|
3060
|
+
* @param policy - The DOMPolicy to associated with the template.
|
|
3061
|
+
* @returns A ViewTemplate.
|
|
3062
|
+
* @remarks
|
|
3063
|
+
* This API should not be used directly under normal circumstances because constructing
|
|
3064
|
+
* a template in this way, if not done properly, can open up the application to XSS
|
|
3065
|
+
* attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
|
|
3066
|
+
* and also be sure to manually sanitize all static strings particularly if they can
|
|
3067
|
+
* come from user input.
|
|
3068
|
+
*/
|
|
3069
|
+
static create(strings, values, policy) {
|
|
3070
|
+
let html = "";
|
|
3071
|
+
const factories = Object.create(null);
|
|
3072
|
+
const add = (factory) => {
|
|
3073
|
+
var _a;
|
|
3074
|
+
const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
|
|
3075
|
+
factories[id] = factory;
|
|
3076
|
+
return id;
|
|
3077
|
+
};
|
|
3078
|
+
for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
|
|
3079
|
+
const currentString = strings[i];
|
|
3080
|
+
let currentValue = values[i];
|
|
3081
|
+
let definition;
|
|
3082
|
+
html += currentString;
|
|
3083
|
+
if (isFunction(currentValue)) {
|
|
3084
|
+
currentValue = new HTMLBindingDirective(bind(currentValue));
|
|
2461
3085
|
}
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
html += createAspectedHTML(bind(() => currentValue, oneTime), currentString, add);
|
|
2465
|
-
}
|
|
2466
|
-
else {
|
|
2467
|
-
if (definition.aspected) {
|
|
2468
|
-
html += createAspectedHTML(currentValue, currentString, add);
|
|
3086
|
+
else if (currentValue instanceof Binding) {
|
|
3087
|
+
currentValue = new HTMLBindingDirective(currentValue);
|
|
2469
3088
|
}
|
|
2470
|
-
else {
|
|
2471
|
-
|
|
3089
|
+
else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
|
|
3090
|
+
const staticValue = currentValue;
|
|
3091
|
+
currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
|
|
2472
3092
|
}
|
|
3093
|
+
html += createHTML(currentValue, currentString, add, definition);
|
|
2473
3094
|
}
|
|
3095
|
+
return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3098
|
+
/**
|
|
3099
|
+
* Transforms a template literal string into a ViewTemplate.
|
|
3100
|
+
* @param strings - The string fragments that are interpolated with the values.
|
|
3101
|
+
* @param values - The values that are interpolated with the string fragments.
|
|
3102
|
+
* @remarks
|
|
3103
|
+
* The html helper supports interpolation of strings, numbers, binding expressions,
|
|
3104
|
+
* other template instances, and Directive instances.
|
|
3105
|
+
* @public
|
|
3106
|
+
*/
|
|
3107
|
+
const html = ((strings, ...values) => {
|
|
3108
|
+
if (Array.isArray(strings) && Array.isArray(strings.raw)) {
|
|
3109
|
+
return ViewTemplate.create(strings, values);
|
|
2474
3110
|
}
|
|
2475
|
-
|
|
2476
|
-
}
|
|
3111
|
+
throw FAST.error(1206 /* Message.directCallToHTMLTagNotAllowed */);
|
|
3112
|
+
});
|
|
3113
|
+
html.partial = (html) => {
|
|
3114
|
+
return new InlineTemplateDirective(html);
|
|
3115
|
+
};
|
|
2477
3116
|
|
|
2478
3117
|
/**
|
|
2479
3118
|
* The runtime behavior for template references.
|
|
@@ -2481,20 +3120,12 @@ function html(strings, ...values) {
|
|
|
2481
3120
|
*/
|
|
2482
3121
|
class RefDirective extends StatelessAttachedAttributeDirective {
|
|
2483
3122
|
/**
|
|
2484
|
-
* Bind this behavior
|
|
2485
|
-
* @param
|
|
2486
|
-
* @param context - The execution context that the binding is operating within.
|
|
2487
|
-
* @param targets - The targets that behaviors in a view can attach to.
|
|
3123
|
+
* Bind this behavior.
|
|
3124
|
+
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
2488
3125
|
*/
|
|
2489
|
-
bind(
|
|
2490
|
-
source[this.options] = targets[this.
|
|
3126
|
+
bind(controller) {
|
|
3127
|
+
controller.source[this.options] = controller.targets[this.targetNodeId];
|
|
2491
3128
|
}
|
|
2492
|
-
/**
|
|
2493
|
-
* Unbinds this behavior from the source.
|
|
2494
|
-
* @param source - The source to unbind from.
|
|
2495
|
-
*/
|
|
2496
|
-
/* eslint-disable-next-line @typescript-eslint/no-empty-function */
|
|
2497
|
-
unbind() { }
|
|
2498
3129
|
}
|
|
2499
3130
|
HTMLDirective.define(RefDirective);
|
|
2500
3131
|
/**
|
|
@@ -2506,27 +3137,34 @@ const ref = (propertyName) => new RefDirective(propertyName);
|
|
|
2506
3137
|
|
|
2507
3138
|
/**
|
|
2508
3139
|
* A directive that enables basic conditional rendering in a template.
|
|
2509
|
-
* @param
|
|
3140
|
+
* @param condition - The condition to test for rendering.
|
|
2510
3141
|
* @param templateOrTemplateBinding - The template or a binding that gets
|
|
2511
3142
|
* the template to render when the condition is true.
|
|
2512
3143
|
* @public
|
|
2513
3144
|
*/
|
|
2514
|
-
function when(
|
|
2515
|
-
const
|
|
3145
|
+
function when(condition, templateOrTemplateBinding) {
|
|
3146
|
+
const dataBinding = isFunction(condition) ? condition : () => condition;
|
|
3147
|
+
const templateBinding = isFunction(templateOrTemplateBinding)
|
|
2516
3148
|
? templateOrTemplateBinding
|
|
2517
3149
|
: () => templateOrTemplateBinding;
|
|
2518
|
-
return (source, context) =>
|
|
3150
|
+
return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
|
|
2519
3151
|
}
|
|
2520
3152
|
|
|
2521
3153
|
const defaultRepeatOptions = Object.freeze({
|
|
2522
3154
|
positioning: false,
|
|
2523
3155
|
recycle: true,
|
|
2524
3156
|
});
|
|
2525
|
-
function bindWithoutPositioning(view, items, index,
|
|
2526
|
-
view.
|
|
3157
|
+
function bindWithoutPositioning(view, items, index, controller) {
|
|
3158
|
+
view.context.parent = controller.source;
|
|
3159
|
+
view.context.parentContext = controller.context;
|
|
3160
|
+
view.bind(items[index]);
|
|
2527
3161
|
}
|
|
2528
|
-
function bindWithPositioning(view, items, index,
|
|
2529
|
-
view.
|
|
3162
|
+
function bindWithPositioning(view, items, index, controller) {
|
|
3163
|
+
view.context.parent = controller.source;
|
|
3164
|
+
view.context.parentContext = controller.context;
|
|
3165
|
+
view.context.length = items.length;
|
|
3166
|
+
view.context.index = index;
|
|
3167
|
+
view.bind(items[index]);
|
|
2530
3168
|
}
|
|
2531
3169
|
/**
|
|
2532
3170
|
* A behavior that renders a template for each item in an array.
|
|
@@ -2536,57 +3174,46 @@ class RepeatBehavior {
|
|
|
2536
3174
|
/**
|
|
2537
3175
|
* Creates an instance of RepeatBehavior.
|
|
2538
3176
|
* @param location - The location in the DOM to render the repeat.
|
|
2539
|
-
* @param
|
|
3177
|
+
* @param dataBinding - The array to render.
|
|
2540
3178
|
* @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
|
|
2541
3179
|
* @param templateBinding - The template to render for each item.
|
|
2542
3180
|
* @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
|
|
2543
3181
|
* @param options - Options used to turn on special repeat features.
|
|
2544
3182
|
*/
|
|
2545
|
-
constructor(
|
|
2546
|
-
this.
|
|
2547
|
-
this.itemsBinding = itemsBinding;
|
|
2548
|
-
this.templateBinding = templateBinding;
|
|
2549
|
-
this.options = options;
|
|
2550
|
-
this.source = null;
|
|
2551
|
-
this.views = [];
|
|
3183
|
+
constructor(directive) {
|
|
3184
|
+
this.directive = directive;
|
|
2552
3185
|
this.items = null;
|
|
2553
3186
|
this.itemsObserver = null;
|
|
2554
|
-
this.context = void 0;
|
|
2555
|
-
this.childContext = void 0;
|
|
2556
3187
|
this.bindView = bindWithoutPositioning;
|
|
2557
|
-
|
|
2558
|
-
this.
|
|
2559
|
-
|
|
3188
|
+
/** @internal */
|
|
3189
|
+
this.views = [];
|
|
3190
|
+
this.itemsBindingObserver = directive.dataBinding.createObserver(directive, this);
|
|
3191
|
+
this.templateBindingObserver = directive.templateBinding.createObserver(directive, this);
|
|
3192
|
+
if (directive.options.positioning) {
|
|
2560
3193
|
this.bindView = bindWithPositioning;
|
|
2561
3194
|
}
|
|
2562
3195
|
}
|
|
2563
3196
|
/**
|
|
2564
|
-
* Bind this behavior
|
|
2565
|
-
* @param
|
|
2566
|
-
* @param context - The execution context that the binding is operating within.
|
|
3197
|
+
* Bind this behavior.
|
|
3198
|
+
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
2567
3199
|
*/
|
|
2568
|
-
bind(
|
|
2569
|
-
this.
|
|
2570
|
-
this.
|
|
2571
|
-
this.
|
|
2572
|
-
this.
|
|
2573
|
-
this.template = this.templateBindingObserver.observe(source, this.context);
|
|
3200
|
+
bind(controller) {
|
|
3201
|
+
this.location = controller.targets[this.directive.targetNodeId];
|
|
3202
|
+
this.controller = controller;
|
|
3203
|
+
this.items = this.itemsBindingObserver.bind(controller);
|
|
3204
|
+
this.template = this.templateBindingObserver.bind(controller);
|
|
2574
3205
|
this.observeItems(true);
|
|
2575
3206
|
this.refreshAllViews();
|
|
3207
|
+
controller.onUnbind(this);
|
|
2576
3208
|
}
|
|
2577
3209
|
/**
|
|
2578
|
-
* Unbinds this behavior
|
|
2579
|
-
* @param source - The source to unbind from.
|
|
3210
|
+
* Unbinds this behavior.
|
|
2580
3211
|
*/
|
|
2581
3212
|
unbind() {
|
|
2582
|
-
this.source = null;
|
|
2583
|
-
this.items = null;
|
|
2584
3213
|
if (this.itemsObserver !== null) {
|
|
2585
3214
|
this.itemsObserver.unsubscribe(this);
|
|
2586
3215
|
}
|
|
2587
3216
|
this.unbindAllViews();
|
|
2588
|
-
this.itemsBindingObserver.dispose();
|
|
2589
|
-
this.templateBindingObserver.dispose();
|
|
2590
3217
|
}
|
|
2591
3218
|
/**
|
|
2592
3219
|
* Handles changes in the array, its items, and the repeat template.
|
|
@@ -2594,15 +3221,18 @@ class RepeatBehavior {
|
|
|
2594
3221
|
* @param args - The details about what was changed.
|
|
2595
3222
|
*/
|
|
2596
3223
|
handleChange(source, args) {
|
|
2597
|
-
if (
|
|
2598
|
-
this.items = this.itemsBindingObserver.
|
|
3224
|
+
if (args === this.itemsBindingObserver) {
|
|
3225
|
+
this.items = this.itemsBindingObserver.bind(this.controller);
|
|
2599
3226
|
this.observeItems();
|
|
2600
3227
|
this.refreshAllViews();
|
|
2601
3228
|
}
|
|
2602
|
-
else if (
|
|
2603
|
-
this.template = this.templateBindingObserver.
|
|
3229
|
+
else if (args === this.templateBindingObserver) {
|
|
3230
|
+
this.template = this.templateBindingObserver.bind(this.controller);
|
|
2604
3231
|
this.refreshAllViews(true);
|
|
2605
3232
|
}
|
|
3233
|
+
else if (!args[0]) {
|
|
3234
|
+
return;
|
|
3235
|
+
}
|
|
2606
3236
|
else if (args[0].reset) {
|
|
2607
3237
|
this.refreshAllViews();
|
|
2608
3238
|
}
|
|
@@ -2627,39 +3257,57 @@ class RepeatBehavior {
|
|
|
2627
3257
|
}
|
|
2628
3258
|
updateViews(splices) {
|
|
2629
3259
|
const views = this.views;
|
|
2630
|
-
const childContext = this.childContext;
|
|
2631
|
-
const totalRemoved = [];
|
|
2632
3260
|
const bindView = this.bindView;
|
|
2633
|
-
let removeDelta = 0;
|
|
2634
|
-
for (let i = 0, ii = splices.length; i < ii; ++i) {
|
|
2635
|
-
const splice = splices[i];
|
|
2636
|
-
const removed = splice.removed;
|
|
2637
|
-
totalRemoved.push(...views.splice(splice.index + removeDelta, removed.length));
|
|
2638
|
-
removeDelta -= splice.addedCount;
|
|
2639
|
-
}
|
|
2640
3261
|
const items = this.items;
|
|
2641
3262
|
const template = this.template;
|
|
3263
|
+
const controller = this.controller;
|
|
3264
|
+
const recycle = this.directive.options.recycle;
|
|
3265
|
+
const leftoverViews = [];
|
|
3266
|
+
let leftoverIndex = 0;
|
|
3267
|
+
let availableViews = 0;
|
|
2642
3268
|
for (let i = 0, ii = splices.length; i < ii; ++i) {
|
|
2643
3269
|
const splice = splices[i];
|
|
3270
|
+
const removed = splice.removed;
|
|
3271
|
+
let removeIndex = 0;
|
|
2644
3272
|
let addIndex = splice.index;
|
|
2645
3273
|
const end = addIndex + splice.addedCount;
|
|
3274
|
+
const removedViews = views.splice(splice.index, removed.length);
|
|
3275
|
+
const totalAvailableViews = (availableViews =
|
|
3276
|
+
leftoverViews.length + removedViews.length);
|
|
2646
3277
|
for (; addIndex < end; ++addIndex) {
|
|
2647
3278
|
const neighbor = views[addIndex];
|
|
2648
3279
|
const location = neighbor ? neighbor.firstChild : this.location;
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
3280
|
+
let view;
|
|
3281
|
+
if (recycle && availableViews > 0) {
|
|
3282
|
+
if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
|
|
3283
|
+
view = removedViews[removeIndex];
|
|
3284
|
+
removeIndex++;
|
|
3285
|
+
}
|
|
3286
|
+
else {
|
|
3287
|
+
view = leftoverViews[leftoverIndex];
|
|
3288
|
+
leftoverIndex++;
|
|
3289
|
+
}
|
|
3290
|
+
availableViews--;
|
|
3291
|
+
}
|
|
3292
|
+
else {
|
|
3293
|
+
view = template.create();
|
|
3294
|
+
}
|
|
2652
3295
|
views.splice(addIndex, 0, view);
|
|
2653
|
-
bindView(view, items, addIndex,
|
|
3296
|
+
bindView(view, items, addIndex, controller);
|
|
2654
3297
|
view.insertBefore(location);
|
|
2655
3298
|
}
|
|
3299
|
+
if (removedViews[removeIndex]) {
|
|
3300
|
+
leftoverViews.push(...removedViews.slice(removeIndex));
|
|
3301
|
+
}
|
|
2656
3302
|
}
|
|
2657
|
-
for (let i =
|
|
2658
|
-
|
|
3303
|
+
for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
|
|
3304
|
+
leftoverViews[i].dispose();
|
|
2659
3305
|
}
|
|
2660
|
-
if (this.options.positioning) {
|
|
2661
|
-
for (let i = 0,
|
|
2662
|
-
views[i].context
|
|
3306
|
+
if (this.directive.options.positioning) {
|
|
3307
|
+
for (let i = 0, viewsLength = views.length; i < viewsLength; ++i) {
|
|
3308
|
+
const context = views[i].context;
|
|
3309
|
+
context.length = viewsLength;
|
|
3310
|
+
context.index = i;
|
|
2663
3311
|
}
|
|
2664
3312
|
}
|
|
2665
3313
|
}
|
|
@@ -2668,11 +3316,11 @@ class RepeatBehavior {
|
|
|
2668
3316
|
const template = this.template;
|
|
2669
3317
|
const location = this.location;
|
|
2670
3318
|
const bindView = this.bindView;
|
|
2671
|
-
const
|
|
3319
|
+
const controller = this.controller;
|
|
2672
3320
|
let itemsLength = items.length;
|
|
2673
3321
|
let views = this.views;
|
|
2674
3322
|
let viewsLength = views.length;
|
|
2675
|
-
if (itemsLength === 0 || templateChanged || !this.options.recycle) {
|
|
3323
|
+
if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
|
|
2676
3324
|
// all views need to be removed
|
|
2677
3325
|
HTMLView.disposeContiguousBatch(views);
|
|
2678
3326
|
viewsLength = 0;
|
|
@@ -2682,7 +3330,7 @@ class RepeatBehavior {
|
|
|
2682
3330
|
this.views = views = new Array(itemsLength);
|
|
2683
3331
|
for (let i = 0; i < itemsLength; ++i) {
|
|
2684
3332
|
const view = template.create();
|
|
2685
|
-
bindView(view, items, i,
|
|
3333
|
+
bindView(view, items, i, controller);
|
|
2686
3334
|
views[i] = view;
|
|
2687
3335
|
view.insertBefore(location);
|
|
2688
3336
|
}
|
|
@@ -2693,11 +3341,11 @@ class RepeatBehavior {
|
|
|
2693
3341
|
for (; i < itemsLength; ++i) {
|
|
2694
3342
|
if (i < viewsLength) {
|
|
2695
3343
|
const view = views[i];
|
|
2696
|
-
bindView(view, items, i,
|
|
3344
|
+
bindView(view, items, i, controller);
|
|
2697
3345
|
}
|
|
2698
3346
|
else {
|
|
2699
3347
|
const view = template.create();
|
|
2700
|
-
bindView(view, items, i,
|
|
3348
|
+
bindView(view, items, i, controller);
|
|
2701
3349
|
views.push(view);
|
|
2702
3350
|
view.insertBefore(location);
|
|
2703
3351
|
}
|
|
@@ -2722,17 +3370,15 @@ class RepeatBehavior {
|
|
|
2722
3370
|
class RepeatDirective {
|
|
2723
3371
|
/**
|
|
2724
3372
|
* Creates an instance of RepeatDirective.
|
|
2725
|
-
* @param
|
|
3373
|
+
* @param dataBinding - The binding that provides the array to render.
|
|
2726
3374
|
* @param templateBinding - The template binding used to obtain a template to render for each item in the array.
|
|
2727
3375
|
* @param options - Options used to turn on special repeat features.
|
|
2728
3376
|
*/
|
|
2729
|
-
constructor(
|
|
2730
|
-
this.
|
|
3377
|
+
constructor(dataBinding, templateBinding, options) {
|
|
3378
|
+
this.dataBinding = dataBinding;
|
|
2731
3379
|
this.templateBinding = templateBinding;
|
|
2732
3380
|
this.options = options;
|
|
2733
3381
|
ArrayObserver.enable();
|
|
2734
|
-
this.isItemsBindingVolatile = Observable.isVolatileBinding(itemsBinding);
|
|
2735
|
-
this.isTemplateBindingVolatile = Observable.isVolatileBinding(templateBinding);
|
|
2736
3382
|
}
|
|
2737
3383
|
/**
|
|
2738
3384
|
* Creates a placeholder string based on the directive's index within the template.
|
|
@@ -2745,24 +3391,23 @@ class RepeatDirective {
|
|
|
2745
3391
|
* Creates a behavior for the provided target node.
|
|
2746
3392
|
* @param target - The node instance to create the behavior for.
|
|
2747
3393
|
*/
|
|
2748
|
-
createBehavior(
|
|
2749
|
-
return new RepeatBehavior(
|
|
3394
|
+
createBehavior() {
|
|
3395
|
+
return new RepeatBehavior(this);
|
|
2750
3396
|
}
|
|
2751
3397
|
}
|
|
2752
3398
|
HTMLDirective.define(RepeatDirective);
|
|
2753
3399
|
/**
|
|
2754
3400
|
* A directive that enables list rendering.
|
|
2755
|
-
* @param
|
|
2756
|
-
* @param
|
|
3401
|
+
* @param items - The array to render.
|
|
3402
|
+
* @param template - The template or a template binding used obtain a template
|
|
2757
3403
|
* to render for each item in the array.
|
|
2758
3404
|
* @param options - Options used to turn on special repeat features.
|
|
2759
3405
|
* @public
|
|
2760
3406
|
*/
|
|
2761
|
-
function repeat(
|
|
2762
|
-
const
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
return new RepeatDirective(itemsBinding, templateBinding, options);
|
|
3407
|
+
function repeat(items, template, options = defaultRepeatOptions) {
|
|
3408
|
+
const dataBinding = normalizeBinding(items);
|
|
3409
|
+
const templateBinding = normalizeBinding(template);
|
|
3410
|
+
return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
|
|
2766
3411
|
}
|
|
2767
3412
|
|
|
2768
3413
|
const selectElements = (value) => value.nodeType === 1;
|
|
@@ -2781,9 +3426,15 @@ const elements = (selector) => selector
|
|
|
2781
3426
|
* Internally used by the SlottedDirective and the ChildrenDirective.
|
|
2782
3427
|
*/
|
|
2783
3428
|
class NodeObservationDirective extends StatelessAttachedAttributeDirective {
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
3429
|
+
/**
|
|
3430
|
+
* The unique id of the factory.
|
|
3431
|
+
*/
|
|
3432
|
+
get id() {
|
|
3433
|
+
return this._id;
|
|
3434
|
+
}
|
|
3435
|
+
set id(value) {
|
|
3436
|
+
this._id = value;
|
|
3437
|
+
this._controllerProperty = `${value}-c`;
|
|
2787
3438
|
}
|
|
2788
3439
|
/**
|
|
2789
3440
|
* Bind this behavior to the source.
|
|
@@ -2791,11 +3442,12 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
|
|
|
2791
3442
|
* @param context - The execution context that the binding is operating within.
|
|
2792
3443
|
* @param targets - The targets that behaviors in a view can attach to.
|
|
2793
3444
|
*/
|
|
2794
|
-
bind(
|
|
2795
|
-
const target = targets[this.
|
|
2796
|
-
target[this.
|
|
2797
|
-
this.updateTarget(source, this.computeNodes(target));
|
|
3445
|
+
bind(controller) {
|
|
3446
|
+
const target = controller.targets[this.targetNodeId];
|
|
3447
|
+
target[this._controllerProperty] = controller;
|
|
3448
|
+
this.updateTarget(controller.source, this.computeNodes(target));
|
|
2798
3449
|
this.observe(target);
|
|
3450
|
+
controller.onUnbind(this);
|
|
2799
3451
|
}
|
|
2800
3452
|
/**
|
|
2801
3453
|
* Unbinds this behavior from the source.
|
|
@@ -2803,11 +3455,11 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
|
|
|
2803
3455
|
* @param context - The execution context that the binding is operating within.
|
|
2804
3456
|
* @param targets - The targets that behaviors in a view can attach to.
|
|
2805
3457
|
*/
|
|
2806
|
-
unbind(
|
|
2807
|
-
const target = targets[this.
|
|
2808
|
-
this.updateTarget(source, emptyArray);
|
|
3458
|
+
unbind(controller) {
|
|
3459
|
+
const target = controller.targets[this.targetNodeId];
|
|
3460
|
+
this.updateTarget(controller.source, emptyArray);
|
|
2809
3461
|
this.disconnect(target);
|
|
2810
|
-
target[this.
|
|
3462
|
+
target[this._controllerProperty] = null;
|
|
2811
3463
|
}
|
|
2812
3464
|
/**
|
|
2813
3465
|
* Gets the data source for the target.
|
|
@@ -2815,7 +3467,7 @@ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
|
|
|
2815
3467
|
* @returns The source.
|
|
2816
3468
|
*/
|
|
2817
3469
|
getSource(target) {
|
|
2818
|
-
return target[this.
|
|
3470
|
+
return target[this._controllerProperty].source;
|
|
2819
3471
|
}
|
|
2820
3472
|
/**
|
|
2821
3473
|
* Updates the source property with the computed nodes.
|
|
@@ -2911,9 +3563,13 @@ class ChildrenDirective extends NodeObservationDirective {
|
|
|
2911
3563
|
* @param target - The target to observe.
|
|
2912
3564
|
*/
|
|
2913
3565
|
observe(target) {
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
3566
|
+
let observer = target[this.observerProperty];
|
|
3567
|
+
if (!observer) {
|
|
3568
|
+
observer = new MutationObserver(this.handleEvent);
|
|
3569
|
+
observer.toJSON = noop;
|
|
3570
|
+
observer.target = target;
|
|
3571
|
+
target[this.observerProperty] = observer;
|
|
3572
|
+
}
|
|
2917
3573
|
observer.observe(target, this.options);
|
|
2918
3574
|
}
|
|
2919
3575
|
/**
|
|
@@ -2954,6 +3610,16 @@ function children(propertyOrOptions) {
|
|
|
2954
3610
|
|
|
2955
3611
|
const booleanMode = "boolean";
|
|
2956
3612
|
const reflectMode = "reflect";
|
|
3613
|
+
/**
|
|
3614
|
+
* Metadata used to configure a custom attribute's behavior.
|
|
3615
|
+
* @public
|
|
3616
|
+
*/
|
|
3617
|
+
const AttributeConfiguration = Object.freeze({
|
|
3618
|
+
/**
|
|
3619
|
+
* Locates all attribute configurations associated with a type.
|
|
3620
|
+
*/
|
|
3621
|
+
locate: createMetadataLocator(),
|
|
3622
|
+
});
|
|
2957
3623
|
/**
|
|
2958
3624
|
* A {@link ValueConverter} that converts to and from `boolean` values.
|
|
2959
3625
|
* @remarks
|
|
@@ -2974,6 +3640,20 @@ const booleanConverter = {
|
|
|
2974
3640
|
: true;
|
|
2975
3641
|
},
|
|
2976
3642
|
};
|
|
3643
|
+
/**
|
|
3644
|
+
* A {@link ValueConverter} that converts to and from `boolean` values. `null`, `undefined`, `""`, and `void` values are converted to `null`.
|
|
3645
|
+
* @public
|
|
3646
|
+
*/
|
|
3647
|
+
const nullableBooleanConverter = {
|
|
3648
|
+
toView(value) {
|
|
3649
|
+
return typeof value === "boolean" ? value.toString() : "";
|
|
3650
|
+
},
|
|
3651
|
+
fromView(value) {
|
|
3652
|
+
return [null, undefined, void 0].includes(value)
|
|
3653
|
+
? null
|
|
3654
|
+
: booleanConverter.fromView(value);
|
|
3655
|
+
},
|
|
3656
|
+
};
|
|
2977
3657
|
function toNumber(value) {
|
|
2978
3658
|
if (value === null || value === undefined) {
|
|
2979
3659
|
return null;
|
|
@@ -3091,7 +3771,7 @@ class AttributeDefinition {
|
|
|
3091
3771
|
*/
|
|
3092
3772
|
static collect(Owner, ...attributeLists) {
|
|
3093
3773
|
const attributes = [];
|
|
3094
|
-
attributeLists.push(Owner
|
|
3774
|
+
attributeLists.push(AttributeConfiguration.locate(Owner));
|
|
3095
3775
|
for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
|
|
3096
3776
|
const list = attributeLists[i];
|
|
3097
3777
|
if (list === void 0) {
|
|
@@ -3121,9 +3801,7 @@ function attr(configOrTarget, prop) {
|
|
|
3121
3801
|
// - @attr({...opts})
|
|
3122
3802
|
config.property = $prop;
|
|
3123
3803
|
}
|
|
3124
|
-
|
|
3125
|
-
($target.constructor.attributes = []);
|
|
3126
|
-
attributes.push(config);
|
|
3804
|
+
AttributeConfiguration.locate($target.constructor).push(config);
|
|
3127
3805
|
}
|
|
3128
3806
|
if (arguments.length > 1) {
|
|
3129
3807
|
// Non invocation:
|
|
@@ -3141,25 +3819,24 @@ function attr(configOrTarget, prop) {
|
|
|
3141
3819
|
|
|
3142
3820
|
const defaultShadowOptions = { mode: "open" };
|
|
3143
3821
|
const defaultElementOptions = {};
|
|
3144
|
-
const
|
|
3822
|
+
const fastElementBaseTypes = new Set();
|
|
3823
|
+
const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry, () => createTypeRegistry());
|
|
3145
3824
|
/**
|
|
3146
3825
|
* Defines metadata for a FASTElement.
|
|
3147
3826
|
* @public
|
|
3148
3827
|
*/
|
|
3149
3828
|
class FASTElementDefinition {
|
|
3150
|
-
/**
|
|
3151
|
-
* Creates an instance of FASTElementDefinition.
|
|
3152
|
-
* @param type - The type this definition is being created for.
|
|
3153
|
-
* @param nameOrConfig - The name of the element to define or a config object
|
|
3154
|
-
* that describes the element to define.
|
|
3155
|
-
*/
|
|
3156
3829
|
constructor(type, nameOrConfig = type.definition) {
|
|
3830
|
+
var _a;
|
|
3831
|
+
this.platformDefined = false;
|
|
3157
3832
|
if (isString(nameOrConfig)) {
|
|
3158
3833
|
nameOrConfig = { name: nameOrConfig };
|
|
3159
3834
|
}
|
|
3160
3835
|
this.type = type;
|
|
3161
3836
|
this.name = nameOrConfig.name;
|
|
3162
3837
|
this.template = nameOrConfig.template;
|
|
3838
|
+
this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
|
|
3839
|
+
const proto = type.prototype;
|
|
3163
3840
|
const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
|
|
3164
3841
|
const observedAttributes = new Array(attributes.length);
|
|
3165
3842
|
const propertyLookup = {};
|
|
@@ -3169,9 +3846,13 @@ class FASTElementDefinition {
|
|
|
3169
3846
|
observedAttributes[i] = current.attribute;
|
|
3170
3847
|
propertyLookup[current.name] = current;
|
|
3171
3848
|
attributeLookup[current.attribute] = current;
|
|
3849
|
+
Observable.defineProperty(proto, current);
|
|
3172
3850
|
}
|
|
3851
|
+
Reflect.defineProperty(type, "observedAttributes", {
|
|
3852
|
+
value: observedAttributes,
|
|
3853
|
+
enumerable: true,
|
|
3854
|
+
});
|
|
3173
3855
|
this.attributes = attributes;
|
|
3174
|
-
this.observedAttributes = observedAttributes;
|
|
3175
3856
|
this.propertyLookup = propertyLookup;
|
|
3176
3857
|
this.attributeLookup = attributeLookup;
|
|
3177
3858
|
this.shadowOptions =
|
|
@@ -3184,20 +3865,14 @@ class FASTElementDefinition {
|
|
|
3184
3865
|
nameOrConfig.elementOptions === void 0
|
|
3185
3866
|
? defaultElementOptions
|
|
3186
3867
|
: Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
|
|
3187
|
-
this.styles =
|
|
3188
|
-
|
|
3189
|
-
? void 0
|
|
3190
|
-
: Array.isArray(nameOrConfig.styles)
|
|
3191
|
-
? new ElementStyles(nameOrConfig.styles)
|
|
3192
|
-
: nameOrConfig.styles instanceof ElementStyles
|
|
3193
|
-
? nameOrConfig.styles
|
|
3194
|
-
: new ElementStyles([nameOrConfig.styles]);
|
|
3868
|
+
this.styles = ElementStyles.normalize(nameOrConfig.styles);
|
|
3869
|
+
fastElementRegistry.register(this);
|
|
3195
3870
|
}
|
|
3196
3871
|
/**
|
|
3197
3872
|
* Indicates if this element has been defined in at least one registry.
|
|
3198
3873
|
*/
|
|
3199
3874
|
get isDefined() {
|
|
3200
|
-
return
|
|
3875
|
+
return this.platformDefined;
|
|
3201
3876
|
}
|
|
3202
3877
|
/**
|
|
3203
3878
|
* Defines a custom element based on this definition.
|
|
@@ -3205,24 +3880,35 @@ class FASTElementDefinition {
|
|
|
3205
3880
|
* @remarks
|
|
3206
3881
|
* This operation is idempotent per registry.
|
|
3207
3882
|
*/
|
|
3208
|
-
define(registry =
|
|
3883
|
+
define(registry = this.registry) {
|
|
3209
3884
|
const type = this.type;
|
|
3210
|
-
if (fastElementRegistry.register(this)) {
|
|
3211
|
-
const attributes = this.attributes;
|
|
3212
|
-
const proto = type.prototype;
|
|
3213
|
-
for (let i = 0, ii = attributes.length; i < ii; ++i) {
|
|
3214
|
-
Observable.defineProperty(proto, attributes[i]);
|
|
3215
|
-
}
|
|
3216
|
-
Reflect.defineProperty(type, "observedAttributes", {
|
|
3217
|
-
value: this.observedAttributes,
|
|
3218
|
-
enumerable: true,
|
|
3219
|
-
});
|
|
3220
|
-
}
|
|
3221
3885
|
if (!registry.get(this.name)) {
|
|
3886
|
+
this.platformDefined = true;
|
|
3222
3887
|
registry.define(this.name, type, this.elementOptions);
|
|
3223
3888
|
}
|
|
3224
3889
|
return this;
|
|
3225
3890
|
}
|
|
3891
|
+
/**
|
|
3892
|
+
* Creates an instance of FASTElementDefinition.
|
|
3893
|
+
* @param type - The type this definition is being created for.
|
|
3894
|
+
* @param nameOrDef - The name of the element to define or a config object
|
|
3895
|
+
* that describes the element to define.
|
|
3896
|
+
*/
|
|
3897
|
+
static compose(type, nameOrDef) {
|
|
3898
|
+
if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
|
|
3899
|
+
return new FASTElementDefinition(class extends type {
|
|
3900
|
+
}, nameOrDef);
|
|
3901
|
+
}
|
|
3902
|
+
return new FASTElementDefinition(type, nameOrDef);
|
|
3903
|
+
}
|
|
3904
|
+
/**
|
|
3905
|
+
* Registers a FASTElement base type.
|
|
3906
|
+
* @param type - The type to register as a base type.
|
|
3907
|
+
* @internal
|
|
3908
|
+
*/
|
|
3909
|
+
static registerBaseType(type) {
|
|
3910
|
+
fastElementBaseTypes.add(type);
|
|
3911
|
+
}
|
|
3226
3912
|
}
|
|
3227
3913
|
/**
|
|
3228
3914
|
* Gets the element definition associated with the specified type.
|
|
@@ -3235,22 +3921,23 @@ FASTElementDefinition.getByType = fastElementRegistry.getByType;
|
|
|
3235
3921
|
*/
|
|
3236
3922
|
FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
|
|
3237
3923
|
|
|
3238
|
-
const shadowRoots = new WeakMap();
|
|
3239
3924
|
const defaultEventOptions = {
|
|
3240
3925
|
bubbles: true,
|
|
3241
3926
|
composed: true,
|
|
3242
3927
|
cancelable: true,
|
|
3243
3928
|
};
|
|
3929
|
+
const isConnectedPropertyName = "isConnected";
|
|
3930
|
+
const shadowRoots = new WeakMap();
|
|
3244
3931
|
function getShadowRoot(element) {
|
|
3245
3932
|
var _a, _b;
|
|
3246
3933
|
return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
|
|
3247
3934
|
}
|
|
3248
|
-
|
|
3935
|
+
let elementControllerStrategy;
|
|
3249
3936
|
/**
|
|
3250
3937
|
* Controls the lifecycle and rendering of a `FASTElement`.
|
|
3251
3938
|
* @public
|
|
3252
3939
|
*/
|
|
3253
|
-
class
|
|
3940
|
+
class ElementController extends PropertyChangeNotifier {
|
|
3254
3941
|
/**
|
|
3255
3942
|
* Creates a Controller to control the specified element.
|
|
3256
3943
|
* @param element - The element to be controlled by this controller.
|
|
@@ -3261,12 +3948,18 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3261
3948
|
constructor(element, definition) {
|
|
3262
3949
|
super(element);
|
|
3263
3950
|
this.boundObservables = null;
|
|
3264
|
-
this.behaviors = null;
|
|
3265
3951
|
this.needsInitialization = true;
|
|
3266
3952
|
this.hasExistingShadowRoot = false;
|
|
3267
3953
|
this._template = null;
|
|
3268
|
-
this.
|
|
3269
|
-
|
|
3954
|
+
this.stage = 3 /* Stages.disconnected */;
|
|
3955
|
+
/**
|
|
3956
|
+
* A guard against connecting behaviors multiple times
|
|
3957
|
+
* during connect in scenarios where a behavior adds
|
|
3958
|
+
* another behavior during it's connectedCallback
|
|
3959
|
+
*/
|
|
3960
|
+
this.guardBehaviorConnection = false;
|
|
3961
|
+
this.behaviors = null;
|
|
3962
|
+
this._mainStyles = null;
|
|
3270
3963
|
/**
|
|
3271
3964
|
* This allows Observable.getNotifier(...) to return the Controller
|
|
3272
3965
|
* when the notifier for the Controller itself is being requested. The
|
|
@@ -3282,7 +3975,12 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3282
3975
|
* If `null` then the element is managing its own rendering.
|
|
3283
3976
|
*/
|
|
3284
3977
|
this.view = null;
|
|
3285
|
-
|
|
3978
|
+
/**
|
|
3979
|
+
* Opts out of JSON stringification.
|
|
3980
|
+
* @internal
|
|
3981
|
+
*/
|
|
3982
|
+
this.toJSON = noop;
|
|
3983
|
+
this.source = element;
|
|
3286
3984
|
this.definition = definition;
|
|
3287
3985
|
const shadowOptions = definition.shadowOptions;
|
|
3288
3986
|
if (shadowOptions !== void 0) {
|
|
@@ -3320,11 +4018,7 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3320
4018
|
*/
|
|
3321
4019
|
get isConnected() {
|
|
3322
4020
|
Observable.track(this, isConnectedPropertyName);
|
|
3323
|
-
return this.
|
|
3324
|
-
}
|
|
3325
|
-
setIsConnected(value) {
|
|
3326
|
-
this._isConnected = value;
|
|
3327
|
-
Observable.notify(this, isConnectedPropertyName);
|
|
4021
|
+
return this.stage === 1 /* Stages.connected */;
|
|
3328
4022
|
}
|
|
3329
4023
|
/**
|
|
3330
4024
|
* Gets/sets the template used to render the component.
|
|
@@ -3336,9 +4030,9 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3336
4030
|
// 1. Template overrides take top precedence.
|
|
3337
4031
|
if (this._template === null) {
|
|
3338
4032
|
const definition = this.definition;
|
|
3339
|
-
if (this.
|
|
4033
|
+
if (this.source.resolveTemplate) {
|
|
3340
4034
|
// 2. Allow for element instance overrides next.
|
|
3341
|
-
this._template = this.
|
|
4035
|
+
this._template = this.source.resolveTemplate();
|
|
3342
4036
|
}
|
|
3343
4037
|
else if (definition.template) {
|
|
3344
4038
|
// 3. Default to the static definition.
|
|
@@ -3357,56 +4051,104 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3357
4051
|
}
|
|
3358
4052
|
}
|
|
3359
4053
|
/**
|
|
3360
|
-
*
|
|
3361
|
-
*
|
|
3362
|
-
* This value can only be accurately read after connect but can be set at any time.
|
|
4054
|
+
* The main set of styles used for the component, independent
|
|
4055
|
+
* of any dynamically added styles.
|
|
3363
4056
|
*/
|
|
3364
|
-
get
|
|
4057
|
+
get mainStyles() {
|
|
3365
4058
|
var _a;
|
|
3366
4059
|
// 1. Styles overrides take top precedence.
|
|
3367
|
-
if (this.
|
|
4060
|
+
if (this._mainStyles === null) {
|
|
3368
4061
|
const definition = this.definition;
|
|
3369
|
-
if (this.
|
|
4062
|
+
if (this.source.resolveStyles) {
|
|
3370
4063
|
// 2. Allow for element instance overrides next.
|
|
3371
|
-
this.
|
|
4064
|
+
this._mainStyles = this.source.resolveStyles();
|
|
3372
4065
|
}
|
|
3373
4066
|
else if (definition.styles) {
|
|
3374
4067
|
// 3. Default to the static definition.
|
|
3375
|
-
this.
|
|
4068
|
+
this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
|
|
3376
4069
|
}
|
|
3377
4070
|
}
|
|
3378
|
-
return this.
|
|
4071
|
+
return this._mainStyles;
|
|
3379
4072
|
}
|
|
3380
|
-
set
|
|
3381
|
-
if (this.
|
|
4073
|
+
set mainStyles(value) {
|
|
4074
|
+
if (this._mainStyles === value) {
|
|
3382
4075
|
return;
|
|
3383
4076
|
}
|
|
3384
|
-
if (this.
|
|
3385
|
-
this.removeStyles(this.
|
|
4077
|
+
if (this._mainStyles !== null) {
|
|
4078
|
+
this.removeStyles(this._mainStyles);
|
|
3386
4079
|
}
|
|
3387
|
-
this.
|
|
4080
|
+
this._mainStyles = value;
|
|
3388
4081
|
if (!this.needsInitialization) {
|
|
3389
4082
|
this.addStyles(value);
|
|
3390
4083
|
}
|
|
3391
4084
|
}
|
|
4085
|
+
/**
|
|
4086
|
+
* Adds the behavior to the component.
|
|
4087
|
+
* @param behavior - The behavior to add.
|
|
4088
|
+
*/
|
|
4089
|
+
addBehavior(behavior) {
|
|
4090
|
+
var _a, _b;
|
|
4091
|
+
const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
|
|
4092
|
+
const count = (_b = targetBehaviors.get(behavior)) !== null && _b !== void 0 ? _b : 0;
|
|
4093
|
+
if (count === 0) {
|
|
4094
|
+
targetBehaviors.set(behavior, 1);
|
|
4095
|
+
behavior.addedCallback && behavior.addedCallback(this);
|
|
4096
|
+
if (behavior.connectedCallback &&
|
|
4097
|
+
!this.guardBehaviorConnection &&
|
|
4098
|
+
(this.stage === 1 /* Stages.connected */ || this.stage === 0 /* Stages.connecting */)) {
|
|
4099
|
+
behavior.connectedCallback(this);
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
else {
|
|
4103
|
+
targetBehaviors.set(behavior, count + 1);
|
|
4104
|
+
}
|
|
4105
|
+
}
|
|
4106
|
+
/**
|
|
4107
|
+
* Removes the behavior from the component.
|
|
4108
|
+
* @param behavior - The behavior to remove.
|
|
4109
|
+
* @param force - Forces removal even if this behavior was added more than once.
|
|
4110
|
+
*/
|
|
4111
|
+
removeBehavior(behavior, force = false) {
|
|
4112
|
+
const targetBehaviors = this.behaviors;
|
|
4113
|
+
if (targetBehaviors === null) {
|
|
4114
|
+
return;
|
|
4115
|
+
}
|
|
4116
|
+
const count = targetBehaviors.get(behavior);
|
|
4117
|
+
if (count === void 0) {
|
|
4118
|
+
return;
|
|
4119
|
+
}
|
|
4120
|
+
if (count === 1 || force) {
|
|
4121
|
+
targetBehaviors.delete(behavior);
|
|
4122
|
+
if (behavior.disconnectedCallback && this.stage !== 3 /* Stages.disconnected */) {
|
|
4123
|
+
behavior.disconnectedCallback(this);
|
|
4124
|
+
}
|
|
4125
|
+
behavior.removedCallback && behavior.removedCallback(this);
|
|
4126
|
+
}
|
|
4127
|
+
else {
|
|
4128
|
+
targetBehaviors.set(behavior, count - 1);
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
3392
4131
|
/**
|
|
3393
4132
|
* Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot.
|
|
3394
4133
|
* @param styles - The styles to add.
|
|
3395
4134
|
*/
|
|
3396
4135
|
addStyles(styles) {
|
|
4136
|
+
var _a;
|
|
3397
4137
|
if (!styles) {
|
|
3398
4138
|
return;
|
|
3399
4139
|
}
|
|
3400
|
-
const
|
|
3401
|
-
this.element.getRootNode();
|
|
4140
|
+
const source = this.source;
|
|
3402
4141
|
if (styles instanceof HTMLElement) {
|
|
4142
|
+
const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : this.source;
|
|
3403
4143
|
target.append(styles);
|
|
3404
4144
|
}
|
|
3405
|
-
else if (!styles.isAttachedTo(
|
|
4145
|
+
else if (!styles.isAttachedTo(source)) {
|
|
3406
4146
|
const sourceBehaviors = styles.behaviors;
|
|
3407
|
-
styles.addStylesTo(
|
|
4147
|
+
styles.addStylesTo(source);
|
|
3408
4148
|
if (sourceBehaviors !== null) {
|
|
3409
|
-
|
|
4149
|
+
for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
|
|
4150
|
+
this.addBehavior(sourceBehaviors[i]);
|
|
4151
|
+
}
|
|
3410
4152
|
}
|
|
3411
4153
|
}
|
|
3412
4154
|
}
|
|
@@ -3415,121 +4157,82 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3415
4157
|
* @param styles - the styles to remove.
|
|
3416
4158
|
*/
|
|
3417
4159
|
removeStyles(styles) {
|
|
4160
|
+
var _a;
|
|
3418
4161
|
if (!styles) {
|
|
3419
4162
|
return;
|
|
3420
4163
|
}
|
|
3421
|
-
const
|
|
3422
|
-
this.element.getRootNode();
|
|
4164
|
+
const source = this.source;
|
|
3423
4165
|
if (styles instanceof HTMLElement) {
|
|
4166
|
+
const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source;
|
|
3424
4167
|
target.removeChild(styles);
|
|
3425
4168
|
}
|
|
3426
|
-
else if (styles.isAttachedTo(
|
|
4169
|
+
else if (styles.isAttachedTo(source)) {
|
|
3427
4170
|
const sourceBehaviors = styles.behaviors;
|
|
3428
|
-
styles.removeStylesFrom(
|
|
4171
|
+
styles.removeStylesFrom(source);
|
|
3429
4172
|
if (sourceBehaviors !== null) {
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
}
|
|
3434
|
-
/**
|
|
3435
|
-
* Adds behaviors to this element.
|
|
3436
|
-
* @param behaviors - The behaviors to add.
|
|
3437
|
-
*/
|
|
3438
|
-
addBehaviors(behaviors) {
|
|
3439
|
-
var _a;
|
|
3440
|
-
const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
|
|
3441
|
-
const length = behaviors.length;
|
|
3442
|
-
const behaviorsToBind = [];
|
|
3443
|
-
for (let i = 0; i < length; ++i) {
|
|
3444
|
-
const behavior = behaviors[i];
|
|
3445
|
-
if (targetBehaviors.has(behavior)) {
|
|
3446
|
-
targetBehaviors.set(behavior, targetBehaviors.get(behavior) + 1);
|
|
3447
|
-
}
|
|
3448
|
-
else {
|
|
3449
|
-
targetBehaviors.set(behavior, 1);
|
|
3450
|
-
behaviorsToBind.push(behavior);
|
|
3451
|
-
}
|
|
3452
|
-
}
|
|
3453
|
-
if (this._isConnected) {
|
|
3454
|
-
const element = this.element;
|
|
3455
|
-
const context = ExecutionContext.default;
|
|
3456
|
-
for (let i = 0; i < behaviorsToBind.length; ++i) {
|
|
3457
|
-
behaviorsToBind[i].bind(element, context);
|
|
4173
|
+
for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
|
|
4174
|
+
this.addBehavior(sourceBehaviors[i]);
|
|
4175
|
+
}
|
|
3458
4176
|
}
|
|
3459
4177
|
}
|
|
3460
4178
|
}
|
|
3461
4179
|
/**
|
|
3462
|
-
*
|
|
3463
|
-
* @param behaviors - The behaviors to remove.
|
|
3464
|
-
* @param force - Forces unbinding of behaviors.
|
|
4180
|
+
* Runs connected lifecycle behavior on the associated element.
|
|
3465
4181
|
*/
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
if (targetBehaviors === null) {
|
|
4182
|
+
connect() {
|
|
4183
|
+
if (this.stage !== 3 /* Stages.disconnected */) {
|
|
3469
4184
|
return;
|
|
3470
4185
|
}
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
const
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
4186
|
+
this.stage = 0 /* Stages.connecting */;
|
|
4187
|
+
// If we have any observables that were bound, re-apply their values.
|
|
4188
|
+
if (this.boundObservables !== null) {
|
|
4189
|
+
const element = this.source;
|
|
4190
|
+
const boundObservables = this.boundObservables;
|
|
4191
|
+
const propertyNames = Object.keys(boundObservables);
|
|
4192
|
+
for (let i = 0, ii = propertyNames.length; i < ii; ++i) {
|
|
4193
|
+
const propertyName = propertyNames[i];
|
|
4194
|
+
element[propertyName] = boundObservables[propertyName];
|
|
3480
4195
|
}
|
|
4196
|
+
this.boundObservables = null;
|
|
3481
4197
|
}
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
for (
|
|
3486
|
-
|
|
4198
|
+
const behaviors = this.behaviors;
|
|
4199
|
+
if (behaviors !== null) {
|
|
4200
|
+
this.guardBehaviorConnection = true;
|
|
4201
|
+
for (const key of behaviors.keys()) {
|
|
4202
|
+
key.connectedCallback && key.connectedCallback(this);
|
|
3487
4203
|
}
|
|
4204
|
+
this.guardBehaviorConnection = false;
|
|
3488
4205
|
}
|
|
3489
|
-
}
|
|
3490
|
-
/**
|
|
3491
|
-
* Runs connected lifecycle behavior on the associated element.
|
|
3492
|
-
*/
|
|
3493
|
-
onConnectedCallback() {
|
|
3494
|
-
if (this._isConnected) {
|
|
3495
|
-
return;
|
|
3496
|
-
}
|
|
3497
|
-
const element = this.element;
|
|
3498
|
-
const context = ExecutionContext.default;
|
|
3499
4206
|
if (this.needsInitialization) {
|
|
3500
|
-
this.
|
|
4207
|
+
this.renderTemplate(this.template);
|
|
4208
|
+
this.addStyles(this.mainStyles);
|
|
4209
|
+
this.needsInitialization = false;
|
|
3501
4210
|
}
|
|
3502
4211
|
else if (this.view !== null) {
|
|
3503
|
-
this.view.bind(
|
|
3504
|
-
}
|
|
3505
|
-
const behaviors = this.behaviors;
|
|
3506
|
-
if (behaviors !== null) {
|
|
3507
|
-
for (const behavior of behaviors.keys()) {
|
|
3508
|
-
behavior.bind(element, context);
|
|
3509
|
-
}
|
|
4212
|
+
this.view.bind(this.source);
|
|
3510
4213
|
}
|
|
3511
|
-
this.
|
|
4214
|
+
this.stage = 1 /* Stages.connected */;
|
|
4215
|
+
Observable.notify(this, isConnectedPropertyName);
|
|
3512
4216
|
}
|
|
3513
4217
|
/**
|
|
3514
4218
|
* Runs disconnected lifecycle behavior on the associated element.
|
|
3515
4219
|
*/
|
|
3516
|
-
|
|
3517
|
-
if (
|
|
4220
|
+
disconnect() {
|
|
4221
|
+
if (this.stage !== 1 /* Stages.connected */) {
|
|
3518
4222
|
return;
|
|
3519
4223
|
}
|
|
3520
|
-
this.
|
|
3521
|
-
|
|
3522
|
-
if (view !== null) {
|
|
3523
|
-
view.unbind();
|
|
4224
|
+
this.stage = 2 /* Stages.disconnecting */;
|
|
4225
|
+
Observable.notify(this, isConnectedPropertyName);
|
|
4226
|
+
if (this.view !== null) {
|
|
4227
|
+
this.view.unbind();
|
|
3524
4228
|
}
|
|
3525
4229
|
const behaviors = this.behaviors;
|
|
3526
4230
|
if (behaviors !== null) {
|
|
3527
|
-
const
|
|
3528
|
-
|
|
3529
|
-
for (const behavior of behaviors.keys()) {
|
|
3530
|
-
behavior.unbind(element, context);
|
|
4231
|
+
for (const key of behaviors.keys()) {
|
|
4232
|
+
key.disconnectedCallback && key.disconnectedCallback(this);
|
|
3531
4233
|
}
|
|
3532
4234
|
}
|
|
4235
|
+
this.stage = 3 /* Stages.disconnected */;
|
|
3533
4236
|
}
|
|
3534
4237
|
/**
|
|
3535
4238
|
* Runs the attribute changed callback for the associated element.
|
|
@@ -3540,7 +4243,7 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3540
4243
|
onAttributeChangedCallback(name, oldValue, newValue) {
|
|
3541
4244
|
const attrDef = this.definition.attributeLookup[name];
|
|
3542
4245
|
if (attrDef !== void 0) {
|
|
3543
|
-
attrDef.onAttributeChangedCallback(this.
|
|
4246
|
+
attrDef.onAttributeChangedCallback(this.source, newValue);
|
|
3544
4247
|
}
|
|
3545
4248
|
}
|
|
3546
4249
|
/**
|
|
@@ -3552,33 +4255,17 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3552
4255
|
* Only emits events if connected.
|
|
3553
4256
|
*/
|
|
3554
4257
|
emit(type, detail, options) {
|
|
3555
|
-
if (this.
|
|
3556
|
-
return this.
|
|
4258
|
+
if (this.stage === 1 /* Stages.connected */) {
|
|
4259
|
+
return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
|
|
3557
4260
|
}
|
|
3558
4261
|
return false;
|
|
3559
4262
|
}
|
|
3560
|
-
finishInitialization() {
|
|
3561
|
-
const element = this.element;
|
|
3562
|
-
const boundObservables = this.boundObservables;
|
|
3563
|
-
// If we have any observables that were bound, re-apply their values.
|
|
3564
|
-
if (boundObservables !== null) {
|
|
3565
|
-
const propertyNames = Object.keys(boundObservables);
|
|
3566
|
-
for (let i = 0, ii = propertyNames.length; i < ii; ++i) {
|
|
3567
|
-
const propertyName = propertyNames[i];
|
|
3568
|
-
element[propertyName] = boundObservables[propertyName];
|
|
3569
|
-
}
|
|
3570
|
-
this.boundObservables = null;
|
|
3571
|
-
}
|
|
3572
|
-
this.renderTemplate(this.template);
|
|
3573
|
-
this.addStyles(this.styles);
|
|
3574
|
-
this.needsInitialization = false;
|
|
3575
|
-
}
|
|
3576
4263
|
renderTemplate(template) {
|
|
3577
4264
|
var _a;
|
|
3578
|
-
const element = this.element;
|
|
3579
4265
|
// When getting the host to render to, we start by looking
|
|
3580
4266
|
// up the shadow root. If there isn't one, then that means
|
|
3581
4267
|
// we're doing a Light DOM render to the element's direct children.
|
|
4268
|
+
const element = this.source;
|
|
3582
4269
|
const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
|
|
3583
4270
|
if (this.view !== null) {
|
|
3584
4271
|
// If there's already a view, we need to unbind and remove through dispose.
|
|
@@ -3595,6 +4282,8 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3595
4282
|
if (template) {
|
|
3596
4283
|
// If a new template was provided, render it.
|
|
3597
4284
|
this.view = template.render(element, host, element);
|
|
4285
|
+
this.view.sourceLifetime =
|
|
4286
|
+
SourceLifetime.coupled;
|
|
3598
4287
|
}
|
|
3599
4288
|
}
|
|
3600
4289
|
/**
|
|
@@ -3614,31 +4303,145 @@ class Controller extends PropertyChangeNotifier {
|
|
|
3614
4303
|
if (definition === void 0) {
|
|
3615
4304
|
throw FAST.error(1401 /* Message.missingElementDefinition */);
|
|
3616
4305
|
}
|
|
3617
|
-
return (element.$fastController = new
|
|
4306
|
+
return (element.$fastController = new elementControllerStrategy(element, definition));
|
|
4307
|
+
}
|
|
4308
|
+
/**
|
|
4309
|
+
* Sets the strategy that ElementController.forCustomElement uses to construct
|
|
4310
|
+
* ElementController instances for an element.
|
|
4311
|
+
* @param strategy - The strategy to use.
|
|
4312
|
+
*/
|
|
4313
|
+
static setStrategy(strategy) {
|
|
4314
|
+
elementControllerStrategy = strategy;
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
// Set default strategy for ElementController
|
|
4318
|
+
ElementController.setStrategy(ElementController);
|
|
4319
|
+
/**
|
|
4320
|
+
* Converts a styleTarget into the operative target. When the provided target is an Element
|
|
4321
|
+
* that is a FASTElement, the function will return the ShadowRoot for that element. Otherwise,
|
|
4322
|
+
* it will return the root node for the element.
|
|
4323
|
+
* @param target
|
|
4324
|
+
* @returns
|
|
4325
|
+
*/
|
|
4326
|
+
function normalizeStyleTarget(target) {
|
|
4327
|
+
var _a;
|
|
4328
|
+
if ("adoptedStyleSheets" in target) {
|
|
4329
|
+
return target;
|
|
4330
|
+
}
|
|
4331
|
+
else {
|
|
4332
|
+
return ((_a = getShadowRoot(target)) !== null && _a !== void 0 ? _a : target.getRootNode());
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
// Default StyleStrategy implementations are defined in this module because they
|
|
4336
|
+
// require access to element shadowRoots, and we don't want to leak shadowRoot
|
|
4337
|
+
// objects out of this module.
|
|
4338
|
+
/**
|
|
4339
|
+
* https://wicg.github.io/construct-stylesheets/
|
|
4340
|
+
* https://developers.google.com/web/updates/2019/02/constructable-stylesheets
|
|
4341
|
+
*
|
|
4342
|
+
* @internal
|
|
4343
|
+
*/
|
|
4344
|
+
class AdoptedStyleSheetsStrategy {
|
|
4345
|
+
constructor(styles) {
|
|
4346
|
+
const styleSheetCache = AdoptedStyleSheetsStrategy.styleSheetCache;
|
|
4347
|
+
this.sheets = styles.map((x) => {
|
|
4348
|
+
if (x instanceof CSSStyleSheet) {
|
|
4349
|
+
return x;
|
|
4350
|
+
}
|
|
4351
|
+
let sheet = styleSheetCache.get(x);
|
|
4352
|
+
if (sheet === void 0) {
|
|
4353
|
+
sheet = new CSSStyleSheet();
|
|
4354
|
+
sheet.replaceSync(x);
|
|
4355
|
+
styleSheetCache.set(x, sheet);
|
|
4356
|
+
}
|
|
4357
|
+
return sheet;
|
|
4358
|
+
});
|
|
4359
|
+
}
|
|
4360
|
+
addStylesTo(target) {
|
|
4361
|
+
const t = normalizeStyleTarget(target);
|
|
4362
|
+
t.adoptedStyleSheets = [...t.adoptedStyleSheets, ...this.sheets];
|
|
4363
|
+
}
|
|
4364
|
+
removeStylesFrom(target) {
|
|
4365
|
+
const t = normalizeStyleTarget(target);
|
|
4366
|
+
const sheets = this.sheets;
|
|
4367
|
+
t.adoptedStyleSheets = t.adoptedStyleSheets.filter((x) => sheets.indexOf(x) === -1);
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
4370
|
+
AdoptedStyleSheetsStrategy.styleSheetCache = new Map();
|
|
4371
|
+
let id = 0;
|
|
4372
|
+
const nextStyleId = () => `fast-${++id}`;
|
|
4373
|
+
function usableStyleTarget(target) {
|
|
4374
|
+
return target === document ? document.body : target;
|
|
4375
|
+
}
|
|
4376
|
+
/**
|
|
4377
|
+
* @internal
|
|
4378
|
+
*/
|
|
4379
|
+
class StyleElementStrategy {
|
|
4380
|
+
constructor(styles) {
|
|
4381
|
+
this.styles = styles;
|
|
4382
|
+
this.styleClass = nextStyleId();
|
|
4383
|
+
}
|
|
4384
|
+
addStylesTo(target) {
|
|
4385
|
+
target = usableStyleTarget(normalizeStyleTarget(target));
|
|
4386
|
+
const styles = this.styles;
|
|
4387
|
+
const styleClass = this.styleClass;
|
|
4388
|
+
for (let i = 0; i < styles.length; i++) {
|
|
4389
|
+
const element = document.createElement("style");
|
|
4390
|
+
element.innerHTML = styles[i];
|
|
4391
|
+
element.className = styleClass;
|
|
4392
|
+
target.append(element);
|
|
4393
|
+
}
|
|
4394
|
+
}
|
|
4395
|
+
removeStylesFrom(target) {
|
|
4396
|
+
target = usableStyleTarget(normalizeStyleTarget(target));
|
|
4397
|
+
const styles = target.querySelectorAll(`.${this.styleClass}`);
|
|
4398
|
+
for (let i = 0, ii = styles.length; i < ii; ++i) {
|
|
4399
|
+
target.removeChild(styles[i]);
|
|
4400
|
+
}
|
|
3618
4401
|
}
|
|
3619
4402
|
}
|
|
4403
|
+
ElementStyles.setDefaultStrategy(ElementStyles.supportsAdoptedStyleSheets
|
|
4404
|
+
? AdoptedStyleSheetsStrategy
|
|
4405
|
+
: StyleElementStrategy);
|
|
3620
4406
|
|
|
3621
4407
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
3622
4408
|
function createFASTElement(BaseType) {
|
|
3623
|
-
|
|
4409
|
+
const type = class extends BaseType {
|
|
3624
4410
|
constructor() {
|
|
3625
4411
|
/* eslint-disable-next-line */
|
|
3626
4412
|
super();
|
|
3627
|
-
|
|
4413
|
+
ElementController.forCustomElement(this);
|
|
3628
4414
|
}
|
|
3629
4415
|
$emit(type, detail, options) {
|
|
3630
4416
|
return this.$fastController.emit(type, detail, options);
|
|
3631
4417
|
}
|
|
3632
4418
|
connectedCallback() {
|
|
3633
|
-
this.$fastController.
|
|
4419
|
+
this.$fastController.connect();
|
|
3634
4420
|
}
|
|
3635
4421
|
disconnectedCallback() {
|
|
3636
|
-
this.$fastController.
|
|
4422
|
+
this.$fastController.disconnect();
|
|
3637
4423
|
}
|
|
3638
4424
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
3639
4425
|
this.$fastController.onAttributeChangedCallback(name, oldValue, newValue);
|
|
3640
4426
|
}
|
|
3641
4427
|
};
|
|
4428
|
+
FASTElementDefinition.registerBaseType(type);
|
|
4429
|
+
return type;
|
|
4430
|
+
}
|
|
4431
|
+
function compose(type, nameOrDef) {
|
|
4432
|
+
if (isFunction(type)) {
|
|
4433
|
+
return FASTElementDefinition.compose(type, nameOrDef);
|
|
4434
|
+
}
|
|
4435
|
+
return FASTElementDefinition.compose(this, type);
|
|
4436
|
+
}
|
|
4437
|
+
function define(type, nameOrDef) {
|
|
4438
|
+
if (isFunction(type)) {
|
|
4439
|
+
return FASTElementDefinition.compose(type, nameOrDef).define().type;
|
|
4440
|
+
}
|
|
4441
|
+
return FASTElementDefinition.compose(this, type).define().type;
|
|
4442
|
+
}
|
|
4443
|
+
function from(BaseType) {
|
|
4444
|
+
return createFASTElement(BaseType);
|
|
3642
4445
|
}
|
|
3643
4446
|
/**
|
|
3644
4447
|
* A minimal base class for FASTElements that also provides
|
|
@@ -3651,26 +4454,19 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
|
|
|
3651
4454
|
* provided base type.
|
|
3652
4455
|
* @param BaseType - The base element type to inherit from.
|
|
3653
4456
|
*/
|
|
3654
|
-
from
|
|
3655
|
-
return createFASTElement(BaseType);
|
|
3656
|
-
},
|
|
4457
|
+
from,
|
|
3657
4458
|
/**
|
|
3658
4459
|
* Defines a platform custom element based on the provided type and definition.
|
|
3659
4460
|
* @param type - The custom element type to define.
|
|
3660
4461
|
* @param nameOrDef - The name of the element to define or a definition object
|
|
3661
4462
|
* that describes the element to define.
|
|
3662
4463
|
*/
|
|
3663
|
-
define
|
|
3664
|
-
return this.metadata(type, nameOrDef).define().type;
|
|
3665
|
-
},
|
|
4464
|
+
define,
|
|
3666
4465
|
/**
|
|
3667
4466
|
* Defines metadata for a FASTElement which can be used to later define the element.
|
|
3668
|
-
* IMPORTANT: This API will be renamed to "compose" in a future beta.
|
|
3669
4467
|
* @public
|
|
3670
4468
|
*/
|
|
3671
|
-
|
|
3672
|
-
return new FASTElementDefinition(type, nameOrDef);
|
|
3673
|
-
},
|
|
4469
|
+
compose,
|
|
3674
4470
|
});
|
|
3675
4471
|
/**
|
|
3676
4472
|
* Decorator: Defines a platform custom element based on `FASTElement`.
|
|
@@ -3681,8 +4477,10 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
|
|
|
3681
4477
|
function customElement(nameOrDef) {
|
|
3682
4478
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
3683
4479
|
return function (type) {
|
|
3684
|
-
|
|
4480
|
+
define(type, nameOrDef);
|
|
3685
4481
|
};
|
|
3686
4482
|
}
|
|
3687
4483
|
|
|
3688
|
-
|
|
4484
|
+
DOM.setPolicy(DOMPolicy.create());
|
|
4485
|
+
|
|
4486
|
+
export { ArrayObserver, AttributeConfiguration, AttributeDefinition, Binding, CSSDirective, ChildrenDirective, Compiler, DOM, DOMAspect, ElementController, ElementStyles, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, InlineTemplateDirective, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RepeatBehavior, RepeatDirective, SlottedDirective, SourceLifetime, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, Updates, ViewTemplate, attr, bind, booleanConverter, children, createMetadataLocator, createTypeRegistry, css, cssDirective, customElement, elements, emptyArray, html, htmlDirective, lengthOf, listener, normalizeBinding, nullableBooleanConverter, nullableNumberConverter, observable, oneTime, ref, repeat, slotted, volatile, when };
|