htmx.org 2.0.0-alpha2 → 2.0.0-beta1
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/ext/README.md +9 -0
- package/dist/ext/ajax-header.js +7 -0
- package/dist/ext/alpine-morph.js +16 -0
- package/dist/ext/class-tools.js +92 -0
- package/dist/ext/client-side-templates.js +96 -0
- package/dist/ext/debug.js +11 -0
- package/dist/ext/disable-element.js +18 -0
- package/dist/ext/event-header.js +37 -0
- package/dist/ext/head-support.js +141 -0
- package/dist/ext/include-vals.js +24 -0
- package/dist/ext/json-enc.js +12 -0
- package/dist/ext/loading-states.js +183 -0
- package/dist/ext/method-override.js +11 -0
- package/dist/ext/morphdom-swap.js +17 -0
- package/dist/ext/multi-swap.js +45 -0
- package/dist/ext/path-deps.js +60 -0
- package/dist/ext/path-params.js +11 -0
- package/dist/ext/preload.js +147 -0
- package/dist/ext/rails-method.js +10 -0
- package/dist/ext/remove-me.js +27 -0
- package/dist/ext/response-targets.js +130 -0
- package/dist/ext/restored.js +15 -0
- package/dist/ext/sse.js +369 -0
- package/dist/ext/ws.js +476 -0
- package/dist/htmx.amd.js +1508 -340
- package/dist/htmx.cjs.js +1508 -340
- package/dist/htmx.esm.js +1508 -340
- package/dist/htmx.js +1508 -340
- package/dist/htmx.min.js +1 -1
- package/dist/htmx.min.js.gz +0 -0
- package/package.json +3 -3
package/dist/htmx.amd.js
CHANGED
|
@@ -3,81 +3,300 @@ var htmx = (function() {
|
|
|
3
3
|
'use strict'
|
|
4
4
|
|
|
5
5
|
// Public API
|
|
6
|
-
//* * @type {import("./htmx").HtmxApi} */
|
|
7
6
|
const htmx = {
|
|
7
|
+
// Tsc madness here, assigning the functions directly results in an invalid TypeScript output, but reassigning is fine
|
|
8
8
|
/* Event processing */
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
/** @type {typeof onLoadHelper} */
|
|
10
|
+
onLoad: null,
|
|
11
|
+
/** @type {typeof processNode} */
|
|
12
|
+
process: null,
|
|
13
|
+
/** @type {typeof addEventListenerImpl} */
|
|
14
|
+
on: null,
|
|
15
|
+
/** @type {typeof removeEventListenerImpl} */
|
|
16
|
+
off: null,
|
|
17
|
+
/** @type {typeof triggerEvent} */
|
|
18
|
+
trigger: null,
|
|
19
|
+
/** @type {typeof ajaxHelper} */
|
|
20
|
+
ajax: null,
|
|
15
21
|
/* DOM querying helpers */
|
|
16
|
-
find
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
/** @type {typeof find} */
|
|
23
|
+
find: null,
|
|
24
|
+
/** @type {typeof findAll} */
|
|
25
|
+
findAll: null,
|
|
26
|
+
/** @type {typeof closest} */
|
|
27
|
+
closest: null,
|
|
28
|
+
/**
|
|
29
|
+
* Returns the input values that would resolve for a given element via the htmx value resolution mechanism
|
|
30
|
+
*
|
|
31
|
+
* @see https://htmx.org/api/#values
|
|
32
|
+
*
|
|
33
|
+
* @param {Element} elt the element to resolve values on
|
|
34
|
+
* @param {HttpVerb} type the request type (e.g. **get** or **post**) non-GET's will include the enclosing form of the element. Defaults to **post**
|
|
35
|
+
* @returns {Object}
|
|
36
|
+
*/
|
|
19
37
|
values: function(elt, type) {
|
|
20
38
|
const inputValues = getInputValues(elt, type || 'post')
|
|
21
39
|
return inputValues.values
|
|
22
40
|
},
|
|
23
41
|
/* DOM manipulation helpers */
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
42
|
+
/** @type {typeof removeElement} */
|
|
43
|
+
remove: null,
|
|
44
|
+
/** @type {typeof addClassToElement} */
|
|
45
|
+
addClass: null,
|
|
46
|
+
/** @type {typeof removeClassFromElement} */
|
|
47
|
+
removeClass: null,
|
|
48
|
+
/** @type {typeof toggleClassOnElement} */
|
|
49
|
+
toggleClass: null,
|
|
50
|
+
/** @type {typeof takeClassForElement} */
|
|
51
|
+
takeClass: null,
|
|
52
|
+
/** @type {typeof swap} */
|
|
53
|
+
swap: null,
|
|
30
54
|
/* Extension entrypoints */
|
|
31
|
-
defineExtension
|
|
32
|
-
|
|
55
|
+
/** @type {typeof defineExtension} */
|
|
56
|
+
defineExtension: null,
|
|
57
|
+
/** @type {typeof removeExtension} */
|
|
58
|
+
removeExtension: null,
|
|
59
|
+
/* Debugging */
|
|
60
|
+
/** @type {typeof logAll} */
|
|
61
|
+
logAll: null,
|
|
62
|
+
/** @type {typeof logNone} */
|
|
63
|
+
logNone: null,
|
|
33
64
|
/* Debugging */
|
|
34
|
-
|
|
35
|
-
|
|
65
|
+
/**
|
|
66
|
+
* The logger htmx uses to log with
|
|
67
|
+
*
|
|
68
|
+
* @see https://htmx.org/api/#logger
|
|
69
|
+
*/
|
|
36
70
|
logger: null,
|
|
71
|
+
/**
|
|
72
|
+
* A property holding the configuration htmx uses at runtime.
|
|
73
|
+
*
|
|
74
|
+
* Note that using a [meta tag](https://htmx.org/docs/#config) is the preferred mechanism for setting these properties.
|
|
75
|
+
*
|
|
76
|
+
* @see https://htmx.org/api/#config
|
|
77
|
+
*/
|
|
37
78
|
config: {
|
|
79
|
+
/**
|
|
80
|
+
* Whether to use history.
|
|
81
|
+
* @type boolean
|
|
82
|
+
* @default true
|
|
83
|
+
*/
|
|
38
84
|
historyEnabled: true,
|
|
85
|
+
/**
|
|
86
|
+
* The number of pages to keep in **localStorage** for history support.
|
|
87
|
+
* @type number
|
|
88
|
+
* @default 10
|
|
89
|
+
*/
|
|
39
90
|
historyCacheSize: 10,
|
|
91
|
+
/**
|
|
92
|
+
* @type boolean
|
|
93
|
+
* @default false
|
|
94
|
+
*/
|
|
40
95
|
refreshOnHistoryMiss: false,
|
|
96
|
+
/**
|
|
97
|
+
* The default swap style to use if **[hx-swap](https://htmx.org/attributes/hx-swap)** is omitted.
|
|
98
|
+
* @type HtmxSwapStyle
|
|
99
|
+
* @default 'innerHTML'
|
|
100
|
+
*/
|
|
41
101
|
defaultSwapStyle: 'innerHTML',
|
|
102
|
+
/**
|
|
103
|
+
* The default delay between receiving a response from the server and doing the swap.
|
|
104
|
+
* @type number
|
|
105
|
+
* @default 0
|
|
106
|
+
*/
|
|
42
107
|
defaultSwapDelay: 0,
|
|
108
|
+
/**
|
|
109
|
+
* The default delay between completing the content swap and settling attributes.
|
|
110
|
+
* @type number
|
|
111
|
+
* @default 20
|
|
112
|
+
*/
|
|
43
113
|
defaultSettleDelay: 20,
|
|
114
|
+
/**
|
|
115
|
+
* If true, htmx will inject a small amount of CSS into the page to make indicators invisible unless the **htmx-indicator** class is present.
|
|
116
|
+
* @type boolean
|
|
117
|
+
* @default true
|
|
118
|
+
*/
|
|
44
119
|
includeIndicatorStyles: true,
|
|
120
|
+
/**
|
|
121
|
+
* The class to place on indicators when a request is in flight.
|
|
122
|
+
* @type string
|
|
123
|
+
* @default 'htmx-indicator'
|
|
124
|
+
*/
|
|
45
125
|
indicatorClass: 'htmx-indicator',
|
|
126
|
+
/**
|
|
127
|
+
* The class to place on triggering elements when a request is in flight.
|
|
128
|
+
* @type string
|
|
129
|
+
* @default 'htmx-request'
|
|
130
|
+
*/
|
|
46
131
|
requestClass: 'htmx-request',
|
|
132
|
+
/**
|
|
133
|
+
* The class to temporarily place on elements that htmx has added to the DOM.
|
|
134
|
+
* @type string
|
|
135
|
+
* @default 'htmx-added'
|
|
136
|
+
*/
|
|
47
137
|
addedClass: 'htmx-added',
|
|
138
|
+
/**
|
|
139
|
+
* The class to place on target elements when htmx is in the settling phase.
|
|
140
|
+
* @type string
|
|
141
|
+
* @default 'htmx-settling'
|
|
142
|
+
*/
|
|
48
143
|
settlingClass: 'htmx-settling',
|
|
144
|
+
/**
|
|
145
|
+
* The class to place on target elements when htmx is in the swapping phase.
|
|
146
|
+
* @type string
|
|
147
|
+
* @default 'htmx-swapping'
|
|
148
|
+
*/
|
|
49
149
|
swappingClass: 'htmx-swapping',
|
|
150
|
+
/**
|
|
151
|
+
* Allows the use of eval-like functionality in htmx, to enable **hx-vars**, trigger conditions & script tag evaluation. Can be set to **false** for CSP compatibility.
|
|
152
|
+
* @type boolean
|
|
153
|
+
* @default true
|
|
154
|
+
*/
|
|
50
155
|
allowEval: true,
|
|
156
|
+
/**
|
|
157
|
+
* If set to false, disables the interpretation of script tags.
|
|
158
|
+
* @type boolean
|
|
159
|
+
* @default true
|
|
160
|
+
*/
|
|
51
161
|
allowScriptTags: true,
|
|
162
|
+
/**
|
|
163
|
+
* If set, the nonce will be added to inline scripts.
|
|
164
|
+
* @type string
|
|
165
|
+
* @default ''
|
|
166
|
+
*/
|
|
52
167
|
inlineScriptNonce: '',
|
|
168
|
+
/**
|
|
169
|
+
* The attributes to settle during the settling phase.
|
|
170
|
+
* @type string[]
|
|
171
|
+
* @default ['class', 'style', 'width', 'height']
|
|
172
|
+
*/
|
|
53
173
|
attributesToSettle: ['class', 'style', 'width', 'height'],
|
|
174
|
+
/**
|
|
175
|
+
* Allow cross-site Access-Control requests using credentials such as cookies, authorization headers or TLS client certificates.
|
|
176
|
+
* @type boolean
|
|
177
|
+
* @default false
|
|
178
|
+
*/
|
|
54
179
|
withCredentials: false,
|
|
180
|
+
/**
|
|
181
|
+
* @type number
|
|
182
|
+
* @default 0
|
|
183
|
+
*/
|
|
55
184
|
timeout: 0,
|
|
185
|
+
/**
|
|
186
|
+
* The default implementation of **getWebSocketReconnectDelay** for reconnecting after unexpected connection loss by the event code **Abnormal Closure**, **Service Restart** or **Try Again Later**.
|
|
187
|
+
* @type {'full-jitter' | ((retryCount:number) => number)}
|
|
188
|
+
* @default "full-jitter"
|
|
189
|
+
*/
|
|
56
190
|
wsReconnectDelay: 'full-jitter',
|
|
191
|
+
/**
|
|
192
|
+
* The type of binary data being received over the WebSocket connection
|
|
193
|
+
* @type BinaryType
|
|
194
|
+
* @default 'blob'
|
|
195
|
+
*/
|
|
57
196
|
wsBinaryType: 'blob',
|
|
197
|
+
/**
|
|
198
|
+
* @type string
|
|
199
|
+
* @default '[hx-disable], [data-hx-disable]'
|
|
200
|
+
*/
|
|
58
201
|
disableSelector: '[hx-disable], [data-hx-disable]',
|
|
202
|
+
/**
|
|
203
|
+
* @type {'auto' | 'instant' | 'smooth'}
|
|
204
|
+
* @default 'smooth'
|
|
205
|
+
*/
|
|
59
206
|
scrollBehavior: 'instant',
|
|
207
|
+
/**
|
|
208
|
+
* If the focused element should be scrolled into view.
|
|
209
|
+
* @type boolean
|
|
210
|
+
* @default false
|
|
211
|
+
*/
|
|
60
212
|
defaultFocusScroll: false,
|
|
213
|
+
/**
|
|
214
|
+
* If set to true htmx will include a cache-busting parameter in GET requests to avoid caching partial responses by the browser
|
|
215
|
+
* @type boolean
|
|
216
|
+
* @default false
|
|
217
|
+
*/
|
|
61
218
|
getCacheBusterParam: false,
|
|
219
|
+
/**
|
|
220
|
+
* If set to true, htmx will use the View Transition API when swapping in new content.
|
|
221
|
+
* @type boolean
|
|
222
|
+
* @default false
|
|
223
|
+
*/
|
|
62
224
|
globalViewTransitions: false,
|
|
225
|
+
/**
|
|
226
|
+
* htmx will format requests with these methods by encoding their parameters in the URL, not the request body
|
|
227
|
+
* @type {(HttpVerb)[]}
|
|
228
|
+
* @default ['get', 'delete']
|
|
229
|
+
*/
|
|
63
230
|
methodsThatUseUrlParams: ['get', 'delete'],
|
|
231
|
+
/**
|
|
232
|
+
* If set to true, disables htmx-based requests to non-origin hosts.
|
|
233
|
+
* @type boolean
|
|
234
|
+
* @default false
|
|
235
|
+
*/
|
|
64
236
|
selfRequestsOnly: true,
|
|
237
|
+
/**
|
|
238
|
+
* If set to true htmx will not update the title of the document when a title tag is found in new content
|
|
239
|
+
* @type boolean
|
|
240
|
+
* @default false
|
|
241
|
+
*/
|
|
65
242
|
ignoreTitle: false,
|
|
243
|
+
/**
|
|
244
|
+
* Whether the target of a boosted element is scrolled into the viewport.
|
|
245
|
+
* @type boolean
|
|
246
|
+
* @default true
|
|
247
|
+
*/
|
|
66
248
|
scrollIntoViewOnBoost: true,
|
|
249
|
+
/**
|
|
250
|
+
* The cache to store evaluated trigger specifications into.
|
|
251
|
+
* You may define a simple object to use a never-clearing cache, or implement your own system using a [proxy object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Proxy)
|
|
252
|
+
* @type {Object|null}
|
|
253
|
+
* @default null
|
|
254
|
+
*/
|
|
67
255
|
triggerSpecsCache: null,
|
|
256
|
+
/** @type boolean */
|
|
68
257
|
disableInheritance: false,
|
|
258
|
+
/** @type HtmxResponseHandlingConfig[] */
|
|
69
259
|
responseHandling: [
|
|
70
260
|
{ code: '204', swap: false },
|
|
71
261
|
{ code: '[23]..', swap: true },
|
|
72
262
|
{ code: '[45]..', swap: false, error: true }
|
|
73
|
-
]
|
|
263
|
+
],
|
|
264
|
+
/**
|
|
265
|
+
* Whether to process OOB swaps on elements that are nested within the main response element.
|
|
266
|
+
* @type boolean
|
|
267
|
+
* @default true
|
|
268
|
+
*/
|
|
269
|
+
allowNestedOobSwaps: true
|
|
74
270
|
},
|
|
75
|
-
parseInterval
|
|
76
|
-
|
|
271
|
+
/** @type {typeof parseInterval} */
|
|
272
|
+
parseInterval: null,
|
|
273
|
+
/** @type {typeof internalEval} */
|
|
274
|
+
_: null,
|
|
77
275
|
version: '2.0a'
|
|
78
276
|
}
|
|
277
|
+
// Tsc madness part 2
|
|
278
|
+
htmx.onLoad = onLoadHelper
|
|
279
|
+
htmx.process = processNode
|
|
280
|
+
htmx.on = addEventListenerImpl
|
|
281
|
+
htmx.off = removeEventListenerImpl
|
|
282
|
+
htmx.trigger = triggerEvent
|
|
283
|
+
htmx.ajax = ajaxHelper
|
|
284
|
+
htmx.find = find
|
|
285
|
+
htmx.findAll = findAll
|
|
286
|
+
htmx.closest = closest
|
|
287
|
+
htmx.remove = removeElement
|
|
288
|
+
htmx.addClass = addClassToElement
|
|
289
|
+
htmx.removeClass = removeClassFromElement
|
|
290
|
+
htmx.toggleClass = toggleClassOnElement
|
|
291
|
+
htmx.takeClass = takeClassForElement
|
|
292
|
+
htmx.swap = swap
|
|
293
|
+
htmx.defineExtension = defineExtension
|
|
294
|
+
htmx.removeExtension = removeExtension
|
|
295
|
+
htmx.logAll = logAll
|
|
296
|
+
htmx.logNone = logNone
|
|
297
|
+
htmx.parseInterval = parseInterval
|
|
298
|
+
htmx._ = internalEval
|
|
79
299
|
|
|
80
|
-
/** @type {import("./htmx").HtmxInternalApi} */
|
|
81
300
|
const internalAPI = {
|
|
82
301
|
addTriggerHandler,
|
|
83
302
|
bodyContains,
|
|
@@ -129,6 +348,16 @@ var htmx = (function() {
|
|
|
129
348
|
global ? 'gim' : 'im')
|
|
130
349
|
}
|
|
131
350
|
|
|
351
|
+
/**
|
|
352
|
+
* Parses an interval string consistent with the way htmx does. Useful for plugins that have timing-related attributes.
|
|
353
|
+
*
|
|
354
|
+
* Caution: Accepts an int followed by either **s** or **ms**. All other values use **parseFloat**
|
|
355
|
+
*
|
|
356
|
+
* @see https://htmx.org/api/#parseInterval
|
|
357
|
+
*
|
|
358
|
+
* @param {string} str timing string
|
|
359
|
+
* @returns {number|undefined}
|
|
360
|
+
*/
|
|
132
361
|
function parseInterval(str) {
|
|
133
362
|
if (str == undefined) {
|
|
134
363
|
return undefined
|
|
@@ -148,23 +377,28 @@ var htmx = (function() {
|
|
|
148
377
|
}
|
|
149
378
|
|
|
150
379
|
/**
|
|
151
|
-
* @param {
|
|
380
|
+
* @param {Node} elt
|
|
152
381
|
* @param {string} name
|
|
153
382
|
* @returns {(string | null)}
|
|
154
383
|
*/
|
|
155
384
|
function getRawAttribute(elt, name) {
|
|
156
|
-
return elt
|
|
385
|
+
return elt instanceof Element && elt.getAttribute(name)
|
|
157
386
|
}
|
|
158
387
|
|
|
388
|
+
/**
|
|
389
|
+
* @param {Element} elt
|
|
390
|
+
* @param {string} qualifiedName
|
|
391
|
+
* @returns {boolean}
|
|
392
|
+
*/
|
|
159
393
|
// resolve with both hx and data-hx prefixes
|
|
160
394
|
function hasAttribute(elt, qualifiedName) {
|
|
161
|
-
return elt.hasAttribute && (elt.hasAttribute(qualifiedName) ||
|
|
395
|
+
return !!elt.hasAttribute && (elt.hasAttribute(qualifiedName) ||
|
|
162
396
|
elt.hasAttribute('data-' + qualifiedName))
|
|
163
397
|
}
|
|
164
398
|
|
|
165
399
|
/**
|
|
166
400
|
*
|
|
167
|
-
* @param {
|
|
401
|
+
* @param {Node} elt
|
|
168
402
|
* @param {string} qualifiedName
|
|
169
403
|
* @returns {(string | null)}
|
|
170
404
|
*/
|
|
@@ -173,8 +407,8 @@ var htmx = (function() {
|
|
|
173
407
|
}
|
|
174
408
|
|
|
175
409
|
/**
|
|
176
|
-
* @param {
|
|
177
|
-
* @returns {
|
|
410
|
+
* @param {Node} elt
|
|
411
|
+
* @returns {Node | null}
|
|
178
412
|
*/
|
|
179
413
|
function parentElt(elt) {
|
|
180
414
|
const parent = elt.parentElement
|
|
@@ -190,16 +424,18 @@ var htmx = (function() {
|
|
|
190
424
|
}
|
|
191
425
|
|
|
192
426
|
/**
|
|
193
|
-
* @
|
|
427
|
+
* @param {Node} elt
|
|
428
|
+
* @param {boolean} global
|
|
429
|
+
* @returns {Node|Document}
|
|
194
430
|
*/
|
|
195
431
|
function getRootNode(elt, global) {
|
|
196
432
|
return elt.getRootNode ? elt.getRootNode({ composed: global }) : getDocument()
|
|
197
433
|
}
|
|
198
434
|
|
|
199
435
|
/**
|
|
200
|
-
* @param {
|
|
201
|
-
* @param {(e:
|
|
202
|
-
* @returns {
|
|
436
|
+
* @param {Node} elt
|
|
437
|
+
* @param {(e:Node) => boolean} condition
|
|
438
|
+
* @returns {Node | null}
|
|
203
439
|
*/
|
|
204
440
|
function getClosestMatch(elt, condition) {
|
|
205
441
|
while (elt && !condition(elt)) {
|
|
@@ -209,6 +445,12 @@ var htmx = (function() {
|
|
|
209
445
|
return elt || null
|
|
210
446
|
}
|
|
211
447
|
|
|
448
|
+
/**
|
|
449
|
+
* @param {Element} initialElement
|
|
450
|
+
* @param {Element} ancestor
|
|
451
|
+
* @param {string} attributeName
|
|
452
|
+
* @returns {string|null}
|
|
453
|
+
*/
|
|
212
454
|
function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName) {
|
|
213
455
|
const attributeValue = getAttributeValue(ancestor, attributeName)
|
|
214
456
|
const disinherit = getAttributeValue(ancestor, 'hx-disinherit')
|
|
@@ -229,14 +471,14 @@ var htmx = (function() {
|
|
|
229
471
|
}
|
|
230
472
|
|
|
231
473
|
/**
|
|
232
|
-
* @param {
|
|
474
|
+
* @param {Element} elt
|
|
233
475
|
* @param {string} attributeName
|
|
234
476
|
* @returns {string | null}
|
|
235
477
|
*/
|
|
236
478
|
function getClosestAttributeValue(elt, attributeName) {
|
|
237
479
|
let closestAttr = null
|
|
238
480
|
getClosestMatch(elt, function(e) {
|
|
239
|
-
return closestAttr = getAttributeValueWithDisinheritance(elt, e, attributeName)
|
|
481
|
+
return !!(closestAttr = getAttributeValueWithDisinheritance(elt, asElement(e), attributeName))
|
|
240
482
|
})
|
|
241
483
|
if (closestAttr !== 'unset') {
|
|
242
484
|
return closestAttr
|
|
@@ -244,15 +486,15 @@ var htmx = (function() {
|
|
|
244
486
|
}
|
|
245
487
|
|
|
246
488
|
/**
|
|
247
|
-
* @param {
|
|
489
|
+
* @param {Node} elt
|
|
248
490
|
* @param {string} selector
|
|
249
491
|
* @returns {boolean}
|
|
250
492
|
*/
|
|
251
493
|
function matches(elt, selector) {
|
|
252
494
|
// @ts-ignore: non-standard properties for browser compatibility
|
|
253
495
|
// noinspection JSUnresolvedVariable
|
|
254
|
-
const matchesFunction = elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector
|
|
255
|
-
return matchesFunction && matchesFunction.call(elt, selector)
|
|
496
|
+
const matchesFunction = elt instanceof Element && (elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector)
|
|
497
|
+
return !!matchesFunction && matchesFunction.call(elt, selector)
|
|
256
498
|
}
|
|
257
499
|
|
|
258
500
|
/**
|
|
@@ -270,9 +512,7 @@ var htmx = (function() {
|
|
|
270
512
|
}
|
|
271
513
|
|
|
272
514
|
/**
|
|
273
|
-
*
|
|
274
515
|
* @param {string} resp
|
|
275
|
-
* @param {number} depth
|
|
276
516
|
* @returns {Document}
|
|
277
517
|
*/
|
|
278
518
|
function parseHTML(resp) {
|
|
@@ -280,12 +520,20 @@ var htmx = (function() {
|
|
|
280
520
|
return parser.parseFromString(resp, 'text/html')
|
|
281
521
|
}
|
|
282
522
|
|
|
523
|
+
/**
|
|
524
|
+
* @param {DocumentFragment} fragment
|
|
525
|
+
* @param {Node} elt
|
|
526
|
+
*/
|
|
283
527
|
function takeChildrenFor(fragment, elt) {
|
|
284
528
|
while (elt.childNodes.length > 0) {
|
|
285
529
|
fragment.append(elt.childNodes[0])
|
|
286
530
|
}
|
|
287
531
|
}
|
|
288
532
|
|
|
533
|
+
/**
|
|
534
|
+
* @param {HTMLScriptElement} script
|
|
535
|
+
* @returns {HTMLScriptElement}
|
|
536
|
+
*/
|
|
289
537
|
function duplicateScript(script) {
|
|
290
538
|
const newScript = getDocument().createElement('script')
|
|
291
539
|
forEach(script.attributes, function(attr) {
|
|
@@ -299,16 +547,23 @@ var htmx = (function() {
|
|
|
299
547
|
return newScript
|
|
300
548
|
}
|
|
301
549
|
|
|
550
|
+
/**
|
|
551
|
+
* @param {HTMLScriptElement} script
|
|
552
|
+
* @returns {boolean}
|
|
553
|
+
*/
|
|
302
554
|
function isJavaScriptScriptNode(script) {
|
|
303
555
|
return script.matches('script') && (script.type === 'text/javascript' || script.type === 'module' || script.type === '')
|
|
304
556
|
}
|
|
305
557
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
558
|
+
/**
|
|
559
|
+
* we have to make new copies of script tags that we are going to insert because
|
|
560
|
+
* SOME browsers (not saying who, but it involves an element and an animal) don't
|
|
561
|
+
* execute scripts created in <template> tags when they are inserted into the DOM
|
|
562
|
+
* and all the others do lmao
|
|
563
|
+
* @param {DocumentFragment} fragment
|
|
564
|
+
*/
|
|
310
565
|
function normalizeScriptTags(fragment) {
|
|
311
|
-
Array.from(fragment.querySelectorAll('script')).forEach((script) => {
|
|
566
|
+
Array.from(fragment.querySelectorAll('script')).forEach(/** @param {HTMLScriptElement} script */ (script) => {
|
|
312
567
|
if (isJavaScriptScriptNode(script)) {
|
|
313
568
|
const newScript = duplicateScript(script)
|
|
314
569
|
const parent = script.parentNode
|
|
@@ -324,31 +579,37 @@ var htmx = (function() {
|
|
|
324
579
|
}
|
|
325
580
|
|
|
326
581
|
/**
|
|
327
|
-
* @
|
|
328
|
-
* @
|
|
582
|
+
* @typedef {DocumentFragment & {title?: string}} DocumentFragmentWithTitle
|
|
583
|
+
* @description a document fragment representing the response HTML, including
|
|
329
584
|
* a `title` property for any title information found
|
|
330
585
|
*/
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* @param {string} response HTML
|
|
589
|
+
* @returns {DocumentFragmentWithTitle}
|
|
590
|
+
*/
|
|
331
591
|
function makeFragment(response) {
|
|
332
592
|
// strip head tag to determine shape of response we are dealing with
|
|
333
593
|
const responseWithNoHead = response.replace(HEAD_TAG_REGEX, '')
|
|
334
594
|
const startTag = getStartTag(responseWithNoHead)
|
|
335
|
-
|
|
595
|
+
/** @type DocumentFragmentWithTitle */
|
|
596
|
+
let fragment
|
|
336
597
|
if (startTag === 'html') {
|
|
337
598
|
// if it is a full document, parse it and return the body
|
|
338
|
-
fragment = new DocumentFragment()
|
|
599
|
+
fragment = /** @type DocumentFragmentWithTitle */ (new DocumentFragment())
|
|
339
600
|
const doc = parseHTML(response)
|
|
340
601
|
takeChildrenFor(fragment, doc.body)
|
|
341
602
|
fragment.title = doc.title
|
|
342
603
|
} else if (startTag === 'body') {
|
|
343
604
|
// parse body w/o wrapping in template
|
|
344
|
-
fragment = new DocumentFragment()
|
|
605
|
+
fragment = /** @type DocumentFragmentWithTitle */ (new DocumentFragment())
|
|
345
606
|
const doc = parseHTML(responseWithNoHead)
|
|
346
607
|
takeChildrenFor(fragment, doc.body)
|
|
347
608
|
fragment.title = doc.title
|
|
348
609
|
} else {
|
|
349
610
|
// otherwise we have non-body partial HTML content, so wrap it in a template to maximize parsing flexibility
|
|
350
611
|
const doc = parseHTML('<body><template class="internal-htmx-wrapper">' + responseWithNoHead + '</template></body>')
|
|
351
|
-
fragment = doc.querySelector('template').content
|
|
612
|
+
fragment = /** @type DocumentFragmentWithTitle */ (doc.querySelector('template').content)
|
|
352
613
|
// extract title into fragment for later processing
|
|
353
614
|
fragment.title = doc.title
|
|
354
615
|
|
|
@@ -393,7 +654,7 @@ var htmx = (function() {
|
|
|
393
654
|
* @returns {o is Function}
|
|
394
655
|
*/
|
|
395
656
|
function isFunction(o) {
|
|
396
|
-
return
|
|
657
|
+
return typeof o === 'function'
|
|
397
658
|
}
|
|
398
659
|
|
|
399
660
|
/**
|
|
@@ -404,10 +665,51 @@ var htmx = (function() {
|
|
|
404
665
|
return isType(o, 'Object')
|
|
405
666
|
}
|
|
406
667
|
|
|
668
|
+
/**
|
|
669
|
+
* @typedef {Object} OnHandler
|
|
670
|
+
* @property {(keyof HTMLElementEventMap)|string} event
|
|
671
|
+
* @property {EventListener} listener
|
|
672
|
+
*/
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* @typedef {Object} ListenerInfo
|
|
676
|
+
* @property {string} trigger
|
|
677
|
+
* @property {EventListener} listener
|
|
678
|
+
* @property {EventTarget} on
|
|
679
|
+
*/
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* @typedef {Object} HtmxNodeInternalData
|
|
683
|
+
* Element data
|
|
684
|
+
* @property {number} [initHash]
|
|
685
|
+
* @property {boolean} [boosted]
|
|
686
|
+
* @property {OnHandler[]} [onHandlers]
|
|
687
|
+
* @property {number} [timeout]
|
|
688
|
+
* @property {ListenerInfo[]} [listenerInfos]
|
|
689
|
+
* @property {boolean} [cancelled]
|
|
690
|
+
* @property {boolean} [triggeredOnce]
|
|
691
|
+
* @property {number} [delayed]
|
|
692
|
+
* @property {number|null} [throttle]
|
|
693
|
+
* @property {string} [lastValue]
|
|
694
|
+
* @property {boolean} [loaded]
|
|
695
|
+
* @property {string} [path]
|
|
696
|
+
* @property {string} [verb]
|
|
697
|
+
* @property {boolean} [polling]
|
|
698
|
+
* @property {HTMLButtonElement|HTMLInputElement|null} [lastButtonClicked]
|
|
699
|
+
* @property {number} [requestCount]
|
|
700
|
+
* @property {XMLHttpRequest} [xhr]
|
|
701
|
+
* @property {(() => void)[]} [queuedRequests]
|
|
702
|
+
* @property {boolean} [abortable]
|
|
703
|
+
*
|
|
704
|
+
* Event data
|
|
705
|
+
* @property {HtmxTriggerSpecification} [triggerSpec]
|
|
706
|
+
* @property {EventTarget[]} [handledFor]
|
|
707
|
+
*/
|
|
708
|
+
|
|
407
709
|
/**
|
|
408
710
|
* getInternalData retrieves "private" data stored by htmx within an element
|
|
409
|
-
* @param {
|
|
410
|
-
* @returns {
|
|
711
|
+
* @param {EventTarget|Event} elt
|
|
712
|
+
* @returns {HtmxNodeInternalData}
|
|
411
713
|
*/
|
|
412
714
|
function getInternalData(elt) {
|
|
413
715
|
const dataProp = 'htmx-internal-data'
|
|
@@ -420,8 +722,9 @@ var htmx = (function() {
|
|
|
420
722
|
|
|
421
723
|
/**
|
|
422
724
|
* toArray converts an ArrayLike object into a real array.
|
|
423
|
-
* @
|
|
424
|
-
* @
|
|
725
|
+
* @template T
|
|
726
|
+
* @param {ArrayLike<T>} arr
|
|
727
|
+
* @returns {T[]}
|
|
425
728
|
*/
|
|
426
729
|
function toArray(arr) {
|
|
427
730
|
const returnArr = []
|
|
@@ -435,13 +738,8 @@ var htmx = (function() {
|
|
|
435
738
|
|
|
436
739
|
/**
|
|
437
740
|
* @template T
|
|
438
|
-
* @
|
|
439
|
-
* @param {T}
|
|
440
|
-
*/
|
|
441
|
-
/**
|
|
442
|
-
* @template T
|
|
443
|
-
* @param {{[index: number]: T, length: number}} arr
|
|
444
|
-
* @param {forEachCallback<T>} func
|
|
741
|
+
* @param {T[]|NamedNodeMap|HTMLCollection|HTMLFormControlsCollection|ArrayLike<T>} arr
|
|
742
|
+
* @param {(T) => void} func
|
|
445
743
|
*/
|
|
446
744
|
function forEach(arr, func) {
|
|
447
745
|
if (arr) {
|
|
@@ -451,6 +749,10 @@ var htmx = (function() {
|
|
|
451
749
|
}
|
|
452
750
|
}
|
|
453
751
|
|
|
752
|
+
/**
|
|
753
|
+
* @param {Element} el
|
|
754
|
+
* @returns {boolean}
|
|
755
|
+
*/
|
|
454
756
|
function isScrolledIntoView(el) {
|
|
455
757
|
const rect = el.getBoundingClientRect()
|
|
456
758
|
const elemTop = rect.top
|
|
@@ -458,35 +760,52 @@ var htmx = (function() {
|
|
|
458
760
|
return elemTop < window.innerHeight && elemBottom >= 0
|
|
459
761
|
}
|
|
460
762
|
|
|
763
|
+
/**
|
|
764
|
+
* @param {Node} elt
|
|
765
|
+
* @returns {boolean}
|
|
766
|
+
*/
|
|
461
767
|
function bodyContains(elt) {
|
|
462
768
|
// IE Fix
|
|
463
|
-
|
|
464
|
-
|
|
769
|
+
const rootNode = elt.getRootNode && elt.getRootNode()
|
|
770
|
+
if (rootNode && rootNode instanceof window.ShadowRoot) {
|
|
771
|
+
return getDocument().body.contains(rootNode.host)
|
|
465
772
|
} else {
|
|
466
773
|
return getDocument().body.contains(elt)
|
|
467
774
|
}
|
|
468
775
|
}
|
|
469
776
|
|
|
777
|
+
/**
|
|
778
|
+
* @param {string} trigger
|
|
779
|
+
* @returns {string[]}
|
|
780
|
+
*/
|
|
470
781
|
function splitOnWhitespace(trigger) {
|
|
471
782
|
return trigger.trim().split(/\s+/)
|
|
472
783
|
}
|
|
473
784
|
|
|
474
785
|
/**
|
|
475
|
-
* mergeObjects takes all
|
|
786
|
+
* mergeObjects takes all the keys from
|
|
476
787
|
* obj2 and duplicates them into obj1
|
|
477
|
-
* @
|
|
478
|
-
* @
|
|
479
|
-
* @
|
|
788
|
+
* @template T1
|
|
789
|
+
* @template T2
|
|
790
|
+
* @param {T1} obj1
|
|
791
|
+
* @param {T2} obj2
|
|
792
|
+
* @returns {T1 & T2}
|
|
480
793
|
*/
|
|
481
794
|
function mergeObjects(obj1, obj2) {
|
|
482
795
|
for (const key in obj2) {
|
|
483
796
|
if (obj2.hasOwnProperty(key)) {
|
|
797
|
+
// @ts-ignore tsc doesn't seem to properly handle types merging
|
|
484
798
|
obj1[key] = obj2[key]
|
|
485
799
|
}
|
|
486
800
|
}
|
|
801
|
+
// @ts-ignore tsc doesn't seem to properly handle types merging
|
|
487
802
|
return obj1
|
|
488
803
|
}
|
|
489
804
|
|
|
805
|
+
/**
|
|
806
|
+
* @param {string} jString
|
|
807
|
+
* @returns {any|null}
|
|
808
|
+
*/
|
|
490
809
|
function parseJSON(jString) {
|
|
491
810
|
try {
|
|
492
811
|
return JSON.parse(jString)
|
|
@@ -496,6 +815,9 @@ var htmx = (function() {
|
|
|
496
815
|
}
|
|
497
816
|
}
|
|
498
817
|
|
|
818
|
+
/**
|
|
819
|
+
* @returns {boolean}
|
|
820
|
+
*/
|
|
499
821
|
function canAccessLocalStorage() {
|
|
500
822
|
const test = 'htmx:localStorageTest'
|
|
501
823
|
try {
|
|
@@ -507,6 +829,10 @@ var htmx = (function() {
|
|
|
507
829
|
}
|
|
508
830
|
}
|
|
509
831
|
|
|
832
|
+
/**
|
|
833
|
+
* @param {string} path
|
|
834
|
+
* @returns {string}
|
|
835
|
+
*/
|
|
510
836
|
function normalizePath(path) {
|
|
511
837
|
try {
|
|
512
838
|
const url = new URL(path)
|
|
@@ -528,19 +854,36 @@ var htmx = (function() {
|
|
|
528
854
|
// public API
|
|
529
855
|
//= =========================================================================================
|
|
530
856
|
|
|
857
|
+
/**
|
|
858
|
+
* @param {string} str
|
|
859
|
+
* @returns {any}
|
|
860
|
+
*/
|
|
531
861
|
function internalEval(str) {
|
|
532
862
|
return maybeEval(getDocument().body, function() {
|
|
533
863
|
return eval(str)
|
|
534
864
|
})
|
|
535
865
|
}
|
|
536
866
|
|
|
867
|
+
/**
|
|
868
|
+
* Adds a callback for the **htmx:load** event. This can be used to process new content, for example initializing the content with a javascript library
|
|
869
|
+
*
|
|
870
|
+
* @see https://htmx.org/api/#onLoad
|
|
871
|
+
*
|
|
872
|
+
* @param {(elt: Node) => void} callback the callback to call on newly loaded content
|
|
873
|
+
* @returns {EventListener}
|
|
874
|
+
*/
|
|
537
875
|
function onLoadHelper(callback) {
|
|
538
|
-
const value = htmx.on('htmx:load', function(evt) {
|
|
876
|
+
const value = htmx.on('htmx:load', /** @param {CustomEvent} evt */ function(evt) {
|
|
539
877
|
callback(evt.detail.elt)
|
|
540
878
|
})
|
|
541
879
|
return value
|
|
542
880
|
}
|
|
543
881
|
|
|
882
|
+
/**
|
|
883
|
+
* Log all htmx events, useful for debugging.
|
|
884
|
+
*
|
|
885
|
+
* @see https://htmx.org/api/#logAll
|
|
886
|
+
*/
|
|
544
887
|
function logAll() {
|
|
545
888
|
htmx.logger = function(elt, event, data) {
|
|
546
889
|
if (console) {
|
|
@@ -553,26 +896,59 @@ var htmx = (function() {
|
|
|
553
896
|
htmx.logger = null
|
|
554
897
|
}
|
|
555
898
|
|
|
899
|
+
/**
|
|
900
|
+
* Finds an element matching the selector
|
|
901
|
+
*
|
|
902
|
+
* @see https://htmx.org/api/#find
|
|
903
|
+
*
|
|
904
|
+
* @param {ParentNode|string} eltOrSelector the root element to find the matching element in, inclusive | the selector to match
|
|
905
|
+
* @param {string} [selector] the selector to match
|
|
906
|
+
* @returns {Element|null}
|
|
907
|
+
*/
|
|
556
908
|
function find(eltOrSelector, selector) {
|
|
557
|
-
if (
|
|
909
|
+
if (typeof eltOrSelector !== 'string') {
|
|
558
910
|
return eltOrSelector.querySelector(selector)
|
|
559
911
|
} else {
|
|
560
912
|
return find(getDocument(), eltOrSelector)
|
|
561
913
|
}
|
|
562
914
|
}
|
|
563
915
|
|
|
916
|
+
/**
|
|
917
|
+
* Finds all elements matching the selector
|
|
918
|
+
*
|
|
919
|
+
* @see https://htmx.org/api/#findAll
|
|
920
|
+
*
|
|
921
|
+
* @param {ParentNode|string} eltOrSelector the root element to find the matching elements in, inclusive | the selector to match
|
|
922
|
+
* @param {string} [selector] the selector to match
|
|
923
|
+
* @returns {NodeListOf<Element>}
|
|
924
|
+
*/
|
|
564
925
|
function findAll(eltOrSelector, selector) {
|
|
565
|
-
if (
|
|
926
|
+
if (typeof eltOrSelector !== 'string') {
|
|
566
927
|
return eltOrSelector.querySelectorAll(selector)
|
|
567
928
|
} else {
|
|
568
929
|
return findAll(getDocument(), eltOrSelector)
|
|
569
930
|
}
|
|
570
931
|
}
|
|
571
932
|
|
|
933
|
+
/**
|
|
934
|
+
* @returns Window
|
|
935
|
+
*/
|
|
936
|
+
function getWindow() {
|
|
937
|
+
return window
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* Removes an element from the DOM
|
|
942
|
+
*
|
|
943
|
+
* @see https://htmx.org/api/#remove
|
|
944
|
+
*
|
|
945
|
+
* @param {Node} elt
|
|
946
|
+
* @param {number} [delay]
|
|
947
|
+
*/
|
|
572
948
|
function removeElement(elt, delay) {
|
|
573
949
|
elt = resolveTarget(elt)
|
|
574
950
|
if (delay) {
|
|
575
|
-
setTimeout(function() {
|
|
951
|
+
getWindow().setTimeout(function() {
|
|
576
952
|
removeElement(elt)
|
|
577
953
|
elt = null
|
|
578
954
|
}, delay)
|
|
@@ -581,10 +957,54 @@ var htmx = (function() {
|
|
|
581
957
|
}
|
|
582
958
|
}
|
|
583
959
|
|
|
960
|
+
/**
|
|
961
|
+
* @param {any} elt
|
|
962
|
+
* @return {Element|null}
|
|
963
|
+
*/
|
|
964
|
+
function asElement(elt) {
|
|
965
|
+
return elt instanceof Element ? elt : null
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* @param {any} elt
|
|
970
|
+
* @return {HTMLElement|null}
|
|
971
|
+
*/
|
|
972
|
+
function asHtmlElement(elt) {
|
|
973
|
+
return elt instanceof HTMLElement ? elt : null
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* @param {any} value
|
|
978
|
+
* @return {string|null}
|
|
979
|
+
*/
|
|
980
|
+
function asString(value) {
|
|
981
|
+
return typeof value === 'string' ? value : null
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* @param {EventTarget} elt
|
|
986
|
+
* @return {ParentNode|null}
|
|
987
|
+
*/
|
|
988
|
+
function asParentNode(elt) {
|
|
989
|
+
return elt instanceof Element || elt instanceof Document || elt instanceof DocumentFragment ? elt : null
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/**
|
|
993
|
+
* This method adds a class to the given element.
|
|
994
|
+
*
|
|
995
|
+
* @see https://htmx.org/api/#addClass
|
|
996
|
+
*
|
|
997
|
+
* @param {Element|string} elt the element to add the class to
|
|
998
|
+
* @param {string} clazz the class to add
|
|
999
|
+
* @param {number} [delay] the delay (in milliseconds) before class is added
|
|
1000
|
+
*/
|
|
584
1001
|
function addClassToElement(elt, clazz, delay) {
|
|
585
|
-
elt = resolveTarget(elt)
|
|
1002
|
+
elt = asElement(resolveTarget(elt))
|
|
1003
|
+
if (!elt) {
|
|
1004
|
+
return
|
|
1005
|
+
}
|
|
586
1006
|
if (delay) {
|
|
587
|
-
setTimeout(function() {
|
|
1007
|
+
getWindow().setTimeout(function() {
|
|
588
1008
|
addClassToElement(elt, clazz)
|
|
589
1009
|
elt = null
|
|
590
1010
|
}, delay)
|
|
@@ -593,10 +1013,22 @@ var htmx = (function() {
|
|
|
593
1013
|
}
|
|
594
1014
|
}
|
|
595
1015
|
|
|
596
|
-
|
|
597
|
-
|
|
1016
|
+
/**
|
|
1017
|
+
* Removes a class from the given element
|
|
1018
|
+
*
|
|
1019
|
+
* @see https://htmx.org/api/#removeClass
|
|
1020
|
+
*
|
|
1021
|
+
* @param {Node|string} node element to remove the class from
|
|
1022
|
+
* @param {string} clazz the class to remove
|
|
1023
|
+
* @param {number} [delay] the delay (in milliseconds before class is removed)
|
|
1024
|
+
*/
|
|
1025
|
+
function removeClassFromElement(node, clazz, delay) {
|
|
1026
|
+
let elt = asElement(resolveTarget(node))
|
|
1027
|
+
if (!elt) {
|
|
1028
|
+
return
|
|
1029
|
+
}
|
|
598
1030
|
if (delay) {
|
|
599
|
-
setTimeout(function() {
|
|
1031
|
+
getWindow().setTimeout(function() {
|
|
600
1032
|
removeClassFromElement(elt, clazz)
|
|
601
1033
|
elt = null
|
|
602
1034
|
}, delay)
|
|
@@ -611,22 +1043,47 @@ var htmx = (function() {
|
|
|
611
1043
|
}
|
|
612
1044
|
}
|
|
613
1045
|
|
|
1046
|
+
/**
|
|
1047
|
+
* Toggles the given class on an element
|
|
1048
|
+
*
|
|
1049
|
+
* @see https://htmx.org/api/#toggleClass
|
|
1050
|
+
*
|
|
1051
|
+
* @param {Element|string} elt the element to toggle the class on
|
|
1052
|
+
* @param {string} clazz the class to toggle
|
|
1053
|
+
*/
|
|
614
1054
|
function toggleClassOnElement(elt, clazz) {
|
|
615
1055
|
elt = resolveTarget(elt)
|
|
616
1056
|
elt.classList.toggle(clazz)
|
|
617
1057
|
}
|
|
618
1058
|
|
|
1059
|
+
/**
|
|
1060
|
+
* Takes the given class from its siblings, so that among its siblings, only the given element will have the class.
|
|
1061
|
+
*
|
|
1062
|
+
* @see https://htmx.org/api/#takeClass
|
|
1063
|
+
*
|
|
1064
|
+
* @param {Node|string} elt the element that will take the class
|
|
1065
|
+
* @param {string} clazz the class to take
|
|
1066
|
+
*/
|
|
619
1067
|
function takeClassForElement(elt, clazz) {
|
|
620
1068
|
elt = resolveTarget(elt)
|
|
621
1069
|
forEach(elt.parentElement.children, function(child) {
|
|
622
1070
|
removeClassFromElement(child, clazz)
|
|
623
1071
|
})
|
|
624
|
-
addClassToElement(elt, clazz)
|
|
1072
|
+
addClassToElement(asElement(elt), clazz)
|
|
625
1073
|
}
|
|
626
1074
|
|
|
1075
|
+
/**
|
|
1076
|
+
* Finds the closest matching element in the given elements parentage, inclusive of the element
|
|
1077
|
+
*
|
|
1078
|
+
* @see https://htmx.org/api/#closest
|
|
1079
|
+
*
|
|
1080
|
+
* @param {Element|string} elt the element to find the selector from
|
|
1081
|
+
* @param {string} selector the selector to find
|
|
1082
|
+
* @returns {Element|null}
|
|
1083
|
+
*/
|
|
627
1084
|
function closest(elt, selector) {
|
|
628
|
-
elt = resolveTarget(elt)
|
|
629
|
-
if (elt.closest) {
|
|
1085
|
+
elt = asElement(resolveTarget(elt))
|
|
1086
|
+
if (elt && elt.closest) {
|
|
630
1087
|
return elt.closest(selector)
|
|
631
1088
|
} else {
|
|
632
1089
|
// TODO remove when IE goes away
|
|
@@ -635,19 +1092,33 @@ var htmx = (function() {
|
|
|
635
1092
|
return elt
|
|
636
1093
|
}
|
|
637
1094
|
}
|
|
638
|
-
while (elt = elt && parentElt(elt))
|
|
1095
|
+
while (elt = elt && asElement(parentElt(elt)))
|
|
639
1096
|
return null
|
|
640
1097
|
}
|
|
641
1098
|
}
|
|
642
1099
|
|
|
1100
|
+
/**
|
|
1101
|
+
* @param {string} str
|
|
1102
|
+
* @param {string} prefix
|
|
1103
|
+
* @returns {boolean}
|
|
1104
|
+
*/
|
|
643
1105
|
function startsWith(str, prefix) {
|
|
644
1106
|
return str.substring(0, prefix.length) === prefix
|
|
645
1107
|
}
|
|
646
1108
|
|
|
1109
|
+
/**
|
|
1110
|
+
* @param {string} str
|
|
1111
|
+
* @param {string} suffix
|
|
1112
|
+
* @returns {boolean}
|
|
1113
|
+
*/
|
|
647
1114
|
function endsWith(str, suffix) {
|
|
648
1115
|
return str.substring(str.length - suffix.length) === suffix
|
|
649
1116
|
}
|
|
650
1117
|
|
|
1118
|
+
/**
|
|
1119
|
+
* @param {string} selector
|
|
1120
|
+
* @returns {string}
|
|
1121
|
+
*/
|
|
651
1122
|
function normalizeSelector(selector) {
|
|
652
1123
|
const trimmedSelector = selector.trim()
|
|
653
1124
|
if (startsWith(trimmedSelector, '<') && endsWith(trimmedSelector, '/>')) {
|
|
@@ -657,17 +1128,24 @@ var htmx = (function() {
|
|
|
657
1128
|
}
|
|
658
1129
|
}
|
|
659
1130
|
|
|
1131
|
+
/**
|
|
1132
|
+
* @param {Node|Element|Document|string} elt
|
|
1133
|
+
* @param {string} selector
|
|
1134
|
+
* @param {boolean=} global
|
|
1135
|
+
* @returns {(Node|Window)[]}
|
|
1136
|
+
*/
|
|
660
1137
|
function querySelectorAllExt(elt, selector, global) {
|
|
1138
|
+
elt = resolveTarget(elt)
|
|
661
1139
|
if (selector.indexOf('closest ') === 0) {
|
|
662
|
-
return [closest(elt, normalizeSelector(selector.substr(8)))]
|
|
1140
|
+
return [closest(asElement(elt), normalizeSelector(selector.substr(8)))]
|
|
663
1141
|
} else if (selector.indexOf('find ') === 0) {
|
|
664
|
-
return [find(elt, normalizeSelector(selector.substr(5)))]
|
|
1142
|
+
return [find(asParentNode(elt), normalizeSelector(selector.substr(5)))]
|
|
665
1143
|
} else if (selector === 'next') {
|
|
666
|
-
return [elt.nextElementSibling]
|
|
1144
|
+
return [asElement(elt).nextElementSibling]
|
|
667
1145
|
} else if (selector.indexOf('next ') === 0) {
|
|
668
1146
|
return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)), !!global)]
|
|
669
1147
|
} else if (selector === 'previous') {
|
|
670
|
-
return [elt.previousElementSibling]
|
|
1148
|
+
return [asElement(elt).previousElementSibling]
|
|
671
1149
|
} else if (selector.indexOf('previous ') === 0) {
|
|
672
1150
|
return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)), !!global)]
|
|
673
1151
|
} else if (selector === 'document') {
|
|
@@ -681,12 +1159,18 @@ var htmx = (function() {
|
|
|
681
1159
|
} else if (selector.indexOf('global ') === 0) {
|
|
682
1160
|
return querySelectorAllExt(elt, selector.slice(7), true)
|
|
683
1161
|
} else {
|
|
684
|
-
return getRootNode(elt, !!global).querySelectorAll(normalizeSelector(selector))
|
|
1162
|
+
return toArray(asParentNode(getRootNode(elt, !!global)).querySelectorAll(normalizeSelector(selector)))
|
|
685
1163
|
}
|
|
686
1164
|
}
|
|
687
1165
|
|
|
1166
|
+
/**
|
|
1167
|
+
* @param {Node} start
|
|
1168
|
+
* @param {string} match
|
|
1169
|
+
* @param {boolean} global
|
|
1170
|
+
* @returns {Element}
|
|
1171
|
+
*/
|
|
688
1172
|
var scanForwardQuery = function(start, match, global) {
|
|
689
|
-
const results = getRootNode(start, global).querySelectorAll(match)
|
|
1173
|
+
const results = asParentNode(getRootNode(start, global)).querySelectorAll(match)
|
|
690
1174
|
for (let i = 0; i < results.length; i++) {
|
|
691
1175
|
const elt = results[i]
|
|
692
1176
|
if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_PRECEDING) {
|
|
@@ -695,8 +1179,14 @@ var htmx = (function() {
|
|
|
695
1179
|
}
|
|
696
1180
|
}
|
|
697
1181
|
|
|
1182
|
+
/**
|
|
1183
|
+
* @param {Node} start
|
|
1184
|
+
* @param {string} match
|
|
1185
|
+
* @param {boolean} global
|
|
1186
|
+
* @returns {Element}
|
|
1187
|
+
*/
|
|
698
1188
|
var scanBackwardsQuery = function(start, match, global) {
|
|
699
|
-
const results = getRootNode(start, global).querySelectorAll(match)
|
|
1189
|
+
const results = asParentNode(getRootNode(start, global)).querySelectorAll(match)
|
|
700
1190
|
for (let i = results.length - 1; i >= 0; i--) {
|
|
701
1191
|
const elt = results[i]
|
|
702
1192
|
if (elt.compareDocumentPosition(start) === Node.DOCUMENT_POSITION_FOLLOWING) {
|
|
@@ -705,8 +1195,13 @@ var htmx = (function() {
|
|
|
705
1195
|
}
|
|
706
1196
|
}
|
|
707
1197
|
|
|
1198
|
+
/**
|
|
1199
|
+
* @param {Node|string} eltOrSelector
|
|
1200
|
+
* @param {string=} selector
|
|
1201
|
+
* @returns {Node|Window}
|
|
1202
|
+
*/
|
|
708
1203
|
function querySelectorExt(eltOrSelector, selector) {
|
|
709
|
-
if (
|
|
1204
|
+
if (typeof eltOrSelector !== 'string') {
|
|
710
1205
|
return querySelectorAllExt(eltOrSelector, selector)[0]
|
|
711
1206
|
} else {
|
|
712
1207
|
return querySelectorAllExt(getDocument().body, eltOrSelector)[0]
|
|
@@ -714,36 +1209,62 @@ var htmx = (function() {
|
|
|
714
1209
|
}
|
|
715
1210
|
|
|
716
1211
|
/**
|
|
717
|
-
*
|
|
718
|
-
* @param {string
|
|
719
|
-
* @param {
|
|
720
|
-
* @returns {Element}
|
|
1212
|
+
* @template {EventTarget} T
|
|
1213
|
+
* @param {T|string} eltOrSelector
|
|
1214
|
+
* @param {T} [context]
|
|
1215
|
+
* @returns {Element|T|null}
|
|
721
1216
|
*/
|
|
722
|
-
function resolveTarget(
|
|
723
|
-
if (
|
|
724
|
-
return find(context || document,
|
|
1217
|
+
function resolveTarget(eltOrSelector, context) {
|
|
1218
|
+
if (typeof eltOrSelector === 'string') {
|
|
1219
|
+
return find(asParentNode(context) || document, eltOrSelector)
|
|
725
1220
|
} else {
|
|
726
|
-
|
|
727
|
-
return arg2
|
|
1221
|
+
return eltOrSelector
|
|
728
1222
|
}
|
|
729
1223
|
}
|
|
730
1224
|
|
|
1225
|
+
/**
|
|
1226
|
+
* @typedef {keyof HTMLElementEventMap|string} AnyEventName
|
|
1227
|
+
*/
|
|
1228
|
+
|
|
1229
|
+
/**
|
|
1230
|
+
* @typedef {Object} EventArgs
|
|
1231
|
+
* @property {EventTarget} target
|
|
1232
|
+
* @property {AnyEventName} event
|
|
1233
|
+
* @property {EventListener} listener
|
|
1234
|
+
*/
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* @param {EventTarget|AnyEventName} arg1
|
|
1238
|
+
* @param {AnyEventName|EventListener} arg2
|
|
1239
|
+
* @param {EventListener} [arg3]
|
|
1240
|
+
* @returns {EventArgs}
|
|
1241
|
+
*/
|
|
731
1242
|
function processEventArgs(arg1, arg2, arg3) {
|
|
732
1243
|
if (isFunction(arg2)) {
|
|
733
1244
|
return {
|
|
734
1245
|
target: getDocument().body,
|
|
735
|
-
event: arg1,
|
|
1246
|
+
event: asString(arg1),
|
|
736
1247
|
listener: arg2
|
|
737
1248
|
}
|
|
738
1249
|
} else {
|
|
739
1250
|
return {
|
|
740
1251
|
target: resolveTarget(arg1),
|
|
741
|
-
event: arg2,
|
|
1252
|
+
event: asString(arg2),
|
|
742
1253
|
listener: arg3
|
|
743
1254
|
}
|
|
744
1255
|
}
|
|
745
1256
|
}
|
|
746
1257
|
|
|
1258
|
+
/**
|
|
1259
|
+
* Adds an event listener to an element
|
|
1260
|
+
*
|
|
1261
|
+
* @see https://htmx.org/api/#on
|
|
1262
|
+
*
|
|
1263
|
+
* @param {EventTarget|string} arg1 the element to add the listener to | the event name to add the listener for
|
|
1264
|
+
* @param {string|EventListener} arg2 the event name to add the listener for | the listener to add
|
|
1265
|
+
* @param {EventListener} [arg3] the listener to add
|
|
1266
|
+
* @returns {EventListener}
|
|
1267
|
+
*/
|
|
747
1268
|
function addEventListenerImpl(arg1, arg2, arg3) {
|
|
748
1269
|
ready(function() {
|
|
749
1270
|
const eventArgs = processEventArgs(arg1, arg2, arg3)
|
|
@@ -753,6 +1274,16 @@ var htmx = (function() {
|
|
|
753
1274
|
return b ? arg2 : arg3
|
|
754
1275
|
}
|
|
755
1276
|
|
|
1277
|
+
/**
|
|
1278
|
+
* Removes an event listener from an element
|
|
1279
|
+
*
|
|
1280
|
+
* @see https://htmx.org/api/#off
|
|
1281
|
+
*
|
|
1282
|
+
* @param {EventTarget|string} arg1 the element to remove the listener from | the event name to remove the listener from
|
|
1283
|
+
* @param {string|EventListener} arg2 the event name to remove the listener from | the listener to remove
|
|
1284
|
+
* @param {EventListener} [arg3] the listener to remove
|
|
1285
|
+
* @returns {EventListener}
|
|
1286
|
+
*/
|
|
756
1287
|
function removeEventListenerImpl(arg1, arg2, arg3) {
|
|
757
1288
|
ready(function() {
|
|
758
1289
|
const eventArgs = processEventArgs(arg1, arg2, arg3)
|
|
@@ -766,6 +1297,11 @@ var htmx = (function() {
|
|
|
766
1297
|
//= ===================================================================
|
|
767
1298
|
|
|
768
1299
|
const DUMMY_ELT = getDocument().createElement('output') // dummy element for bad selectors
|
|
1300
|
+
/**
|
|
1301
|
+
* @param {Element} elt
|
|
1302
|
+
* @param {string} attrName
|
|
1303
|
+
* @returns {(Node|Window)[]}
|
|
1304
|
+
*/
|
|
769
1305
|
function findAttributeTargets(elt, attrName) {
|
|
770
1306
|
const attrTarget = getClosestAttributeValue(elt, attrName)
|
|
771
1307
|
if (attrTarget) {
|
|
@@ -783,12 +1319,21 @@ var htmx = (function() {
|
|
|
783
1319
|
}
|
|
784
1320
|
}
|
|
785
1321
|
|
|
1322
|
+
/**
|
|
1323
|
+
* @param {Element} elt
|
|
1324
|
+
* @param {string} attribute
|
|
1325
|
+
* @returns {Element|null}
|
|
1326
|
+
*/
|
|
786
1327
|
function findThisElement(elt, attribute) {
|
|
787
|
-
return getClosestMatch(elt, function(elt) {
|
|
788
|
-
return getAttributeValue(elt, attribute) != null
|
|
789
|
-
})
|
|
1328
|
+
return asElement(getClosestMatch(elt, function(elt) {
|
|
1329
|
+
return getAttributeValue(asElement(elt), attribute) != null
|
|
1330
|
+
}))
|
|
790
1331
|
}
|
|
791
1332
|
|
|
1333
|
+
/**
|
|
1334
|
+
* @param {Element} elt
|
|
1335
|
+
* @returns {Node|Window|null}
|
|
1336
|
+
*/
|
|
792
1337
|
function getTarget(elt) {
|
|
793
1338
|
const targetStr = getClosestAttributeValue(elt, 'hx-target')
|
|
794
1339
|
if (targetStr) {
|
|
@@ -807,6 +1352,10 @@ var htmx = (function() {
|
|
|
807
1352
|
}
|
|
808
1353
|
}
|
|
809
1354
|
|
|
1355
|
+
/**
|
|
1356
|
+
* @param {string} name
|
|
1357
|
+
* @returns {boolean}
|
|
1358
|
+
*/
|
|
810
1359
|
function shouldSettleAttribute(name) {
|
|
811
1360
|
const attributesToSettle = htmx.config.attributesToSettle
|
|
812
1361
|
for (let i = 0; i < attributesToSettle.length; i++) {
|
|
@@ -817,6 +1366,10 @@ var htmx = (function() {
|
|
|
817
1366
|
return false
|
|
818
1367
|
}
|
|
819
1368
|
|
|
1369
|
+
/**
|
|
1370
|
+
* @param {Element} mergeTo
|
|
1371
|
+
* @param {Element} mergeFrom
|
|
1372
|
+
*/
|
|
820
1373
|
function cloneAttributes(mergeTo, mergeFrom) {
|
|
821
1374
|
forEach(mergeTo.attributes, function(attr) {
|
|
822
1375
|
if (!mergeFrom.hasAttribute(attr.name) && shouldSettleAttribute(attr.name)) {
|
|
@@ -830,6 +1383,11 @@ var htmx = (function() {
|
|
|
830
1383
|
})
|
|
831
1384
|
}
|
|
832
1385
|
|
|
1386
|
+
/**
|
|
1387
|
+
* @param {HtmxSwapStyle} swapStyle
|
|
1388
|
+
* @param {Element} target
|
|
1389
|
+
* @returns {boolean}
|
|
1390
|
+
*/
|
|
833
1391
|
function isInlineSwap(swapStyle, target) {
|
|
834
1392
|
const extensions = getExtensions(target)
|
|
835
1393
|
for (let i = 0; i < extensions.length; i++) {
|
|
@@ -846,14 +1404,14 @@ var htmx = (function() {
|
|
|
846
1404
|
}
|
|
847
1405
|
|
|
848
1406
|
/**
|
|
849
|
-
*
|
|
850
1407
|
* @param {string} oobValue
|
|
851
1408
|
* @param {Element} oobElement
|
|
852
|
-
* @param {
|
|
1409
|
+
* @param {HtmxSettleInfo} settleInfo
|
|
853
1410
|
* @returns
|
|
854
1411
|
*/
|
|
855
1412
|
function oobSwap(oobValue, oobElement, settleInfo) {
|
|
856
1413
|
let selector = '#' + getRawAttribute(oobElement, 'id')
|
|
1414
|
+
/** @type HtmxSwapStyle */
|
|
857
1415
|
let swapStyle = 'outerHTML'
|
|
858
1416
|
if (oobValue === 'true') {
|
|
859
1417
|
// do nothing
|
|
@@ -874,7 +1432,7 @@ var htmx = (function() {
|
|
|
874
1432
|
fragment = getDocument().createDocumentFragment()
|
|
875
1433
|
fragment.appendChild(oobElementClone)
|
|
876
1434
|
if (!isInlineSwap(swapStyle, target)) {
|
|
877
|
-
fragment = oobElementClone // if this is not an inline swap, we use the content of the node, not the node itself
|
|
1435
|
+
fragment = asParentNode(oobElementClone) // if this is not an inline swap, we use the content of the node, not the node itself
|
|
878
1436
|
}
|
|
879
1437
|
|
|
880
1438
|
const beforeSwapDetails = { shouldSwap: true, target, fragment }
|
|
@@ -897,6 +1455,9 @@ var htmx = (function() {
|
|
|
897
1455
|
return oobValue
|
|
898
1456
|
}
|
|
899
1457
|
|
|
1458
|
+
/**
|
|
1459
|
+
* @param {DocumentFragment} fragment
|
|
1460
|
+
*/
|
|
900
1461
|
function handlePreservedElements(fragment) {
|
|
901
1462
|
forEach(findAll(fragment, '[hx-preserve], [data-hx-preserve]'), function(preservedElt) {
|
|
902
1463
|
const id = getAttributeValue(preservedElt, 'id')
|
|
@@ -907,14 +1468,20 @@ var htmx = (function() {
|
|
|
907
1468
|
})
|
|
908
1469
|
}
|
|
909
1470
|
|
|
1471
|
+
/**
|
|
1472
|
+
* @param {Node} parentNode
|
|
1473
|
+
* @param {ParentNode} fragment
|
|
1474
|
+
* @param {HtmxSettleInfo} settleInfo
|
|
1475
|
+
*/
|
|
910
1476
|
function handleAttributes(parentNode, fragment, settleInfo) {
|
|
911
1477
|
forEach(fragment.querySelectorAll('[id]'), function(newNode) {
|
|
912
1478
|
const id = getRawAttribute(newNode, 'id')
|
|
913
1479
|
if (id && id.length > 0) {
|
|
914
1480
|
const normalizedId = id.replace("'", "\\'")
|
|
915
1481
|
const normalizedTag = newNode.tagName.replace(':', '\\:')
|
|
916
|
-
const
|
|
917
|
-
|
|
1482
|
+
const parentElt = asParentNode(parentNode)
|
|
1483
|
+
const oldNode = parentElt && parentElt.querySelector(normalizedTag + "[id='" + normalizedId + "']")
|
|
1484
|
+
if (oldNode && oldNode !== parentElt) {
|
|
918
1485
|
const newAttributes = newNode.cloneNode()
|
|
919
1486
|
cloneAttributes(newNode, oldNode)
|
|
920
1487
|
settleInfo.tasks.push(function() {
|
|
@@ -925,28 +1492,41 @@ var htmx = (function() {
|
|
|
925
1492
|
})
|
|
926
1493
|
}
|
|
927
1494
|
|
|
1495
|
+
/**
|
|
1496
|
+
* @param {Node} child
|
|
1497
|
+
* @returns {HtmxSettleTask}
|
|
1498
|
+
*/
|
|
928
1499
|
function makeAjaxLoadTask(child) {
|
|
929
1500
|
return function() {
|
|
930
1501
|
removeClassFromElement(child, htmx.config.addedClass)
|
|
931
|
-
processNode(child)
|
|
932
|
-
processFocus(child)
|
|
1502
|
+
processNode(asElement(child))
|
|
1503
|
+
processFocus(asParentNode(child))
|
|
933
1504
|
triggerEvent(child, 'htmx:load')
|
|
934
1505
|
}
|
|
935
1506
|
}
|
|
936
1507
|
|
|
1508
|
+
/**
|
|
1509
|
+
* @param {ParentNode} child
|
|
1510
|
+
*/
|
|
937
1511
|
function processFocus(child) {
|
|
938
1512
|
const autofocus = '[autofocus]'
|
|
939
|
-
const autoFocusedElt = matches(child, autofocus) ? child : child.querySelector(autofocus)
|
|
1513
|
+
const autoFocusedElt = asHtmlElement(matches(child, autofocus) ? child : child.querySelector(autofocus))
|
|
940
1514
|
if (autoFocusedElt != null) {
|
|
941
1515
|
autoFocusedElt.focus()
|
|
942
1516
|
}
|
|
943
1517
|
}
|
|
944
1518
|
|
|
1519
|
+
/**
|
|
1520
|
+
* @param {Node} parentNode
|
|
1521
|
+
* @param {Node} insertBefore
|
|
1522
|
+
* @param {ParentNode} fragment
|
|
1523
|
+
* @param {HtmxSettleInfo} settleInfo
|
|
1524
|
+
*/
|
|
945
1525
|
function insertNodesBefore(parentNode, insertBefore, fragment, settleInfo) {
|
|
946
1526
|
handleAttributes(parentNode, fragment, settleInfo)
|
|
947
1527
|
while (fragment.childNodes.length > 0) {
|
|
948
1528
|
const child = fragment.firstChild
|
|
949
|
-
addClassToElement(child, htmx.config.addedClass)
|
|
1529
|
+
addClassToElement(asElement(child), htmx.config.addedClass)
|
|
950
1530
|
parentNode.insertBefore(child, insertBefore)
|
|
951
1531
|
if (child.nodeType !== Node.TEXT_NODE && child.nodeType !== Node.COMMENT_NODE) {
|
|
952
1532
|
settleInfo.tasks.push(makeAjaxLoadTask(child))
|
|
@@ -954,8 +1534,13 @@ var htmx = (function() {
|
|
|
954
1534
|
}
|
|
955
1535
|
}
|
|
956
1536
|
|
|
957
|
-
|
|
958
|
-
|
|
1537
|
+
/**
|
|
1538
|
+
* based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0,
|
|
1539
|
+
* derived from Java's string hashcode implementation
|
|
1540
|
+
* @param {string} string
|
|
1541
|
+
* @param {number} hash
|
|
1542
|
+
* @returns {number}
|
|
1543
|
+
*/
|
|
959
1544
|
function stringHash(string, hash) {
|
|
960
1545
|
let char = 0
|
|
961
1546
|
while (char < string.length) {
|
|
@@ -964,6 +1549,10 @@ var htmx = (function() {
|
|
|
964
1549
|
return hash
|
|
965
1550
|
}
|
|
966
1551
|
|
|
1552
|
+
/**
|
|
1553
|
+
* @param {Element} elt
|
|
1554
|
+
* @returns {number}
|
|
1555
|
+
*/
|
|
967
1556
|
function attributeHash(elt) {
|
|
968
1557
|
let hash = 0
|
|
969
1558
|
// IE fix
|
|
@@ -979,17 +1568,23 @@ var htmx = (function() {
|
|
|
979
1568
|
return hash
|
|
980
1569
|
}
|
|
981
1570
|
|
|
1571
|
+
/**
|
|
1572
|
+
* @param {EventTarget} elt
|
|
1573
|
+
*/
|
|
982
1574
|
function deInitOnHandlers(elt) {
|
|
983
1575
|
const internalData = getInternalData(elt)
|
|
984
1576
|
if (internalData.onHandlers) {
|
|
985
1577
|
for (let i = 0; i < internalData.onHandlers.length; i++) {
|
|
986
1578
|
const handlerInfo = internalData.onHandlers[i]
|
|
987
|
-
elt
|
|
1579
|
+
removeEventListenerImpl(elt, handlerInfo.event, handlerInfo.listener)
|
|
988
1580
|
}
|
|
989
1581
|
delete internalData.onHandlers
|
|
990
1582
|
}
|
|
991
1583
|
}
|
|
992
1584
|
|
|
1585
|
+
/**
|
|
1586
|
+
* @param {Node} element
|
|
1587
|
+
*/
|
|
993
1588
|
function deInitNode(element) {
|
|
994
1589
|
const internalData = getInternalData(element)
|
|
995
1590
|
if (internalData.timeout) {
|
|
@@ -998,7 +1593,7 @@ var htmx = (function() {
|
|
|
998
1593
|
if (internalData.listenerInfos) {
|
|
999
1594
|
forEach(internalData.listenerInfos, function(info) {
|
|
1000
1595
|
if (info.on) {
|
|
1001
|
-
info.on
|
|
1596
|
+
removeEventListenerImpl(info.on, info.trigger, info.listener)
|
|
1002
1597
|
}
|
|
1003
1598
|
})
|
|
1004
1599
|
}
|
|
@@ -1006,16 +1601,27 @@ var htmx = (function() {
|
|
|
1006
1601
|
forEach(Object.keys(internalData), function(key) { delete internalData[key] })
|
|
1007
1602
|
}
|
|
1008
1603
|
|
|
1604
|
+
/**
|
|
1605
|
+
* @param {Node} element
|
|
1606
|
+
*/
|
|
1009
1607
|
function cleanUpElement(element) {
|
|
1010
1608
|
triggerEvent(element, 'htmx:beforeCleanupElement')
|
|
1011
1609
|
deInitNode(element)
|
|
1610
|
+
// @ts-ignore IE11 code
|
|
1611
|
+
// noinspection JSUnresolvedReference
|
|
1012
1612
|
if (element.children) { // IE
|
|
1613
|
+
// @ts-ignore
|
|
1013
1614
|
forEach(element.children, function(child) { cleanUpElement(child) })
|
|
1014
1615
|
}
|
|
1015
1616
|
}
|
|
1016
1617
|
|
|
1618
|
+
/**
|
|
1619
|
+
* @param {Node} target
|
|
1620
|
+
* @param {ParentNode} fragment
|
|
1621
|
+
* @param {HtmxSettleInfo} settleInfo
|
|
1622
|
+
*/
|
|
1017
1623
|
function swapOuterHTML(target, fragment, settleInfo) {
|
|
1018
|
-
|
|
1624
|
+
/** @type {Node} */
|
|
1019
1625
|
let newElt
|
|
1020
1626
|
const eltBeforeNewContent = target.previousSibling
|
|
1021
1627
|
insertNodesBefore(parentElt(target), target, fragment, settleInfo)
|
|
@@ -1026,35 +1632,70 @@ var htmx = (function() {
|
|
|
1026
1632
|
}
|
|
1027
1633
|
settleInfo.elts = settleInfo.elts.filter(function(e) { return e !== target })
|
|
1028
1634
|
while (newElt && newElt !== target) {
|
|
1029
|
-
if (newElt
|
|
1635
|
+
if (newElt instanceof Element) {
|
|
1030
1636
|
settleInfo.elts.push(newElt)
|
|
1637
|
+
newElt = newElt.nextElementSibling
|
|
1638
|
+
} else {
|
|
1639
|
+
newElt = null
|
|
1031
1640
|
}
|
|
1032
|
-
newElt = newElt.nextElementSibling
|
|
1033
1641
|
}
|
|
1034
1642
|
cleanUpElement(target)
|
|
1035
|
-
target
|
|
1643
|
+
if (target instanceof Element) {
|
|
1644
|
+
target.remove()
|
|
1645
|
+
} else {
|
|
1646
|
+
target.parentNode.removeChild(target)
|
|
1647
|
+
}
|
|
1036
1648
|
}
|
|
1037
1649
|
|
|
1650
|
+
/**
|
|
1651
|
+
* @param {Node} target
|
|
1652
|
+
* @param {ParentNode} fragment
|
|
1653
|
+
* @param {HtmxSettleInfo} settleInfo
|
|
1654
|
+
*/
|
|
1038
1655
|
function swapAfterBegin(target, fragment, settleInfo) {
|
|
1039
1656
|
return insertNodesBefore(target, target.firstChild, fragment, settleInfo)
|
|
1040
1657
|
}
|
|
1041
1658
|
|
|
1659
|
+
/**
|
|
1660
|
+
* @param {Node} target
|
|
1661
|
+
* @param {ParentNode} fragment
|
|
1662
|
+
* @param {HtmxSettleInfo} settleInfo
|
|
1663
|
+
*/
|
|
1042
1664
|
function swapBeforeBegin(target, fragment, settleInfo) {
|
|
1043
1665
|
return insertNodesBefore(parentElt(target), target, fragment, settleInfo)
|
|
1044
1666
|
}
|
|
1045
1667
|
|
|
1668
|
+
/**
|
|
1669
|
+
* @param {Node} target
|
|
1670
|
+
* @param {ParentNode} fragment
|
|
1671
|
+
* @param {HtmxSettleInfo} settleInfo
|
|
1672
|
+
*/
|
|
1046
1673
|
function swapBeforeEnd(target, fragment, settleInfo) {
|
|
1047
1674
|
return insertNodesBefore(target, null, fragment, settleInfo)
|
|
1048
1675
|
}
|
|
1049
1676
|
|
|
1677
|
+
/**
|
|
1678
|
+
* @param {Node} target
|
|
1679
|
+
* @param {ParentNode} fragment
|
|
1680
|
+
* @param {HtmxSettleInfo} settleInfo
|
|
1681
|
+
*/
|
|
1050
1682
|
function swapAfterEnd(target, fragment, settleInfo) {
|
|
1051
1683
|
return insertNodesBefore(parentElt(target), target.nextSibling, fragment, settleInfo)
|
|
1052
1684
|
}
|
|
1053
|
-
|
|
1685
|
+
|
|
1686
|
+
/**
|
|
1687
|
+
* @param {Node} target
|
|
1688
|
+
*/
|
|
1689
|
+
function swapDelete(target) {
|
|
1054
1690
|
cleanUpElement(target)
|
|
1055
1691
|
return parentElt(target).removeChild(target)
|
|
1056
1692
|
}
|
|
1057
1693
|
|
|
1694
|
+
/**
|
|
1695
|
+
* @param {Node} target
|
|
1696
|
+
* @param {ParentNode} fragment
|
|
1697
|
+
* @param {HtmxSettleInfo} settleInfo
|
|
1698
|
+
*/
|
|
1058
1699
|
function swapInnerHTML(target, fragment, settleInfo) {
|
|
1059
1700
|
const firstChild = target.firstChild
|
|
1060
1701
|
insertNodesBefore(target, firstChild, fragment, settleInfo)
|
|
@@ -1069,11 +1710,11 @@ var htmx = (function() {
|
|
|
1069
1710
|
}
|
|
1070
1711
|
|
|
1071
1712
|
/**
|
|
1072
|
-
* @param {
|
|
1073
|
-
* @param {
|
|
1074
|
-
* @param {
|
|
1075
|
-
* @param {
|
|
1076
|
-
* @param {
|
|
1713
|
+
* @param {HtmxSwapStyle} swapStyle
|
|
1714
|
+
* @param {Element} elt
|
|
1715
|
+
* @param {Node} target
|
|
1716
|
+
* @param {ParentNode} fragment
|
|
1717
|
+
* @param {HtmxSettleInfo} settleInfo
|
|
1077
1718
|
*/
|
|
1078
1719
|
function swapWithStyle(swapStyle, elt, target, fragment, settleInfo) {
|
|
1079
1720
|
switch (swapStyle) {
|
|
@@ -1095,7 +1736,7 @@ var htmx = (function() {
|
|
|
1095
1736
|
swapAfterEnd(target, fragment, settleInfo)
|
|
1096
1737
|
return
|
|
1097
1738
|
case 'delete':
|
|
1098
|
-
swapDelete(target
|
|
1739
|
+
swapDelete(target)
|
|
1099
1740
|
return
|
|
1100
1741
|
default:
|
|
1101
1742
|
var extensions = getExtensions(elt)
|
|
@@ -1127,37 +1768,31 @@ var htmx = (function() {
|
|
|
1127
1768
|
}
|
|
1128
1769
|
}
|
|
1129
1770
|
|
|
1771
|
+
/**
|
|
1772
|
+
* @param {DocumentFragment} fragment
|
|
1773
|
+
* @param {HtmxSettleInfo} settleInfo
|
|
1774
|
+
*/
|
|
1130
1775
|
function findAndSwapOobElements(fragment, settleInfo) {
|
|
1131
1776
|
forEach(findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]'), function(oobElement) {
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1777
|
+
if (htmx.config.allowNestedOobSwaps || oobElement.parentElement === null) {
|
|
1778
|
+
const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
|
|
1779
|
+
if (oobValue != null) {
|
|
1780
|
+
oobSwap(oobValue, oobElement, settleInfo)
|
|
1781
|
+
}
|
|
1782
|
+
} else {
|
|
1783
|
+
oobElement.removeAttribute('hx-swap-oob')
|
|
1784
|
+
oobElement.removeAttribute('data-hx-swap-oob')
|
|
1135
1785
|
}
|
|
1136
1786
|
})
|
|
1137
1787
|
}
|
|
1138
1788
|
|
|
1139
|
-
/**
|
|
1140
|
-
* @callback swapCallback
|
|
1141
|
-
*/
|
|
1142
|
-
|
|
1143
|
-
/**
|
|
1144
|
-
* @typedef {Object} SwapOptions
|
|
1145
|
-
* @property {?string} select
|
|
1146
|
-
* @property {?string} selectOOB
|
|
1147
|
-
* @property {?*} eventInfo
|
|
1148
|
-
* @property {?*} anchor
|
|
1149
|
-
* @property {?HTMLElement} contextElement
|
|
1150
|
-
* @property {?swapCallback} afterSwapCallback
|
|
1151
|
-
* @property {?swapCallback} afterSettleCallback
|
|
1152
|
-
*/
|
|
1153
|
-
|
|
1154
1789
|
/**
|
|
1155
1790
|
* Implements complete swapping pipeline, including: focus and selection preservation,
|
|
1156
1791
|
* title updates, scroll, OOB swapping, normal swapping and settling
|
|
1157
1792
|
* @param {string|Element} target
|
|
1158
1793
|
* @param {string} content
|
|
1159
|
-
* @param {
|
|
1160
|
-
* @param {SwapOptions} swapOptions
|
|
1794
|
+
* @param {HtmxSwapSpecification} swapSpec
|
|
1795
|
+
* @param {SwapOptions} [swapOptions]
|
|
1161
1796
|
*/
|
|
1162
1797
|
function swap(target, content, swapSpec, swapOptions) {
|
|
1163
1798
|
if (!swapOptions) {
|
|
@@ -1182,51 +1817,57 @@ var htmx = (function() {
|
|
|
1182
1817
|
}
|
|
1183
1818
|
const settleInfo = makeSettleInfo(target)
|
|
1184
1819
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1820
|
+
// For text content swaps, don't parse the response as HTML, just insert it
|
|
1821
|
+
if (swapSpec.swapStyle === 'textContent') {
|
|
1822
|
+
target.textContent = content
|
|
1823
|
+
// Otherwise, make the fragment and process it
|
|
1824
|
+
} else {
|
|
1825
|
+
let fragment = makeFragment(content)
|
|
1826
|
+
|
|
1827
|
+
settleInfo.title = fragment.title
|
|
1828
|
+
|
|
1829
|
+
// select-oob swaps
|
|
1830
|
+
if (swapOptions.selectOOB) {
|
|
1831
|
+
const oobSelectValues = swapOptions.selectOOB.split(',')
|
|
1832
|
+
for (let i = 0; i < oobSelectValues.length; i++) {
|
|
1833
|
+
const oobSelectValue = oobSelectValues[i].split(':', 2)
|
|
1834
|
+
let id = oobSelectValue[0].trim()
|
|
1835
|
+
if (id.indexOf('#') === 0) {
|
|
1836
|
+
id = id.substring(1)
|
|
1837
|
+
}
|
|
1838
|
+
const oobValue = oobSelectValue[1] || 'true'
|
|
1839
|
+
const oobElement = fragment.querySelector('#' + id)
|
|
1840
|
+
if (oobElement) {
|
|
1841
|
+
oobSwap(oobValue, oobElement, settleInfo)
|
|
1842
|
+
}
|
|
1202
1843
|
}
|
|
1203
1844
|
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
if (template.content.childElementCount === 0) {
|
|
1845
|
+
// oob swaps
|
|
1846
|
+
findAndSwapOobElements(fragment, settleInfo)
|
|
1847
|
+
forEach(findAll(fragment, 'template'), /** @param {HTMLTemplateElement} template */function(template) {
|
|
1848
|
+
findAndSwapOobElements(template.content, settleInfo)
|
|
1849
|
+
if (template.content.childElementCount === 0) {
|
|
1210
1850
|
// Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
})
|
|
1214
|
-
|
|
1215
|
-
// normal swap
|
|
1216
|
-
if (swapOptions.select) {
|
|
1217
|
-
const newFragment = getDocument().createDocumentFragment()
|
|
1218
|
-
forEach(fragment.querySelectorAll(swapOptions.select), function(node) {
|
|
1219
|
-
newFragment.appendChild(node)
|
|
1851
|
+
template.remove()
|
|
1852
|
+
}
|
|
1220
1853
|
})
|
|
1221
|
-
|
|
1854
|
+
|
|
1855
|
+
// normal swap
|
|
1856
|
+
if (swapOptions.select) {
|
|
1857
|
+
const newFragment = getDocument().createDocumentFragment()
|
|
1858
|
+
forEach(fragment.querySelectorAll(swapOptions.select), function(node) {
|
|
1859
|
+
newFragment.appendChild(node)
|
|
1860
|
+
})
|
|
1861
|
+
fragment = newFragment
|
|
1862
|
+
}
|
|
1863
|
+
handlePreservedElements(fragment)
|
|
1864
|
+
swapWithStyle(swapSpec.swapStyle, swapOptions.contextElement, target, fragment, settleInfo)
|
|
1222
1865
|
}
|
|
1223
|
-
handlePreservedElements(fragment)
|
|
1224
|
-
swapWithStyle(swapSpec.swapStyle, swapOptions.contextElement, target, fragment, settleInfo)
|
|
1225
1866
|
|
|
1226
1867
|
// apply saved focus and selection information to swapped content
|
|
1227
1868
|
if (selectionInfo.elt &&
|
|
1228
|
-
|
|
1229
|
-
|
|
1869
|
+
!bodyContains(selectionInfo.elt) &&
|
|
1870
|
+
getRawAttribute(selectionInfo.elt, 'id')) {
|
|
1230
1871
|
const newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, 'id'))
|
|
1231
1872
|
const focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }
|
|
1232
1873
|
if (newActiveElt) {
|
|
@@ -1272,7 +1913,7 @@ var htmx = (function() {
|
|
|
1272
1913
|
})
|
|
1273
1914
|
|
|
1274
1915
|
if (swapOptions.anchor) {
|
|
1275
|
-
const anchorTarget = resolveTarget('#' + swapOptions.anchor)
|
|
1916
|
+
const anchorTarget = asElement(resolveTarget('#' + swapOptions.anchor))
|
|
1276
1917
|
if (anchorTarget) {
|
|
1277
1918
|
anchorTarget.scrollIntoView({ block: 'start', behavior: 'auto' })
|
|
1278
1919
|
}
|
|
@@ -1285,12 +1926,17 @@ var htmx = (function() {
|
|
|
1285
1926
|
}
|
|
1286
1927
|
|
|
1287
1928
|
if (swapSpec.settleDelay > 0) {
|
|
1288
|
-
setTimeout(doSettle, swapSpec.settleDelay)
|
|
1929
|
+
getWindow().setTimeout(doSettle, swapSpec.settleDelay)
|
|
1289
1930
|
} else {
|
|
1290
1931
|
doSettle()
|
|
1291
1932
|
}
|
|
1292
1933
|
}
|
|
1293
1934
|
|
|
1935
|
+
/**
|
|
1936
|
+
* @param {XMLHttpRequest} xhr
|
|
1937
|
+
* @param {string} header
|
|
1938
|
+
* @param {EventTarget} elt
|
|
1939
|
+
*/
|
|
1294
1940
|
function handleTriggerHeader(xhr, header, elt) {
|
|
1295
1941
|
const triggerBody = xhr.getResponseHeader(header)
|
|
1296
1942
|
if (triggerBody.indexOf('{') === 0) {
|
|
@@ -1320,7 +1966,13 @@ var htmx = (function() {
|
|
|
1320
1966
|
const NOT_WHITESPACE = /[^\s]/
|
|
1321
1967
|
const COMBINED_SELECTOR_START = /[{(]/
|
|
1322
1968
|
const COMBINED_SELECTOR_END = /[})]/
|
|
1969
|
+
|
|
1970
|
+
/**
|
|
1971
|
+
* @param {string} str
|
|
1972
|
+
* @returns {string[]}
|
|
1973
|
+
*/
|
|
1323
1974
|
function tokenizeString(str) {
|
|
1975
|
+
/** @type string[] */
|
|
1324
1976
|
const tokens = []
|
|
1325
1977
|
let position = 0
|
|
1326
1978
|
while (position < str.length) {
|
|
@@ -1350,6 +2002,12 @@ var htmx = (function() {
|
|
|
1350
2002
|
return tokens
|
|
1351
2003
|
}
|
|
1352
2004
|
|
|
2005
|
+
/**
|
|
2006
|
+
* @param {string} token
|
|
2007
|
+
* @param {string|null} last
|
|
2008
|
+
* @param {string} paramName
|
|
2009
|
+
* @returns {boolean}
|
|
2010
|
+
*/
|
|
1353
2011
|
function isPossibleRelativeReference(token, last, paramName) {
|
|
1354
2012
|
return SYMBOL_START.exec(token.charAt(0)) &&
|
|
1355
2013
|
token !== 'true' &&
|
|
@@ -1359,6 +2017,12 @@ var htmx = (function() {
|
|
|
1359
2017
|
last !== '.'
|
|
1360
2018
|
}
|
|
1361
2019
|
|
|
2020
|
+
/**
|
|
2021
|
+
* @param {EventTarget|string} elt
|
|
2022
|
+
* @param {string[]} tokens
|
|
2023
|
+
* @param {string} paramName
|
|
2024
|
+
* @returns {ConditionalFunction|null}
|
|
2025
|
+
*/
|
|
1362
2026
|
function maybeGenerateConditional(elt, tokens, paramName) {
|
|
1363
2027
|
if (tokens[0] === '[') {
|
|
1364
2028
|
tokens.shift()
|
|
@@ -1367,6 +2031,7 @@ var htmx = (function() {
|
|
|
1367
2031
|
let last = null
|
|
1368
2032
|
while (tokens.length > 0) {
|
|
1369
2033
|
const token = tokens[0]
|
|
2034
|
+
// @ts-ignore For some reason tsc doesn't understand the shift call, and thinks we're comparing the same value here, i.e. '[' vs ']'
|
|
1370
2035
|
if (token === ']') {
|
|
1371
2036
|
bracketCount--
|
|
1372
2037
|
if (bracketCount === 0) {
|
|
@@ -1400,6 +2065,11 @@ var htmx = (function() {
|
|
|
1400
2065
|
}
|
|
1401
2066
|
}
|
|
1402
2067
|
|
|
2068
|
+
/**
|
|
2069
|
+
* @param {string[]} tokens
|
|
2070
|
+
* @param {RegExp} match
|
|
2071
|
+
* @returns {string}
|
|
2072
|
+
*/
|
|
1403
2073
|
function consumeUntil(tokens, match) {
|
|
1404
2074
|
let result = ''
|
|
1405
2075
|
while (tokens.length > 0 && !match.test(tokens[0])) {
|
|
@@ -1408,6 +2078,10 @@ var htmx = (function() {
|
|
|
1408
2078
|
return result
|
|
1409
2079
|
}
|
|
1410
2080
|
|
|
2081
|
+
/**
|
|
2082
|
+
* @param {string[]} tokens
|
|
2083
|
+
* @returns {string}
|
|
2084
|
+
*/
|
|
1411
2085
|
function consumeCSSSelector(tokens) {
|
|
1412
2086
|
let result
|
|
1413
2087
|
if (tokens.length > 0 && COMBINED_SELECTOR_START.test(tokens[0])) {
|
|
@@ -1423,12 +2097,13 @@ var htmx = (function() {
|
|
|
1423
2097
|
const INPUT_SELECTOR = 'input, textarea, select'
|
|
1424
2098
|
|
|
1425
2099
|
/**
|
|
1426
|
-
* @param {
|
|
2100
|
+
* @param {Element} elt
|
|
1427
2101
|
* @param {string} explicitTrigger
|
|
1428
|
-
* @param {
|
|
1429
|
-
* @returns {
|
|
2102
|
+
* @param {Object} cache for trigger specs
|
|
2103
|
+
* @returns {HtmxTriggerSpecification[]}
|
|
1430
2104
|
*/
|
|
1431
2105
|
function parseAndCacheTrigger(elt, explicitTrigger, cache) {
|
|
2106
|
+
/** @type HtmxTriggerSpecification[] */
|
|
1432
2107
|
const triggerSpecs = []
|
|
1433
2108
|
const tokens = tokenizeString(explicitTrigger)
|
|
1434
2109
|
do {
|
|
@@ -1437,6 +2112,7 @@ var htmx = (function() {
|
|
|
1437
2112
|
const trigger = consumeUntil(tokens, /[,\[\s]/)
|
|
1438
2113
|
if (trigger !== '') {
|
|
1439
2114
|
if (trigger === 'every') {
|
|
2115
|
+
/** @type HtmxTriggerSpecification */
|
|
1440
2116
|
const every = { trigger: 'every' }
|
|
1441
2117
|
consumeUntil(tokens, NOT_WHITESPACE)
|
|
1442
2118
|
every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/))
|
|
@@ -1447,6 +2123,7 @@ var htmx = (function() {
|
|
|
1447
2123
|
}
|
|
1448
2124
|
triggerSpecs.push(every)
|
|
1449
2125
|
} else {
|
|
2126
|
+
/** @type HtmxTriggerSpecification */
|
|
1450
2127
|
const triggerSpec = { trigger }
|
|
1451
2128
|
var eventFilter = maybeGenerateConditional(elt, tokens, 'event')
|
|
1452
2129
|
if (eventFilter) {
|
|
@@ -1514,8 +2191,8 @@ var htmx = (function() {
|
|
|
1514
2191
|
}
|
|
1515
2192
|
|
|
1516
2193
|
/**
|
|
1517
|
-
* @param {
|
|
1518
|
-
* @returns {
|
|
2194
|
+
* @param {Element} elt
|
|
2195
|
+
* @returns {HtmxTriggerSpecification[]}
|
|
1519
2196
|
*/
|
|
1520
2197
|
function getTriggerSpecs(elt) {
|
|
1521
2198
|
const explicitTrigger = getAttributeValue(elt, 'hx-trigger')
|
|
@@ -1538,13 +2215,21 @@ var htmx = (function() {
|
|
|
1538
2215
|
}
|
|
1539
2216
|
}
|
|
1540
2217
|
|
|
2218
|
+
/**
|
|
2219
|
+
* @param {Element} elt
|
|
2220
|
+
*/
|
|
1541
2221
|
function cancelPolling(elt) {
|
|
1542
2222
|
getInternalData(elt).cancelled = true
|
|
1543
2223
|
}
|
|
1544
2224
|
|
|
2225
|
+
/**
|
|
2226
|
+
* @param {Element} elt
|
|
2227
|
+
* @param {TriggerHandler} handler
|
|
2228
|
+
* @param {HtmxTriggerSpecification} spec
|
|
2229
|
+
*/
|
|
1545
2230
|
function processPolling(elt, handler, spec) {
|
|
1546
2231
|
const nodeData = getInternalData(elt)
|
|
1547
|
-
nodeData.timeout = setTimeout(function() {
|
|
2232
|
+
nodeData.timeout = getWindow().setTimeout(function() {
|
|
1548
2233
|
if (bodyContains(elt) && nodeData.cancelled !== true) {
|
|
1549
2234
|
if (!maybeFilterEvent(spec, elt, makeEvent('hx:poll:trigger', {
|
|
1550
2235
|
triggerSpec: spec,
|
|
@@ -1557,14 +2242,23 @@ var htmx = (function() {
|
|
|
1557
2242
|
}, spec.pollInterval)
|
|
1558
2243
|
}
|
|
1559
2244
|
|
|
2245
|
+
/**
|
|
2246
|
+
* @param {HTMLAnchorElement} elt
|
|
2247
|
+
* @returns {boolean}
|
|
2248
|
+
*/
|
|
1560
2249
|
function isLocalLink(elt) {
|
|
1561
2250
|
return location.hostname === elt.hostname &&
|
|
1562
2251
|
getRawAttribute(elt, 'href') &&
|
|
1563
2252
|
getRawAttribute(elt, 'href').indexOf('#') !== 0
|
|
1564
2253
|
}
|
|
1565
2254
|
|
|
2255
|
+
/**
|
|
2256
|
+
* @param {Element} elt
|
|
2257
|
+
* @param {HtmxNodeInternalData} nodeData
|
|
2258
|
+
* @param {HtmxTriggerSpecification[]} triggerSpecs
|
|
2259
|
+
*/
|
|
1566
2260
|
function boostElement(elt, nodeData, triggerSpecs) {
|
|
1567
|
-
if ((elt
|
|
2261
|
+
if ((elt instanceof HTMLAnchorElement && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || elt.tagName === 'FORM') {
|
|
1568
2262
|
nodeData.boosted = true
|
|
1569
2263
|
let verb, path
|
|
1570
2264
|
if (elt.tagName === 'A') {
|
|
@@ -1578,7 +2272,8 @@ var htmx = (function() {
|
|
|
1578
2272
|
path = getRawAttribute(elt, 'action')
|
|
1579
2273
|
}
|
|
1580
2274
|
triggerSpecs.forEach(function(triggerSpec) {
|
|
1581
|
-
addEventListener(elt, function(
|
|
2275
|
+
addEventListener(elt, function(node, evt) {
|
|
2276
|
+
const elt = asElement(node)
|
|
1582
2277
|
if (closest(elt, htmx.config.disableSelector)) {
|
|
1583
2278
|
cleanUpElement(elt)
|
|
1584
2279
|
return
|
|
@@ -1590,12 +2285,15 @@ var htmx = (function() {
|
|
|
1590
2285
|
}
|
|
1591
2286
|
|
|
1592
2287
|
/**
|
|
1593
|
-
*
|
|
1594
2288
|
* @param {Event} evt
|
|
1595
|
-
* @param {
|
|
1596
|
-
* @returns
|
|
2289
|
+
* @param {Node} node
|
|
2290
|
+
* @returns {boolean}
|
|
1597
2291
|
*/
|
|
1598
|
-
function shouldCancel(evt,
|
|
2292
|
+
function shouldCancel(evt, node) {
|
|
2293
|
+
const elt = asElement(node)
|
|
2294
|
+
if (!elt) {
|
|
2295
|
+
return false
|
|
2296
|
+
}
|
|
1599
2297
|
if (evt.type === 'submit' || evt.type === 'click') {
|
|
1600
2298
|
if (elt.tagName === 'FORM') {
|
|
1601
2299
|
return true
|
|
@@ -1603,7 +2301,7 @@ var htmx = (function() {
|
|
|
1603
2301
|
if (matches(elt, 'input[type="submit"], button') && closest(elt, 'form') !== null) {
|
|
1604
2302
|
return true
|
|
1605
2303
|
}
|
|
1606
|
-
if (elt
|
|
2304
|
+
if (elt instanceof HTMLAnchorElement && elt.href &&
|
|
1607
2305
|
(elt.getAttribute('href') === '#' || elt.getAttribute('href').indexOf('#') !== 0)) {
|
|
1608
2306
|
return true
|
|
1609
2307
|
}
|
|
@@ -1611,25 +2309,47 @@ var htmx = (function() {
|
|
|
1611
2309
|
return false
|
|
1612
2310
|
}
|
|
1613
2311
|
|
|
2312
|
+
/**
|
|
2313
|
+
* @param {Node} elt
|
|
2314
|
+
* @param {Event|MouseEvent|KeyboardEvent|TouchEvent} evt
|
|
2315
|
+
* @returns {boolean}
|
|
2316
|
+
*/
|
|
1614
2317
|
function ignoreBoostedAnchorCtrlClick(elt, evt) {
|
|
1615
|
-
return getInternalData(elt).boosted && elt
|
|
2318
|
+
return getInternalData(elt).boosted && elt instanceof HTMLAnchorElement && evt.type === 'click' &&
|
|
2319
|
+
// @ts-ignore this will resolve to undefined for events that don't define those properties, which is fine
|
|
2320
|
+
(evt.ctrlKey || evt.metaKey)
|
|
1616
2321
|
}
|
|
1617
2322
|
|
|
2323
|
+
/**
|
|
2324
|
+
* @param {HtmxTriggerSpecification} triggerSpec
|
|
2325
|
+
* @param {Node} elt
|
|
2326
|
+
* @param {Event} evt
|
|
2327
|
+
* @returns {boolean}
|
|
2328
|
+
*/
|
|
1618
2329
|
function maybeFilterEvent(triggerSpec, elt, evt) {
|
|
1619
2330
|
const eventFilter = triggerSpec.eventFilter
|
|
1620
2331
|
if (eventFilter) {
|
|
1621
2332
|
try {
|
|
1622
2333
|
return eventFilter.call(elt, evt) !== true
|
|
1623
2334
|
} catch (e) {
|
|
1624
|
-
|
|
2335
|
+
const source = eventFilter.source
|
|
2336
|
+
triggerErrorEvent(getDocument().body, 'htmx:eventFilter:error', { error: e, source })
|
|
1625
2337
|
return true
|
|
1626
2338
|
}
|
|
1627
2339
|
}
|
|
1628
2340
|
return false
|
|
1629
2341
|
}
|
|
1630
2342
|
|
|
2343
|
+
/**
|
|
2344
|
+
* @param {Node} elt
|
|
2345
|
+
* @param {TriggerHandler} handler
|
|
2346
|
+
* @param {HtmxNodeInternalData} nodeData
|
|
2347
|
+
* @param {HtmxTriggerSpecification} triggerSpec
|
|
2348
|
+
* @param {boolean} [explicitCancel]
|
|
2349
|
+
*/
|
|
1631
2350
|
function addEventListener(elt, handler, nodeData, triggerSpec, explicitCancel) {
|
|
1632
2351
|
const elementData = getInternalData(elt)
|
|
2352
|
+
/** @type {(Node|Window)[]} */
|
|
1633
2353
|
let eltsToListenOn
|
|
1634
2354
|
if (triggerSpec.from) {
|
|
1635
2355
|
eltsToListenOn = querySelectorAllExt(elt, triggerSpec.from)
|
|
@@ -1640,10 +2360,12 @@ var htmx = (function() {
|
|
|
1640
2360
|
if (triggerSpec.changed) {
|
|
1641
2361
|
eltsToListenOn.forEach(function(eltToListenOn) {
|
|
1642
2362
|
const eltToListenOnData = getInternalData(eltToListenOn)
|
|
2363
|
+
// @ts-ignore value will be undefined for non-input elements, which is fine
|
|
1643
2364
|
eltToListenOnData.lastValue = eltToListenOn.value
|
|
1644
2365
|
})
|
|
1645
2366
|
}
|
|
1646
2367
|
forEach(eltsToListenOn, function(eltToListenOn) {
|
|
2368
|
+
/** @type EventListener */
|
|
1647
2369
|
const eventListener = function(evt) {
|
|
1648
2370
|
if (!bodyContains(elt)) {
|
|
1649
2371
|
eltToListenOn.removeEventListener(triggerSpec.trigger, eventListener)
|
|
@@ -1669,7 +2391,7 @@ var htmx = (function() {
|
|
|
1669
2391
|
evt.stopPropagation()
|
|
1670
2392
|
}
|
|
1671
2393
|
if (triggerSpec.target && evt.target) {
|
|
1672
|
-
if (!matches(evt.target, triggerSpec.target)) {
|
|
2394
|
+
if (!matches(asElement(evt.target), triggerSpec.target)) {
|
|
1673
2395
|
return
|
|
1674
2396
|
}
|
|
1675
2397
|
}
|
|
@@ -1682,10 +2404,12 @@ var htmx = (function() {
|
|
|
1682
2404
|
}
|
|
1683
2405
|
if (triggerSpec.changed) {
|
|
1684
2406
|
const eltToListenOnData = getInternalData(eltToListenOn)
|
|
1685
|
-
|
|
2407
|
+
// @ts-ignore value will be undefined for non-input elements, which is fine
|
|
2408
|
+
const value = eltToListenOn.value
|
|
2409
|
+
if (eltToListenOnData.lastValue === value) {
|
|
1686
2410
|
return
|
|
1687
2411
|
}
|
|
1688
|
-
eltToListenOnData.lastValue =
|
|
2412
|
+
eltToListenOnData.lastValue = value
|
|
1689
2413
|
}
|
|
1690
2414
|
if (elementData.delayed) {
|
|
1691
2415
|
clearTimeout(elementData.delayed)
|
|
@@ -1697,12 +2421,12 @@ var htmx = (function() {
|
|
|
1697
2421
|
if (triggerSpec.throttle > 0) {
|
|
1698
2422
|
if (!elementData.throttle) {
|
|
1699
2423
|
handler(elt, evt)
|
|
1700
|
-
elementData.throttle = setTimeout(function() {
|
|
2424
|
+
elementData.throttle = getWindow().setTimeout(function() {
|
|
1701
2425
|
elementData.throttle = null
|
|
1702
2426
|
}, triggerSpec.throttle)
|
|
1703
2427
|
}
|
|
1704
2428
|
} else if (triggerSpec.delay > 0) {
|
|
1705
|
-
elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay)
|
|
2429
|
+
elementData.delayed = getWindow().setTimeout(function() { handler(elt, evt) }, triggerSpec.delay)
|
|
1706
2430
|
} else {
|
|
1707
2431
|
triggerEvent(elt, 'htmx:trigger')
|
|
1708
2432
|
handler(elt, evt)
|
|
@@ -1740,6 +2464,9 @@ var htmx = (function() {
|
|
|
1740
2464
|
}
|
|
1741
2465
|
}
|
|
1742
2466
|
|
|
2467
|
+
/**
|
|
2468
|
+
* @param {Element} elt
|
|
2469
|
+
*/
|
|
1743
2470
|
function maybeReveal(elt) {
|
|
1744
2471
|
if (!hasAttribute(elt, 'data-hx-revealed') && isScrolledIntoView(elt)) {
|
|
1745
2472
|
elt.setAttribute('data-hx-revealed', 'true')
|
|
@@ -1748,13 +2475,19 @@ var htmx = (function() {
|
|
|
1748
2475
|
triggerEvent(elt, 'revealed')
|
|
1749
2476
|
} else {
|
|
1750
2477
|
// if the node isn't initialized, wait for it before triggering the request
|
|
1751
|
-
elt.addEventListener('htmx:afterProcessNode', function(
|
|
2478
|
+
elt.addEventListener('htmx:afterProcessNode', function() { triggerEvent(elt, 'revealed') }, { once: true })
|
|
1752
2479
|
}
|
|
1753
2480
|
}
|
|
1754
2481
|
}
|
|
1755
2482
|
|
|
1756
2483
|
//= ===================================================================
|
|
1757
2484
|
|
|
2485
|
+
/**
|
|
2486
|
+
* @param {Element} elt
|
|
2487
|
+
* @param {TriggerHandler} handler
|
|
2488
|
+
* @param {HtmxNodeInternalData} nodeData
|
|
2489
|
+
* @param {number} delay
|
|
2490
|
+
*/
|
|
1758
2491
|
function loadImmediately(elt, handler, nodeData, delay) {
|
|
1759
2492
|
const load = function() {
|
|
1760
2493
|
if (!nodeData.loaded) {
|
|
@@ -1763,12 +2496,18 @@ var htmx = (function() {
|
|
|
1763
2496
|
}
|
|
1764
2497
|
}
|
|
1765
2498
|
if (delay > 0) {
|
|
1766
|
-
setTimeout(load, delay)
|
|
2499
|
+
getWindow().setTimeout(load, delay)
|
|
1767
2500
|
} else {
|
|
1768
2501
|
load()
|
|
1769
2502
|
}
|
|
1770
2503
|
}
|
|
1771
2504
|
|
|
2505
|
+
/**
|
|
2506
|
+
* @param {Element} elt
|
|
2507
|
+
* @param {HtmxNodeInternalData} nodeData
|
|
2508
|
+
* @param {HtmxTriggerSpecification[]} triggerSpecs
|
|
2509
|
+
* @returns {boolean}
|
|
2510
|
+
*/
|
|
1772
2511
|
function processVerbs(elt, nodeData, triggerSpecs) {
|
|
1773
2512
|
let explicitAction = false
|
|
1774
2513
|
forEach(VERBS, function(verb) {
|
|
@@ -1778,7 +2517,8 @@ var htmx = (function() {
|
|
|
1778
2517
|
nodeData.path = path
|
|
1779
2518
|
nodeData.verb = verb
|
|
1780
2519
|
triggerSpecs.forEach(function(triggerSpec) {
|
|
1781
|
-
addTriggerHandler(elt, triggerSpec, nodeData, function(
|
|
2520
|
+
addTriggerHandler(elt, triggerSpec, nodeData, function(node, evt) {
|
|
2521
|
+
const elt = asElement(node)
|
|
1782
2522
|
if (closest(elt, htmx.config.disableSelector)) {
|
|
1783
2523
|
cleanUpElement(elt)
|
|
1784
2524
|
return
|
|
@@ -1791,11 +2531,23 @@ var htmx = (function() {
|
|
|
1791
2531
|
return explicitAction
|
|
1792
2532
|
}
|
|
1793
2533
|
|
|
2534
|
+
/**
|
|
2535
|
+
* @callback TriggerHandler
|
|
2536
|
+
* @param {Node} elt
|
|
2537
|
+
* @param {Event} [evt]
|
|
2538
|
+
*/
|
|
2539
|
+
|
|
2540
|
+
/**
|
|
2541
|
+
* @param {Node} elt
|
|
2542
|
+
* @param {HtmxTriggerSpecification} triggerSpec
|
|
2543
|
+
* @param {HtmxNodeInternalData} nodeData
|
|
2544
|
+
* @param {TriggerHandler} handler
|
|
2545
|
+
*/
|
|
1794
2546
|
function addTriggerHandler(elt, triggerSpec, nodeData, handler) {
|
|
1795
2547
|
if (triggerSpec.trigger === 'revealed') {
|
|
1796
2548
|
initScrollHandler()
|
|
1797
2549
|
addEventListener(elt, handler, nodeData, triggerSpec)
|
|
1798
|
-
maybeReveal(elt)
|
|
2550
|
+
maybeReveal(asElement(elt))
|
|
1799
2551
|
} else if (triggerSpec.trigger === 'intersect') {
|
|
1800
2552
|
const observerOptions = {}
|
|
1801
2553
|
if (triggerSpec.root) {
|
|
@@ -1813,21 +2565,29 @@ var htmx = (function() {
|
|
|
1813
2565
|
}
|
|
1814
2566
|
}
|
|
1815
2567
|
}, observerOptions)
|
|
1816
|
-
observer.observe(elt)
|
|
1817
|
-
addEventListener(elt, handler, nodeData, triggerSpec)
|
|
2568
|
+
observer.observe(asElement(elt))
|
|
2569
|
+
addEventListener(asElement(elt), handler, nodeData, triggerSpec)
|
|
1818
2570
|
} else if (triggerSpec.trigger === 'load') {
|
|
1819
2571
|
if (!maybeFilterEvent(triggerSpec, elt, makeEvent('load', { elt }))) {
|
|
1820
|
-
loadImmediately(elt, handler, nodeData, triggerSpec.delay)
|
|
2572
|
+
loadImmediately(asElement(elt), handler, nodeData, triggerSpec.delay)
|
|
1821
2573
|
}
|
|
1822
2574
|
} else if (triggerSpec.pollInterval > 0) {
|
|
1823
2575
|
nodeData.polling = true
|
|
1824
|
-
processPolling(elt, handler, triggerSpec)
|
|
2576
|
+
processPolling(asElement(elt), handler, triggerSpec)
|
|
1825
2577
|
} else {
|
|
1826
2578
|
addEventListener(elt, handler, nodeData, triggerSpec)
|
|
1827
2579
|
}
|
|
1828
2580
|
}
|
|
1829
2581
|
|
|
1830
|
-
|
|
2582
|
+
/**
|
|
2583
|
+
* @param {Node} node
|
|
2584
|
+
* @returns {boolean}
|
|
2585
|
+
*/
|
|
2586
|
+
function shouldProcessHxOn(node) {
|
|
2587
|
+
const elt = asElement(node)
|
|
2588
|
+
if (!elt) {
|
|
2589
|
+
return false
|
|
2590
|
+
}
|
|
1831
2591
|
const attributes = elt.attributes
|
|
1832
2592
|
for (let j = 0; j < attributes.length; j++) {
|
|
1833
2593
|
const attrName = attributes[j].name
|
|
@@ -1839,22 +2599,31 @@ var htmx = (function() {
|
|
|
1839
2599
|
return false
|
|
1840
2600
|
}
|
|
1841
2601
|
|
|
2602
|
+
/**
|
|
2603
|
+
* @param {Node} elt
|
|
2604
|
+
* @returns {Element[]}
|
|
2605
|
+
*/
|
|
1842
2606
|
function findHxOnWildcardElements(elt) {
|
|
1843
2607
|
let node = null
|
|
2608
|
+
/** @type {Element[]} */
|
|
1844
2609
|
const elements = []
|
|
1845
2610
|
|
|
1846
2611
|
if (!(elt instanceof ShadowRoot)) {
|
|
1847
2612
|
if (shouldProcessHxOn(elt)) {
|
|
1848
|
-
elements.push(elt)
|
|
2613
|
+
elements.push(asElement(elt))
|
|
1849
2614
|
}
|
|
1850
2615
|
|
|
1851
2616
|
const iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' +
|
|
1852
2617
|
' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt)
|
|
1853
|
-
while (node = iter.iterateNext()) elements.push(node)
|
|
2618
|
+
while (node = iter.iterateNext()) elements.push(asElement(node))
|
|
1854
2619
|
}
|
|
1855
2620
|
return elements
|
|
1856
2621
|
}
|
|
1857
2622
|
|
|
2623
|
+
/**
|
|
2624
|
+
* @param {Element} elt
|
|
2625
|
+
* @returns {NodeListOf<Element>|[]}
|
|
2626
|
+
*/
|
|
1858
2627
|
function findElementsToProcess(elt) {
|
|
1859
2628
|
if (elt.querySelectorAll) {
|
|
1860
2629
|
const boostedSelector = ', [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]'
|
|
@@ -1866,23 +2635,35 @@ var htmx = (function() {
|
|
|
1866
2635
|
}
|
|
1867
2636
|
}
|
|
1868
2637
|
|
|
1869
|
-
|
|
1870
|
-
|
|
2638
|
+
/**
|
|
2639
|
+
* Handle submit buttons/inputs that have the form attribute set
|
|
2640
|
+
* see https://developer.mozilla.org/docs/Web/HTML/Element/button
|
|
2641
|
+
* @param {Event} evt
|
|
2642
|
+
*/
|
|
1871
2643
|
function maybeSetLastButtonClicked(evt) {
|
|
1872
|
-
const elt = closest(evt.target, "button, input[type='submit']")
|
|
2644
|
+
const elt = /** @type {HTMLButtonElement|HTMLInputElement} */ (closest(asElement(evt.target), "button, input[type='submit']"))
|
|
1873
2645
|
const internalData = getRelatedFormData(evt)
|
|
1874
2646
|
if (internalData) {
|
|
1875
2647
|
internalData.lastButtonClicked = elt
|
|
1876
2648
|
}
|
|
1877
|
-
}
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
/**
|
|
2652
|
+
* @param {Event} evt
|
|
2653
|
+
*/
|
|
1878
2654
|
function maybeUnsetLastButtonClicked(evt) {
|
|
1879
2655
|
const internalData = getRelatedFormData(evt)
|
|
1880
2656
|
if (internalData) {
|
|
1881
2657
|
internalData.lastButtonClicked = null
|
|
1882
2658
|
}
|
|
1883
2659
|
}
|
|
2660
|
+
|
|
2661
|
+
/**
|
|
2662
|
+
* @param {Event} evt
|
|
2663
|
+
* @returns {HtmxNodeInternalData|undefined}
|
|
2664
|
+
*/
|
|
1884
2665
|
function getRelatedFormData(evt) {
|
|
1885
|
-
const elt = closest(evt.target, "button, input[type='submit']")
|
|
2666
|
+
const elt = closest(asElement(evt.target), "button, input[type='submit']")
|
|
1886
2667
|
if (!elt) {
|
|
1887
2668
|
return
|
|
1888
2669
|
}
|
|
@@ -1892,6 +2673,10 @@ var htmx = (function() {
|
|
|
1892
2673
|
}
|
|
1893
2674
|
return getInternalData(form)
|
|
1894
2675
|
}
|
|
2676
|
+
|
|
2677
|
+
/**
|
|
2678
|
+
* @param {EventTarget} elt
|
|
2679
|
+
*/
|
|
1895
2680
|
function initButtonTracking(elt) {
|
|
1896
2681
|
// need to handle both click and focus in:
|
|
1897
2682
|
// focusin - in case someone tabs in to a button and hits the space bar
|
|
@@ -1901,28 +2686,20 @@ var htmx = (function() {
|
|
|
1901
2686
|
elt.addEventListener('focusout', maybeUnsetLastButtonClicked)
|
|
1902
2687
|
}
|
|
1903
2688
|
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
if (token === '{') {
|
|
1910
|
-
netCurlies++
|
|
1911
|
-
} else if (token === '}') {
|
|
1912
|
-
netCurlies--
|
|
1913
|
-
}
|
|
1914
|
-
}
|
|
1915
|
-
return netCurlies
|
|
1916
|
-
}
|
|
1917
|
-
|
|
2689
|
+
/**
|
|
2690
|
+
* @param {EventTarget} elt
|
|
2691
|
+
* @param {string} eventName
|
|
2692
|
+
* @param {string} code
|
|
2693
|
+
*/
|
|
1918
2694
|
function addHxOnEventHandler(elt, eventName, code) {
|
|
1919
2695
|
const nodeData = getInternalData(elt)
|
|
1920
2696
|
if (!Array.isArray(nodeData.onHandlers)) {
|
|
1921
2697
|
nodeData.onHandlers = []
|
|
1922
2698
|
}
|
|
1923
2699
|
let func
|
|
2700
|
+
/** @type EventListener */
|
|
1924
2701
|
const listener = function(e) {
|
|
1925
|
-
|
|
2702
|
+
maybeEval(elt, function() {
|
|
1926
2703
|
if (!func) {
|
|
1927
2704
|
func = new Function('event', code)
|
|
1928
2705
|
}
|
|
@@ -1933,6 +2710,9 @@ var htmx = (function() {
|
|
|
1933
2710
|
nodeData.onHandlers.push({ event: eventName, listener })
|
|
1934
2711
|
}
|
|
1935
2712
|
|
|
2713
|
+
/**
|
|
2714
|
+
* @param {Element} elt
|
|
2715
|
+
*/
|
|
1936
2716
|
function processHxOnWildcard(elt) {
|
|
1937
2717
|
// wipe any previous on handlers so that this function takes precedence
|
|
1938
2718
|
deInitOnHandlers(elt)
|
|
@@ -1960,6 +2740,9 @@ var htmx = (function() {
|
|
|
1960
2740
|
}
|
|
1961
2741
|
}
|
|
1962
2742
|
|
|
2743
|
+
/**
|
|
2744
|
+
* @param {Element|HTMLInputElement} elt
|
|
2745
|
+
*/
|
|
1963
2746
|
function initNode(elt) {
|
|
1964
2747
|
if (closest(elt, htmx.config.disableSelector)) {
|
|
1965
2748
|
cleanUpElement(elt)
|
|
@@ -1974,7 +2757,9 @@ var htmx = (function() {
|
|
|
1974
2757
|
|
|
1975
2758
|
triggerEvent(elt, 'htmx:beforeProcessNode')
|
|
1976
2759
|
|
|
2760
|
+
// @ts-ignore value will be undefined for non-input elements, which is fine
|
|
1977
2761
|
if (elt.value) {
|
|
2762
|
+
// @ts-ignore
|
|
1978
2763
|
nodeData.lastValue = elt.value
|
|
1979
2764
|
}
|
|
1980
2765
|
|
|
@@ -2003,6 +2788,13 @@ var htmx = (function() {
|
|
|
2003
2788
|
}
|
|
2004
2789
|
}
|
|
2005
2790
|
|
|
2791
|
+
/**
|
|
2792
|
+
* Processes new content, enabling htmx behavior. This can be useful if you have content that is added to the DOM outside of the normal htmx request cycle but still want htmx attributes to work.
|
|
2793
|
+
*
|
|
2794
|
+
* @see https://htmx.org/api/#process
|
|
2795
|
+
*
|
|
2796
|
+
* @param {Element|string} elt element to process
|
|
2797
|
+
*/
|
|
2006
2798
|
function processNode(elt) {
|
|
2007
2799
|
elt = resolveTarget(elt)
|
|
2008
2800
|
if (closest(elt, htmx.config.disableSelector)) {
|
|
@@ -2018,10 +2810,19 @@ var htmx = (function() {
|
|
|
2018
2810
|
// Event/Log Support
|
|
2019
2811
|
//= ===================================================================
|
|
2020
2812
|
|
|
2813
|
+
/**
|
|
2814
|
+
* @param {string} str
|
|
2815
|
+
* @returns {string}
|
|
2816
|
+
*/
|
|
2021
2817
|
function kebabEventName(str) {
|
|
2022
2818
|
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()
|
|
2023
2819
|
}
|
|
2024
2820
|
|
|
2821
|
+
/**
|
|
2822
|
+
* @param {string} eventName
|
|
2823
|
+
* @param {any} detail
|
|
2824
|
+
* @returns {CustomEvent}
|
|
2825
|
+
*/
|
|
2025
2826
|
function makeEvent(eventName, detail) {
|
|
2026
2827
|
let evt
|
|
2027
2828
|
if (window.CustomEvent && typeof window.CustomEvent === 'function') {
|
|
@@ -2035,10 +2836,19 @@ var htmx = (function() {
|
|
|
2035
2836
|
return evt
|
|
2036
2837
|
}
|
|
2037
2838
|
|
|
2839
|
+
/**
|
|
2840
|
+
* @param {EventTarget|string} elt
|
|
2841
|
+
* @param {string} eventName
|
|
2842
|
+
* @param {any=} detail
|
|
2843
|
+
*/
|
|
2038
2844
|
function triggerErrorEvent(elt, eventName, detail) {
|
|
2039
2845
|
triggerEvent(elt, eventName, mergeObjects({ error: eventName }, detail))
|
|
2040
2846
|
}
|
|
2041
2847
|
|
|
2848
|
+
/**
|
|
2849
|
+
* @param {string} eventName
|
|
2850
|
+
* @returns {boolean}
|
|
2851
|
+
*/
|
|
2042
2852
|
function ignoreEventForLogging(eventName) {
|
|
2043
2853
|
return eventName === 'htmx:afterProcessNode'
|
|
2044
2854
|
}
|
|
@@ -2048,8 +2858,8 @@ var htmx = (function() {
|
|
|
2048
2858
|
* executes the provided function using each of the active extensions. It should
|
|
2049
2859
|
* be called internally at every extendable execution point in htmx.
|
|
2050
2860
|
*
|
|
2051
|
-
* @param {
|
|
2052
|
-
* @param {(extension:
|
|
2861
|
+
* @param {Element} elt
|
|
2862
|
+
* @param {(extension:HtmxExtension) => void} toDo
|
|
2053
2863
|
* @returns void
|
|
2054
2864
|
*/
|
|
2055
2865
|
function withExtensions(elt, toDo) {
|
|
@@ -2070,6 +2880,16 @@ var htmx = (function() {
|
|
|
2070
2880
|
}
|
|
2071
2881
|
}
|
|
2072
2882
|
|
|
2883
|
+
/**
|
|
2884
|
+
* Triggers a given event on an element
|
|
2885
|
+
*
|
|
2886
|
+
* @see https://htmx.org/api/#trigger
|
|
2887
|
+
*
|
|
2888
|
+
* @param {EventTarget|string} elt the element to trigger the event on
|
|
2889
|
+
* @param {string} eventName the name of the event to trigger
|
|
2890
|
+
* @param {any=} detail details for the event
|
|
2891
|
+
* @returns {boolean}
|
|
2892
|
+
*/
|
|
2073
2893
|
function triggerEvent(elt, eventName, detail) {
|
|
2074
2894
|
elt = resolveTarget(elt)
|
|
2075
2895
|
if (detail == null) {
|
|
@@ -2090,7 +2910,7 @@ var htmx = (function() {
|
|
|
2090
2910
|
const kebabedEvent = makeEvent(kebabName, event.detail)
|
|
2091
2911
|
eventResult = eventResult && elt.dispatchEvent(kebabedEvent)
|
|
2092
2912
|
}
|
|
2093
|
-
withExtensions(elt, function(extension) {
|
|
2913
|
+
withExtensions(asElement(elt), function(extension) {
|
|
2094
2914
|
eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented)
|
|
2095
2915
|
})
|
|
2096
2916
|
return eventResult
|
|
@@ -2101,11 +2921,18 @@ var htmx = (function() {
|
|
|
2101
2921
|
//= ===================================================================
|
|
2102
2922
|
let currentPathForHistory = location.pathname + location.search
|
|
2103
2923
|
|
|
2924
|
+
/**
|
|
2925
|
+
* @returns {Element}
|
|
2926
|
+
*/
|
|
2104
2927
|
function getHistoryElement() {
|
|
2105
2928
|
const historyElt = getDocument().querySelector('[hx-history-elt],[data-hx-history-elt]')
|
|
2106
2929
|
return historyElt || getDocument().body
|
|
2107
2930
|
}
|
|
2108
2931
|
|
|
2932
|
+
/**
|
|
2933
|
+
* @param {string} url
|
|
2934
|
+
* @param {Element} rootElt
|
|
2935
|
+
*/
|
|
2109
2936
|
function saveToHistoryCache(url, rootElt) {
|
|
2110
2937
|
if (!canAccessLocalStorage()) {
|
|
2111
2938
|
return
|
|
@@ -2132,6 +2959,7 @@ var htmx = (function() {
|
|
|
2132
2959
|
}
|
|
2133
2960
|
}
|
|
2134
2961
|
|
|
2962
|
+
/** @type HtmxHistoryItem */
|
|
2135
2963
|
const newHistoryItem = { url, content: innerHTML, title, scroll }
|
|
2136
2964
|
|
|
2137
2965
|
triggerEvent(getDocument().body, 'htmx:historyItemCreated', { item: newHistoryItem, cache: historyCache })
|
|
@@ -2153,6 +2981,18 @@ var htmx = (function() {
|
|
|
2153
2981
|
}
|
|
2154
2982
|
}
|
|
2155
2983
|
|
|
2984
|
+
/**
|
|
2985
|
+
* @typedef {Object} HtmxHistoryItem
|
|
2986
|
+
* @property {string} url
|
|
2987
|
+
* @property {string} content
|
|
2988
|
+
* @property {string} title
|
|
2989
|
+
* @property {number} scroll
|
|
2990
|
+
*/
|
|
2991
|
+
|
|
2992
|
+
/**
|
|
2993
|
+
* @param {string} url
|
|
2994
|
+
* @returns {HtmxHistoryItem|null}
|
|
2995
|
+
*/
|
|
2156
2996
|
function getCachedHistory(url) {
|
|
2157
2997
|
if (!canAccessLocalStorage()) {
|
|
2158
2998
|
return null
|
|
@@ -2169,9 +3009,13 @@ var htmx = (function() {
|
|
|
2169
3009
|
return null
|
|
2170
3010
|
}
|
|
2171
3011
|
|
|
3012
|
+
/**
|
|
3013
|
+
* @param {Element} elt
|
|
3014
|
+
* @returns {string}
|
|
3015
|
+
*/
|
|
2172
3016
|
function cleanInnerHtmlForHistory(elt) {
|
|
2173
3017
|
const className = htmx.config.requestClass
|
|
2174
|
-
const clone = elt.cloneNode(true)
|
|
3018
|
+
const clone = /** @type Element */ (elt.cloneNode(true))
|
|
2175
3019
|
forEach(findAll(clone, '.' + className), function(child) {
|
|
2176
3020
|
removeClassFromElement(child, className)
|
|
2177
3021
|
})
|
|
@@ -2202,6 +3046,9 @@ var htmx = (function() {
|
|
|
2202
3046
|
if (htmx.config.historyEnabled) history.replaceState({ htmx: true }, getDocument().title, window.location.href)
|
|
2203
3047
|
}
|
|
2204
3048
|
|
|
3049
|
+
/**
|
|
3050
|
+
* @param {string} path
|
|
3051
|
+
*/
|
|
2205
3052
|
function pushUrlIntoHistory(path) {
|
|
2206
3053
|
// remove the cache buster parameter, if any
|
|
2207
3054
|
if (htmx.config.getCacheBusterParam) {
|
|
@@ -2216,17 +3063,26 @@ var htmx = (function() {
|
|
|
2216
3063
|
currentPathForHistory = path
|
|
2217
3064
|
}
|
|
2218
3065
|
|
|
3066
|
+
/**
|
|
3067
|
+
* @param {string} path
|
|
3068
|
+
*/
|
|
2219
3069
|
function replaceUrlInHistory(path) {
|
|
2220
3070
|
if (htmx.config.historyEnabled) history.replaceState({ htmx: true }, '', path)
|
|
2221
3071
|
currentPathForHistory = path
|
|
2222
3072
|
}
|
|
2223
3073
|
|
|
3074
|
+
/**
|
|
3075
|
+
* @param {HtmxSettleTask[]} tasks
|
|
3076
|
+
*/
|
|
2224
3077
|
function settleImmediately(tasks) {
|
|
2225
3078
|
forEach(tasks, function(task) {
|
|
2226
|
-
task.call()
|
|
3079
|
+
task.call(undefined)
|
|
2227
3080
|
})
|
|
2228
3081
|
}
|
|
2229
3082
|
|
|
3083
|
+
/**
|
|
3084
|
+
* @param {string} path
|
|
3085
|
+
*/
|
|
2230
3086
|
function loadHistoryFromServer(path) {
|
|
2231
3087
|
const request = new XMLHttpRequest()
|
|
2232
3088
|
const details = { path, xhr: request }
|
|
@@ -2239,13 +3095,12 @@ var htmx = (function() {
|
|
|
2239
3095
|
if (this.status >= 200 && this.status < 400) {
|
|
2240
3096
|
triggerEvent(getDocument().body, 'htmx:historyCacheMissLoad', details)
|
|
2241
3097
|
const fragment = makeFragment(this.response)
|
|
2242
|
-
|
|
3098
|
+
/** @type ParentNode */
|
|
2243
3099
|
const content = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment
|
|
2244
3100
|
const historyElement = getHistoryElement()
|
|
2245
3101
|
const settleInfo = makeSettleInfo(historyElement)
|
|
2246
3102
|
handleTitle(fragment.title)
|
|
2247
3103
|
|
|
2248
|
-
// @ts-ignore
|
|
2249
3104
|
swapInnerHTML(historyElement, content, settleInfo)
|
|
2250
3105
|
settleImmediately(settleInfo.tasks)
|
|
2251
3106
|
currentPathForHistory = path
|
|
@@ -2257,6 +3112,9 @@ var htmx = (function() {
|
|
|
2257
3112
|
request.send()
|
|
2258
3113
|
}
|
|
2259
3114
|
|
|
3115
|
+
/**
|
|
3116
|
+
* @param {string} [path]
|
|
3117
|
+
*/
|
|
2260
3118
|
function restoreHistory(path) {
|
|
2261
3119
|
saveCurrentPageToHistory()
|
|
2262
3120
|
path = path || location.pathname + location.search
|
|
@@ -2268,14 +3126,15 @@ var htmx = (function() {
|
|
|
2268
3126
|
handleTitle(fragment.title)
|
|
2269
3127
|
swapInnerHTML(historyElement, fragment, settleInfo)
|
|
2270
3128
|
settleImmediately(settleInfo.tasks)
|
|
2271
|
-
setTimeout(function() {
|
|
3129
|
+
getWindow().setTimeout(function() {
|
|
2272
3130
|
window.scrollTo(0, cached.scroll)
|
|
2273
3131
|
}, 0) // next 'tick', so browser has time to render layout
|
|
2274
3132
|
currentPathForHistory = path
|
|
2275
3133
|
triggerEvent(getDocument().body, 'htmx:historyRestore', { path, item: cached })
|
|
2276
3134
|
} else {
|
|
2277
3135
|
if (htmx.config.refreshOnHistoryMiss) {
|
|
2278
|
-
|
|
3136
|
+
// @ts-ignore: optional parameter in reload() function throws error
|
|
3137
|
+
// noinspection JSUnresolvedReference
|
|
2279
3138
|
window.location.reload(true)
|
|
2280
3139
|
} else {
|
|
2281
3140
|
loadHistoryFromServer(path)
|
|
@@ -2283,8 +3142,12 @@ var htmx = (function() {
|
|
|
2283
3142
|
}
|
|
2284
3143
|
}
|
|
2285
3144
|
|
|
3145
|
+
/**
|
|
3146
|
+
* @param {Element} elt
|
|
3147
|
+
* @returns {Element[]}
|
|
3148
|
+
*/
|
|
2286
3149
|
function addRequestIndicatorClasses(elt) {
|
|
2287
|
-
let indicators = findAttributeTargets(elt, 'hx-indicator')
|
|
3150
|
+
let indicators = /** @type Element[] */ (findAttributeTargets(elt, 'hx-indicator'))
|
|
2288
3151
|
if (indicators == null) {
|
|
2289
3152
|
indicators = [elt]
|
|
2290
3153
|
}
|
|
@@ -2296,8 +3159,12 @@ var htmx = (function() {
|
|
|
2296
3159
|
return indicators
|
|
2297
3160
|
}
|
|
2298
3161
|
|
|
3162
|
+
/**
|
|
3163
|
+
* @param {Element} elt
|
|
3164
|
+
* @returns {Element[]}
|
|
3165
|
+
*/
|
|
2299
3166
|
function disableElements(elt) {
|
|
2300
|
-
let disabledElts = findAttributeTargets(elt, 'hx-disabled-elt')
|
|
3167
|
+
let disabledElts = /** @type Element[] */ (findAttributeTargets(elt, 'hx-disabled-elt'))
|
|
2301
3168
|
if (disabledElts == null) {
|
|
2302
3169
|
disabledElts = []
|
|
2303
3170
|
}
|
|
@@ -2309,6 +3176,10 @@ var htmx = (function() {
|
|
|
2309
3176
|
return disabledElts
|
|
2310
3177
|
}
|
|
2311
3178
|
|
|
3179
|
+
/**
|
|
3180
|
+
* @param {Element[]} indicators
|
|
3181
|
+
* @param {Element[]} disabled
|
|
3182
|
+
*/
|
|
2312
3183
|
function removeRequestIndicators(indicators, disabled) {
|
|
2313
3184
|
forEach(indicators, function(ic) {
|
|
2314
3185
|
const internalData = getInternalData(ic)
|
|
@@ -2331,8 +3202,8 @@ var htmx = (function() {
|
|
|
2331
3202
|
//= ===================================================================
|
|
2332
3203
|
|
|
2333
3204
|
/**
|
|
2334
|
-
* @param {
|
|
2335
|
-
* @param {
|
|
3205
|
+
* @param {Element[]} processed
|
|
3206
|
+
* @param {Element} elt
|
|
2336
3207
|
* @returns {boolean}
|
|
2337
3208
|
*/
|
|
2338
3209
|
function haveSeenNode(processed, elt) {
|
|
@@ -2345,7 +3216,13 @@ var htmx = (function() {
|
|
|
2345
3216
|
return false
|
|
2346
3217
|
}
|
|
2347
3218
|
|
|
2348
|
-
|
|
3219
|
+
/**
|
|
3220
|
+
* @param {Element} element
|
|
3221
|
+
* @return {boolean}
|
|
3222
|
+
*/
|
|
3223
|
+
function shouldInclude(element) {
|
|
3224
|
+
// Cast to trick tsc, undefined values will work fine here
|
|
3225
|
+
const elt = /** @type {HTMLInputElement} */ (element)
|
|
2349
3226
|
if (elt.name === '' || elt.name == null || elt.disabled || closest(elt, 'fieldset[disabled]')) {
|
|
2350
3227
|
return false
|
|
2351
3228
|
}
|
|
@@ -2360,7 +3237,7 @@ var htmx = (function() {
|
|
|
2360
3237
|
}
|
|
2361
3238
|
|
|
2362
3239
|
/** @param {string} name
|
|
2363
|
-
* @param {string|Array} value
|
|
3240
|
+
* @param {string|Array|FormDataEntryValue} value
|
|
2364
3241
|
* @param {FormData} formData */
|
|
2365
3242
|
function addValueToFormData(name, value, formData) {
|
|
2366
3243
|
if (name != null && value != null) {
|
|
@@ -2389,10 +3266,10 @@ var htmx = (function() {
|
|
|
2389
3266
|
}
|
|
2390
3267
|
|
|
2391
3268
|
/**
|
|
2392
|
-
* @param {
|
|
3269
|
+
* @param {Element[]} processed
|
|
2393
3270
|
* @param {FormData} formData
|
|
2394
3271
|
* @param {HtmxElementValidationError[]} errors
|
|
2395
|
-
* @param {
|
|
3272
|
+
* @param {Element|HTMLInputElement|HTMLSelectElement|HTMLFormElement} elt
|
|
2396
3273
|
* @param {boolean} validate
|
|
2397
3274
|
*/
|
|
2398
3275
|
function processInputValue(processed, formData, errors, elt, validate) {
|
|
@@ -2403,12 +3280,13 @@ var htmx = (function() {
|
|
|
2403
3280
|
}
|
|
2404
3281
|
if (shouldInclude(elt)) {
|
|
2405
3282
|
const name = getRawAttribute(elt, 'name')
|
|
3283
|
+
// @ts-ignore value will be undefined for non-input elements, which is fine
|
|
2406
3284
|
let value = elt.value
|
|
2407
|
-
if (elt
|
|
2408
|
-
value = toArray(elt.querySelectorAll('option:checked')).map(function(e) { return e.value })
|
|
3285
|
+
if (elt instanceof HTMLSelectElement && elt.multiple) {
|
|
3286
|
+
value = toArray(elt.querySelectorAll('option:checked')).map(function(e) { return (/** @type HTMLOptionElement */(e)).value })
|
|
2409
3287
|
}
|
|
2410
3288
|
// include file inputs
|
|
2411
|
-
if (elt.files) {
|
|
3289
|
+
if (elt instanceof HTMLInputElement && elt.files) {
|
|
2412
3290
|
value = toArray(elt.files)
|
|
2413
3291
|
}
|
|
2414
3292
|
addValueToFormData(name, value, formData)
|
|
@@ -2416,7 +3294,7 @@ var htmx = (function() {
|
|
|
2416
3294
|
validateElement(elt, errors)
|
|
2417
3295
|
}
|
|
2418
3296
|
}
|
|
2419
|
-
if (
|
|
3297
|
+
if (elt instanceof HTMLFormElement) {
|
|
2420
3298
|
forEach(elt.elements, function(input) {
|
|
2421
3299
|
if (processed.indexOf(input) >= 0) {
|
|
2422
3300
|
// The input has already been processed and added to the values, but the FormData that will be
|
|
@@ -2436,16 +3314,13 @@ var htmx = (function() {
|
|
|
2436
3314
|
}
|
|
2437
3315
|
}
|
|
2438
3316
|
|
|
2439
|
-
/**
|
|
2440
|
-
* @typedef {{elt: HTMLElement, message: string, validity: ValidityState}} HtmxElementValidationError
|
|
2441
|
-
*/
|
|
2442
|
-
|
|
2443
3317
|
/**
|
|
2444
3318
|
*
|
|
2445
|
-
* @param {
|
|
3319
|
+
* @param {Element} elt
|
|
2446
3320
|
* @param {HtmxElementValidationError[]} errors
|
|
2447
3321
|
*/
|
|
2448
|
-
function validateElement(
|
|
3322
|
+
function validateElement(elt, errors) {
|
|
3323
|
+
const element = /** @type {HTMLElement & ElementInternals} */ (elt)
|
|
2449
3324
|
if (element.willValidate) {
|
|
2450
3325
|
triggerEvent(element, 'htmx:validation:validate')
|
|
2451
3326
|
if (!element.checkValidity()) {
|
|
@@ -2472,12 +3347,12 @@ var htmx = (function() {
|
|
|
2472
3347
|
}
|
|
2473
3348
|
|
|
2474
3349
|
/**
|
|
2475
|
-
* @param {
|
|
2476
|
-
* @param {
|
|
3350
|
+
* @param {Element|HTMLFormElement} elt
|
|
3351
|
+
* @param {HttpVerb} verb
|
|
2477
3352
|
* @returns {{errors: HtmxElementValidationError[], formData: FormData, values: Object}}
|
|
2478
3353
|
*/
|
|
2479
3354
|
function getInputValues(elt, verb) {
|
|
2480
|
-
/** @type
|
|
3355
|
+
/** @type Element[] */
|
|
2481
3356
|
const processed = []
|
|
2482
3357
|
const formData = new FormData()
|
|
2483
3358
|
const priorityFormData = new FormData()
|
|
@@ -2490,7 +3365,7 @@ var htmx = (function() {
|
|
|
2490
3365
|
|
|
2491
3366
|
// only validate when form is directly submitted and novalidate or formnovalidate are not set
|
|
2492
3367
|
// or if the element has an explicit hx-validate="true" on it
|
|
2493
|
-
let validate = (
|
|
3368
|
+
let validate = (elt instanceof HTMLFormElement && elt.noValidate !== true) || getAttributeValue(elt, 'hx-validate') === 'true'
|
|
2494
3369
|
if (internalData.lastButtonClicked) {
|
|
2495
3370
|
validate = validate && internalData.lastButtonClicked.formNoValidate !== true
|
|
2496
3371
|
}
|
|
@@ -2506,7 +3381,7 @@ var htmx = (function() {
|
|
|
2506
3381
|
// if a button or submit was clicked last, include its value
|
|
2507
3382
|
if (internalData.lastButtonClicked || elt.tagName === 'BUTTON' ||
|
|
2508
3383
|
(elt.tagName === 'INPUT' && getRawAttribute(elt, 'type') === 'submit')) {
|
|
2509
|
-
const button = internalData.lastButtonClicked || elt
|
|
3384
|
+
const button = internalData.lastButtonClicked || (/** @type HTMLInputElement|HTMLButtonElement */(elt))
|
|
2510
3385
|
const name = getRawAttribute(button, 'name')
|
|
2511
3386
|
addValueToFormData(name, button.value, priorityFormData)
|
|
2512
3387
|
}
|
|
@@ -2514,10 +3389,10 @@ var htmx = (function() {
|
|
|
2514
3389
|
// include any explicit includes
|
|
2515
3390
|
const includes = findAttributeTargets(elt, 'hx-include')
|
|
2516
3391
|
forEach(includes, function(node) {
|
|
2517
|
-
processInputValue(processed, formData, errors, node, validate)
|
|
3392
|
+
processInputValue(processed, formData, errors, asElement(node), validate)
|
|
2518
3393
|
// if a non-form is included, include any input values within it
|
|
2519
3394
|
if (!matches(node, 'form')) {
|
|
2520
|
-
forEach(node.querySelectorAll(INPUT_SELECTOR), function(descendant) {
|
|
3395
|
+
forEach(asParentNode(node).querySelectorAll(INPUT_SELECTOR), function(descendant) {
|
|
2521
3396
|
processInputValue(processed, formData, errors, descendant, validate)
|
|
2522
3397
|
})
|
|
2523
3398
|
}
|
|
@@ -2565,12 +3440,13 @@ var htmx = (function() {
|
|
|
2565
3440
|
//= ===================================================================
|
|
2566
3441
|
|
|
2567
3442
|
/**
|
|
2568
|
-
* @param {
|
|
2569
|
-
* @param {
|
|
3443
|
+
* @param {Element} elt
|
|
3444
|
+
* @param {Element} target
|
|
2570
3445
|
* @param {string} prompt
|
|
2571
|
-
* @returns {
|
|
3446
|
+
* @returns {HtmxHeaderSpecification}
|
|
2572
3447
|
*/
|
|
2573
3448
|
function getHeaders(elt, target, prompt) {
|
|
3449
|
+
/** @type HtmxHeaderSpecification */
|
|
2574
3450
|
const headers = {
|
|
2575
3451
|
'HX-Request': 'true',
|
|
2576
3452
|
'HX-Trigger': getRawAttribute(elt, 'id'),
|
|
@@ -2593,7 +3469,7 @@ var htmx = (function() {
|
|
|
2593
3469
|
* and returns a new object that only contains keys that are
|
|
2594
3470
|
* specified by the closest "hx-params" attribute
|
|
2595
3471
|
* @param {FormData} inputValues
|
|
2596
|
-
* @param {
|
|
3472
|
+
* @param {Element} elt
|
|
2597
3473
|
* @returns {FormData}
|
|
2598
3474
|
*/
|
|
2599
3475
|
function filterValues(inputValues, elt) {
|
|
@@ -2624,19 +3500,22 @@ var htmx = (function() {
|
|
|
2624
3500
|
}
|
|
2625
3501
|
}
|
|
2626
3502
|
|
|
3503
|
+
/**
|
|
3504
|
+
* @param {Element} elt
|
|
3505
|
+
* @return {boolean}
|
|
3506
|
+
*/
|
|
2627
3507
|
function isAnchorLink(elt) {
|
|
2628
|
-
return getRawAttribute(elt, 'href') && getRawAttribute(elt, 'href').indexOf('#') >= 0
|
|
3508
|
+
return !!getRawAttribute(elt, 'href') && getRawAttribute(elt, 'href').indexOf('#') >= 0
|
|
2629
3509
|
}
|
|
2630
3510
|
|
|
2631
3511
|
/**
|
|
2632
|
-
*
|
|
2633
|
-
* @param {
|
|
2634
|
-
* @
|
|
2635
|
-
* @returns {import("./htmx").HtmxSwapSpecification}
|
|
3512
|
+
* @param {Element} elt
|
|
3513
|
+
* @param {HtmxSwapStyle} [swapInfoOverride]
|
|
3514
|
+
* @returns {HtmxSwapSpecification}
|
|
2636
3515
|
*/
|
|
2637
3516
|
function getSwapSpecification(elt, swapInfoOverride) {
|
|
2638
3517
|
const swapInfo = swapInfoOverride || getClosestAttributeValue(elt, 'hx-swap')
|
|
2639
|
-
/** @type
|
|
3518
|
+
/** @type HtmxSwapSpecification */
|
|
2640
3519
|
const swapSpec = {
|
|
2641
3520
|
swapStyle: getInternalData(elt).boosted ? 'innerHTML' : htmx.config.defaultSwapStyle,
|
|
2642
3521
|
swapDelay: htmx.config.defaultSwapDelay,
|
|
@@ -2663,6 +3542,7 @@ var htmx = (function() {
|
|
|
2663
3542
|
var splitSpec = scrollSpec.split(':')
|
|
2664
3543
|
const scrollVal = splitSpec.pop()
|
|
2665
3544
|
var selectorVal = splitSpec.length > 0 ? splitSpec.join(':') : null
|
|
3545
|
+
// @ts-ignore
|
|
2666
3546
|
swapSpec.scroll = scrollVal
|
|
2667
3547
|
swapSpec.scrollTarget = selectorVal
|
|
2668
3548
|
} else if (value.indexOf('show:') === 0) {
|
|
@@ -2686,6 +3566,10 @@ var htmx = (function() {
|
|
|
2686
3566
|
return swapSpec
|
|
2687
3567
|
}
|
|
2688
3568
|
|
|
3569
|
+
/**
|
|
3570
|
+
* @param {Element} elt
|
|
3571
|
+
* @return {boolean}
|
|
3572
|
+
*/
|
|
2689
3573
|
function usesFormData(elt) {
|
|
2690
3574
|
return getClosestAttributeValue(elt, 'hx-encoding') === 'multipart/form-data' ||
|
|
2691
3575
|
(matches(elt, 'form') && getRawAttribute(elt, 'enctype') === 'multipart/form-data')
|
|
@@ -2693,7 +3577,7 @@ var htmx = (function() {
|
|
|
2693
3577
|
|
|
2694
3578
|
/**
|
|
2695
3579
|
* @param {XMLHttpRequest} xhr
|
|
2696
|
-
* @param {
|
|
3580
|
+
* @param {Element} elt
|
|
2697
3581
|
* @param {FormData} filteredParameters
|
|
2698
3582
|
* @returns {*|string|null}
|
|
2699
3583
|
*/
|
|
@@ -2718,19 +3602,23 @@ var htmx = (function() {
|
|
|
2718
3602
|
/**
|
|
2719
3603
|
*
|
|
2720
3604
|
* @param {Element} target
|
|
2721
|
-
* @returns {
|
|
3605
|
+
* @returns {HtmxSettleInfo}
|
|
2722
3606
|
*/
|
|
2723
3607
|
function makeSettleInfo(target) {
|
|
2724
3608
|
return { tasks: [], elts: [target] }
|
|
2725
3609
|
}
|
|
2726
3610
|
|
|
3611
|
+
/**
|
|
3612
|
+
* @param {Element[]} content
|
|
3613
|
+
* @param {HtmxSwapSpecification} swapSpec
|
|
3614
|
+
*/
|
|
2727
3615
|
function updateScrollState(content, swapSpec) {
|
|
2728
3616
|
const first = content[0]
|
|
2729
3617
|
const last = content[content.length - 1]
|
|
2730
3618
|
if (swapSpec.scroll) {
|
|
2731
3619
|
var target = null
|
|
2732
3620
|
if (swapSpec.scrollTarget) {
|
|
2733
|
-
target = querySelectorExt(first, swapSpec.scrollTarget)
|
|
3621
|
+
target = asElement(querySelectorExt(first, swapSpec.scrollTarget))
|
|
2734
3622
|
}
|
|
2735
3623
|
if (swapSpec.scroll === 'top' && (first || target)) {
|
|
2736
3624
|
target = target || first
|
|
@@ -2748,21 +3636,23 @@ var htmx = (function() {
|
|
|
2748
3636
|
if (swapSpec.showTarget === 'window') {
|
|
2749
3637
|
targetStr = 'body'
|
|
2750
3638
|
}
|
|
2751
|
-
target = querySelectorExt(first, targetStr)
|
|
3639
|
+
target = asElement(querySelectorExt(first, targetStr))
|
|
2752
3640
|
}
|
|
2753
3641
|
if (swapSpec.show === 'top' && (first || target)) {
|
|
2754
3642
|
target = target || first
|
|
3643
|
+
// @ts-ignore For some reason tsc doesn't recognize "instant" as a valid option for now
|
|
2755
3644
|
target.scrollIntoView({ block: 'start', behavior: htmx.config.scrollBehavior })
|
|
2756
3645
|
}
|
|
2757
3646
|
if (swapSpec.show === 'bottom' && (last || target)) {
|
|
2758
3647
|
target = target || last
|
|
3648
|
+
// @ts-ignore For some reason tsc doesn't recognize "instant" as a valid option for now
|
|
2759
3649
|
target.scrollIntoView({ block: 'end', behavior: htmx.config.scrollBehavior })
|
|
2760
3650
|
}
|
|
2761
3651
|
}
|
|
2762
3652
|
}
|
|
2763
3653
|
|
|
2764
3654
|
/**
|
|
2765
|
-
* @param {
|
|
3655
|
+
* @param {Element} elt
|
|
2766
3656
|
* @param {string} attr
|
|
2767
3657
|
* @param {boolean=} evalAsDefault
|
|
2768
3658
|
* @param {Object=} values
|
|
@@ -2806,9 +3696,15 @@ var htmx = (function() {
|
|
|
2806
3696
|
}
|
|
2807
3697
|
}
|
|
2808
3698
|
}
|
|
2809
|
-
return getValuesForElement(parentElt(elt), attr, evalAsDefault, values)
|
|
3699
|
+
return getValuesForElement(asElement(parentElt(elt)), attr, evalAsDefault, values)
|
|
2810
3700
|
}
|
|
2811
3701
|
|
|
3702
|
+
/**
|
|
3703
|
+
* @param {EventTarget|string} elt
|
|
3704
|
+
* @param {() => any} toEval
|
|
3705
|
+
* @param {any=} defaultVal
|
|
3706
|
+
* @returns {any}
|
|
3707
|
+
*/
|
|
2812
3708
|
function maybeEval(elt, toEval, defaultVal) {
|
|
2813
3709
|
if (htmx.config.allowEval) {
|
|
2814
3710
|
return toEval()
|
|
@@ -2819,7 +3715,7 @@ var htmx = (function() {
|
|
|
2819
3715
|
}
|
|
2820
3716
|
|
|
2821
3717
|
/**
|
|
2822
|
-
* @param {
|
|
3718
|
+
* @param {Element} elt
|
|
2823
3719
|
* @param {*?} expressionVars
|
|
2824
3720
|
* @returns
|
|
2825
3721
|
*/
|
|
@@ -2828,7 +3724,7 @@ var htmx = (function() {
|
|
|
2828
3724
|
}
|
|
2829
3725
|
|
|
2830
3726
|
/**
|
|
2831
|
-
* @param {
|
|
3727
|
+
* @param {Element} elt
|
|
2832
3728
|
* @param {*?} expressionVars
|
|
2833
3729
|
* @returns
|
|
2834
3730
|
*/
|
|
@@ -2837,13 +3733,18 @@ var htmx = (function() {
|
|
|
2837
3733
|
}
|
|
2838
3734
|
|
|
2839
3735
|
/**
|
|
2840
|
-
* @param {
|
|
3736
|
+
* @param {Element} elt
|
|
2841
3737
|
* @returns {FormData}
|
|
2842
3738
|
*/
|
|
2843
3739
|
function getExpressionVars(elt) {
|
|
2844
3740
|
return formDataFromObject(mergeObjects(getHXVarsForElement(elt), getHXValsForElement(elt)))
|
|
2845
3741
|
}
|
|
2846
3742
|
|
|
3743
|
+
/**
|
|
3744
|
+
* @param {XMLHttpRequest} xhr
|
|
3745
|
+
* @param {string} header
|
|
3746
|
+
* @param {string|null} headerValue
|
|
3747
|
+
*/
|
|
2847
3748
|
function safelySetHeaderValue(xhr, header, headerValue) {
|
|
2848
3749
|
if (headerValue !== null) {
|
|
2849
3750
|
try {
|
|
@@ -2856,6 +3757,10 @@ var htmx = (function() {
|
|
|
2856
3757
|
}
|
|
2857
3758
|
}
|
|
2858
3759
|
|
|
3760
|
+
/**
|
|
3761
|
+
* @param {XMLHttpRequest} xhr
|
|
3762
|
+
* @return {string}
|
|
3763
|
+
*/
|
|
2859
3764
|
function getPathFromResponse(xhr) {
|
|
2860
3765
|
// NB: IE11 does not support this stuff
|
|
2861
3766
|
if (xhr.responseURL && typeof (URL) !== 'undefined') {
|
|
@@ -2868,14 +3773,29 @@ var htmx = (function() {
|
|
|
2868
3773
|
}
|
|
2869
3774
|
}
|
|
2870
3775
|
|
|
3776
|
+
/**
|
|
3777
|
+
* @param {XMLHttpRequest} xhr
|
|
3778
|
+
* @param {RegExp} regexp
|
|
3779
|
+
* @return {boolean}
|
|
3780
|
+
*/
|
|
2871
3781
|
function hasHeader(xhr, regexp) {
|
|
2872
3782
|
return regexp.test(xhr.getAllResponseHeaders())
|
|
2873
3783
|
}
|
|
2874
3784
|
|
|
3785
|
+
/**
|
|
3786
|
+
* Issues an htmx-style AJAX request
|
|
3787
|
+
*
|
|
3788
|
+
* @see https://htmx.org/api/#ajax
|
|
3789
|
+
*
|
|
3790
|
+
* @param {HttpVerb} verb
|
|
3791
|
+
* @param {string} path the URL path to make the AJAX
|
|
3792
|
+
* @param {Element|string|HtmxAjaxHelperContext} context the element to target (defaults to the **body**) | a selector for the target | a context object that contains any of the following
|
|
3793
|
+
* @return {Promise<void>} Promise that resolves immediately if no request is sent, or when the request is complete
|
|
3794
|
+
*/
|
|
2875
3795
|
function ajaxHelper(verb, path, context) {
|
|
2876
|
-
verb = verb.toLowerCase()
|
|
3796
|
+
verb = (/** @type HttpVerb */(verb.toLowerCase()))
|
|
2877
3797
|
if (context) {
|
|
2878
|
-
if (context instanceof Element ||
|
|
3798
|
+
if (context instanceof Element || typeof context === 'string') {
|
|
2879
3799
|
return issueAjaxRequest(verb, path, null, null, {
|
|
2880
3800
|
targetOverride: resolveTarget(context),
|
|
2881
3801
|
returnPromise: true
|
|
@@ -2899,6 +3819,10 @@ var htmx = (function() {
|
|
|
2899
3819
|
}
|
|
2900
3820
|
}
|
|
2901
3821
|
|
|
3822
|
+
/**
|
|
3823
|
+
* @param {Element} elt
|
|
3824
|
+
* @return {Element[]}
|
|
3825
|
+
*/
|
|
2902
3826
|
function hierarchyForElt(elt) {
|
|
2903
3827
|
const arr = []
|
|
2904
3828
|
while (elt) {
|
|
@@ -2908,6 +3832,12 @@ var htmx = (function() {
|
|
|
2908
3832
|
return arr
|
|
2909
3833
|
}
|
|
2910
3834
|
|
|
3835
|
+
/**
|
|
3836
|
+
* @param {Element} elt
|
|
3837
|
+
* @param {string} path
|
|
3838
|
+
* @param {HtmxRequestConfig} requestConfig
|
|
3839
|
+
* @return {boolean}
|
|
3840
|
+
*/
|
|
2911
3841
|
function verifyPath(elt, path, requestConfig) {
|
|
2912
3842
|
let sameHost
|
|
2913
3843
|
let url
|
|
@@ -2929,6 +3859,10 @@ var htmx = (function() {
|
|
|
2929
3859
|
return triggerEvent(elt, 'htmx:validateUrl', mergeObjects({ url, sameHost }, requestConfig))
|
|
2930
3860
|
}
|
|
2931
3861
|
|
|
3862
|
+
/**
|
|
3863
|
+
* @param {Object|FormData} obj
|
|
3864
|
+
* @return {FormData}
|
|
3865
|
+
*/
|
|
2932
3866
|
function formDataFromObject(obj) {
|
|
2933
3867
|
if (obj instanceof FormData) return obj
|
|
2934
3868
|
const formData = new FormData()
|
|
@@ -2948,7 +3882,7 @@ var htmx = (function() {
|
|
|
2948
3882
|
|
|
2949
3883
|
/**
|
|
2950
3884
|
* @param {FormData} formData
|
|
2951
|
-
* @param {string
|
|
3885
|
+
* @param {string} name
|
|
2952
3886
|
* @param {Array} array
|
|
2953
3887
|
* @returns {Array}
|
|
2954
3888
|
*/
|
|
@@ -2996,7 +3930,7 @@ var htmx = (function() {
|
|
|
2996
3930
|
get: function(target, name) {
|
|
2997
3931
|
if (typeof name === 'symbol') {
|
|
2998
3932
|
// Forward symbol calls to the FormData itself directly
|
|
2999
|
-
return Reflect.get(
|
|
3933
|
+
return Reflect.get(target, name)
|
|
3000
3934
|
}
|
|
3001
3935
|
if (name === 'toJSON') {
|
|
3002
3936
|
// Support JSON.stringify call on proxy
|
|
@@ -3023,6 +3957,9 @@ var htmx = (function() {
|
|
|
3023
3957
|
}
|
|
3024
3958
|
},
|
|
3025
3959
|
set: function(target, name, value) {
|
|
3960
|
+
if (typeof name !== 'string') {
|
|
3961
|
+
return false
|
|
3962
|
+
}
|
|
3026
3963
|
target.delete(name)
|
|
3027
3964
|
if (typeof value.forEach === 'function') {
|
|
3028
3965
|
value.forEach(function(v) { target.append(name, v) })
|
|
@@ -3032,7 +3969,9 @@ var htmx = (function() {
|
|
|
3032
3969
|
return true
|
|
3033
3970
|
},
|
|
3034
3971
|
deleteProperty: function(target, name) {
|
|
3035
|
-
|
|
3972
|
+
if (typeof name === 'string') {
|
|
3973
|
+
target.delete(name)
|
|
3974
|
+
}
|
|
3036
3975
|
return true
|
|
3037
3976
|
},
|
|
3038
3977
|
// Support Object.assign call from proxy
|
|
@@ -3045,6 +3984,15 @@ var htmx = (function() {
|
|
|
3045
3984
|
})
|
|
3046
3985
|
}
|
|
3047
3986
|
|
|
3987
|
+
/**
|
|
3988
|
+
* @param {HttpVerb} verb
|
|
3989
|
+
* @param {string} path
|
|
3990
|
+
* @param {Element} elt
|
|
3991
|
+
* @param {Event} event
|
|
3992
|
+
* @param {HtmxAjaxEtc} [etc]
|
|
3993
|
+
* @param {boolean} [confirmed]
|
|
3994
|
+
* @return {Promise<void>}
|
|
3995
|
+
*/
|
|
3048
3996
|
function issueAjaxRequest(verb, path, elt, event, etc, confirmed) {
|
|
3049
3997
|
let resolve = null
|
|
3050
3998
|
let reject = null
|
|
@@ -3066,7 +4014,7 @@ var htmx = (function() {
|
|
|
3066
4014
|
maybeCall(resolve)
|
|
3067
4015
|
return promise
|
|
3068
4016
|
}
|
|
3069
|
-
const target = etc.targetOverride || getTarget(elt)
|
|
4017
|
+
const target = etc.targetOverride || asElement(getTarget(elt))
|
|
3070
4018
|
if (target == null || target == DUMMY_ELT) {
|
|
3071
4019
|
triggerErrorEvent(elt, 'htmx:targetError', { target: getAttributeValue(elt, 'hx-target') })
|
|
3072
4020
|
maybeCall(reject)
|
|
@@ -3086,7 +4034,7 @@ var htmx = (function() {
|
|
|
3086
4034
|
if (buttonVerb != null) {
|
|
3087
4035
|
// ignore buttons with formmethod="dialog"
|
|
3088
4036
|
if (buttonVerb.toLowerCase() !== 'dialog') {
|
|
3089
|
-
verb = buttonVerb
|
|
4037
|
+
verb = (/** @type HttpVerb */(buttonVerb))
|
|
3090
4038
|
}
|
|
3091
4039
|
}
|
|
3092
4040
|
}
|
|
@@ -3114,7 +4062,7 @@ var htmx = (function() {
|
|
|
3114
4062
|
if (selector === 'this') {
|
|
3115
4063
|
syncElt = findThisElement(elt, 'hx-sync')
|
|
3116
4064
|
} else {
|
|
3117
|
-
syncElt = querySelectorExt(elt, selector)
|
|
4065
|
+
syncElt = asElement(querySelectorExt(elt, selector))
|
|
3118
4066
|
}
|
|
3119
4067
|
// default to the drop strategy
|
|
3120
4068
|
syncStrategy = (syncStrings[1] || 'drop').trim()
|
|
@@ -3234,12 +4182,19 @@ var htmx = (function() {
|
|
|
3234
4182
|
path = getDocument().location.href
|
|
3235
4183
|
}
|
|
3236
4184
|
|
|
4185
|
+
/**
|
|
4186
|
+
* @type {Object}
|
|
4187
|
+
* @property {boolean} [credentials]
|
|
4188
|
+
* @property {number} [timeout]
|
|
4189
|
+
* @property {boolean} [noHeaders]
|
|
4190
|
+
*/
|
|
3237
4191
|
const requestAttrValues = getValuesForElement(elt, 'hx-request')
|
|
3238
4192
|
|
|
3239
4193
|
const eltIsBoosted = getInternalData(elt).boosted
|
|
3240
4194
|
|
|
3241
4195
|
let useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(verb) >= 0
|
|
3242
4196
|
|
|
4197
|
+
/** @type HtmxRequestConfig */
|
|
3243
4198
|
const requestConfig = {
|
|
3244
4199
|
boosted: eltIsBoosted,
|
|
3245
4200
|
useUrlParams,
|
|
@@ -3303,7 +4258,7 @@ var htmx = (function() {
|
|
|
3303
4258
|
triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig)
|
|
3304
4259
|
maybeCall(reject)
|
|
3305
4260
|
return promise
|
|
3306
|
-
}
|
|
4261
|
+
}
|
|
3307
4262
|
|
|
3308
4263
|
xhr.open(verb.toUpperCase(), finalPath, true)
|
|
3309
4264
|
xhr.overrideMimeType('text/html')
|
|
@@ -3322,6 +4277,7 @@ var htmx = (function() {
|
|
|
3322
4277
|
}
|
|
3323
4278
|
}
|
|
3324
4279
|
|
|
4280
|
+
/** @type {HtmxResponseInfo} */
|
|
3325
4281
|
const responseInfo = {
|
|
3326
4282
|
xhr,
|
|
3327
4283
|
target,
|
|
@@ -3332,6 +4288,7 @@ var htmx = (function() {
|
|
|
3332
4288
|
pathInfo: {
|
|
3333
4289
|
requestPath: path,
|
|
3334
4290
|
finalRequestPath: finalPath,
|
|
4291
|
+
responsePath: null,
|
|
3335
4292
|
anchor
|
|
3336
4293
|
}
|
|
3337
4294
|
}
|
|
@@ -3412,6 +4369,17 @@ var htmx = (function() {
|
|
|
3412
4369
|
return promise
|
|
3413
4370
|
}
|
|
3414
4371
|
|
|
4372
|
+
/**
|
|
4373
|
+
* @typedef {Object} HtmxHistoryUpdate
|
|
4374
|
+
* @property {string|null} [type]
|
|
4375
|
+
* @property {string|null} [path]
|
|
4376
|
+
*/
|
|
4377
|
+
|
|
4378
|
+
/**
|
|
4379
|
+
* @param {Element} elt
|
|
4380
|
+
* @param {HtmxResponseInfo} responseInfo
|
|
4381
|
+
* @return {HtmxHistoryUpdate}
|
|
4382
|
+
*/
|
|
3415
4383
|
function determineHistoryUpdates(elt, responseInfo) {
|
|
3416
4384
|
const xhr = responseInfo.xhr
|
|
3417
4385
|
|
|
@@ -3492,13 +4460,23 @@ var htmx = (function() {
|
|
|
3492
4460
|
}
|
|
3493
4461
|
}
|
|
3494
4462
|
|
|
4463
|
+
/**
|
|
4464
|
+
* @param {HtmxResponseHandlingConfig} responseHandlingConfig
|
|
4465
|
+
* @param {number} status
|
|
4466
|
+
* @return {boolean}
|
|
4467
|
+
*/
|
|
3495
4468
|
function codeMatches(responseHandlingConfig, status) {
|
|
3496
4469
|
var regExp = new RegExp(responseHandlingConfig.code)
|
|
3497
|
-
return regExp.test(status)
|
|
4470
|
+
return regExp.test(status.toString(10))
|
|
3498
4471
|
}
|
|
3499
4472
|
|
|
4473
|
+
/**
|
|
4474
|
+
* @param {XMLHttpRequest} xhr
|
|
4475
|
+
* @return {HtmxResponseHandlingConfig}
|
|
4476
|
+
*/
|
|
3500
4477
|
function resolveResponseHandling(xhr) {
|
|
3501
4478
|
for (var i = 0; i < htmx.config.responseHandling.length; i++) {
|
|
4479
|
+
/** @type HtmxResponseHandlingConfig */
|
|
3502
4480
|
var responseHandlingElement = htmx.config.responseHandling[i]
|
|
3503
4481
|
if (codeMatches(responseHandlingElement, xhr.status)) {
|
|
3504
4482
|
return responseHandlingElement
|
|
@@ -3510,6 +4488,9 @@ var htmx = (function() {
|
|
|
3510
4488
|
}
|
|
3511
4489
|
}
|
|
3512
4490
|
|
|
4491
|
+
/**
|
|
4492
|
+
* @param {string} title
|
|
4493
|
+
*/
|
|
3513
4494
|
function handleTitle(title) {
|
|
3514
4495
|
if (title) {
|
|
3515
4496
|
const titleElt = find('title')
|
|
@@ -3521,6 +4502,10 @@ var htmx = (function() {
|
|
|
3521
4502
|
}
|
|
3522
4503
|
}
|
|
3523
4504
|
|
|
4505
|
+
/**
|
|
4506
|
+
* @param {Element} elt
|
|
4507
|
+
* @param {HtmxResponseInfo} responseInfo
|
|
4508
|
+
*/
|
|
3524
4509
|
function handleAjaxResponse(elt, responseInfo) {
|
|
3525
4510
|
const xhr = responseInfo.xhr
|
|
3526
4511
|
let target = responseInfo.target
|
|
@@ -3536,14 +4521,15 @@ var htmx = (function() {
|
|
|
3536
4521
|
if (hasHeader(xhr, /HX-Location:/i)) {
|
|
3537
4522
|
saveCurrentPageToHistory()
|
|
3538
4523
|
let redirectPath = xhr.getResponseHeader('HX-Location')
|
|
3539
|
-
|
|
4524
|
+
/** @type {HtmxAjaxHelperContext&{path:string}} */
|
|
4525
|
+
var redirectSwapSpec
|
|
3540
4526
|
if (redirectPath.indexOf('{') === 0) {
|
|
3541
|
-
|
|
4527
|
+
redirectSwapSpec = parseJSON(redirectPath)
|
|
3542
4528
|
// what's the best way to throw an error if the user didn't include this
|
|
3543
|
-
redirectPath =
|
|
3544
|
-
delete
|
|
4529
|
+
redirectPath = redirectSwapSpec.path
|
|
4530
|
+
delete redirectSwapSpec.path
|
|
3545
4531
|
}
|
|
3546
|
-
ajaxHelper('
|
|
4532
|
+
ajaxHelper('get', redirectPath, redirectSwapSpec).then(function() {
|
|
3547
4533
|
pushUrlIntoHistory(redirectPath)
|
|
3548
4534
|
})
|
|
3549
4535
|
return
|
|
@@ -3566,7 +4552,7 @@ var htmx = (function() {
|
|
|
3566
4552
|
if (xhr.getResponseHeader('HX-Retarget') === 'this') {
|
|
3567
4553
|
responseInfo.target = elt
|
|
3568
4554
|
} else {
|
|
3569
|
-
responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader('HX-Retarget'))
|
|
4555
|
+
responseInfo.target = asElement(querySelectorExt(elt, xhr.getResponseHeader('HX-Retarget')))
|
|
3570
4556
|
}
|
|
3571
4557
|
}
|
|
3572
4558
|
|
|
@@ -3578,7 +4564,7 @@ var htmx = (function() {
|
|
|
3578
4564
|
let ignoreTitle = htmx.config.ignoreTitle || responseHandling.ignoreTitle
|
|
3579
4565
|
let selectOverride = responseHandling.select
|
|
3580
4566
|
if (responseHandling.target) {
|
|
3581
|
-
responseInfo.target = querySelectorExt(elt, responseHandling.target)
|
|
4567
|
+
responseInfo.target = asElement(querySelectorExt(elt, responseHandling.target))
|
|
3582
4568
|
}
|
|
3583
4569
|
var swapOverride = etc.swapOverride
|
|
3584
4570
|
if (swapOverride == null && responseHandling.swapOverride) {
|
|
@@ -3590,7 +4576,7 @@ var htmx = (function() {
|
|
|
3590
4576
|
if (xhr.getResponseHeader('HX-Retarget') === 'this') {
|
|
3591
4577
|
responseInfo.target = elt
|
|
3592
4578
|
} else {
|
|
3593
|
-
responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader('HX-Retarget'))
|
|
4579
|
+
responseInfo.target = asElement(querySelectorExt(elt, xhr.getResponseHeader('HX-Retarget')))
|
|
3594
4580
|
}
|
|
3595
4581
|
}
|
|
3596
4582
|
if (hasHeader(xhr, /HX-Reswap:/i)) {
|
|
@@ -3598,6 +4584,7 @@ var htmx = (function() {
|
|
|
3598
4584
|
}
|
|
3599
4585
|
|
|
3600
4586
|
var serverResponse = xhr.response
|
|
4587
|
+
/** @type HtmxBeforeSwapDetails */
|
|
3601
4588
|
var beforeSwapDetails = mergeObjects({
|
|
3602
4589
|
shouldSwap,
|
|
3603
4590
|
serverResponse,
|
|
@@ -3714,7 +4701,9 @@ var htmx = (function() {
|
|
|
3714
4701
|
|
|
3715
4702
|
if (shouldTransition &&
|
|
3716
4703
|
triggerEvent(elt, 'htmx:beforeTransition', responseInfo) &&
|
|
3717
|
-
typeof Promise !== 'undefined' &&
|
|
4704
|
+
typeof Promise !== 'undefined' &&
|
|
4705
|
+
// @ts-ignore experimental feature atm
|
|
4706
|
+
document.startViewTransition) {
|
|
3718
4707
|
const settlePromise = new Promise(function(_resolve, _reject) {
|
|
3719
4708
|
settleResolve = _resolve
|
|
3720
4709
|
settleReject = _reject
|
|
@@ -3722,6 +4711,7 @@ var htmx = (function() {
|
|
|
3722
4711
|
// wrap the original doSwap() in a call to startViewTransition()
|
|
3723
4712
|
const innerDoSwap = doSwap
|
|
3724
4713
|
doSwap = function() {
|
|
4714
|
+
// @ts-ignore experimental feature atm
|
|
3725
4715
|
document.startViewTransition(function() {
|
|
3726
4716
|
innerDoSwap()
|
|
3727
4717
|
return settlePromise
|
|
@@ -3730,7 +4720,7 @@ var htmx = (function() {
|
|
|
3730
4720
|
}
|
|
3731
4721
|
|
|
3732
4722
|
if (swapSpec.swapDelay > 0) {
|
|
3733
|
-
setTimeout(doSwap, swapSpec.swapDelay)
|
|
4723
|
+
getWindow().setTimeout(doSwap, swapSpec.swapDelay)
|
|
3734
4724
|
} else {
|
|
3735
4725
|
doSwap()
|
|
3736
4726
|
}
|
|
@@ -3744,13 +4734,13 @@ var htmx = (function() {
|
|
|
3744
4734
|
// Extensions API
|
|
3745
4735
|
//= ===================================================================
|
|
3746
4736
|
|
|
3747
|
-
/** @type {Object<string,
|
|
4737
|
+
/** @type {Object<string, HtmxExtension>} */
|
|
3748
4738
|
const extensions = {}
|
|
3749
4739
|
|
|
3750
4740
|
/**
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
4741
|
+
* extensionBase defines the default functions for all extensions.
|
|
4742
|
+
* @returns {HtmxExtension}
|
|
4743
|
+
*/
|
|
3754
4744
|
function extensionBase() {
|
|
3755
4745
|
return {
|
|
3756
4746
|
init: function(api) { return null },
|
|
@@ -3763,11 +4753,13 @@ var htmx = (function() {
|
|
|
3763
4753
|
}
|
|
3764
4754
|
|
|
3765
4755
|
/**
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
4756
|
+
* defineExtension initializes the extension and adds it to the htmx registry
|
|
4757
|
+
*
|
|
4758
|
+
* @see https://htmx.org/api/#defineExtension
|
|
4759
|
+
*
|
|
4760
|
+
* @param {string} name the extension name
|
|
4761
|
+
* @param {HtmxExtension} extension the extension definition
|
|
4762
|
+
*/
|
|
3771
4763
|
function defineExtension(name, extension) {
|
|
3772
4764
|
if (extension.init) {
|
|
3773
4765
|
extension.init(internalAPI)
|
|
@@ -3776,21 +4768,24 @@ var htmx = (function() {
|
|
|
3776
4768
|
}
|
|
3777
4769
|
|
|
3778
4770
|
/**
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
4771
|
+
* removeExtension removes an extension from the htmx registry
|
|
4772
|
+
*
|
|
4773
|
+
* @see https://htmx.org/api/#removeExtension
|
|
4774
|
+
*
|
|
4775
|
+
* @param {string} name
|
|
4776
|
+
*/
|
|
3783
4777
|
function removeExtension(name) {
|
|
3784
4778
|
delete extensions[name]
|
|
3785
4779
|
}
|
|
3786
4780
|
|
|
3787
4781
|
/**
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
4782
|
+
* getExtensions searches up the DOM tree to return all extensions that can be applied to a given element
|
|
4783
|
+
*
|
|
4784
|
+
* @param {Element} elt
|
|
4785
|
+
* @param {HtmxExtension[]=} extensionsToReturn
|
|
4786
|
+
* @param {string[]=} extensionsToIgnore
|
|
4787
|
+
* @returns {HtmxExtension[]}
|
|
4788
|
+
*/
|
|
3794
4789
|
function getExtensions(elt, extensionsToReturn, extensionsToIgnore) {
|
|
3795
4790
|
if (extensionsToReturn == undefined) {
|
|
3796
4791
|
extensionsToReturn = []
|
|
@@ -3817,7 +4812,7 @@ var htmx = (function() {
|
|
|
3817
4812
|
}
|
|
3818
4813
|
})
|
|
3819
4814
|
}
|
|
3820
|
-
return getExtensions(parentElt(elt), extensionsToReturn, extensionsToIgnore)
|
|
4815
|
+
return getExtensions(asElement(parentElt(elt)), extensionsToReturn, extensionsToIgnore)
|
|
3821
4816
|
}
|
|
3822
4817
|
|
|
3823
4818
|
//= ===================================================================
|
|
@@ -3829,12 +4824,12 @@ var htmx = (function() {
|
|
|
3829
4824
|
})
|
|
3830
4825
|
|
|
3831
4826
|
/**
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
4827
|
+
* Execute a function now if DOMContentLoaded has fired, otherwise listen for it.
|
|
4828
|
+
*
|
|
4829
|
+
* This function uses isReady because there is no reliable way to ask the browser whether
|
|
4830
|
+
* the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded
|
|
4831
|
+
* firing and readystate=complete.
|
|
4832
|
+
*/
|
|
3838
4833
|
function ready(fn) {
|
|
3839
4834
|
// Checking readyState here is a failsafe in case the htmx script tag entered the DOM by
|
|
3840
4835
|
// some means other than the initial page load.
|
|
@@ -3857,9 +4852,9 @@ var htmx = (function() {
|
|
|
3857
4852
|
}
|
|
3858
4853
|
|
|
3859
4854
|
function getMetaConfig() {
|
|
4855
|
+
/** @type HTMLMetaElement */
|
|
3860
4856
|
const element = getDocument().querySelector('meta[name="htmx-config"]')
|
|
3861
4857
|
if (element) {
|
|
3862
|
-
// @ts-ignore
|
|
3863
4858
|
return parseJSON(element.content)
|
|
3864
4859
|
} else {
|
|
3865
4860
|
return null
|
|
@@ -3907,7 +4902,7 @@ var htmx = (function() {
|
|
|
3907
4902
|
}
|
|
3908
4903
|
}
|
|
3909
4904
|
}
|
|
3910
|
-
setTimeout(function() {
|
|
4905
|
+
getWindow().setTimeout(function() {
|
|
3911
4906
|
triggerEvent(body, 'htmx:load', {}) // give ready handlers a chance to load up before firing this event
|
|
3912
4907
|
body = null // kill reference for gc
|
|
3913
4908
|
}, 0)
|
|
@@ -3915,5 +4910,178 @@ var htmx = (function() {
|
|
|
3915
4910
|
|
|
3916
4911
|
return htmx
|
|
3917
4912
|
})()
|
|
4913
|
+
|
|
4914
|
+
/** @typedef {'get'|'head'|'post'|'put'|'delete'|'connect'|'options'|'trace'|'patch'} HttpVerb */
|
|
4915
|
+
|
|
4916
|
+
/**
|
|
4917
|
+
* @typedef {Object} SwapOptions
|
|
4918
|
+
* @property {string} [select]
|
|
4919
|
+
* @property {string} [selectOOB]
|
|
4920
|
+
* @property {*} [eventInfo]
|
|
4921
|
+
* @property {string} [anchor]
|
|
4922
|
+
* @property {Element} [contextElement]
|
|
4923
|
+
* @property {swapCallback} [afterSwapCallback]
|
|
4924
|
+
* @property {swapCallback} [afterSettleCallback]
|
|
4925
|
+
*/
|
|
4926
|
+
|
|
4927
|
+
/**
|
|
4928
|
+
* @callback swapCallback
|
|
4929
|
+
*/
|
|
4930
|
+
|
|
4931
|
+
/**
|
|
4932
|
+
* @typedef {'innerHTML' | 'outerHTML' | 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend' | 'delete' | 'none' | string} HtmxSwapStyle
|
|
4933
|
+
*/
|
|
4934
|
+
|
|
4935
|
+
/**
|
|
4936
|
+
* @typedef HtmxSwapSpecification
|
|
4937
|
+
* @property {HtmxSwapStyle} swapStyle
|
|
4938
|
+
* @property {number} swapDelay
|
|
4939
|
+
* @property {number} settleDelay
|
|
4940
|
+
* @property {boolean} [transition]
|
|
4941
|
+
* @property {boolean} [ignoreTitle]
|
|
4942
|
+
* @property {string} [head]
|
|
4943
|
+
* @property {'top' | 'bottom'} [scroll]
|
|
4944
|
+
* @property {string} [scrollTarget]
|
|
4945
|
+
* @property {string} [show]
|
|
4946
|
+
* @property {string} [showTarget]
|
|
4947
|
+
* @property {boolean} [focusScroll]
|
|
4948
|
+
*/
|
|
4949
|
+
|
|
4950
|
+
/**
|
|
4951
|
+
* @typedef {((this:Node, evt:Event) => boolean) & {source: string}} ConditionalFunction
|
|
4952
|
+
*/
|
|
4953
|
+
|
|
4954
|
+
/**
|
|
4955
|
+
* @typedef {Object} HtmxTriggerSpecification
|
|
4956
|
+
* @property {string} trigger
|
|
4957
|
+
* @property {number} [pollInterval]
|
|
4958
|
+
* @property {ConditionalFunction} [eventFilter]
|
|
4959
|
+
* @property {boolean} [changed]
|
|
4960
|
+
* @property {boolean} [once]
|
|
4961
|
+
* @property {boolean} [consume]
|
|
4962
|
+
* @property {number} [delay]
|
|
4963
|
+
* @property {string} [from]
|
|
4964
|
+
* @property {string} [target]
|
|
4965
|
+
* @property {number} [throttle]
|
|
4966
|
+
* @property {string} [queue]
|
|
4967
|
+
* @property {string} [root]
|
|
4968
|
+
* @property {string} [threshold]
|
|
4969
|
+
*/
|
|
4970
|
+
|
|
4971
|
+
/**
|
|
4972
|
+
* @typedef {{elt: Element, message: string, validity: ValidityState}} HtmxElementValidationError
|
|
4973
|
+
*/
|
|
4974
|
+
|
|
4975
|
+
/**
|
|
4976
|
+
* @typedef {Record<string, string>} HtmxHeaderSpecification
|
|
4977
|
+
* @property {'true'} HX-Request
|
|
4978
|
+
* @property {string|null} HX-Trigger
|
|
4979
|
+
* @property {string|null} HX-Trigger-Name
|
|
4980
|
+
* @property {string|null} HX-Target
|
|
4981
|
+
* @property {string} HX-Current-URL
|
|
4982
|
+
* @property {string} [HX-Prompt]
|
|
4983
|
+
* @property {'true'} [HX-Boosted]
|
|
4984
|
+
* @property {string} [Content-Type]
|
|
4985
|
+
* @property {'true'} [HX-History-Restore-Request]
|
|
4986
|
+
*/
|
|
4987
|
+
|
|
4988
|
+
/** @typedef HtmxAjaxHelperContext
|
|
4989
|
+
* @property {Element|string} [source]
|
|
4990
|
+
* @property {Event} [event]
|
|
4991
|
+
* @property {HtmxAjaxHandler} [handler]
|
|
4992
|
+
* @property {Element|string} target
|
|
4993
|
+
* @property {HtmxSwapStyle} [swap]
|
|
4994
|
+
* @property {Object|FormData} [values]
|
|
4995
|
+
* @property {Record<string,string>} [headers]
|
|
4996
|
+
* @property {string} [select]
|
|
4997
|
+
*/
|
|
4998
|
+
|
|
4999
|
+
/**
|
|
5000
|
+
* @typedef {Object} HtmxRequestConfig
|
|
5001
|
+
* @property {boolean} boosted
|
|
5002
|
+
* @property {boolean} useUrlParams
|
|
5003
|
+
* @property {FormData} formData
|
|
5004
|
+
* @property {Object} parameters formData proxy
|
|
5005
|
+
* @property {FormData} unfilteredFormData
|
|
5006
|
+
* @property {Object} unfilteredParameters unfilteredFormData proxy
|
|
5007
|
+
* @property {HtmxHeaderSpecification} headers
|
|
5008
|
+
* @property {Element} target
|
|
5009
|
+
* @property {HttpVerb} verb
|
|
5010
|
+
* @property {HtmxElementValidationError[]} errors
|
|
5011
|
+
* @property {boolean} withCredentials
|
|
5012
|
+
* @property {number} timeout
|
|
5013
|
+
* @property {string} path
|
|
5014
|
+
* @property {Event} triggeringEvent
|
|
5015
|
+
*/
|
|
5016
|
+
|
|
5017
|
+
/**
|
|
5018
|
+
* @typedef {Object} HtmxResponseInfo
|
|
5019
|
+
* @property {XMLHttpRequest} xhr
|
|
5020
|
+
* @property {Element} target
|
|
5021
|
+
* @property {HtmxRequestConfig} requestConfig
|
|
5022
|
+
* @property {HtmxAjaxEtc} etc
|
|
5023
|
+
* @property {boolean} boosted
|
|
5024
|
+
* @property {string} select
|
|
5025
|
+
* @property {{requestPath: string, finalRequestPath: string, responsePath: string|null, anchor: string}} pathInfo
|
|
5026
|
+
* @property {boolean} [failed]
|
|
5027
|
+
* @property {boolean} [successful]
|
|
5028
|
+
*/
|
|
5029
|
+
|
|
5030
|
+
/**
|
|
5031
|
+
* @typedef {Object} HtmxAjaxEtc
|
|
5032
|
+
* @property {boolean} [returnPromise]
|
|
5033
|
+
* @property {HtmxAjaxHandler} [handler]
|
|
5034
|
+
* @property {string} [select]
|
|
5035
|
+
* @property {Element} [targetOverride]
|
|
5036
|
+
* @property {HtmxSwapStyle} [swapOverride]
|
|
5037
|
+
* @property {Record<string,string>} [headers]
|
|
5038
|
+
* @property {Object|FormData} [values]
|
|
5039
|
+
* @property {boolean} [credentials]
|
|
5040
|
+
* @property {number} [timeout]
|
|
5041
|
+
*/
|
|
5042
|
+
|
|
5043
|
+
/**
|
|
5044
|
+
* @typedef {Object} HtmxResponseHandlingConfig
|
|
5045
|
+
* @property {string} [code]
|
|
5046
|
+
* @property {boolean} swap
|
|
5047
|
+
* @property {boolean} [error]
|
|
5048
|
+
* @property {boolean} [ignoreTitle]
|
|
5049
|
+
* @property {string} [select]
|
|
5050
|
+
* @property {string} [target]
|
|
5051
|
+
* @property {string} [swapOverride]
|
|
5052
|
+
* @property {string} [event]
|
|
5053
|
+
*/
|
|
5054
|
+
|
|
5055
|
+
/**
|
|
5056
|
+
* @typedef {HtmxResponseInfo & {shouldSwap: boolean, serverResponse: any, isError: boolean, ignoreTitle: boolean, selectOverride:string}} HtmxBeforeSwapDetails
|
|
5057
|
+
*/
|
|
5058
|
+
|
|
5059
|
+
/**
|
|
5060
|
+
* @callback HtmxAjaxHandler
|
|
5061
|
+
* @param {Element} elt
|
|
5062
|
+
* @param {HtmxResponseInfo} responseInfo
|
|
5063
|
+
*/
|
|
5064
|
+
|
|
5065
|
+
/**
|
|
5066
|
+
* @typedef {(() => void)} HtmxSettleTask
|
|
5067
|
+
*/
|
|
5068
|
+
|
|
5069
|
+
/**
|
|
5070
|
+
* @typedef {Object} HtmxSettleInfo
|
|
5071
|
+
* @property {HtmxSettleTask[]} tasks
|
|
5072
|
+
* @property {Element[]} elts
|
|
5073
|
+
* @property {string} [title]
|
|
5074
|
+
*/
|
|
5075
|
+
|
|
5076
|
+
/**
|
|
5077
|
+
* @typedef {Object} HtmxExtension
|
|
5078
|
+
* @see https://htmx.org/extensions/#defining
|
|
5079
|
+
* @property {(api: any) => void} init
|
|
5080
|
+
* @property {(name: string, event: Event|CustomEvent) => boolean} onEvent
|
|
5081
|
+
* @property {(text: string, xhr: XMLHttpRequest, elt: Element) => string} transformResponse
|
|
5082
|
+
* @property {(swapStyle: HtmxSwapStyle) => boolean} isInlineSwap
|
|
5083
|
+
* @property {(swapStyle: HtmxSwapStyle, target: Element, fragment: Node, settleInfo: HtmxSettleInfo) => boolean} handleSwap
|
|
5084
|
+
* @property {(xhr: XMLHttpRequest, parameters: FormData, elt: Element) => *|string|null} encodeParameters
|
|
5085
|
+
*/
|
|
3918
5086
|
return htmx
|
|
3919
5087
|
})
|