@microsoft/fast-element 2.0.0-beta.17 → 2.0.0-beta.18
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 +21 -0
- package/CHANGELOG.md +10 -1
- package/dist/dts/dom-policy.d.ts +68 -0
- package/dist/dts/dom.d.ts +116 -0
- package/dist/dts/index.d.ts +3 -2
- 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 +15 -24
- package/dist/dts/polyfills.d.ts +0 -1
- package/dist/dts/templating/binding-signal.d.ts +3 -1
- package/dist/dts/templating/binding-two-way.d.ts +3 -1
- package/dist/dts/templating/binding.d.ts +16 -5
- package/dist/dts/templating/compiler.d.ts +11 -13
- package/dist/dts/templating/dangerous-html.d.ts +18 -0
- package/dist/dts/templating/html-directive.d.ts +50 -119
- package/dist/dts/templating/node-observation.d.ts +11 -1
- package/dist/dts/templating/ref.d.ts +4 -0
- package/dist/dts/templating/render.d.ts +30 -6
- package/dist/dts/templating/repeat.d.ts +1 -5
- package/dist/dts/templating/template.d.ts +39 -13
- package/dist/dts/templating/view.d.ts +2 -2
- package/dist/dts/utilities.d.ts +39 -0
- package/dist/esm/components/attributes.js +1 -1
- package/dist/esm/debug.js +4 -1
- package/dist/esm/dom-policy.js +337 -0
- package/dist/esm/dom.js +117 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.rollup.debug.js +3 -1
- package/dist/esm/index.rollup.js +3 -1
- package/dist/esm/platform.js +1 -1
- package/dist/esm/polyfills.js +3 -7
- package/dist/esm/templating/binding-signal.js +3 -2
- package/dist/esm/templating/binding-two-way.js +3 -2
- package/dist/esm/templating/binding.js +31 -54
- package/dist/esm/templating/compiler.js +31 -38
- package/dist/esm/templating/dangerous-html.js +23 -0
- package/dist/esm/templating/html-directive.js +38 -135
- package/dist/esm/templating/node-observation.js +14 -8
- package/dist/esm/templating/ref.js +1 -1
- package/dist/esm/templating/render.js +17 -6
- package/dist/esm/templating/repeat.js +2 -6
- package/dist/esm/templating/template.js +81 -56
- package/dist/esm/testing/fixture.js +1 -1
- package/dist/esm/utilities.js +68 -0
- package/dist/fast-element.api.json +1088 -608
- package/dist/fast-element.d.ts +190 -147
- package/dist/fast-element.debug.js +708 -384
- package/dist/fast-element.debug.min.js +1 -1
- package/dist/fast-element.js +679 -358
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +190 -147
- package/docs/api-report.md +66 -56
- package/package.json +5 -1
- package/yarn-error.log +177 -0
- package/dist/dts/templating/dom.d.ts +0 -41
- package/dist/esm/templating/dom.js +0 -49
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import { isFunction, noop } from "../interfaces.js";
|
|
2
2
|
import { ExecutionContext, Observable, } from "../observation/observable.js";
|
|
3
3
|
import { FAST } from "../platform.js";
|
|
4
|
-
import { DOM } from "
|
|
5
|
-
import {
|
|
6
|
-
import { Markup
|
|
7
|
-
const createInnerHTMLBinding = globalThis.TrustedHTML
|
|
8
|
-
? (binding) => (s, c) => {
|
|
9
|
-
const value = binding(s, c);
|
|
10
|
-
if (value instanceof TrustedHTML) {
|
|
11
|
-
return value;
|
|
12
|
-
}
|
|
13
|
-
throw FAST.error(1202 /* Message.bindingInnerHTMLRequiresTrustedTypes */);
|
|
14
|
-
}
|
|
15
|
-
: (binding) => binding;
|
|
4
|
+
import { DOM, DOMAspect } from "../dom.js";
|
|
5
|
+
import { Binding, HTMLDirective, } from "./html-directive.js";
|
|
6
|
+
import { Markup } from "./markup.js";
|
|
16
7
|
class OnChangeBinding extends Binding {
|
|
17
8
|
createObserver(_, subscriber) {
|
|
18
9
|
return Observable.binding(this.evaluate, subscriber, this.isVolatile);
|
|
@@ -125,8 +116,14 @@ function updateTokenList(target, aspect, value) {
|
|
|
125
116
|
}
|
|
126
117
|
}
|
|
127
118
|
}
|
|
128
|
-
const
|
|
129
|
-
|
|
119
|
+
const sinkLookup = {
|
|
120
|
+
[DOMAspect.attribute]: DOM.setAttribute,
|
|
121
|
+
[DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
|
|
122
|
+
[DOMAspect.property]: (t, a, v) => (t[a] = v),
|
|
123
|
+
[DOMAspect.content]: updateContent,
|
|
124
|
+
[DOMAspect.tokenList]: updateTokenList,
|
|
125
|
+
[DOMAspect.event]: () => void 0,
|
|
126
|
+
};
|
|
130
127
|
/**
|
|
131
128
|
* A directive that applies bindings.
|
|
132
129
|
* @public
|
|
@@ -139,15 +136,10 @@ export class HTMLBindingDirective {
|
|
|
139
136
|
constructor(dataBinding) {
|
|
140
137
|
this.dataBinding = dataBinding;
|
|
141
138
|
this.updateTarget = null;
|
|
142
|
-
/**
|
|
143
|
-
* The unique id of the factory.
|
|
144
|
-
*/
|
|
145
|
-
this.id = nextId();
|
|
146
139
|
/**
|
|
147
140
|
* The type of aspect to target.
|
|
148
141
|
*/
|
|
149
|
-
this.aspectType =
|
|
150
|
-
this.data = `${this.id}-d`;
|
|
142
|
+
this.aspectType = DOMAspect.content;
|
|
151
143
|
}
|
|
152
144
|
/**
|
|
153
145
|
* Creates HTML to be used within a template.
|
|
@@ -160,45 +152,28 @@ export class HTMLBindingDirective {
|
|
|
160
152
|
* Creates a behavior.
|
|
161
153
|
*/
|
|
162
154
|
createBehavior() {
|
|
155
|
+
var _a;
|
|
163
156
|
if (this.updateTarget === null) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
case 1:
|
|
169
|
-
this.updateTarget = DOM.setAttribute;
|
|
170
|
-
break;
|
|
171
|
-
case 2:
|
|
172
|
-
this.updateTarget = DOM.setBooleanAttribute;
|
|
173
|
-
break;
|
|
174
|
-
case 3:
|
|
175
|
-
this.updateTarget = setProperty;
|
|
176
|
-
break;
|
|
177
|
-
case 4:
|
|
178
|
-
this.updateTarget = updateContent;
|
|
179
|
-
break;
|
|
180
|
-
case 5:
|
|
181
|
-
this.updateTarget = updateTokenList;
|
|
182
|
-
break;
|
|
183
|
-
case 6:
|
|
184
|
-
this.updateTarget = eventTarget;
|
|
185
|
-
break;
|
|
186
|
-
default:
|
|
187
|
-
throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
|
|
157
|
+
const sink = sinkLookup[this.aspectType];
|
|
158
|
+
const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
|
|
159
|
+
if (!sink) {
|
|
160
|
+
throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
|
|
188
161
|
}
|
|
162
|
+
this.data = `${this.id}-d`;
|
|
163
|
+
this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
|
|
189
164
|
}
|
|
190
165
|
return this;
|
|
191
166
|
}
|
|
192
167
|
/** @internal */
|
|
193
168
|
bind(controller) {
|
|
194
169
|
var _a;
|
|
195
|
-
const target = controller.targets[this.
|
|
196
|
-
switch (this.
|
|
197
|
-
case
|
|
170
|
+
const target = controller.targets[this.targetNodeId];
|
|
171
|
+
switch (this.aspectType) {
|
|
172
|
+
case DOMAspect.event:
|
|
198
173
|
target[this.data] = controller;
|
|
199
174
|
target.addEventListener(this.targetAspect, this, this.dataBinding.options);
|
|
200
175
|
break;
|
|
201
|
-
case
|
|
176
|
+
case DOMAspect.content:
|
|
202
177
|
controller.onUnbind(this);
|
|
203
178
|
// intentional fall through
|
|
204
179
|
default:
|
|
@@ -211,7 +186,7 @@ export class HTMLBindingDirective {
|
|
|
211
186
|
}
|
|
212
187
|
/** @internal */
|
|
213
188
|
unbind(controller) {
|
|
214
|
-
const target = controller.targets[this.
|
|
189
|
+
const target = controller.targets[this.targetNodeId];
|
|
215
190
|
const view = target.$fastView;
|
|
216
191
|
if (view !== void 0 && view.isComposed) {
|
|
217
192
|
view.unbind();
|
|
@@ -241,21 +216,23 @@ HTMLDirective.define(HTMLBindingDirective, { aspected: true });
|
|
|
241
216
|
/**
|
|
242
217
|
* Creates an standard binding.
|
|
243
218
|
* @param expression - The binding to refresh when changed.
|
|
219
|
+
* @param policy - The security policy to associate with th binding.
|
|
244
220
|
* @param isVolatile - Indicates whether the binding is volatile or not.
|
|
245
221
|
* @returns A binding configuration.
|
|
246
222
|
* @public
|
|
247
223
|
*/
|
|
248
|
-
export function bind(expression, isVolatile = Observable.isVolatileBinding(expression)) {
|
|
249
|
-
return new OnChangeBinding(expression, isVolatile);
|
|
224
|
+
export function bind(expression, policy, isVolatile = Observable.isVolatileBinding(expression)) {
|
|
225
|
+
return new OnChangeBinding(expression, policy, isVolatile);
|
|
250
226
|
}
|
|
251
227
|
/**
|
|
252
228
|
* Creates a one time binding
|
|
253
229
|
* @param expression - The binding to refresh when signaled.
|
|
230
|
+
* @param policy - The security policy to associate with th binding.
|
|
254
231
|
* @returns A binding configuration.
|
|
255
232
|
* @public
|
|
256
233
|
*/
|
|
257
|
-
export function oneTime(expression) {
|
|
258
|
-
return new OneTimeBinding(expression);
|
|
234
|
+
export function oneTime(expression, policy) {
|
|
235
|
+
return new OneTimeBinding(expression, policy);
|
|
259
236
|
}
|
|
260
237
|
/**
|
|
261
238
|
* Creates an event listener binding.
|
|
@@ -265,7 +242,7 @@ export function oneTime(expression) {
|
|
|
265
242
|
* @public
|
|
266
243
|
*/
|
|
267
244
|
export function listener(expression, options) {
|
|
268
|
-
const config = new OnChangeBinding(expression
|
|
245
|
+
const config = new OnChangeBinding(expression);
|
|
269
246
|
config.options = options;
|
|
270
247
|
return config;
|
|
271
248
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { isFunction, isString } from "../interfaces.js";
|
|
2
2
|
import { FAST } from "../platform.js";
|
|
3
|
-
import {
|
|
3
|
+
import { DOM } from "../dom.js";
|
|
4
|
+
import { nextId, Parser } from "./markup.js";
|
|
4
5
|
import { HTMLBindingDirective, oneTime } from "./binding.js";
|
|
5
|
-
import {
|
|
6
|
+
import { HTMLDirective, } from "./html-directive.js";
|
|
6
7
|
import { HTMLView } from "./view.js";
|
|
7
8
|
const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
|
|
8
9
|
const descriptorCache = {};
|
|
@@ -28,20 +29,25 @@ const warningHost = new Proxy(document.createElement("div"), {
|
|
|
28
29
|
},
|
|
29
30
|
});
|
|
30
31
|
class CompilationContext {
|
|
31
|
-
constructor(fragment, directives) {
|
|
32
|
+
constructor(fragment, directives, policy) {
|
|
32
33
|
this.fragment = fragment;
|
|
33
34
|
this.directives = directives;
|
|
35
|
+
this.policy = policy;
|
|
34
36
|
this.proto = null;
|
|
35
37
|
this.nodeIds = new Set();
|
|
36
38
|
this.descriptors = {};
|
|
37
39
|
this.factories = [];
|
|
38
40
|
}
|
|
39
|
-
addFactory(factory, parentId, nodeId, targetIndex) {
|
|
41
|
+
addFactory(factory, parentId, nodeId, targetIndex, tagName) {
|
|
42
|
+
var _a, _b;
|
|
40
43
|
if (!this.nodeIds.has(nodeId)) {
|
|
41
44
|
this.nodeIds.add(nodeId);
|
|
42
45
|
this.addTargetDescriptor(parentId, nodeId, targetIndex);
|
|
43
46
|
}
|
|
44
|
-
factory.
|
|
47
|
+
factory.id = (_a = factory.id) !== null && _a !== void 0 ? _a : nextId();
|
|
48
|
+
factory.targetNodeId = nodeId;
|
|
49
|
+
factory.targetTagName = tagName;
|
|
50
|
+
factory.policy = (_b = factory.policy) !== null && _b !== void 0 ? _b : this.policy;
|
|
45
51
|
this.factories.push(factory);
|
|
46
52
|
}
|
|
47
53
|
freeze() {
|
|
@@ -94,19 +100,19 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
|
|
|
94
100
|
let result = null;
|
|
95
101
|
if (parseResult === null) {
|
|
96
102
|
if (includeBasicValues) {
|
|
97
|
-
result = new HTMLBindingDirective(oneTime(() => attrValue));
|
|
98
|
-
|
|
103
|
+
result = new HTMLBindingDirective(oneTime(() => attrValue, context.policy));
|
|
104
|
+
HTMLDirective.assignAspect(result, attr.name);
|
|
99
105
|
}
|
|
100
106
|
}
|
|
101
107
|
else {
|
|
102
108
|
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
103
|
-
result = Compiler.aggregate(parseResult);
|
|
109
|
+
result = Compiler.aggregate(parseResult, context.policy);
|
|
104
110
|
}
|
|
105
111
|
if (result !== null) {
|
|
106
112
|
node.removeAttributeNode(attr);
|
|
107
113
|
i--;
|
|
108
114
|
ii--;
|
|
109
|
-
context.addFactory(result, parentId, nodeId, nodeIndex);
|
|
115
|
+
context.addFactory(result, parentId, nodeId, nodeIndex, node.tagName);
|
|
110
116
|
}
|
|
111
117
|
}
|
|
112
118
|
}
|
|
@@ -131,8 +137,8 @@ function compileContent(context, node, parentId, nodeId, nodeIndex) {
|
|
|
131
137
|
}
|
|
132
138
|
else {
|
|
133
139
|
currentNode.textContent = " ";
|
|
134
|
-
|
|
135
|
-
context.addFactory(currentPart, parentId, nodeId, nodeIndex);
|
|
140
|
+
HTMLDirective.assignAspect(currentPart);
|
|
141
|
+
context.addFactory(currentPart, parentId, nodeId, nodeIndex, null);
|
|
136
142
|
}
|
|
137
143
|
lastNode = currentNode;
|
|
138
144
|
}
|
|
@@ -164,7 +170,7 @@ function compileNode(context, parentId, node, nodeIndex) {
|
|
|
164
170
|
if (parts !== null) {
|
|
165
171
|
context.addFactory(
|
|
166
172
|
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
167
|
-
Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
|
|
173
|
+
Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
|
|
168
174
|
}
|
|
169
175
|
break;
|
|
170
176
|
}
|
|
@@ -178,45 +184,28 @@ function isMarker(node, directives) {
|
|
|
178
184
|
Parser.parse(node.data, directives) !== null);
|
|
179
185
|
}
|
|
180
186
|
const templateTag = "TEMPLATE";
|
|
181
|
-
const policyOptions = { createHTML: html => html };
|
|
182
|
-
let htmlPolicy = globalThis.trustedTypes
|
|
183
|
-
? globalThis.trustedTypes.createPolicy("fast-html", policyOptions)
|
|
184
|
-
: policyOptions;
|
|
185
|
-
const fastHTMLPolicy = htmlPolicy;
|
|
186
187
|
/**
|
|
187
188
|
* Common APIs related to compilation.
|
|
188
189
|
* @public
|
|
189
190
|
*/
|
|
190
191
|
export const Compiler = {
|
|
191
|
-
/**
|
|
192
|
-
* Sets the HTML trusted types policy used by the compiler.
|
|
193
|
-
* @param policy - The policy to set for HTML.
|
|
194
|
-
* @remarks
|
|
195
|
-
* This API can only be called once, for security reasons. It should be
|
|
196
|
-
* called by the application developer at the start of their program.
|
|
197
|
-
*/
|
|
198
|
-
setHTMLPolicy(policy) {
|
|
199
|
-
if (htmlPolicy !== fastHTMLPolicy) {
|
|
200
|
-
throw FAST.error(1201 /* Message.onlySetHTMLPolicyOnce */);
|
|
201
|
-
}
|
|
202
|
-
htmlPolicy = policy;
|
|
203
|
-
},
|
|
204
192
|
/**
|
|
205
193
|
* Compiles a template and associated directives into a compilation
|
|
206
194
|
* result which can be used to create views.
|
|
207
195
|
* @param html - The html string or template element to compile.
|
|
208
|
-
* @param
|
|
196
|
+
* @param factories - The behavior factories referenced by the template.
|
|
197
|
+
* @param policy - The security policy to compile the html with.
|
|
209
198
|
* @remarks
|
|
210
199
|
* The template that is provided for compilation is altered in-place
|
|
211
200
|
* and cannot be compiled again. If the original template must be preserved,
|
|
212
201
|
* it is recommended that you clone the original and pass the clone to this API.
|
|
213
202
|
* @public
|
|
214
203
|
*/
|
|
215
|
-
compile(html,
|
|
204
|
+
compile(html, factories, policy = DOM.policy) {
|
|
216
205
|
let template;
|
|
217
206
|
if (isString(html)) {
|
|
218
207
|
template = document.createElement(templateTag);
|
|
219
|
-
template.innerHTML =
|
|
208
|
+
template.innerHTML = policy.createHTML(html);
|
|
220
209
|
const fec = template.content.firstElementChild;
|
|
221
210
|
if (fec !== null && fec.tagName === templateTag) {
|
|
222
211
|
template = fec;
|
|
@@ -227,18 +216,18 @@ export const Compiler = {
|
|
|
227
216
|
}
|
|
228
217
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
|
|
229
218
|
const fragment = document.adoptNode(template.content);
|
|
230
|
-
const context = new CompilationContext(fragment,
|
|
219
|
+
const context = new CompilationContext(fragment, factories, policy);
|
|
231
220
|
compileAttributes(context, "", template, /* host */ "h", 0, true);
|
|
232
221
|
if (
|
|
233
222
|
// If the first node in a fragment is a marker, that means it's an unstable first node,
|
|
234
223
|
// because something like a when, repeat, etc. could add nodes before the marker.
|
|
235
224
|
// To mitigate this, we insert a stable first node. However, if we insert a node,
|
|
236
225
|
// that will alter the result of the TreeWalker. So, we also need to offset the target index.
|
|
237
|
-
isMarker(fragment.firstChild,
|
|
226
|
+
isMarker(fragment.firstChild, factories) ||
|
|
238
227
|
// Or if there is only one node and a directive, it means the template's content
|
|
239
228
|
// is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
|
|
240
229
|
// the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
|
|
241
|
-
(fragment.childNodes.length === 1 && Object.keys(
|
|
230
|
+
(fragment.childNodes.length === 1 && Object.keys(factories).length > 0)) {
|
|
242
231
|
fragment.insertBefore(document.createComment(""), fragment.firstChild);
|
|
243
232
|
}
|
|
244
233
|
compileChildren(context, fragment, /* root */ "r");
|
|
@@ -257,15 +246,17 @@ export const Compiler = {
|
|
|
257
246
|
* Aggregates an array of strings and directives into a single directive.
|
|
258
247
|
* @param parts - A heterogeneous array of static strings interspersed with
|
|
259
248
|
* directives.
|
|
249
|
+
* @param policy - The security policy to use with the aggregated bindings.
|
|
260
250
|
* @returns A single inline directive that aggregates the behavior of all the parts.
|
|
261
251
|
*/
|
|
262
|
-
aggregate(parts) {
|
|
252
|
+
aggregate(parts, policy = DOM.policy) {
|
|
263
253
|
if (parts.length === 1) {
|
|
264
254
|
return parts[0];
|
|
265
255
|
}
|
|
266
256
|
let sourceAspect;
|
|
267
257
|
let binding;
|
|
268
258
|
let isVolatile = false;
|
|
259
|
+
let bindingPolicy = void 0;
|
|
269
260
|
const partCount = parts.length;
|
|
270
261
|
const finalParts = parts.map((x) => {
|
|
271
262
|
if (isString(x)) {
|
|
@@ -274,6 +265,7 @@ export const Compiler = {
|
|
|
274
265
|
sourceAspect = x.sourceAspect || sourceAspect;
|
|
275
266
|
binding = x.dataBinding || binding;
|
|
276
267
|
isVolatile = isVolatile || x.dataBinding.isVolatile;
|
|
268
|
+
bindingPolicy = bindingPolicy || x.dataBinding.policy;
|
|
277
269
|
return x.dataBinding.evaluate;
|
|
278
270
|
});
|
|
279
271
|
const expression = (scope, context) => {
|
|
@@ -285,8 +277,9 @@ export const Compiler = {
|
|
|
285
277
|
};
|
|
286
278
|
binding.evaluate = expression;
|
|
287
279
|
binding.isVolatile = isVolatile;
|
|
280
|
+
binding.policy = bindingPolicy !== null && bindingPolicy !== void 0 ? bindingPolicy : policy;
|
|
288
281
|
const directive = new HTMLBindingDirective(binding);
|
|
289
|
-
|
|
282
|
+
HTMLDirective.assignAspect(directive, sourceAspect);
|
|
290
283
|
return directive;
|
|
291
284
|
},
|
|
292
285
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { HTMLDirective } from "./html-directive.js";
|
|
2
|
+
/**
|
|
3
|
+
* A directive capable of injecting static HTML platform runtime protection.
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
export class DangerousHTMLDirective {
|
|
7
|
+
constructor(html) {
|
|
8
|
+
this.html = html;
|
|
9
|
+
}
|
|
10
|
+
createHTML() {
|
|
11
|
+
return this.html;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
HTMLDirective.define(DangerousHTMLDirective);
|
|
15
|
+
/**
|
|
16
|
+
* Injects static HTML without platform protection.
|
|
17
|
+
* @param html - The html to injection.
|
|
18
|
+
* @returns A DangerousHTMLDirective.
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export function dangerousHTML(html) {
|
|
22
|
+
return new DangerousHTMLDirective(html);
|
|
23
|
+
}
|
|
@@ -1,68 +1,7 @@
|
|
|
1
|
+
import { DOMAspect } from "../dom.js";
|
|
1
2
|
import { noop } from "../interfaces.js";
|
|
2
|
-
import { ExecutionContext, } from "../observation/observable.js";
|
|
3
3
|
import { createTypeRegistry } from "../platform.js";
|
|
4
|
-
import { Markup
|
|
5
|
-
/**
|
|
6
|
-
* Bridges between ViewBehaviors and HostBehaviors, enabling a host to
|
|
7
|
-
* control ViewBehaviors.
|
|
8
|
-
* @public
|
|
9
|
-
*/
|
|
10
|
-
export const ViewBehaviorOrchestrator = Object.freeze({
|
|
11
|
-
/**
|
|
12
|
-
* Creates a ViewBehaviorOrchestrator.
|
|
13
|
-
* @param source - The source to to associate behaviors with.
|
|
14
|
-
* @returns A ViewBehaviorOrchestrator.
|
|
15
|
-
*/
|
|
16
|
-
create(source) {
|
|
17
|
-
const behaviors = [];
|
|
18
|
-
const targets = {};
|
|
19
|
-
let unbindables = null;
|
|
20
|
-
let isConnected = false;
|
|
21
|
-
return {
|
|
22
|
-
source,
|
|
23
|
-
context: ExecutionContext.default,
|
|
24
|
-
targets,
|
|
25
|
-
get isBound() {
|
|
26
|
-
return isConnected;
|
|
27
|
-
},
|
|
28
|
-
addBehaviorFactory(factory, target) {
|
|
29
|
-
const nodeId = factory.nodeId || (factory.nodeId = nextId());
|
|
30
|
-
factory.id || (factory.id = nextId());
|
|
31
|
-
this.addTarget(nodeId, target);
|
|
32
|
-
this.addBehavior(factory.createBehavior());
|
|
33
|
-
},
|
|
34
|
-
addTarget(nodeId, target) {
|
|
35
|
-
targets[nodeId] = target;
|
|
36
|
-
},
|
|
37
|
-
addBehavior(behavior) {
|
|
38
|
-
behaviors.push(behavior);
|
|
39
|
-
if (isConnected) {
|
|
40
|
-
behavior.bind(this);
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
onUnbind(unbindable) {
|
|
44
|
-
if (unbindables === null) {
|
|
45
|
-
unbindables = [];
|
|
46
|
-
}
|
|
47
|
-
unbindables.push(unbindable);
|
|
48
|
-
},
|
|
49
|
-
connectedCallback(controller) {
|
|
50
|
-
if (!isConnected) {
|
|
51
|
-
isConnected = true;
|
|
52
|
-
behaviors.forEach(x => x.bind(this));
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
disconnectedCallback(controller) {
|
|
56
|
-
if (isConnected) {
|
|
57
|
-
isConnected = false;
|
|
58
|
-
if (unbindables !== null) {
|
|
59
|
-
unbindables.forEach(x => x.unbind(this));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
},
|
|
65
|
-
});
|
|
4
|
+
import { Markup } from "./markup.js";
|
|
66
5
|
const registry = createTypeRegistry();
|
|
67
6
|
/**
|
|
68
7
|
* Instructs the template engine to apply behavior to a node.
|
|
@@ -90,67 +29,6 @@ export const HTMLDirective = Object.freeze({
|
|
|
90
29
|
registry.register(options);
|
|
91
30
|
return type;
|
|
92
31
|
},
|
|
93
|
-
});
|
|
94
|
-
/**
|
|
95
|
-
* Decorator: Defines an HTMLDirective.
|
|
96
|
-
* @param options - Provides options that specify the directive's application.
|
|
97
|
-
* @public
|
|
98
|
-
*/
|
|
99
|
-
export function htmlDirective(options) {
|
|
100
|
-
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
101
|
-
return function (type) {
|
|
102
|
-
HTMLDirective.define(type, options);
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Captures a binding expression along with related information and capabilities.
|
|
107
|
-
*
|
|
108
|
-
* @public
|
|
109
|
-
*/
|
|
110
|
-
export class Binding {
|
|
111
|
-
/**
|
|
112
|
-
* Creates a binding.
|
|
113
|
-
* @param evaluate - Evaluates the binding.
|
|
114
|
-
* @param isVolatile - Indicates whether the binding is volatile.
|
|
115
|
-
*/
|
|
116
|
-
constructor(evaluate, isVolatile = false) {
|
|
117
|
-
this.evaluate = evaluate;
|
|
118
|
-
this.isVolatile = isVolatile;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* The type of HTML aspect to target.
|
|
123
|
-
* @public
|
|
124
|
-
*/
|
|
125
|
-
export const Aspect = Object.freeze({
|
|
126
|
-
/**
|
|
127
|
-
* Not aspected.
|
|
128
|
-
*/
|
|
129
|
-
none: 0,
|
|
130
|
-
/**
|
|
131
|
-
* An attribute.
|
|
132
|
-
*/
|
|
133
|
-
attribute: 1,
|
|
134
|
-
/**
|
|
135
|
-
* A boolean attribute.
|
|
136
|
-
*/
|
|
137
|
-
booleanAttribute: 2,
|
|
138
|
-
/**
|
|
139
|
-
* A property.
|
|
140
|
-
*/
|
|
141
|
-
property: 3,
|
|
142
|
-
/**
|
|
143
|
-
* Content
|
|
144
|
-
*/
|
|
145
|
-
content: 4,
|
|
146
|
-
/**
|
|
147
|
-
* A token list.
|
|
148
|
-
*/
|
|
149
|
-
tokenList: 5,
|
|
150
|
-
/**
|
|
151
|
-
* An event.
|
|
152
|
-
*/
|
|
153
|
-
event: 6,
|
|
154
32
|
/**
|
|
155
33
|
*
|
|
156
34
|
* @param directive - The directive to assign the aspect to.
|
|
@@ -158,9 +36,9 @@ export const Aspect = Object.freeze({
|
|
|
158
36
|
* @remarks
|
|
159
37
|
* If a falsy value is provided, then the content aspect will be assigned.
|
|
160
38
|
*/
|
|
161
|
-
|
|
39
|
+
assignAspect(directive, value) {
|
|
162
40
|
if (!value) {
|
|
163
|
-
directive.aspectType =
|
|
41
|
+
directive.aspectType = DOMAspect.content;
|
|
164
42
|
return;
|
|
165
43
|
}
|
|
166
44
|
directive.sourceAspect = value;
|
|
@@ -169,24 +47,53 @@ export const Aspect = Object.freeze({
|
|
|
169
47
|
directive.targetAspect = value.substring(1);
|
|
170
48
|
directive.aspectType =
|
|
171
49
|
directive.targetAspect === "classList"
|
|
172
|
-
?
|
|
173
|
-
:
|
|
50
|
+
? DOMAspect.tokenList
|
|
51
|
+
: DOMAspect.property;
|
|
174
52
|
break;
|
|
175
53
|
case "?":
|
|
176
54
|
directive.targetAspect = value.substring(1);
|
|
177
|
-
directive.aspectType =
|
|
55
|
+
directive.aspectType = DOMAspect.booleanAttribute;
|
|
178
56
|
break;
|
|
179
57
|
case "@":
|
|
180
58
|
directive.targetAspect = value.substring(1);
|
|
181
|
-
directive.aspectType =
|
|
59
|
+
directive.aspectType = DOMAspect.event;
|
|
182
60
|
break;
|
|
183
61
|
default:
|
|
184
62
|
directive.targetAspect = value;
|
|
185
|
-
directive.aspectType =
|
|
63
|
+
directive.aspectType = DOMAspect.attribute;
|
|
186
64
|
break;
|
|
187
65
|
}
|
|
188
66
|
},
|
|
189
67
|
});
|
|
68
|
+
/**
|
|
69
|
+
* Decorator: Defines an HTMLDirective.
|
|
70
|
+
* @param options - Provides options that specify the directive's application.
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
export function htmlDirective(options) {
|
|
74
|
+
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
75
|
+
return function (type) {
|
|
76
|
+
HTMLDirective.define(type, options);
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Captures a binding expression along with related information and capabilities.
|
|
81
|
+
*
|
|
82
|
+
* @public
|
|
83
|
+
*/
|
|
84
|
+
export class Binding {
|
|
85
|
+
/**
|
|
86
|
+
* Creates a binding.
|
|
87
|
+
* @param evaluate - Evaluates the binding.
|
|
88
|
+
* @param policy - The security policy to associate with this binding.
|
|
89
|
+
* @param isVolatile - Indicates whether the binding is volatile.
|
|
90
|
+
*/
|
|
91
|
+
constructor(evaluate, policy, isVolatile = false) {
|
|
92
|
+
this.evaluate = evaluate;
|
|
93
|
+
this.policy = policy;
|
|
94
|
+
this.isVolatile = isVolatile;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
190
97
|
/**
|
|
191
98
|
* A base class used for attribute directives that don't need internal state.
|
|
192
99
|
* @public
|
|
@@ -198,10 +105,6 @@ export class StatelessAttachedAttributeDirective {
|
|
|
198
105
|
*/
|
|
199
106
|
constructor(options) {
|
|
200
107
|
this.options = options;
|
|
201
|
-
/**
|
|
202
|
-
* The unique id of the factory.
|
|
203
|
-
*/
|
|
204
|
-
this.id = nextId();
|
|
205
108
|
/**
|
|
206
109
|
* Opts out of JSON stringification.
|
|
207
110
|
* @internal
|
|
@@ -16,9 +16,15 @@ export const elements = (selector) => selector
|
|
|
16
16
|
* Internally used by the SlottedDirective and the ChildrenDirective.
|
|
17
17
|
*/
|
|
18
18
|
export class NodeObservationDirective extends StatelessAttachedAttributeDirective {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/**
|
|
20
|
+
* The unique id of the factory.
|
|
21
|
+
*/
|
|
22
|
+
get id() {
|
|
23
|
+
return this._id;
|
|
24
|
+
}
|
|
25
|
+
set id(value) {
|
|
26
|
+
this._id = value;
|
|
27
|
+
this._controllerProperty = `${value}-c`;
|
|
22
28
|
}
|
|
23
29
|
/**
|
|
24
30
|
* Bind this behavior to the source.
|
|
@@ -27,8 +33,8 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
|
|
|
27
33
|
* @param targets - The targets that behaviors in a view can attach to.
|
|
28
34
|
*/
|
|
29
35
|
bind(controller) {
|
|
30
|
-
const target = controller.targets[this.
|
|
31
|
-
target[this.
|
|
36
|
+
const target = controller.targets[this.targetNodeId];
|
|
37
|
+
target[this._controllerProperty] = controller;
|
|
32
38
|
this.updateTarget(controller.source, this.computeNodes(target));
|
|
33
39
|
this.observe(target);
|
|
34
40
|
controller.onUnbind(this);
|
|
@@ -40,10 +46,10 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
|
|
|
40
46
|
* @param targets - The targets that behaviors in a view can attach to.
|
|
41
47
|
*/
|
|
42
48
|
unbind(controller) {
|
|
43
|
-
const target = controller.targets[this.
|
|
49
|
+
const target = controller.targets[this.targetNodeId];
|
|
44
50
|
this.updateTarget(controller.source, emptyArray);
|
|
45
51
|
this.disconnect(target);
|
|
46
|
-
target[this.
|
|
52
|
+
target[this._controllerProperty] = null;
|
|
47
53
|
}
|
|
48
54
|
/**
|
|
49
55
|
* Gets the data source for the target.
|
|
@@ -51,7 +57,7 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
|
|
|
51
57
|
* @returns The source.
|
|
52
58
|
*/
|
|
53
59
|
getSource(target) {
|
|
54
|
-
return target[this.
|
|
60
|
+
return target[this._controllerProperty].source;
|
|
55
61
|
}
|
|
56
62
|
/**
|
|
57
63
|
* Updates the source property with the computed nodes.
|
|
@@ -9,7 +9,7 @@ export class RefDirective extends StatelessAttachedAttributeDirective {
|
|
|
9
9
|
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
10
10
|
*/
|
|
11
11
|
bind(controller) {
|
|
12
|
-
controller.source[this.options] = controller.targets[this.
|
|
12
|
+
controller.source[this.options] = controller.targets[this.targetNodeId];
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
HTMLDirective.define(RefDirective);
|