htmx.org 4.0.0-alpha1 → 4.0.0-alpha3
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/README.md +1 -1
- package/dist/editors/jetbrains/htmx.svg +46 -0
- package/dist/editors/jetbrains/htmx_dark.svg +46 -0
- package/dist/ext/hx-live.js +624 -0
- package/dist/ext/hx-live.min.js +1 -0
- package/dist/ext/hx-live.min.js.map +1 -0
- package/dist/ext/hx-optimistic.js +81 -0
- package/dist/ext/hx-optimistic.min.js +1 -0
- package/dist/ext/hx-optimistic.min.js.map +1 -0
- package/dist/ext/hx-preload.js +83 -0
- package/dist/ext/hx-preload.min.js +1 -0
- package/dist/ext/hx-preload.min.js.map +1 -0
- package/dist/htmx.esm.js +368 -359
- package/dist/htmx.esm.min.js +1 -1
- package/dist/htmx.esm.min.js.map +1 -0
- package/dist/htmx.js +362 -354
- package/dist/htmx.min.js +1 -1
- package/dist/htmx.min.js.map +1 -0
- package/package.json +16 -9
- package/dist/htmx.esm.js.br +0 -0
- package/dist/htmx.esm.min.js.br +0 -0
- package/dist/htmx.js.br +0 -0
- package/dist/htmx.min.js.br +0 -0
- /package/{src → dist}/editors/jetbrains/htmx.web-types.json +0 -0
package/dist/htmx.esm.js
CHANGED
|
@@ -59,7 +59,8 @@ var htmx = (() => {
|
|
|
59
59
|
class Htmx {
|
|
60
60
|
|
|
61
61
|
#extMethods = new Map();
|
|
62
|
-
#approvedExt =
|
|
62
|
+
#approvedExt = '';
|
|
63
|
+
#registeredExt = new Set();
|
|
63
64
|
#internalAPI;
|
|
64
65
|
#actionSelector
|
|
65
66
|
#boostSelector = "a,form";
|
|
@@ -72,7 +73,7 @@ var htmx = (() => {
|
|
|
72
73
|
this.#initHtmxConfig();
|
|
73
74
|
this.#initRequestIndicatorCss();
|
|
74
75
|
this.#actionSelector = `[${this.#prefix("hx-action")}],[${this.#prefix("hx-get")}],[${this.#prefix("hx-post")}],[${this.#prefix("hx-put")}],[${this.#prefix("hx-patch")}],[${this.#prefix("hx-delete")}]`;
|
|
75
|
-
this.#hxOnQuery = new XPathEvaluator().createExpression(`.//*[@*[ starts-with(name(), "${this.#prefix("hx-on
|
|
76
|
+
this.#hxOnQuery = new XPathEvaluator().createExpression(`.//*[@*[ starts-with(name(), "${this.#prefix("hx-on")}")]]`);
|
|
76
77
|
this.#internalAPI = {
|
|
77
78
|
attributeValue: this.#attributeValue.bind(this),
|
|
78
79
|
parseTriggerSpecs: this.#parseTriggerSpecs.bind(this),
|
|
@@ -89,6 +90,7 @@ var htmx = (() => {
|
|
|
89
90
|
|
|
90
91
|
#initHtmxConfig() {
|
|
91
92
|
this.config = {
|
|
93
|
+
version: '4.0.0-alpha3',
|
|
92
94
|
logAll: false,
|
|
93
95
|
prefix: "",
|
|
94
96
|
transitions: true,
|
|
@@ -101,7 +103,7 @@ var htmx = (() => {
|
|
|
101
103
|
includeIndicatorCSS: true,
|
|
102
104
|
defaultTimeout: 60000, /* 60 second default timeout */
|
|
103
105
|
extensions: '',
|
|
104
|
-
|
|
106
|
+
sse: {
|
|
105
107
|
mode: 'once',
|
|
106
108
|
maxRetries: Infinity,
|
|
107
109
|
initialDelay: 500,
|
|
@@ -109,11 +111,13 @@ var htmx = (() => {
|
|
|
109
111
|
pauseHidden: false
|
|
110
112
|
},
|
|
111
113
|
morphIgnore: ["data-htmx-powered"],
|
|
112
|
-
noSwap: [204],
|
|
114
|
+
noSwap: [204, 304],
|
|
115
|
+
implicitInheritance: false
|
|
113
116
|
}
|
|
114
117
|
let metaConfig = document.querySelector('meta[name="htmx:config"]');
|
|
115
118
|
if (metaConfig) {
|
|
116
|
-
let
|
|
119
|
+
let content = metaConfig.content;
|
|
120
|
+
let overrides = this.#parseConfig(content);
|
|
117
121
|
// Deep merge nested config objects
|
|
118
122
|
for (let key in overrides) {
|
|
119
123
|
let val = overrides[key];
|
|
@@ -124,7 +128,7 @@ var htmx = (() => {
|
|
|
124
128
|
}
|
|
125
129
|
}
|
|
126
130
|
}
|
|
127
|
-
this.#approvedExt =
|
|
131
|
+
this.#approvedExt = this.config.extensions;
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
#initRequestIndicatorCss() {
|
|
@@ -144,7 +148,9 @@ var htmx = (() => {
|
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
defineExtension(name, extension) {
|
|
147
|
-
if (!this.#approvedExt.
|
|
151
|
+
if (this.#approvedExt && !this.#approvedExt.split(/,\s*/).includes(name)) return false;
|
|
152
|
+
if (this.#registeredExt.has(name)) return false;
|
|
153
|
+
this.#registeredExt.add(name);
|
|
148
154
|
if (extension.init) extension.init(this.#internalAPI);
|
|
149
155
|
Object.entries(extension).forEach(([key, value]) => {
|
|
150
156
|
if(!this.#extMethods.get(key)?.push(value)) this.#extMethods.set(key, [value]);
|
|
@@ -174,102 +180,78 @@ var htmx = (() => {
|
|
|
174
180
|
style === 'append' ? 'beforeend' : style;
|
|
175
181
|
}
|
|
176
182
|
|
|
177
|
-
#attributeValue(elt, name, defaultVal) {
|
|
183
|
+
#attributeValue(elt, name, defaultVal, returnElt) {
|
|
178
184
|
name = this.#prefix(name);
|
|
179
|
-
let appendName = name + ":append";
|
|
180
|
-
let inheritName = name + ":inherited";
|
|
181
|
-
let inheritAppendName = name + ":inherited:append";
|
|
185
|
+
let appendName = name + this.#maybeAdjustMetaCharacter(":append");
|
|
186
|
+
let inheritName = name + (this.config.implicitInheritance ? "" : this.#maybeAdjustMetaCharacter(":inherited"));
|
|
187
|
+
let inheritAppendName = name + this.#maybeAdjustMetaCharacter(":inherited:append");
|
|
182
188
|
|
|
183
|
-
if (elt.hasAttribute(name)
|
|
184
|
-
return elt
|
|
189
|
+
if (elt.hasAttribute(name)) {
|
|
190
|
+
return returnElt ? elt : elt.getAttribute(name);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (elt.hasAttribute(inheritName)) {
|
|
194
|
+
return returnElt ? elt : elt.getAttribute(inheritName);
|
|
185
195
|
}
|
|
186
196
|
|
|
187
197
|
if (elt.hasAttribute(appendName) || elt.hasAttribute(inheritAppendName)) {
|
|
188
198
|
let appendValue = elt.getAttribute(appendName) || elt.getAttribute(inheritAppendName);
|
|
189
199
|
let parent = elt.parentNode?.closest?.(`[${CSS.escape(inheritName)}],[${CSS.escape(inheritAppendName)}]`);
|
|
190
200
|
if (parent) {
|
|
191
|
-
let
|
|
192
|
-
return
|
|
201
|
+
let inherited = this.#attributeValue(parent, name, undefined, returnElt);
|
|
202
|
+
return returnElt ? inherited : (inherited ? inherited + "," + appendValue : appendValue);
|
|
203
|
+
} else {
|
|
204
|
+
return returnElt ? elt : appendValue;
|
|
193
205
|
}
|
|
194
|
-
return appendValue;
|
|
195
206
|
}
|
|
196
207
|
|
|
197
208
|
let parent = elt.parentNode?.closest?.(`[${CSS.escape(inheritName)}],[${CSS.escape(inheritAppendName)}]`);
|
|
198
209
|
if (parent) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
#tokenize(str) {
|
|
205
|
-
let tokens = [], i = 0;
|
|
206
|
-
while (i < str.length) {
|
|
207
|
-
let c = str[i];
|
|
208
|
-
if (c === '"' || c === "'") {
|
|
209
|
-
let q = c, s = c;
|
|
210
|
-
i++;
|
|
211
|
-
while (i < str.length) {
|
|
212
|
-
c = str[i];
|
|
213
|
-
s += c;
|
|
214
|
-
i++;
|
|
215
|
-
if (c === '\\' && i < str.length) {
|
|
216
|
-
s += str[i];
|
|
217
|
-
i++;
|
|
218
|
-
} else if (c === q) break;
|
|
219
|
-
}
|
|
220
|
-
tokens.push(s);
|
|
221
|
-
} else if (/\s/.test(c)) {
|
|
222
|
-
while (i < str.length && /\s/.test(str[i])) i++;
|
|
223
|
-
} else if (c === ':' || c === ',') {
|
|
224
|
-
tokens.push(c);
|
|
225
|
-
i++;
|
|
226
|
-
} else {
|
|
227
|
-
let t = '';
|
|
228
|
-
while (i < str.length && !/[\s"':,]/.test(str[i])) t += str[i++];
|
|
229
|
-
tokens.push(t);
|
|
210
|
+
let val = this.#attributeValue(parent, name, undefined, returnElt);
|
|
211
|
+
if (!returnElt && val && this.config.implicitInheritance) {
|
|
212
|
+
this.#triggerExtensions(elt, "htmx:after:implicitInheritance", {elt, parent})
|
|
230
213
|
}
|
|
214
|
+
return val;
|
|
231
215
|
}
|
|
232
|
-
return
|
|
216
|
+
return returnElt ? elt : defaultVal;
|
|
233
217
|
}
|
|
234
218
|
|
|
235
|
-
#
|
|
236
|
-
|
|
237
|
-
let
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
let
|
|
241
|
-
if (
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
throw "unterminated:" + token;
|
|
249
|
-
}
|
|
250
|
-
currentSpec = {name: token};
|
|
251
|
-
specs.push(currentSpec);
|
|
252
|
-
} else if (tokens[i + 1] === ":") {
|
|
253
|
-
currentSpec[token] = tokens[i += 2];
|
|
254
|
-
} else {
|
|
255
|
-
currentSpec[token] = true;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
219
|
+
#parseConfig(configString) {
|
|
220
|
+
if (configString[0] === '{') return JSON.parse(configString);
|
|
221
|
+
let configPattern = /([^\s,]+?)(?:\s*:\s*(?:"([^"]*)"|'([^']*)'|<([^>]+)\/>|([^\s,]+)))?(?=\s|,|$)/g;
|
|
222
|
+
return [...configString.matchAll(configPattern)].reduce((result, match) => {
|
|
223
|
+
let keyPath = match[1].split('.');
|
|
224
|
+
let value = (match[2] ?? match[3] ?? match[4] ?? match[5] ?? 'true').trim();
|
|
225
|
+
if (value === 'true') value = true;
|
|
226
|
+
else if (value === 'false') value = false;
|
|
227
|
+
else if (/^\d+$/.test(value)) value = parseInt(value);
|
|
228
|
+
keyPath.slice(0, -1).reduce((obj, key) => obj[key] ??= {}, result)[keyPath.at(-1)] = value;
|
|
229
|
+
return result;
|
|
230
|
+
}, {});
|
|
231
|
+
}
|
|
258
232
|
|
|
259
|
-
|
|
233
|
+
#parseTriggerSpecs(spec) {
|
|
234
|
+
return spec.split(',').map(s => {
|
|
235
|
+
let m = s.match(/^\s*(\S+\[[^\]]*\]|\S+)\s*(.*?)\s*$/);
|
|
236
|
+
if (!m || !m[1]) return null;
|
|
237
|
+
if (m[1].includes('[') && !m[1].includes(']')) throw "unterminated:" + m[1];
|
|
238
|
+
let result = m[2] ? this.#parseConfig(m[2]) : {};
|
|
239
|
+
result.name = m[1];
|
|
240
|
+
return result;
|
|
241
|
+
}).filter(s => s);
|
|
260
242
|
}
|
|
261
243
|
|
|
262
244
|
#determineMethodAndAction(elt, evt) {
|
|
263
245
|
if (this.#isBoosted(elt)) {
|
|
264
246
|
return this.#boostedMethodAndAction(elt, evt)
|
|
265
247
|
} else {
|
|
266
|
-
let method = this.#attributeValue(elt, "hx-method") || "
|
|
248
|
+
let method = this.#attributeValue(elt, "hx-method") || "GET"
|
|
267
249
|
let action = this.#attributeValue(elt, "hx-action");
|
|
268
250
|
if (!action) {
|
|
269
251
|
for (let verb of this.#verbs) {
|
|
270
|
-
let
|
|
271
|
-
if (
|
|
272
|
-
action =
|
|
252
|
+
let verbAction = this.#attributeValue(elt, "hx-" + verb);
|
|
253
|
+
if (verbAction) {
|
|
254
|
+
action = verbAction;
|
|
273
255
|
method = verb;
|
|
274
256
|
break;
|
|
275
257
|
}
|
|
@@ -295,7 +277,6 @@ var htmx = (() => {
|
|
|
295
277
|
elt._htmx = {eventHandler: this.#createHtmxEventHandler(elt)}
|
|
296
278
|
elt.setAttribute('data-htmx-powered', 'true');
|
|
297
279
|
this.#initializeTriggers(elt);
|
|
298
|
-
this.#initializeStreamConfig(elt);
|
|
299
280
|
this.#initializeAbortListener(elt)
|
|
300
281
|
this.#trigger(elt, "htmx:after:init", {}, true)
|
|
301
282
|
this.#trigger(elt, "load", {}, false)
|
|
@@ -321,11 +302,12 @@ var htmx = (() => {
|
|
|
321
302
|
status: "created",
|
|
322
303
|
select: this.#attributeValue(sourceElement, "hx-select"),
|
|
323
304
|
selectOOB: this.#attributeValue(sourceElement, "hx-select-oob"),
|
|
324
|
-
target: this.#attributeValue(sourceElement, "hx-target"),
|
|
305
|
+
target: this.#resolveTarget(sourceElement, this.#attributeValue(sourceElement, "hx-target")),
|
|
325
306
|
swap: this.#attributeValue(sourceElement, "hx-swap", this.config.defaultSwap),
|
|
326
307
|
push: this.#attributeValue(sourceElement, "hx-push-url"),
|
|
327
308
|
replace: this.#attributeValue(sourceElement, "hx-replace-url"),
|
|
328
309
|
transition: this.config.transitions,
|
|
310
|
+
confirm: this.#attributeValue(sourceElement, "hx-confirm"),
|
|
329
311
|
request: {
|
|
330
312
|
validate: "true" === this.#attributeValue(sourceElement, "hx-validate", sourceElement.matches('form') ? "true" : "false"),
|
|
331
313
|
action,
|
|
@@ -337,28 +319,35 @@ var htmx = (() => {
|
|
|
337
319
|
// Apply hx-config overrides
|
|
338
320
|
let configAttr = this.#attributeValue(sourceElement, "hx-config");
|
|
339
321
|
if (configAttr) {
|
|
340
|
-
let configOverrides =
|
|
341
|
-
let
|
|
322
|
+
let configOverrides = this.#parseConfig(configAttr);
|
|
323
|
+
let req = ctx.request;
|
|
342
324
|
for (let key in configOverrides) {
|
|
343
325
|
if (key.startsWith('+')) {
|
|
344
326
|
let actualKey = key.substring(1);
|
|
345
|
-
if (
|
|
346
|
-
Object.assign(
|
|
327
|
+
if (req[actualKey] && typeof req[actualKey] === 'object') {
|
|
328
|
+
Object.assign(req[actualKey], configOverrides[key]);
|
|
347
329
|
} else {
|
|
348
|
-
|
|
330
|
+
req[actualKey] = configOverrides[key];
|
|
349
331
|
}
|
|
350
332
|
} else {
|
|
351
|
-
|
|
333
|
+
req[key] = configOverrides[key];
|
|
352
334
|
}
|
|
353
335
|
}
|
|
336
|
+
if (req.etag) {
|
|
337
|
+
(sourceElement._htmx ||= {}).etag ||= req.etag
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (sourceElement._htmx?.etag) {
|
|
341
|
+
ctx.request.headers["If-none-match"] = sourceElement._htmx.etag
|
|
354
342
|
}
|
|
355
|
-
|
|
356
343
|
return ctx;
|
|
357
344
|
}
|
|
358
345
|
|
|
359
346
|
#determineHeaders(elt) {
|
|
360
347
|
let headers = {
|
|
361
348
|
"HX-Request": "true",
|
|
349
|
+
"HX-Source": elt.id || elt.name,
|
|
350
|
+
"HX-Current-URL": location.href,
|
|
362
351
|
"Accept": "text/html, text/event-stream"
|
|
363
352
|
};
|
|
364
353
|
if (this.#isBoosted(elt)) {
|
|
@@ -366,7 +355,7 @@ var htmx = (() => {
|
|
|
366
355
|
}
|
|
367
356
|
let headersAttribute = this.#attributeValue(elt, "hx-headers");
|
|
368
357
|
if (headersAttribute) {
|
|
369
|
-
Object.assign(headers,
|
|
358
|
+
Object.assign(headers, this.#parseConfig(headersAttribute));
|
|
370
359
|
}
|
|
371
360
|
return headers;
|
|
372
361
|
}
|
|
@@ -375,11 +364,7 @@ var htmx = (() => {
|
|
|
375
364
|
if (selector instanceof Element) {
|
|
376
365
|
return selector;
|
|
377
366
|
} else if (selector === 'this') {
|
|
378
|
-
|
|
379
|
-
return elt;
|
|
380
|
-
} else {
|
|
381
|
-
return elt.closest(`[${this.#prefix("hx-target")}\\:inherited='this']`)
|
|
382
|
-
}
|
|
367
|
+
return this.#attributeValue(elt, "hx-target", undefined, true);
|
|
383
368
|
} else if (selector != null) {
|
|
384
369
|
return this.find(elt, selector);
|
|
385
370
|
} else if (this.#isBoosted(elt)) {
|
|
@@ -390,10 +375,10 @@ var htmx = (() => {
|
|
|
390
375
|
}
|
|
391
376
|
|
|
392
377
|
#isBoosted(elt) {
|
|
393
|
-
return elt
|
|
378
|
+
return elt?._htmx?.boosted;
|
|
394
379
|
}
|
|
395
380
|
|
|
396
|
-
async
|
|
381
|
+
async #handleTriggerEvent(ctx) {
|
|
397
382
|
let elt = ctx.sourceElement
|
|
398
383
|
let evt = ctx.sourceEvent
|
|
399
384
|
if (!elt.isConnected) return
|
|
@@ -402,13 +387,11 @@ var htmx = (() => {
|
|
|
402
387
|
|
|
403
388
|
if (this.#shouldCancel(evt)) evt.preventDefault()
|
|
404
389
|
|
|
405
|
-
// Resolve swap target
|
|
406
|
-
ctx.target = this.#resolveTarget(elt, ctx.target);
|
|
407
|
-
|
|
408
390
|
// Build request body
|
|
409
391
|
let form = elt.form || elt.closest("form")
|
|
410
392
|
let body = this.#collectFormData(elt, form, evt.submitter)
|
|
411
|
-
this.#handleHxVals(elt, body)
|
|
393
|
+
let valsResult = this.#handleHxVals(elt, body)
|
|
394
|
+
if (valsResult) await valsResult // Only await if it returned a promise
|
|
412
395
|
if (ctx.values) {
|
|
413
396
|
for (let k in ctx.values) {
|
|
414
397
|
body.delete(k);
|
|
@@ -419,7 +402,7 @@ var htmx = (() => {
|
|
|
419
402
|
// Setup abort controller and action
|
|
420
403
|
let ac = new AbortController()
|
|
421
404
|
let action = ctx.request.action.replace?.(/#.*$/, '')
|
|
422
|
-
// TODO - consider how this works with hx-config, move most to
|
|
405
|
+
// TODO - consider how this works with hx-config, move most to #createRequestContext?
|
|
423
406
|
Object.assign(ctx.request, {
|
|
424
407
|
originalAction: ctx.request.action,
|
|
425
408
|
action,
|
|
@@ -452,7 +435,7 @@ var htmx = (() => {
|
|
|
452
435
|
await this.#issueRequest(ctx);
|
|
453
436
|
}
|
|
454
437
|
|
|
455
|
-
async
|
|
438
|
+
async #issueRequest(ctx) {
|
|
456
439
|
let elt = ctx.sourceElement
|
|
457
440
|
let syncStrategy = this.#determineSyncStrategy(elt);
|
|
458
441
|
let requestQueue = this.#getRequestQueue(elt);
|
|
@@ -468,25 +451,23 @@ var htmx = (() => {
|
|
|
468
451
|
let disableElements = this.#disableElements(elt, disableSelector);
|
|
469
452
|
|
|
470
453
|
try {
|
|
471
|
-
//
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
let
|
|
475
|
-
|
|
476
|
-
if (
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
} else {
|
|
480
|
-
if (!window.confirm(confirmVal)) {
|
|
481
|
-
return;
|
|
454
|
+
// Handle confirmation
|
|
455
|
+
if (ctx.confirm) {
|
|
456
|
+
let issueRequest = null;
|
|
457
|
+
let confirmed = await new Promise(resolve => {
|
|
458
|
+
issueRequest = resolve;
|
|
459
|
+
if (this.#trigger(elt, "htmx:confirm", {ctx, issueRequest: (skip) => issueRequest?.(skip !== false)})) {
|
|
460
|
+
let js = this.#extractJavascriptContent(ctx.confirm);
|
|
461
|
+
resolve(js ? this.#executeJavaScriptAsync(elt, {}, js, true) : window.confirm(ctx.confirm));
|
|
482
462
|
}
|
|
483
|
-
}
|
|
463
|
+
});
|
|
464
|
+
if (!confirmed) return;
|
|
484
465
|
}
|
|
485
|
-
|
|
486
|
-
ctx.fetch ||= window.fetch
|
|
466
|
+
|
|
467
|
+
ctx.fetch ||= window.fetch.bind(window)
|
|
487
468
|
if (!this.#trigger(elt, "htmx:before:request", {ctx})) return;
|
|
488
469
|
|
|
489
|
-
let response = await
|
|
470
|
+
let response = await ctx.fetch(ctx.request.action, ctx.request);
|
|
490
471
|
|
|
491
472
|
ctx.response = {
|
|
492
473
|
raw: response,
|
|
@@ -494,9 +475,13 @@ var htmx = (() => {
|
|
|
494
475
|
headers: response.headers,
|
|
495
476
|
}
|
|
496
477
|
this.#extractHxHeaders(ctx);
|
|
478
|
+
ctx.isSSE = response.headers.get("Content-Type")?.includes('text/event-stream');
|
|
479
|
+
if (!ctx.isSSE) {
|
|
480
|
+
ctx.text = await response.text();
|
|
481
|
+
}
|
|
497
482
|
if (!this.#trigger(elt, "htmx:after:request", {ctx})) return;
|
|
498
483
|
|
|
499
|
-
if(this.#
|
|
484
|
+
if(this.#handleHeadersAndMaybeReturnEarly(ctx)){
|
|
500
485
|
return
|
|
501
486
|
}
|
|
502
487
|
|
|
@@ -506,16 +491,13 @@ var htmx = (() => {
|
|
|
506
491
|
await this.#handleSSE(ctx, elt, response);
|
|
507
492
|
} else {
|
|
508
493
|
// HTTP response
|
|
509
|
-
ctx.text = await response.text();
|
|
510
494
|
if (ctx.status === "issuing") {
|
|
511
|
-
if (ctx.hx.retarget) ctx.target =
|
|
495
|
+
if (ctx.hx.retarget) ctx.target = ctx.hx.retarget;
|
|
512
496
|
if (ctx.hx.reswap) ctx.swap = ctx.hx.reswap;
|
|
513
497
|
if (ctx.hx.reselect) ctx.select = ctx.hx.reselect;
|
|
514
498
|
ctx.status = "response received";
|
|
515
499
|
this.#handleStatusCodes(ctx);
|
|
516
|
-
this.#handleHistoryUpdate(ctx);
|
|
517
500
|
await this.swap(ctx);
|
|
518
|
-
this.#handleAnchorScroll(ctx)
|
|
519
501
|
ctx.status = "swapped";
|
|
520
502
|
}
|
|
521
503
|
}
|
|
@@ -537,7 +519,7 @@ var htmx = (() => {
|
|
|
537
519
|
}
|
|
538
520
|
|
|
539
521
|
// Extract HX-* headers into ctx.hx
|
|
540
|
-
#extractHxHeaders(ctx
|
|
522
|
+
#extractHxHeaders(ctx) {
|
|
541
523
|
ctx.hx = {}
|
|
542
524
|
for (let [k, v] of ctx.response.raw.headers) {
|
|
543
525
|
if (k.toLowerCase().startsWith('hx-')) {
|
|
@@ -547,7 +529,7 @@ var htmx = (() => {
|
|
|
547
529
|
}
|
|
548
530
|
|
|
549
531
|
// returns true if the header aborts the current response handling
|
|
550
|
-
#
|
|
532
|
+
#handleHeadersAndMaybeReturnEarly(ctx) {
|
|
551
533
|
if (ctx.hx.trigger) {
|
|
552
534
|
this.#handleTriggerHeader(ctx.hx.trigger, ctx.sourceElement);
|
|
553
535
|
}
|
|
@@ -561,8 +543,8 @@ var htmx = (() => {
|
|
|
561
543
|
}
|
|
562
544
|
if (ctx.hx.location) {
|
|
563
545
|
let path = ctx.hx.location, opts = {};
|
|
564
|
-
if (path[0] === '{') {
|
|
565
|
-
opts =
|
|
546
|
+
if (path[0] === '{' || /[\s,]/.test(path)) {
|
|
547
|
+
opts = this.#parseConfig(path);
|
|
566
548
|
path = opts.path;
|
|
567
549
|
delete opts.path;
|
|
568
550
|
}
|
|
@@ -570,10 +552,16 @@ var htmx = (() => {
|
|
|
570
552
|
this.ajax('GET', path, opts);
|
|
571
553
|
return true // TODO this seems legit
|
|
572
554
|
}
|
|
555
|
+
if(ctx.response?.headers?.get?.("Etag")) {
|
|
556
|
+
ctx.sourceElement._htmx ||= {}
|
|
557
|
+
ctx.sourceElement._htmx.etag = ctx.response.headers.get("Etag");
|
|
558
|
+
}
|
|
573
559
|
}
|
|
574
560
|
|
|
575
|
-
async
|
|
576
|
-
let config =
|
|
561
|
+
async #handleSSE(ctx, elt, response) {
|
|
562
|
+
let config = {...this.config.sse, ...ctx.request.sse};
|
|
563
|
+
if (config.once) config.mode = 'once';
|
|
564
|
+
if (config.continuous) config.mode = 'continuous';
|
|
577
565
|
|
|
578
566
|
let waitForVisible = () => new Promise(r => {
|
|
579
567
|
let onVisible = () => !document.hidden && (document.removeEventListener('visibilitychange', onVisible), r());
|
|
@@ -592,7 +580,7 @@ var htmx = (() => {
|
|
|
592
580
|
if (!elt.isConnected) break;
|
|
593
581
|
}
|
|
594
582
|
|
|
595
|
-
let delay = Math.min(config.initialDelay * Math.pow(2, attempt - 1), config.maxDelay);
|
|
583
|
+
let delay = Math.min(this.parseInterval(config.initialDelay) * Math.pow(2, attempt - 1), this.parseInterval(config.maxDelay));
|
|
596
584
|
let reconnect = {attempt, delay, lastEventId, cancelled: false};
|
|
597
585
|
|
|
598
586
|
ctx.status = "reconnecting to stream";
|
|
@@ -650,9 +638,7 @@ var htmx = (() => {
|
|
|
650
638
|
ctx.status = "stream message received";
|
|
651
639
|
|
|
652
640
|
if (!ctx.response.cancelled) {
|
|
653
|
-
this.#handleHistoryUpdate(ctx);
|
|
654
641
|
await this.swap(ctx);
|
|
655
|
-
this.#handleAnchorScroll(ctx);
|
|
656
642
|
ctx.status = "swapped";
|
|
657
643
|
}
|
|
658
644
|
this.#trigger(elt, "htmx:after:sse:message", {ctx, message: msg});
|
|
@@ -669,30 +655,63 @@ var htmx = (() => {
|
|
|
669
655
|
}
|
|
670
656
|
}
|
|
671
657
|
|
|
672
|
-
async*
|
|
673
|
-
let
|
|
674
|
-
|
|
658
|
+
async* #parseSSE(response) {
|
|
659
|
+
let reader = response.body.getReader();
|
|
660
|
+
let decoder = new TextDecoder();
|
|
661
|
+
let buffer = '';
|
|
662
|
+
let message = {data: '', event: '', id: '', retry: null};
|
|
663
|
+
|
|
675
664
|
try {
|
|
676
|
-
while (
|
|
677
|
-
let {done, value} = await
|
|
665
|
+
while (true) {
|
|
666
|
+
let {done, value} = await reader.read();
|
|
678
667
|
if (done) break;
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
668
|
+
|
|
669
|
+
// Decode chunk and add to buffer
|
|
670
|
+
buffer += decoder.decode(value, {stream: true});
|
|
671
|
+
let lines = buffer.split('\n');
|
|
672
|
+
// Keep incomplete line in buffer
|
|
673
|
+
buffer = lines.pop() || '';
|
|
674
|
+
|
|
675
|
+
for (let line of lines) {
|
|
676
|
+
// Empty line or carriage return indicates end of message
|
|
677
|
+
if (!line || line === '\r') {
|
|
678
|
+
if (message.data) {
|
|
679
|
+
yield message;
|
|
680
|
+
message = {data: '', event: '', id: '', retry: null};
|
|
681
|
+
}
|
|
682
|
+
continue;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Parse field: value
|
|
686
|
+
let colonIndex = line.indexOf(':');
|
|
687
|
+
if (colonIndex <= 0) continue;
|
|
688
|
+
|
|
689
|
+
let field = line.slice(0, colonIndex);
|
|
690
|
+
let value = line.slice(colonIndex + 1).trimStart();
|
|
691
|
+
|
|
692
|
+
if (field === 'data') {
|
|
693
|
+
message.data += (message.data ? '\n' : '') + value;
|
|
694
|
+
} else if (field === 'event') {
|
|
695
|
+
message.event = value;
|
|
696
|
+
} else if (field === 'id') {
|
|
697
|
+
message.id = value;
|
|
698
|
+
} else if (field === 'retry') {
|
|
699
|
+
let retryValue = parseInt(value, 10);
|
|
700
|
+
if (!isNaN(retryValue)) {
|
|
701
|
+
message.retry = retryValue;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
686
705
|
}
|
|
687
706
|
} finally {
|
|
688
|
-
|
|
707
|
+
reader.releaseLock();
|
|
689
708
|
}
|
|
690
709
|
}
|
|
691
710
|
|
|
692
711
|
#initTimeout(ctx) {
|
|
693
712
|
let timeoutInterval;
|
|
694
713
|
if (ctx.request.timeout) {
|
|
695
|
-
timeoutInterval =
|
|
714
|
+
timeoutInterval = this.parseInterval(ctx.request.timeout);
|
|
696
715
|
} else {
|
|
697
716
|
timeoutInterval = this.config.defaultTimeout;
|
|
698
717
|
}
|
|
@@ -891,35 +910,7 @@ var htmx = (() => {
|
|
|
891
910
|
}
|
|
892
911
|
}
|
|
893
912
|
|
|
894
|
-
#initializeStreamConfig(elt) {
|
|
895
|
-
let streamSpec = this.#attributeValue(elt, 'hx-stream');
|
|
896
|
-
if (!streamSpec) return;
|
|
897
|
-
|
|
898
|
-
// Start with global defaults
|
|
899
|
-
let streamConfig = {...this.config.streams};
|
|
900
|
-
let tokens = this.#tokenize(streamSpec);
|
|
901
|
-
|
|
902
|
-
for (let i = 0; i < tokens.length; i++) {
|
|
903
|
-
let token = tokens[i];
|
|
904
|
-
// Main value: once or continuous
|
|
905
|
-
if (token === 'once' || token === 'continuous') {
|
|
906
|
-
streamConfig.mode = token;
|
|
907
|
-
} else if (token === 'pauseHidden') {
|
|
908
|
-
streamConfig.pauseHidden = true;
|
|
909
|
-
} else if (tokens[i + 1] === ':') {
|
|
910
|
-
let key = token, value = tokens[i + 2];
|
|
911
|
-
if (key === 'mode') streamConfig.mode = value;
|
|
912
|
-
else if (key === 'maxRetries') streamConfig.maxRetries = parseInt(value);
|
|
913
|
-
else if (key === 'initialDelay') streamConfig.initialDelay = this.parseInterval(value);
|
|
914
|
-
else if (key === 'maxDelay') streamConfig.maxDelay = this.parseInterval(value);
|
|
915
|
-
else if (key === 'pauseHidden') streamConfig.pauseHidden = value === 'true';
|
|
916
|
-
i += 2;
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
913
|
|
|
920
|
-
if (!elt._htmx) elt._htmx = {};
|
|
921
|
-
elt._htmx.streamConfig = streamConfig;
|
|
922
|
-
}
|
|
923
914
|
|
|
924
915
|
#extractFilter(str) {
|
|
925
916
|
let match = str.match(/^([^\[]*)\[([^\]]*)]/);
|
|
@@ -929,7 +920,7 @@ var htmx = (() => {
|
|
|
929
920
|
|
|
930
921
|
#handleTriggerHeader(value, elt) {
|
|
931
922
|
if (value[0] === '{') {
|
|
932
|
-
let triggers =
|
|
923
|
+
let triggers = this.#parseConfig(value);
|
|
933
924
|
for (let name in triggers) {
|
|
934
925
|
let detail = triggers[name];
|
|
935
926
|
if (detail?.target) elt = this.find(detail.target) || elt;
|
|
@@ -961,7 +952,7 @@ var htmx = (() => {
|
|
|
961
952
|
return bound;
|
|
962
953
|
}
|
|
963
954
|
|
|
964
|
-
async
|
|
955
|
+
async #executeJavaScriptAsync(thisArg, obj, code, expression = true) {
|
|
965
956
|
let args = {}
|
|
966
957
|
Object.assign(args, this.#apiMethods(thisArg))
|
|
967
958
|
Object.assign(args, obj)
|
|
@@ -1002,23 +993,45 @@ var htmx = (() => {
|
|
|
1002
993
|
}
|
|
1003
994
|
|
|
1004
995
|
#maybeBoost(elt) {
|
|
1005
|
-
if (this.#attributeValue(elt, "hx-boost") === "true") {
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
elt.
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
elt.
|
|
1015
|
-
|
|
1016
|
-
|
|
996
|
+
if (this.#attributeValue(elt, "hx-boost") === "true" && this.#shouldBoost(elt)) {
|
|
997
|
+
elt._htmx = {eventHandler: this.#createHtmxEventHandler(elt), requests: [], boosted: true}
|
|
998
|
+
elt.setAttribute('data-htmx-powered', 'true');
|
|
999
|
+
if (elt.matches('a') && !elt.hasAttribute("target")) {
|
|
1000
|
+
elt.addEventListener('click', (click) => {
|
|
1001
|
+
elt._htmx.eventHandler(click)
|
|
1002
|
+
})
|
|
1003
|
+
} else {
|
|
1004
|
+
elt.addEventListener('submit', (submit) => {
|
|
1005
|
+
elt._htmx.eventHandler(submit)
|
|
1006
|
+
})
|
|
1007
|
+
}
|
|
1008
|
+
this.#trigger(elt, "htmx:after:init", {}, true)
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
#shouldBoost(elt) {
|
|
1013
|
+
if (this.#shouldInitialize(elt)) {
|
|
1014
|
+
if (elt.tagName === "A") {
|
|
1015
|
+
if (elt.target === '' || elt.target === '_self') {
|
|
1016
|
+
return !elt.getAttribute('href')?.startsWith?.("#") && this.#isSameOrigin(elt.href)
|
|
1017
1017
|
}
|
|
1018
|
+
} else if (elt.tagName === "FORM") {
|
|
1019
|
+
return elt.method !== 'dialog' && this.#isSameOrigin(elt.action);
|
|
1018
1020
|
}
|
|
1019
1021
|
}
|
|
1020
1022
|
}
|
|
1021
1023
|
|
|
1024
|
+
#isSameOrigin(url) {
|
|
1025
|
+
try {
|
|
1026
|
+
// URL constructor handles both relative and absolute URLs
|
|
1027
|
+
const parsed = new URL(url, window.location.href);
|
|
1028
|
+
return parsed.origin === window.location.origin;
|
|
1029
|
+
} catch (e) {
|
|
1030
|
+
// If URL parsing fails, assume not same-origin
|
|
1031
|
+
return false;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1022
1035
|
#shouldInitialize(elt) {
|
|
1023
1036
|
return !elt._htmx && !this.#ignore(elt);
|
|
1024
1037
|
}
|
|
@@ -1076,8 +1089,9 @@ var htmx = (() => {
|
|
|
1076
1089
|
}
|
|
1077
1090
|
|
|
1078
1091
|
#makeFragment(text) {
|
|
1079
|
-
let response = text.replace(/<
|
|
1080
|
-
|
|
1092
|
+
let response = text.replace(/<hx-([a-z]+)(\s+|>)/gi, '<template hx type="$1"$2').replace(/<\/hx-[a-z]+>/gi, '</template>');
|
|
1093
|
+
let title = '';
|
|
1094
|
+
response = response.replace(/<title[^>]*>[\s\S]*?<\/title>/i, m => (title = this.#parseHTML(m).title, ''));
|
|
1081
1095
|
let responseWithNoHead = response.replace(/<head(\s[^>]*)?>[\s\S]*?<\/head>/i, '');
|
|
1082
1096
|
let startTag = responseWithNoHead.match(/<([a-z][^\/>\x20\t\r\n\f]*)/i)?.[1]?.toLowerCase();
|
|
1083
1097
|
|
|
@@ -1092,49 +1106,28 @@ var htmx = (() => {
|
|
|
1092
1106
|
doc = this.#parseHTML(`<template>${responseWithNoHead}</template>`);
|
|
1093
1107
|
fragment = doc.querySelector('template').content;
|
|
1094
1108
|
}
|
|
1109
|
+
this.#processScripts(fragment);
|
|
1095
1110
|
|
|
1096
1111
|
return {
|
|
1097
1112
|
fragment,
|
|
1098
|
-
title
|
|
1113
|
+
title
|
|
1099
1114
|
};
|
|
1100
1115
|
}
|
|
1101
1116
|
|
|
1102
1117
|
#createOOBTask(tasks, elt, oobValue, sourceElement) {
|
|
1103
|
-
// Handle legacy format: swapStyle:target (only if no spaces, which indicate modifiers)
|
|
1104
1118
|
let target = elt.id ? '#' + CSS.escape(elt.id) : null;
|
|
1105
1119
|
if (oobValue !== 'true' && oobValue && !oobValue.includes(' ')) {
|
|
1106
|
-
|
|
1107
|
-
if (colonIdx !== -1) {
|
|
1108
|
-
target = oobValue.substring(colonIdx + 1);
|
|
1109
|
-
oobValue = oobValue.substring(0, colonIdx);
|
|
1110
|
-
}
|
|
1120
|
+
[oobValue, target = target] = oobValue.split(/:(.*)/);
|
|
1111
1121
|
}
|
|
1112
1122
|
if (oobValue === 'true' || !oobValue) oobValue = 'outerHTML';
|
|
1113
1123
|
|
|
1114
1124
|
let swapSpec = this.#parseSwapSpec(oobValue);
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
let fragment;
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
}
|
|
1122
|
-
if (swapSpec.strip) {
|
|
1123
|
-
fragment = oobElementClone.content || oobElementClone;
|
|
1124
|
-
} else {
|
|
1125
|
-
fragment = document.createDocumentFragment();
|
|
1126
|
-
fragment.appendChild(oobElementClone);
|
|
1127
|
-
}
|
|
1128
|
-
elt.remove();
|
|
1129
|
-
if (!target && !oobValue.includes('target:')) return;
|
|
1130
|
-
|
|
1131
|
-
tasks.push({
|
|
1132
|
-
type: 'oob',
|
|
1133
|
-
fragment,
|
|
1134
|
-
target,
|
|
1135
|
-
swapSpec,
|
|
1136
|
-
sourceElement
|
|
1137
|
-
});
|
|
1125
|
+
target = swapSpec.target || target;
|
|
1126
|
+
swapSpec.strip ??= !swapSpec.style.startsWith('outer');
|
|
1127
|
+
if (!target) return;
|
|
1128
|
+
let fragment = document.createDocumentFragment();
|
|
1129
|
+
fragment.append(elt);
|
|
1130
|
+
tasks.push({type: 'oob', fragment, target, swapSpec, sourceElement});
|
|
1138
1131
|
}
|
|
1139
1132
|
|
|
1140
1133
|
#processOOB(fragment, sourceElement, selectOOB) {
|
|
@@ -1143,9 +1136,7 @@ var htmx = (() => {
|
|
|
1143
1136
|
// Process hx-select-oob first (select elements from response)
|
|
1144
1137
|
if (selectOOB) {
|
|
1145
1138
|
for (let spec of selectOOB.split(',')) {
|
|
1146
|
-
let [selector,
|
|
1147
|
-
let oobValue = rest.length ? rest.join(':') : 'true';
|
|
1148
|
-
|
|
1139
|
+
let [selector, oobValue = 'true'] = spec.split(/:(.*)/);
|
|
1149
1140
|
for (let elt of fragment.querySelectorAll(selector)) {
|
|
1150
1141
|
this.#createOOBTask(tasks, elt, oobValue, sourceElement);
|
|
1151
1142
|
}
|
|
@@ -1171,64 +1162,63 @@ var htmx = (() => {
|
|
|
1171
1162
|
}
|
|
1172
1163
|
|
|
1173
1164
|
#parseSwapSpec(swapStr) {
|
|
1174
|
-
|
|
1175
|
-
let
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
if (tokens[i + 1] === ':') {
|
|
1181
|
-
let key = tokens[i], value = tokens[i = i + 2];
|
|
1182
|
-
if (key === 'swap') config.swapDelay = this.parseInterval(value);
|
|
1183
|
-
else if (key === 'transition' || key === 'ignoreTitle' || key === 'strip') config[key] = value === 'true';
|
|
1184
|
-
else if (key === 'focus-scroll') config.focusScroll = value === 'true';
|
|
1185
|
-
else if (key === 'scroll' || key === 'show') {
|
|
1186
|
-
let parts = [value];
|
|
1187
|
-
while (tokens[i + 1] === ':') {
|
|
1188
|
-
parts.push(tokens[i + 2]);
|
|
1189
|
-
i += 2;
|
|
1190
|
-
}
|
|
1191
|
-
config[key] = parts.length === 1 ? parts[0] : parts.pop();
|
|
1192
|
-
if (parts.length > 1) config[key + 'Target'] = parts.join(':');
|
|
1193
|
-
} else if (key === 'target') {
|
|
1194
|
-
let parts = [value];
|
|
1195
|
-
while (i + 1 < tokens.length && tokens[i + 1] !== ':' && tokens[i + 2] !== ':') {
|
|
1196
|
-
parts.push(tokens[i + 1]);
|
|
1197
|
-
i++;
|
|
1198
|
-
}
|
|
1199
|
-
config[key] = parts.join(' ');
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1165
|
+
swapStr = swapStr.trim();
|
|
1166
|
+
let style = this.config.defaultSwap
|
|
1167
|
+
if (swapStr && !/^\S*:/.test(swapStr)) {
|
|
1168
|
+
let m = swapStr.match(/^(\S+)\s*(.*)$/);
|
|
1169
|
+
style = m[1];
|
|
1170
|
+
swapStr = m[2];
|
|
1202
1171
|
}
|
|
1203
|
-
return
|
|
1172
|
+
return {style: this.#normalizeSwapStyle(style), ...this.#parseConfig(swapStr)};
|
|
1204
1173
|
}
|
|
1205
1174
|
|
|
1206
|
-
#processPartials(fragment,
|
|
1175
|
+
#processPartials(fragment, ctx) {
|
|
1207
1176
|
let tasks = [];
|
|
1208
1177
|
|
|
1209
|
-
for (let
|
|
1210
|
-
let
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1178
|
+
for (let templateElt of fragment.querySelectorAll('template[hx]')) {
|
|
1179
|
+
let type = templateElt.getAttribute('type');
|
|
1180
|
+
|
|
1181
|
+
if (type === 'partial') {
|
|
1182
|
+
let swapSpec = this.#parseSwapSpec(templateElt.getAttribute(this.#prefix('hx-swap')) || this.config.defaultSwap);
|
|
1183
|
+
|
|
1184
|
+
tasks.push({
|
|
1185
|
+
type: 'partial',
|
|
1186
|
+
fragment: templateElt.content.cloneNode(true),
|
|
1187
|
+
target: templateElt.getAttribute(this.#prefix('hx-target')),
|
|
1188
|
+
swapSpec,
|
|
1189
|
+
sourceElement: ctx.sourceElement
|
|
1190
|
+
});
|
|
1191
|
+
} else {
|
|
1192
|
+
this.#triggerExtensions(templateElt, 'htmx:process:' + type, { ctx, tasks });
|
|
1193
|
+
}
|
|
1194
|
+
templateElt.remove();
|
|
1220
1195
|
}
|
|
1221
1196
|
|
|
1222
1197
|
return tasks;
|
|
1223
1198
|
}
|
|
1224
1199
|
|
|
1225
|
-
#
|
|
1226
|
-
|
|
1227
|
-
|
|
1200
|
+
#handleAutoFocus(elt) {
|
|
1201
|
+
let autofocus = this.find(elt, "[autofocus]");
|
|
1202
|
+
autofocus?.focus?.()
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
#handleScroll(task) {
|
|
1206
|
+
if (task.swapSpec.scroll) {
|
|
1207
|
+
let target = task.swapSpec.scrollTarget ? this.#findExt(task.swapSpec.scrollTarget) : task.target;
|
|
1208
|
+
if (task.swapSpec.scroll === 'top') {
|
|
1209
|
+
target.scrollTop = 0;
|
|
1210
|
+
} else if (task.swapSpec.scroll === 'bottom'){
|
|
1211
|
+
target.scrollTop = target.scrollHeight;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
if (task.swapSpec.show) {
|
|
1215
|
+
let target = task.swapSpec.showTarget ? this.#findExt(task.swapSpec.showTarget) : task.target;
|
|
1216
|
+
target.scrollIntoView(task.swapSpec.show === 'top')
|
|
1217
|
+
}
|
|
1228
1218
|
}
|
|
1229
1219
|
|
|
1230
1220
|
#handleAnchorScroll(ctx) {
|
|
1231
|
-
let anchor = ctx.request
|
|
1221
|
+
let anchor = ctx.request?.originalAction?.split('#')[1];
|
|
1232
1222
|
if (anchor) {
|
|
1233
1223
|
document.getElementById(anchor)?.scrollIntoView({block: 'start', behavior: 'auto'});
|
|
1234
1224
|
}
|
|
@@ -1254,16 +1244,18 @@ var htmx = (() => {
|
|
|
1254
1244
|
//============================================================================================
|
|
1255
1245
|
|
|
1256
1246
|
async swap(ctx) {
|
|
1247
|
+
this.#handleHistoryUpdate(ctx);
|
|
1257
1248
|
let {fragment, title} = this.#makeFragment(ctx.text);
|
|
1249
|
+
ctx.title = title;
|
|
1258
1250
|
let tasks = [];
|
|
1259
1251
|
|
|
1260
1252
|
// Process OOB and partials
|
|
1261
1253
|
let oobTasks = this.#processOOB(fragment, ctx.sourceElement, ctx.selectOOB);
|
|
1262
|
-
let partialTasks = this.#processPartials(fragment, ctx
|
|
1254
|
+
let partialTasks = this.#processPartials(fragment, ctx);
|
|
1263
1255
|
tasks.push(...oobTasks, ...partialTasks);
|
|
1264
1256
|
|
|
1265
1257
|
// Process main swap
|
|
1266
|
-
let mainSwap = this.#processMainSwap(ctx, fragment, partialTasks
|
|
1258
|
+
let mainSwap = this.#processMainSwap(ctx, fragment, partialTasks);
|
|
1267
1259
|
if (mainSwap) {
|
|
1268
1260
|
tasks.push(mainSwap);
|
|
1269
1261
|
}
|
|
@@ -1281,8 +1273,8 @@ var htmx = (() => {
|
|
|
1281
1273
|
|
|
1282
1274
|
// insert non-transition tasks immediately or with delay
|
|
1283
1275
|
for (let task of nonTransitionTasks) {
|
|
1284
|
-
if (task.swapSpec?.
|
|
1285
|
-
setTimeout(() => this.#insertContent(task), task.swapSpec.
|
|
1276
|
+
if (task.swapSpec?.swap) {
|
|
1277
|
+
setTimeout(() => this.#insertContent(task), this.parseInterval(task.swapSpec.swap));
|
|
1286
1278
|
} else {
|
|
1287
1279
|
this.#insertContent(task)
|
|
1288
1280
|
}
|
|
@@ -1299,7 +1291,7 @@ var htmx = (() => {
|
|
|
1299
1291
|
}
|
|
1300
1292
|
|
|
1301
1293
|
this.#trigger(document, "htmx:after:swap", {ctx});
|
|
1302
|
-
if (mainSwap?.
|
|
1294
|
+
if (ctx.title && !mainSwap?.swapSpec?.ignoreTitle) document.title = ctx.title;
|
|
1303
1295
|
await this.timeout(1);
|
|
1304
1296
|
// invoke restore tasks
|
|
1305
1297
|
for (let task of tasks) {
|
|
@@ -1307,38 +1299,32 @@ var htmx = (() => {
|
|
|
1307
1299
|
restore()
|
|
1308
1300
|
}
|
|
1309
1301
|
}
|
|
1310
|
-
this.#trigger(document, "htmx:after:restore", {ctx});
|
|
1302
|
+
this.#trigger(document, "htmx:after:restore", { ctx });
|
|
1303
|
+
this.#handleAnchorScroll(ctx);
|
|
1311
1304
|
// TODO this stuff should be an extension
|
|
1312
1305
|
// if (ctx.hx?.triggerafterswap) this.#handleTriggerHeader(ctx.hx.triggerafterswap, ctx.sourceElement);
|
|
1313
1306
|
}
|
|
1314
1307
|
|
|
1315
|
-
#processMainSwap(ctx, fragment, partialTasks
|
|
1308
|
+
#processMainSwap(ctx, fragment, partialTasks) {
|
|
1316
1309
|
// Create main task if needed
|
|
1317
1310
|
let swapSpec = this.#parseSwapSpec(ctx.swap || this.config.defaultSwap);
|
|
1318
1311
|
// skip creating main swap if extracting partials resulted in empty response except for delete style
|
|
1319
1312
|
if (swapSpec.style === 'delete' || /\S/.test(fragment.innerHTML || '') || !partialTasks.length) {
|
|
1320
|
-
let resultFragment = document.createDocumentFragment();
|
|
1321
1313
|
if (ctx.select) {
|
|
1322
|
-
let selected = fragment.
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
} else {
|
|
1331
|
-
resultFragment.append(...fragment.childNodes);
|
|
1314
|
+
let selected = fragment.querySelectorAll(ctx.select);
|
|
1315
|
+
fragment = document.createDocumentFragment();
|
|
1316
|
+
fragment.append(...selected);
|
|
1317
|
+
}
|
|
1318
|
+
if (this.#isBoosted(ctx.sourceElement)) {
|
|
1319
|
+
swapSpec.show ||= 'top';
|
|
1332
1320
|
}
|
|
1333
|
-
|
|
1334
1321
|
let mainSwap = {
|
|
1335
1322
|
type: 'main',
|
|
1336
|
-
fragment
|
|
1337
|
-
target: swapSpec.target || ctx.target,
|
|
1323
|
+
fragment,
|
|
1324
|
+
target: this.#resolveTarget(ctx.sourceElement || document.body, swapSpec.target || ctx.target),
|
|
1338
1325
|
swapSpec,
|
|
1339
1326
|
sourceElement: ctx.sourceElement,
|
|
1340
|
-
transition: (ctx.transition !== false) && (swapSpec.transition !== false)
|
|
1341
|
-
title
|
|
1327
|
+
transition: (ctx.transition !== false) && (swapSpec.transition !== false)
|
|
1342
1328
|
};
|
|
1343
1329
|
return mainSwap;
|
|
1344
1330
|
}
|
|
@@ -1350,8 +1336,13 @@ var htmx = (() => {
|
|
|
1350
1336
|
target = document.querySelector(target);
|
|
1351
1337
|
}
|
|
1352
1338
|
if (!target) return;
|
|
1339
|
+
if (swapSpec.strip && fragment.firstElementChild) {
|
|
1340
|
+
task.unstripped = fragment;
|
|
1341
|
+
fragment = document.createDocumentFragment();
|
|
1342
|
+
fragment.append(...(task.fragment.firstElementChild.content || task.fragment.firstElementChild).childNodes);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1353
1345
|
let pantry = this.#handlePreservedElements(fragment);
|
|
1354
|
-
this.#processScripts(fragment);
|
|
1355
1346
|
let parentNode = target.parentNode;
|
|
1356
1347
|
let newContent = [...fragment.childNodes]
|
|
1357
1348
|
if (swapSpec.style === 'innerHTML') {
|
|
@@ -1391,22 +1382,26 @@ var htmx = (() => {
|
|
|
1391
1382
|
return;
|
|
1392
1383
|
} else if (swapSpec.style === 'none') {
|
|
1393
1384
|
return;
|
|
1394
|
-
} else if (!this.#triggerExtensions(target, 'htmx:handle:swap', task)) {
|
|
1395
|
-
return;
|
|
1396
1385
|
} else {
|
|
1386
|
+
task.target = target;
|
|
1387
|
+
task.fragment = fragment;
|
|
1388
|
+
if (!this.#triggerExtensions(target, 'htmx:handle:swap', task)) return;
|
|
1397
1389
|
throw new Error(`Unknown swap style: ${swapSpec.style}`);
|
|
1398
1390
|
}
|
|
1399
1391
|
this.#restorePreservedElements(pantry);
|
|
1400
1392
|
for (const elt of newContent) {
|
|
1401
|
-
this.process(elt);
|
|
1393
|
+
this.process(elt);
|
|
1394
|
+
this.#handleAutoFocus(elt);
|
|
1402
1395
|
}
|
|
1403
|
-
|
|
1396
|
+
this.#handleScroll(task);
|
|
1404
1397
|
}
|
|
1405
1398
|
|
|
1406
1399
|
#trigger(on, eventName, detail = {}, bubbles = true) {
|
|
1407
1400
|
if (this.config.logAll) {
|
|
1408
1401
|
console.log(eventName, detail, on)
|
|
1409
1402
|
}
|
|
1403
|
+
on = this.#normalizeElement(on)
|
|
1404
|
+
this.#triggerExtensions(on, this.#maybeAdjustMetaCharacter(eventName), detail);
|
|
1410
1405
|
return this.trigger(on, eventName, detail, bubbles)
|
|
1411
1406
|
}
|
|
1412
1407
|
|
|
@@ -1425,9 +1420,7 @@ var htmx = (() => {
|
|
|
1425
1420
|
}
|
|
1426
1421
|
|
|
1427
1422
|
timeout(time) {
|
|
1428
|
-
|
|
1429
|
-
time = this.parseInterval(time)
|
|
1430
|
-
}
|
|
1423
|
+
time = this.parseInterval(time);
|
|
1431
1424
|
if (time > 0) {
|
|
1432
1425
|
return new Promise(resolve => setTimeout(resolve, time));
|
|
1433
1426
|
}
|
|
@@ -1449,8 +1442,19 @@ var htmx = (() => {
|
|
|
1449
1442
|
})
|
|
1450
1443
|
}
|
|
1451
1444
|
|
|
1452
|
-
|
|
1453
|
-
|
|
1445
|
+
onLoad(callback) {
|
|
1446
|
+
this.on("htmx:after:init", (evt) => {
|
|
1447
|
+
callback(evt.target)
|
|
1448
|
+
})
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
takeClass(element, className, container = element.parentElement) {
|
|
1452
|
+
for (let elt of this.findAll(this.#normalizeElement(container), "." + className)) {
|
|
1453
|
+
elt.classList.remove(className);
|
|
1454
|
+
}
|
|
1455
|
+
element.classList.add(className);
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1454
1458
|
on(eventOrElt, eventOrCallback, callback) {
|
|
1455
1459
|
let event;
|
|
1456
1460
|
let elt = document;
|
|
@@ -1474,6 +1478,7 @@ var htmx = (() => {
|
|
|
1474
1478
|
}
|
|
1475
1479
|
|
|
1476
1480
|
parseInterval(str) {
|
|
1481
|
+
if (typeof str === 'number') return str;
|
|
1477
1482
|
let m = {ms: 1, s: 1000, m: 60000};
|
|
1478
1483
|
let [, n, u] = str?.match(/^([\d.]+)(ms|s|m)?$/) || [];
|
|
1479
1484
|
let v = parseFloat(n) * (m[u] || 1);
|
|
@@ -1482,7 +1487,6 @@ var htmx = (() => {
|
|
|
1482
1487
|
|
|
1483
1488
|
trigger(on, eventName, detail = {}, bubbles = true) {
|
|
1484
1489
|
on = this.#normalizeElement(on)
|
|
1485
|
-
this.#triggerExtensions(on, eventName, detail);
|
|
1486
1490
|
let evt = new CustomEvent(eventName, {
|
|
1487
1491
|
detail,
|
|
1488
1492
|
cancelable: true,
|
|
@@ -1503,17 +1507,22 @@ var htmx = (() => {
|
|
|
1503
1507
|
|
|
1504
1508
|
let sourceElt = typeof context.source === 'string' ?
|
|
1505
1509
|
document.querySelector(context.source) : context.source;
|
|
1506
|
-
let targetElt = context.target ?
|
|
1507
|
-
this.#resolveTarget(sourceElt || document.body, context.target) : sourceElt;
|
|
1508
1510
|
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
+
// If source selector was provided but didn't match, reject
|
|
1512
|
+
if (typeof context.source === 'string' && !sourceElt) {
|
|
1513
|
+
return Promise.reject(new Error('Source not found'));
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
// Resolve target, defaulting to body only if no source or target provided
|
|
1517
|
+
let target = this.#resolveTarget(document.body, context.target || sourceElt);
|
|
1518
|
+
if (!target) {
|
|
1519
|
+
return Promise.reject(new Error('Target not found'));
|
|
1511
1520
|
}
|
|
1512
1521
|
|
|
1513
|
-
sourceElt
|
|
1522
|
+
sourceElt ||= target;
|
|
1514
1523
|
|
|
1515
1524
|
let ctx = this.#createRequestContext(sourceElt, context.event || {});
|
|
1516
|
-
Object.assign(ctx, context, {target
|
|
1525
|
+
Object.assign(ctx, context, {target});
|
|
1517
1526
|
Object.assign(ctx.request, {action: path, method: verb.toUpperCase()});
|
|
1518
1527
|
if (context.headers) Object.assign(ctx.request.headers, context.headers);
|
|
1519
1528
|
|
|
@@ -1526,11 +1535,13 @@ var htmx = (() => {
|
|
|
1526
1535
|
|
|
1527
1536
|
#initHistoryHandling() {
|
|
1528
1537
|
if (!this.config.history) return;
|
|
1529
|
-
|
|
1538
|
+
if (!history.state) {
|
|
1539
|
+
history.replaceState({htmx: true}, '', location.pathname + location.search);
|
|
1540
|
+
}
|
|
1530
1541
|
window.addEventListener('popstate', (event) => {
|
|
1531
1542
|
if (event.state && event.state.htmx) {
|
|
1532
1543
|
this.#restoreHistory();
|
|
1533
|
-
}
|
|
1544
|
+
}
|
|
1534
1545
|
});
|
|
1535
1546
|
}
|
|
1536
1547
|
|
|
@@ -1554,10 +1565,11 @@ var htmx = (() => {
|
|
|
1554
1565
|
} else {
|
|
1555
1566
|
this.ajax('GET', path, {
|
|
1556
1567
|
target: 'body',
|
|
1557
|
-
swap: 'outerHTML',
|
|
1558
1568
|
request: {headers: {'HX-History-Restore-Request': 'true'}}
|
|
1559
1569
|
});
|
|
1560
1570
|
}
|
|
1571
|
+
} else if (elt.tagName === "FORM") {
|
|
1572
|
+
return elt.method !== 'dialog' && this.#isSameOrigin(elt.action);
|
|
1561
1573
|
}
|
|
1562
1574
|
}
|
|
1563
1575
|
|
|
@@ -1573,7 +1585,7 @@ var htmx = (() => {
|
|
|
1573
1585
|
}
|
|
1574
1586
|
|
|
1575
1587
|
let path = push || replace;
|
|
1576
|
-
if (!path || path === 'false') return;
|
|
1588
|
+
if (!path || path === 'false' || path === false) return;
|
|
1577
1589
|
|
|
1578
1590
|
if (path === 'true') {
|
|
1579
1591
|
path = ctx.request.originalAction;
|
|
@@ -1597,8 +1609,9 @@ var htmx = (() => {
|
|
|
1597
1609
|
|
|
1598
1610
|
#handleHxOnAttributes(node) {
|
|
1599
1611
|
for (let attr of node.getAttributeNames()) {
|
|
1600
|
-
|
|
1601
|
-
|
|
1612
|
+
var searchString = this.#maybeAdjustMetaCharacter(this.#prefix("hx-on:"));
|
|
1613
|
+
if (attr.startsWith(searchString)) {
|
|
1614
|
+
let evtName = attr.substring(searchString.length)
|
|
1602
1615
|
let code = node.getAttribute(attr);
|
|
1603
1616
|
node.addEventListener(evtName, async (evt) => {
|
|
1604
1617
|
try {
|
|
@@ -1662,11 +1675,9 @@ var htmx = (() => {
|
|
|
1662
1675
|
}
|
|
1663
1676
|
|
|
1664
1677
|
#collectFormData(elt, form, submitter) {
|
|
1665
|
-
let formData = new FormData()
|
|
1666
|
-
let included = new Set()
|
|
1667
|
-
if (form) {
|
|
1668
|
-
this.#addInputValues(form, included, formData)
|
|
1669
|
-
} else if (elt.name) {
|
|
1678
|
+
let formData = form ? new FormData(form) : new FormData()
|
|
1679
|
+
let included = form ? new Set(form.elements) : new Set()
|
|
1680
|
+
if (!form && elt.name) {
|
|
1670
1681
|
formData.append(elt.name, elt.value)
|
|
1671
1682
|
included.add(elt);
|
|
1672
1683
|
}
|
|
@@ -1688,21 +1699,21 @@ var htmx = (() => {
|
|
|
1688
1699
|
let inputs = this.#queryEltAndDescendants(elt, 'input:not([disabled]), select:not([disabled]), textarea:not([disabled])');
|
|
1689
1700
|
|
|
1690
1701
|
for (let input of inputs) {
|
|
1691
|
-
// Skip elements without a name or already seen
|
|
1692
1702
|
if (!input.name || included.has(input)) continue;
|
|
1693
1703
|
included.add(input);
|
|
1694
1704
|
|
|
1695
|
-
|
|
1705
|
+
let type = input.type;
|
|
1706
|
+
if (type === 'checkbox' || type === 'radio') {
|
|
1696
1707
|
// Only add if checked
|
|
1697
1708
|
if (input.checked) {
|
|
1698
1709
|
formData.append(input.name, input.value);
|
|
1699
1710
|
}
|
|
1700
|
-
} else if (
|
|
1711
|
+
} else if (type === 'file') {
|
|
1701
1712
|
// Add all selected files
|
|
1702
1713
|
for (let file of input.files) {
|
|
1703
1714
|
formData.append(input.name, file);
|
|
1704
1715
|
}
|
|
1705
|
-
} else if (
|
|
1716
|
+
} else if (type === 'select-multiple') {
|
|
1706
1717
|
// Add all selected options
|
|
1707
1718
|
for (let option of input.selectedOptions) {
|
|
1708
1719
|
formData.append(input.name, option.value);
|
|
@@ -1717,12 +1728,20 @@ var htmx = (() => {
|
|
|
1717
1728
|
#handleHxVals(elt, body) {
|
|
1718
1729
|
let hxValsValue = this.#attributeValue(elt, "hx-vals");
|
|
1719
1730
|
if (hxValsValue) {
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1731
|
+
let javascriptContent = this.#extractJavascriptContent(hxValsValue);
|
|
1732
|
+
if (javascriptContent) {
|
|
1733
|
+
// Return promise for async evaluation
|
|
1734
|
+
return this.#executeJavaScriptAsync(elt, {}, javascriptContent, true).then(obj => {
|
|
1735
|
+
for (let key in obj) {
|
|
1736
|
+
body.append(key, obj[key])
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
} else {
|
|
1740
|
+
// Synchronous path
|
|
1741
|
+
let obj = this.#parseConfig(hxValsValue);
|
|
1742
|
+
for (let key in obj) {
|
|
1743
|
+
body.append(key, obj[key])
|
|
1744
|
+
}
|
|
1726
1745
|
}
|
|
1727
1746
|
}
|
|
1728
1747
|
}
|
|
@@ -1733,11 +1752,13 @@ var htmx = (() => {
|
|
|
1733
1752
|
}
|
|
1734
1753
|
|
|
1735
1754
|
#findAllExt(eltOrSelector, maybeSelector, global) {
|
|
1736
|
-
let
|
|
1755
|
+
let selector = maybeSelector ?? eltOrSelector;
|
|
1756
|
+
let elt = maybeSelector ? this.#normalizeElement(eltOrSelector) : document;
|
|
1737
1757
|
if (selector.startsWith('global ')) {
|
|
1738
1758
|
return this.#findAllExt(elt, selector.slice(7), true);
|
|
1739
1759
|
}
|
|
1740
|
-
let parts =
|
|
1760
|
+
let parts = selector ? selector.replace(/<[^>]+\/>/g, m => m.replace(/,/g, '%2C'))
|
|
1761
|
+
.split(',').map(p => p.replace(/%2C/g, ',')) : [];
|
|
1741
1762
|
let result = []
|
|
1742
1763
|
let unprocessedParts = []
|
|
1743
1764
|
for (const part of parts) {
|
|
@@ -1783,28 +1804,6 @@ var htmx = (() => {
|
|
|
1783
1804
|
return result
|
|
1784
1805
|
}
|
|
1785
1806
|
|
|
1786
|
-
#normalizeElementAndSelector(eltOrSelector, selector) {
|
|
1787
|
-
if (selector === undefined) {
|
|
1788
|
-
return [document, eltOrSelector];
|
|
1789
|
-
} else {
|
|
1790
|
-
return [this.#normalizeElement(eltOrSelector), selector];
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
|
|
1794
|
-
#tokenizeExtendedSelector(selector) {
|
|
1795
|
-
let parts = [], depth = 0, start = 0;
|
|
1796
|
-
for (let i = 0; i <= selector.length; i++) {
|
|
1797
|
-
let c = selector[i];
|
|
1798
|
-
if (c === '<') depth++;
|
|
1799
|
-
else if (c === '/' && selector[i + 1] === '>') depth--;
|
|
1800
|
-
else if ((c === ',' && !depth) || i === selector.length) {
|
|
1801
|
-
if (i > start) parts.push(selector.substring(start, i));
|
|
1802
|
-
start = i + 1;
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
|
-
return parts;
|
|
1806
|
-
}
|
|
1807
|
-
|
|
1808
1807
|
#scanForwardQuery(start, match, global) {
|
|
1809
1808
|
return this.#scanUntilComparison(this.#getRootNode(start, global).querySelectorAll(match), start, Node.DOCUMENT_POSITION_PRECEDING);
|
|
1810
1809
|
}
|
|
@@ -2060,16 +2059,18 @@ var htmx = (() => {
|
|
|
2060
2059
|
|
|
2061
2060
|
#handleStatusCodes(ctx) {
|
|
2062
2061
|
let status = ctx.response.raw.status;
|
|
2063
|
-
|
|
2064
|
-
ctx.swap = "none";
|
|
2065
|
-
}
|
|
2062
|
+
let noSwapStrings = this.config.noSwap.map(x => x + "");
|
|
2066
2063
|
let str = status + ""
|
|
2067
2064
|
for (let pattern of [str, str.slice(0, 2) + 'x', str[0] + 'xx']) {
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
ctx.swap = swap
|
|
2065
|
+
if (noSwapStrings.includes(pattern)) {
|
|
2066
|
+
ctx.swap = "none";
|
|
2071
2067
|
return
|
|
2072
2068
|
}
|
|
2069
|
+
let statusValue = this.#attributeValue(ctx.sourceElement, "hx-status:" + pattern);
|
|
2070
|
+
if (statusValue) {
|
|
2071
|
+
Object.assign(ctx, this.#parseConfig(statusValue));
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
2073
2074
|
}
|
|
2074
2075
|
}
|
|
2075
2076
|
|
|
@@ -2083,7 +2084,7 @@ var htmx = (() => {
|
|
|
2083
2084
|
});
|
|
2084
2085
|
}
|
|
2085
2086
|
|
|
2086
|
-
async
|
|
2087
|
+
async #processTransitionQueue() {
|
|
2087
2088
|
if (this.#transitionQueue.length === 0 || this.#processingTransition) {
|
|
2088
2089
|
return;
|
|
2089
2090
|
}
|
|
@@ -2093,9 +2094,8 @@ var htmx = (() => {
|
|
|
2093
2094
|
|
|
2094
2095
|
try {
|
|
2095
2096
|
if (document.startViewTransition) {
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
await finished;
|
|
2097
|
+
this.#trigger(document, "htmx:before:viewTransition", {task})
|
|
2098
|
+
await document.startViewTransition(task).finished;
|
|
2099
2099
|
this.#trigger(document, "htmx:after:viewTransition", {task})
|
|
2100
2100
|
} else {
|
|
2101
2101
|
task();
|
|
@@ -2133,8 +2133,17 @@ var htmx = (() => {
|
|
|
2133
2133
|
return cssOrElement
|
|
2134
2134
|
}
|
|
2135
2135
|
}
|
|
2136
|
+
|
|
2137
|
+
#maybeAdjustMetaCharacter(string) {
|
|
2138
|
+
if (this.config.metaCharacter) {
|
|
2139
|
+
return string.replace(/:/g, this.config.metaCharacter);
|
|
2140
|
+
} else {
|
|
2141
|
+
return string;
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2136
2144
|
}
|
|
2137
2145
|
|
|
2138
2146
|
return new Htmx()
|
|
2139
2147
|
})()
|
|
2148
|
+
|
|
2140
2149
|
export default htmx
|