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