htmx.org 4.0.0-alpha4 → 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-compat.js +60 -6
- package/dist/ext/hx-compat.min.js +1 -1
- package/dist/ext/hx-compat.min.js.map +1 -1
- package/dist/ext/hx-head.js +129 -0
- package/dist/ext/hx-head.min.js +1 -0
- package/dist/ext/hx-head.min.js.map +1 -0
- package/dist/ext/hx-preload.js +23 -10
- package/dist/ext/hx-preload.min.js +1 -1
- package/dist/ext/hx-preload.min.js.map +1 -1
- 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 +316 -143
- package/dist/ext/hx-ws.min.js +1 -1
- package/dist/ext/hx-ws.min.js.map +1 -1
- package/dist/htmx.esm.js +338 -236
- package/dist/htmx.esm.min.js +1 -1
- package/dist/htmx.esm.min.js.map +1 -1
- package/dist/htmx.js +338 -236
- package/dist/htmx.min.js +1 -1
- package/dist/htmx.min.js.map +1 -1
- package/package.json +5 -5
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";
|
|
@@ -80,7 +80,9 @@ var htmx = (() => {
|
|
|
80
80
|
determineMethodAndAction: this.#determineMethodAndAction.bind(this),
|
|
81
81
|
createRequestContext: this.#createRequestContext.bind(this),
|
|
82
82
|
collectFormData: this.#collectFormData.bind(this),
|
|
83
|
-
handleHxVals: this.#handleHxVals.bind(this)
|
|
83
|
+
handleHxVals: this.#handleHxVals.bind(this),
|
|
84
|
+
insertContent: this.#insertContent.bind(this),
|
|
85
|
+
morph: this.#morph.bind(this)
|
|
84
86
|
};
|
|
85
87
|
document.addEventListener("DOMContentLoaded", () => {
|
|
86
88
|
this.#initHistoryHandling();
|
|
@@ -90,12 +92,11 @@ var htmx = (() => {
|
|
|
90
92
|
|
|
91
93
|
#initHtmxConfig() {
|
|
92
94
|
this.config = {
|
|
93
|
-
version: '4.0.0-
|
|
95
|
+
version: '4.0.0-alpha6',
|
|
94
96
|
logAll: false,
|
|
95
97
|
prefix: "",
|
|
96
|
-
transitions:
|
|
98
|
+
transitions: false,
|
|
97
99
|
history: true,
|
|
98
|
-
historyReload: false,
|
|
99
100
|
mode: 'same-origin',
|
|
100
101
|
defaultSwap: "innerHTML",
|
|
101
102
|
indicatorClass: "htmx-indicator",
|
|
@@ -112,22 +113,13 @@ 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
|
}
|
|
118
|
-
let metaConfig = document.querySelector('meta[name="htmx
|
|
120
|
+
let metaConfig = document.querySelector('meta[name="htmx-config"]');
|
|
119
121
|
if (metaConfig) {
|
|
120
|
-
|
|
121
|
-
let overrides = this.#parseConfig(content);
|
|
122
|
-
// Deep merge nested config objects
|
|
123
|
-
for (let key in overrides) {
|
|
124
|
-
let val = overrides[key];
|
|
125
|
-
if (val && typeof val === 'object' && !Array.isArray(val) && this.config[key]) {
|
|
126
|
-
Object.assign(this.config[key], val);
|
|
127
|
-
} else {
|
|
128
|
-
this.config[key] = val;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
122
|
+
this.#mergeConfig(metaConfig.content, this.config);
|
|
131
123
|
}
|
|
132
124
|
this.#approvedExt = this.config.extensions;
|
|
133
125
|
}
|
|
@@ -181,48 +173,60 @@ var htmx = (() => {
|
|
|
181
173
|
style === 'append' ? 'beforeend' : style;
|
|
182
174
|
}
|
|
183
175
|
|
|
184
|
-
#
|
|
176
|
+
#findThisElements(elt, attrName) {
|
|
177
|
+
let result = [];
|
|
178
|
+
this.#attributeValue(elt, attrName, undefined, (val, elt) => {
|
|
179
|
+
if (val?.split(/\s*,\s*/).includes('this')) result.push(elt);
|
|
180
|
+
});
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#attributeValue(elt, name, defaultVal, eltCollector) {
|
|
185
185
|
name = this.#prefix(name);
|
|
186
186
|
let appendName = name + this.#maybeAdjustMetaCharacter(":append");
|
|
187
187
|
let inheritName = name + (this.config.implicitInheritance ? "" : this.#maybeAdjustMetaCharacter(":inherited"));
|
|
188
188
|
let inheritAppendName = name + this.#maybeAdjustMetaCharacter(":inherited:append");
|
|
189
189
|
|
|
190
190
|
if (elt.hasAttribute(name)) {
|
|
191
|
-
|
|
191
|
+
let val = elt.getAttribute(name);
|
|
192
|
+
return eltCollector ? eltCollector(val, elt) : val;
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
if (elt.hasAttribute(inheritName)) {
|
|
195
|
-
|
|
196
|
+
let val = elt.getAttribute(inheritName);
|
|
197
|
+
return eltCollector ? eltCollector(val, elt) : val;
|
|
196
198
|
}
|
|
197
199
|
|
|
198
200
|
if (elt.hasAttribute(appendName) || elt.hasAttribute(inheritAppendName)) {
|
|
199
201
|
let appendValue = elt.getAttribute(appendName) || elt.getAttribute(inheritAppendName);
|
|
200
202
|
let parent = elt.parentNode?.closest?.(`[${CSS.escape(inheritName)}],[${CSS.escape(inheritAppendName)}]`);
|
|
203
|
+
if (eltCollector) {
|
|
204
|
+
eltCollector(appendValue, elt);
|
|
205
|
+
}
|
|
201
206
|
if (parent) {
|
|
202
|
-
let inherited = this.#attributeValue(parent, name, undefined,
|
|
203
|
-
return
|
|
204
|
-
} else {
|
|
205
|
-
return returnElt ? elt : appendValue;
|
|
207
|
+
let inherited = this.#attributeValue(parent, name, undefined, eltCollector);
|
|
208
|
+
return inherited ? (inherited + "," + appendValue).replace(/[{}]/g, '') : appendValue;
|
|
206
209
|
}
|
|
210
|
+
return appendValue;
|
|
207
211
|
}
|
|
208
212
|
|
|
209
213
|
let parent = elt.parentNode?.closest?.(`[${CSS.escape(inheritName)}],[${CSS.escape(inheritAppendName)}]`);
|
|
210
214
|
if (parent) {
|
|
211
|
-
let val = this.#attributeValue(parent, name, undefined,
|
|
212
|
-
if (!
|
|
213
|
-
this.#triggerExtensions(elt, "htmx:after:implicitInheritance", {elt, parent})
|
|
215
|
+
let val = this.#attributeValue(parent, name, undefined, eltCollector);
|
|
216
|
+
if (!eltCollector && val && this.config.implicitInheritance) {
|
|
217
|
+
this.#triggerExtensions(elt, "htmx:after:implicitInheritance", {elt, name, parent})
|
|
214
218
|
}
|
|
215
219
|
return val;
|
|
216
220
|
}
|
|
217
|
-
return
|
|
221
|
+
return defaultVal;
|
|
218
222
|
}
|
|
219
223
|
|
|
220
224
|
#parseConfig(configString) {
|
|
221
225
|
if (configString[0] === '{') return JSON.parse(configString);
|
|
222
|
-
let configPattern = /([^\s
|
|
226
|
+
let configPattern = /(?:"([^"]+)"|([^\s,:]+))(?:\s*:\s*(?:"([^"]*)"|'([^']*)'|<([^>]+)\/>|([^\s,]+)))?(?=\s|,|$)/g;
|
|
223
227
|
return [...configString.matchAll(configPattern)].reduce((result, match) => {
|
|
224
|
-
let keyPath = match[1].split('.');
|
|
225
|
-
let value = (match[
|
|
228
|
+
let keyPath = (match[1] ?? match[2]).split('.');
|
|
229
|
+
let value = (match[3] ?? match[4] ?? match[5] ?? match[6] ?? 'true').trim();
|
|
226
230
|
if (value === 'true') value = true;
|
|
227
231
|
else if (value === 'false') value = false;
|
|
228
232
|
else if (/^\d+$/.test(value)) value = parseInt(value);
|
|
@@ -231,6 +235,19 @@ var htmx = (() => {
|
|
|
231
235
|
}, {});
|
|
232
236
|
}
|
|
233
237
|
|
|
238
|
+
#mergeConfig(configString, target) {
|
|
239
|
+
let parsed = this.#parseConfig(configString);
|
|
240
|
+
for (let key in parsed) {
|
|
241
|
+
let val = parsed[key];
|
|
242
|
+
if (val && typeof val === 'object' && !Array.isArray(val) && target[key]) {
|
|
243
|
+
Object.assign(target[key], val);
|
|
244
|
+
} else {
|
|
245
|
+
target[key] = val;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return target;
|
|
249
|
+
}
|
|
250
|
+
|
|
234
251
|
#parseTriggerSpecs(spec) {
|
|
235
252
|
return spec.split(',').map(s => {
|
|
236
253
|
let m = s.match(/^\s*(\S+\[[^\]]*\]|\S+)\s*(.*?)\s*$/);
|
|
@@ -305,44 +322,36 @@ var htmx = (() => {
|
|
|
305
322
|
status: "created",
|
|
306
323
|
select: this.#attributeValue(sourceElement, "hx-select"),
|
|
307
324
|
selectOOB: this.#attributeValue(sourceElement, "hx-select-oob"),
|
|
308
|
-
target: this.#
|
|
309
|
-
swap: this.#attributeValue(sourceElement, "hx-swap"
|
|
325
|
+
target: this.#attributeValue(sourceElement, "hx-target"),
|
|
326
|
+
swap: this.#attributeValue(sourceElement, "hx-swap") ?? this.config.defaultSwap,
|
|
310
327
|
push: this.#attributeValue(sourceElement, "hx-push-url"),
|
|
311
328
|
replace: this.#attributeValue(sourceElement, "hx-replace-url"),
|
|
312
329
|
transition: this.config.transitions,
|
|
313
330
|
confirm: this.#attributeValue(sourceElement, "hx-confirm"),
|
|
314
331
|
request: {
|
|
315
|
-
validate: "true" === this.#attributeValue(sourceElement, "hx-validate", sourceElement.matches('form') ? "true" : "false"),
|
|
332
|
+
validate: "true" === this.#attributeValue(sourceElement, "hx-validate", sourceElement.matches('form') && !sourceElement.noValidate && !sourceEvent.submitter?.formNoValidate ? "true" : "false"),
|
|
316
333
|
action: fullAction,
|
|
317
334
|
anchor,
|
|
318
335
|
method,
|
|
319
|
-
headers: this.#
|
|
336
|
+
headers: this.#createCoreHeaders(sourceElement),
|
|
320
337
|
abort: ac.abort.bind(ac),
|
|
321
338
|
credentials: "same-origin",
|
|
322
339
|
signal: ac.signal,
|
|
323
340
|
mode: this.config.mode
|
|
324
341
|
}
|
|
325
342
|
};
|
|
343
|
+
// Apply boost config overrides
|
|
344
|
+
if (sourceElement._htmx?.boosted) {
|
|
345
|
+
this.#mergeConfig(sourceElement._htmx.boosted, ctx);
|
|
346
|
+
}
|
|
347
|
+
ctx.target = this.#resolveTarget(sourceElement, ctx.target);
|
|
326
348
|
|
|
327
349
|
// Apply hx-config overrides
|
|
328
350
|
let configAttr = this.#attributeValue(sourceElement, "hx-config");
|
|
329
351
|
if (configAttr) {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (key.startsWith('+')) {
|
|
334
|
-
let actualKey = key.substring(1);
|
|
335
|
-
if (req[actualKey] && typeof req[actualKey] === 'object') {
|
|
336
|
-
Object.assign(req[actualKey], configOverrides[key]);
|
|
337
|
-
} else {
|
|
338
|
-
req[actualKey] = configOverrides[key];
|
|
339
|
-
}
|
|
340
|
-
} else {
|
|
341
|
-
req[key] = configOverrides[key];
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
if (req.etag) {
|
|
345
|
-
(sourceElement._htmx ||= {}).etag ||= req.etag
|
|
352
|
+
this.#mergeConfig(configAttr, ctx.request);
|
|
353
|
+
if (ctx.request.etag) {
|
|
354
|
+
(sourceElement._htmx ||= {}).etag ||= ctx.request.etag
|
|
346
355
|
}
|
|
347
356
|
}
|
|
348
357
|
if (sourceElement._htmx?.etag) {
|
|
@@ -351,30 +360,34 @@ var htmx = (() => {
|
|
|
351
360
|
return ctx;
|
|
352
361
|
}
|
|
353
362
|
|
|
354
|
-
#
|
|
363
|
+
#buildIdentifier(elt) {
|
|
364
|
+
return `${elt.tagName.toLowerCase()}${elt.id ? '#' + elt.id : ''}`;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
#createCoreHeaders(elt) {
|
|
355
368
|
let headers = {
|
|
356
369
|
"HX-Request": "true",
|
|
357
|
-
"HX-Source": elt
|
|
370
|
+
"HX-Source": this.#buildIdentifier(elt),
|
|
358
371
|
"HX-Current-URL": location.href,
|
|
359
372
|
"Accept": "text/html, text/event-stream"
|
|
360
373
|
};
|
|
361
374
|
if (this.#isBoosted(elt)) {
|
|
362
375
|
headers["HX-Boosted"] = "true"
|
|
363
376
|
}
|
|
364
|
-
let headersAttribute = this.#attributeValue(elt, "hx-headers");
|
|
365
|
-
if (headersAttribute) {
|
|
366
|
-
Object.assign(headers, this.#parseConfig(headersAttribute));
|
|
367
|
-
}
|
|
368
377
|
return headers;
|
|
369
378
|
}
|
|
370
379
|
|
|
380
|
+
#handleHxHeaders(elt, headers) {
|
|
381
|
+
return this.#getAttributeObject(elt, "hx-headers", obj => {
|
|
382
|
+
for (let key in obj) headers[key] = String(obj[key]);
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
371
386
|
#resolveTarget(elt, selector) {
|
|
372
387
|
if (selector instanceof Element) {
|
|
373
388
|
return selector;
|
|
374
|
-
} else if (selector === 'this') {
|
|
375
|
-
return this.#attributeValue(elt, "hx-target", undefined, true);
|
|
376
389
|
} else if (selector != null) {
|
|
377
|
-
return this
|
|
390
|
+
return this.#findExt(elt, selector, "hx-target");
|
|
378
391
|
} else if (this.#isBoosted(elt)) {
|
|
379
392
|
return document.body
|
|
380
393
|
} else {
|
|
@@ -397,7 +410,8 @@ var htmx = (() => {
|
|
|
397
410
|
|
|
398
411
|
// Build request body
|
|
399
412
|
let form = elt.form || elt.closest("form")
|
|
400
|
-
let body = this.#collectFormData(elt, form, evt.submitter)
|
|
413
|
+
let body = this.#collectFormData(elt, form, evt.submitter, ctx.request.validate)
|
|
414
|
+
if (!body) return // Validation failed
|
|
401
415
|
let valsResult = this.#handleHxVals(elt, body)
|
|
402
416
|
if (valsResult) await valsResult // Only await if it returned a promise
|
|
403
417
|
if (ctx.values) {
|
|
@@ -407,6 +421,16 @@ var htmx = (() => {
|
|
|
407
421
|
}
|
|
408
422
|
}
|
|
409
423
|
|
|
424
|
+
// Handle dynamic headers
|
|
425
|
+
let headersResult = this.#handleHxHeaders(elt, ctx.request.headers)
|
|
426
|
+
if (headersResult) await headersResult // Only await if it returned a promise
|
|
427
|
+
|
|
428
|
+
// Add HX-Request-Type and HX-Target headers
|
|
429
|
+
ctx.request.headers["HX-Request-Type"] = (ctx.target === document.body || ctx.select) ? "full" : "partial";
|
|
430
|
+
if (ctx.target) {
|
|
431
|
+
ctx.request.headers["HX-Target"] = this.#buildIdentifier(ctx.target);
|
|
432
|
+
}
|
|
433
|
+
|
|
410
434
|
// Setup event-dependent request details
|
|
411
435
|
Object.assign(ctx.request, {
|
|
412
436
|
form,
|
|
@@ -416,7 +440,6 @@ var htmx = (() => {
|
|
|
416
440
|
|
|
417
441
|
if (!this.#trigger(elt, "htmx:config:request", {ctx: ctx})) return
|
|
418
442
|
if (!this.#verbs.includes(ctx.request.method.toLowerCase())) return
|
|
419
|
-
if (ctx.request.validate && ctx.request.form && !ctx.request.form.reportValidity()) return
|
|
420
443
|
|
|
421
444
|
let javascriptContent = this.#extractJavascriptContent(ctx.request.action);
|
|
422
445
|
if (javascriptContent != null) {
|
|
@@ -425,15 +448,20 @@ var htmx = (() => {
|
|
|
425
448
|
return
|
|
426
449
|
} else if (/GET|DELETE/.test(ctx.request.method)) {
|
|
427
450
|
let url = new URL(ctx.request.action, document.baseURI);
|
|
428
|
-
|
|
451
|
+
|
|
429
452
|
for (let key of ctx.request.body.keys()) {
|
|
430
453
|
url.searchParams.delete(key);
|
|
431
454
|
}
|
|
432
455
|
for (let [key, value] of ctx.request.body) {
|
|
433
456
|
url.searchParams.append(key, value);
|
|
434
457
|
}
|
|
435
|
-
|
|
436
|
-
|
|
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
|
+
}
|
|
437
465
|
ctx.request.body = null;
|
|
438
466
|
} else if (this.#attributeValue(elt, "hx-encoding") !== "multipart/form-data") {
|
|
439
467
|
ctx.request.body = new URLSearchParams(ctx.request.body);
|
|
@@ -452,10 +480,8 @@ var htmx = (() => {
|
|
|
452
480
|
ctx.status = "issuing"
|
|
453
481
|
this.#initTimeout(ctx);
|
|
454
482
|
|
|
455
|
-
let
|
|
456
|
-
let
|
|
457
|
-
let disableSelector = this.#attributeValue(elt, "hx-disable");
|
|
458
|
-
let disableElements = this.#disableElements(elt, disableSelector);
|
|
483
|
+
let indicators = this.#showIndicators(elt);
|
|
484
|
+
let disableElements = this.#disableElements(elt);
|
|
459
485
|
|
|
460
486
|
try {
|
|
461
487
|
// Handle confirmation
|
|
@@ -739,7 +765,7 @@ var htmx = (() => {
|
|
|
739
765
|
if (syncValue && syncValue.includes(":")) {
|
|
740
766
|
let strings = syncValue.split(":");
|
|
741
767
|
let selector = strings[0];
|
|
742
|
-
syncElt = this.#findExt(selector);
|
|
768
|
+
syncElt = this.#findExt(elt, selector, "hx-sync");
|
|
743
769
|
}
|
|
744
770
|
return syncElt._htmxRequestQueue ||= new ReqQ()
|
|
745
771
|
}
|
|
@@ -1001,8 +1027,9 @@ var htmx = (() => {
|
|
|
1001
1027
|
}
|
|
1002
1028
|
|
|
1003
1029
|
#maybeBoost(elt) {
|
|
1004
|
-
|
|
1005
|
-
|
|
1030
|
+
let boostValue = this.#attributeValue(elt, "hx-boost");
|
|
1031
|
+
if (boostValue && boostValue !== "false" && this.#shouldBoost(elt)) {
|
|
1032
|
+
elt._htmx = {eventHandler: this.#createHtmxEventHandler(elt), requests: [], boosted: boostValue}
|
|
1006
1033
|
elt.setAttribute('data-htmx-powered', 'true');
|
|
1007
1034
|
if (elt.matches('a') && !elt.hasAttribute("target")) {
|
|
1008
1035
|
elt.addEventListener('click', (click) => {
|
|
@@ -1057,8 +1084,10 @@ var htmx = (() => {
|
|
|
1057
1084
|
}
|
|
1058
1085
|
this.#trigger(elt, "htmx:after:cleanup")
|
|
1059
1086
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1087
|
+
if (elt.firstChild) {
|
|
1088
|
+
for (let child of elt.querySelectorAll('[data-htmx-powered]')) {
|
|
1089
|
+
this.#cleanup(child);
|
|
1090
|
+
}
|
|
1062
1091
|
}
|
|
1063
1092
|
}
|
|
1064
1093
|
|
|
@@ -1069,10 +1098,12 @@ var htmx = (() => {
|
|
|
1069
1098
|
let newPreservedElts = fragment.querySelectorAll?.(`[${this.#prefix('hx-preserve')}]`) || [];
|
|
1070
1099
|
for (let preservedElt of newPreservedElts) {
|
|
1071
1100
|
let currentElt = document.getElementById(preservedElt.id);
|
|
1072
|
-
if (
|
|
1073
|
-
pantry.moveBefore
|
|
1074
|
-
|
|
1075
|
-
|
|
1101
|
+
if (currentElt) {
|
|
1102
|
+
if (pantry.moveBefore) {
|
|
1103
|
+
pantry.moveBefore(currentElt, null);
|
|
1104
|
+
} else {
|
|
1105
|
+
pantry.appendChild(currentElt);
|
|
1106
|
+
}
|
|
1076
1107
|
}
|
|
1077
1108
|
}
|
|
1078
1109
|
return pantry
|
|
@@ -1081,13 +1112,15 @@ var htmx = (() => {
|
|
|
1081
1112
|
#restorePreservedElements(pantry) {
|
|
1082
1113
|
for (let preservedElt of pantry.children) {
|
|
1083
1114
|
let newElt = document.getElementById(preservedElt.id);
|
|
1084
|
-
if (newElt
|
|
1085
|
-
newElt.parentNode.moveBefore
|
|
1086
|
-
|
|
1087
|
-
|
|
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()
|
|
1088
1123
|
}
|
|
1089
|
-
this.#cleanup(newElt)
|
|
1090
|
-
newElt.remove()
|
|
1091
1124
|
}
|
|
1092
1125
|
pantry.remove();
|
|
1093
1126
|
}
|
|
@@ -1187,13 +1220,11 @@ var htmx = (() => {
|
|
|
1187
1220
|
let type = templateElt.getAttribute('type');
|
|
1188
1221
|
|
|
1189
1222
|
if (type === 'partial') {
|
|
1190
|
-
let swapSpec = this.#parseSwapSpec(templateElt.getAttribute(this.#prefix('hx-swap')) || this.config.defaultSwap);
|
|
1191
|
-
|
|
1192
1223
|
tasks.push({
|
|
1193
1224
|
type: 'partial',
|
|
1194
1225
|
fragment: templateElt.content.cloneNode(true),
|
|
1195
1226
|
target: templateElt.getAttribute(this.#prefix('hx-target')),
|
|
1196
|
-
swapSpec,
|
|
1227
|
+
swapSpec: this.#parseSwapSpec(templateElt.getAttribute(this.#prefix('hx-swap')) || this.config.defaultSwap),
|
|
1197
1228
|
sourceElement: ctx.sourceElement
|
|
1198
1229
|
});
|
|
1199
1230
|
} else {
|
|
@@ -1210,18 +1241,18 @@ var htmx = (() => {
|
|
|
1210
1241
|
autofocus?.focus?.()
|
|
1211
1242
|
}
|
|
1212
1243
|
|
|
1213
|
-
#handleScroll(
|
|
1214
|
-
if (
|
|
1215
|
-
let
|
|
1216
|
-
if (
|
|
1217
|
-
|
|
1218
|
-
} else if (
|
|
1219
|
-
|
|
1244
|
+
#handleScroll(swapSpec, target) {
|
|
1245
|
+
if (swapSpec.scroll) {
|
|
1246
|
+
let scrollTarget = swapSpec.scrollTarget ? this.#findExt(swapSpec.scrollTarget) : target;
|
|
1247
|
+
if (swapSpec.scroll === 'top') {
|
|
1248
|
+
scrollTarget.scrollTop = 0;
|
|
1249
|
+
} else if (swapSpec.scroll === 'bottom'){
|
|
1250
|
+
scrollTarget.scrollTop = scrollTarget.scrollHeight;
|
|
1220
1251
|
}
|
|
1221
1252
|
}
|
|
1222
|
-
if (
|
|
1223
|
-
let
|
|
1224
|
-
|
|
1253
|
+
if (swapSpec.show) {
|
|
1254
|
+
let showTarget = swapSpec.showTarget ? this.#findExt(swapSpec.showTarget) : target;
|
|
1255
|
+
showTarget.scrollIntoView(swapSpec.show === 'top')
|
|
1225
1256
|
}
|
|
1226
1257
|
}
|
|
1227
1258
|
|
|
@@ -1261,62 +1292,48 @@ var htmx = (() => {
|
|
|
1261
1292
|
let partialTasks = this.#processPartials(fragment, ctx);
|
|
1262
1293
|
tasks.push(...oobTasks, ...partialTasks);
|
|
1263
1294
|
|
|
1264
|
-
// Process main swap
|
|
1295
|
+
// Process main swap first
|
|
1265
1296
|
let mainSwap = this.#processMainSwap(ctx, fragment, partialTasks);
|
|
1266
1297
|
if (mainSwap) {
|
|
1267
|
-
tasks.
|
|
1298
|
+
tasks.unshift(mainSwap);
|
|
1268
1299
|
}
|
|
1269
1300
|
|
|
1270
|
-
// TODO - can we remove this and just let the function complete?
|
|
1271
|
-
if (tasks.length === 0) return;
|
|
1272
|
-
|
|
1273
|
-
// Separate transition/nonTransition tasks
|
|
1274
|
-
let transitionTasks = tasks.filter(t => t.transition);
|
|
1275
|
-
let nonTransitionTasks = tasks.filter(t => !t.transition);
|
|
1276
|
-
|
|
1277
1301
|
if(!this.#trigger(document, "htmx:before:swap", {ctx, tasks})){
|
|
1278
1302
|
return
|
|
1279
1303
|
}
|
|
1280
1304
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1305
|
+
let swapPromises = [];
|
|
1306
|
+
let transitionTasks = [];
|
|
1307
|
+
for (let task of tasks) {
|
|
1308
|
+
if (task.swapSpec?.transition ?? mainSwap?.transition) {
|
|
1309
|
+
transitionTasks.push(task);
|
|
1285
1310
|
} else {
|
|
1286
|
-
this.#insertContent(task)
|
|
1311
|
+
swapPromises.push(this.#insertContent(task));
|
|
1287
1312
|
}
|
|
1288
1313
|
}
|
|
1289
1314
|
|
|
1290
|
-
//
|
|
1315
|
+
// submit all transition tasks in the transition queue w/no CSS transitions
|
|
1291
1316
|
if (transitionTasks.length > 0) {
|
|
1292
|
-
let tasksWrapper = ()=> {
|
|
1317
|
+
let tasksWrapper = async ()=> {
|
|
1293
1318
|
for (let task of transitionTasks) {
|
|
1294
|
-
this.#insertContent(task)
|
|
1319
|
+
await this.#insertContent(task, false)
|
|
1295
1320
|
}
|
|
1296
1321
|
}
|
|
1297
|
-
|
|
1322
|
+
swapPromises.push(this.#submitTransitionTask(tasksWrapper));
|
|
1298
1323
|
}
|
|
1299
1324
|
|
|
1325
|
+
await Promise.all(swapPromises);
|
|
1326
|
+
|
|
1300
1327
|
this.#trigger(document, "htmx:after:swap", {ctx});
|
|
1301
1328
|
if (ctx.title && !mainSwap?.swapSpec?.ignoreTitle) document.title = ctx.title;
|
|
1302
|
-
await this.timeout(1);
|
|
1303
|
-
// invoke restore tasks
|
|
1304
|
-
for (let task of tasks) {
|
|
1305
|
-
for (let restore of task.restoreTasks || []) {
|
|
1306
|
-
restore()
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
this.#trigger(document, "htmx:after:restore", { ctx });
|
|
1310
1329
|
this.#handleAnchorScroll(ctx);
|
|
1311
|
-
// TODO this stuff should be an extension
|
|
1312
|
-
// if (ctx.hx?.triggerafterswap) this.#handleTriggerHeader(ctx.hx.triggerafterswap, ctx.sourceElement);
|
|
1313
1330
|
}
|
|
1314
1331
|
|
|
1315
1332
|
#processMainSwap(ctx, fragment, partialTasks) {
|
|
1316
1333
|
// Create main task if needed
|
|
1317
1334
|
let swapSpec = this.#parseSwapSpec(ctx.swap || this.config.defaultSwap);
|
|
1318
1335
|
// skip creating main swap if extracting partials resulted in empty response except for delete style
|
|
1319
|
-
if (swapSpec.style === 'delete' || /\S/.test(fragment.
|
|
1336
|
+
if (swapSpec.style === 'delete' || fragment.childElementCount > 0 || /\S/.test(fragment.textContent) || !partialTasks.length) {
|
|
1320
1337
|
if (ctx.select) {
|
|
1321
1338
|
let selected = fragment.querySelectorAll(ctx.select);
|
|
1322
1339
|
fragment = document.createDocumentFragment();
|
|
@@ -1337,70 +1354,122 @@ var htmx = (() => {
|
|
|
1337
1354
|
}
|
|
1338
1355
|
}
|
|
1339
1356
|
|
|
1340
|
-
#insertContent(task) {
|
|
1357
|
+
async #insertContent(task, cssTransition = true) {
|
|
1341
1358
|
let {target, swapSpec, fragment} = task;
|
|
1342
1359
|
if (typeof target === 'string') {
|
|
1343
1360
|
target = document.querySelector(target);
|
|
1344
1361
|
}
|
|
1345
1362
|
if (!target) return;
|
|
1363
|
+
if (typeof swapSpec === 'string') {
|
|
1364
|
+
swapSpec = this.#parseSwapSpec(swapSpec);
|
|
1365
|
+
}
|
|
1366
|
+
if (swapSpec.style === 'none') return;
|
|
1346
1367
|
if (swapSpec.strip && fragment.firstElementChild) {
|
|
1347
|
-
task.unstripped = fragment;
|
|
1348
1368
|
fragment = document.createDocumentFragment();
|
|
1349
1369
|
fragment.append(...(task.fragment.firstElementChild.content || task.fragment.firstElementChild).childNodes);
|
|
1350
1370
|
}
|
|
1351
1371
|
|
|
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);
|
|
1381
|
+
}
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
if (swapSpec.style === 'textContent') {
|
|
1386
|
+
target.textContent = fragment.textContent;
|
|
1387
|
+
target.classList.remove("htmx-swapping")
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1352
1391
|
let pantry = this.#handlePreservedElements(fragment);
|
|
1353
1392
|
let parentNode = target.parentNode;
|
|
1354
1393
|
let newContent = [...fragment.childNodes]
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
this.#
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
if (
|
|
1374
|
-
this.#
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
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}`);
|
|
1442
|
+
}
|
|
1388
1443
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
return;
|
|
1392
|
-
} else {
|
|
1393
|
-
task.target = target;
|
|
1394
|
-
task.fragment = fragment;
|
|
1395
|
-
if (!this.#triggerExtensions(target, 'htmx:handle:swap', task)) return;
|
|
1396
|
-
throw new Error(`Unknown swap style: ${swapSpec.style}`);
|
|
1444
|
+
} finally {
|
|
1445
|
+
target.classList.remove("htmx-swapping")
|
|
1397
1446
|
}
|
|
1398
1447
|
this.#restorePreservedElements(pantry);
|
|
1448
|
+
|
|
1449
|
+
this.#trigger(target, "htmx:before:settle", {task, newContent, settleTasks})
|
|
1450
|
+
|
|
1399
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
|
+
|
|
1467
|
+
for (const elt of newContent) {
|
|
1468
|
+
elt.classList?.remove?.("htmx-added")
|
|
1400
1469
|
this.process(elt);
|
|
1401
1470
|
this.#handleAutoFocus(elt);
|
|
1402
1471
|
}
|
|
1403
|
-
this.#handleScroll(
|
|
1472
|
+
this.#handleScroll(swapSpec, target);
|
|
1404
1473
|
}
|
|
1405
1474
|
|
|
1406
1475
|
#trigger(on, eventName, detail = {}, bubbles = true) {
|
|
@@ -1409,7 +1478,7 @@ var htmx = (() => {
|
|
|
1409
1478
|
}
|
|
1410
1479
|
on = this.#normalizeElement(on)
|
|
1411
1480
|
this.#triggerExtensions(on, eventName, detail);
|
|
1412
|
-
return this.trigger(on, eventName, detail, bubbles)
|
|
1481
|
+
return this.trigger(on, this.#maybeAdjustMetaCharacter(eventName), detail, bubbles)
|
|
1413
1482
|
}
|
|
1414
1483
|
|
|
1415
1484
|
#triggerExtensions(elt, eventName, detail = {}) {
|
|
@@ -1456,7 +1525,7 @@ var htmx = (() => {
|
|
|
1456
1525
|
}
|
|
1457
1526
|
|
|
1458
1527
|
takeClass(element, className, container = element.parentElement) {
|
|
1459
|
-
for (let elt of this
|
|
1528
|
+
for (let elt of this.#findAllExt(this.#normalizeElement(container), "." + className)) {
|
|
1460
1529
|
elt.classList.remove(className);
|
|
1461
1530
|
}
|
|
1462
1531
|
element.classList.add(className);
|
|
@@ -1567,7 +1636,7 @@ var htmx = (() => {
|
|
|
1567
1636
|
#restoreHistory(path) {
|
|
1568
1637
|
path = path || location.pathname + location.search;
|
|
1569
1638
|
if (this.#trigger(document, "htmx:before:restore:history", {path, cacheMiss: true})) {
|
|
1570
|
-
if (this.config.
|
|
1639
|
+
if (this.config.history === "reload") {
|
|
1571
1640
|
location.reload();
|
|
1572
1641
|
} else {
|
|
1573
1642
|
this.ajax('GET', path, {
|
|
@@ -1595,7 +1664,9 @@ var htmx = (() => {
|
|
|
1595
1664
|
if (!path || path === 'false' || path === false) return;
|
|
1596
1665
|
|
|
1597
1666
|
if (path === 'true') {
|
|
1598
|
-
|
|
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 : '');
|
|
1599
1670
|
}
|
|
1600
1671
|
|
|
1601
1672
|
let type = push ? 'push' : 'replace';
|
|
@@ -1631,15 +1702,18 @@ var htmx = (() => {
|
|
|
1631
1702
|
}
|
|
1632
1703
|
}
|
|
1633
1704
|
|
|
1634
|
-
#showIndicators(elt
|
|
1635
|
-
let
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1705
|
+
#showIndicators(elt) {
|
|
1706
|
+
let indicatorsSelector = this.#attributeValue(elt, "hx-indicator");
|
|
1707
|
+
let indicatorElements;
|
|
1708
|
+
if (!indicatorsSelector) {
|
|
1709
|
+
indicatorElements = [elt]
|
|
1710
|
+
} else {
|
|
1711
|
+
indicatorElements = this.#findAllExt(elt, indicatorsSelector, "hx-indicator");
|
|
1712
|
+
}
|
|
1713
|
+
for (const indicator of indicatorElements) {
|
|
1714
|
+
indicator._htmxReqCount ||= 0
|
|
1715
|
+
indicator._htmxReqCount++
|
|
1716
|
+
indicator.classList.add(this.config.requestClass)
|
|
1643
1717
|
}
|
|
1644
1718
|
return indicatorElements
|
|
1645
1719
|
}
|
|
@@ -1656,10 +1730,11 @@ var htmx = (() => {
|
|
|
1656
1730
|
}
|
|
1657
1731
|
}
|
|
1658
1732
|
|
|
1659
|
-
#disableElements(elt
|
|
1733
|
+
#disableElements(elt) {
|
|
1734
|
+
let disabledSelector = this.#attributeValue(elt, "hx-disable");
|
|
1660
1735
|
let disabledElements = []
|
|
1661
1736
|
if (disabledSelector) {
|
|
1662
|
-
disabledElements = this.#
|
|
1737
|
+
disabledElements = this.#findAllExt(elt, disabledSelector, "hx-disable");
|
|
1663
1738
|
for (let indicator of disabledElements) {
|
|
1664
1739
|
indicator._htmxDisableCount ||= 0
|
|
1665
1740
|
indicator._htmxDisableCount++
|
|
@@ -1681,10 +1756,13 @@ var htmx = (() => {
|
|
|
1681
1756
|
}
|
|
1682
1757
|
}
|
|
1683
1758
|
|
|
1684
|
-
#collectFormData(elt, form, submitter) {
|
|
1759
|
+
#collectFormData(elt, form, submitter, validate) {
|
|
1760
|
+
if (validate && form && !form.reportValidity()) return
|
|
1761
|
+
|
|
1685
1762
|
let formData = form ? new FormData(form) : new FormData()
|
|
1686
1763
|
let included = form ? new Set(form.elements) : new Set()
|
|
1687
1764
|
if (!form && elt.name) {
|
|
1765
|
+
if (validate && elt.reportValidity && !elt.reportValidity()) return
|
|
1688
1766
|
formData.append(elt.name, elt.value)
|
|
1689
1767
|
included.add(elt);
|
|
1690
1768
|
}
|
|
@@ -1694,8 +1772,8 @@ var htmx = (() => {
|
|
|
1694
1772
|
}
|
|
1695
1773
|
let includeSelector = this.#attributeValue(elt, "hx-include");
|
|
1696
1774
|
if (includeSelector) {
|
|
1697
|
-
let
|
|
1698
|
-
|
|
1775
|
+
for (let node of this.#findAllExt(elt, includeSelector)) {
|
|
1776
|
+
if (validate && node.reportValidity && !node.reportValidity()) return
|
|
1699
1777
|
this.#addInputValues(node, included, formData);
|
|
1700
1778
|
}
|
|
1701
1779
|
}
|
|
@@ -1732,37 +1810,42 @@ var htmx = (() => {
|
|
|
1732
1810
|
}
|
|
1733
1811
|
}
|
|
1734
1812
|
|
|
1735
|
-
#
|
|
1736
|
-
let
|
|
1737
|
-
if (
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
}
|
|
1745
|
-
});
|
|
1746
|
-
} else {
|
|
1747
|
-
// Synchronous path
|
|
1748
|
-
let obj = this.#parseConfig(hxValsValue);
|
|
1749
|
-
for (let key in obj) {
|
|
1750
|
-
body.append(key, obj[key])
|
|
1751
|
-
}
|
|
1813
|
+
#getAttributeObject(elt, attrName, callback) {
|
|
1814
|
+
let attrValue = this.#attributeValue(elt, attrName);
|
|
1815
|
+
if (!attrValue) return null;
|
|
1816
|
+
|
|
1817
|
+
let javascriptContent = this.#extractJavascriptContent(attrValue);
|
|
1818
|
+
if (javascriptContent) {
|
|
1819
|
+
// Wrap in braces if not already wrapped (for htmx 2.x compatibility)
|
|
1820
|
+
if (javascriptContent.indexOf('{') !== 0) {
|
|
1821
|
+
javascriptContent = '{' + javascriptContent + '}';
|
|
1752
1822
|
}
|
|
1823
|
+
// Return promise for async evaluation
|
|
1824
|
+
return this.#executeJavaScriptAsync(elt, {}, javascriptContent, true).then(obj => {
|
|
1825
|
+
callback(obj);
|
|
1826
|
+
});
|
|
1827
|
+
} else {
|
|
1828
|
+
// Synchronous path - return the parsed object directly
|
|
1829
|
+
callback(this.#parseConfig(attrValue));
|
|
1753
1830
|
}
|
|
1754
1831
|
}
|
|
1755
1832
|
|
|
1833
|
+
#handleHxVals(elt, body) {
|
|
1834
|
+
return this.#getAttributeObject(elt, "hx-vals", obj => {
|
|
1835
|
+
for (let key in obj) body.set(key, obj[key]);
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1756
1839
|
#stringHyperscriptStyleSelector(selector) {
|
|
1757
1840
|
let s = selector.trim();
|
|
1758
1841
|
return s.startsWith('<') && s.endsWith('/>') ? s.slice(1, -2) : s;
|
|
1759
1842
|
}
|
|
1760
1843
|
|
|
1761
|
-
#findAllExt(eltOrSelector, maybeSelector, global) {
|
|
1844
|
+
#findAllExt(eltOrSelector, maybeSelector, thisAttr, global) {
|
|
1762
1845
|
let selector = maybeSelector ?? eltOrSelector;
|
|
1763
1846
|
let elt = maybeSelector ? this.#normalizeElement(eltOrSelector) : document;
|
|
1764
1847
|
if (selector.startsWith('global ')) {
|
|
1765
|
-
return this.#findAllExt(elt, selector.slice(7), true);
|
|
1848
|
+
return this.#findAllExt(elt, selector.slice(7), thisAttr, true);
|
|
1766
1849
|
}
|
|
1767
1850
|
let parts = selector ? selector.replace(/<[^>]+\/>/g, m => m.replace(/,/g, '%2C'))
|
|
1768
1851
|
.split(',').map(p => p.replace(/%2C/g, ',')) : [];
|
|
@@ -1774,7 +1857,9 @@ var htmx = (() => {
|
|
|
1774
1857
|
if (selector.startsWith('closest ')) {
|
|
1775
1858
|
item = elt.closest(selector.slice(8))
|
|
1776
1859
|
} else if (selector.startsWith('find ')) {
|
|
1777
|
-
item =
|
|
1860
|
+
item = elt.querySelector(selector.slice(5))
|
|
1861
|
+
} else if (selector.startsWith('findAll ')) {
|
|
1862
|
+
result.push(...elt.querySelectorAll(selector.slice(8)))
|
|
1778
1863
|
} else if (selector === 'next' || selector === 'nextElementSibling') {
|
|
1779
1864
|
item = elt.nextElementSibling
|
|
1780
1865
|
} else if (selector.startsWith('next ')) {
|
|
@@ -1789,10 +1874,14 @@ var htmx = (() => {
|
|
|
1789
1874
|
item = window
|
|
1790
1875
|
} else if (selector === 'body') {
|
|
1791
1876
|
item = document.body
|
|
1792
|
-
} else if (selector === 'root') {
|
|
1793
|
-
item = this.#getRootNode(elt, !!global)
|
|
1794
1877
|
} else if (selector === 'host') {
|
|
1795
1878
|
item = (elt.getRootNode()).host
|
|
1879
|
+
} else if (selector === 'this') {
|
|
1880
|
+
if (thisAttr) {
|
|
1881
|
+
result.push(...this.#findThisElements(elt, thisAttr));
|
|
1882
|
+
continue;
|
|
1883
|
+
}
|
|
1884
|
+
item = elt
|
|
1796
1885
|
} else {
|
|
1797
1886
|
unprocessedParts.push(selector)
|
|
1798
1887
|
}
|
|
@@ -1808,7 +1897,7 @@ var htmx = (() => {
|
|
|
1808
1897
|
result.push(...rootNode.querySelectorAll(standardSelector))
|
|
1809
1898
|
}
|
|
1810
1899
|
|
|
1811
|
-
return result
|
|
1900
|
+
return [...new Set(result)]
|
|
1812
1901
|
}
|
|
1813
1902
|
|
|
1814
1903
|
#scanForwardQuery(start, match, global) {
|
|
@@ -1836,8 +1925,8 @@ var htmx = (() => {
|
|
|
1836
1925
|
}
|
|
1837
1926
|
}
|
|
1838
1927
|
|
|
1839
|
-
#findExt(eltOrSelector, selector) {
|
|
1840
|
-
return this.#findAllExt(eltOrSelector, selector)[0]
|
|
1928
|
+
#findExt(eltOrSelector, selector, thisAttr) {
|
|
1929
|
+
return this.#findAllExt(eltOrSelector, selector, thisAttr)[0]
|
|
1841
1930
|
}
|
|
1842
1931
|
|
|
1843
1932
|
#extractJavascriptContent(string) {
|
|
@@ -1937,26 +2026,35 @@ var htmx = (() => {
|
|
|
1937
2026
|
}
|
|
1938
2027
|
|
|
1939
2028
|
#findBestMatch(ctx, node, startPoint, endPoint) {
|
|
1940
|
-
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
|
|
1941
2031
|
let newSet = ctx.idMap.get(node), nodeMatchCount = newSet?.size || 0;
|
|
1942
2032
|
let cursor = startPoint;
|
|
1943
2033
|
while (cursor && cursor != endPoint) {
|
|
1944
2034
|
let oldSet = ctx.idMap.get(cursor);
|
|
1945
2035
|
if (this.#isSoftMatch(cursor, node)) {
|
|
2036
|
+
// Hard match: matching IDs found in both nodes
|
|
1946
2037
|
if (oldSet && newSet && [...oldSet].some(id => newSet.has(id))) return cursor;
|
|
1947
|
-
if (
|
|
1948
|
-
|
|
1949
|
-
|
|
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;
|
|
1950
2043
|
}
|
|
1951
2044
|
}
|
|
2045
|
+
// Stop if too many ID elements would be displaced
|
|
1952
2046
|
displaceMatchCount += oldSet?.size || 0;
|
|
1953
2047
|
if (displaceMatchCount > nodeMatchCount) break;
|
|
1954
|
-
|
|
1955
|
-
|
|
2048
|
+
// Look ahead: if next siblings match exactly, abort to let them match instead
|
|
2049
|
+
if (nextSibling && scanLimit > 0 && cursor.isEqualNode(nextSibling)) {
|
|
2050
|
+
siblingMatchCount++;
|
|
1956
2051
|
nextSibling = nextSibling.nextSibling;
|
|
1957
|
-
if (
|
|
2052
|
+
if (siblingMatchCount >= 2) return null;
|
|
1958
2053
|
}
|
|
2054
|
+
// Don't move elements containing focus
|
|
1959
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;
|
|
1960
2058
|
cursor = cursor.nextSibling;
|
|
1961
2059
|
}
|
|
1962
2060
|
return softMatch || null;
|
|
@@ -1992,8 +2090,8 @@ var htmx = (() => {
|
|
|
1992
2090
|
let type = newNode.nodeType;
|
|
1993
2091
|
|
|
1994
2092
|
if (type === 1) {
|
|
1995
|
-
|
|
1996
|
-
this.#copyAttributes(oldNode, newNode
|
|
2093
|
+
if (this.config.morphSkip && oldNode.matches?.(this.config.morphSkip)) return;
|
|
2094
|
+
this.#copyAttributes(oldNode, newNode);
|
|
1997
2095
|
if (oldNode instanceof HTMLTextAreaElement && oldNode.defaultValue != newNode.defaultValue) {
|
|
1998
2096
|
oldNode.value = newNode.value;
|
|
1999
2097
|
}
|
|
@@ -2002,10 +2100,13 @@ var htmx = (() => {
|
|
|
2002
2100
|
if ((type === 8 || type === 3) && oldNode.nodeValue !== newNode.nodeValue) {
|
|
2003
2101
|
oldNode.nodeValue = newNode.nodeValue;
|
|
2004
2102
|
}
|
|
2005
|
-
|
|
2103
|
+
|
|
2104
|
+
let skipChildren = this.config.morphSkipChildren && oldNode.matches?.(this.config.morphSkipChildren);
|
|
2105
|
+
if (!skipChildren && !oldNode.isEqualNode(newNode)) this.#morphChildren(ctx, oldNode, newNode);
|
|
2006
2106
|
}
|
|
2007
2107
|
|
|
2008
|
-
#copyAttributes(destination, source
|
|
2108
|
+
#copyAttributes(destination, source) {
|
|
2109
|
+
let attributesToIgnore = this.config.morphIgnore || [];
|
|
2009
2110
|
for (const attr of source.attributes) {
|
|
2010
2111
|
if (!attributesToIgnore.includes(attr.name) && destination.getAttribute(attr.name) !== attr.value) {
|
|
2011
2112
|
destination.setAttribute(attr.name, attr.value);
|
|
@@ -2075,7 +2176,7 @@ var htmx = (() => {
|
|
|
2075
2176
|
}
|
|
2076
2177
|
let statusValue = this.#attributeValue(ctx.sourceElement, "hx-status:" + pattern);
|
|
2077
2178
|
if (statusValue) {
|
|
2078
|
-
|
|
2179
|
+
this.#mergeConfig(statusValue, ctx);
|
|
2079
2180
|
return;
|
|
2080
2181
|
}
|
|
2081
2182
|
}
|
|
@@ -2116,21 +2217,22 @@ var htmx = (() => {
|
|
|
2116
2217
|
}
|
|
2117
2218
|
}
|
|
2118
2219
|
|
|
2119
|
-
#
|
|
2220
|
+
#startCSSTransitions(fragment, root) {
|
|
2120
2221
|
let idElements = root.querySelectorAll("[id]");
|
|
2121
2222
|
let existingElementsById = Object.fromEntries([...idElements].map(e => [e.id, e]));
|
|
2122
|
-
let newElementsWithIds =
|
|
2123
|
-
|
|
2223
|
+
let newElementsWithIds = fragment.querySelectorAll("[id]");
|
|
2224
|
+
let restoreTasks = []
|
|
2124
2225
|
for (let elt of newElementsWithIds) {
|
|
2125
2226
|
let existing = existingElementsById[elt.id];
|
|
2126
2227
|
if (existing?.tagName === elt.tagName) {
|
|
2127
2228
|
let clone = elt.cloneNode(false); // shallow clone node
|
|
2128
|
-
this.#copyAttributes(elt, existing
|
|
2129
|
-
|
|
2130
|
-
this.#copyAttributes(elt, clone
|
|
2229
|
+
this.#copyAttributes(elt, existing)
|
|
2230
|
+
restoreTasks.push(()=>{
|
|
2231
|
+
this.#copyAttributes(elt, clone)
|
|
2131
2232
|
})
|
|
2132
2233
|
}
|
|
2133
2234
|
}
|
|
2235
|
+
return restoreTasks;
|
|
2134
2236
|
}
|
|
2135
2237
|
|
|
2136
2238
|
#normalizeElement(cssOrElement) {
|