htmx.org 2.0.2 → 2.0.3

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
@@ -278,7 +278,7 @@ var htmx = (function() {
278
278
  parseInterval: null,
279
279
  /** @type {typeof internalEval} */
280
280
  _: null,
281
- version: '2.0.2'
281
+ version: '2.0.3'
282
282
  }
283
283
  // Tsc madness part 2
284
284
  htmx.onLoad = onLoadHelper
@@ -338,22 +338,10 @@ var htmx = (function() {
338
338
  return '[hx-' + verb + '], [data-hx-' + verb + ']'
339
339
  }).join(', ')
340
340
 
341
- const HEAD_TAG_REGEX = makeTagRegEx('head')
342
-
343
341
  //= ===================================================================
344
342
  // Utilities
345
343
  //= ===================================================================
346
344
 
347
- /**
348
- * @param {string} tag
349
- * @param {boolean} global
350
- * @returns {RegExp}
351
- */
352
- function makeTagRegEx(tag, global = false) {
353
- return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`,
354
- global ? 'gim' : 'im')
355
- }
356
-
357
345
  /**
358
346
  * Parses an interval string consistent with the way htmx does. Useful for plugins that have timing-related attributes.
359
347
  *
@@ -596,7 +584,7 @@ var htmx = (function() {
596
584
  */
597
585
  function makeFragment(response) {
598
586
  // strip head tag to determine shape of response we are dealing with
599
- const responseWithNoHead = response.replace(HEAD_TAG_REGEX, '')
587
+ const responseWithNoHead = response.replace(/<head(\s[^>]*)?>[\s\S]*?<\/head>/i, '')
600
588
  const startTag = getStartTag(responseWithNoHead)
601
589
  /** @type DocumentFragmentWithTitle */
602
590
  let fragment
@@ -696,7 +684,7 @@ var htmx = (function() {
696
684
  * @property {boolean} [triggeredOnce]
697
685
  * @property {number} [delayed]
698
686
  * @property {number|null} [throttle]
699
- * @property {string} [lastValue]
687
+ * @property {WeakMap<HtmxTriggerSpecification,WeakMap<EventTarget,string>>} [lastValue]
700
688
  * @property {boolean} [loaded]
701
689
  * @property {string} [path]
702
690
  * @property {string} [verb]
@@ -1162,6 +1150,8 @@ var htmx = (function() {
1162
1150
  return [document.body]
1163
1151
  } else if (selector === 'root') {
1164
1152
  return [getRootNode(elt, !!global)]
1153
+ } else if (selector === 'host') {
1154
+ return [(/** @type ShadowRoot */(elt.getRootNode())).host]
1165
1155
  } else if (selector.indexOf('global ') === 0) {
1166
1156
  return querySelectorAllExt(elt, selector.slice(7), true)
1167
1157
  } else {
@@ -1237,26 +1227,30 @@ var htmx = (function() {
1237
1227
  * @property {EventTarget} target
1238
1228
  * @property {AnyEventName} event
1239
1229
  * @property {EventListener} listener
1230
+ * @property {Object|boolean} options
1240
1231
  */
1241
1232
 
1242
1233
  /**
1243
1234
  * @param {EventTarget|AnyEventName} arg1
1244
1235
  * @param {AnyEventName|EventListener} arg2
1245
- * @param {EventListener} [arg3]
1236
+ * @param {EventListener|Object|boolean} [arg3]
1237
+ * @param {Object|boolean} [arg4]
1246
1238
  * @returns {EventArgs}
1247
1239
  */
1248
- function processEventArgs(arg1, arg2, arg3) {
1240
+ function processEventArgs(arg1, arg2, arg3, arg4) {
1249
1241
  if (isFunction(arg2)) {
1250
1242
  return {
1251
1243
  target: getDocument().body,
1252
1244
  event: asString(arg1),
1253
- listener: arg2
1245
+ listener: arg2,
1246
+ options: arg3
1254
1247
  }
1255
1248
  } else {
1256
1249
  return {
1257
1250
  target: resolveTarget(arg1),
1258
1251
  event: asString(arg2),
1259
- listener: arg3
1252
+ listener: arg3,
1253
+ options: arg4
1260
1254
  }
1261
1255
  }
1262
1256
  }
@@ -1268,13 +1262,14 @@ var htmx = (function() {
1268
1262
  *
1269
1263
  * @param {EventTarget|string} arg1 the element to add the listener to | the event name to add the listener for
1270
1264
  * @param {string|EventListener} arg2 the event name to add the listener for | the listener to add
1271
- * @param {EventListener} [arg3] the listener to add
1265
+ * @param {EventListener|Object|boolean} [arg3] the listener to add | options to add
1266
+ * @param {Object|boolean} [arg4] options to add
1272
1267
  * @returns {EventListener}
1273
1268
  */
1274
- function addEventListenerImpl(arg1, arg2, arg3) {
1269
+ function addEventListenerImpl(arg1, arg2, arg3, arg4) {
1275
1270
  ready(function() {
1276
- const eventArgs = processEventArgs(arg1, arg2, arg3)
1277
- eventArgs.target.addEventListener(eventArgs.event, eventArgs.listener)
1271
+ const eventArgs = processEventArgs(arg1, arg2, arg3, arg4)
1272
+ eventArgs.target.addEventListener(eventArgs.event, eventArgs.listener, eventArgs.options)
1278
1273
  })
1279
1274
  const b = isFunction(arg2)
1280
1275
  return b ? arg2 : arg3
@@ -1413,9 +1408,11 @@ var htmx = (function() {
1413
1408
  * @param {string} oobValue
1414
1409
  * @param {Element} oobElement
1415
1410
  * @param {HtmxSettleInfo} settleInfo
1411
+ * @param {Node|Document} [rootNode]
1416
1412
  * @returns
1417
1413
  */
1418
- function oobSwap(oobValue, oobElement, settleInfo) {
1414
+ function oobSwap(oobValue, oobElement, settleInfo, rootNode) {
1415
+ rootNode = rootNode || getDocument()
1419
1416
  let selector = '#' + getRawAttribute(oobElement, 'id')
1420
1417
  /** @type HtmxSwapStyle */
1421
1418
  let swapStyle = 'outerHTML'
@@ -1427,8 +1424,10 @@ var htmx = (function() {
1427
1424
  } else {
1428
1425
  swapStyle = oobValue
1429
1426
  }
1427
+ oobElement.removeAttribute('hx-swap-oob')
1428
+ oobElement.removeAttribute('data-hx-swap-oob')
1430
1429
 
1431
- const targets = getDocument().querySelectorAll(selector)
1430
+ const targets = querySelectorAllExt(rootNode, selector, false)
1432
1431
  if (targets) {
1433
1432
  forEach(
1434
1433
  targets,
@@ -1446,7 +1445,9 @@ var htmx = (function() {
1446
1445
 
1447
1446
  target = beforeSwapDetails.target // allow re-targeting
1448
1447
  if (beforeSwapDetails.shouldSwap) {
1448
+ handlePreservedElements(fragment)
1449
1449
  swapWithStyle(swapStyle, target, target, fragment, settleInfo)
1450
+ restorePreservedElements()
1450
1451
  }
1451
1452
  forEach(settleInfo.elts, function(elt) {
1452
1453
  triggerEvent(elt, 'htmx:oobAfterSwap', beforeSwapDetails)
@@ -1461,15 +1462,39 @@ var htmx = (function() {
1461
1462
  return oobValue
1462
1463
  }
1463
1464
 
1465
+ function restorePreservedElements() {
1466
+ const pantry = find('#--htmx-preserve-pantry--')
1467
+ if (pantry) {
1468
+ for (const preservedElt of [...pantry.children]) {
1469
+ const existingElement = find('#' + preservedElt.id)
1470
+ // @ts-ignore - use proposed moveBefore feature
1471
+ existingElement.parentNode.moveBefore(preservedElt, existingElement)
1472
+ existingElement.remove()
1473
+ }
1474
+ pantry.remove()
1475
+ }
1476
+ }
1477
+
1464
1478
  /**
1465
- * @param {DocumentFragment} fragment
1479
+ * @param {DocumentFragment|ParentNode} fragment
1466
1480
  */
1467
1481
  function handlePreservedElements(fragment) {
1468
1482
  forEach(findAll(fragment, '[hx-preserve], [data-hx-preserve]'), function(preservedElt) {
1469
1483
  const id = getAttributeValue(preservedElt, 'id')
1470
- const oldElt = getDocument().getElementById(id)
1471
- if (oldElt != null) {
1472
- preservedElt.parentNode.replaceChild(oldElt, preservedElt)
1484
+ const existingElement = getDocument().getElementById(id)
1485
+ if (existingElement != null) {
1486
+ if (preservedElt.moveBefore) { // if the moveBefore API exists, use it
1487
+ // get or create a storage spot for stuff
1488
+ let pantry = find('#--htmx-preserve-pantry--')
1489
+ if (pantry == null) {
1490
+ getDocument().body.insertAdjacentHTML('afterend', "<div id='--htmx-preserve-pantry--'></div>")
1491
+ pantry = find('#--htmx-preserve-pantry--')
1492
+ }
1493
+ // @ts-ignore - use proposed moveBefore feature
1494
+ pantry.moveBefore(existingElement, null)
1495
+ } else {
1496
+ preservedElt.parentNode.replaceChild(existingElement, preservedElt)
1497
+ }
1473
1498
  }
1474
1499
  })
1475
1500
  }
@@ -1633,9 +1658,13 @@ var htmx = (function() {
1633
1658
  /** @type {Node} */
1634
1659
  let newElt
1635
1660
  const eltBeforeNewContent = target.previousSibling
1636
- insertNodesBefore(parentElt(target), target, fragment, settleInfo)
1661
+ const parentNode = parentElt(target)
1662
+ if (!parentNode) { // when parent node disappears, we can't do anything
1663
+ return
1664
+ }
1665
+ insertNodesBefore(parentNode, target, fragment, settleInfo)
1637
1666
  if (eltBeforeNewContent == null) {
1638
- newElt = parentElt(target).firstChild
1667
+ newElt = parentNode.firstChild
1639
1668
  } else {
1640
1669
  newElt = eltBeforeNewContent.nextSibling
1641
1670
  }
@@ -1697,7 +1726,10 @@ var htmx = (function() {
1697
1726
  */
1698
1727
  function swapDelete(target) {
1699
1728
  cleanUpElement(target)
1700
- return parentElt(target).removeChild(target)
1729
+ const parent = parentElt(target)
1730
+ if (parent) {
1731
+ return parent.removeChild(target)
1732
+ }
1701
1733
  }
1702
1734
 
1703
1735
  /**
@@ -1780,14 +1812,15 @@ var htmx = (function() {
1780
1812
  /**
1781
1813
  * @param {DocumentFragment} fragment
1782
1814
  * @param {HtmxSettleInfo} settleInfo
1815
+ * @param {Node|Document} [rootNode]
1783
1816
  */
1784
- function findAndSwapOobElements(fragment, settleInfo) {
1817
+ function findAndSwapOobElements(fragment, settleInfo, rootNode) {
1785
1818
  var oobElts = findAll(fragment, '[hx-swap-oob], [data-hx-swap-oob]')
1786
1819
  forEach(oobElts, function(oobElement) {
1787
1820
  if (htmx.config.allowNestedOobSwaps || oobElement.parentElement === null) {
1788
1821
  const oobValue = getAttributeValue(oobElement, 'hx-swap-oob')
1789
1822
  if (oobValue != null) {
1790
- oobSwap(oobValue, oobElement, settleInfo)
1823
+ oobSwap(oobValue, oobElement, settleInfo, rootNode)
1791
1824
  }
1792
1825
  } else {
1793
1826
  oobElement.removeAttribute('hx-swap-oob')
@@ -1811,6 +1844,7 @@ var htmx = (function() {
1811
1844
  }
1812
1845
 
1813
1846
  target = resolveTarget(target)
1847
+ const rootNode = swapOptions.contextElement ? getRootNode(swapOptions.contextElement, false) : getDocument()
1814
1848
 
1815
1849
  // preserve focus and selection
1816
1850
  const activeElt = document.activeElement
@@ -1849,14 +1883,14 @@ var htmx = (function() {
1849
1883
  const oobValue = oobSelectValue[1] || 'true'
1850
1884
  const oobElement = fragment.querySelector('#' + id)
1851
1885
  if (oobElement) {
1852
- oobSwap(oobValue, oobElement, settleInfo)
1886
+ oobSwap(oobValue, oobElement, settleInfo, rootNode)
1853
1887
  }
1854
1888
  }
1855
1889
  }
1856
1890
  // oob swaps
1857
- findAndSwapOobElements(fragment, settleInfo)
1891
+ findAndSwapOobElements(fragment, settleInfo, rootNode)
1858
1892
  forEach(findAll(fragment, 'template'), /** @param {HTMLTemplateElement} template */function(template) {
1859
- if (findAndSwapOobElements(template.content, settleInfo)) {
1893
+ if (findAndSwapOobElements(template.content, settleInfo, rootNode)) {
1860
1894
  // Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
1861
1895
  template.remove()
1862
1896
  }
@@ -1872,6 +1906,7 @@ var htmx = (function() {
1872
1906
  }
1873
1907
  handlePreservedElements(fragment)
1874
1908
  swapWithStyle(swapSpec.swapStyle, swapOptions.contextElement, target, fragment, settleInfo)
1909
+ restorePreservedElements()
1875
1910
  }
1876
1911
 
1877
1912
  // apply saved focus and selection information to swapped content
@@ -2142,8 +2177,8 @@ var htmx = (function() {
2142
2177
  if (eventFilter) {
2143
2178
  triggerSpec.eventFilter = eventFilter
2144
2179
  }
2180
+ consumeUntil(tokens, NOT_WHITESPACE)
2145
2181
  while (tokens.length > 0 && tokens[0] !== ',') {
2146
- consumeUntil(tokens, NOT_WHITESPACE)
2147
2182
  const token = tokens.shift()
2148
2183
  if (token === 'changed') {
2149
2184
  triggerSpec.changed = true
@@ -2188,6 +2223,7 @@ var htmx = (function() {
2188
2223
  } else {
2189
2224
  triggerErrorEvent(elt, 'htmx:syntax:error', { token: tokens.shift() })
2190
2225
  }
2226
+ consumeUntil(tokens, NOT_WHITESPACE)
2191
2227
  }
2192
2228
  triggerSpecs.push(triggerSpec)
2193
2229
  }
@@ -2282,14 +2318,15 @@ var htmx = (function() {
2282
2318
  nodeData.boosted = true
2283
2319
  let verb, path
2284
2320
  if (elt.tagName === 'A') {
2285
- verb = 'get'
2321
+ verb = (/** @type HttpVerb */('get'))
2286
2322
  path = getRawAttribute(elt, 'href')
2287
2323
  } else {
2288
2324
  const rawAttribute = getRawAttribute(elt, 'method')
2289
- verb = rawAttribute ? rawAttribute.toLowerCase() : 'get'
2290
- if (verb === 'get') {
2291
- }
2325
+ verb = (/** @type HttpVerb */(rawAttribute ? rawAttribute.toLowerCase() : 'get'))
2292
2326
  path = getRawAttribute(elt, 'action')
2327
+ if (verb === 'get' && path.includes('?')) {
2328
+ path = path.replace(/\?[^#]+/, '')
2329
+ }
2293
2330
  }
2294
2331
  triggerSpecs.forEach(function(triggerSpec) {
2295
2332
  addEventListener(elt, function(node, evt) {
@@ -2378,10 +2415,15 @@ var htmx = (function() {
2378
2415
  }
2379
2416
  // store the initial values of the elements, so we can tell if they change
2380
2417
  if (triggerSpec.changed) {
2418
+ if (!('lastValue' in elementData)) {
2419
+ elementData.lastValue = new WeakMap()
2420
+ }
2381
2421
  eltsToListenOn.forEach(function(eltToListenOn) {
2382
- const eltToListenOnData = getInternalData(eltToListenOn)
2422
+ if (!elementData.lastValue.has(triggerSpec)) {
2423
+ elementData.lastValue.set(triggerSpec, new WeakMap())
2424
+ }
2383
2425
  // @ts-ignore value will be undefined for non-input elements, which is fine
2384
- eltToListenOnData.lastValue = eltToListenOn.value
2426
+ elementData.lastValue.get(triggerSpec).set(eltToListenOn, eltToListenOn.value)
2385
2427
  })
2386
2428
  }
2387
2429
  forEach(eltsToListenOn, function(eltToListenOn) {
@@ -2423,13 +2465,14 @@ var htmx = (function() {
2423
2465
  }
2424
2466
  }
2425
2467
  if (triggerSpec.changed) {
2426
- const eltToListenOnData = getInternalData(eltToListenOn)
2468
+ const node = event.target
2427
2469
  // @ts-ignore value will be undefined for non-input elements, which is fine
2428
- const value = eltToListenOn.value
2429
- if (eltToListenOnData.lastValue === value) {
2470
+ const value = node.value
2471
+ const lastValue = elementData.lastValue.get(triggerSpec)
2472
+ if (lastValue.has(node) && lastValue.get(node) === value) {
2430
2473
  return
2431
2474
  }
2432
- eltToListenOnData.lastValue = value
2475
+ lastValue.set(node, value)
2433
2476
  }
2434
2477
  if (elementData.delayed) {
2435
2478
  clearTimeout(elementData.delayed)
@@ -2477,6 +2520,7 @@ var htmx = (function() {
2477
2520
  windowIsScrolling = true
2478
2521
  }
2479
2522
  window.addEventListener('scroll', scrollHandler)
2523
+ window.addEventListener('resize', scrollHandler)
2480
2524
  setInterval(function() {
2481
2525
  if (windowIsScrolling) {
2482
2526
  windowIsScrolling = false
@@ -2806,12 +2850,6 @@ var htmx = (function() {
2806
2850
 
2807
2851
  triggerEvent(elt, 'htmx:beforeProcessNode')
2808
2852
 
2809
- // @ts-ignore value will be undefined for non-input elements, which is fine
2810
- if (elt.value) {
2811
- // @ts-ignore
2812
- nodeData.lastValue = elt.value
2813
- }
2814
-
2815
2853
  const triggerSpecs = getTriggerSpecs(elt)
2816
2854
  const hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs)
2817
2855
 
@@ -3154,7 +3192,9 @@ var htmx = (function() {
3154
3192
  const settleInfo = makeSettleInfo(historyElement)
3155
3193
  handleTitle(fragment.title)
3156
3194
 
3195
+ handlePreservedElements(fragment)
3157
3196
  swapInnerHTML(historyElement, content, settleInfo)
3197
+ restorePreservedElements()
3158
3198
  settleImmediately(settleInfo.tasks)
3159
3199
  currentPathForHistory = path
3160
3200
  triggerEvent(getDocument().body, 'htmx:historyRestore', { path, cacheMiss: true, serverResponse: this.response })
@@ -3176,8 +3216,10 @@ var htmx = (function() {
3176
3216
  const fragment = makeFragment(cached.content)
3177
3217
  const historyElement = getHistoryElement()
3178
3218
  const settleInfo = makeSettleInfo(historyElement)
3179
- handleTitle(fragment.title)
3219
+ handleTitle(cached.title)
3220
+ handlePreservedElements(fragment)
3180
3221
  swapInnerHTML(historyElement, fragment, settleInfo)
3222
+ restorePreservedElements()
3181
3223
  settleImmediately(settleInfo.tasks)
3182
3224
  getWindow().setTimeout(function() {
3183
3225
  window.scrollTo(0, cached.scroll)
@@ -3235,16 +3277,18 @@ var htmx = (function() {
3235
3277
  * @param {Element[]} disabled
3236
3278
  */
3237
3279
  function removeRequestIndicators(indicators, disabled) {
3280
+ forEach(indicators.concat(disabled), function(ele) {
3281
+ const internalData = getInternalData(ele)
3282
+ internalData.requestCount = (internalData.requestCount || 1) - 1
3283
+ })
3238
3284
  forEach(indicators, function(ic) {
3239
3285
  const internalData = getInternalData(ic)
3240
- internalData.requestCount = (internalData.requestCount || 0) - 1
3241
3286
  if (internalData.requestCount === 0) {
3242
3287
  ic.classList.remove.call(ic.classList, htmx.config.requestClass)
3243
3288
  }
3244
3289
  })
3245
3290
  forEach(disabled, function(disabledElement) {
3246
3291
  const internalData = getInternalData(disabledElement)
3247
- internalData.requestCount = (internalData.requestCount || 0) - 1
3248
3292
  if (internalData.requestCount === 0) {
3249
3293
  disabledElement.removeAttribute('disabled')
3250
3294
  disabledElement.removeAttribute('data-disabled-by-htmx')
@@ -3857,16 +3901,22 @@ var htmx = (function() {
3857
3901
  if (context) {
3858
3902
  if (context instanceof Element || typeof context === 'string') {
3859
3903
  return issueAjaxRequest(verb, path, null, null, {
3860
- targetOverride: resolveTarget(context),
3904
+ targetOverride: resolveTarget(context) || DUMMY_ELT,
3861
3905
  returnPromise: true
3862
3906
  })
3863
3907
  } else {
3908
+ let resolvedTarget = resolveTarget(context.target)
3909
+ // If target is supplied but can't resolve OR both target and source can't be resolved
3910
+ // then use DUMMY_ELT to abort the request with htmx:targetError to avoid it replacing body by mistake
3911
+ if ((context.target && !resolvedTarget) || (!resolvedTarget && !resolveTarget(context.source))) {
3912
+ resolvedTarget = DUMMY_ELT
3913
+ }
3864
3914
  return issueAjaxRequest(verb, path, resolveTarget(context.source), context.event,
3865
3915
  {
3866
3916
  handler: context.handler,
3867
3917
  headers: context.headers,
3868
3918
  values: context.values,
3869
- targetOverride: resolveTarget(context.target),
3919
+ targetOverride: resolvedTarget,
3870
3920
  swapOverride: context.swap,
3871
3921
  select: context.select,
3872
3922
  returnPromise: true
@@ -3928,7 +3978,7 @@ var htmx = (function() {
3928
3978
  const formData = new FormData()
3929
3979
  for (const key in obj) {
3930
3980
  if (obj.hasOwnProperty(key)) {
3931
- if (typeof obj[key].forEach === 'function') {
3981
+ if (obj[key] && typeof obj[key].forEach === 'function') {
3932
3982
  obj[key].forEach(function(v) { formData.append(key, v) })
3933
3983
  } else if (typeof obj[key] === 'object' && !(obj[key] instanceof Blob)) {
3934
3984
  formData.append(key, JSON.stringify(obj[key]))
@@ -4021,7 +4071,7 @@ var htmx = (function() {
4021
4071
  return false
4022
4072
  }
4023
4073
  target.delete(name)
4024
- if (typeof value.forEach === 'function') {
4074
+ if (value && typeof value.forEach === 'function') {
4025
4075
  value.forEach(function(v) { target.append(name, v) })
4026
4076
  } else if (typeof value === 'object' && !(value instanceof Blob)) {
4027
4077
  target.append(name, JSON.stringify(value))
@@ -4656,7 +4706,8 @@ var htmx = (function() {
4656
4706
  serverResponse,
4657
4707
  isError,
4658
4708
  ignoreTitle,
4659
- selectOverride
4709
+ selectOverride,
4710
+ swapOverride
4660
4711
  }, responseInfo)
4661
4712
 
4662
4713
  if (responseHandling.event && !triggerEvent(target, responseHandling.event, beforeSwapDetails)) return
@@ -4668,6 +4719,7 @@ var htmx = (function() {
4668
4719
  isError = beforeSwapDetails.isError // allow updating error
4669
4720
  ignoreTitle = beforeSwapDetails.ignoreTitle // allow updating ignoring title
4670
4721
  selectOverride = beforeSwapDetails.selectOverride // allow updating select override
4722
+ swapOverride = beforeSwapDetails.swapOverride // allow updating swap override
4671
4723
 
4672
4724
  responseInfo.target = target // Make updated target available to response events
4673
4725
  responseInfo.failed = isError // Make failed property available to response events
@@ -4687,9 +4739,6 @@ var htmx = (function() {
4687
4739
  saveCurrentPageToHistory()
4688
4740
  }
4689
4741
 
4690
- if (hasHeader(xhr, /HX-Reswap:/i)) {
4691
- swapOverride = xhr.getResponseHeader('HX-Reswap')
4692
- }
4693
4742
  var swapSpec = getSwapSpecification(elt, swapOverride)
4694
4743
 
4695
4744
  if (!swapSpec.hasOwnProperty('ignoreTitle')) {
@@ -5122,7 +5171,7 @@ var htmx = (function() {
5122
5171
  */
5123
5172
 
5124
5173
  /**
5125
- * @typedef {HtmxResponseInfo & {shouldSwap: boolean, serverResponse: any, isError: boolean, ignoreTitle: boolean, selectOverride:string}} HtmxBeforeSwapDetails
5174
+ * @typedef {HtmxResponseInfo & {shouldSwap: boolean, serverResponse: any, isError: boolean, ignoreTitle: boolean, selectOverride:string, swapOverride:string}} HtmxBeforeSwapDetails
5126
5175
  */
5127
5176
 
5128
5177
  /**