@symfony/ux-live-component 3.2.0 → 3.5.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/dist/live_controller.d.ts +13 -0
- package/dist/live_controller.js +746 -530
- package/package.json +2 -2
package/dist/live_controller.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Controller } from "@hotwired/stimulus";
|
|
2
2
|
var BackendRequest_default = class {
|
|
3
|
+
promise;
|
|
4
|
+
actions;
|
|
5
|
+
updatedModels;
|
|
6
|
+
isResolved = false;
|
|
3
7
|
constructor(promise, actions, updateModels) {
|
|
4
|
-
this.isResolved = false;
|
|
5
8
|
this.promise = promise;
|
|
6
9
|
this.promise.then((response) => {
|
|
7
10
|
this.isResolved = true;
|
|
@@ -18,6 +21,9 @@ var BackendRequest_default = class {
|
|
|
18
21
|
}
|
|
19
22
|
};
|
|
20
23
|
var RequestBuilder_default = class {
|
|
24
|
+
url;
|
|
25
|
+
method;
|
|
26
|
+
credentials;
|
|
21
27
|
constructor(url, method = "post", credentials = "same-origin") {
|
|
22
28
|
this.url = url;
|
|
23
29
|
this.method = method;
|
|
@@ -77,6 +83,7 @@ var RequestBuilder_default = class {
|
|
|
77
83
|
}
|
|
78
84
|
};
|
|
79
85
|
var Backend_default = class {
|
|
86
|
+
requestBuilder;
|
|
80
87
|
constructor(url, method = "post", credentials = "same-origin") {
|
|
81
88
|
this.requestBuilder = new RequestBuilder_default(url, method, credentials);
|
|
82
89
|
}
|
|
@@ -86,18 +93,55 @@ var Backend_default = class {
|
|
|
86
93
|
}
|
|
87
94
|
};
|
|
88
95
|
var BackendResponse_default = class {
|
|
96
|
+
response;
|
|
97
|
+
body;
|
|
98
|
+
liveUrl;
|
|
99
|
+
download = null;
|
|
100
|
+
parsePromise = null;
|
|
89
101
|
constructor(response) {
|
|
90
102
|
this.response = response;
|
|
91
103
|
}
|
|
92
104
|
async getBody() {
|
|
93
|
-
if (!this.
|
|
105
|
+
if (!this.parsePromise) this.parsePromise = this.parse();
|
|
106
|
+
await this.parsePromise;
|
|
94
107
|
return this.body;
|
|
95
108
|
}
|
|
109
|
+
getDownload() {
|
|
110
|
+
return this.download;
|
|
111
|
+
}
|
|
96
112
|
getLiveUrl() {
|
|
97
113
|
if (void 0 === this.liveUrl) this.liveUrl = this.response.headers.get("X-Live-Url");
|
|
98
114
|
return this.liveUrl;
|
|
99
115
|
}
|
|
116
|
+
getDownloadUrl() {
|
|
117
|
+
return this.response.headers.get("X-Live-Download-Url");
|
|
118
|
+
}
|
|
119
|
+
isRemoved() {
|
|
120
|
+
return this.response.headers.has("X-Live-Remove");
|
|
121
|
+
}
|
|
122
|
+
async parse() {
|
|
123
|
+
const htmlLength = this.response.headers.get("X-Live-Html-Length");
|
|
124
|
+
if (null === htmlLength) {
|
|
125
|
+
this.body = await this.response.text();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const buffer = await this.response.arrayBuffer();
|
|
129
|
+
const splitAt = Number.parseInt(htmlLength, 10);
|
|
130
|
+
this.body = new TextDecoder().decode(buffer.slice(0, splitAt));
|
|
131
|
+
this.download = {
|
|
132
|
+
filename: decodeFilename(this.response.headers.get("X-Live-Download-Filename")),
|
|
133
|
+
blob: new Blob([buffer.slice(splitAt)], { type: this.response.headers.get("X-Live-Download-Type") ?? "application/octet-stream" })
|
|
134
|
+
};
|
|
135
|
+
}
|
|
100
136
|
};
|
|
137
|
+
function decodeFilename(value) {
|
|
138
|
+
if (!value) return "download";
|
|
139
|
+
try {
|
|
140
|
+
return decodeURIComponent(value) || "download";
|
|
141
|
+
} catch {
|
|
142
|
+
return "download";
|
|
143
|
+
}
|
|
144
|
+
}
|
|
101
145
|
function getElementAsTagText(element) {
|
|
102
146
|
return element.innerHTML ? element.outerHTML.slice(0, element.outerHTML.indexOf(element.innerHTML)) : element.outerHTML;
|
|
103
147
|
}
|
|
@@ -262,6 +306,7 @@ function trimAll(str) {
|
|
|
262
306
|
return str.replace(/[\s]+/g, " ").trim();
|
|
263
307
|
}
|
|
264
308
|
function normalizeModelName(model) {
|
|
309
|
+
if (!model.includes("[") && !model.includes("]")) return model;
|
|
265
310
|
return model.replace(/\[]$/, "").split("[").map((s) => s.replace("]", "")).join(".");
|
|
266
311
|
}
|
|
267
312
|
function getValueFromElement(element, valueStore) {
|
|
@@ -386,9 +431,7 @@ function isNumericalInputElement(element) {
|
|
|
386
431
|
return element instanceof HTMLInputElement && ["number", "range"].includes(element.type);
|
|
387
432
|
}
|
|
388
433
|
var HookManager_default = class {
|
|
389
|
-
|
|
390
|
-
this.hooks = /* @__PURE__ */ new Map();
|
|
391
|
-
}
|
|
434
|
+
hooks = /* @__PURE__ */ new Map();
|
|
392
435
|
register(hookName, callback) {
|
|
393
436
|
const hooks = this.hooks.get(hookName) || [];
|
|
394
437
|
hooks.push(callback);
|
|
@@ -409,8 +452,8 @@ var HookManager_default = class {
|
|
|
409
452
|
};
|
|
410
453
|
var Idiomorph = (function() {
|
|
411
454
|
"use strict";
|
|
412
|
-
|
|
413
|
-
|
|
455
|
+
const noOp = () => {};
|
|
456
|
+
const defaults = {
|
|
414
457
|
morphStyle: "outerHTML",
|
|
415
458
|
callbacks: {
|
|
416
459
|
beforeNodeAdded: noOp,
|
|
@@ -423,190 +466,282 @@ var Idiomorph = (function() {
|
|
|
423
466
|
},
|
|
424
467
|
head: {
|
|
425
468
|
style: "merge",
|
|
426
|
-
shouldPreserve:
|
|
427
|
-
|
|
428
|
-
},
|
|
429
|
-
shouldReAppend: function(elt) {
|
|
430
|
-
return elt.getAttribute("im-re-append") === "true";
|
|
431
|
-
},
|
|
469
|
+
shouldPreserve: (elt) => elt.getAttribute("im-preserve") === "true",
|
|
470
|
+
shouldReAppend: (elt) => elt.getAttribute("im-re-append") === "true",
|
|
432
471
|
shouldRemove: noOp,
|
|
433
472
|
afterHeadMorphed: noOp
|
|
434
|
-
}
|
|
473
|
+
},
|
|
474
|
+
restoreFocus: true
|
|
435
475
|
};
|
|
436
476
|
function morph(oldNode, newContent, config = {}) {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
477
|
+
oldNode = normalizeElement(oldNode);
|
|
478
|
+
const newNode = normalizeParent(newContent);
|
|
479
|
+
const ctx = createMorphContext(oldNode, newNode, config);
|
|
480
|
+
const morphedNodes = saveAndRestoreFocus(ctx, () => {
|
|
481
|
+
return withHeadBlocking(ctx, oldNode, newNode, (ctx) => {
|
|
482
|
+
if (ctx.morphStyle === "innerHTML") {
|
|
483
|
+
morphChildren(ctx, oldNode, newNode);
|
|
484
|
+
return Array.from(oldNode.childNodes);
|
|
485
|
+
} else return morphOuterHTML(ctx, oldNode, newNode);
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
ctx.pantry.remove();
|
|
489
|
+
return morphedNodes;
|
|
490
|
+
}
|
|
491
|
+
function morphOuterHTML(ctx, oldNode, newNode) {
|
|
492
|
+
const oldParent = normalizeParent(oldNode);
|
|
493
|
+
morphChildren(ctx, oldParent, newNode, oldNode, oldNode.nextSibling);
|
|
494
|
+
return Array.from(oldParent.childNodes);
|
|
495
|
+
}
|
|
496
|
+
function saveAndRestoreFocus(ctx, fn) {
|
|
497
|
+
if (!ctx.config.restoreFocus) return fn();
|
|
498
|
+
let activeElement = document.activeElement;
|
|
499
|
+
if (!(activeElement instanceof HTMLInputElement || activeElement instanceof HTMLTextAreaElement)) return fn();
|
|
500
|
+
const { id: activeElementId, selectionStart, selectionEnd } = activeElement;
|
|
501
|
+
const results = fn();
|
|
502
|
+
if (activeElementId && activeElementId !== document.activeElement?.getAttribute("id")) {
|
|
503
|
+
activeElement = ctx.target.querySelector(`[id="${activeElementId}"]`);
|
|
504
|
+
activeElement?.focus();
|
|
505
|
+
}
|
|
506
|
+
if (activeElement && !activeElement.selectionEnd && selectionEnd) activeElement.setSelectionRange(selectionStart, selectionEnd);
|
|
507
|
+
return results;
|
|
508
|
+
}
|
|
509
|
+
const morphChildren = (function() {
|
|
510
|
+
function morphChildren(ctx, oldParent, newParent, insertionPoint = null, endPoint = null) {
|
|
511
|
+
if (oldParent instanceof HTMLTemplateElement && newParent instanceof HTMLTemplateElement) {
|
|
512
|
+
oldParent = oldParent.content;
|
|
513
|
+
newParent = newParent.content;
|
|
514
|
+
}
|
|
515
|
+
insertionPoint ||= oldParent.firstChild;
|
|
516
|
+
for (const newChild of newParent.childNodes) {
|
|
517
|
+
if (insertionPoint && insertionPoint != endPoint) {
|
|
518
|
+
const bestMatch = findBestMatch(ctx, newChild, insertionPoint, endPoint);
|
|
519
|
+
if (bestMatch) {
|
|
520
|
+
if (bestMatch !== insertionPoint) removeNodesBetween(ctx, insertionPoint, bestMatch);
|
|
521
|
+
morphNode(bestMatch, newChild, ctx);
|
|
522
|
+
insertionPoint = bestMatch.nextSibling;
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
if (newChild instanceof Element) {
|
|
527
|
+
const newChildId = newChild.getAttribute("id");
|
|
528
|
+
if (ctx.persistentIds.has(newChildId)) {
|
|
529
|
+
const movedChild = moveBeforeById(oldParent, newChildId, insertionPoint, ctx);
|
|
530
|
+
morphNode(movedChild, newChild, ctx);
|
|
531
|
+
insertionPoint = movedChild.nextSibling;
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
const insertedNode = createNode(oldParent, newChild, insertionPoint, ctx);
|
|
536
|
+
if (insertedNode) insertionPoint = insertedNode.nextSibling;
|
|
537
|
+
}
|
|
538
|
+
while (insertionPoint && insertionPoint != endPoint) {
|
|
539
|
+
const tempNode = insertionPoint;
|
|
540
|
+
insertionPoint = insertionPoint.nextSibling;
|
|
541
|
+
removeNode(ctx, tempNode);
|
|
456
542
|
}
|
|
457
543
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
return ctx.ignoreActiveValue && possibleActiveElement === document.activeElement;
|
|
472
|
-
}
|
|
473
|
-
function morphOldNodeTo(oldNode, newContent, ctx) {
|
|
474
|
-
if (ctx.ignoreActive && oldNode === document.activeElement) {} else if (newContent == null) {
|
|
475
|
-
if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode;
|
|
476
|
-
oldNode.remove();
|
|
477
|
-
ctx.callbacks.afterNodeRemoved(oldNode);
|
|
478
|
-
return null;
|
|
479
|
-
} else if (!isSoftMatch(oldNode, newContent)) {
|
|
480
|
-
if (ctx.callbacks.beforeNodeRemoved(oldNode) === false) return oldNode;
|
|
481
|
-
if (ctx.callbacks.beforeNodeAdded(newContent) === false) return oldNode;
|
|
482
|
-
oldNode.parentElement.replaceChild(newContent, oldNode);
|
|
483
|
-
ctx.callbacks.afterNodeAdded(newContent);
|
|
484
|
-
ctx.callbacks.afterNodeRemoved(oldNode);
|
|
485
|
-
return newContent;
|
|
486
|
-
} else {
|
|
487
|
-
if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) return oldNode;
|
|
488
|
-
if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) {} else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") handleHeadElement(newContent, oldNode, ctx);
|
|
489
|
-
else {
|
|
490
|
-
syncNodeFrom(newContent, oldNode, ctx);
|
|
491
|
-
if (!ignoreValueOfActiveElement(oldNode, ctx)) morphChildren(newContent, oldNode, ctx);
|
|
544
|
+
function createNode(oldParent, newChild, insertionPoint, ctx) {
|
|
545
|
+
if (ctx.callbacks.beforeNodeAdded(newChild) === false) return null;
|
|
546
|
+
if (ctx.idMap.has(newChild)) {
|
|
547
|
+
const newEmptyChild = document.createElement(newChild.tagName);
|
|
548
|
+
oldParent.insertBefore(newEmptyChild, insertionPoint);
|
|
549
|
+
morphNode(newEmptyChild, newChild, ctx);
|
|
550
|
+
ctx.callbacks.afterNodeAdded(newEmptyChild);
|
|
551
|
+
return newEmptyChild;
|
|
552
|
+
} else {
|
|
553
|
+
const newClonedChild = document.importNode(newChild, true);
|
|
554
|
+
oldParent.insertBefore(newClonedChild, insertionPoint);
|
|
555
|
+
ctx.callbacks.afterNodeAdded(newClonedChild);
|
|
556
|
+
return newClonedChild;
|
|
492
557
|
}
|
|
493
|
-
ctx.callbacks.afterNodeMorphed(oldNode, newContent);
|
|
494
|
-
return oldNode;
|
|
495
558
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
559
|
+
const findBestMatch = (function() {
|
|
560
|
+
function findBestMatch(ctx, node, startPoint, endPoint) {
|
|
561
|
+
let softMatch = null;
|
|
562
|
+
let nextSibling = node.nextSibling;
|
|
563
|
+
let siblingSoftMatchCount = 0;
|
|
564
|
+
let cursor = startPoint;
|
|
565
|
+
while (cursor && cursor != endPoint) {
|
|
566
|
+
if (isSoftMatch(cursor, node)) {
|
|
567
|
+
if (isIdSetMatch(ctx, cursor, node)) return cursor;
|
|
568
|
+
if (softMatch === null) {
|
|
569
|
+
if (!ctx.idMap.has(cursor)) softMatch = cursor;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (softMatch === null && nextSibling && isSoftMatch(cursor, nextSibling)) {
|
|
573
|
+
siblingSoftMatchCount++;
|
|
574
|
+
nextSibling = nextSibling.nextSibling;
|
|
575
|
+
if (siblingSoftMatchCount >= 2) softMatch = void 0;
|
|
576
|
+
}
|
|
577
|
+
if (ctx.activeElementAndParents.includes(cursor)) break;
|
|
578
|
+
cursor = cursor.nextSibling;
|
|
579
|
+
}
|
|
580
|
+
return softMatch || null;
|
|
510
581
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
582
|
+
function isIdSetMatch(ctx, oldNode, newNode) {
|
|
583
|
+
let oldSet = ctx.idMap.get(oldNode);
|
|
584
|
+
let newSet = ctx.idMap.get(newNode);
|
|
585
|
+
if (!newSet || !oldSet) return false;
|
|
586
|
+
for (const id of oldSet) if (newSet.has(id)) return true;
|
|
587
|
+
return false;
|
|
516
588
|
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
removeIdsFromConsideration(ctx, newChild);
|
|
522
|
-
continue;
|
|
589
|
+
function isSoftMatch(oldNode, newNode) {
|
|
590
|
+
const oldElt = oldNode;
|
|
591
|
+
const newElt = newNode;
|
|
592
|
+
return oldElt.nodeType === newElt.nodeType && oldElt.tagName === newElt.tagName && (!oldElt.getAttribute?.("id") || oldElt.getAttribute?.("id") === newElt.getAttribute?.("id"));
|
|
523
593
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
594
|
+
return findBestMatch;
|
|
595
|
+
})();
|
|
596
|
+
function removeNode(ctx, node) {
|
|
597
|
+
if (ctx.idMap.has(node)) moveBefore(ctx.pantry, node, null);
|
|
598
|
+
else {
|
|
599
|
+
if (ctx.callbacks.beforeNodeRemoved(node) === false) return;
|
|
600
|
+
node.parentNode?.removeChild(node);
|
|
601
|
+
ctx.callbacks.afterNodeRemoved(node);
|
|
530
602
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
insertionPoint = insertionPoint.nextSibling;
|
|
539
|
-
removeNode(tempNode, ctx);
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
function ignoreAttribute(attr, to, updateType, ctx) {
|
|
543
|
-
if (attr === "value" && ctx.ignoreActiveValue && to === document.activeElement) return true;
|
|
544
|
-
return ctx.callbacks.beforeAttributeUpdated(attr, to, updateType) === false;
|
|
545
|
-
}
|
|
546
|
-
function syncNodeFrom(from, to, ctx) {
|
|
547
|
-
let type = from.nodeType;
|
|
548
|
-
if (type === 1) {
|
|
549
|
-
const fromAttributes = from.attributes;
|
|
550
|
-
const toAttributes = to.attributes;
|
|
551
|
-
for (const fromAttribute of fromAttributes) {
|
|
552
|
-
if (ignoreAttribute(fromAttribute.name, to, "update", ctx)) continue;
|
|
553
|
-
if (to.getAttribute(fromAttribute.name) !== fromAttribute.value) to.setAttribute(fromAttribute.name, fromAttribute.value);
|
|
603
|
+
}
|
|
604
|
+
function removeNodesBetween(ctx, startInclusive, endExclusive) {
|
|
605
|
+
let cursor = startInclusive;
|
|
606
|
+
while (cursor && cursor !== endExclusive) {
|
|
607
|
+
let tempNode = cursor;
|
|
608
|
+
cursor = cursor.nextSibling;
|
|
609
|
+
removeNode(ctx, tempNode);
|
|
554
610
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
611
|
+
return cursor;
|
|
612
|
+
}
|
|
613
|
+
function moveBeforeById(parentNode, id, after, ctx) {
|
|
614
|
+
const target = ctx.target.getAttribute?.("id") === id && ctx.target || ctx.target.querySelector(`[id="${id}"]`) || ctx.pantry.querySelector(`[id="${id}"]`);
|
|
615
|
+
removeElementFromAncestorsIdMaps(target, ctx);
|
|
616
|
+
moveBefore(parentNode, target, after);
|
|
617
|
+
return target;
|
|
618
|
+
}
|
|
619
|
+
function removeElementFromAncestorsIdMaps(element, ctx) {
|
|
620
|
+
const id = element.getAttribute("id");
|
|
621
|
+
while (element = element.parentNode) {
|
|
622
|
+
let idSet = ctx.idMap.get(element);
|
|
623
|
+
if (idSet) {
|
|
624
|
+
idSet.delete(id);
|
|
625
|
+
if (!idSet.size) ctx.idMap.delete(element);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
function moveBefore(parentNode, element, after) {
|
|
630
|
+
if (parentNode.moveBefore) try {
|
|
631
|
+
parentNode.moveBefore(element, after);
|
|
632
|
+
} catch (e) {
|
|
633
|
+
parentNode.insertBefore(element, after);
|
|
634
|
+
}
|
|
635
|
+
else parentNode.insertBefore(element, after);
|
|
636
|
+
}
|
|
637
|
+
return morphChildren;
|
|
638
|
+
})();
|
|
639
|
+
const morphNode = (function() {
|
|
640
|
+
function morphNode(oldNode, newContent, ctx) {
|
|
641
|
+
if (ctx.ignoreActive && oldNode === document.activeElement) return null;
|
|
642
|
+
if (ctx.callbacks.beforeNodeMorphed(oldNode, newContent) === false) return oldNode;
|
|
643
|
+
if (oldNode instanceof HTMLHeadElement && ctx.head.ignore) {} else if (oldNode instanceof HTMLHeadElement && ctx.head.style !== "morph") handleHeadElement(oldNode, newContent, ctx);
|
|
644
|
+
else {
|
|
645
|
+
morphAttributes(oldNode, newContent, ctx);
|
|
646
|
+
if (!ignoreValueOfActiveElement(oldNode, ctx)) morphChildren(ctx, oldNode, newContent);
|
|
559
647
|
}
|
|
648
|
+
ctx.callbacks.afterNodeMorphed(oldNode, newContent);
|
|
649
|
+
return oldNode;
|
|
560
650
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
if (!ignoreUpdate) to.setAttribute(attributeName, from[attributeName]);
|
|
572
|
-
} else if (!ignoreAttribute(attributeName, to, "remove", ctx)) to.removeAttribute(attributeName);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
function syncInputValue(from, to, ctx) {
|
|
576
|
-
if (from instanceof HTMLInputElement && to instanceof HTMLInputElement && from.type !== "file") {
|
|
577
|
-
let fromValue = from.value;
|
|
578
|
-
let toValue = to.value;
|
|
579
|
-
syncBooleanAttribute(from, to, "checked", ctx);
|
|
580
|
-
syncBooleanAttribute(from, to, "disabled", ctx);
|
|
581
|
-
if (!from.hasAttribute("value")) {
|
|
582
|
-
if (!ignoreAttribute("value", to, "remove", ctx)) {
|
|
583
|
-
to.value = "";
|
|
584
|
-
to.removeAttribute("value");
|
|
651
|
+
function morphAttributes(oldNode, newNode, ctx) {
|
|
652
|
+
let type = newNode.nodeType;
|
|
653
|
+
if (type === 1) {
|
|
654
|
+
const oldElt = oldNode;
|
|
655
|
+
const newElt = newNode;
|
|
656
|
+
const oldAttributes = oldElt.attributes;
|
|
657
|
+
const newAttributes = newElt.attributes;
|
|
658
|
+
for (const newAttribute of newAttributes) {
|
|
659
|
+
if (ignoreAttribute(newAttribute.name, oldElt, "update", ctx)) continue;
|
|
660
|
+
if (oldElt.getAttribute(newAttribute.name) !== newAttribute.value) oldElt.setAttribute(newAttribute.name, newAttribute.value);
|
|
585
661
|
}
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
662
|
+
for (let i = oldAttributes.length - 1; 0 <= i; i--) {
|
|
663
|
+
const oldAttribute = oldAttributes[i];
|
|
664
|
+
if (!oldAttribute) continue;
|
|
665
|
+
if (!newElt.hasAttribute(oldAttribute.name)) {
|
|
666
|
+
if (ignoreAttribute(oldAttribute.name, oldElt, "remove", ctx)) continue;
|
|
667
|
+
oldElt.removeAttribute(oldAttribute.name);
|
|
668
|
+
}
|
|
590
669
|
}
|
|
670
|
+
if (!ignoreValueOfActiveElement(oldElt, ctx)) syncInputValue(oldElt, newElt, ctx);
|
|
671
|
+
}
|
|
672
|
+
if (type === 8 || type === 3) {
|
|
673
|
+
if (oldNode.nodeValue !== newNode.nodeValue) oldNode.nodeValue = newNode.nodeValue;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
function syncInputValue(oldElement, newElement, ctx) {
|
|
677
|
+
if (oldElement instanceof HTMLInputElement && newElement instanceof HTMLInputElement && newElement.type !== "file") {
|
|
678
|
+
let newValue = newElement.value;
|
|
679
|
+
let oldValue = oldElement.value;
|
|
680
|
+
syncBooleanAttribute(oldElement, newElement, "checked", ctx);
|
|
681
|
+
syncBooleanAttribute(oldElement, newElement, "disabled", ctx);
|
|
682
|
+
if (!newElement.hasAttribute("value")) {
|
|
683
|
+
if (!ignoreAttribute("value", oldElement, "remove", ctx)) {
|
|
684
|
+
oldElement.value = "";
|
|
685
|
+
oldElement.removeAttribute("value");
|
|
686
|
+
}
|
|
687
|
+
} else if (oldValue !== newValue) {
|
|
688
|
+
if (!ignoreAttribute("value", oldElement, "update", ctx)) {
|
|
689
|
+
oldElement.setAttribute("value", newValue);
|
|
690
|
+
oldElement.value = newValue;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
} else if (oldElement instanceof HTMLOptionElement && newElement instanceof HTMLOptionElement) syncBooleanAttribute(oldElement, newElement, "selected", ctx);
|
|
694
|
+
else if (oldElement instanceof HTMLTextAreaElement && newElement instanceof HTMLTextAreaElement) {
|
|
695
|
+
let newValue = newElement.value;
|
|
696
|
+
let oldValue = oldElement.value;
|
|
697
|
+
if (ignoreAttribute("value", oldElement, "update", ctx)) return;
|
|
698
|
+
if (newValue !== oldValue) oldElement.value = newValue;
|
|
699
|
+
if (oldElement.firstChild && oldElement.firstChild.nodeValue !== newValue) oldElement.firstChild.nodeValue = newValue;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function syncBooleanAttribute(oldElement, newElement, attributeName, ctx) {
|
|
703
|
+
const newLiveValue = newElement[attributeName];
|
|
704
|
+
if (newLiveValue !== oldElement[attributeName]) {
|
|
705
|
+
const ignoreUpdate = ignoreAttribute(attributeName, oldElement, "update", ctx);
|
|
706
|
+
if (!ignoreUpdate) oldElement[attributeName] = newElement[attributeName];
|
|
707
|
+
if (newLiveValue) {
|
|
708
|
+
if (!ignoreUpdate) oldElement.setAttribute(attributeName, "");
|
|
709
|
+
} else if (!ignoreAttribute(attributeName, oldElement, "remove", ctx)) oldElement.removeAttribute(attributeName);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
function ignoreAttribute(attr, element, updateType, ctx) {
|
|
713
|
+
if (attr === "value" && ctx.ignoreActiveValue && element === document.activeElement) return true;
|
|
714
|
+
return ctx.callbacks.beforeAttributeUpdated(attr, element, updateType) === false;
|
|
715
|
+
}
|
|
716
|
+
function ignoreValueOfActiveElement(possibleActiveElement, ctx) {
|
|
717
|
+
return !!ctx.ignoreActiveValue && possibleActiveElement === document.activeElement && possibleActiveElement !== document.body;
|
|
718
|
+
}
|
|
719
|
+
return morphNode;
|
|
720
|
+
})();
|
|
721
|
+
function withHeadBlocking(ctx, oldNode, newNode, callback) {
|
|
722
|
+
if (ctx.head.block) {
|
|
723
|
+
const oldHead = oldNode.querySelector("head");
|
|
724
|
+
const newHead = newNode.querySelector("head");
|
|
725
|
+
if (oldHead && newHead) {
|
|
726
|
+
const promises = handleHeadElement(oldHead, newHead, ctx);
|
|
727
|
+
return Promise.all(promises).then(() => {
|
|
728
|
+
return callback(Object.assign(ctx, { head: {
|
|
729
|
+
block: false,
|
|
730
|
+
ignore: true
|
|
731
|
+
} }));
|
|
732
|
+
});
|
|
591
733
|
}
|
|
592
|
-
} else if (from instanceof HTMLOptionElement) syncBooleanAttribute(from, to, "selected", ctx);
|
|
593
|
-
else if (from instanceof HTMLTextAreaElement && to instanceof HTMLTextAreaElement) {
|
|
594
|
-
let fromValue = from.value;
|
|
595
|
-
let toValue = to.value;
|
|
596
|
-
if (ignoreAttribute("value", to, "update", ctx)) return;
|
|
597
|
-
if (fromValue !== toValue) to.value = fromValue;
|
|
598
|
-
if (to.firstChild && to.firstChild.nodeValue !== fromValue) to.firstChild.nodeValue = fromValue;
|
|
599
734
|
}
|
|
735
|
+
return callback(ctx);
|
|
600
736
|
}
|
|
601
|
-
function handleHeadElement(
|
|
737
|
+
function handleHeadElement(oldHead, newHead, ctx) {
|
|
602
738
|
let added = [];
|
|
603
739
|
let removed = [];
|
|
604
740
|
let preserved = [];
|
|
605
741
|
let nodesToAppend = [];
|
|
606
|
-
let headMergeStyle = ctx.head.style;
|
|
607
742
|
let srcToNewHeadNodes = /* @__PURE__ */ new Map();
|
|
608
|
-
for (const newHeadChild of
|
|
609
|
-
for (const currentHeadElt of
|
|
743
|
+
for (const newHeadChild of newHead.children) srcToNewHeadNodes.set(newHeadChild.outerHTML, newHeadChild);
|
|
744
|
+
for (const currentHeadElt of oldHead.children) {
|
|
610
745
|
let inNewContent = srcToNewHeadNodes.has(currentHeadElt.outerHTML);
|
|
611
746
|
let isReAppended = ctx.head.shouldReAppend(currentHeadElt);
|
|
612
747
|
let isPreserved = ctx.head.shouldPreserve(currentHeadElt);
|
|
@@ -615,7 +750,7 @@ var Idiomorph = (function() {
|
|
|
615
750
|
srcToNewHeadNodes.delete(currentHeadElt.outerHTML);
|
|
616
751
|
preserved.push(currentHeadElt);
|
|
617
752
|
}
|
|
618
|
-
else if (
|
|
753
|
+
else if (ctx.head.style === "append") {
|
|
619
754
|
if (isReAppended) {
|
|
620
755
|
removed.push(currentHeadElt);
|
|
621
756
|
nodesToAppend.push(currentHeadElt);
|
|
@@ -627,8 +762,8 @@ var Idiomorph = (function() {
|
|
|
627
762
|
for (const newNode of nodesToAppend) {
|
|
628
763
|
let newElt = document.createRange().createContextualFragment(newNode.outerHTML).firstChild;
|
|
629
764
|
if (ctx.callbacks.beforeNodeAdded(newElt) !== false) {
|
|
630
|
-
if (newElt.href || newElt.src) {
|
|
631
|
-
let resolve
|
|
765
|
+
if ("href" in newElt && newElt.href || "src" in newElt && newElt.src) {
|
|
766
|
+
/** @type {(result?: any) => void} */ let resolve;
|
|
632
767
|
let promise = new Promise(function(_resolve) {
|
|
633
768
|
resolve = _resolve;
|
|
634
769
|
});
|
|
@@ -637,218 +772,197 @@ var Idiomorph = (function() {
|
|
|
637
772
|
});
|
|
638
773
|
promises.push(promise);
|
|
639
774
|
}
|
|
640
|
-
|
|
775
|
+
oldHead.appendChild(newElt);
|
|
641
776
|
ctx.callbacks.afterNodeAdded(newElt);
|
|
642
777
|
added.push(newElt);
|
|
643
778
|
}
|
|
644
779
|
}
|
|
645
780
|
for (const removedElement of removed) if (ctx.callbacks.beforeNodeRemoved(removedElement) !== false) {
|
|
646
|
-
|
|
781
|
+
oldHead.removeChild(removedElement);
|
|
647
782
|
ctx.callbacks.afterNodeRemoved(removedElement);
|
|
648
783
|
}
|
|
649
|
-
ctx.head.afterHeadMorphed(
|
|
784
|
+
ctx.head.afterHeadMorphed(oldHead, {
|
|
650
785
|
added,
|
|
651
786
|
kept: preserved,
|
|
652
787
|
removed
|
|
653
788
|
});
|
|
654
789
|
return promises;
|
|
655
790
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
removeNode(tempNode, ctx);
|
|
699
|
-
}
|
|
700
|
-
removeIdsFromConsideration(ctx, endExclusive);
|
|
701
|
-
return endExclusive.nextSibling;
|
|
702
|
-
}
|
|
703
|
-
function findIdSetMatch(newContent, oldParent, newChild, insertionPoint, ctx) {
|
|
704
|
-
let newChildPotentialIdCount = getIdIntersectionCount(ctx, newChild, oldParent);
|
|
705
|
-
let potentialMatch = null;
|
|
706
|
-
if (newChildPotentialIdCount > 0) {
|
|
707
|
-
let potentialMatch = insertionPoint;
|
|
708
|
-
let otherMatchCount = 0;
|
|
709
|
-
while (potentialMatch != null) {
|
|
710
|
-
if (isIdSetMatch(newChild, potentialMatch, ctx)) return potentialMatch;
|
|
711
|
-
otherMatchCount += getIdIntersectionCount(ctx, potentialMatch, newContent);
|
|
712
|
-
if (otherMatchCount > newChildPotentialIdCount) return null;
|
|
713
|
-
potentialMatch = potentialMatch.nextSibling;
|
|
791
|
+
const createMorphContext = (function() {
|
|
792
|
+
function createMorphContext(oldNode, newContent, config) {
|
|
793
|
+
const { persistentIds, idMap } = createIdMaps(oldNode, newContent);
|
|
794
|
+
const mergedConfig = mergeDefaults(config);
|
|
795
|
+
const morphStyle = mergedConfig.morphStyle || "outerHTML";
|
|
796
|
+
if (!["innerHTML", "outerHTML"].includes(morphStyle)) throw `Do not understand how to morph style ${morphStyle}`;
|
|
797
|
+
return {
|
|
798
|
+
target: oldNode,
|
|
799
|
+
newContent,
|
|
800
|
+
config: mergedConfig,
|
|
801
|
+
morphStyle,
|
|
802
|
+
ignoreActive: mergedConfig.ignoreActive,
|
|
803
|
+
ignoreActiveValue: mergedConfig.ignoreActiveValue,
|
|
804
|
+
restoreFocus: mergedConfig.restoreFocus,
|
|
805
|
+
idMap,
|
|
806
|
+
persistentIds,
|
|
807
|
+
pantry: createPantry(),
|
|
808
|
+
activeElementAndParents: createActiveElementAndParents(oldNode),
|
|
809
|
+
callbacks: mergedConfig.callbacks,
|
|
810
|
+
head: mergedConfig.head
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
function mergeDefaults(config) {
|
|
814
|
+
let finalConfig = Object.assign({}, defaults);
|
|
815
|
+
Object.assign(finalConfig, config);
|
|
816
|
+
finalConfig.callbacks = Object.assign({}, defaults.callbacks, config.callbacks);
|
|
817
|
+
finalConfig.head = Object.assign({}, defaults.head, config.head);
|
|
818
|
+
return finalConfig;
|
|
819
|
+
}
|
|
820
|
+
function createPantry() {
|
|
821
|
+
const pantry = document.createElement("div");
|
|
822
|
+
pantry.hidden = true;
|
|
823
|
+
document.body.insertAdjacentElement("afterend", pantry);
|
|
824
|
+
return pantry;
|
|
825
|
+
}
|
|
826
|
+
function createActiveElementAndParents(oldNode) {
|
|
827
|
+
let activeElementAndParents = [];
|
|
828
|
+
let elt = document.activeElement;
|
|
829
|
+
if (elt?.tagName !== "BODY" && oldNode.contains(elt)) while (elt) {
|
|
830
|
+
activeElementAndParents.push(elt);
|
|
831
|
+
if (elt === oldNode) break;
|
|
832
|
+
elt = elt.parentElement;
|
|
714
833
|
}
|
|
834
|
+
return activeElementAndParents;
|
|
835
|
+
}
|
|
836
|
+
function findIdElements(root) {
|
|
837
|
+
let elements = Array.from(root.querySelectorAll("[id]"));
|
|
838
|
+
if (root.getAttribute?.("id")) elements.push(root);
|
|
839
|
+
return elements;
|
|
715
840
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
841
|
+
function populateIdMapWithTree(idMap, persistentIds, root, elements) {
|
|
842
|
+
for (const elt of elements) {
|
|
843
|
+
const id = elt.getAttribute("id");
|
|
844
|
+
if (persistentIds.has(id)) {
|
|
845
|
+
let current = elt;
|
|
846
|
+
while (current) {
|
|
847
|
+
let idSet = idMap.get(current);
|
|
848
|
+
if (idSet == null) {
|
|
849
|
+
idSet = /* @__PURE__ */ new Set();
|
|
850
|
+
idMap.set(current, idSet);
|
|
851
|
+
}
|
|
852
|
+
idSet.add(id);
|
|
853
|
+
if (current === root) break;
|
|
854
|
+
current = current.parentElement;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
729
857
|
}
|
|
730
|
-
potentialSoftMatch = potentialSoftMatch.nextSibling;
|
|
731
858
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
859
|
+
function createIdMaps(oldContent, newContent) {
|
|
860
|
+
const oldIdElements = findIdElements(oldContent);
|
|
861
|
+
const newIdElements = findIdElements(newContent);
|
|
862
|
+
const persistentIds = createPersistentIds(oldIdElements, newIdElements);
|
|
863
|
+
let idMap = /* @__PURE__ */ new Map();
|
|
864
|
+
populateIdMapWithTree(idMap, persistentIds, oldContent, oldIdElements);
|
|
865
|
+
populateIdMapWithTree(idMap, persistentIds, newContent.__idiomorphRoot || newContent, newIdElements);
|
|
866
|
+
return {
|
|
867
|
+
persistentIds,
|
|
868
|
+
idMap
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
function createPersistentIds(oldIdElements, newIdElements) {
|
|
872
|
+
let duplicateIds = /* @__PURE__ */ new Set();
|
|
873
|
+
let oldIdTagNameMap = /* @__PURE__ */ new Map();
|
|
874
|
+
for (const { id, tagName } of oldIdElements) if (oldIdTagNameMap.has(id)) duplicateIds.add(id);
|
|
875
|
+
else oldIdTagNameMap.set(id, tagName);
|
|
876
|
+
let persistentIds = /* @__PURE__ */ new Set();
|
|
877
|
+
for (const { id, tagName } of newIdElements) if (persistentIds.has(id)) duplicateIds.add(id);
|
|
878
|
+
else if (oldIdTagNameMap.get(id) === tagName) persistentIds.add(id);
|
|
879
|
+
for (const id of duplicateIds) persistentIds.delete(id);
|
|
880
|
+
return persistentIds;
|
|
881
|
+
}
|
|
882
|
+
return createMorphContext;
|
|
883
|
+
})();
|
|
884
|
+
const { normalizeElement, normalizeParent } = (function() {
|
|
885
|
+
const generatedByIdiomorph = /* @__PURE__ */ new WeakSet();
|
|
886
|
+
function normalizeElement(content) {
|
|
887
|
+
if (content instanceof Document) return content.documentElement;
|
|
888
|
+
else return content;
|
|
889
|
+
}
|
|
890
|
+
function normalizeParent(newContent) {
|
|
891
|
+
if (newContent == null) return document.createElement("div");
|
|
892
|
+
else if (typeof newContent === "string") return normalizeParent(parseContent(newContent));
|
|
893
|
+
else if (generatedByIdiomorph.has(newContent)) return newContent;
|
|
894
|
+
else if (newContent instanceof Node) if (newContent.parentNode) return new SlicedParentNode(newContent);
|
|
895
|
+
else {
|
|
896
|
+
const dummyParent = document.createElement("div");
|
|
897
|
+
dummyParent.append(newContent);
|
|
898
|
+
return dummyParent;
|
|
899
|
+
}
|
|
900
|
+
else {
|
|
901
|
+
const dummyParent = document.createElement("div");
|
|
902
|
+
for (const elt of [...newContent]) dummyParent.append(elt);
|
|
903
|
+
return dummyParent;
|
|
748
904
|
}
|
|
749
|
-
} else {
|
|
750
|
-
let content = parser.parseFromString("<body><template>" + newContent + "</template></body>", "text/html").body.querySelector("template").content;
|
|
751
|
-
content.generatedByIdiomorph = true;
|
|
752
|
-
return content;
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
function normalizeContent(newContent) {
|
|
756
|
-
if (newContent == null) return document.createElement("div");
|
|
757
|
-
else if (newContent.generatedByIdiomorph) return newContent;
|
|
758
|
-
else if (newContent instanceof Node) {
|
|
759
|
-
const dummyParent = document.createElement("div");
|
|
760
|
-
dummyParent.append(newContent);
|
|
761
|
-
return dummyParent;
|
|
762
|
-
} else {
|
|
763
|
-
const dummyParent = document.createElement("div");
|
|
764
|
-
for (const elt of [...newContent]) dummyParent.append(elt);
|
|
765
|
-
return dummyParent;
|
|
766
905
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
score = newScore;
|
|
906
|
+
class SlicedParentNode {
|
|
907
|
+
constructor(node) {
|
|
908
|
+
this.originalNode = node;
|
|
909
|
+
this.realParentNode = node.parentNode;
|
|
910
|
+
this.previousSibling = node.previousSibling;
|
|
911
|
+
this.nextSibling = node.nextSibling;
|
|
912
|
+
}
|
|
913
|
+
get childNodes() {
|
|
914
|
+
const nodes = [];
|
|
915
|
+
let cursor = this.previousSibling ? this.previousSibling.nextSibling : this.realParentNode.firstChild;
|
|
916
|
+
while (cursor && cursor != this.nextSibling) {
|
|
917
|
+
nodes.push(cursor);
|
|
918
|
+
cursor = cursor.nextSibling;
|
|
919
|
+
}
|
|
920
|
+
return nodes;
|
|
921
|
+
}
|
|
922
|
+
querySelectorAll(selector) {
|
|
923
|
+
return this.childNodes.reduce((results, node) => {
|
|
924
|
+
if (node instanceof Element) {
|
|
925
|
+
if (node.matches(selector)) results.push(node);
|
|
926
|
+
const nodeList = node.querySelectorAll(selector);
|
|
927
|
+
for (let i = 0; i < nodeList.length; i++) results.push(nodeList[i]);
|
|
928
|
+
}
|
|
929
|
+
return results;
|
|
930
|
+
}, []);
|
|
931
|
+
}
|
|
932
|
+
insertBefore(node, referenceNode) {
|
|
933
|
+
return this.realParentNode.insertBefore(node, referenceNode);
|
|
934
|
+
}
|
|
935
|
+
moveBefore(node, referenceNode) {
|
|
936
|
+
return this.realParentNode.moveBefore(node, referenceNode);
|
|
799
937
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
}
|
|
817
|
-
function idIsWithinNode(ctx, id, targetNode) {
|
|
818
|
-
return (ctx.idMap.get(targetNode) || EMPTY_SET).has(id);
|
|
819
|
-
}
|
|
820
|
-
function removeIdsFromConsideration(ctx, node) {
|
|
821
|
-
let idSet = ctx.idMap.get(node) || EMPTY_SET;
|
|
822
|
-
for (const id of idSet) ctx.deadIds.add(id);
|
|
823
|
-
}
|
|
824
|
-
function getIdIntersectionCount(ctx, node1, node2) {
|
|
825
|
-
let sourceSet = ctx.idMap.get(node1) || EMPTY_SET;
|
|
826
|
-
let matchCount = 0;
|
|
827
|
-
for (const id of sourceSet) if (isIdInConsideration(ctx, id) && idIsWithinNode(ctx, id, node2)) ++matchCount;
|
|
828
|
-
return matchCount;
|
|
829
|
-
}
|
|
830
|
-
function populateIdMapForNode(node, idMap) {
|
|
831
|
-
let nodeParent = node.parentElement;
|
|
832
|
-
let idElements = node.querySelectorAll("[id]");
|
|
833
|
-
for (const elt of idElements) {
|
|
834
|
-
let current = elt;
|
|
835
|
-
while (current !== nodeParent && current != null) {
|
|
836
|
-
let idSet = idMap.get(current);
|
|
837
|
-
if (idSet == null) {
|
|
838
|
-
idSet = /* @__PURE__ */ new Set();
|
|
839
|
-
idMap.set(current, idSet);
|
|
938
|
+
get __idiomorphRoot() {
|
|
939
|
+
return this.originalNode;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
function parseContent(newContent) {
|
|
943
|
+
let parser = new DOMParser();
|
|
944
|
+
let contentWithSvgsRemoved = newContent.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim, "");
|
|
945
|
+
if (contentWithSvgsRemoved.match(/<\/html>/) || contentWithSvgsRemoved.match(/<\/head>/) || contentWithSvgsRemoved.match(/<\/body>/)) {
|
|
946
|
+
let content = parser.parseFromString(newContent, "text/html");
|
|
947
|
+
if (contentWithSvgsRemoved.match(/<\/html>/)) {
|
|
948
|
+
generatedByIdiomorph.add(content);
|
|
949
|
+
return content;
|
|
950
|
+
} else {
|
|
951
|
+
let htmlElement = content.firstChild;
|
|
952
|
+
if (htmlElement) generatedByIdiomorph.add(htmlElement);
|
|
953
|
+
return htmlElement;
|
|
840
954
|
}
|
|
841
|
-
|
|
842
|
-
|
|
955
|
+
} else {
|
|
956
|
+
let content = parser.parseFromString("<body><template>" + newContent + "</template></body>", "text/html").body.querySelector("template").content;
|
|
957
|
+
generatedByIdiomorph.add(content);
|
|
958
|
+
return content;
|
|
843
959
|
}
|
|
844
960
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
return idMap;
|
|
851
|
-
}
|
|
961
|
+
return {
|
|
962
|
+
normalizeElement,
|
|
963
|
+
normalizeParent
|
|
964
|
+
};
|
|
965
|
+
})();
|
|
852
966
|
return {
|
|
853
967
|
morph,
|
|
854
968
|
defaults
|
|
@@ -869,89 +983,110 @@ const syncAttributes = (fromEl, toEl) => {
|
|
|
869
983
|
toEl.setAttribute(attr.name, attr.value);
|
|
870
984
|
}
|
|
871
985
|
};
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
const
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
if (
|
|
911
|
-
|
|
986
|
+
const setId = (element, id) => {
|
|
987
|
+
if (id === null) element.removeAttribute("id");
|
|
988
|
+
else element.setAttribute("id", id);
|
|
989
|
+
};
|
|
990
|
+
function executeMorphdom(rootFromElement, rootToElement, modifiedFieldElements, getElementValue, externalMutationTracker, childComponents = []) {
|
|
991
|
+
const externallyChangedIds = /* @__PURE__ */ new Map();
|
|
992
|
+
try {
|
|
993
|
+
for (const [element, originalId] of externalMutationTracker.getOriginalIds()) {
|
|
994
|
+
externallyChangedIds.set(element, element.getAttribute("id"));
|
|
995
|
+
setId(element, originalId);
|
|
996
|
+
}
|
|
997
|
+
for (const { element, id } of childComponents) if (id && element.getAttribute("id") !== id) {
|
|
998
|
+
externallyChangedIds.set(element, element.getAttribute("id"));
|
|
999
|
+
setId(element, id);
|
|
1000
|
+
}
|
|
1001
|
+
const originalElementIdsToSwapAfter = [];
|
|
1002
|
+
const originalElementsToPreserve = /* @__PURE__ */ new Map();
|
|
1003
|
+
const markElementAsNeedingPostMorphSwap = (id, replaceWithClone) => {
|
|
1004
|
+
const oldElement = originalElementsToPreserve.get(id);
|
|
1005
|
+
if (!(oldElement instanceof HTMLElement)) throw new Error(`Original element with id ${id} not found`);
|
|
1006
|
+
originalElementIdsToSwapAfter.push(id);
|
|
1007
|
+
if (!replaceWithClone) return null;
|
|
1008
|
+
const clonedOldElement = cloneHTMLElement(oldElement);
|
|
1009
|
+
oldElement.replaceWith(clonedOldElement);
|
|
1010
|
+
return clonedOldElement;
|
|
1011
|
+
};
|
|
1012
|
+
rootToElement.querySelectorAll("[data-live-preserve]").forEach((newElement) => {
|
|
1013
|
+
const id = newElement.id;
|
|
1014
|
+
if (!id) throw new Error("The data-live-preserve attribute requires an id attribute to be set on the element");
|
|
1015
|
+
const oldElement = rootFromElement.querySelector(`#${id}`);
|
|
1016
|
+
if (!(oldElement instanceof HTMLElement)) throw new Error(`The element with id "${id}" was not found in the original HTML`);
|
|
1017
|
+
newElement.removeAttribute("data-live-preserve");
|
|
1018
|
+
originalElementsToPreserve.set(id, oldElement);
|
|
1019
|
+
syncAttributes(newElement, oldElement);
|
|
1020
|
+
});
|
|
1021
|
+
Idiomorph.morph(rootFromElement, rootToElement, { callbacks: {
|
|
1022
|
+
beforeNodeMorphed: (fromEl, toEl) => {
|
|
1023
|
+
if (!(fromEl instanceof Element) || !(toEl instanceof Element)) return true;
|
|
1024
|
+
if (fromEl === rootFromElement) return true;
|
|
1025
|
+
if (fromEl.id && originalElementsToPreserve.has(fromEl.id)) {
|
|
1026
|
+
if (fromEl.id === toEl.id) return false;
|
|
1027
|
+
const clonedFromEl = markElementAsNeedingPostMorphSwap(fromEl.id, true);
|
|
1028
|
+
if (!clonedFromEl) throw new Error("missing clone");
|
|
1029
|
+
Idiomorph.morph(clonedFromEl, toEl);
|
|
912
1030
|
return false;
|
|
913
1031
|
}
|
|
914
|
-
if (
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1032
|
+
if (fromEl instanceof HTMLElement && toEl instanceof HTMLElement) {
|
|
1033
|
+
if (typeof fromEl.__x !== "undefined") {
|
|
1034
|
+
if (!window.Alpine) throw new Error("Unable to access Alpine.js though the global window.Alpine variable. Please make sure Alpine.js is loaded before Symfony UX LiveComponent.");
|
|
1035
|
+
if (typeof window.Alpine.morph !== "function") throw new Error("Unable to access Alpine.js morph function. Please make sure the Alpine.js Morph plugin is installed and loaded, see https://alpinejs.dev/plugins/morph for more information.");
|
|
1036
|
+
window.Alpine.morph(fromEl.__x, toEl);
|
|
1037
|
+
}
|
|
1038
|
+
if (externalMutationTracker.wasElementAdded(fromEl)) {
|
|
1039
|
+
const id = fromEl.getAttribute("id");
|
|
1040
|
+
if (id && id === toEl.getAttribute("id")) return false;
|
|
1041
|
+
toEl.before(document.createComment(""));
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
if (modifiedFieldElements.includes(fromEl)) setValueOnElement(toEl, getElementValue(fromEl));
|
|
1045
|
+
if (fromEl === document.activeElement && fromEl !== document.body && null !== getModelDirectiveFromElement(fromEl, false)) setValueOnElement(toEl, getElementValue(fromEl));
|
|
1046
|
+
const elementChanges = externalMutationTracker.getChangedElement(fromEl);
|
|
1047
|
+
if (elementChanges) {
|
|
1048
|
+
const serverId = toEl.getAttribute("id");
|
|
1049
|
+
elementChanges.applyToElement(toEl);
|
|
1050
|
+
if (externallyChangedIds.has(fromEl)) setId(toEl, serverId);
|
|
1051
|
+
}
|
|
1052
|
+
if (fromEl.nodeName.toUpperCase() !== "OPTION" && fromEl.isEqualNode(toEl)) {
|
|
1053
|
+
const normalizedFromEl = cloneHTMLElement(fromEl);
|
|
1054
|
+
normalizeAttributesForComparison(normalizedFromEl);
|
|
1055
|
+
const normalizedToEl = cloneHTMLElement(toEl);
|
|
1056
|
+
normalizeAttributesForComparison(normalizedToEl);
|
|
1057
|
+
if (normalizedFromEl.isEqualNode(normalizedToEl)) return false;
|
|
1058
|
+
}
|
|
924
1059
|
}
|
|
1060
|
+
if (fromEl.hasAttribute("data-skip-morph") || fromEl.id && fromEl.id !== toEl.id) {
|
|
1061
|
+
fromEl.innerHTML = toEl.innerHTML;
|
|
1062
|
+
return true;
|
|
1063
|
+
}
|
|
1064
|
+
if (fromEl.parentElement?.hasAttribute("data-skip-morph")) return false;
|
|
1065
|
+
return !fromEl.hasAttribute("data-live-ignore");
|
|
1066
|
+
},
|
|
1067
|
+
beforeNodeRemoved(node) {
|
|
1068
|
+
if (!(node instanceof HTMLElement)) return true;
|
|
1069
|
+
if (node.id && originalElementsToPreserve.has(node.id)) {
|
|
1070
|
+
markElementAsNeedingPostMorphSwap(node.id, false);
|
|
1071
|
+
return true;
|
|
1072
|
+
}
|
|
1073
|
+
if (externalMutationTracker.wasElementAdded(node)) return false;
|
|
1074
|
+
return !node.hasAttribute("data-live-ignore");
|
|
925
1075
|
}
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
if (
|
|
931
|
-
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
markElementAsNeedingPostMorphSwap(node.id, false);
|
|
937
|
-
return true;
|
|
938
|
-
}
|
|
939
|
-
if (externalMutationTracker.wasElementAdded(node)) return false;
|
|
940
|
-
return !node.hasAttribute("data-live-ignore");
|
|
941
|
-
}
|
|
942
|
-
} });
|
|
943
|
-
originalElementIdsToSwapAfter.forEach((id) => {
|
|
944
|
-
const newElement = rootFromElement.querySelector(`#${id}`);
|
|
945
|
-
const originalElement = originalElementsToPreserve.get(id);
|
|
946
|
-
if (!(newElement instanceof HTMLElement) || !(originalElement instanceof HTMLElement)) throw new Error("Missing elements.");
|
|
947
|
-
newElement.replaceWith(originalElement);
|
|
948
|
-
});
|
|
1076
|
+
} });
|
|
1077
|
+
originalElementIdsToSwapAfter.forEach((id) => {
|
|
1078
|
+
const newElement = rootFromElement.querySelector(`#${id}`);
|
|
1079
|
+
const originalElement = originalElementsToPreserve.get(id);
|
|
1080
|
+
if (!(newElement instanceof HTMLElement) || !(originalElement instanceof HTMLElement)) throw new Error("Missing elements.");
|
|
1081
|
+
newElement.replaceWith(originalElement);
|
|
1082
|
+
});
|
|
1083
|
+
} finally {
|
|
1084
|
+
for (const [element, id] of externallyChangedIds) setId(element, id);
|
|
1085
|
+
}
|
|
949
1086
|
}
|
|
950
1087
|
var ChangingItemsTracker_default = class {
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
this.removedItems = /* @__PURE__ */ new Map();
|
|
954
|
-
}
|
|
1088
|
+
changedItems = /* @__PURE__ */ new Map();
|
|
1089
|
+
removedItems = /* @__PURE__ */ new Map();
|
|
955
1090
|
setItem(itemName, newValue, previousValue) {
|
|
956
1091
|
if (this.removedItems.has(itemName)) {
|
|
957
1092
|
const removedRecord = this.removedItems.get(itemName);
|
|
@@ -998,12 +1133,10 @@ var ChangingItemsTracker_default = class {
|
|
|
998
1133
|
}
|
|
999
1134
|
};
|
|
1000
1135
|
var ElementChanges = class {
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
this.attributeChanges = new ChangingItemsTracker_default();
|
|
1006
|
-
}
|
|
1136
|
+
addedClasses = /* @__PURE__ */ new Set();
|
|
1137
|
+
removedClasses = /* @__PURE__ */ new Set();
|
|
1138
|
+
styleChanges = new ChangingItemsTracker_default();
|
|
1139
|
+
attributeChanges = new ChangingItemsTracker_default();
|
|
1007
1140
|
addClass(className) {
|
|
1008
1141
|
if (!this.removedClasses.delete(className)) this.addedClasses.add(className);
|
|
1009
1142
|
}
|
|
@@ -1062,12 +1195,16 @@ var ElementChanges = class {
|
|
|
1062
1195
|
}
|
|
1063
1196
|
};
|
|
1064
1197
|
var ExternalMutationTracker_default = class {
|
|
1198
|
+
element;
|
|
1199
|
+
shouldTrackChangeCallback;
|
|
1200
|
+
mutationObserver;
|
|
1201
|
+
changedElements = /* @__PURE__ */ new WeakMap();
|
|
1202
|
+
changedElementsCount = 0;
|
|
1203
|
+
addedElements = [];
|
|
1204
|
+
removedElements = [];
|
|
1205
|
+
isStarted = false;
|
|
1206
|
+
originalIds = /* @__PURE__ */ new Map();
|
|
1065
1207
|
constructor(element, shouldTrackChangeCallback) {
|
|
1066
|
-
this.changedElements = /* @__PURE__ */ new WeakMap();
|
|
1067
|
-
this.changedElementsCount = 0;
|
|
1068
|
-
this.addedElements = [];
|
|
1069
|
-
this.removedElements = [];
|
|
1070
|
-
this.isStarted = false;
|
|
1071
1208
|
this.element = element;
|
|
1072
1209
|
this.shouldTrackChangeCallback = shouldTrackChangeCallback;
|
|
1073
1210
|
this.mutationObserver = new MutationObserver(this.onMutations.bind(this));
|
|
@@ -1091,6 +1228,10 @@ var ExternalMutationTracker_default = class {
|
|
|
1091
1228
|
getChangedElement(element) {
|
|
1092
1229
|
return this.changedElements.has(element) ? this.changedElements.get(element) : null;
|
|
1093
1230
|
}
|
|
1231
|
+
getOriginalIds() {
|
|
1232
|
+
for (const element of this.originalIds.keys()) if (!this.element.contains(element)) this.originalIds.delete(element);
|
|
1233
|
+
return this.originalIds;
|
|
1234
|
+
}
|
|
1094
1235
|
getAddedElements() {
|
|
1095
1236
|
return this.addedElements;
|
|
1096
1237
|
}
|
|
@@ -1197,6 +1338,10 @@ var ExternalMutationTracker_default = class {
|
|
|
1197
1338
|
handleGenericAttributeMutation(mutation, elementChanges) {
|
|
1198
1339
|
const attributeName = mutation.attributeName;
|
|
1199
1340
|
const element = mutation.target;
|
|
1341
|
+
if (attributeName === "id") {
|
|
1342
|
+
if (!this.originalIds.has(element)) this.originalIds.set(element, mutation.oldValue);
|
|
1343
|
+
if (element.getAttribute("id") === this.originalIds.get(element)) this.originalIds.delete(element);
|
|
1344
|
+
}
|
|
1200
1345
|
let oldValue = mutation.oldValue;
|
|
1201
1346
|
let newValue = element.getAttribute(attributeName);
|
|
1202
1347
|
if (oldValue === attributeName) oldValue = "";
|
|
@@ -1224,11 +1369,14 @@ var ExternalMutationTracker_default = class {
|
|
|
1224
1369
|
}
|
|
1225
1370
|
};
|
|
1226
1371
|
var UnsyncedInputsTracker_default = class {
|
|
1372
|
+
component;
|
|
1373
|
+
modelElementResolver;
|
|
1374
|
+
unsyncedInputs;
|
|
1375
|
+
elementEventListeners = [{
|
|
1376
|
+
event: "input",
|
|
1377
|
+
callback: (event) => this.handleInputEvent(event)
|
|
1378
|
+
}];
|
|
1227
1379
|
constructor(component, modelElementResolver) {
|
|
1228
|
-
this.elementEventListeners = [{
|
|
1229
|
-
event: "input",
|
|
1230
|
-
callback: (event) => this.handleInputEvent(event)
|
|
1231
|
-
}];
|
|
1232
1380
|
this.component = component;
|
|
1233
1381
|
this.modelElementResolver = modelElementResolver;
|
|
1234
1382
|
this.unsyncedInputs = new UnsyncedInputContainer();
|
|
@@ -1268,9 +1416,10 @@ var UnsyncedInputsTracker_default = class {
|
|
|
1268
1416
|
}
|
|
1269
1417
|
};
|
|
1270
1418
|
var UnsyncedInputContainer = class {
|
|
1419
|
+
unsyncedModelFields;
|
|
1420
|
+
unsyncedNonModelFields = [];
|
|
1421
|
+
unsyncedModelNames = [];
|
|
1271
1422
|
constructor() {
|
|
1272
|
-
this.unsyncedNonModelFields = [];
|
|
1273
|
-
this.unsyncedModelNames = [];
|
|
1274
1423
|
this.unsyncedModelFields = /* @__PURE__ */ new Map();
|
|
1275
1424
|
}
|
|
1276
1425
|
add(element, modelName = null) {
|
|
@@ -1316,11 +1465,11 @@ const parseDeepData = (data, propertyPath) => {
|
|
|
1316
1465
|
};
|
|
1317
1466
|
};
|
|
1318
1467
|
var ValueStore_default = class {
|
|
1468
|
+
props = {};
|
|
1469
|
+
dirtyProps = {};
|
|
1470
|
+
pendingProps = {};
|
|
1471
|
+
updatedPropsFromParent = {};
|
|
1319
1472
|
constructor(props) {
|
|
1320
|
-
this.props = {};
|
|
1321
|
-
this.dirtyProps = {};
|
|
1322
|
-
this.pendingProps = {};
|
|
1323
|
-
this.updatedPropsFromParent = {};
|
|
1324
1473
|
this.props = props;
|
|
1325
1474
|
}
|
|
1326
1475
|
get(name) {
|
|
@@ -1372,14 +1521,27 @@ var ValueStore_default = class {
|
|
|
1372
1521
|
}
|
|
1373
1522
|
};
|
|
1374
1523
|
var Component = class {
|
|
1524
|
+
element;
|
|
1525
|
+
name;
|
|
1526
|
+
listeners;
|
|
1527
|
+
backend;
|
|
1528
|
+
elementDriver;
|
|
1529
|
+
id;
|
|
1530
|
+
fingerprint = "";
|
|
1531
|
+
valueStore;
|
|
1532
|
+
unsyncedInputsTracker;
|
|
1533
|
+
hooks;
|
|
1534
|
+
defaultDebounce = 150;
|
|
1535
|
+
backendRequest = null;
|
|
1536
|
+
pendingActions = [];
|
|
1537
|
+
pendingFiles = {};
|
|
1538
|
+
isRequestPending = false;
|
|
1539
|
+
isRemoved = false;
|
|
1540
|
+
requestDebounceTimeout = null;
|
|
1541
|
+
nextRequestPromise;
|
|
1542
|
+
nextRequestPromiseResolve;
|
|
1543
|
+
externalMutationTracker;
|
|
1375
1544
|
constructor(element, name, props, listeners, id, backend, elementDriver) {
|
|
1376
|
-
this.fingerprint = "";
|
|
1377
|
-
this.defaultDebounce = 150;
|
|
1378
|
-
this.backendRequest = null;
|
|
1379
|
-
this.pendingActions = [];
|
|
1380
|
-
this.pendingFiles = {};
|
|
1381
|
-
this.isRequestPending = false;
|
|
1382
|
-
this.requestDebounceTimeout = null;
|
|
1383
1545
|
this.element = element;
|
|
1384
1546
|
this.name = name;
|
|
1385
1547
|
this.backend = backend;
|
|
@@ -1397,6 +1559,11 @@ var Component = class {
|
|
|
1397
1559
|
this.externalMutationTracker = new ExternalMutationTracker_default(this.element, (element) => elementBelongsToThisComponent(element, this));
|
|
1398
1560
|
this.externalMutationTracker.start();
|
|
1399
1561
|
}
|
|
1562
|
+
getOriginalId() {
|
|
1563
|
+
this.externalMutationTracker.handlePendingChanges();
|
|
1564
|
+
const originalIds = this.externalMutationTracker.getOriginalIds();
|
|
1565
|
+
return originalIds.has(this.element) ? originalIds.get(this.element) : this.element.getAttribute("id");
|
|
1566
|
+
}
|
|
1400
1567
|
addPlugin(plugin) {
|
|
1401
1568
|
plugin.attachToComponent(this);
|
|
1402
1569
|
}
|
|
@@ -1477,7 +1644,21 @@ var Component = class {
|
|
|
1477
1644
|
isTurboEnabled() {
|
|
1478
1645
|
return typeof Turbo !== "undefined" && !this.element.closest("[data-turbo=\"false\"]");
|
|
1479
1646
|
}
|
|
1647
|
+
removeFromPage() {
|
|
1648
|
+
this.isRemoved = true;
|
|
1649
|
+
this.disconnect();
|
|
1650
|
+
const element = this.element;
|
|
1651
|
+
for (const name of element.getAttributeNames()) if (name.startsWith("data-live-") && name.endsWith("-value")) element.removeAttribute(name);
|
|
1652
|
+
element.setAttribute("data-live-removing", "");
|
|
1653
|
+
requestAnimationFrame(() => {
|
|
1654
|
+
const animations = (element.getAnimations?.({ subtree: true }) ?? []).filter((animation) => animation.effect?.getComputedTiming().endTime !== Number.POSITIVE_INFINITY);
|
|
1655
|
+
Promise.allSettled(animations.map((animation) => animation.finished)).then(() => {
|
|
1656
|
+
element.remove();
|
|
1657
|
+
});
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1480
1660
|
tryStartingRequest() {
|
|
1661
|
+
if (this.isRemoved) return;
|
|
1481
1662
|
if (!this.backendRequest) {
|
|
1482
1663
|
this.performRequest();
|
|
1483
1664
|
return;
|
|
@@ -1508,10 +1689,10 @@ var Component = class {
|
|
|
1508
1689
|
this.isRequestPending = remainingActions.length > 0;
|
|
1509
1690
|
this.backendRequest.promise.then(async (response) => {
|
|
1510
1691
|
const backendResponse = new BackendResponse_default(response);
|
|
1511
|
-
const html = await backendResponse.getBody();
|
|
1512
|
-
for (const input of Object.values(this.pendingFiles)) input.value = "";
|
|
1513
1692
|
const headers = backendResponse.response.headers;
|
|
1514
|
-
|
|
1693
|
+
for (const input of Object.values(this.pendingFiles)) input.value = "";
|
|
1694
|
+
const html = await backendResponse.getBody();
|
|
1695
|
+
if (!headers.get("Content-Type")?.includes("application/vnd.live-component+html") && !headers.get("X-Live-Redirect") && !headers.has("X-Live-Remove")) {
|
|
1515
1696
|
const controls = { displayError: true };
|
|
1516
1697
|
this.valueStore.pushPendingPropsBackToDirty();
|
|
1517
1698
|
this.hooks.triggerHook("response:error", backendResponse, controls);
|
|
@@ -1520,6 +1701,14 @@ var Component = class {
|
|
|
1520
1701
|
thisPromiseResolve(backendResponse);
|
|
1521
1702
|
return response;
|
|
1522
1703
|
}
|
|
1704
|
+
if (backendResponse.isRemoved()) {
|
|
1705
|
+
this.isRemoved = true;
|
|
1706
|
+
this.processRerender(html, backendResponse);
|
|
1707
|
+
this.backendRequest = null;
|
|
1708
|
+
thisPromiseResolve(backendResponse);
|
|
1709
|
+
this.removeFromPage();
|
|
1710
|
+
return response;
|
|
1711
|
+
}
|
|
1523
1712
|
const liveUrl = backendResponse.getLiveUrl();
|
|
1524
1713
|
if (liveUrl) history.replaceState(history.state, "", new URL(liveUrl + window.location.hash, window.location.origin));
|
|
1525
1714
|
this.processRerender(html, backendResponse);
|
|
@@ -1556,7 +1745,7 @@ var Component = class {
|
|
|
1556
1745
|
}
|
|
1557
1746
|
this.externalMutationTracker.handlePendingChanges();
|
|
1558
1747
|
this.externalMutationTracker.stop();
|
|
1559
|
-
executeMorphdom(this.element, newElement, this.unsyncedInputsTracker.getUnsyncedInputs(), (element) => getValueFromElement(element, this.valueStore), this.externalMutationTracker);
|
|
1748
|
+
executeMorphdom(this.element, newElement, this.unsyncedInputsTracker.getUnsyncedInputs(), (element) => getValueFromElement(element, this.valueStore), this.externalMutationTracker, findChildren(this));
|
|
1560
1749
|
this.externalMutationTracker.start();
|
|
1561
1750
|
const newProps = this.elementDriver.getComponentProps();
|
|
1562
1751
|
this.valueStore.reinitializeAllProps(newProps);
|
|
@@ -1582,6 +1771,14 @@ var Component = class {
|
|
|
1582
1771
|
bubbles: true
|
|
1583
1772
|
}));
|
|
1584
1773
|
});
|
|
1774
|
+
try {
|
|
1775
|
+
const downloadUrl = backendResponse.getDownloadUrl();
|
|
1776
|
+
const download = backendResponse.getDownload();
|
|
1777
|
+
if (downloadUrl) triggerDownload({ url: downloadUrl });
|
|
1778
|
+
else if (download) triggerDownload(download);
|
|
1779
|
+
} catch (error) {
|
|
1780
|
+
console.error("Could not start the download:", error);
|
|
1781
|
+
}
|
|
1585
1782
|
this.hooks.triggerHook("render:finished", this);
|
|
1586
1783
|
}
|
|
1587
1784
|
calculateDebounce(debounce) {
|
|
@@ -1662,6 +1859,7 @@ function proxifyComponent(component) {
|
|
|
1662
1859
|
return Reflect.get(component, prop);
|
|
1663
1860
|
}
|
|
1664
1861
|
if (component.valueStore.has(prop)) return component.getData(prop);
|
|
1862
|
+
if ("toJSON" === prop || "then" === prop) return;
|
|
1665
1863
|
return (args) => {
|
|
1666
1864
|
return component.action.apply(component, [prop, args]);
|
|
1667
1865
|
};
|
|
@@ -1676,7 +1874,23 @@ function proxifyComponent(component) {
|
|
|
1676
1874
|
}
|
|
1677
1875
|
});
|
|
1678
1876
|
}
|
|
1877
|
+
function triggerDownload(download) {
|
|
1878
|
+
const fromUrl = "url" in download;
|
|
1879
|
+
const href = fromUrl ? download.url : URL.createObjectURL(download.blob);
|
|
1880
|
+
const link = Object.assign(document.createElement("a"), {
|
|
1881
|
+
href,
|
|
1882
|
+
download: fromUrl ? "" : download.filename,
|
|
1883
|
+
style: "display: none"
|
|
1884
|
+
});
|
|
1885
|
+
document.body.appendChild(link);
|
|
1886
|
+
link.click();
|
|
1887
|
+
setTimeout(() => {
|
|
1888
|
+
document.body.removeChild(link);
|
|
1889
|
+
if (!fromUrl) URL.revokeObjectURL(href);
|
|
1890
|
+
}, 75);
|
|
1891
|
+
}
|
|
1679
1892
|
var StimulusElementDriver = class {
|
|
1893
|
+
controller;
|
|
1680
1894
|
constructor(controller) {
|
|
1681
1895
|
this.controller = controller;
|
|
1682
1896
|
}
|
|
@@ -1745,8 +1959,9 @@ function get_model_binding_default(modelDirective) {
|
|
|
1745
1959
|
};
|
|
1746
1960
|
}
|
|
1747
1961
|
var ChildComponentPlugin_default = class {
|
|
1962
|
+
component;
|
|
1963
|
+
parentModelBindings = [];
|
|
1748
1964
|
constructor(component) {
|
|
1749
|
-
this.parentModelBindings = [];
|
|
1750
1965
|
this.component = component;
|
|
1751
1966
|
const modelDirectives = getAllModelDirectiveFromElements(this.component.element);
|
|
1752
1967
|
this.parentModelBindings = modelDirectives.map(get_model_binding_default);
|
|
@@ -1783,9 +1998,7 @@ var ChildComponentPlugin_default = class {
|
|
|
1783
1998
|
}
|
|
1784
1999
|
};
|
|
1785
2000
|
var LazyPlugin_default = class {
|
|
1786
|
-
|
|
1787
|
-
this.intersectionObserver = null;
|
|
1788
|
-
}
|
|
2001
|
+
intersectionObserver = null;
|
|
1789
2002
|
attachToComponent(component) {
|
|
1790
2003
|
if ("lazy" !== component.element.attributes.getNamedItem("loading")?.value) return;
|
|
1791
2004
|
component.on("connect", () => {
|
|
@@ -1895,6 +2108,7 @@ var LoadingPlugin_default = class {
|
|
|
1895
2108
|
const loadingDirectives = [];
|
|
1896
2109
|
let matchingElements = Array.from(element.querySelectorAll("[data-loading]"));
|
|
1897
2110
|
matchingElements = matchingElements.filter((elt) => elementBelongsToThisComponent(elt, component));
|
|
2111
|
+
matchingElements = matchingElements.filter((elt) => !elt.closest("[data-live-ignore]"));
|
|
1898
2112
|
if (element.hasAttribute("data-loading")) matchingElements = [element, ...matchingElements];
|
|
1899
2113
|
matchingElements.forEach((element) => {
|
|
1900
2114
|
if (!(element instanceof HTMLElement) && !(element instanceof SVGElement)) throw new Error("Invalid Element Type");
|
|
@@ -1942,9 +2156,7 @@ const parseLoadingAction = (action, isLoading) => {
|
|
|
1942
2156
|
throw new Error(`Unknown data-loading action "${action}"`);
|
|
1943
2157
|
};
|
|
1944
2158
|
var PageUnloadingPlugin_default = class {
|
|
1945
|
-
|
|
1946
|
-
this.isConnected = false;
|
|
1947
|
-
}
|
|
2159
|
+
isConnected = false;
|
|
1948
2160
|
attachToComponent(component) {
|
|
1949
2161
|
component.on("render:started", (html, response, controls) => {
|
|
1950
2162
|
if (!this.isConnected) controls.shouldRender = false;
|
|
@@ -1958,9 +2170,11 @@ var PageUnloadingPlugin_default = class {
|
|
|
1958
2170
|
}
|
|
1959
2171
|
};
|
|
1960
2172
|
var PollingDirector_default = class {
|
|
2173
|
+
component;
|
|
2174
|
+
isPollingActive = true;
|
|
2175
|
+
polls;
|
|
2176
|
+
pollingIntervals = [];
|
|
1961
2177
|
constructor(component) {
|
|
1962
|
-
this.isPollingActive = true;
|
|
1963
|
-
this.pollingIntervals = [];
|
|
1964
2178
|
this.component = component;
|
|
1965
2179
|
}
|
|
1966
2180
|
addPoll(actionName, duration) {
|
|
@@ -2003,6 +2217,8 @@ var PollingDirector_default = class {
|
|
|
2003
2217
|
}
|
|
2004
2218
|
};
|
|
2005
2219
|
var PollingPlugin_default = class {
|
|
2220
|
+
element;
|
|
2221
|
+
pollingDirector;
|
|
2006
2222
|
attachToComponent(component) {
|
|
2007
2223
|
this.element = component.element;
|
|
2008
2224
|
this.pollingDirector = new PollingDirector_default(component);
|
|
@@ -2077,18 +2293,59 @@ var ValidatedFieldsPlugin_default = class {
|
|
|
2077
2293
|
}
|
|
2078
2294
|
};
|
|
2079
2295
|
var LiveControllerDefault = class LiveControllerDefault extends Controller {
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
},
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2296
|
+
static values = {
|
|
2297
|
+
name: String,
|
|
2298
|
+
url: String,
|
|
2299
|
+
props: {
|
|
2300
|
+
type: Object,
|
|
2301
|
+
default: {}
|
|
2302
|
+
},
|
|
2303
|
+
propsUpdatedFromParent: {
|
|
2304
|
+
type: Object,
|
|
2305
|
+
default: {}
|
|
2306
|
+
},
|
|
2307
|
+
listeners: {
|
|
2308
|
+
type: Array,
|
|
2309
|
+
default: []
|
|
2310
|
+
},
|
|
2311
|
+
eventsToEmit: {
|
|
2312
|
+
type: Array,
|
|
2313
|
+
default: []
|
|
2314
|
+
},
|
|
2315
|
+
eventsToDispatch: {
|
|
2316
|
+
type: Array,
|
|
2317
|
+
default: []
|
|
2318
|
+
},
|
|
2319
|
+
debounce: {
|
|
2320
|
+
type: Number,
|
|
2321
|
+
default: 150
|
|
2322
|
+
},
|
|
2323
|
+
fingerprint: {
|
|
2324
|
+
type: String,
|
|
2325
|
+
default: ""
|
|
2326
|
+
},
|
|
2327
|
+
requestMethod: {
|
|
2328
|
+
type: String,
|
|
2329
|
+
default: "post"
|
|
2330
|
+
},
|
|
2331
|
+
fetchCredentials: {
|
|
2332
|
+
type: String,
|
|
2333
|
+
default: "same-origin"
|
|
2334
|
+
}
|
|
2335
|
+
};
|
|
2336
|
+
proxiedComponent;
|
|
2337
|
+
mutationObserver;
|
|
2338
|
+
component;
|
|
2339
|
+
pendingActionTriggerModelElement = null;
|
|
2340
|
+
elementEventListeners = [{
|
|
2341
|
+
event: "input",
|
|
2342
|
+
callback: (event) => this.handleInputEvent(event)
|
|
2343
|
+
}, {
|
|
2344
|
+
event: "change",
|
|
2345
|
+
callback: (event) => this.handleChangeEvent(event)
|
|
2346
|
+
}];
|
|
2347
|
+
pendingFiles = {};
|
|
2348
|
+
static backendFactory = (controller) => new Backend_default(controller.urlValue, controller.requestMethodValue, controller.fetchCredentialsValue);
|
|
2092
2349
|
initialize() {
|
|
2093
2350
|
this.mutationObserver = new MutationObserver(this.onMutations.bind(this));
|
|
2094
2351
|
this.createComponent();
|
|
@@ -2287,7 +2544,7 @@ var LiveControllerDefault = class LiveControllerDefault extends Controller {
|
|
|
2287
2544
|
}
|
|
2288
2545
|
onMutations(mutations) {
|
|
2289
2546
|
mutations.forEach((mutation) => {
|
|
2290
|
-
if (mutation.type === "attributes" && mutation.attributeName === "id" && this.
|
|
2547
|
+
if (mutation.type === "attributes" && mutation.attributeName === "id" && this.component.getOriginalId() !== this.component.id) {
|
|
2291
2548
|
this.disconnectComponent();
|
|
2292
2549
|
this.createComponent();
|
|
2293
2550
|
this.connectComponent();
|
|
@@ -2295,45 +2552,4 @@ var LiveControllerDefault = class LiveControllerDefault extends Controller {
|
|
|
2295
2552
|
});
|
|
2296
2553
|
}
|
|
2297
2554
|
};
|
|
2298
|
-
LiveControllerDefault.values = {
|
|
2299
|
-
name: String,
|
|
2300
|
-
url: String,
|
|
2301
|
-
props: {
|
|
2302
|
-
type: Object,
|
|
2303
|
-
default: {}
|
|
2304
|
-
},
|
|
2305
|
-
propsUpdatedFromParent: {
|
|
2306
|
-
type: Object,
|
|
2307
|
-
default: {}
|
|
2308
|
-
},
|
|
2309
|
-
listeners: {
|
|
2310
|
-
type: Array,
|
|
2311
|
-
default: []
|
|
2312
|
-
},
|
|
2313
|
-
eventsToEmit: {
|
|
2314
|
-
type: Array,
|
|
2315
|
-
default: []
|
|
2316
|
-
},
|
|
2317
|
-
eventsToDispatch: {
|
|
2318
|
-
type: Array,
|
|
2319
|
-
default: []
|
|
2320
|
-
},
|
|
2321
|
-
debounce: {
|
|
2322
|
-
type: Number,
|
|
2323
|
-
default: 150
|
|
2324
|
-
},
|
|
2325
|
-
fingerprint: {
|
|
2326
|
-
type: String,
|
|
2327
|
-
default: ""
|
|
2328
|
-
},
|
|
2329
|
-
requestMethod: {
|
|
2330
|
-
type: String,
|
|
2331
|
-
default: "post"
|
|
2332
|
-
},
|
|
2333
|
-
fetchCredentials: {
|
|
2334
|
-
type: String,
|
|
2335
|
-
default: "same-origin"
|
|
2336
|
-
}
|
|
2337
|
-
};
|
|
2338
|
-
LiveControllerDefault.backendFactory = (controller) => new Backend_default(controller.urlValue, controller.requestMethodValue, controller.fetchCredentialsValue);
|
|
2339
2555
|
export { Component, LiveControllerDefault as default, getComponent };
|