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