htmx.org 4.0.0-alpha5 → 4.0.0-alpha6
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/ext/hx-upsert.js +89 -0
- package/dist/ext/hx-upsert.min.js +1 -0
- package/dist/ext/hx-upsert.min.js.map +1 -0
- package/dist/ext/hx-ws.js +292 -145
- package/dist/ext/hx-ws.min.js +1 -1
- package/dist/ext/hx-ws.min.js.map +1 -1
- package/dist/htmx.esm.js +178 -154
- package/dist/htmx.esm.min.js +1 -1
- package/dist/htmx.esm.min.js.map +1 -1
- package/dist/htmx.js +178 -154
- package/dist/htmx.min.js +1 -1
- package/dist/htmx.min.js.map +1 -1
- package/package.json +3 -4
package/dist/htmx.esm.js
CHANGED
|
@@ -6,17 +6,17 @@ var htmx = (() => {
|
|
|
6
6
|
#q = []
|
|
7
7
|
|
|
8
8
|
issue(ctx, queueStrategy) {
|
|
9
|
+
ctx.queueStrategy = queueStrategy
|
|
9
10
|
if (!this.#c) {
|
|
10
11
|
this.#c = ctx
|
|
11
12
|
return true
|
|
12
13
|
} else {
|
|
13
|
-
//
|
|
14
|
-
if (queueStrategy === "replace") {
|
|
14
|
+
// Replace strategy OR current is abortable: abort current and issue new
|
|
15
|
+
if (queueStrategy === "replace" || (queueStrategy !== "abort" && this.#c.queueStrategy === "abort")) {
|
|
15
16
|
this.#q.map(value => value.status = "dropped");
|
|
16
17
|
this.#q = []
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
18
|
+
this.#c.request.abort();
|
|
19
|
+
this.#c = ctx
|
|
20
20
|
return true
|
|
21
21
|
} else if (queueStrategy === "queue all") {
|
|
22
22
|
this.#q.push(ctx)
|
|
@@ -28,7 +28,7 @@ var htmx = (() => {
|
|
|
28
28
|
this.#q.map(value => value.status = "dropped");
|
|
29
29
|
this.#q = [ctx]
|
|
30
30
|
ctx.status = "queued";
|
|
31
|
-
} else if (this.#q.length === 0) {
|
|
31
|
+
} else if (this.#q.length === 0 && queueStrategy !== "abort") {
|
|
32
32
|
// default queue first
|
|
33
33
|
this.#q.push(ctx)
|
|
34
34
|
ctx.status = "queued";
|
|
@@ -81,7 +81,8 @@ var htmx = (() => {
|
|
|
81
81
|
createRequestContext: this.#createRequestContext.bind(this),
|
|
82
82
|
collectFormData: this.#collectFormData.bind(this),
|
|
83
83
|
handleHxVals: this.#handleHxVals.bind(this),
|
|
84
|
-
insertContent: this.#insertContent.bind(this)
|
|
84
|
+
insertContent: this.#insertContent.bind(this),
|
|
85
|
+
morph: this.#morph.bind(this)
|
|
85
86
|
};
|
|
86
87
|
document.addEventListener("DOMContentLoaded", () => {
|
|
87
88
|
this.#initHistoryHandling();
|
|
@@ -91,7 +92,7 @@ var htmx = (() => {
|
|
|
91
92
|
|
|
92
93
|
#initHtmxConfig() {
|
|
93
94
|
this.config = {
|
|
94
|
-
version: '4.0.0-
|
|
95
|
+
version: '4.0.0-alpha6',
|
|
95
96
|
logAll: false,
|
|
96
97
|
prefix: "",
|
|
97
98
|
transitions: false,
|
|
@@ -112,6 +113,7 @@ var htmx = (() => {
|
|
|
112
113
|
pauseInBackground: false
|
|
113
114
|
},
|
|
114
115
|
morphIgnore: ["data-htmx-powered"],
|
|
116
|
+
morphScanLimit: 10,
|
|
115
117
|
noSwap: [204, 304],
|
|
116
118
|
implicitInheritance: false
|
|
117
119
|
}
|
|
@@ -376,20 +378,9 @@ var htmx = (() => {
|
|
|
376
378
|
}
|
|
377
379
|
|
|
378
380
|
#handleHxHeaders(elt, headers) {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
return result.then(obj => {
|
|
383
|
-
for (let key in obj) {
|
|
384
|
-
headers[key] = String(obj[key]);
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
} else {
|
|
388
|
-
for (let key in result) {
|
|
389
|
-
headers[key] = String(result[key]);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
381
|
+
return this.#getAttributeObject(elt, "hx-headers", obj => {
|
|
382
|
+
for (let key in obj) headers[key] = String(obj[key]);
|
|
383
|
+
});
|
|
393
384
|
}
|
|
394
385
|
|
|
395
386
|
#resolveTarget(elt, selector) {
|
|
@@ -457,15 +448,20 @@ var htmx = (() => {
|
|
|
457
448
|
return
|
|
458
449
|
} else if (/GET|DELETE/.test(ctx.request.method)) {
|
|
459
450
|
let url = new URL(ctx.request.action, document.baseURI);
|
|
460
|
-
|
|
451
|
+
|
|
461
452
|
for (let key of ctx.request.body.keys()) {
|
|
462
453
|
url.searchParams.delete(key);
|
|
463
454
|
}
|
|
464
455
|
for (let [key, value] of ctx.request.body) {
|
|
465
456
|
url.searchParams.append(key, value);
|
|
466
457
|
}
|
|
467
|
-
|
|
468
|
-
|
|
458
|
+
|
|
459
|
+
// Keep relative if same origin, otherwise use full URL
|
|
460
|
+
if (url.origin === location.origin) {
|
|
461
|
+
ctx.request.action = url.pathname + url.search;
|
|
462
|
+
} else {
|
|
463
|
+
ctx.request.action = url.href;
|
|
464
|
+
}
|
|
469
465
|
ctx.request.body = null;
|
|
470
466
|
} else if (this.#attributeValue(elt, "hx-encoding") !== "multipart/form-data") {
|
|
471
467
|
ctx.request.body = new URLSearchParams(ctx.request.body);
|
|
@@ -769,7 +765,7 @@ var htmx = (() => {
|
|
|
769
765
|
if (syncValue && syncValue.includes(":")) {
|
|
770
766
|
let strings = syncValue.split(":");
|
|
771
767
|
let selector = strings[0];
|
|
772
|
-
syncElt = this.#findExt(selector);
|
|
768
|
+
syncElt = this.#findExt(elt, selector, "hx-sync");
|
|
773
769
|
}
|
|
774
770
|
return syncElt._htmxRequestQueue ||= new ReqQ()
|
|
775
771
|
}
|
|
@@ -1088,8 +1084,10 @@ var htmx = (() => {
|
|
|
1088
1084
|
}
|
|
1089
1085
|
this.#trigger(elt, "htmx:after:cleanup")
|
|
1090
1086
|
}
|
|
1091
|
-
|
|
1092
|
-
|
|
1087
|
+
if (elt.firstChild) {
|
|
1088
|
+
for (let child of elt.querySelectorAll('[data-htmx-powered]')) {
|
|
1089
|
+
this.#cleanup(child);
|
|
1090
|
+
}
|
|
1093
1091
|
}
|
|
1094
1092
|
}
|
|
1095
1093
|
|
|
@@ -1100,10 +1098,12 @@ var htmx = (() => {
|
|
|
1100
1098
|
let newPreservedElts = fragment.querySelectorAll?.(`[${this.#prefix('hx-preserve')}]`) || [];
|
|
1101
1099
|
for (let preservedElt of newPreservedElts) {
|
|
1102
1100
|
let currentElt = document.getElementById(preservedElt.id);
|
|
1103
|
-
if (
|
|
1104
|
-
pantry.moveBefore
|
|
1105
|
-
|
|
1106
|
-
|
|
1101
|
+
if (currentElt) {
|
|
1102
|
+
if (pantry.moveBefore) {
|
|
1103
|
+
pantry.moveBefore(currentElt, null);
|
|
1104
|
+
} else {
|
|
1105
|
+
pantry.appendChild(currentElt);
|
|
1106
|
+
}
|
|
1107
1107
|
}
|
|
1108
1108
|
}
|
|
1109
1109
|
return pantry
|
|
@@ -1112,13 +1112,15 @@ var htmx = (() => {
|
|
|
1112
1112
|
#restorePreservedElements(pantry) {
|
|
1113
1113
|
for (let preservedElt of pantry.children) {
|
|
1114
1114
|
let newElt = document.getElementById(preservedElt.id);
|
|
1115
|
-
if (newElt
|
|
1116
|
-
newElt.parentNode.moveBefore
|
|
1117
|
-
|
|
1118
|
-
|
|
1115
|
+
if (newElt) {
|
|
1116
|
+
if (newElt.parentNode.moveBefore) {
|
|
1117
|
+
newElt.parentNode.moveBefore(preservedElt, newElt);
|
|
1118
|
+
} else {
|
|
1119
|
+
newElt.replaceWith(preservedElt);
|
|
1120
|
+
}
|
|
1121
|
+
this.#cleanup(newElt)
|
|
1122
|
+
newElt.remove()
|
|
1119
1123
|
}
|
|
1120
|
-
this.#cleanup(newElt)
|
|
1121
|
-
newElt.remove()
|
|
1122
1124
|
}
|
|
1123
1125
|
pantry.remove();
|
|
1124
1126
|
}
|
|
@@ -1290,65 +1292,41 @@ var htmx = (() => {
|
|
|
1290
1292
|
let partialTasks = this.#processPartials(fragment, ctx);
|
|
1291
1293
|
tasks.push(...oobTasks, ...partialTasks);
|
|
1292
1294
|
|
|
1293
|
-
// Process main swap
|
|
1295
|
+
// Process main swap first
|
|
1294
1296
|
let mainSwap = this.#processMainSwap(ctx, fragment, partialTasks);
|
|
1295
1297
|
if (mainSwap) {
|
|
1296
|
-
tasks.
|
|
1298
|
+
tasks.unshift(mainSwap);
|
|
1297
1299
|
}
|
|
1298
1300
|
|
|
1299
|
-
// TODO - can we remove this and just let the function complete?
|
|
1300
|
-
if (tasks.length === 0) return;
|
|
1301
|
-
|
|
1302
1301
|
if(!this.#trigger(document, "htmx:before:swap", {ctx, tasks})){
|
|
1303
1302
|
return
|
|
1304
1303
|
}
|
|
1305
1304
|
|
|
1306
|
-
|
|
1305
|
+
let swapPromises = [];
|
|
1307
1306
|
let transitionTasks = [];
|
|
1308
1307
|
for (let task of tasks) {
|
|
1309
|
-
|
|
1310
|
-
let swapDelay = task.swapSpec?.swap;
|
|
1311
|
-
if (!(task.swapSpec?.transition ?? mainSwap?.transition) || (swapDelay && task !== mainSwap)) {
|
|
1312
|
-
if (swapDelay) {
|
|
1313
|
-
if (task === mainSwap) {
|
|
1314
|
-
await this.timeout(swapDelay);
|
|
1315
|
-
} else {
|
|
1316
|
-
setTimeout(() => this.#insertContent(task), this.parseInterval(swapDelay));
|
|
1317
|
-
continue;
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
this.#insertContent(task)
|
|
1321
|
-
} else {
|
|
1308
|
+
if (task.swapSpec?.transition ?? mainSwap?.transition) {
|
|
1322
1309
|
transitionTasks.push(task);
|
|
1310
|
+
} else {
|
|
1311
|
+
swapPromises.push(this.#insertContent(task));
|
|
1323
1312
|
}
|
|
1324
1313
|
}
|
|
1325
1314
|
|
|
1326
|
-
//
|
|
1315
|
+
// submit all transition tasks in the transition queue w/no CSS transitions
|
|
1327
1316
|
if (transitionTasks.length > 0) {
|
|
1328
|
-
|
|
1329
|
-
await this.timeout(mainSwap.swapSpec.swap);
|
|
1330
|
-
}
|
|
1331
|
-
let tasksWrapper = ()=> {
|
|
1317
|
+
let tasksWrapper = async ()=> {
|
|
1332
1318
|
for (let task of transitionTasks) {
|
|
1333
|
-
this.#insertContent(task)
|
|
1319
|
+
await this.#insertContent(task, false)
|
|
1334
1320
|
}
|
|
1335
1321
|
}
|
|
1336
|
-
|
|
1322
|
+
swapPromises.push(this.#submitTransitionTask(tasksWrapper));
|
|
1337
1323
|
}
|
|
1338
1324
|
|
|
1325
|
+
await Promise.all(swapPromises);
|
|
1326
|
+
|
|
1339
1327
|
this.#trigger(document, "htmx:after:swap", {ctx});
|
|
1340
1328
|
if (ctx.title && !mainSwap?.swapSpec?.ignoreTitle) document.title = ctx.title;
|
|
1341
|
-
await this.timeout(1);
|
|
1342
|
-
// invoke restore tasks
|
|
1343
|
-
for (let task of tasks) {
|
|
1344
|
-
for (let restore of task.restoreTasks || []) {
|
|
1345
|
-
restore()
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
this.#trigger(document, "htmx:after:restore", { ctx });
|
|
1349
1329
|
this.#handleAnchorScroll(ctx);
|
|
1350
|
-
// TODO this stuff should be an extension
|
|
1351
|
-
// if (ctx.hx?.triggerafterswap) this.#handleTriggerHeader(ctx.hx.triggerafterswap, ctx.sourceElement);
|
|
1352
1330
|
}
|
|
1353
1331
|
|
|
1354
1332
|
#processMainSwap(ctx, fragment, partialTasks) {
|
|
@@ -1376,7 +1354,7 @@ var htmx = (() => {
|
|
|
1376
1354
|
}
|
|
1377
1355
|
}
|
|
1378
1356
|
|
|
1379
|
-
#insertContent(task) {
|
|
1357
|
+
async #insertContent(task, cssTransition = true) {
|
|
1380
1358
|
let {target, swapSpec, fragment} = task;
|
|
1381
1359
|
if (typeof target === 'string') {
|
|
1382
1360
|
target = document.querySelector(target);
|
|
@@ -1385,66 +1363,109 @@ var htmx = (() => {
|
|
|
1385
1363
|
if (typeof swapSpec === 'string') {
|
|
1386
1364
|
swapSpec = this.#parseSwapSpec(swapSpec);
|
|
1387
1365
|
}
|
|
1366
|
+
if (swapSpec.style === 'none') return;
|
|
1388
1367
|
if (swapSpec.strip && fragment.firstElementChild) {
|
|
1389
1368
|
fragment = document.createDocumentFragment();
|
|
1390
1369
|
fragment.append(...(task.fragment.firstElementChild.content || task.fragment.firstElementChild).childNodes);
|
|
1391
1370
|
}
|
|
1392
1371
|
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
} else if (swapSpec.style === 'outerHTML') {
|
|
1403
|
-
if (parentNode) {
|
|
1404
|
-
this.#captureCSSTransitions(task, parentNode);
|
|
1405
|
-
this.#insertNodes(parentNode, target, fragment);
|
|
1406
|
-
this.#cleanup(target)
|
|
1407
|
-
parentNode.removeChild(target);
|
|
1408
|
-
}
|
|
1409
|
-
} else if (swapSpec.style === 'innerMorph') {
|
|
1410
|
-
this.#morph(target, fragment, true);
|
|
1411
|
-
} else if (swapSpec.style === 'outerMorph') {
|
|
1412
|
-
this.#morph(target, fragment, false);
|
|
1413
|
-
} else if (swapSpec.style === 'beforebegin') {
|
|
1414
|
-
if (parentNode) {
|
|
1415
|
-
this.#insertNodes(parentNode, target, fragment);
|
|
1416
|
-
}
|
|
1417
|
-
} else if (swapSpec.style === 'afterbegin') {
|
|
1418
|
-
this.#insertNodes(target, target.firstChild, fragment);
|
|
1419
|
-
} else if (swapSpec.style === 'beforeend') {
|
|
1420
|
-
this.#insertNodes(target, null, fragment);
|
|
1421
|
-
} else if (swapSpec.style === 'afterend') {
|
|
1422
|
-
if (parentNode) {
|
|
1423
|
-
this.#insertNodes(parentNode, target.nextSibling, fragment);
|
|
1424
|
-
}
|
|
1425
|
-
} else if (swapSpec.style === 'delete') {
|
|
1426
|
-
if (parentNode) {
|
|
1427
|
-
this.#cleanup(target)
|
|
1428
|
-
parentNode.removeChild(target)
|
|
1372
|
+
target.classList.add("htmx-swapping")
|
|
1373
|
+
if (cssTransition && task.swapSpec?.swap) {
|
|
1374
|
+
await this.timeout(task.swapSpec?.swap)
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
if (swapSpec.style === 'delete') {
|
|
1378
|
+
if (target.parentNode) {
|
|
1379
|
+
this.#cleanup(target);
|
|
1380
|
+
target.parentNode.removeChild(target);
|
|
1429
1381
|
}
|
|
1430
1382
|
return;
|
|
1431
|
-
}
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
if (swapSpec.style === 'textContent') {
|
|
1386
|
+
target.textContent = fragment.textContent;
|
|
1387
|
+
target.classList.remove("htmx-swapping")
|
|
1432
1388
|
return;
|
|
1433
|
-
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
let pantry = this.#handlePreservedElements(fragment);
|
|
1392
|
+
let parentNode = target.parentNode;
|
|
1393
|
+
let newContent = [...fragment.childNodes]
|
|
1394
|
+
let settleTasks = []
|
|
1395
|
+
try {
|
|
1396
|
+
if (swapSpec.style === 'innerHTML') {
|
|
1397
|
+
settleTasks = cssTransition ? this.#startCSSTransitions(fragment, target) : []
|
|
1398
|
+
for (const child of target.children) {
|
|
1399
|
+
this.#cleanup(child)
|
|
1400
|
+
}
|
|
1401
|
+
target.replaceChildren(...fragment.childNodes);
|
|
1402
|
+
} else if (swapSpec.style === 'outerHTML') {
|
|
1403
|
+
if (parentNode) {
|
|
1404
|
+
settleTasks = cssTransition ? this.#startCSSTransitions(fragment, target) : []
|
|
1405
|
+
this.#insertNodes(parentNode, target, fragment);
|
|
1406
|
+
this.#cleanup(target)
|
|
1407
|
+
parentNode.removeChild(target);
|
|
1408
|
+
}
|
|
1409
|
+
} else if (swapSpec.style === 'innerMorph') {
|
|
1410
|
+
this.#morph(target, fragment, true);
|
|
1411
|
+
newContent = [...target.childNodes];
|
|
1412
|
+
} else if (swapSpec.style === 'outerMorph') {
|
|
1413
|
+
this.#morph(target, fragment, false);
|
|
1414
|
+
newContent.push(target);
|
|
1415
|
+
} else if (swapSpec.style === 'beforebegin') {
|
|
1416
|
+
if (parentNode) {
|
|
1417
|
+
this.#insertNodes(parentNode, target, fragment);
|
|
1418
|
+
}
|
|
1419
|
+
} else if (swapSpec.style === 'afterbegin') {
|
|
1420
|
+
this.#insertNodes(target, target.firstChild, fragment);
|
|
1421
|
+
} else if (swapSpec.style === 'beforeend') {
|
|
1422
|
+
this.#insertNodes(target, null, fragment);
|
|
1423
|
+
} else if (swapSpec.style === 'afterend') {
|
|
1424
|
+
if (parentNode) {
|
|
1425
|
+
this.#insertNodes(parentNode, target.nextSibling, fragment);
|
|
1426
|
+
}
|
|
1427
|
+
} else {
|
|
1428
|
+
let methods = this.#extMethods.get('handle_swap')
|
|
1429
|
+
let handled = false;
|
|
1430
|
+
for (const method of methods) {
|
|
1431
|
+
let result = method(swapSpec.style, target, fragment, swapSpec);
|
|
1432
|
+
if (result) {
|
|
1433
|
+
handled = true;
|
|
1434
|
+
if (Array.isArray(result)) {
|
|
1435
|
+
newContent = result;
|
|
1436
|
+
}
|
|
1437
|
+
break;
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
if (!handled) {
|
|
1441
|
+
throw new Error(`Unknown swap style: ${swapSpec.style}`);
|
|
1440
1442
|
}
|
|
1441
1443
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
}
|
|
1444
|
+
} finally {
|
|
1445
|
+
target.classList.remove("htmx-swapping")
|
|
1445
1446
|
}
|
|
1446
1447
|
this.#restorePreservedElements(pantry);
|
|
1448
|
+
|
|
1449
|
+
this.#trigger(target, "htmx:before:settle", {task, newContent, settleTasks})
|
|
1450
|
+
|
|
1451
|
+
for (const elt of newContent) {
|
|
1452
|
+
elt.classList?.add?.("htmx-added")
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
if (cssTransition) {
|
|
1456
|
+
target.classList.add("htmx-settling")
|
|
1457
|
+
await this.timeout(swapSpec.settle ?? 1);
|
|
1458
|
+
// invoke settle tasks
|
|
1459
|
+
for (let settleTask of settleTasks) {
|
|
1460
|
+
settleTask()
|
|
1461
|
+
}
|
|
1462
|
+
target.classList.remove("htmx-settling")
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
this.#trigger(target, "htmx:after:settle", {task, newContent, settleTasks})
|
|
1466
|
+
|
|
1447
1467
|
for (const elt of newContent) {
|
|
1468
|
+
elt.classList?.remove?.("htmx-added")
|
|
1448
1469
|
this.process(elt);
|
|
1449
1470
|
this.#handleAutoFocus(elt);
|
|
1450
1471
|
}
|
|
@@ -1457,7 +1478,7 @@ var htmx = (() => {
|
|
|
1457
1478
|
}
|
|
1458
1479
|
on = this.#normalizeElement(on)
|
|
1459
1480
|
this.#triggerExtensions(on, eventName, detail);
|
|
1460
|
-
return this.trigger(on, eventName, detail, bubbles)
|
|
1481
|
+
return this.trigger(on, this.#maybeAdjustMetaCharacter(eventName), detail, bubbles)
|
|
1461
1482
|
}
|
|
1462
1483
|
|
|
1463
1484
|
#triggerExtensions(elt, eventName, detail = {}) {
|
|
@@ -1643,7 +1664,9 @@ var htmx = (() => {
|
|
|
1643
1664
|
if (!path || path === 'false' || path === false) return;
|
|
1644
1665
|
|
|
1645
1666
|
if (path === 'true') {
|
|
1646
|
-
|
|
1667
|
+
let finalUrl = response?.raw?.url || ctx.request.action;
|
|
1668
|
+
let url = new URL(finalUrl, location.href);
|
|
1669
|
+
path = url.pathname + url.search + (ctx.request.anchor ? '#' + ctx.request.anchor : '');
|
|
1647
1670
|
}
|
|
1648
1671
|
|
|
1649
1672
|
let type = push ? 'push' : 'replace';
|
|
@@ -1787,7 +1810,7 @@ var htmx = (() => {
|
|
|
1787
1810
|
}
|
|
1788
1811
|
}
|
|
1789
1812
|
|
|
1790
|
-
#getAttributeObject(elt, attrName) {
|
|
1813
|
+
#getAttributeObject(elt, attrName, callback) {
|
|
1791
1814
|
let attrValue = this.#attributeValue(elt, attrName);
|
|
1792
1815
|
if (!attrValue) return null;
|
|
1793
1816
|
|
|
@@ -1798,28 +1821,19 @@ var htmx = (() => {
|
|
|
1798
1821
|
javascriptContent = '{' + javascriptContent + '}';
|
|
1799
1822
|
}
|
|
1800
1823
|
// Return promise for async evaluation
|
|
1801
|
-
return this.#executeJavaScriptAsync(elt, {}, javascriptContent, true)
|
|
1824
|
+
return this.#executeJavaScriptAsync(elt, {}, javascriptContent, true).then(obj => {
|
|
1825
|
+
callback(obj);
|
|
1826
|
+
});
|
|
1802
1827
|
} else {
|
|
1803
1828
|
// Synchronous path - return the parsed object directly
|
|
1804
|
-
|
|
1829
|
+
callback(this.#parseConfig(attrValue));
|
|
1805
1830
|
}
|
|
1806
1831
|
}
|
|
1807
1832
|
|
|
1808
1833
|
#handleHxVals(elt, body) {
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
return result.then(obj => {
|
|
1813
|
-
for (let key in obj) {
|
|
1814
|
-
body.set(key, obj[key])
|
|
1815
|
-
}
|
|
1816
|
-
});
|
|
1817
|
-
} else {
|
|
1818
|
-
for (let key in result) {
|
|
1819
|
-
body.set(key, result[key])
|
|
1820
|
-
}
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1834
|
+
return this.#getAttributeObject(elt, "hx-vals", obj => {
|
|
1835
|
+
for (let key in obj) body.set(key, obj[key]);
|
|
1836
|
+
});
|
|
1823
1837
|
}
|
|
1824
1838
|
|
|
1825
1839
|
#stringHyperscriptStyleSelector(selector) {
|
|
@@ -2012,26 +2026,35 @@ var htmx = (() => {
|
|
|
2012
2026
|
}
|
|
2013
2027
|
|
|
2014
2028
|
#findBestMatch(ctx, node, startPoint, endPoint) {
|
|
2015
|
-
let softMatch = null, nextSibling = node.nextSibling,
|
|
2029
|
+
let softMatch = null, nextSibling = node.nextSibling, siblingMatchCount = 0, displaceMatchCount = 0, scanLimit = this.config.morphScanLimit;
|
|
2030
|
+
// Get ID count for this node to prioritize ID-based matches
|
|
2016
2031
|
let newSet = ctx.idMap.get(node), nodeMatchCount = newSet?.size || 0;
|
|
2017
2032
|
let cursor = startPoint;
|
|
2018
2033
|
while (cursor && cursor != endPoint) {
|
|
2019
2034
|
let oldSet = ctx.idMap.get(cursor);
|
|
2020
2035
|
if (this.#isSoftMatch(cursor, node)) {
|
|
2036
|
+
// Hard match: matching IDs found in both nodes
|
|
2021
2037
|
if (oldSet && newSet && [...oldSet].some(id => newSet.has(id))) return cursor;
|
|
2022
|
-
if (
|
|
2023
|
-
|
|
2024
|
-
|
|
2038
|
+
if (!oldSet) {
|
|
2039
|
+
// Exact match: nodes are identical
|
|
2040
|
+
if (scanLimit > 0 && cursor.isEqualNode(node)) return cursor;
|
|
2041
|
+
// Soft match: same tag/type, save as fallback
|
|
2042
|
+
if (!softMatch) softMatch = cursor;
|
|
2025
2043
|
}
|
|
2026
2044
|
}
|
|
2045
|
+
// Stop if too many ID elements would be displaced
|
|
2027
2046
|
displaceMatchCount += oldSet?.size || 0;
|
|
2028
2047
|
if (displaceMatchCount > nodeMatchCount) break;
|
|
2029
|
-
|
|
2030
|
-
|
|
2048
|
+
// Look ahead: if next siblings match exactly, abort to let them match instead
|
|
2049
|
+
if (nextSibling && scanLimit > 0 && cursor.isEqualNode(nextSibling)) {
|
|
2050
|
+
siblingMatchCount++;
|
|
2031
2051
|
nextSibling = nextSibling.nextSibling;
|
|
2032
|
-
if (
|
|
2052
|
+
if (siblingMatchCount >= 2) return null;
|
|
2033
2053
|
}
|
|
2054
|
+
// Don't move elements containing focus
|
|
2034
2055
|
if (cursor.contains(document.activeElement)) break;
|
|
2056
|
+
// Stop scanning if limit reached and no IDs to match
|
|
2057
|
+
if (--scanLimit < 1 && nodeMatchCount === 0) break;
|
|
2035
2058
|
cursor = cursor.nextSibling;
|
|
2036
2059
|
}
|
|
2037
2060
|
return softMatch || null;
|
|
@@ -2194,21 +2217,22 @@ var htmx = (() => {
|
|
|
2194
2217
|
}
|
|
2195
2218
|
}
|
|
2196
2219
|
|
|
2197
|
-
#
|
|
2220
|
+
#startCSSTransitions(fragment, root) {
|
|
2198
2221
|
let idElements = root.querySelectorAll("[id]");
|
|
2199
2222
|
let existingElementsById = Object.fromEntries([...idElements].map(e => [e.id, e]));
|
|
2200
|
-
let newElementsWithIds =
|
|
2201
|
-
|
|
2223
|
+
let newElementsWithIds = fragment.querySelectorAll("[id]");
|
|
2224
|
+
let restoreTasks = []
|
|
2202
2225
|
for (let elt of newElementsWithIds) {
|
|
2203
2226
|
let existing = existingElementsById[elt.id];
|
|
2204
2227
|
if (existing?.tagName === elt.tagName) {
|
|
2205
2228
|
let clone = elt.cloneNode(false); // shallow clone node
|
|
2206
|
-
this.#copyAttributes(elt, existing
|
|
2207
|
-
|
|
2208
|
-
this.#copyAttributes(elt, clone
|
|
2229
|
+
this.#copyAttributes(elt, existing)
|
|
2230
|
+
restoreTasks.push(()=>{
|
|
2231
|
+
this.#copyAttributes(elt, clone)
|
|
2209
2232
|
})
|
|
2210
2233
|
}
|
|
2211
2234
|
}
|
|
2235
|
+
return restoreTasks;
|
|
2212
2236
|
}
|
|
2213
2237
|
|
|
2214
2238
|
#normalizeElement(cssOrElement) {
|