@lushly-dev/fast-element 3.0.1-preview.0 → 3.0.1-preview.1
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.md +6 -0
- package/dist/command/command.api.json +7857 -0
- package/dist/context/context.api.json +1084 -0
- package/dist/declarative/declarative.api.json +8130 -0
- package/dist/definition-schema/definition-schema.api.json +2689 -0
- package/dist/di/di.api.json +4945 -0
- package/dist/dts/arrays.d.ts +2 -0
- package/dist/dts/binding/binding.d.ts +59 -0
- package/dist/dts/binding/exports.d.ts +9 -0
- package/dist/dts/binding/normalize.d.ts +9 -0
- package/dist/dts/binding/one-time.d.ts +11 -0
- package/dist/dts/binding/one-way.d.ts +20 -0
- package/dist/dts/binding/signal.d.ts +36 -0
- package/dist/dts/binding/two-way.d.ts +44 -0
- package/dist/dts/command/behavior.d.ts +94 -0
- package/dist/dts/command/behaviors/confirm-behavior.d.ts +139 -0
- package/dist/dts/command/behaviors/undo-behavior.d.ts +178 -0
- package/dist/dts/command/command.d.ts +79 -0
- package/dist/dts/command/component-registry.d.ts +50 -0
- package/dist/dts/command/components/confirm-dialog.d.ts +117 -0
- package/dist/dts/command/index.d.ts +21 -0
- package/dist/dts/command/registry.d.ts +201 -0
- package/dist/dts/command/schema-validation.d.ts +45 -0
- package/dist/dts/command/types.d.ts +332 -0
- package/dist/dts/command/validation.d.ts +116 -0
- package/dist/dts/command/weak-ref.d.ts +11 -0
- package/dist/dts/components/attributes.d.ts +153 -0
- package/dist/dts/components/definition-schema-transforms.d.ts +9 -0
- package/dist/dts/components/element-controller.d.ts +349 -0
- package/dist/dts/components/element-hydration.d.ts +2 -0
- package/dist/dts/components/enable-hydration.d.ts +66 -0
- package/dist/dts/components/fast-definitions.d.ts +211 -0
- package/dist/dts/components/fast-element-registry.d.ts +23 -0
- package/dist/dts/components/fast-element.d.ts +91 -0
- package/dist/dts/components/hydration-tracker.d.ts +110 -0
- package/dist/dts/components/hydration.d.ts +52 -0
- package/dist/dts/components/schema.d.ts +205 -0
- package/dist/dts/context.d.ts +171 -0
- package/dist/dts/debug.d.ts +5 -0
- package/dist/dts/declarative/attribute-map.d.ts +58 -0
- package/dist/dts/declarative/debug.d.ts +4 -0
- package/dist/dts/declarative/index.d.ts +14 -0
- package/dist/dts/declarative/interfaces.d.ts +8 -0
- package/dist/dts/declarative/observer-map-utilities.d.ts +58 -0
- package/dist/dts/declarative/observer-map.d.ts +89 -0
- package/dist/dts/declarative/runtime.d.ts +5 -0
- package/dist/dts/declarative/syntax.d.ts +21 -0
- package/dist/dts/declarative/template-bridge.d.ts +33 -0
- package/dist/dts/declarative/template-parser.d.ts +98 -0
- package/dist/dts/declarative/template.d.ts +9 -0
- package/dist/dts/declarative/utilities.d.ts +358 -0
- package/dist/dts/definition-schema/api.d.ts +42 -0
- package/dist/dts/definition-schema/cem.d.ts +78 -0
- package/dist/dts/definition-schema/extension.d.ts +18 -0
- package/dist/dts/definition-schema/extract.d.ts +38 -0
- package/dist/dts/definition-schema/index.d.ts +9 -0
- package/dist/dts/definition-schema/storage.d.ts +35 -0
- package/dist/dts/definition-schema/types.d.ts +176 -0
- package/dist/dts/dev-mode.d.ts +13 -0
- package/dist/dts/di/di.d.ts +894 -0
- package/dist/dts/dom-policy.d.ts +101 -0
- package/dist/dts/dom.d.ts +88 -0
- package/dist/dts/hydration/diagnostics.d.ts +93 -0
- package/dist/dts/hydration/exports.d.ts +18 -0
- package/dist/dts/hydration/hydration-debugger.d.ts +35 -0
- package/dist/dts/hydration/messages.d.ts +62 -0
- package/dist/dts/hydration/runtime.d.ts +7 -0
- package/dist/dts/hydration/target-builder.d.ts +107 -0
- package/dist/dts/index.d.ts +51 -0
- package/dist/dts/index.debug.d.ts +1 -0
- package/dist/dts/index.rollup.d.ts +1 -0
- package/dist/dts/index.rollup.debug.d.ts +1 -0
- package/dist/dts/interfaces.d.ts +106 -0
- package/dist/dts/metadata.d.ts +26 -0
- package/dist/dts/observation/arrays.d.ts +248 -0
- package/dist/dts/observation/notifier.d.ts +127 -0
- package/dist/dts/observation/observable.d.ts +276 -0
- package/dist/dts/observation/update-queue.d.ts +40 -0
- package/dist/dts/observation/volatile.d.ts +9 -0
- package/dist/dts/platform.d.ts +87 -0
- package/dist/dts/registry.d.ts +2 -0
- package/dist/dts/render.d.ts +7 -0
- package/dist/dts/schema.d.ts +1 -0
- package/dist/dts/scoped/detection.d.ts +55 -0
- package/dist/dts/scoped/index.d.ts +68 -0
- package/dist/dts/scoped/mixin.d.ts +118 -0
- package/dist/dts/scoped/registry.d.ts +76 -0
- package/dist/dts/scoped/types.d.ts +48 -0
- 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 +50 -0
- package/dist/dts/styles/css.d.ts +34 -0
- package/dist/dts/styles/element-styles.d.ts +61 -0
- package/dist/dts/styles/host.d.ts +65 -0
- package/dist/dts/styles/style-strategy.d.ts +42 -0
- package/dist/dts/templating/children.d.ts +62 -0
- package/dist/dts/templating/compiler.d.ts +54 -0
- package/dist/dts/templating/html-binding-directive.d.ts +155 -0
- package/dist/dts/templating/html-directive.d.ts +218 -0
- package/dist/dts/templating/html.d.ts +1 -0
- package/dist/dts/templating/hydration-view.d.ts +130 -0
- package/dist/dts/templating/markup.d.ts +48 -0
- package/dist/dts/templating/node-observation.d.ts +99 -0
- package/dist/dts/templating/ref.d.ts +23 -0
- package/dist/dts/templating/render.d.ts +318 -0
- package/dist/dts/templating/repeat.d.ts +131 -0
- package/dist/dts/templating/slotted.d.ts +38 -0
- package/dist/dts/templating/template.d.ts +221 -0
- package/dist/dts/templating/view.d.ts +268 -0
- package/dist/dts/templating/when.d.ts +12 -0
- package/dist/dts/testing/exports.d.ts +3 -0
- package/dist/dts/testing/fakes.d.ts +15 -0
- package/dist/dts/testing/fixture.d.ts +84 -0
- package/dist/dts/testing/models.d.ts +20 -0
- package/dist/dts/testing/timeout.d.ts +7 -0
- package/dist/dts/trust/index.d.ts +8 -0
- package/dist/dts/trust/types.d.ts +48 -0
- package/dist/dts/trust/utils.d.ts +26 -0
- package/dist/dts/tsdoc-metadata.json +11 -0
- package/dist/dts/utilities.d.ts +76 -0
- package/dist/esm/arrays.js +1 -0
- package/dist/esm/binding/binding.js +18 -0
- package/dist/esm/binding/exports.js +6 -0
- package/dist/esm/binding/normalize.js +17 -0
- package/dist/esm/binding/one-time.js +21 -0
- package/dist/esm/binding/one-way.js +30 -0
- package/dist/esm/binding/signal.js +99 -0
- package/dist/esm/binding/two-way.js +110 -0
- package/dist/esm/command/behavior.js +155 -0
- package/dist/esm/command/behaviors/confirm-behavior.js +225 -0
- package/dist/esm/command/behaviors/undo-behavior.js +312 -0
- package/dist/esm/command/command.js +104 -0
- package/dist/esm/command/component-registry.js +107 -0
- package/dist/esm/command/components/confirm-dialog.js +370 -0
- package/dist/esm/command/index.js +22 -0
- package/dist/esm/command/registry.js +752 -0
- package/dist/esm/command/schema-validation.js +194 -0
- package/dist/esm/command/types.js +86 -0
- package/dist/esm/command/validation.js +511 -0
- package/dist/esm/command/weak-ref.js +5 -0
- package/dist/esm/components/attributes.js +219 -0
- package/dist/esm/components/definition-schema-transforms.js +23 -0
- package/dist/esm/components/element-controller.js +839 -0
- package/dist/esm/components/element-hydration.js +2 -0
- package/dist/esm/components/enable-hydration.js +123 -0
- package/dist/esm/components/fast-definitions.js +294 -0
- package/dist/esm/components/fast-element-registry.js +67 -0
- package/dist/esm/components/fast-element.js +82 -0
- package/dist/esm/components/hydration-tracker.js +210 -0
- package/dist/esm/components/hydration.js +187 -0
- package/dist/esm/components/schema.js +253 -0
- package/dist/esm/context.js +185 -0
- package/dist/esm/debug.js +71 -0
- package/dist/esm/declarative/attribute-map.js +121 -0
- package/dist/esm/declarative/debug.js +4 -0
- package/dist/esm/declarative/index.js +10 -0
- package/dist/esm/declarative/interfaces.js +9 -0
- package/dist/esm/declarative/observer-map-utilities.js +565 -0
- package/dist/esm/declarative/observer-map.js +216 -0
- package/dist/esm/declarative/runtime.js +14 -0
- package/dist/esm/declarative/syntax.js +36 -0
- package/dist/esm/declarative/template-bridge.js +160 -0
- package/dist/esm/declarative/template-parser.js +306 -0
- package/dist/esm/declarative/template.js +116 -0
- package/dist/esm/declarative/utilities.js +1069 -0
- package/dist/esm/definition-schema/api.js +129 -0
- package/dist/esm/definition-schema/cem.js +89 -0
- package/dist/esm/definition-schema/extension.js +53 -0
- package/dist/esm/definition-schema/extract.js +167 -0
- package/dist/esm/definition-schema/index.js +8 -0
- package/dist/esm/definition-schema/storage.js +58 -0
- package/dist/esm/definition-schema/types.js +8 -0
- package/dist/esm/dev-mode.js +22 -0
- package/dist/esm/di/di.js +1440 -0
- package/dist/esm/dom-policy.js +374 -0
- package/dist/esm/dom.js +101 -0
- package/dist/esm/hydration/diagnostics.js +50 -0
- package/dist/esm/hydration/exports.js +6 -0
- package/dist/esm/hydration/hydration-debugger.js +112 -0
- package/dist/esm/hydration/messages.js +84 -0
- package/dist/esm/hydration/runtime.js +33 -0
- package/dist/esm/hydration/target-builder.js +264 -0
- package/dist/esm/index.debug.js +3 -0
- package/dist/esm/index.js +49 -0
- package/dist/esm/index.rollup.debug.js +6 -0
- package/dist/esm/index.rollup.js +4 -0
- package/dist/esm/interfaces.js +55 -0
- package/dist/esm/metadata.js +63 -0
- package/dist/esm/observation/arrays.js +661 -0
- package/dist/esm/observation/notifier.js +164 -0
- package/dist/esm/observation/observable.js +324 -0
- package/dist/esm/observation/update-queue.js +56 -0
- package/dist/esm/observation/volatile.js +16 -0
- package/dist/esm/platform.js +98 -0
- package/dist/esm/registry.js +2 -0
- package/dist/esm/render.js +1 -0
- package/dist/esm/schema.js +1 -0
- package/dist/esm/scoped/detection.js +102 -0
- package/dist/esm/scoped/index.js +69 -0
- package/dist/esm/scoped/mixin.js +127 -0
- package/dist/esm/scoped/registry.js +125 -0
- package/dist/esm/scoped/types.js +4 -0
- 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-directive.js +36 -0
- package/dist/esm/styles/css.js +56 -0
- package/dist/esm/styles/element-styles.js +144 -0
- package/dist/esm/styles/host.js +1 -0
- package/dist/esm/styles/style-strategy.js +1 -0
- package/dist/esm/templating/children.js +67 -0
- package/dist/esm/templating/compiler.js +359 -0
- package/dist/esm/templating/html-binding-directive.js +288 -0
- package/dist/esm/templating/html-directive.js +113 -0
- package/dist/esm/templating/html.js +1 -0
- package/dist/esm/templating/hydration-view.js +228 -0
- package/dist/esm/templating/markup.js +83 -0
- package/dist/esm/templating/node-observation.js +84 -0
- package/dist/esm/templating/ref.js +21 -0
- package/dist/esm/templating/render.js +468 -0
- package/dist/esm/templating/repeat.js +434 -0
- package/dist/esm/templating/slotted.js +49 -0
- package/dist/esm/templating/template.js +206 -0
- package/dist/esm/templating/view.js +298 -0
- package/dist/esm/templating/when.js +22 -0
- 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/models.js +58 -0
- package/dist/esm/testing/timeout.js +24 -0
- package/dist/esm/trust/index.js +7 -0
- package/dist/esm/trust/types.js +6 -0
- package/dist/esm/trust/utils.js +126 -0
- package/dist/esm/utilities.js +140 -0
- package/dist/fast-element.api.json +26798 -0
- package/dist/fast-element.d.ts +3985 -0
- package/dist/fast-element.debug.js +7618 -0
- package/dist/fast-element.debug.min.js +3 -0
- package/dist/fast-element.js +7617 -0
- package/dist/fast-element.min.js +3 -0
- package/dist/fast-element.untrimmed.d.ts +4103 -0
- package/dist/observation/arrays/arrays.api.json +2621 -0
- package/dist/scoped/scoped.api.json +891 -0
- package/dist/trust/trust.api.json +629 -0
- package/package.json +12 -12
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import { normalizeBinding } from "../binding/normalize.js";
|
|
2
|
+
import { HydrationMarkup, isHydratable } from "../components/hydration.js";
|
|
3
|
+
import { ArrayObserver } from "../observation/arrays.js";
|
|
4
|
+
import { Observable, } from "../observation/observable.js";
|
|
5
|
+
import { emptyArray } from "../platform.js";
|
|
6
|
+
import { HTMLDirective, } from "./html-directive.js";
|
|
7
|
+
import { HydrationStage } from "./hydration-view.js";
|
|
8
|
+
import { Markup } from "./markup.js";
|
|
9
|
+
import { createChildView, HTMLView } from "./view.js";
|
|
10
|
+
const defaultRepeatOptions = Object.freeze({
|
|
11
|
+
positioning: false,
|
|
12
|
+
recycle: true,
|
|
13
|
+
});
|
|
14
|
+
function bindWithoutPositioning(view, items, index, controller) {
|
|
15
|
+
view.context.parent = controller.source;
|
|
16
|
+
view.context.parentContext = controller.context;
|
|
17
|
+
view.bind(items[index]);
|
|
18
|
+
}
|
|
19
|
+
function bindWithPositioning(view, items, index, controller) {
|
|
20
|
+
view.context.parent = controller.source;
|
|
21
|
+
view.context.parentContext = controller.context;
|
|
22
|
+
view.context.length = items.length;
|
|
23
|
+
view.context.index = index;
|
|
24
|
+
view.bind(items[index]);
|
|
25
|
+
}
|
|
26
|
+
function isCommentNode(node) {
|
|
27
|
+
return node.nodeType === Node.COMMENT_NODE;
|
|
28
|
+
}
|
|
29
|
+
function removeNodeRange(first, last) {
|
|
30
|
+
const parentNode = first.parentNode;
|
|
31
|
+
if (parentNode === null) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
let current = first;
|
|
35
|
+
while (current !== null) {
|
|
36
|
+
const next = current.nextSibling;
|
|
37
|
+
parentNode.removeChild(current);
|
|
38
|
+
if (current === last) {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
current = next;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class HydrationRepeatError extends Error {
|
|
45
|
+
constructor(
|
|
46
|
+
/**
|
|
47
|
+
* The error message
|
|
48
|
+
*/
|
|
49
|
+
message, propertyBag) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.propertyBag = propertyBag;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A behavior that renders a template for each item in an array.
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
export class RepeatBehavior {
|
|
59
|
+
/**
|
|
60
|
+
* Creates an instance of RepeatBehavior.
|
|
61
|
+
* @param location - The location in the DOM to render the repeat.
|
|
62
|
+
* @param dataBinding - The array to render.
|
|
63
|
+
* @param isItemsBindingVolatile - Indicates whether the items binding has volatile dependencies.
|
|
64
|
+
* @param templateBinding - The template to render for each item.
|
|
65
|
+
* @param isTemplateBindingVolatile - Indicates whether the template binding has volatile dependencies.
|
|
66
|
+
* @param options - Options used to turn on special repeat features.
|
|
67
|
+
*/
|
|
68
|
+
constructor(directive) {
|
|
69
|
+
this.directive = directive;
|
|
70
|
+
this.isBound = false;
|
|
71
|
+
this.items = null;
|
|
72
|
+
this.itemsObserver = null;
|
|
73
|
+
this.bindView = bindWithoutPositioning;
|
|
74
|
+
/** @internal */
|
|
75
|
+
this.views = [];
|
|
76
|
+
this.itemsBindingObserver = directive.dataBinding.createObserver(this, directive);
|
|
77
|
+
this.templateBindingObserver = directive.templateBinding.createObserver(this, directive);
|
|
78
|
+
if (directive.options.positioning) {
|
|
79
|
+
this.bindView = bindWithPositioning;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Bind this behavior.
|
|
84
|
+
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
85
|
+
*/
|
|
86
|
+
bind(controller) {
|
|
87
|
+
this.location = controller.targets[this.directive.targetNodeId];
|
|
88
|
+
this.controller = controller;
|
|
89
|
+
this.isBound = true;
|
|
90
|
+
this.items = this.itemsBindingObserver.bind(controller);
|
|
91
|
+
this.template = this.templateBindingObserver.bind(controller);
|
|
92
|
+
this.observeItems(true);
|
|
93
|
+
if (isHydratable(this.template) &&
|
|
94
|
+
isHydratable(controller) &&
|
|
95
|
+
controller.hydrationStage !== HydrationStage.hydrated) {
|
|
96
|
+
this.hydrateViews(this.template);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
this.refreshAllViews();
|
|
100
|
+
}
|
|
101
|
+
controller.onUnbind(this);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Unbinds this behavior.
|
|
105
|
+
*/
|
|
106
|
+
unbind() {
|
|
107
|
+
this.isBound = false;
|
|
108
|
+
if (this.itemsObserver !== null) {
|
|
109
|
+
this.itemsObserver.unsubscribe(this);
|
|
110
|
+
}
|
|
111
|
+
this.unbindAllViews();
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Handles changes in the array, its items, and the repeat template.
|
|
115
|
+
* @param source - The source of the change.
|
|
116
|
+
* @param args - The details about what was changed.
|
|
117
|
+
*/
|
|
118
|
+
handleChange(source, args) {
|
|
119
|
+
// Notifications queued before unbind must not reobserve a cleared view source.
|
|
120
|
+
if (!this.isBound) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (args === this.itemsBindingObserver) {
|
|
124
|
+
this.items = this.itemsBindingObserver.bind(this.controller);
|
|
125
|
+
this.observeItems();
|
|
126
|
+
this.refreshAllViews();
|
|
127
|
+
}
|
|
128
|
+
else if (args === this.templateBindingObserver) {
|
|
129
|
+
this.template = this.templateBindingObserver.bind(this.controller);
|
|
130
|
+
this.refreshAllViews(true);
|
|
131
|
+
}
|
|
132
|
+
else if (!args[0]) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
else if (args[0].reset) {
|
|
136
|
+
this.refreshAllViews();
|
|
137
|
+
}
|
|
138
|
+
else if (args[0].sorted) {
|
|
139
|
+
this.updateSortedViews(args);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.updateSplicedViews(args);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
observeItems(force = false) {
|
|
146
|
+
if (!this.items) {
|
|
147
|
+
this.items = emptyArray;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const oldObserver = this.itemsObserver;
|
|
151
|
+
const newObserver = (this.itemsObserver = Observable.getNotifier(this.items));
|
|
152
|
+
const hasNewObserver = oldObserver !== newObserver;
|
|
153
|
+
if (hasNewObserver && oldObserver !== null) {
|
|
154
|
+
oldObserver.unsubscribe(this);
|
|
155
|
+
}
|
|
156
|
+
if (hasNewObserver || force) {
|
|
157
|
+
newObserver.subscribe(this);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
updateSortedViews(sorts) {
|
|
161
|
+
const views = this.views;
|
|
162
|
+
for (let i = 0, ii = sorts.length; i < ii; ++i) {
|
|
163
|
+
const sortedItems = sorts[i].sorted.slice();
|
|
164
|
+
const unsortedItems = sortedItems.slice().sort();
|
|
165
|
+
for (let j = 0, jj = sortedItems.length; j < jj; ++j) {
|
|
166
|
+
const sortedIndex = sortedItems.find(value => sortedItems[j] === unsortedItems[value]);
|
|
167
|
+
if (sortedIndex !== j) {
|
|
168
|
+
const removedItems = unsortedItems.splice(sortedIndex, 1);
|
|
169
|
+
unsortedItems.splice(j, 0, ...removedItems);
|
|
170
|
+
const neighbor = views[j];
|
|
171
|
+
const location = neighbor ? neighbor.firstChild : this.location;
|
|
172
|
+
views[sortedIndex].remove();
|
|
173
|
+
views[sortedIndex].insertBefore(location);
|
|
174
|
+
const removedViews = views.splice(sortedIndex, 1);
|
|
175
|
+
views.splice(j, 0, ...removedViews);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
updateSplicedViews(splices) {
|
|
181
|
+
const views = this.views;
|
|
182
|
+
const bindView = this.bindView;
|
|
183
|
+
const items = this.items;
|
|
184
|
+
const template = this.template;
|
|
185
|
+
const controller = this.controller;
|
|
186
|
+
const recycle = this.directive.options.recycle;
|
|
187
|
+
const leftoverViews = [];
|
|
188
|
+
let leftoverIndex = 0;
|
|
189
|
+
let availableViews = 0;
|
|
190
|
+
for (let i = 0, ii = splices.length; i < ii; ++i) {
|
|
191
|
+
const splice = splices[i];
|
|
192
|
+
const removed = splice.removed;
|
|
193
|
+
let removeIndex = 0;
|
|
194
|
+
let addIndex = splice.index;
|
|
195
|
+
const end = addIndex + splice.addedCount;
|
|
196
|
+
const removedViews = views.splice(splice.index, removed.length);
|
|
197
|
+
const totalAvailableViews = (availableViews =
|
|
198
|
+
leftoverViews.length + removedViews.length);
|
|
199
|
+
for (; addIndex < end; ++addIndex) {
|
|
200
|
+
const neighbor = views[addIndex];
|
|
201
|
+
const location = neighbor ? neighbor.firstChild : this.location;
|
|
202
|
+
let view;
|
|
203
|
+
if (recycle && availableViews > 0) {
|
|
204
|
+
if (removeIndex <= totalAvailableViews && removedViews.length > 0) {
|
|
205
|
+
view = removedViews[removeIndex];
|
|
206
|
+
removeIndex++;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
view = leftoverViews[leftoverIndex];
|
|
210
|
+
leftoverIndex++;
|
|
211
|
+
}
|
|
212
|
+
availableViews--;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
view = createChildView(template, controller);
|
|
216
|
+
}
|
|
217
|
+
views.splice(addIndex, 0, view);
|
|
218
|
+
bindView(view, items, addIndex, controller);
|
|
219
|
+
view.insertBefore(location);
|
|
220
|
+
}
|
|
221
|
+
if (removedViews[removeIndex]) {
|
|
222
|
+
leftoverViews.push(...removedViews.slice(removeIndex));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
for (let i = leftoverIndex, ii = leftoverViews.length; i < ii; ++i) {
|
|
226
|
+
leftoverViews[i].dispose();
|
|
227
|
+
}
|
|
228
|
+
if (this.directive.options.positioning) {
|
|
229
|
+
for (let i = 0, viewsLength = views.length; i < viewsLength; ++i) {
|
|
230
|
+
const context = views[i].context;
|
|
231
|
+
context.length = viewsLength;
|
|
232
|
+
context.index = i;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
refreshAllViews(templateChanged = false) {
|
|
237
|
+
const items = this.items;
|
|
238
|
+
const template = this.template;
|
|
239
|
+
const location = this.location;
|
|
240
|
+
const bindView = this.bindView;
|
|
241
|
+
const controller = this.controller;
|
|
242
|
+
let itemsLength = items.length;
|
|
243
|
+
let views = this.views;
|
|
244
|
+
let viewsLength = views.length;
|
|
245
|
+
if (itemsLength === 0 || templateChanged || !this.directive.options.recycle) {
|
|
246
|
+
// all views need to be removed
|
|
247
|
+
HTMLView.disposeContiguousBatch(views);
|
|
248
|
+
viewsLength = 0;
|
|
249
|
+
}
|
|
250
|
+
if (viewsLength === 0) {
|
|
251
|
+
// all views need to be created
|
|
252
|
+
this.views = views = new Array(itemsLength);
|
|
253
|
+
for (let i = 0; i < itemsLength; ++i) {
|
|
254
|
+
const view = createChildView(template, controller);
|
|
255
|
+
bindView(view, items, i, controller);
|
|
256
|
+
views[i] = view;
|
|
257
|
+
view.insertBefore(location);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
// attempt to reuse existing views with new data
|
|
262
|
+
let i = 0;
|
|
263
|
+
for (; i < itemsLength; ++i) {
|
|
264
|
+
if (i < viewsLength) {
|
|
265
|
+
const view = views[i];
|
|
266
|
+
if (!view) {
|
|
267
|
+
const serializer = new XMLSerializer();
|
|
268
|
+
throw new HydrationRepeatError(`View is null or undefined inside "${this.location.getRootNode().host.nodeName}".`, {
|
|
269
|
+
index: i,
|
|
270
|
+
hydrationStage: this.controller
|
|
271
|
+
.hydrationStage,
|
|
272
|
+
itemsLength,
|
|
273
|
+
viewsState: views.map(v => (v ? "hydrated" : "empty")),
|
|
274
|
+
viewTemplateString: serializer.serializeToString(template.create().fragment),
|
|
275
|
+
rootNodeContent: serializer.serializeToString(this.location.getRootNode()),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
bindView(view, items, i, controller);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
const view = createChildView(template, controller);
|
|
282
|
+
bindView(view, items, i, controller);
|
|
283
|
+
views.push(view);
|
|
284
|
+
view.insertBefore(location);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const removed = views.splice(i, viewsLength - i);
|
|
288
|
+
for (i = 0, itemsLength = removed.length; i < itemsLength; ++i) {
|
|
289
|
+
removed[i].dispose();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
unbindAllViews() {
|
|
294
|
+
const views = this.views;
|
|
295
|
+
for (let i = 0, ii = views.length; i < ii; ++i) {
|
|
296
|
+
const view = views[i];
|
|
297
|
+
if (!view) {
|
|
298
|
+
const serializer = new XMLSerializer();
|
|
299
|
+
throw new HydrationRepeatError(`View is null or undefined inside "${this.location.getRootNode().host.nodeName}".`, {
|
|
300
|
+
index: i,
|
|
301
|
+
hydrationStage: this.controller.hydrationStage,
|
|
302
|
+
viewsState: views.map(v => (v ? "hydrated" : "empty")),
|
|
303
|
+
rootNodeContent: serializer.serializeToString(this.location.getRootNode()),
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
view.unbind();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
hydrateViews(template) {
|
|
310
|
+
var _a;
|
|
311
|
+
if (!this.items) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const items = this.items;
|
|
315
|
+
const itemCount = items.length;
|
|
316
|
+
const views = (this.views = new Array(itemCount));
|
|
317
|
+
// First pass: collect all repeat marker pairs by walking backward.
|
|
318
|
+
// Each entry tracks both the item content range and its SSR markers.
|
|
319
|
+
const itemRanges = [];
|
|
320
|
+
let current = this.location.previousSibling;
|
|
321
|
+
while (current !== null) {
|
|
322
|
+
if (!isCommentNode(current) ||
|
|
323
|
+
!HydrationMarkup.isRepeatViewEndMarker(current.data)) {
|
|
324
|
+
current = current.previousSibling;
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const endMarker = current;
|
|
328
|
+
endMarker.data = "";
|
|
329
|
+
const end = endMarker.previousSibling;
|
|
330
|
+
if (!end) {
|
|
331
|
+
throw new Error(`Error when hydrating inside "${this.location.getRootNode().host.nodeName}": end should never be null.`);
|
|
332
|
+
}
|
|
333
|
+
// Find matching start marker via balanced counting
|
|
334
|
+
let start = end;
|
|
335
|
+
let depth = 0;
|
|
336
|
+
while (start !== null) {
|
|
337
|
+
if (isCommentNode(start)) {
|
|
338
|
+
if (HydrationMarkup.isRepeatViewEndMarker(start.data)) {
|
|
339
|
+
depth++;
|
|
340
|
+
}
|
|
341
|
+
else if (HydrationMarkup.isRepeatViewStartMarker(start.data)) {
|
|
342
|
+
if (depth === 0) {
|
|
343
|
+
const startMarker = start;
|
|
344
|
+
startMarker.data = "";
|
|
345
|
+
current = startMarker.previousSibling;
|
|
346
|
+
const itemStart = (_a = startMarker.nextSibling) !== null && _a !== void 0 ? _a : endMarker;
|
|
347
|
+
// Empty item: start and end markers are adjacent.
|
|
348
|
+
const itemEnd = end === startMarker ? itemStart : end;
|
|
349
|
+
itemRanges.push({
|
|
350
|
+
start: itemStart,
|
|
351
|
+
end: itemEnd,
|
|
352
|
+
startMarker,
|
|
353
|
+
endMarker,
|
|
354
|
+
});
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
depth--;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
start = start.previousSibling;
|
|
361
|
+
}
|
|
362
|
+
if (!start) {
|
|
363
|
+
throw new Error(`Error when hydrating inside "${this.location.getRootNode().host.nodeName}": repeat start marker not found.`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// Ranges were collected backward (last item first).
|
|
367
|
+
// Reverse so index 0 = first SSR item.
|
|
368
|
+
itemRanges.reverse();
|
|
369
|
+
// Hydrate each SSR item at its correct index (0-based from start).
|
|
370
|
+
const hydrationCount = Math.min(itemRanges.length, itemCount);
|
|
371
|
+
for (let i = 0; i < hydrationCount; i++) {
|
|
372
|
+
const { start, end } = itemRanges[i];
|
|
373
|
+
const view = template.hydrate(start, end);
|
|
374
|
+
views[i] = view;
|
|
375
|
+
this.bindView(view, items, i, this.controller);
|
|
376
|
+
}
|
|
377
|
+
for (let i = hydrationCount; i < itemCount; i++) {
|
|
378
|
+
const view = createChildView(template, this.controller);
|
|
379
|
+
views[i] = view;
|
|
380
|
+
this.bindView(view, items, i, this.controller);
|
|
381
|
+
view.insertBefore(this.location);
|
|
382
|
+
}
|
|
383
|
+
for (let i = itemCount, ii = itemRanges.length; i < ii; i++) {
|
|
384
|
+
const { startMarker, endMarker } = itemRanges[i];
|
|
385
|
+
removeNodeRange(startMarker, endMarker);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* A directive that configures list rendering.
|
|
391
|
+
* @public
|
|
392
|
+
*/
|
|
393
|
+
export class RepeatDirective {
|
|
394
|
+
/**
|
|
395
|
+
* Creates a placeholder string based on the directive's index within the template.
|
|
396
|
+
* @param index - The index of the directive within the template.
|
|
397
|
+
*/
|
|
398
|
+
createHTML(add) {
|
|
399
|
+
return Markup.comment(add(this));
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Creates an instance of RepeatDirective.
|
|
403
|
+
* @param dataBinding - The binding that provides the array to render.
|
|
404
|
+
* @param templateBinding - The template binding used to obtain a template to render for each item in the array.
|
|
405
|
+
* @param options - Options used to turn on special repeat features.
|
|
406
|
+
*/
|
|
407
|
+
constructor(dataBinding, templateBinding, options) {
|
|
408
|
+
this.dataBinding = dataBinding;
|
|
409
|
+
this.templateBinding = templateBinding;
|
|
410
|
+
this.options = options;
|
|
411
|
+
ArrayObserver.enable();
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Creates a behavior for the provided target node.
|
|
415
|
+
* @param target - The node instance to create the behavior for.
|
|
416
|
+
*/
|
|
417
|
+
createBehavior() {
|
|
418
|
+
return new RepeatBehavior(this);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
HTMLDirective.define(RepeatDirective);
|
|
422
|
+
/**
|
|
423
|
+
* A directive that enables list rendering.
|
|
424
|
+
* @param items - The array to render.
|
|
425
|
+
* @param template - The template or a template binding used obtain a template
|
|
426
|
+
* to render for each item in the array.
|
|
427
|
+
* @param options - Options used to turn on special repeat features.
|
|
428
|
+
* @public
|
|
429
|
+
*/
|
|
430
|
+
export function repeat(items, template, options = defaultRepeatOptions) {
|
|
431
|
+
const dataBinding = normalizeBinding(items);
|
|
432
|
+
const templateBinding = normalizeBinding(template);
|
|
433
|
+
return new RepeatDirective(dataBinding, templateBinding, Object.assign(Object.assign({}, defaultRepeatOptions), options));
|
|
434
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { isString } from "../interfaces.js";
|
|
2
|
+
import { HTMLDirective } from "./html-directive.js";
|
|
3
|
+
import { NodeObservationDirective, } from "./node-observation.js";
|
|
4
|
+
const slotEvent = "slotchange";
|
|
5
|
+
/**
|
|
6
|
+
* The runtime behavior for slotted node observation.
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export class SlottedDirective extends NodeObservationDirective {
|
|
10
|
+
/**
|
|
11
|
+
* Begins observation of the nodes.
|
|
12
|
+
* @param target - The target to observe.
|
|
13
|
+
*/
|
|
14
|
+
observe(target) {
|
|
15
|
+
target.addEventListener(slotEvent, this);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Disconnects observation of the nodes.
|
|
19
|
+
* @param target - The target to unobserve.
|
|
20
|
+
*/
|
|
21
|
+
disconnect(target) {
|
|
22
|
+
target.removeEventListener(slotEvent, this);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Retrieves the raw nodes that should be assigned to the source property.
|
|
26
|
+
* @param target - The target to get the node to.
|
|
27
|
+
*/
|
|
28
|
+
getNodes(target) {
|
|
29
|
+
return target.assignedNodes(this.options);
|
|
30
|
+
}
|
|
31
|
+
/** @internal */
|
|
32
|
+
handleEvent(event) {
|
|
33
|
+
const target = event.currentTarget;
|
|
34
|
+
this.updateTarget(this.getSource(target), this.computeNodes(target));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
HTMLDirective.define(SlottedDirective);
|
|
38
|
+
/**
|
|
39
|
+
* A directive that observes the `assignedNodes()` of a slot and updates a property
|
|
40
|
+
* whenever they change.
|
|
41
|
+
* @param propertyOrOptions - The options used to configure slotted node observation.
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
44
|
+
export function slotted(propertyOrOptions) {
|
|
45
|
+
if (isString(propertyOrOptions)) {
|
|
46
|
+
propertyOrOptions = { property: propertyOrOptions };
|
|
47
|
+
}
|
|
48
|
+
return new SlottedDirective(propertyOrOptions);
|
|
49
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { Binding } from "../binding/binding.js";
|
|
2
|
+
import { oneTime } from "../binding/one-time.js";
|
|
3
|
+
import { oneWay } from "../binding/one-way.js";
|
|
4
|
+
import { isFunction, isString, Message } from "../interfaces.js";
|
|
5
|
+
import { FAST, makeSerializationNoop } from "../platform.js";
|
|
6
|
+
import { Compiler } from "./compiler.js";
|
|
7
|
+
import { HTMLBindingDirective } from "./html-binding-directive.js";
|
|
8
|
+
import { HTMLDirective, } from "./html-directive.js";
|
|
9
|
+
import { nextId } from "./markup.js";
|
|
10
|
+
// Much thanks to LitHTML for working this out!
|
|
11
|
+
const lastAttributeNameRegex =
|
|
12
|
+
/* eslint-disable-next-line no-control-regex, max-len */
|
|
13
|
+
/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
|
|
14
|
+
const noFactories = Object.create(null);
|
|
15
|
+
/**
|
|
16
|
+
* Inlines a template into another template.
|
|
17
|
+
* @public
|
|
18
|
+
*/
|
|
19
|
+
export class InlineTemplateDirective {
|
|
20
|
+
/**
|
|
21
|
+
* Creates an instance of InlineTemplateDirective.
|
|
22
|
+
* @param template - The template to inline.
|
|
23
|
+
*/
|
|
24
|
+
constructor(html, factories = noFactories) {
|
|
25
|
+
this.html = html;
|
|
26
|
+
this.factories = factories;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Creates HTML to be used within a template.
|
|
30
|
+
* @param add - Can be used to add behavior factories to a template.
|
|
31
|
+
*/
|
|
32
|
+
createHTML(add) {
|
|
33
|
+
const factories = this.factories;
|
|
34
|
+
for (const key in factories) {
|
|
35
|
+
add(factories[key]);
|
|
36
|
+
}
|
|
37
|
+
return this.html;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* An empty template partial.
|
|
42
|
+
*/
|
|
43
|
+
InlineTemplateDirective.empty = new InlineTemplateDirective("");
|
|
44
|
+
HTMLDirective.define(InlineTemplateDirective);
|
|
45
|
+
function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
|
|
46
|
+
if (definition.aspected) {
|
|
47
|
+
const match = lastAttributeNameRegex.exec(prevString);
|
|
48
|
+
if (match !== null) {
|
|
49
|
+
HTMLDirective.assignAspect(value, match[2]);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return value.createHTML(add);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A template capable of creating HTMLView instances or rendering directly to DOM.
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
export class ViewTemplate {
|
|
59
|
+
/**
|
|
60
|
+
* Creates an instance of ViewTemplate.
|
|
61
|
+
* @param html - The html representing what this template will instantiate, including placeholders for directives.
|
|
62
|
+
* @param factories - The directives that will be connected to placeholders in the html.
|
|
63
|
+
* @param policy - The security policy to use when compiling this template.
|
|
64
|
+
*/
|
|
65
|
+
constructor(html, factories = {}, policy) {
|
|
66
|
+
this.policy = policy;
|
|
67
|
+
this.result = null;
|
|
68
|
+
this.html = html;
|
|
69
|
+
this.factories = factories;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @internal
|
|
73
|
+
*/
|
|
74
|
+
compile() {
|
|
75
|
+
if (this.result === null) {
|
|
76
|
+
this.result = Compiler.compile(this.html, this.factories, this.policy);
|
|
77
|
+
}
|
|
78
|
+
return this.result;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns a directive that can inline the template.
|
|
82
|
+
*/
|
|
83
|
+
inline() {
|
|
84
|
+
return new InlineTemplateDirective(isString(this.html) ? this.html : this.html.innerHTML, this.factories);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Sets the DOMPolicy for this template.
|
|
88
|
+
* @param policy - The policy to associated with this template.
|
|
89
|
+
* @returns The modified template instance.
|
|
90
|
+
* @remarks
|
|
91
|
+
* The DOMPolicy can only be set once for a template and cannot be
|
|
92
|
+
* set after the template is compiled.
|
|
93
|
+
*/
|
|
94
|
+
withPolicy(policy) {
|
|
95
|
+
if (this.result) {
|
|
96
|
+
throw FAST.error(Message.cannotSetTemplatePolicyAfterCompilation);
|
|
97
|
+
}
|
|
98
|
+
if (this.policy) {
|
|
99
|
+
throw FAST.error(Message.onlySetTemplatePolicyOnce);
|
|
100
|
+
}
|
|
101
|
+
this.policy = policy;
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
|
|
106
|
+
* @param source - The data source to bind the template to.
|
|
107
|
+
* @param host - The Element where the template will be rendered.
|
|
108
|
+
* @param hostBindingTarget - An HTML element to target the host bindings at if different from the
|
|
109
|
+
* host that the template is being attached to.
|
|
110
|
+
* @param scope - The shadow root whose scoped custom element registry child
|
|
111
|
+
* elements should be created against, when it has one.
|
|
112
|
+
*/
|
|
113
|
+
render(source, host, hostBindingTarget, scope) {
|
|
114
|
+
const view = this.create(hostBindingTarget, scope);
|
|
115
|
+
view.bind(source);
|
|
116
|
+
view.appendTo(host);
|
|
117
|
+
return view;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Creates an HTMLView instance based on this template definition.
|
|
121
|
+
* @param hostBindingTarget - The element that host behaviors will be bound to.
|
|
122
|
+
* @param scope - The shadow root whose scoped custom element registry child
|
|
123
|
+
* elements should be created against, when it has one.
|
|
124
|
+
*/
|
|
125
|
+
create(hostBindingTarget, scope) {
|
|
126
|
+
return this.compile().createView(hostBindingTarget, scope);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Processes the tagged template literal's static strings and interpolated values and
|
|
130
|
+
* creates a ViewTemplate.
|
|
131
|
+
*
|
|
132
|
+
* For each interpolated value:
|
|
133
|
+
* 1. Functions (binding expressions, e.g., `x => x.name`) → wrapped in a one-way HTMLBindingDirective
|
|
134
|
+
* 2. Binding instances → wrapped in an HTMLBindingDirective
|
|
135
|
+
* 3. HTMLDirective instances → used as-is
|
|
136
|
+
* 4. Static values (strings, numbers) → wrapped in a one-time HTMLBindingDirective
|
|
137
|
+
*
|
|
138
|
+
* Each directive's createHTML() is called with an `add` callback that registers
|
|
139
|
+
* the factory in the factories record under a unique ID and returns that ID.
|
|
140
|
+
* The directive inserts a placeholder marker (e.g., `fast-abc123{0}fast-abc123`) into
|
|
141
|
+
* the HTML string so the compiler can later find and associate it with the factory.
|
|
142
|
+
*
|
|
143
|
+
* Aspect detection happens here too: the `lastAttributeNameRegex` checks whether
|
|
144
|
+
* the placeholder appears inside an attribute value, and if so, assignAspect()
|
|
145
|
+
* sets the correct DOMAspect (attribute, property, event, etc.) based on the
|
|
146
|
+
* attribute name prefix.
|
|
147
|
+
*
|
|
148
|
+
* @param strings - The static strings to create the template with.
|
|
149
|
+
* @param values - The dynamic values to create the template with.
|
|
150
|
+
* @param policy - The DOMPolicy to associated with the template.
|
|
151
|
+
* @returns A ViewTemplate.
|
|
152
|
+
* @remarks
|
|
153
|
+
* This API should not be used directly under normal circumstances because constructing
|
|
154
|
+
* a template in this way, if not done properly, can open up the application to XSS
|
|
155
|
+
* attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
|
|
156
|
+
* and also be sure to manually sanitize all static strings particularly if they can
|
|
157
|
+
* come from user input.
|
|
158
|
+
*/
|
|
159
|
+
static create(strings, values, policy) {
|
|
160
|
+
let html = "";
|
|
161
|
+
const factories = Object.create(null);
|
|
162
|
+
const add = (factory) => {
|
|
163
|
+
var _a;
|
|
164
|
+
const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
|
|
165
|
+
factories[id] = factory;
|
|
166
|
+
return id;
|
|
167
|
+
};
|
|
168
|
+
for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
|
|
169
|
+
const currentString = strings[i];
|
|
170
|
+
let currentValue = values[i];
|
|
171
|
+
let definition;
|
|
172
|
+
html += currentString;
|
|
173
|
+
if (isFunction(currentValue)) {
|
|
174
|
+
currentValue = new HTMLBindingDirective(oneWay(currentValue));
|
|
175
|
+
}
|
|
176
|
+
else if (currentValue instanceof Binding) {
|
|
177
|
+
currentValue = new HTMLBindingDirective(currentValue);
|
|
178
|
+
}
|
|
179
|
+
else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
|
|
180
|
+
const staticValue = currentValue;
|
|
181
|
+
currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
|
|
182
|
+
}
|
|
183
|
+
html += createHTML(currentValue, currentString, add, definition);
|
|
184
|
+
}
|
|
185
|
+
return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
makeSerializationNoop(ViewTemplate);
|
|
189
|
+
/**
|
|
190
|
+
* Transforms a template literal string into a ViewTemplate.
|
|
191
|
+
* @param strings - The string fragments that are interpolated with the values.
|
|
192
|
+
* @param values - The values that are interpolated with the string fragments.
|
|
193
|
+
* @remarks
|
|
194
|
+
* The html helper supports interpolation of strings, numbers, binding expressions,
|
|
195
|
+
* other template instances, and Directive instances.
|
|
196
|
+
* @public
|
|
197
|
+
*/
|
|
198
|
+
export const html = ((strings, ...values) => {
|
|
199
|
+
if (Array.isArray(strings) && Array.isArray(strings.raw)) {
|
|
200
|
+
return ViewTemplate.create(strings, values);
|
|
201
|
+
}
|
|
202
|
+
throw FAST.error(Message.directCallToHTMLTagNotAllowed);
|
|
203
|
+
});
|
|
204
|
+
html.partial = (html) => {
|
|
205
|
+
return new InlineTemplateDirective(html);
|
|
206
|
+
};
|