htmx.org 4.0.0-alpha4 → 4.0.0-alpha5
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-ws.js +33 -7
- package/dist/ext/hx-ws.min.js +1 -1
- package/dist/ext/hx-ws.min.js.map +1 -1
- package/dist/htmx.esm.js +214 -136
- package/dist/htmx.esm.min.js +1 -1
- package/dist/htmx.esm.min.js.map +1 -1
- package/dist/htmx.js +214 -136
- package/dist/htmx.min.js +1 -1
- package/dist/htmx.min.js.map +1 -1
- package/package.json +3 -2
package/dist/htmx.esm.js
CHANGED
|
@@ -80,7 +80,8 @@ 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)
|
|
84
85
|
};
|
|
85
86
|
document.addEventListener("DOMContentLoaded", () => {
|
|
86
87
|
this.#initHistoryHandling();
|
|
@@ -90,12 +91,11 @@ var htmx = (() => {
|
|
|
90
91
|
|
|
91
92
|
#initHtmxConfig() {
|
|
92
93
|
this.config = {
|
|
93
|
-
version: '4.0.0-
|
|
94
|
+
version: '4.0.0-alpha5',
|
|
94
95
|
logAll: false,
|
|
95
96
|
prefix: "",
|
|
96
|
-
transitions:
|
|
97
|
+
transitions: false,
|
|
97
98
|
history: true,
|
|
98
|
-
historyReload: false,
|
|
99
99
|
mode: 'same-origin',
|
|
100
100
|
defaultSwap: "innerHTML",
|
|
101
101
|
indicatorClass: "htmx-indicator",
|
|
@@ -115,19 +115,9 @@ var htmx = (() => {
|
|
|
115
115
|
noSwap: [204, 304],
|
|
116
116
|
implicitInheritance: false
|
|
117
117
|
}
|
|
118
|
-
let metaConfig = document.querySelector('meta[name="htmx
|
|
118
|
+
let metaConfig = document.querySelector('meta[name="htmx-config"]');
|
|
119
119
|
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
|
-
}
|
|
120
|
+
this.#mergeConfig(metaConfig.content, this.config);
|
|
131
121
|
}
|
|
132
122
|
this.#approvedExt = this.config.extensions;
|
|
133
123
|
}
|
|
@@ -181,48 +171,60 @@ var htmx = (() => {
|
|
|
181
171
|
style === 'append' ? 'beforeend' : style;
|
|
182
172
|
}
|
|
183
173
|
|
|
184
|
-
#
|
|
174
|
+
#findThisElements(elt, attrName) {
|
|
175
|
+
let result = [];
|
|
176
|
+
this.#attributeValue(elt, attrName, undefined, (val, elt) => {
|
|
177
|
+
if (val?.split(/\s*,\s*/).includes('this')) result.push(elt);
|
|
178
|
+
});
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
#attributeValue(elt, name, defaultVal, eltCollector) {
|
|
185
183
|
name = this.#prefix(name);
|
|
186
184
|
let appendName = name + this.#maybeAdjustMetaCharacter(":append");
|
|
187
185
|
let inheritName = name + (this.config.implicitInheritance ? "" : this.#maybeAdjustMetaCharacter(":inherited"));
|
|
188
186
|
let inheritAppendName = name + this.#maybeAdjustMetaCharacter(":inherited:append");
|
|
189
187
|
|
|
190
188
|
if (elt.hasAttribute(name)) {
|
|
191
|
-
|
|
189
|
+
let val = elt.getAttribute(name);
|
|
190
|
+
return eltCollector ? eltCollector(val, elt) : val;
|
|
192
191
|
}
|
|
193
192
|
|
|
194
193
|
if (elt.hasAttribute(inheritName)) {
|
|
195
|
-
|
|
194
|
+
let val = elt.getAttribute(inheritName);
|
|
195
|
+
return eltCollector ? eltCollector(val, elt) : val;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
if (elt.hasAttribute(appendName) || elt.hasAttribute(inheritAppendName)) {
|
|
199
199
|
let appendValue = elt.getAttribute(appendName) || elt.getAttribute(inheritAppendName);
|
|
200
200
|
let parent = elt.parentNode?.closest?.(`[${CSS.escape(inheritName)}],[${CSS.escape(inheritAppendName)}]`);
|
|
201
|
+
if (eltCollector) {
|
|
202
|
+
eltCollector(appendValue, elt);
|
|
203
|
+
}
|
|
201
204
|
if (parent) {
|
|
202
|
-
let inherited = this.#attributeValue(parent, name, undefined,
|
|
203
|
-
return
|
|
204
|
-
} else {
|
|
205
|
-
return returnElt ? elt : appendValue;
|
|
205
|
+
let inherited = this.#attributeValue(parent, name, undefined, eltCollector);
|
|
206
|
+
return inherited ? (inherited + "," + appendValue).replace(/[{}]/g, '') : appendValue;
|
|
206
207
|
}
|
|
208
|
+
return appendValue;
|
|
207
209
|
}
|
|
208
210
|
|
|
209
211
|
let parent = elt.parentNode?.closest?.(`[${CSS.escape(inheritName)}],[${CSS.escape(inheritAppendName)}]`);
|
|
210
212
|
if (parent) {
|
|
211
|
-
let val = this.#attributeValue(parent, name, undefined,
|
|
212
|
-
if (!
|
|
213
|
-
this.#triggerExtensions(elt, "htmx:after:implicitInheritance", {elt, parent})
|
|
213
|
+
let val = this.#attributeValue(parent, name, undefined, eltCollector);
|
|
214
|
+
if (!eltCollector && val && this.config.implicitInheritance) {
|
|
215
|
+
this.#triggerExtensions(elt, "htmx:after:implicitInheritance", {elt, name, parent})
|
|
214
216
|
}
|
|
215
217
|
return val;
|
|
216
218
|
}
|
|
217
|
-
return
|
|
219
|
+
return defaultVal;
|
|
218
220
|
}
|
|
219
221
|
|
|
220
222
|
#parseConfig(configString) {
|
|
221
223
|
if (configString[0] === '{') return JSON.parse(configString);
|
|
222
|
-
let configPattern = /([^\s
|
|
224
|
+
let configPattern = /(?:"([^"]+)"|([^\s,:]+))(?:\s*:\s*(?:"([^"]*)"|'([^']*)'|<([^>]+)\/>|([^\s,]+)))?(?=\s|,|$)/g;
|
|
223
225
|
return [...configString.matchAll(configPattern)].reduce((result, match) => {
|
|
224
|
-
let keyPath = match[1].split('.');
|
|
225
|
-
let value = (match[
|
|
226
|
+
let keyPath = (match[1] ?? match[2]).split('.');
|
|
227
|
+
let value = (match[3] ?? match[4] ?? match[5] ?? match[6] ?? 'true').trim();
|
|
226
228
|
if (value === 'true') value = true;
|
|
227
229
|
else if (value === 'false') value = false;
|
|
228
230
|
else if (/^\d+$/.test(value)) value = parseInt(value);
|
|
@@ -231,6 +233,19 @@ var htmx = (() => {
|
|
|
231
233
|
}, {});
|
|
232
234
|
}
|
|
233
235
|
|
|
236
|
+
#mergeConfig(configString, target) {
|
|
237
|
+
let parsed = this.#parseConfig(configString);
|
|
238
|
+
for (let key in parsed) {
|
|
239
|
+
let val = parsed[key];
|
|
240
|
+
if (val && typeof val === 'object' && !Array.isArray(val) && target[key]) {
|
|
241
|
+
Object.assign(target[key], val);
|
|
242
|
+
} else {
|
|
243
|
+
target[key] = val;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return target;
|
|
247
|
+
}
|
|
248
|
+
|
|
234
249
|
#parseTriggerSpecs(spec) {
|
|
235
250
|
return spec.split(',').map(s => {
|
|
236
251
|
let m = s.match(/^\s*(\S+\[[^\]]*\]|\S+)\s*(.*?)\s*$/);
|
|
@@ -305,44 +320,36 @@ var htmx = (() => {
|
|
|
305
320
|
status: "created",
|
|
306
321
|
select: this.#attributeValue(sourceElement, "hx-select"),
|
|
307
322
|
selectOOB: this.#attributeValue(sourceElement, "hx-select-oob"),
|
|
308
|
-
target: this.#
|
|
309
|
-
swap: this.#attributeValue(sourceElement, "hx-swap"
|
|
323
|
+
target: this.#attributeValue(sourceElement, "hx-target"),
|
|
324
|
+
swap: this.#attributeValue(sourceElement, "hx-swap") ?? this.config.defaultSwap,
|
|
310
325
|
push: this.#attributeValue(sourceElement, "hx-push-url"),
|
|
311
326
|
replace: this.#attributeValue(sourceElement, "hx-replace-url"),
|
|
312
327
|
transition: this.config.transitions,
|
|
313
328
|
confirm: this.#attributeValue(sourceElement, "hx-confirm"),
|
|
314
329
|
request: {
|
|
315
|
-
validate: "true" === this.#attributeValue(sourceElement, "hx-validate", sourceElement.matches('form') ? "true" : "false"),
|
|
330
|
+
validate: "true" === this.#attributeValue(sourceElement, "hx-validate", sourceElement.matches('form') && !sourceElement.noValidate && !sourceEvent.submitter?.formNoValidate ? "true" : "false"),
|
|
316
331
|
action: fullAction,
|
|
317
332
|
anchor,
|
|
318
333
|
method,
|
|
319
|
-
headers: this.#
|
|
334
|
+
headers: this.#createCoreHeaders(sourceElement),
|
|
320
335
|
abort: ac.abort.bind(ac),
|
|
321
336
|
credentials: "same-origin",
|
|
322
337
|
signal: ac.signal,
|
|
323
338
|
mode: this.config.mode
|
|
324
339
|
}
|
|
325
340
|
};
|
|
341
|
+
// Apply boost config overrides
|
|
342
|
+
if (sourceElement._htmx?.boosted) {
|
|
343
|
+
this.#mergeConfig(sourceElement._htmx.boosted, ctx);
|
|
344
|
+
}
|
|
345
|
+
ctx.target = this.#resolveTarget(sourceElement, ctx.target);
|
|
326
346
|
|
|
327
347
|
// Apply hx-config overrides
|
|
328
348
|
let configAttr = this.#attributeValue(sourceElement, "hx-config");
|
|
329
349
|
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
|
|
350
|
+
this.#mergeConfig(configAttr, ctx.request);
|
|
351
|
+
if (ctx.request.etag) {
|
|
352
|
+
(sourceElement._htmx ||= {}).etag ||= ctx.request.etag
|
|
346
353
|
}
|
|
347
354
|
}
|
|
348
355
|
if (sourceElement._htmx?.etag) {
|
|
@@ -351,30 +358,45 @@ var htmx = (() => {
|
|
|
351
358
|
return ctx;
|
|
352
359
|
}
|
|
353
360
|
|
|
354
|
-
#
|
|
361
|
+
#buildIdentifier(elt) {
|
|
362
|
+
return `${elt.tagName.toLowerCase()}${elt.id ? '#' + elt.id : ''}`;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
#createCoreHeaders(elt) {
|
|
355
366
|
let headers = {
|
|
356
367
|
"HX-Request": "true",
|
|
357
|
-
"HX-Source": elt
|
|
368
|
+
"HX-Source": this.#buildIdentifier(elt),
|
|
358
369
|
"HX-Current-URL": location.href,
|
|
359
370
|
"Accept": "text/html, text/event-stream"
|
|
360
371
|
};
|
|
361
372
|
if (this.#isBoosted(elt)) {
|
|
362
373
|
headers["HX-Boosted"] = "true"
|
|
363
374
|
}
|
|
364
|
-
let headersAttribute = this.#attributeValue(elt, "hx-headers");
|
|
365
|
-
if (headersAttribute) {
|
|
366
|
-
Object.assign(headers, this.#parseConfig(headersAttribute));
|
|
367
|
-
}
|
|
368
375
|
return headers;
|
|
369
376
|
}
|
|
370
377
|
|
|
378
|
+
#handleHxHeaders(elt, headers) {
|
|
379
|
+
let result = this.#getAttributeObject(elt, "hx-headers");
|
|
380
|
+
if (result) {
|
|
381
|
+
if (result instanceof Promise) {
|
|
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
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
371
395
|
#resolveTarget(elt, selector) {
|
|
372
396
|
if (selector instanceof Element) {
|
|
373
397
|
return selector;
|
|
374
|
-
} else if (selector === 'this') {
|
|
375
|
-
return this.#attributeValue(elt, "hx-target", undefined, true);
|
|
376
398
|
} else if (selector != null) {
|
|
377
|
-
return this
|
|
399
|
+
return this.#findExt(elt, selector, "hx-target");
|
|
378
400
|
} else if (this.#isBoosted(elt)) {
|
|
379
401
|
return document.body
|
|
380
402
|
} else {
|
|
@@ -397,7 +419,8 @@ var htmx = (() => {
|
|
|
397
419
|
|
|
398
420
|
// Build request body
|
|
399
421
|
let form = elt.form || elt.closest("form")
|
|
400
|
-
let body = this.#collectFormData(elt, form, evt.submitter)
|
|
422
|
+
let body = this.#collectFormData(elt, form, evt.submitter, ctx.request.validate)
|
|
423
|
+
if (!body) return // Validation failed
|
|
401
424
|
let valsResult = this.#handleHxVals(elt, body)
|
|
402
425
|
if (valsResult) await valsResult // Only await if it returned a promise
|
|
403
426
|
if (ctx.values) {
|
|
@@ -407,6 +430,16 @@ var htmx = (() => {
|
|
|
407
430
|
}
|
|
408
431
|
}
|
|
409
432
|
|
|
433
|
+
// Handle dynamic headers
|
|
434
|
+
let headersResult = this.#handleHxHeaders(elt, ctx.request.headers)
|
|
435
|
+
if (headersResult) await headersResult // Only await if it returned a promise
|
|
436
|
+
|
|
437
|
+
// Add HX-Request-Type and HX-Target headers
|
|
438
|
+
ctx.request.headers["HX-Request-Type"] = (ctx.target === document.body || ctx.select) ? "full" : "partial";
|
|
439
|
+
if (ctx.target) {
|
|
440
|
+
ctx.request.headers["HX-Target"] = this.#buildIdentifier(ctx.target);
|
|
441
|
+
}
|
|
442
|
+
|
|
410
443
|
// Setup event-dependent request details
|
|
411
444
|
Object.assign(ctx.request, {
|
|
412
445
|
form,
|
|
@@ -416,7 +449,6 @@ var htmx = (() => {
|
|
|
416
449
|
|
|
417
450
|
if (!this.#trigger(elt, "htmx:config:request", {ctx: ctx})) return
|
|
418
451
|
if (!this.#verbs.includes(ctx.request.method.toLowerCase())) return
|
|
419
|
-
if (ctx.request.validate && ctx.request.form && !ctx.request.form.reportValidity()) return
|
|
420
452
|
|
|
421
453
|
let javascriptContent = this.#extractJavascriptContent(ctx.request.action);
|
|
422
454
|
if (javascriptContent != null) {
|
|
@@ -452,10 +484,8 @@ var htmx = (() => {
|
|
|
452
484
|
ctx.status = "issuing"
|
|
453
485
|
this.#initTimeout(ctx);
|
|
454
486
|
|
|
455
|
-
let
|
|
456
|
-
let
|
|
457
|
-
let disableSelector = this.#attributeValue(elt, "hx-disable");
|
|
458
|
-
let disableElements = this.#disableElements(elt, disableSelector);
|
|
487
|
+
let indicators = this.#showIndicators(elt);
|
|
488
|
+
let disableElements = this.#disableElements(elt);
|
|
459
489
|
|
|
460
490
|
try {
|
|
461
491
|
// Handle confirmation
|
|
@@ -1001,8 +1031,9 @@ var htmx = (() => {
|
|
|
1001
1031
|
}
|
|
1002
1032
|
|
|
1003
1033
|
#maybeBoost(elt) {
|
|
1004
|
-
|
|
1005
|
-
|
|
1034
|
+
let boostValue = this.#attributeValue(elt, "hx-boost");
|
|
1035
|
+
if (boostValue && boostValue !== "false" && this.#shouldBoost(elt)) {
|
|
1036
|
+
elt._htmx = {eventHandler: this.#createHtmxEventHandler(elt), requests: [], boosted: boostValue}
|
|
1006
1037
|
elt.setAttribute('data-htmx-powered', 'true');
|
|
1007
1038
|
if (elt.matches('a') && !elt.hasAttribute("target")) {
|
|
1008
1039
|
elt.addEventListener('click', (click) => {
|
|
@@ -1187,13 +1218,11 @@ var htmx = (() => {
|
|
|
1187
1218
|
let type = templateElt.getAttribute('type');
|
|
1188
1219
|
|
|
1189
1220
|
if (type === 'partial') {
|
|
1190
|
-
let swapSpec = this.#parseSwapSpec(templateElt.getAttribute(this.#prefix('hx-swap')) || this.config.defaultSwap);
|
|
1191
|
-
|
|
1192
1221
|
tasks.push({
|
|
1193
1222
|
type: 'partial',
|
|
1194
1223
|
fragment: templateElt.content.cloneNode(true),
|
|
1195
1224
|
target: templateElt.getAttribute(this.#prefix('hx-target')),
|
|
1196
|
-
swapSpec,
|
|
1225
|
+
swapSpec: this.#parseSwapSpec(templateElt.getAttribute(this.#prefix('hx-swap')) || this.config.defaultSwap),
|
|
1197
1226
|
sourceElement: ctx.sourceElement
|
|
1198
1227
|
});
|
|
1199
1228
|
} else {
|
|
@@ -1210,18 +1239,18 @@ var htmx = (() => {
|
|
|
1210
1239
|
autofocus?.focus?.()
|
|
1211
1240
|
}
|
|
1212
1241
|
|
|
1213
|
-
#handleScroll(
|
|
1214
|
-
if (
|
|
1215
|
-
let
|
|
1216
|
-
if (
|
|
1217
|
-
|
|
1218
|
-
} else if (
|
|
1219
|
-
|
|
1242
|
+
#handleScroll(swapSpec, target) {
|
|
1243
|
+
if (swapSpec.scroll) {
|
|
1244
|
+
let scrollTarget = swapSpec.scrollTarget ? this.#findExt(swapSpec.scrollTarget) : target;
|
|
1245
|
+
if (swapSpec.scroll === 'top') {
|
|
1246
|
+
scrollTarget.scrollTop = 0;
|
|
1247
|
+
} else if (swapSpec.scroll === 'bottom'){
|
|
1248
|
+
scrollTarget.scrollTop = scrollTarget.scrollHeight;
|
|
1220
1249
|
}
|
|
1221
1250
|
}
|
|
1222
|
-
if (
|
|
1223
|
-
let
|
|
1224
|
-
|
|
1251
|
+
if (swapSpec.show) {
|
|
1252
|
+
let showTarget = swapSpec.showTarget ? this.#findExt(swapSpec.showTarget) : target;
|
|
1253
|
+
showTarget.scrollIntoView(swapSpec.show === 'top')
|
|
1225
1254
|
}
|
|
1226
1255
|
}
|
|
1227
1256
|
|
|
@@ -1270,25 +1299,35 @@ var htmx = (() => {
|
|
|
1270
1299
|
// TODO - can we remove this and just let the function complete?
|
|
1271
1300
|
if (tasks.length === 0) return;
|
|
1272
1301
|
|
|
1273
|
-
// Separate transition/nonTransition tasks
|
|
1274
|
-
let transitionTasks = tasks.filter(t => t.transition);
|
|
1275
|
-
let nonTransitionTasks = tasks.filter(t => !t.transition);
|
|
1276
|
-
|
|
1277
1302
|
if(!this.#trigger(document, "htmx:before:swap", {ctx, tasks})){
|
|
1278
1303
|
return
|
|
1279
1304
|
}
|
|
1280
1305
|
|
|
1281
|
-
// insert non-transition tasks immediately or with delay
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1306
|
+
// insert non-transition tasks immediately or with delay, collect transition tasks
|
|
1307
|
+
let transitionTasks = [];
|
|
1308
|
+
for (let task of tasks) {
|
|
1309
|
+
// OOB/partial tasks with swap delays should be non-transition (non-blocking)
|
|
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
|
+
}
|
|
1286
1320
|
this.#insertContent(task)
|
|
1321
|
+
} else {
|
|
1322
|
+
transitionTasks.push(task);
|
|
1287
1323
|
}
|
|
1288
1324
|
}
|
|
1289
1325
|
|
|
1290
1326
|
// insert transition tasks in the transition queue
|
|
1291
1327
|
if (transitionTasks.length > 0) {
|
|
1328
|
+
if (mainSwap?.transition && mainSwap?.swapSpec?.swap) {
|
|
1329
|
+
await this.timeout(mainSwap.swapSpec.swap);
|
|
1330
|
+
}
|
|
1292
1331
|
let tasksWrapper = ()=> {
|
|
1293
1332
|
for (let task of transitionTasks) {
|
|
1294
1333
|
this.#insertContent(task)
|
|
@@ -1316,7 +1355,7 @@ var htmx = (() => {
|
|
|
1316
1355
|
// Create main task if needed
|
|
1317
1356
|
let swapSpec = this.#parseSwapSpec(ctx.swap || this.config.defaultSwap);
|
|
1318
1357
|
// skip creating main swap if extracting partials resulted in empty response except for delete style
|
|
1319
|
-
if (swapSpec.style === 'delete' || /\S/.test(fragment.
|
|
1358
|
+
if (swapSpec.style === 'delete' || fragment.childElementCount > 0 || /\S/.test(fragment.textContent) || !partialTasks.length) {
|
|
1320
1359
|
if (ctx.select) {
|
|
1321
1360
|
let selected = fragment.querySelectorAll(ctx.select);
|
|
1322
1361
|
fragment = document.createDocumentFragment();
|
|
@@ -1343,8 +1382,10 @@ var htmx = (() => {
|
|
|
1343
1382
|
target = document.querySelector(target);
|
|
1344
1383
|
}
|
|
1345
1384
|
if (!target) return;
|
|
1385
|
+
if (typeof swapSpec === 'string') {
|
|
1386
|
+
swapSpec = this.#parseSwapSpec(swapSpec);
|
|
1387
|
+
}
|
|
1346
1388
|
if (swapSpec.strip && fragment.firstElementChild) {
|
|
1347
|
-
task.unstripped = fragment;
|
|
1348
1389
|
fragment = document.createDocumentFragment();
|
|
1349
1390
|
fragment.append(...(task.fragment.firstElementChild.content || task.fragment.firstElementChild).childNodes);
|
|
1350
1391
|
}
|
|
@@ -1390,17 +1431,24 @@ var htmx = (() => {
|
|
|
1390
1431
|
} else if (swapSpec.style === 'none') {
|
|
1391
1432
|
return;
|
|
1392
1433
|
} else {
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1434
|
+
let methods = this.#extMethods.get('handle_swap')
|
|
1435
|
+
let handled = false;
|
|
1436
|
+
for (const method of methods) {
|
|
1437
|
+
if (method(swapSpec.style, target, fragment)) {
|
|
1438
|
+
handled = true;
|
|
1439
|
+
break;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
if (!handled) {
|
|
1443
|
+
throw new Error(`Unknown swap style: ${swapSpec.style}`);
|
|
1444
|
+
}
|
|
1397
1445
|
}
|
|
1398
1446
|
this.#restorePreservedElements(pantry);
|
|
1399
1447
|
for (const elt of newContent) {
|
|
1400
1448
|
this.process(elt);
|
|
1401
1449
|
this.#handleAutoFocus(elt);
|
|
1402
1450
|
}
|
|
1403
|
-
this.#handleScroll(
|
|
1451
|
+
this.#handleScroll(swapSpec, target);
|
|
1404
1452
|
}
|
|
1405
1453
|
|
|
1406
1454
|
#trigger(on, eventName, detail = {}, bubbles = true) {
|
|
@@ -1456,7 +1504,7 @@ var htmx = (() => {
|
|
|
1456
1504
|
}
|
|
1457
1505
|
|
|
1458
1506
|
takeClass(element, className, container = element.parentElement) {
|
|
1459
|
-
for (let elt of this
|
|
1507
|
+
for (let elt of this.#findAllExt(this.#normalizeElement(container), "." + className)) {
|
|
1460
1508
|
elt.classList.remove(className);
|
|
1461
1509
|
}
|
|
1462
1510
|
element.classList.add(className);
|
|
@@ -1567,7 +1615,7 @@ var htmx = (() => {
|
|
|
1567
1615
|
#restoreHistory(path) {
|
|
1568
1616
|
path = path || location.pathname + location.search;
|
|
1569
1617
|
if (this.#trigger(document, "htmx:before:restore:history", {path, cacheMiss: true})) {
|
|
1570
|
-
if (this.config.
|
|
1618
|
+
if (this.config.history === "reload") {
|
|
1571
1619
|
location.reload();
|
|
1572
1620
|
} else {
|
|
1573
1621
|
this.ajax('GET', path, {
|
|
@@ -1631,15 +1679,18 @@ var htmx = (() => {
|
|
|
1631
1679
|
}
|
|
1632
1680
|
}
|
|
1633
1681
|
|
|
1634
|
-
#showIndicators(elt
|
|
1635
|
-
let
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1682
|
+
#showIndicators(elt) {
|
|
1683
|
+
let indicatorsSelector = this.#attributeValue(elt, "hx-indicator");
|
|
1684
|
+
let indicatorElements;
|
|
1685
|
+
if (!indicatorsSelector) {
|
|
1686
|
+
indicatorElements = [elt]
|
|
1687
|
+
} else {
|
|
1688
|
+
indicatorElements = this.#findAllExt(elt, indicatorsSelector, "hx-indicator");
|
|
1689
|
+
}
|
|
1690
|
+
for (const indicator of indicatorElements) {
|
|
1691
|
+
indicator._htmxReqCount ||= 0
|
|
1692
|
+
indicator._htmxReqCount++
|
|
1693
|
+
indicator.classList.add(this.config.requestClass)
|
|
1643
1694
|
}
|
|
1644
1695
|
return indicatorElements
|
|
1645
1696
|
}
|
|
@@ -1656,10 +1707,11 @@ var htmx = (() => {
|
|
|
1656
1707
|
}
|
|
1657
1708
|
}
|
|
1658
1709
|
|
|
1659
|
-
#disableElements(elt
|
|
1710
|
+
#disableElements(elt) {
|
|
1711
|
+
let disabledSelector = this.#attributeValue(elt, "hx-disable");
|
|
1660
1712
|
let disabledElements = []
|
|
1661
1713
|
if (disabledSelector) {
|
|
1662
|
-
disabledElements = this.#
|
|
1714
|
+
disabledElements = this.#findAllExt(elt, disabledSelector, "hx-disable");
|
|
1663
1715
|
for (let indicator of disabledElements) {
|
|
1664
1716
|
indicator._htmxDisableCount ||= 0
|
|
1665
1717
|
indicator._htmxDisableCount++
|
|
@@ -1681,10 +1733,13 @@ var htmx = (() => {
|
|
|
1681
1733
|
}
|
|
1682
1734
|
}
|
|
1683
1735
|
|
|
1684
|
-
#collectFormData(elt, form, submitter) {
|
|
1736
|
+
#collectFormData(elt, form, submitter, validate) {
|
|
1737
|
+
if (validate && form && !form.reportValidity()) return
|
|
1738
|
+
|
|
1685
1739
|
let formData = form ? new FormData(form) : new FormData()
|
|
1686
1740
|
let included = form ? new Set(form.elements) : new Set()
|
|
1687
1741
|
if (!form && elt.name) {
|
|
1742
|
+
if (validate && elt.reportValidity && !elt.reportValidity()) return
|
|
1688
1743
|
formData.append(elt.name, elt.value)
|
|
1689
1744
|
included.add(elt);
|
|
1690
1745
|
}
|
|
@@ -1694,8 +1749,8 @@ var htmx = (() => {
|
|
|
1694
1749
|
}
|
|
1695
1750
|
let includeSelector = this.#attributeValue(elt, "hx-include");
|
|
1696
1751
|
if (includeSelector) {
|
|
1697
|
-
let
|
|
1698
|
-
|
|
1752
|
+
for (let node of this.#findAllExt(elt, includeSelector)) {
|
|
1753
|
+
if (validate && node.reportValidity && !node.reportValidity()) return
|
|
1699
1754
|
this.#addInputValues(node, included, formData);
|
|
1700
1755
|
}
|
|
1701
1756
|
}
|
|
@@ -1732,22 +1787,36 @@ var htmx = (() => {
|
|
|
1732
1787
|
}
|
|
1733
1788
|
}
|
|
1734
1789
|
|
|
1790
|
+
#getAttributeObject(elt, attrName) {
|
|
1791
|
+
let attrValue = this.#attributeValue(elt, attrName);
|
|
1792
|
+
if (!attrValue) return null;
|
|
1793
|
+
|
|
1794
|
+
let javascriptContent = this.#extractJavascriptContent(attrValue);
|
|
1795
|
+
if (javascriptContent) {
|
|
1796
|
+
// Wrap in braces if not already wrapped (for htmx 2.x compatibility)
|
|
1797
|
+
if (javascriptContent.indexOf('{') !== 0) {
|
|
1798
|
+
javascriptContent = '{' + javascriptContent + '}';
|
|
1799
|
+
}
|
|
1800
|
+
// Return promise for async evaluation
|
|
1801
|
+
return this.#executeJavaScriptAsync(elt, {}, javascriptContent, true);
|
|
1802
|
+
} else {
|
|
1803
|
+
// Synchronous path - return the parsed object directly
|
|
1804
|
+
return this.#parseConfig(attrValue);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1735
1808
|
#handleHxVals(elt, body) {
|
|
1736
|
-
let
|
|
1737
|
-
if (
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
// Return promise for async evaluation
|
|
1741
|
-
return this.#executeJavaScriptAsync(elt, {}, javascriptContent, true).then(obj => {
|
|
1809
|
+
let result = this.#getAttributeObject(elt, "hx-vals");
|
|
1810
|
+
if (result) {
|
|
1811
|
+
if (result instanceof Promise) {
|
|
1812
|
+
return result.then(obj => {
|
|
1742
1813
|
for (let key in obj) {
|
|
1743
|
-
body.
|
|
1814
|
+
body.set(key, obj[key])
|
|
1744
1815
|
}
|
|
1745
1816
|
});
|
|
1746
1817
|
} else {
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
for (let key in obj) {
|
|
1750
|
-
body.append(key, obj[key])
|
|
1818
|
+
for (let key in result) {
|
|
1819
|
+
body.set(key, result[key])
|
|
1751
1820
|
}
|
|
1752
1821
|
}
|
|
1753
1822
|
}
|
|
@@ -1758,11 +1827,11 @@ var htmx = (() => {
|
|
|
1758
1827
|
return s.startsWith('<') && s.endsWith('/>') ? s.slice(1, -2) : s;
|
|
1759
1828
|
}
|
|
1760
1829
|
|
|
1761
|
-
#findAllExt(eltOrSelector, maybeSelector, global) {
|
|
1830
|
+
#findAllExt(eltOrSelector, maybeSelector, thisAttr, global) {
|
|
1762
1831
|
let selector = maybeSelector ?? eltOrSelector;
|
|
1763
1832
|
let elt = maybeSelector ? this.#normalizeElement(eltOrSelector) : document;
|
|
1764
1833
|
if (selector.startsWith('global ')) {
|
|
1765
|
-
return this.#findAllExt(elt, selector.slice(7), true);
|
|
1834
|
+
return this.#findAllExt(elt, selector.slice(7), thisAttr, true);
|
|
1766
1835
|
}
|
|
1767
1836
|
let parts = selector ? selector.replace(/<[^>]+\/>/g, m => m.replace(/,/g, '%2C'))
|
|
1768
1837
|
.split(',').map(p => p.replace(/%2C/g, ',')) : [];
|
|
@@ -1774,7 +1843,9 @@ var htmx = (() => {
|
|
|
1774
1843
|
if (selector.startsWith('closest ')) {
|
|
1775
1844
|
item = elt.closest(selector.slice(8))
|
|
1776
1845
|
} else if (selector.startsWith('find ')) {
|
|
1777
|
-
item =
|
|
1846
|
+
item = elt.querySelector(selector.slice(5))
|
|
1847
|
+
} else if (selector.startsWith('findAll ')) {
|
|
1848
|
+
result.push(...elt.querySelectorAll(selector.slice(8)))
|
|
1778
1849
|
} else if (selector === 'next' || selector === 'nextElementSibling') {
|
|
1779
1850
|
item = elt.nextElementSibling
|
|
1780
1851
|
} else if (selector.startsWith('next ')) {
|
|
@@ -1789,10 +1860,14 @@ var htmx = (() => {
|
|
|
1789
1860
|
item = window
|
|
1790
1861
|
} else if (selector === 'body') {
|
|
1791
1862
|
item = document.body
|
|
1792
|
-
} else if (selector === 'root') {
|
|
1793
|
-
item = this.#getRootNode(elt, !!global)
|
|
1794
1863
|
} else if (selector === 'host') {
|
|
1795
1864
|
item = (elt.getRootNode()).host
|
|
1865
|
+
} else if (selector === 'this') {
|
|
1866
|
+
if (thisAttr) {
|
|
1867
|
+
result.push(...this.#findThisElements(elt, thisAttr));
|
|
1868
|
+
continue;
|
|
1869
|
+
}
|
|
1870
|
+
item = elt
|
|
1796
1871
|
} else {
|
|
1797
1872
|
unprocessedParts.push(selector)
|
|
1798
1873
|
}
|
|
@@ -1808,7 +1883,7 @@ var htmx = (() => {
|
|
|
1808
1883
|
result.push(...rootNode.querySelectorAll(standardSelector))
|
|
1809
1884
|
}
|
|
1810
1885
|
|
|
1811
|
-
return result
|
|
1886
|
+
return [...new Set(result)]
|
|
1812
1887
|
}
|
|
1813
1888
|
|
|
1814
1889
|
#scanForwardQuery(start, match, global) {
|
|
@@ -1836,8 +1911,8 @@ var htmx = (() => {
|
|
|
1836
1911
|
}
|
|
1837
1912
|
}
|
|
1838
1913
|
|
|
1839
|
-
#findExt(eltOrSelector, selector) {
|
|
1840
|
-
return this.#findAllExt(eltOrSelector, selector)[0]
|
|
1914
|
+
#findExt(eltOrSelector, selector, thisAttr) {
|
|
1915
|
+
return this.#findAllExt(eltOrSelector, selector, thisAttr)[0]
|
|
1841
1916
|
}
|
|
1842
1917
|
|
|
1843
1918
|
#extractJavascriptContent(string) {
|
|
@@ -1992,8 +2067,8 @@ var htmx = (() => {
|
|
|
1992
2067
|
let type = newNode.nodeType;
|
|
1993
2068
|
|
|
1994
2069
|
if (type === 1) {
|
|
1995
|
-
|
|
1996
|
-
this.#copyAttributes(oldNode, newNode
|
|
2070
|
+
if (this.config.morphSkip && oldNode.matches?.(this.config.morphSkip)) return;
|
|
2071
|
+
this.#copyAttributes(oldNode, newNode);
|
|
1997
2072
|
if (oldNode instanceof HTMLTextAreaElement && oldNode.defaultValue != newNode.defaultValue) {
|
|
1998
2073
|
oldNode.value = newNode.value;
|
|
1999
2074
|
}
|
|
@@ -2002,10 +2077,13 @@ var htmx = (() => {
|
|
|
2002
2077
|
if ((type === 8 || type === 3) && oldNode.nodeValue !== newNode.nodeValue) {
|
|
2003
2078
|
oldNode.nodeValue = newNode.nodeValue;
|
|
2004
2079
|
}
|
|
2005
|
-
|
|
2080
|
+
|
|
2081
|
+
let skipChildren = this.config.morphSkipChildren && oldNode.matches?.(this.config.morphSkipChildren);
|
|
2082
|
+
if (!skipChildren && !oldNode.isEqualNode(newNode)) this.#morphChildren(ctx, oldNode, newNode);
|
|
2006
2083
|
}
|
|
2007
2084
|
|
|
2008
|
-
#copyAttributes(destination, source
|
|
2085
|
+
#copyAttributes(destination, source) {
|
|
2086
|
+
let attributesToIgnore = this.config.morphIgnore || [];
|
|
2009
2087
|
for (const attr of source.attributes) {
|
|
2010
2088
|
if (!attributesToIgnore.includes(attr.name) && destination.getAttribute(attr.name) !== attr.value) {
|
|
2011
2089
|
destination.setAttribute(attr.name, attr.value);
|
|
@@ -2075,7 +2153,7 @@ var htmx = (() => {
|
|
|
2075
2153
|
}
|
|
2076
2154
|
let statusValue = this.#attributeValue(ctx.sourceElement, "hx-status:" + pattern);
|
|
2077
2155
|
if (statusValue) {
|
|
2078
|
-
|
|
2156
|
+
this.#mergeConfig(statusValue, ctx);
|
|
2079
2157
|
return;
|
|
2080
2158
|
}
|
|
2081
2159
|
}
|