@aurumjs/html 0.10.12
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/LICENSE +21 -0
- package/README.md +152 -0
- package/package.json +52 -0
- package/prebuilt/esnext/builtin_components/portal.d.ts +9 -0
- package/prebuilt/esnext/builtin_components/portal.d.ts.map +1 -0
- package/prebuilt/esnext/builtin_components/portal.js +40 -0
- package/prebuilt/esnext/builtin_components/portal.js.map +1 -0
- package/prebuilt/esnext/builtin_components/router_link.d.ts +21 -0
- package/prebuilt/esnext/builtin_components/router_link.d.ts.map +1 -0
- package/prebuilt/esnext/builtin_components/router_link.js +54 -0
- package/prebuilt/esnext/builtin_components/router_link.js.map +1 -0
- package/prebuilt/esnext/index.d.ts +18 -0
- package/prebuilt/esnext/index.d.ts.map +1 -0
- package/prebuilt/esnext/index.js +18 -0
- package/prebuilt/esnext/index.js.map +1 -0
- package/prebuilt/esnext/jsx-dev-runtime.d.ts +2 -0
- package/prebuilt/esnext/jsx-dev-runtime.d.ts.map +1 -0
- package/prebuilt/esnext/jsx-dev-runtime.js +2 -0
- package/prebuilt/esnext/jsx-dev-runtime.js.map +1 -0
- package/prebuilt/esnext/jsx-runtime.d.ts +28 -0
- package/prebuilt/esnext/jsx-runtime.d.ts.map +1 -0
- package/prebuilt/esnext/jsx-runtime.js +26 -0
- package/prebuilt/esnext/jsx-runtime.js.map +1 -0
- package/prebuilt/esnext/nodes/input.d.ts +41 -0
- package/prebuilt/esnext/nodes/input.d.ts.map +1 -0
- package/prebuilt/esnext/nodes/input.js +92 -0
- package/prebuilt/esnext/nodes/input.js.map +1 -0
- package/prebuilt/esnext/nodes/rendering_helpers.d.ts +8 -0
- package/prebuilt/esnext/nodes/rendering_helpers.d.ts.map +1 -0
- package/prebuilt/esnext/nodes/rendering_helpers.js +173 -0
- package/prebuilt/esnext/nodes/rendering_helpers.js.map +1 -0
- package/prebuilt/esnext/nodes/select.d.ts +17 -0
- package/prebuilt/esnext/nodes/select.d.ts.map +1 -0
- package/prebuilt/esnext/nodes/select.js +95 -0
- package/prebuilt/esnext/nodes/select.js.map +1 -0
- package/prebuilt/esnext/nodes/simple_dom_nodes.d.ts +866 -0
- package/prebuilt/esnext/nodes/simple_dom_nodes.d.ts.map +1 -0
- package/prebuilt/esnext/nodes/simple_dom_nodes.js +531 -0
- package/prebuilt/esnext/nodes/simple_dom_nodes.js.map +1 -0
- package/prebuilt/esnext/nodes/textarea.d.ts +34 -0
- package/prebuilt/esnext/nodes/textarea.d.ts.map +1 -0
- package/prebuilt/esnext/nodes/textarea.js +61 -0
- package/prebuilt/esnext/nodes/textarea.js.map +1 -0
- package/prebuilt/esnext/rendering/dom_runtime.d.ts +91 -0
- package/prebuilt/esnext/rendering/dom_runtime.d.ts.map +1 -0
- package/prebuilt/esnext/rendering/dom_runtime.js +970 -0
- package/prebuilt/esnext/rendering/dom_runtime.js.map +1 -0
- package/prebuilt/esnext/rendering/render_batch.d.ts +15 -0
- package/prebuilt/esnext/rendering/render_batch.d.ts.map +1 -0
- package/prebuilt/esnext/rendering/render_batch.js +73 -0
- package/prebuilt/esnext/rendering/render_batch.js.map +1 -0
- package/prebuilt/esnext/rendering/renderers/dom_adapter.d.ts +199 -0
- package/prebuilt/esnext/rendering/renderers/dom_adapter.d.ts.map +1 -0
- package/prebuilt/esnext/rendering/renderers/dom_adapter.js +525 -0
- package/prebuilt/esnext/rendering/renderers/dom_adapter.js.map +1 -0
- package/prebuilt/esnext/rendering/renderers/string_adapter.d.ts +4 -0
- package/prebuilt/esnext/rendering/renderers/string_adapter.d.ts.map +1 -0
- package/prebuilt/esnext/rendering/renderers/string_adapter.js +112 -0
- package/prebuilt/esnext/rendering/renderers/string_adapter.js.map +1 -0
- package/prebuilt/esnext/rendering/renderers/vdom_adapter.d.ts +11 -0
- package/prebuilt/esnext/rendering/renderers/vdom_adapter.d.ts.map +1 -0
- package/prebuilt/esnext/rendering/renderers/vdom_adapter.js +23 -0
- package/prebuilt/esnext/rendering/renderers/vdom_adapter.js.map +1 -0
- package/prebuilt/esnext/utilities/aurum.d.ts +155 -0
- package/prebuilt/esnext/utilities/aurum.d.ts.map +1 -0
- package/prebuilt/esnext/utilities/aurum.js +282 -0
- package/prebuilt/esnext/utilities/aurum.js.map +1 -0
- package/prebuilt/esnext/utilities/sanitize.d.ts +25 -0
- package/prebuilt/esnext/utilities/sanitize.d.ts.map +1 -0
- package/prebuilt/esnext/utilities/sanitize.js +191 -0
- package/prebuilt/esnext/utilities/sanitize.js.map +1 -0
|
@@ -0,0 +1,970 @@
|
|
|
1
|
+
import { AURUM_DEVTOOLS_DEBUG_BUILD_ENABLED, ArrayDataSource, DataSource } from '@aurumjs/streams';
|
|
2
|
+
import { aurumElementModelIdentitiy, createAPI as createCoreAPI, createRenderSession, isAurumDevtoolsDebugBuild, linkAurumDomNodeChildren, registerAurumRenderBinding, traceAurumComponentRender } from '@aurumjs/rendering';
|
|
3
|
+
import { listenToRenderBatchState, queueRenderUpdate } from './render_batch.js';
|
|
4
|
+
const aurumElementDomBounds = new WeakMap();
|
|
5
|
+
export class AurumElement {
|
|
6
|
+
constructor(dataSource, api) {
|
|
7
|
+
this.disposed = false;
|
|
8
|
+
this.children = [];
|
|
9
|
+
this.api = api;
|
|
10
|
+
if (isAurumDevtoolsDebugBuild()) {
|
|
11
|
+
this.renderScope = createRenderSession(api.renderSession);
|
|
12
|
+
this.api.cancellationToken.addCancellable(this.renderScope.sessionToken);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
this.renderScope = api.renderSession;
|
|
16
|
+
}
|
|
17
|
+
this.api.cancellationToken.addCancellable(() => this.dispose());
|
|
18
|
+
if (dataSource) {
|
|
19
|
+
this.api.onAttach(() => {
|
|
20
|
+
if (!this.api.cancellationToken.isCancelled) {
|
|
21
|
+
if (this.hostNode === undefined) {
|
|
22
|
+
throw new Error('illegal state: Attach fired but not actually attached');
|
|
23
|
+
}
|
|
24
|
+
this.render(dataSource);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
dispose() {
|
|
30
|
+
var _a;
|
|
31
|
+
if (this.disposed) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if ((_a = this.hostNode) === null || _a === void 0 ? void 0 : _a.isConnected) {
|
|
35
|
+
this.clearContent();
|
|
36
|
+
this.contentStartMarker.remove();
|
|
37
|
+
this.contentEndMarker.remove();
|
|
38
|
+
}
|
|
39
|
+
this.disposed = true;
|
|
40
|
+
}
|
|
41
|
+
attachToDom(node, index) {
|
|
42
|
+
var _a;
|
|
43
|
+
this.attachToDomBefore(node, (_a = node.childNodes[index]) !== null && _a !== void 0 ? _a : null);
|
|
44
|
+
}
|
|
45
|
+
attachToDomBefore(node, referenceNode) {
|
|
46
|
+
if (this.hostNode) {
|
|
47
|
+
throw new Error('Aurum Element is already attached');
|
|
48
|
+
}
|
|
49
|
+
const id = AurumElement.id++;
|
|
50
|
+
this.hostNode = node;
|
|
51
|
+
this.contentStartMarker = document.createComment('START Aurum Node ' + id);
|
|
52
|
+
//@ts-ignore
|
|
53
|
+
this.contentStartMarker.owner = this;
|
|
54
|
+
this.contentEndMarker = document.createComment('END Aurum Node ' + id);
|
|
55
|
+
aurumElementDomBounds.set(this, [this.contentStartMarker, this.contentEndMarker]);
|
|
56
|
+
node.insertBefore(this.contentStartMarker, referenceNode);
|
|
57
|
+
node.insertBefore(this.contentEndMarker, referenceNode);
|
|
58
|
+
}
|
|
59
|
+
getStartIndex() {
|
|
60
|
+
return this.getWorkIndex() - 1;
|
|
61
|
+
}
|
|
62
|
+
getWorkIndex() {
|
|
63
|
+
if (this.lastStartIndex !== undefined && this.hostNode.childNodes[this.lastStartIndex] === this.contentStartMarker) {
|
|
64
|
+
return this.lastStartIndex + 1;
|
|
65
|
+
}
|
|
66
|
+
for (let i = 0; i < this.hostNode.childNodes.length; i++) {
|
|
67
|
+
if (this.hostNode.childNodes[i] === this.contentStartMarker) {
|
|
68
|
+
this.lastStartIndex = i;
|
|
69
|
+
return i + 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return -1;
|
|
73
|
+
}
|
|
74
|
+
getLastIndex() {
|
|
75
|
+
if (this.lastEndIndex !== undefined && this.hostNode.childNodes[this.lastEndIndex] === this.contentEndMarker) {
|
|
76
|
+
return this.lastEndIndex;
|
|
77
|
+
}
|
|
78
|
+
for (let i = 0; i < this.hostNode.childNodes.length; i++) {
|
|
79
|
+
if (this.hostNode.childNodes[i] === this.contentEndMarker) {
|
|
80
|
+
this.lastEndIndex = i;
|
|
81
|
+
return i;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return -1;
|
|
85
|
+
}
|
|
86
|
+
clearContent() {
|
|
87
|
+
if (this.hostNode === undefined) {
|
|
88
|
+
throw new Error('illegal state: Aurum element was not attched to anything');
|
|
89
|
+
}
|
|
90
|
+
let workIndex = this.getWorkIndex();
|
|
91
|
+
while (this.hostNode.childNodes[workIndex] !== this.contentEndMarker) {
|
|
92
|
+
if (!(this.hostNode.childNodes[workIndex] instanceof Comment)) {
|
|
93
|
+
this.hostNode.removeChild(this.hostNode.childNodes[workIndex]);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
//@ts-ignore
|
|
97
|
+
if (this.hostNode.childNodes[workIndex].owner.disposed) {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
//@ts-ignore
|
|
101
|
+
this.hostNode.childNodes[workIndex].owner.dispose();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
updateDom() {
|
|
106
|
+
var _a;
|
|
107
|
+
const workIndex = this.getWorkIndex();
|
|
108
|
+
if (this.hostNode.childNodes[workIndex] === this.contentEndMarker &&
|
|
109
|
+
this.children.length > 0 &&
|
|
110
|
+
this.children.every((child) => child instanceof HTMLElement || child instanceof Text || child instanceof SVGElement)) {
|
|
111
|
+
const fragment = document.createDocumentFragment();
|
|
112
|
+
for (const child of this.children)
|
|
113
|
+
fragment.appendChild(child);
|
|
114
|
+
const endIndex = this.getLastIndex();
|
|
115
|
+
this.hostNode.insertBefore(fragment, this.contentEndMarker);
|
|
116
|
+
this.lastEndIndex = endIndex + this.children.length;
|
|
117
|
+
this.linkHostChildren();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
let i;
|
|
121
|
+
let offset = 0;
|
|
122
|
+
for (i = 0; i < this.children.length; i++) {
|
|
123
|
+
const child = this.children[i];
|
|
124
|
+
if (child === undefined || child === null) {
|
|
125
|
+
offset--;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (child === this.hostNode.childNodes[i + workIndex + offset]) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (child instanceof AurumElement) {
|
|
132
|
+
if (!child.hostNode) {
|
|
133
|
+
child.attachToDom(this.hostNode, i + workIndex + offset);
|
|
134
|
+
}
|
|
135
|
+
if (child.getStartIndex() === i + workIndex + offset) {
|
|
136
|
+
offset += child.getLastIndex() - i - offset - workIndex;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
let start = child.getStartIndex();
|
|
140
|
+
let end = child.getLastIndex();
|
|
141
|
+
for (let ptr = start, swapIteration = 0; ptr <= end; ptr++, swapIteration++) {
|
|
142
|
+
const itemA = this.hostNode.childNodes[i + workIndex + offset + swapIteration];
|
|
143
|
+
const itemB = this.hostNode.childNodes[ptr];
|
|
144
|
+
const parentA = itemA.parentNode;
|
|
145
|
+
const siblingA = itemA.nextSibling === itemB ? itemB : itemA.nextSibling;
|
|
146
|
+
itemB.parentNode.insertBefore(itemA, itemB);
|
|
147
|
+
parentA.insertBefore(itemB, siblingA);
|
|
148
|
+
}
|
|
149
|
+
offset += child.getLastIndex() - i - offset - workIndex;
|
|
150
|
+
}
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (this.hostNode.childNodes[i + workIndex + offset] !== this.contentEndMarker &&
|
|
154
|
+
this.hostNode.childNodes[i + workIndex + offset] !== this.children[i] &&
|
|
155
|
+
this.hostNode.childNodes[i + workIndex + offset] !== ((_a = this.children[i + 1]) === null || _a === void 0 ? void 0 : _a.contentStartMarker)) {
|
|
156
|
+
if (child instanceof HTMLElement || child instanceof Text || child instanceof SVGElement) {
|
|
157
|
+
this.hostNode.removeChild(this.hostNode.childNodes[i + workIndex + offset]);
|
|
158
|
+
if (this.hostNode.childNodes[i + workIndex + offset]) {
|
|
159
|
+
this.lastEndIndex++;
|
|
160
|
+
this.hostNode.insertBefore(child, this.hostNode.childNodes[i + workIndex + offset]);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
this.lastEndIndex++;
|
|
164
|
+
this.hostNode.appendChild(child);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
throw invalidRenderableError(child);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
if (child instanceof HTMLElement || child instanceof Text || child instanceof SVGElement) {
|
|
173
|
+
if (this.hostNode.childNodes[i + workIndex + offset]) {
|
|
174
|
+
this.lastEndIndex++;
|
|
175
|
+
this.hostNode.insertBefore(child, this.hostNode.childNodes[i + workIndex + offset]);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
this.lastEndIndex++;
|
|
179
|
+
this.hostNode.appendChild(child);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
throw invalidRenderableError(child);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
while (this.hostNode.childNodes[i + workIndex + offset] !== this.contentEndMarker) {
|
|
188
|
+
this.lastEndIndex--;
|
|
189
|
+
this.hostNode.removeChild(this.hostNode.childNodes[i + workIndex + offset]);
|
|
190
|
+
}
|
|
191
|
+
this.linkHostChildren();
|
|
192
|
+
}
|
|
193
|
+
linkHostChildren() {
|
|
194
|
+
if (!AURUM_DEVTOOLS_DEBUG_BUILD_ENABLED)
|
|
195
|
+
return;
|
|
196
|
+
linkAurumDomNodeChildren(this.hostNode, this.children.filter((child) => child instanceof HTMLElement || child instanceof SVGElement), this.renderScope.sessionToken);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
AurumElement.id = 1;
|
|
200
|
+
function invalidRenderableError(child) {
|
|
201
|
+
var _a, _b;
|
|
202
|
+
return new Error(`Aurum was given an unsupported type to render "${(_b = (_a = child === null || child === void 0 ? void 0 : child.constructor) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : typeof child}". This can happen if you pass a component function directly to Aurum instead of "rendering" it using JSX syntax or Aurum.factory
|
|
203
|
+
Example:
|
|
204
|
+
// Wrong
|
|
205
|
+
<div>
|
|
206
|
+
{LoginComponent}
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
vs
|
|
210
|
+
|
|
211
|
+
// Correct
|
|
212
|
+
<div>
|
|
213
|
+
<LoginComponent/>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
or
|
|
217
|
+
|
|
218
|
+
// Correct
|
|
219
|
+
<div>
|
|
220
|
+
{Aurum.factory(LoginComponent, {})}
|
|
221
|
+
</div>`);
|
|
222
|
+
}
|
|
223
|
+
export function renderInternal(element, session, prerendering = false) {
|
|
224
|
+
if (element == undefined || typeof element === 'boolean') {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
if (Array.isArray(element)) {
|
|
228
|
+
const result = [];
|
|
229
|
+
for (const item of element) {
|
|
230
|
+
const rendered = renderInternal(item, session, prerendering);
|
|
231
|
+
// Flatten the rendered content into a single array to avoid having to iterate over nested arrays later
|
|
232
|
+
if (rendered !== undefined && rendered !== null) {
|
|
233
|
+
if (Array.isArray(rendered)) {
|
|
234
|
+
result.push(...rendered);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
result.push(rendered);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
if (!prerendering) {
|
|
244
|
+
const type = typeof element;
|
|
245
|
+
if (type === 'string') {
|
|
246
|
+
return document.createTextNode(element);
|
|
247
|
+
}
|
|
248
|
+
else if (type === 'number' || type === 'bigint') {
|
|
249
|
+
return document.createTextNode(element.toString());
|
|
250
|
+
}
|
|
251
|
+
if (element instanceof Promise) {
|
|
252
|
+
const ds = new DataSource();
|
|
253
|
+
element.then((val) => {
|
|
254
|
+
ds.update(val);
|
|
255
|
+
});
|
|
256
|
+
return new SingularAurumElement(ds, createDOMAPI(session));
|
|
257
|
+
}
|
|
258
|
+
else if (element instanceof DataSource) {
|
|
259
|
+
return new SingularAurumElement(element, createDOMAPI(session));
|
|
260
|
+
}
|
|
261
|
+
else if (element instanceof ArrayDataSource) {
|
|
262
|
+
return new ArrayAurumElement(element, createDOMAPI(session));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (element[aurumElementModelIdentitiy]) {
|
|
266
|
+
const model = element;
|
|
267
|
+
if (model.isIntrinsic) {
|
|
268
|
+
// Optimization: skip creating API for no-props basic HTML nodes because they are by far the most frequent.
|
|
269
|
+
const api = model.props ? createDOMAPI(session) : { renderSession: session };
|
|
270
|
+
return renderInternal(model.factory(model.props, model.children, api), session, prerendering);
|
|
271
|
+
}
|
|
272
|
+
return traceAurumComponentRender(model, session, () => {
|
|
273
|
+
var _a;
|
|
274
|
+
const api = createDOMAPI(session);
|
|
275
|
+
return renderInternal(model.factory((_a = model.props) !== null && _a !== void 0 ? _a : {}, model.children, api), session, prerendering);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
// Unsupported types are returned as is in hope that a transclusion component will transform it into something compatible
|
|
279
|
+
return element;
|
|
280
|
+
}
|
|
281
|
+
/** @internal Creates a component API whose prerender output belongs to the DOM host. */
|
|
282
|
+
export function createDOMAPI(session) {
|
|
283
|
+
return createCoreAPI(session, (target, subSession) => renderInternal(target, subSession, true));
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Marks the members of one longest strictly increasing subsequence.
|
|
287
|
+
* Entries flagged true can keep their DOM position during a reorder.
|
|
288
|
+
*/
|
|
289
|
+
function markLongestIncreasingSubsequence(sequence) {
|
|
290
|
+
const stable = new Array(sequence.length).fill(false);
|
|
291
|
+
if (sequence.length === 0)
|
|
292
|
+
return stable;
|
|
293
|
+
const predecessors = new Array(sequence.length);
|
|
294
|
+
// Indices into sequence of the smallest known tail for each subsequence length.
|
|
295
|
+
const tails = [];
|
|
296
|
+
for (let index = 0; index < sequence.length; index++) {
|
|
297
|
+
const value = sequence[index];
|
|
298
|
+
let low = 0;
|
|
299
|
+
let high = tails.length;
|
|
300
|
+
while (low < high) {
|
|
301
|
+
const mid = (low + high) >> 1;
|
|
302
|
+
if (sequence[tails[mid]] < value)
|
|
303
|
+
low = mid + 1;
|
|
304
|
+
else
|
|
305
|
+
high = mid;
|
|
306
|
+
}
|
|
307
|
+
predecessors[index] = low > 0 ? tails[low - 1] : -1;
|
|
308
|
+
tails[low] = index;
|
|
309
|
+
}
|
|
310
|
+
let cursor = tails[tails.length - 1];
|
|
311
|
+
while (cursor !== -1) {
|
|
312
|
+
stable[cursor] = true;
|
|
313
|
+
cursor = predecessors[cursor];
|
|
314
|
+
}
|
|
315
|
+
return stable;
|
|
316
|
+
}
|
|
317
|
+
export class ArrayAurumElement extends AurumElement {
|
|
318
|
+
constructor(dataSource, api) {
|
|
319
|
+
super(dataSource, api);
|
|
320
|
+
this.entries = [];
|
|
321
|
+
this.batchedContentCommit = (change) => {
|
|
322
|
+
if (!this.disposed)
|
|
323
|
+
this.handleBatchedContent(change);
|
|
324
|
+
};
|
|
325
|
+
this.dataSource = dataSource;
|
|
326
|
+
registerAurumRenderBinding(dataSource, this, 'DOM collection', api.cancellationToken, api.renderSession);
|
|
327
|
+
}
|
|
328
|
+
dispose() {
|
|
329
|
+
var _a;
|
|
330
|
+
if (this.disposed) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
this.api.cancellationToken.cancel();
|
|
334
|
+
if (this.disposed)
|
|
335
|
+
return;
|
|
336
|
+
for (const entry of this.entries) {
|
|
337
|
+
(_a = entry.session) === null || _a === void 0 ? void 0 : _a.sessionToken.cancel();
|
|
338
|
+
}
|
|
339
|
+
super.dispose();
|
|
340
|
+
}
|
|
341
|
+
attachToDomBefore(node, referenceNode) {
|
|
342
|
+
super.attachToDomBefore(node, referenceNode);
|
|
343
|
+
//@ts-ignore
|
|
344
|
+
this.contentStartMarker.dataSource = this.dataSource;
|
|
345
|
+
//@ts-ignore
|
|
346
|
+
this.contentEndMarker.dataSource = this.dataSource;
|
|
347
|
+
}
|
|
348
|
+
render(dataSource) {
|
|
349
|
+
dataSource.listenAndRepeat((n) => {
|
|
350
|
+
if (!this.disposed) {
|
|
351
|
+
this.handleNewContent(n);
|
|
352
|
+
}
|
|
353
|
+
}, this.api.cancellationToken);
|
|
354
|
+
}
|
|
355
|
+
queueBatchedContent(change) {
|
|
356
|
+
queueRenderUpdate(this, this.batchedContentCommit, change);
|
|
357
|
+
}
|
|
358
|
+
static setRenderBatching(active) {
|
|
359
|
+
ArrayAurumElement.prototype.handleNewContent = active ? ArrayAurumElement.prototype.queueBatchedContent : ArrayAurumElement.immediateContentCommit;
|
|
360
|
+
}
|
|
361
|
+
handleBatchedContent(change) {
|
|
362
|
+
const batchedChange = {
|
|
363
|
+
...change,
|
|
364
|
+
operation: 'merge',
|
|
365
|
+
operationDetailed: 'merge',
|
|
366
|
+
index: 0,
|
|
367
|
+
items: change.newState
|
|
368
|
+
};
|
|
369
|
+
// Identity metadata is non-enumerable and therefore is not preserved by
|
|
370
|
+
// the spread above. Carry the final identities into snapshot reconciliation.
|
|
371
|
+
Object.defineProperty(batchedChange, 'newStateIdentities', {
|
|
372
|
+
value: change.newStateIdentities,
|
|
373
|
+
enumerable: false
|
|
374
|
+
});
|
|
375
|
+
ArrayAurumElement.immediateContentCommit.call(this, batchedChange);
|
|
376
|
+
}
|
|
377
|
+
synchronizeChildren() {
|
|
378
|
+
this.children = this.entries.map((entry) => entry.rendered);
|
|
379
|
+
}
|
|
380
|
+
removeEntriesFromDom(index, count) {
|
|
381
|
+
const removed = this.entries.splice(index, count);
|
|
382
|
+
this.children.splice(index, count);
|
|
383
|
+
this.detachEntriesFromDom(removed);
|
|
384
|
+
}
|
|
385
|
+
detachEntriesFromDom(removed) {
|
|
386
|
+
var _a;
|
|
387
|
+
for (const entry of removed) {
|
|
388
|
+
if (!(entry.rendered instanceof AurumElement)) {
|
|
389
|
+
entry.rendered.remove();
|
|
390
|
+
}
|
|
391
|
+
(_a = entry.session) === null || _a === void 0 ? void 0 : _a.sessionToken.cancel();
|
|
392
|
+
}
|
|
393
|
+
this.lastEndIndex = undefined;
|
|
394
|
+
}
|
|
395
|
+
insertEntryGapBefore(entries, anchorIndex) {
|
|
396
|
+
if (entries.length === 0)
|
|
397
|
+
return;
|
|
398
|
+
const anchor = anchorIndex >= this.entries.length ? this.contentEndMarker : this.entries[anchorIndex].rendered;
|
|
399
|
+
if (entries.every((entry) => !(entry.rendered instanceof AurumElement))) {
|
|
400
|
+
const fragment = document.createDocumentFragment();
|
|
401
|
+
for (const entry of entries)
|
|
402
|
+
fragment.appendChild(entry.rendered);
|
|
403
|
+
const referenceNode = anchor instanceof AurumElement ? this.hostNode.childNodes[this.getEntryDomIndex(anchorIndex)] : anchor;
|
|
404
|
+
this.hostNode.insertBefore(fragment, referenceNode);
|
|
405
|
+
this.lastEndIndex = undefined;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const targetIndex = this.getEntryDomIndex(anchorIndex);
|
|
409
|
+
for (let itemIndex = entries.length - 1; itemIndex >= 0; itemIndex--) {
|
|
410
|
+
this.attachRendered(entries[itemIndex].rendered, targetIndex);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
getEntryDomIndex(index) {
|
|
414
|
+
if (index === 0)
|
|
415
|
+
return this.getWorkIndex();
|
|
416
|
+
if (index >= this.entries.length)
|
|
417
|
+
return this.getLastIndex();
|
|
418
|
+
const rendered = this.entries[index].rendered;
|
|
419
|
+
for (let domIndex = this.getWorkIndex(); domIndex < this.getLastIndex(); domIndex++) {
|
|
420
|
+
const node = this.hostNode.childNodes[domIndex];
|
|
421
|
+
if (node === rendered || node.owner === rendered)
|
|
422
|
+
return domIndex;
|
|
423
|
+
}
|
|
424
|
+
return this.getLastIndex();
|
|
425
|
+
}
|
|
426
|
+
insertEntries(index, values, identities, attachCalls) {
|
|
427
|
+
const anchor = index >= this.entries.length ? this.contentEndMarker : this.entries[index].rendered;
|
|
428
|
+
const referenceNode = anchor instanceof AurumElement ? undefined : anchor;
|
|
429
|
+
const newEntries = values.map((value, itemIndex) => this.renderEntry(value, identities === null || identities === void 0 ? void 0 : identities[itemIndex], attachCalls));
|
|
430
|
+
// Ranges still require a numeric DOM position. Resolve it before changing
|
|
431
|
+
// the entry array, but avoid the scan for the overwhelmingly common case
|
|
432
|
+
// where both the inserted entries and their anchor are ordinary nodes.
|
|
433
|
+
const targetIndex = newEntries.some((entry) => entry.rendered instanceof AurumElement) ? this.getEntryDomIndex(index) : undefined;
|
|
434
|
+
this.entries.splice(index, 0, ...newEntries);
|
|
435
|
+
this.children.splice(index, 0, ...newEntries.map((entry) => entry.rendered));
|
|
436
|
+
if (newEntries.length > 0 && newEntries.every((entry) => !(entry.rendered instanceof AurumElement))) {
|
|
437
|
+
const fragment = document.createDocumentFragment();
|
|
438
|
+
for (const entry of newEntries)
|
|
439
|
+
fragment.appendChild(entry.rendered);
|
|
440
|
+
this.hostNode.insertBefore(fragment, referenceNode !== null && referenceNode !== void 0 ? referenceNode : this.contentEndMarker);
|
|
441
|
+
this.lastEndIndex = undefined;
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
// Insert before one stable anchor from right to left. This works for both
|
|
445
|
+
// single DOM nodes and comment-bounded AurumElement ranges.
|
|
446
|
+
for (let itemIndex = newEntries.length - 1; itemIndex >= 0; itemIndex--) {
|
|
447
|
+
this.attachRendered(newEntries[itemIndex].rendered, targetIndex);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
tryApplySubsequenceRemoval(desiredIdentities) {
|
|
451
|
+
if (desiredIdentities.length >= this.entries.length)
|
|
452
|
+
return false;
|
|
453
|
+
const removals = [];
|
|
454
|
+
const retainedEntries = [];
|
|
455
|
+
let desiredIndex = 0;
|
|
456
|
+
let removalStart = -1;
|
|
457
|
+
for (let entryIndex = 0; entryIndex < this.entries.length; entryIndex++) {
|
|
458
|
+
if (desiredIndex < desiredIdentities.length && this.entries[entryIndex].identity === desiredIdentities[desiredIndex]) {
|
|
459
|
+
if (removalStart !== -1) {
|
|
460
|
+
removals.push({ index: removalStart, count: entryIndex - removalStart });
|
|
461
|
+
removalStart = -1;
|
|
462
|
+
}
|
|
463
|
+
retainedEntries.push(this.entries[entryIndex]);
|
|
464
|
+
desiredIndex++;
|
|
465
|
+
}
|
|
466
|
+
else if (removalStart === -1) {
|
|
467
|
+
removalStart = entryIndex;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (desiredIndex !== desiredIdentities.length)
|
|
471
|
+
return false;
|
|
472
|
+
if (removalStart !== -1)
|
|
473
|
+
removals.push({ index: removalStart, count: this.entries.length - removalStart });
|
|
474
|
+
// The DOM groups are independent, so remove them without repeatedly
|
|
475
|
+
// splicing the backing arrays (which becomes quadratic for a filter).
|
|
476
|
+
for (const removal of removals) {
|
|
477
|
+
this.detachEntriesFromDom(this.entries.slice(removal.index, removal.index + removal.count));
|
|
478
|
+
}
|
|
479
|
+
this.entries = retainedEntries;
|
|
480
|
+
this.synchronizeChildren();
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
tryApplySubsequenceGrowth(desiredIdentities, desiredValues, attachCalls) {
|
|
484
|
+
if (desiredIdentities.length <= this.entries.length)
|
|
485
|
+
return false;
|
|
486
|
+
const gaps = [];
|
|
487
|
+
let entryIndex = 0;
|
|
488
|
+
let gapStart = -1;
|
|
489
|
+
for (let desiredIndex = 0; desiredIndex < desiredIdentities.length; desiredIndex++) {
|
|
490
|
+
if (entryIndex < this.entries.length && desiredIdentities[desiredIndex] === this.entries[entryIndex].identity) {
|
|
491
|
+
if (gapStart !== -1) {
|
|
492
|
+
gaps.push({ valueIndex: gapStart, end: desiredIndex, entryIndex });
|
|
493
|
+
gapStart = -1;
|
|
494
|
+
}
|
|
495
|
+
entryIndex++;
|
|
496
|
+
}
|
|
497
|
+
else if (gapStart === -1) {
|
|
498
|
+
gapStart = desiredIndex;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
if (entryIndex !== this.entries.length)
|
|
502
|
+
return false;
|
|
503
|
+
if (gapStart !== -1)
|
|
504
|
+
gaps.push({ valueIndex: gapStart, end: desiredIdentities.length, entryIndex });
|
|
505
|
+
const renderedGaps = gaps.map((gap) => ({
|
|
506
|
+
...gap,
|
|
507
|
+
entries: desiredValues
|
|
508
|
+
.slice(gap.valueIndex, gap.end)
|
|
509
|
+
.map((value, index) => this.renderEntry(value, desiredIdentities[gap.valueIndex + index], attachCalls))
|
|
510
|
+
}));
|
|
511
|
+
// Insert from right to left against the unchanged retained-entry array.
|
|
512
|
+
// This keeps every anchor stable and avoids thousands of array splices.
|
|
513
|
+
for (let index = renderedGaps.length - 1; index >= 0; index--) {
|
|
514
|
+
const gap = renderedGaps[index];
|
|
515
|
+
this.insertEntryGapBefore(gap.entries, gap.entryIndex);
|
|
516
|
+
}
|
|
517
|
+
const desiredEntries = [];
|
|
518
|
+
let retainedStart = 0;
|
|
519
|
+
for (const gap of renderedGaps) {
|
|
520
|
+
desiredEntries.push(...this.entries.slice(retainedStart, gap.entryIndex), ...gap.entries);
|
|
521
|
+
retainedStart = gap.entryIndex;
|
|
522
|
+
}
|
|
523
|
+
desiredEntries.push(...this.entries.slice(retainedStart));
|
|
524
|
+
this.entries = desiredEntries;
|
|
525
|
+
this.synchronizeChildren();
|
|
526
|
+
// Each gap is committed as one fragment when its entries have a single
|
|
527
|
+
// DOM root; retained nodes are never detached or moved.
|
|
528
|
+
return true;
|
|
529
|
+
}
|
|
530
|
+
tryApplySingleRotation(desiredIdentities) {
|
|
531
|
+
const length = this.entries.length;
|
|
532
|
+
if (length < 2 || desiredIdentities.length !== length)
|
|
533
|
+
return false;
|
|
534
|
+
const first = this.entries[0];
|
|
535
|
+
const firstRendered = first.rendered;
|
|
536
|
+
if (desiredIdentities[length - 1] === first.identity) {
|
|
537
|
+
let isLeftRotation = true;
|
|
538
|
+
for (let index = 0; index < length - 1; index++) {
|
|
539
|
+
if (desiredIdentities[index] !== this.entries[index + 1].identity) {
|
|
540
|
+
isLeftRotation = false;
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (isLeftRotation) {
|
|
545
|
+
this.moveRenderedBefore(firstRendered, this.contentEndMarker);
|
|
546
|
+
this.entries.push(this.entries.shift());
|
|
547
|
+
this.children.push(this.children.shift());
|
|
548
|
+
return true;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
const last = this.entries[length - 1];
|
|
552
|
+
const lastRendered = last.rendered;
|
|
553
|
+
if (desiredIdentities[0] === last.identity) {
|
|
554
|
+
let isRightRotation = true;
|
|
555
|
+
for (let index = 1; index < length; index++) {
|
|
556
|
+
if (desiredIdentities[index] !== this.entries[index - 1].identity) {
|
|
557
|
+
isRightRotation = false;
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
if (isRightRotation) {
|
|
562
|
+
this.moveRenderedBefore(lastRendered, this.firstDomNode(firstRendered));
|
|
563
|
+
this.entries.unshift(this.entries.pop());
|
|
564
|
+
this.children.unshift(this.children.pop());
|
|
565
|
+
return true;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Keyed reconciliation for arbitrary merges: retained entries forming a longest
|
|
572
|
+
* increasing subsequence of the previous order stay put, everything else is
|
|
573
|
+
* moved or inserted right-to-left against a stable anchor. DOM operations are
|
|
574
|
+
* proportional to the entries that actually changed position.
|
|
575
|
+
*/
|
|
576
|
+
applyMergeReconciliation(desiredIdentities, desiredValues, attachCalls) {
|
|
577
|
+
const entriesByIdentity = new Map(this.entries.map((entry) => [entry.identity, entry]));
|
|
578
|
+
const retainedEntries = new Set();
|
|
579
|
+
const desiredEntries = new Array(desiredValues.length);
|
|
580
|
+
const isNewEntry = new Array(desiredValues.length);
|
|
581
|
+
for (let index = 0; index < desiredValues.length; index++) {
|
|
582
|
+
const retained = entriesByIdentity.get(desiredIdentities[index]);
|
|
583
|
+
if (retained !== undefined) {
|
|
584
|
+
desiredEntries[index] = retained;
|
|
585
|
+
isNewEntry[index] = false;
|
|
586
|
+
retainedEntries.add(retained);
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
desiredEntries[index] = this.renderEntry(desiredValues[index], desiredIdentities[index], attachCalls);
|
|
590
|
+
isNewEntry[index] = true;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
const removedEntries = [];
|
|
594
|
+
const previousIndexByEntry = new Map();
|
|
595
|
+
for (let index = 0; index < this.entries.length; index++) {
|
|
596
|
+
const entry = this.entries[index];
|
|
597
|
+
if (retainedEntries.has(entry)) {
|
|
598
|
+
previousIndexByEntry.set(entry, index);
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
removedEntries.push(entry);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (removedEntries.length > 0) {
|
|
605
|
+
this.detachEntriesFromDom(removedEntries);
|
|
606
|
+
}
|
|
607
|
+
if (retainedEntries.size === 0 && desiredEntries.every((entry) => !(entry.rendered instanceof AurumElement))) {
|
|
608
|
+
const fragment = document.createDocumentFragment();
|
|
609
|
+
for (const entry of desiredEntries)
|
|
610
|
+
fragment.appendChild(entry.rendered);
|
|
611
|
+
this.hostNode.insertBefore(fragment, this.contentEndMarker);
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
const retainedDesiredIndices = [];
|
|
615
|
+
const previousOrder = [];
|
|
616
|
+
for (let index = 0; index < desiredEntries.length; index++) {
|
|
617
|
+
if (!isNewEntry[index]) {
|
|
618
|
+
retainedDesiredIndices.push(index);
|
|
619
|
+
previousOrder.push(previousIndexByEntry.get(desiredEntries[index]));
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
const stableFlags = markLongestIncreasingSubsequence(previousOrder);
|
|
623
|
+
const stableByDesiredIndex = new Array(desiredEntries.length).fill(false);
|
|
624
|
+
for (let sequenceIndex = 0; sequenceIndex < retainedDesiredIndices.length; sequenceIndex++) {
|
|
625
|
+
if (stableFlags[sequenceIndex])
|
|
626
|
+
stableByDesiredIndex[retainedDesiredIndices[sequenceIndex]] = true;
|
|
627
|
+
}
|
|
628
|
+
let anchor = this.contentEndMarker;
|
|
629
|
+
for (let index = desiredEntries.length - 1; index >= 0; index--) {
|
|
630
|
+
const rendered = desiredEntries[index].rendered;
|
|
631
|
+
if (stableByDesiredIndex[index]) {
|
|
632
|
+
anchor = this.firstDomNode(rendered);
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
if (isNewEntry[index]) {
|
|
636
|
+
if (rendered instanceof AurumElement) {
|
|
637
|
+
rendered.attachToDomBefore(this.hostNode, anchor);
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
this.hostNode.insertBefore(rendered, anchor);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
else {
|
|
644
|
+
this.moveRenderedBefore(rendered, anchor);
|
|
645
|
+
}
|
|
646
|
+
anchor = this.firstDomNode(rendered);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
this.entries = desiredEntries;
|
|
650
|
+
this.synchronizeChildren();
|
|
651
|
+
this.lastEndIndex = undefined;
|
|
652
|
+
this.linkHostChildren();
|
|
653
|
+
}
|
|
654
|
+
handleNewContent(change) {
|
|
655
|
+
var _a, _b, _c;
|
|
656
|
+
if (this.hostNode === undefined) {
|
|
657
|
+
throw new Error('illegal state: Aurum element was not attched to anything');
|
|
658
|
+
}
|
|
659
|
+
let optimized = false;
|
|
660
|
+
const attachCalls = [];
|
|
661
|
+
switch (change.operationDetailed) {
|
|
662
|
+
case 'merge': {
|
|
663
|
+
const desiredIdentities = (_a = change.newStateIdentities) !== null && _a !== void 0 ? _a : [];
|
|
664
|
+
if (this.tryApplySingleRotation(desiredIdentities)) {
|
|
665
|
+
optimized = true;
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
const sharedPrefixLength = Math.min(this.entries.length, desiredIdentities.length);
|
|
669
|
+
let retainedPrefixLength = 0;
|
|
670
|
+
while (retainedPrefixLength < sharedPrefixLength && this.entries[retainedPrefixLength].identity === desiredIdentities[retainedPrefixLength]) {
|
|
671
|
+
retainedPrefixLength++;
|
|
672
|
+
}
|
|
673
|
+
if (retainedPrefixLength === this.entries.length) {
|
|
674
|
+
if (change.newState.length > this.entries.length) {
|
|
675
|
+
this.insertEntries(this.entries.length, change.newState.slice(this.entries.length), desiredIdentities.slice(this.entries.length), attachCalls);
|
|
676
|
+
}
|
|
677
|
+
optimized = true;
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
if (retainedPrefixLength === change.newState.length) {
|
|
681
|
+
this.removeEntriesFromDom(change.newState.length, this.entries.length - change.newState.length);
|
|
682
|
+
optimized = true;
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
if (this.tryApplySubsequenceRemoval(desiredIdentities)) {
|
|
686
|
+
optimized = true;
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
if (this.tryApplySubsequenceGrowth(desiredIdentities, change.newState, attachCalls)) {
|
|
690
|
+
optimized = true;
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
this.applyMergeReconciliation(desiredIdentities, change.newState, attachCalls);
|
|
694
|
+
optimized = true;
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
case 'remove':
|
|
698
|
+
case 'removeLeft':
|
|
699
|
+
case 'removeRight':
|
|
700
|
+
this.removeEntriesFromDom(change.index, change.items.length);
|
|
701
|
+
optimized = true;
|
|
702
|
+
break;
|
|
703
|
+
case 'append': {
|
|
704
|
+
this.insertEntries(this.entries.length, change.items, change.itemIdentities, attachCalls);
|
|
705
|
+
optimized = true;
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
case 'replace': {
|
|
709
|
+
const oldEntry = this.entries[change.index];
|
|
710
|
+
const newEntry = this.renderEntry(change.items[0], (_b = change.itemIdentities) === null || _b === void 0 ? void 0 : _b[0], attachCalls);
|
|
711
|
+
if (!(oldEntry.rendered instanceof AurumElement) && !(newEntry.rendered instanceof AurumElement)) {
|
|
712
|
+
oldEntry.rendered.replaceWith(newEntry.rendered);
|
|
713
|
+
(_c = oldEntry.session) === null || _c === void 0 ? void 0 : _c.sessionToken.cancel();
|
|
714
|
+
this.entries[change.index] = newEntry;
|
|
715
|
+
this.children[change.index] = newEntry.rendered;
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
const targetIndex = this.getEntryDomIndex(change.index);
|
|
719
|
+
this.removeEntriesFromDom(change.index, 1);
|
|
720
|
+
this.entries.splice(change.index, 0, newEntry);
|
|
721
|
+
this.children.splice(change.index, 0, newEntry.rendered);
|
|
722
|
+
this.attachRendered(newEntry.rendered, targetIndex);
|
|
723
|
+
}
|
|
724
|
+
optimized = true;
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
case 'swap': {
|
|
728
|
+
const entryA = this.entries[change.index];
|
|
729
|
+
const entryB = this.entries[change.index2];
|
|
730
|
+
this.swapRendered(entryA.rendered, entryB.rendered, change.index, change.index2);
|
|
731
|
+
this.entries[change.index] = entryB;
|
|
732
|
+
this.entries[change.index2] = entryA;
|
|
733
|
+
this.children[change.index] = entryB.rendered;
|
|
734
|
+
this.children[change.index2] = entryA.rendered;
|
|
735
|
+
optimized = true;
|
|
736
|
+
break;
|
|
737
|
+
}
|
|
738
|
+
case 'prepend':
|
|
739
|
+
this.insertEntries(0, change.items, change.itemIdentities, attachCalls);
|
|
740
|
+
optimized = true;
|
|
741
|
+
break;
|
|
742
|
+
case 'insert':
|
|
743
|
+
this.insertEntries(change.index, change.items, change.itemIdentities, attachCalls);
|
|
744
|
+
optimized = true;
|
|
745
|
+
break;
|
|
746
|
+
case 'clear':
|
|
747
|
+
this.removeEntriesFromDom(0, this.entries.length);
|
|
748
|
+
optimized = true;
|
|
749
|
+
break;
|
|
750
|
+
default:
|
|
751
|
+
throw new Error(`DOM updates from ${change.operationDetailed} are not supported`);
|
|
752
|
+
}
|
|
753
|
+
if (!optimized) {
|
|
754
|
+
this.synchronizeChildren();
|
|
755
|
+
this.updateDom();
|
|
756
|
+
}
|
|
757
|
+
for (const call of attachCalls) {
|
|
758
|
+
call();
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
renderEntry(sourceValue, identity, attachCalls) {
|
|
762
|
+
const sourceType = typeof sourceValue;
|
|
763
|
+
if (sourceType === 'string' || sourceType === 'number' || sourceType === 'bigint') {
|
|
764
|
+
return {
|
|
765
|
+
identity,
|
|
766
|
+
sourceValue,
|
|
767
|
+
rendered: document.createTextNode(String(sourceValue))
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
if (sourceValue instanceof HTMLElement || sourceValue instanceof Text || sourceValue instanceof SVGElement) {
|
|
771
|
+
return { identity, sourceValue, rendered: sourceValue };
|
|
772
|
+
}
|
|
773
|
+
const session = createRenderSession(this.renderScope);
|
|
774
|
+
let rendered = renderInternal(sourceValue, session);
|
|
775
|
+
if (Array.isArray(rendered)) {
|
|
776
|
+
if (rendered.length === 1) {
|
|
777
|
+
rendered = rendered[0];
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
rendered = new StaticAurumElement(rendered, createDOMAPI(session));
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
else if (rendered === undefined || rendered === null) {
|
|
784
|
+
rendered = new StaticAurumElement([], createDOMAPI(session));
|
|
785
|
+
}
|
|
786
|
+
if (rendered instanceof AurumElement) {
|
|
787
|
+
session.sessionToken.addCancellable(() => rendered.dispose());
|
|
788
|
+
}
|
|
789
|
+
attachCalls.push(...session.attachCalls);
|
|
790
|
+
return { identity, sourceValue, rendered: rendered, session };
|
|
791
|
+
}
|
|
792
|
+
attachRendered(rendered, targetIndex) {
|
|
793
|
+
if (rendered instanceof AurumElement) {
|
|
794
|
+
rendered.attachToDom(this.hostNode, targetIndex);
|
|
795
|
+
this.lastEndIndex = this.getLastIndex();
|
|
796
|
+
return this.lastEndIndex;
|
|
797
|
+
}
|
|
798
|
+
if (rendered instanceof HTMLElement || rendered instanceof Text || rendered instanceof SVGElement) {
|
|
799
|
+
this.hostNode.insertBefore(rendered, this.hostNode.childNodes[targetIndex]);
|
|
800
|
+
this.lastEndIndex++;
|
|
801
|
+
return targetIndex + 1;
|
|
802
|
+
}
|
|
803
|
+
throw invalidRenderableError(rendered);
|
|
804
|
+
}
|
|
805
|
+
swapDomNodes(nodeA, nodeB) {
|
|
806
|
+
if (nodeA.nextSibling === nodeB) {
|
|
807
|
+
nodeB.parentNode.insertBefore(nodeB, nodeA);
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
if (nodeB.nextSibling === nodeA) {
|
|
811
|
+
nodeA.parentNode.insertBefore(nodeA, nodeB);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
const parentA = nodeA.parentNode;
|
|
815
|
+
const siblingA = nodeA.nextSibling;
|
|
816
|
+
nodeB.parentNode.insertBefore(nodeA, nodeB);
|
|
817
|
+
parentA.insertBefore(nodeB, siblingA);
|
|
818
|
+
}
|
|
819
|
+
firstDomNode(rendered) {
|
|
820
|
+
return rendered instanceof AurumElement ? aurumElementDomBounds.get(rendered)[0] : rendered;
|
|
821
|
+
}
|
|
822
|
+
lastDomNode(rendered) {
|
|
823
|
+
return rendered instanceof AurumElement ? aurumElementDomBounds.get(rendered)[1] : rendered;
|
|
824
|
+
}
|
|
825
|
+
moveRenderedBefore(rendered, referenceNode) {
|
|
826
|
+
if (!(rendered instanceof AurumElement)) {
|
|
827
|
+
if (rendered.nextSibling !== referenceNode) {
|
|
828
|
+
this.hostNode.insertBefore(rendered, referenceNode);
|
|
829
|
+
}
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
const [first, last] = aurumElementDomBounds.get(rendered);
|
|
833
|
+
if (last.nextSibling === referenceNode)
|
|
834
|
+
return;
|
|
835
|
+
const afterLast = last.nextSibling;
|
|
836
|
+
let node = first;
|
|
837
|
+
while (node !== afterLast) {
|
|
838
|
+
const next = node.nextSibling;
|
|
839
|
+
this.hostNode.insertBefore(node, referenceNode);
|
|
840
|
+
node = next;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
swapRendered(first, second, firstIndex, secondIndex) {
|
|
844
|
+
if (!(first instanceof AurumElement) && !(second instanceof AurumElement)) {
|
|
845
|
+
this.swapDomNodes(first, second);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
const low = firstIndex < secondIndex ? first : second;
|
|
849
|
+
const high = firstIndex < secondIndex ? second : first;
|
|
850
|
+
const adjacent = this.lastDomNode(low).nextSibling === this.firstDomNode(high);
|
|
851
|
+
const afterHigh = this.lastDomNode(high).nextSibling;
|
|
852
|
+
this.moveRenderedBefore(high, this.firstDomNode(low));
|
|
853
|
+
if (!adjacent)
|
|
854
|
+
this.moveRenderedBefore(low, afterHigh);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
ArrayAurumElement.immediateContentCommit = ArrayAurumElement.prototype.handleNewContent;
|
|
858
|
+
export class SingularAurumElement extends AurumElement {
|
|
859
|
+
constructor(dataSource, api) {
|
|
860
|
+
super(dataSource, api);
|
|
861
|
+
this.batchedContentCommit = (newValue) => {
|
|
862
|
+
if (!this.disposed)
|
|
863
|
+
SingularAurumElement.immediateContentCommit.call(this, newValue);
|
|
864
|
+
};
|
|
865
|
+
this.api.cancellationToken.addCancellable(() => { var _a; return (_a = this.renderSession) === null || _a === void 0 ? void 0 : _a.sessionToken.cancel(); });
|
|
866
|
+
this.dataSource = dataSource;
|
|
867
|
+
registerAurumRenderBinding(dataSource, this, 'DOM reactive content', api.cancellationToken, api.renderSession);
|
|
868
|
+
}
|
|
869
|
+
dispose() {
|
|
870
|
+
if (this.disposed) {
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
this.api.cancellationToken.cancel();
|
|
874
|
+
if (this.disposed)
|
|
875
|
+
return;
|
|
876
|
+
super.dispose();
|
|
877
|
+
}
|
|
878
|
+
attachToDomBefore(node, referenceNode) {
|
|
879
|
+
super.attachToDomBefore(node, referenceNode);
|
|
880
|
+
//@ts-ignore
|
|
881
|
+
this.contentStartMarker.dataSource = this.dataSource;
|
|
882
|
+
//@ts-ignore
|
|
883
|
+
this.contentEndMarker.dataSource = this.dataSource;
|
|
884
|
+
}
|
|
885
|
+
render(dataSource) {
|
|
886
|
+
dataSource.listenAndRepeat((n) => {
|
|
887
|
+
if (!this.disposed) {
|
|
888
|
+
this.handleNewContent(n);
|
|
889
|
+
}
|
|
890
|
+
}, this.api.cancellationToken);
|
|
891
|
+
}
|
|
892
|
+
queueBatchedContent(newValue) {
|
|
893
|
+
queueRenderUpdate(this, this.batchedContentCommit, newValue);
|
|
894
|
+
}
|
|
895
|
+
static setRenderBatching(active) {
|
|
896
|
+
SingularAurumElement.prototype.handleNewContent = active
|
|
897
|
+
? SingularAurumElement.prototype.queueBatchedContent
|
|
898
|
+
: SingularAurumElement.immediateContentCommit;
|
|
899
|
+
}
|
|
900
|
+
handleNewContent(newValue) {
|
|
901
|
+
if (this.lastValue === newValue) {
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
let optimized = false;
|
|
905
|
+
if (this.children.length === 1 && this.children[0] instanceof Text) {
|
|
906
|
+
const type = typeof newValue;
|
|
907
|
+
if (type === 'string' || type === 'bigint' || type === 'number') {
|
|
908
|
+
this.children[0].nodeValue = String(newValue);
|
|
909
|
+
optimized = true;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (!optimized) {
|
|
913
|
+
this.fullRebuild(newValue);
|
|
914
|
+
this.updateDom();
|
|
915
|
+
for (const cb of this.renderSession.attachCalls) {
|
|
916
|
+
cb();
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
this.lastValue = newValue;
|
|
920
|
+
}
|
|
921
|
+
fullRebuild(newValue) {
|
|
922
|
+
this.clearContent();
|
|
923
|
+
this.endSession();
|
|
924
|
+
this.renderSession = createRenderSession(this.renderScope);
|
|
925
|
+
let rendered = renderInternal(newValue, this.renderSession);
|
|
926
|
+
if (rendered === undefined) {
|
|
927
|
+
this.children = [];
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
if (!Array.isArray(rendered)) {
|
|
931
|
+
rendered = [rendered];
|
|
932
|
+
}
|
|
933
|
+
for (const item of rendered) {
|
|
934
|
+
if (item instanceof AurumElement) {
|
|
935
|
+
this.renderSession.sessionToken.addCancellable(() => {
|
|
936
|
+
item.dispose();
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (Array.isArray(rendered)) {
|
|
941
|
+
this.children = rendered;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
endSession() {
|
|
945
|
+
if (this.renderSession) {
|
|
946
|
+
this.renderSession.sessionToken.cancel();
|
|
947
|
+
this.renderSession = undefined;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
SingularAurumElement.immediateContentCommit = SingularAurumElement.prototype.handleNewContent;
|
|
952
|
+
/**
|
|
953
|
+
* A lightweight range used only when an array entry has zero or multiple roots.
|
|
954
|
+
*/
|
|
955
|
+
class StaticAurumElement extends AurumElement {
|
|
956
|
+
constructor(children, api) {
|
|
957
|
+
super(undefined, api);
|
|
958
|
+
this.children = children;
|
|
959
|
+
}
|
|
960
|
+
attachToDomBefore(node, referenceNode) {
|
|
961
|
+
super.attachToDomBefore(node, referenceNode);
|
|
962
|
+
this.updateDom();
|
|
963
|
+
}
|
|
964
|
+
render() { }
|
|
965
|
+
}
|
|
966
|
+
listenToRenderBatchState((active) => {
|
|
967
|
+
ArrayAurumElement.setRenderBatching(active);
|
|
968
|
+
SingularAurumElement.setRenderBatching(active);
|
|
969
|
+
});
|
|
970
|
+
//# sourceMappingURL=dom_runtime.js.map
|