htmx.org 2.0.0-alpha2 → 2.0.0-beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/htmx.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
- onLoad: onLoadHelper,
9
- process: processNode,
10
- on: addEventListenerImpl,
11
- off: removeEventListenerImpl,
12
- trigger: triggerEvent,
13
- ajax: ajaxHelper,
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
- findAll,
17
- closest,
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
- remove: removeElement,
24
- addClass: addClassToElement,
25
- removeClass: removeClassFromElement,
26
- toggleClass: toggleClassOnElement,
27
- takeClass: takeClassForElement,
28
- swap,
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
- removeExtension,
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
- logAll,
34
- logNone,
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
- _: internalEval,
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 {Element} elt
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.getAttribute && elt.getAttribute(name)
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 {HTMLElement} elt
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 {HTMLElement} elt
176
- * @returns {HTMLElement | ShadowRoot | null}
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
- * @returns {Document | ShadowRoot}
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 {HTMLElement} elt
200
- * @param {(e:HTMLElement) => boolean} condition
201
- * @returns {HTMLElement | null}
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 {HTMLElement} elt
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 {HTMLElement} elt
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
- // we have to make new copies of script tags that we are going to insert because
306
- // SOME browsers (not saying who, but it involves an element and an animal) don't
307
- // execute scripts created in <template> tags when they are inserted into the DOM
308
- // and all the others do lmao
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
- * @param {string} response HTML
327
- * @returns {DocumentFragment & {title: string}} a document fragment representing the response HTML, including
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
- let fragment = null
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 isType(o, 'Function')
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 {HTMLElement} elt
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
- * @param {ArrayLike} arr
423
- * @returns {any[]}
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
- * @callback forEachCallback
438
- * @param {T} value
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
- if (elt.getRootNode && elt.getRootNode() instanceof window.ShadowRoot) {
463
- return getDocument().body.contains(elt.getRootNode().host)
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 of the keys from
785
+ * mergeObjects takes all the keys from
475
786
  * obj2 and duplicates them into obj1
476
- * @param {Object} obj1
477
- * @param {Object} obj2
478
- * @returns {Object}
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 (selector) {
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 (selector) {
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
- function removeClassFromElement(elt, clazz, delay) {
596
- elt = resolveTarget(elt)
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 (selector) {
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|Element} arg2
718
- * @param {Element} [context]
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(arg2, context) {
722
- if (isType(arg2, 'String')) {
723
- return find(context || document, arg2)
1216
+ function resolveTarget(eltOrSelector, context) {
1217
+ if (typeof eltOrSelector === 'string') {
1218
+ return find(asParentNode(context) || document, eltOrSelector)
724
1219
  } else {
725
- // @ts-ignore
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 {*} settleInfo
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 oldNode = parentNode.querySelector(normalizedTag + "[id='" + normalizedId + "']")
916
- if (oldNode && oldNode !== parentNode) {
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
- // based on https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0,
957
- // derived from Java's string hashcode implementation
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.removeEventListener(handlerInfo.event, handlerInfo.listener)
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.removeEventListener(info.trigger, info.listener)
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
- // @type {HTMLElement}
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.nodeType === Node.ELEMENT_NODE) {
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.remove()
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
- function swapDelete(target, fragment, settleInfo) {
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 {string} swapStyle
1072
- * @param {HTMLElement} elt
1073
- * @param {HTMLElement} target
1074
- * @param {Node} fragment
1075
- * @param {{ tasks: (() => void)[]; }} settleInfo
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, fragment, settleInfo)
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
- const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
1132
- if (oobValue != null) {
1133
- oobSwap(oobValue, oobElement, settleInfo)
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 {import("./htmx").HtmxSwapSpecification} swapSpec
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
- let fragment = makeFragment(content)
1185
-
1186
- settleInfo.title = fragment.title
1187
-
1188
- // select-oob swaps
1189
- if (swapOptions.selectOOB) {
1190
- const oobSelectValues = swapOptions.selectOOB.split(',')
1191
- for (let i = 0; i < oobSelectValues.length; i++) {
1192
- const oobSelectValue = oobSelectValues[i].split(':', 2)
1193
- let id = oobSelectValue[0].trim()
1194
- if (id.indexOf('#') === 0) {
1195
- id = id.substring(1)
1196
- }
1197
- const oobValue = oobSelectValue[1] || 'true'
1198
- const oobElement = fragment.querySelector('#' + id)
1199
- if (oobElement) {
1200
- oobSwap(oobValue, oobElement, settleInfo)
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
- // oob swaps
1205
- findAndSwapOobElements(fragment, settleInfo)
1206
- forEach(findAll(fragment, 'template'), function(template) {
1207
- findAndSwapOobElements(template.content, settleInfo)
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
- template.remove()
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
- fragment = newFragment
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
- !bodyContains(selectionInfo.elt) &&
1228
- getRawAttribute(selectionInfo.elt, 'id')) {
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 {HTMLElement} elt
2099
+ * @param {Element} elt
1426
2100
  * @param {string} explicitTrigger
1427
- * @param {cache} cache for trigger specs
1428
- * @returns {import("./htmx").HtmxTriggerSpecification[]}
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 {HTMLElement} elt
1517
- * @returns {import("./htmx").HtmxTriggerSpecification[]}
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.tagName === 'A' && isLocalLink(elt) && (elt.target === '' || elt.target === '_self')) || elt.tagName === 'FORM') {
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(elt, evt) {
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 {HTMLElement} elt
1595
- * @returns
2288
+ * @param {Node} node
2289
+ * @returns {boolean}
1596
2290
  */
1597
- function shouldCancel(evt, elt) {
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.tagName === 'A' && elt.href &&
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.tagName === 'A' && evt.type === 'click' && (evt.ctrlKey || evt.metaKey)
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
- triggerErrorEvent(getDocument().body, 'htmx:eventFilter:error', { error: e, source: eventFilter.source })
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
- if (eltToListenOnData.lastValue === eltToListenOn.value) {
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 = eltToListenOn.value
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(evt) { triggerEvent(elt, 'revealed') }, { once: true })
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(elt, evt) {
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
- function shouldProcessHxOn(elt) {
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
- // Handle submit buttons/inputs that have the form attribute set
1869
- // see https://developer.mozilla.org/docs/Web/HTML/Element/button
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
- function countCurlies(line) {
1904
- const tokens = tokenizeString(line)
1905
- let netCurlies = 0
1906
- for (let i = 0; i < tokens.length; i++) {
1907
- const token = tokens[i]
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
- return maybeEval(elt, function() {
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 {HTMLElement} elt
2051
- * @param {(extension:import("./htmx").HtmxExtension) => void} toDo
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
- // @ts-ignore
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
- // @ts-ignore: optional parameter in reload() function throws error
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 {HTMLElement[]} processed
2334
- * @param {HTMLElement} elt
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
- function shouldInclude(elt) {
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 {HTMLElement[]} processed
3268
+ * @param {Element[]} processed
2392
3269
  * @param {FormData} formData
2393
3270
  * @param {HtmxElementValidationError[]} errors
2394
- * @param {HTMLElement|HTMLInputElement|HTMLFormElement} elt
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.multiple && elt.tagName === 'SELECT') {
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 (matches(elt, 'form')) {
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 {HTMLElement|HTMLObjectElement} element
3318
+ * @param {Element} elt
2445
3319
  * @param {HtmxElementValidationError[]} errors
2446
3320
  */
2447
- function validateElement(element, errors) {
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 {HTMLElement|HTMLFormElement} elt
2475
- * @param {string} verb
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 HTMLElement[] */
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 = (matches(elt, 'form') && elt.noValidate !== true) || getAttributeValue(elt, 'hx-validate') === 'true'
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 {HTMLElement} elt
2568
- * @param {HTMLElement} target
3442
+ * @param {Element} elt
3443
+ * @param {Element} target
2569
3444
  * @param {string} prompt
2570
- * @returns {Object} // TODO: Define/Improve HtmxHeaderSpecification
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 {HTMLElement} elt
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 {HTMLElement} elt
2633
- * @param {import("./htmx").HtmxSwapStyle} swapInfoOverride
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 import("./htmx").HtmxSwapSpecification */
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 {HTMLElement} elt
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 {import("./htmx").HtmxSettleInfo}
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 {HTMLElement} elt
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 {HTMLElement} elt
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 {HTMLElement} elt
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 {HTMLElement} elt
3735
+ * @param {Element} elt
2840
3736
  * @returns {FormData}
2841
3737
  */
2842
3738
  function getExpressionVars(elt) {
2843
- return formDataFromObject(mergeObjects(getHXVarsForElement(elt), getHXValsForElement(elt)))
3739
+ return 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 || isType(context, 'String')) {
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|Symbol} name
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(...arguments)
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
- target.delete(name)
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()
@@ -3220,7 +4168,7 @@ var htmx = (function() {
3220
4168
  if (etc.values) {
3221
4169
  overrideFormData(rawFormData, formDataFromObject(etc.values))
3222
4170
  }
3223
- const expressionVars = getExpressionVars(elt)
4171
+ const expressionVars = formDataFromObject(getExpressionVars(elt))
3224
4172
  const allFormData = overrideFormData(rawFormData, expressionVars)
3225
4173
  let filteredFormData = filterValues(allFormData, elt)
3226
4174
 
@@ -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
- var swapSpec
4523
+ /** @type {HtmxAjaxHelperContext&{path:string}} */
4524
+ var redirectSwapSpec
3539
4525
  if (redirectPath.indexOf('{') === 0) {
3540
- swapSpec = parseJSON(redirectPath)
4526
+ redirectSwapSpec = parseJSON(redirectPath)
3541
4527
  // what's the best way to throw an error if the user didn't include this
3542
- redirectPath = swapSpec.path
3543
- delete swapSpec.path
4528
+ redirectPath = redirectSwapSpec.path
4529
+ delete redirectSwapSpec.path
3544
4530
  }
3545
- ajaxHelper('GET', redirectPath, swapSpec).then(function() {
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' && document.startViewTransition) {
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, import("./htmx").HtmxExtension>} */
4736
+ /** @type {Object<string, HtmxExtension>} */
3747
4737
  const extensions = {}
3748
4738
 
3749
4739
  /**
3750
- * extensionBase defines the default functions for all extensions.
3751
- * @returns {import("./htmx").HtmxExtension}
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
- * defineExtension initializes the extension and adds it to the htmx registry
3766
- *
3767
- * @param {string} name
3768
- * @param {import("./htmx").HtmxExtension} extension
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
- * removeExtension removes an extension from the htmx registry
3779
- *
3780
- * @param {string} name
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
- * getExtensions searches up the DOM tree to return all extensions that can be applied to a given element
3788
- *
3789
- * @param {HTMLElement} elt
3790
- * @param {import("./htmx").HtmxExtension[]=} extensionsToReturn
3791
- * @param {import("./htmx").HtmxExtension[]=} extensionsToIgnore
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
- * Execute a function now if DOMContentLoaded has fired, otherwise listen for it.
3832
- *
3833
- * This function uses isReady because there is no realiable way to ask the browswer whether
3834
- * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded
3835
- * firing and readystate=complete.
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,3 +4909,176 @@ 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
+ */