htmx.org 2.0.4 → 2.0.6

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
@@ -83,7 +83,7 @@ var htmx = (function() {
83
83
  */
84
84
  historyEnabled: true,
85
85
  /**
86
- * The number of pages to keep in **localStorage** for history support.
86
+ * The number of pages to keep in **sessionStorage** for history support.
87
87
  * @type number
88
88
  * @default 10
89
89
  */
@@ -272,13 +272,25 @@ var htmx = (function() {
272
272
  * @type boolean
273
273
  * @default true
274
274
  */
275
- allowNestedOobSwaps: true
275
+ allowNestedOobSwaps: true,
276
+ /**
277
+ * Whether to treat history cache miss full page reload requests as a "HX-Request" by returning this response header
278
+ * This should always be disabled when using HX-Request header to optionally return partial responses
279
+ * @type boolean
280
+ * @default true
281
+ */
282
+ historyRestoreAsHxRequest: true
276
283
  },
277
284
  /** @type {typeof parseInterval} */
278
285
  parseInterval: null,
286
+ /**
287
+ * proxy of window.location used for page reload functions
288
+ * @type location
289
+ */
290
+ location,
279
291
  /** @type {typeof internalEval} */
280
292
  _: null,
281
- version: '2.0.4'
293
+ version: '2.0.6'
282
294
  }
283
295
  // Tsc madness part 2
284
296
  htmx.onLoad = onLoadHelper
@@ -485,10 +497,7 @@ var htmx = (function() {
485
497
  * @returns {boolean}
486
498
  */
487
499
  function matches(elt, selector) {
488
- // @ts-ignore: non-standard properties for browser compatibility
489
- // noinspection JSUnresolvedVariable
490
- const matchesFunction = elt instanceof Element && (elt.matches || elt.matchesSelector || elt.msMatchesSelector || elt.mozMatchesSelector || elt.webkitMatchesSelector || elt.oMatchesSelector)
491
- return !!matchesFunction && matchesFunction.call(elt, selector)
500
+ return elt instanceof Element && elt.matches(selector)
492
501
  }
493
502
 
494
503
  /**
@@ -811,10 +820,10 @@ var htmx = (function() {
811
820
  * @returns {boolean}
812
821
  */
813
822
  function canAccessLocalStorage() {
814
- const test = 'htmx:localStorageTest'
823
+ const test = 'htmx:sessionStorageTest'
815
824
  try {
816
- localStorage.setItem(test, test)
817
- localStorage.removeItem(test)
825
+ sessionStorage.setItem(test, test)
826
+ sessionStorage.removeItem(test)
818
827
  return true
819
828
  } catch (e) {
820
829
  return false
@@ -826,20 +835,16 @@ var htmx = (function() {
826
835
  * @returns {string}
827
836
  */
828
837
  function normalizePath(path) {
829
- try {
830
- const url = new URL(path)
831
- if (url) {
832
- path = url.pathname + url.search
833
- }
834
- // remove trailing slash, unless index page
835
- if (!(/^\/$/.test(path))) {
836
- path = path.replace(/\/+$/, '')
837
- }
838
- return path
839
- } catch (e) {
840
- // be kind to IE11, which doesn't support URL()
841
- return path
838
+ // use dummy base URL to allow normalize on path only
839
+ const url = new URL(path, 'http://x')
840
+ if (url) {
841
+ path = url.pathname + url.search
842
842
  }
843
+ // remove trailing slash, unless index page
844
+ if (path != '/') {
845
+ path = path.replace(/\/+$/, '')
846
+ }
847
+ return path
843
848
  }
844
849
 
845
850
  //= =========================================================================================
@@ -1075,18 +1080,10 @@ var htmx = (function() {
1075
1080
  */
1076
1081
  function closest(elt, selector) {
1077
1082
  elt = asElement(resolveTarget(elt))
1078
- if (elt && elt.closest) {
1083
+ if (elt) {
1079
1084
  return elt.closest(selector)
1080
- } else {
1081
- // TODO remove when IE goes away
1082
- do {
1083
- if (elt == null || matches(elt, selector)) {
1084
- return elt
1085
- }
1086
- }
1087
- while (elt = elt && asElement(parentElt(elt)))
1088
- return null
1089
1085
  }
1086
+ return null
1090
1087
  }
1091
1088
 
1092
1089
  /**
@@ -1161,17 +1158,17 @@ var htmx = (function() {
1161
1158
  const selector = normalizeSelector(parts.shift())
1162
1159
  let item
1163
1160
  if (selector.indexOf('closest ') === 0) {
1164
- item = closest(asElement(elt), normalizeSelector(selector.substr(8)))
1161
+ item = closest(asElement(elt), normalizeSelector(selector.slice(8)))
1165
1162
  } else if (selector.indexOf('find ') === 0) {
1166
- item = find(asParentNode(elt), normalizeSelector(selector.substr(5)))
1163
+ item = find(asParentNode(elt), normalizeSelector(selector.slice(5)))
1167
1164
  } else if (selector === 'next' || selector === 'nextElementSibling') {
1168
1165
  item = asElement(elt).nextElementSibling
1169
1166
  } else if (selector.indexOf('next ') === 0) {
1170
- item = scanForwardQuery(elt, normalizeSelector(selector.substr(5)), !!global)
1167
+ item = scanForwardQuery(elt, normalizeSelector(selector.slice(5)), !!global)
1171
1168
  } else if (selector === 'previous' || selector === 'previousElementSibling') {
1172
1169
  item = asElement(elt).previousElementSibling
1173
1170
  } else if (selector.indexOf('previous ') === 0) {
1174
- item = scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)), !!global)
1171
+ item = scanBackwardsQuery(elt, normalizeSelector(selector.slice(9)), !!global)
1175
1172
  } else if (selector === 'document') {
1176
1173
  item = document
1177
1174
  } else if (selector === 'window') {
@@ -1351,6 +1348,16 @@ var htmx = (function() {
1351
1348
  return [findThisElement(elt, attrName)]
1352
1349
  } else {
1353
1350
  const result = querySelectorAllExt(elt, attrTarget)
1351
+ // find `inherit` whole word in value, make sure it's surrounded by commas or is at the start/end of string
1352
+ const shouldInherit = /(^|,)(\s*)inherit(\s*)($|,)/.test(attrTarget)
1353
+ if (shouldInherit) {
1354
+ const eltToInheritFrom = asElement(getClosestMatch(elt, function(parent) {
1355
+ return parent !== elt && hasAttribute(asElement(parent), attrName)
1356
+ }))
1357
+ if (eltToInheritFrom) {
1358
+ result.push(...findAttributeTargets(eltToInheritFrom, attrName))
1359
+ }
1360
+ }
1354
1361
  if (result.length === 0) {
1355
1362
  logError('The selector "' + attrTarget + '" on ' + attrName + ' returned no matches!')
1356
1363
  return [DUMMY_ELT]
@@ -1399,13 +1406,7 @@ var htmx = (function() {
1399
1406
  * @returns {boolean}
1400
1407
  */
1401
1408
  function shouldSettleAttribute(name) {
1402
- const attributesToSettle = htmx.config.attributesToSettle
1403
- for (let i = 0; i < attributesToSettle.length; i++) {
1404
- if (name === attributesToSettle[i]) {
1405
- return true
1406
- }
1407
- }
1408
- return false
1409
+ return htmx.config.attributesToSettle.includes(name)
1409
1410
  }
1410
1411
 
1411
1412
  /**
@@ -1454,7 +1455,7 @@ var htmx = (function() {
1454
1455
  */
1455
1456
  function oobSwap(oobValue, oobElement, settleInfo, rootNode) {
1456
1457
  rootNode = rootNode || getDocument()
1457
- let selector = '#' + getRawAttribute(oobElement, 'id')
1458
+ let selector = '#' + CSS.escape(getRawAttribute(oobElement, 'id'))
1458
1459
  /** @type HtmxSwapStyle */
1459
1460
  let swapStyle = 'outerHTML'
1460
1461
  if (oobValue === 'true') {
@@ -1469,7 +1470,7 @@ var htmx = (function() {
1469
1470
  oobElement.removeAttribute('data-hx-swap-oob')
1470
1471
 
1471
1472
  const targets = querySelectorAllExt(rootNode, selector, false)
1472
- if (targets) {
1473
+ if (targets.length) {
1473
1474
  forEach(
1474
1475
  targets,
1475
1476
  function(target) {
@@ -1627,14 +1628,11 @@ var htmx = (function() {
1627
1628
  */
1628
1629
  function attributeHash(elt) {
1629
1630
  let hash = 0
1630
- // IE fix
1631
- if (elt.attributes) {
1632
- for (let i = 0; i < elt.attributes.length; i++) {
1633
- const attribute = elt.attributes[i]
1634
- if (attribute.value) { // only include attributes w/ actual values (empty is same as non-existent)
1635
- hash = stringHash(attribute.name, hash)
1636
- hash = stringHash(attribute.value, hash)
1637
- }
1631
+ for (let i = 0; i < elt.attributes.length; i++) {
1632
+ const attribute = elt.attributes[i]
1633
+ if (attribute.value) { // only include attributes w/ actual values (empty is same as non-existent)
1634
+ hash = stringHash(attribute.name, hash)
1635
+ hash = stringHash(attribute.value, hash)
1638
1636
  }
1639
1637
  }
1640
1638
  return hash
@@ -1679,21 +1677,17 @@ var htmx = (function() {
1679
1677
  function cleanUpElement(element) {
1680
1678
  triggerEvent(element, 'htmx:beforeCleanupElement')
1681
1679
  deInitNode(element)
1682
- // @ts-ignore IE11 code
1683
- // noinspection JSUnresolvedReference
1684
- if (element.children) { // IE
1685
- // @ts-ignore
1686
- forEach(element.children, function(child) { cleanUpElement(child) })
1687
- }
1680
+ // @ts-ignore
1681
+ forEach(element.children, function(child) { cleanUpElement(child) })
1688
1682
  }
1689
1683
 
1690
1684
  /**
1691
- * @param {Node} target
1685
+ * @param {Element} target
1692
1686
  * @param {ParentNode} fragment
1693
1687
  * @param {HtmxSettleInfo} settleInfo
1694
1688
  */
1695
1689
  function swapOuterHTML(target, fragment, settleInfo) {
1696
- if (target instanceof Element && target.tagName === 'BODY') { // special case the body to innerHTML because DocumentFragments can't contain a body elt unfortunately
1690
+ if (target.tagName === 'BODY') { // special case the body to innerHTML because DocumentFragments can't contain a body elt unfortunately
1697
1691
  return swapInnerHTML(target, fragment, settleInfo)
1698
1692
  }
1699
1693
  /** @type {Node} */
@@ -1719,15 +1713,11 @@ var htmx = (function() {
1719
1713
  newElt = newElt.nextSibling
1720
1714
  }
1721
1715
  cleanUpElement(target)
1722
- if (target instanceof Element) {
1723
- target.remove()
1724
- } else {
1725
- target.parentNode.removeChild(target)
1726
- }
1716
+ target.remove()
1727
1717
  }
1728
1718
 
1729
1719
  /**
1730
- * @param {Node} target
1720
+ * @param {Element} target
1731
1721
  * @param {ParentNode} fragment
1732
1722
  * @param {HtmxSettleInfo} settleInfo
1733
1723
  */
@@ -1736,7 +1726,7 @@ var htmx = (function() {
1736
1726
  }
1737
1727
 
1738
1728
  /**
1739
- * @param {Node} target
1729
+ * @param {Element} target
1740
1730
  * @param {ParentNode} fragment
1741
1731
  * @param {HtmxSettleInfo} settleInfo
1742
1732
  */
@@ -1745,7 +1735,7 @@ var htmx = (function() {
1745
1735
  }
1746
1736
 
1747
1737
  /**
1748
- * @param {Node} target
1738
+ * @param {Element} target
1749
1739
  * @param {ParentNode} fragment
1750
1740
  * @param {HtmxSettleInfo} settleInfo
1751
1741
  */
@@ -1754,7 +1744,7 @@ var htmx = (function() {
1754
1744
  }
1755
1745
 
1756
1746
  /**
1757
- * @param {Node} target
1747
+ * @param {Element} target
1758
1748
  * @param {ParentNode} fragment
1759
1749
  * @param {HtmxSettleInfo} settleInfo
1760
1750
  */
@@ -1763,7 +1753,7 @@ var htmx = (function() {
1763
1753
  }
1764
1754
 
1765
1755
  /**
1766
- * @param {Node} target
1756
+ * @param {Element} target
1767
1757
  */
1768
1758
  function swapDelete(target) {
1769
1759
  cleanUpElement(target)
@@ -1774,7 +1764,7 @@ var htmx = (function() {
1774
1764
  }
1775
1765
 
1776
1766
  /**
1777
- * @param {Node} target
1767
+ * @param {Element} target
1778
1768
  * @param {ParentNode} fragment
1779
1769
  * @param {HtmxSettleInfo} settleInfo
1780
1770
  */
@@ -1794,7 +1784,7 @@ var htmx = (function() {
1794
1784
  /**
1795
1785
  * @param {HtmxSwapStyle} swapStyle
1796
1786
  * @param {Element} elt
1797
- * @param {Node} target
1787
+ * @param {Element} target
1798
1788
  * @param {ParentNode} fragment
1799
1789
  * @param {HtmxSettleInfo} settleInfo
1800
1790
  */
@@ -1872,7 +1862,7 @@ var htmx = (function() {
1872
1862
  }
1873
1863
 
1874
1864
  /**
1875
- * Implements complete swapping pipeline, including: focus and selection preservation,
1865
+ * Implements complete swapping pipeline, including: delay, view transitions, focus and selection preservation,
1876
1866
  * title updates, scroll, OOB swapping, normal swapping and settling
1877
1867
  * @param {string|Element} target
1878
1868
  * @param {string} content
@@ -1883,14 +1873,19 @@ var htmx = (function() {
1883
1873
  if (!swapOptions) {
1884
1874
  swapOptions = {}
1885
1875
  }
1876
+ // optional transition API promise callbacks
1877
+ let settleResolve = null
1878
+ let settleReject = null
1886
1879
 
1887
- target = resolveTarget(target)
1888
- const rootNode = swapOptions.contextElement ? getRootNode(swapOptions.contextElement, false) : getDocument()
1880
+ let doSwap = function() {
1881
+ maybeCall(swapOptions.beforeSwapCallback)
1889
1882
 
1890
- // preserve focus and selection
1891
- const activeElt = document.activeElement
1892
- let selectionInfo = {}
1893
- try {
1883
+ target = resolveTarget(target)
1884
+ const rootNode = swapOptions.contextElement ? getRootNode(swapOptions.contextElement, false) : getDocument()
1885
+
1886
+ // preserve focus and selection
1887
+ const activeElt = document.activeElement
1888
+ let selectionInfo = {}
1894
1889
  selectionInfo = {
1895
1890
  elt: activeElt,
1896
1891
  // @ts-ignore
@@ -1898,123 +1893,160 @@ var htmx = (function() {
1898
1893
  // @ts-ignore
1899
1894
  end: activeElt ? activeElt.selectionEnd : null
1900
1895
  }
1901
- } catch (e) {
1902
- // safari issue - see https://github.com/microsoft/playwright/issues/5894
1903
- }
1904
- const settleInfo = makeSettleInfo(target)
1896
+ const settleInfo = makeSettleInfo(target)
1905
1897
 
1906
- // For text content swaps, don't parse the response as HTML, just insert it
1907
- if (swapSpec.swapStyle === 'textContent') {
1908
- target.textContent = content
1909
- // Otherwise, make the fragment and process it
1910
- } else {
1911
- let fragment = makeFragment(content)
1912
-
1913
- settleInfo.title = fragment.title
1914
-
1915
- // select-oob swaps
1916
- if (swapOptions.selectOOB) {
1917
- const oobSelectValues = swapOptions.selectOOB.split(',')
1918
- for (let i = 0; i < oobSelectValues.length; i++) {
1919
- const oobSelectValue = oobSelectValues[i].split(':', 2)
1920
- let id = oobSelectValue[0].trim()
1921
- if (id.indexOf('#') === 0) {
1922
- id = id.substring(1)
1898
+ // For text content swaps, don't parse the response as HTML, just insert it
1899
+ if (swapSpec.swapStyle === 'textContent') {
1900
+ target.textContent = content
1901
+ // Otherwise, make the fragment and process it
1902
+ } else {
1903
+ let fragment = makeFragment(content)
1904
+
1905
+ settleInfo.title = swapOptions.title || fragment.title
1906
+ if (swapOptions.historyRequest) {
1907
+ // @ts-ignore fragment can be a parentNode Element
1908
+ fragment = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment
1909
+ }
1910
+
1911
+ // select-oob swaps
1912
+ if (swapOptions.selectOOB) {
1913
+ const oobSelectValues = swapOptions.selectOOB.split(',')
1914
+ for (let i = 0; i < oobSelectValues.length; i++) {
1915
+ const oobSelectValue = oobSelectValues[i].split(':', 2)
1916
+ let id = oobSelectValue[0].trim()
1917
+ if (id.indexOf('#') === 0) {
1918
+ id = id.substring(1)
1919
+ }
1920
+ const oobValue = oobSelectValue[1] || 'true'
1921
+ const oobElement = fragment.querySelector('#' + id)
1922
+ if (oobElement) {
1923
+ oobSwap(oobValue, oobElement, settleInfo, rootNode)
1924
+ }
1923
1925
  }
1924
- const oobValue = oobSelectValue[1] || 'true'
1925
- const oobElement = fragment.querySelector('#' + id)
1926
- if (oobElement) {
1927
- oobSwap(oobValue, oobElement, settleInfo, rootNode)
1926
+ }
1927
+ // oob swaps
1928
+ findAndSwapOobElements(fragment, settleInfo, rootNode)
1929
+ forEach(findAll(fragment, 'template'), /** @param {HTMLTemplateElement} template */function(template) {
1930
+ if (template.content && findAndSwapOobElements(template.content, settleInfo, rootNode)) {
1931
+ // Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
1932
+ template.remove()
1928
1933
  }
1934
+ })
1935
+
1936
+ // normal swap
1937
+ if (swapOptions.select) {
1938
+ const newFragment = getDocument().createDocumentFragment()
1939
+ forEach(fragment.querySelectorAll(swapOptions.select), function(node) {
1940
+ newFragment.appendChild(node)
1941
+ })
1942
+ fragment = newFragment
1929
1943
  }
1944
+ handlePreservedElements(fragment)
1945
+ swapWithStyle(swapSpec.swapStyle, swapOptions.contextElement, target, fragment, settleInfo)
1946
+ restorePreservedElements()
1930
1947
  }
1931
- // oob swaps
1932
- findAndSwapOobElements(fragment, settleInfo, rootNode)
1933
- forEach(findAll(fragment, 'template'), /** @param {HTMLTemplateElement} template */function(template) {
1934
- if (template.content && findAndSwapOobElements(template.content, settleInfo, rootNode)) {
1935
- // Avoid polluting the DOM with empty templates that were only used to encapsulate oob swap
1936
- template.remove()
1948
+
1949
+ // apply saved focus and selection information to swapped content
1950
+ if (selectionInfo.elt &&
1951
+ !bodyContains(selectionInfo.elt) &&
1952
+ getRawAttribute(selectionInfo.elt, 'id')) {
1953
+ const newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, 'id'))
1954
+ const focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }
1955
+ if (newActiveElt) {
1956
+ // @ts-ignore
1957
+ if (selectionInfo.start && newActiveElt.setSelectionRange) {
1958
+ try {
1959
+ // @ts-ignore
1960
+ newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end)
1961
+ } catch (e) {
1962
+ // the setSelectionRange method is present on fields that don't support it, so just let this fail
1963
+ }
1964
+ }
1965
+ newActiveElt.focus(focusOptions)
1937
1966
  }
1967
+ }
1968
+
1969
+ target.classList.remove(htmx.config.swappingClass)
1970
+ forEach(settleInfo.elts, function(elt) {
1971
+ if (elt.classList) {
1972
+ elt.classList.add(htmx.config.settlingClass)
1973
+ }
1974
+ triggerEvent(elt, 'htmx:afterSwap', swapOptions.eventInfo)
1938
1975
  })
1976
+ maybeCall(swapOptions.afterSwapCallback)
1939
1977
 
1940
- // normal swap
1941
- if (swapOptions.select) {
1942
- const newFragment = getDocument().createDocumentFragment()
1943
- forEach(fragment.querySelectorAll(swapOptions.select), function(node) {
1944
- newFragment.appendChild(node)
1945
- })
1946
- fragment = newFragment
1978
+ // merge in new title after swap but before settle
1979
+ if (!swapSpec.ignoreTitle) {
1980
+ handleTitle(settleInfo.title)
1947
1981
  }
1948
- handlePreservedElements(fragment)
1949
- swapWithStyle(swapSpec.swapStyle, swapOptions.contextElement, target, fragment, settleInfo)
1950
- restorePreservedElements()
1951
- }
1952
1982
 
1953
- // apply saved focus and selection information to swapped content
1954
- if (selectionInfo.elt &&
1955
- !bodyContains(selectionInfo.elt) &&
1956
- getRawAttribute(selectionInfo.elt, 'id')) {
1957
- const newActiveElt = document.getElementById(getRawAttribute(selectionInfo.elt, 'id'))
1958
- const focusOptions = { preventScroll: swapSpec.focusScroll !== undefined ? !swapSpec.focusScroll : !htmx.config.defaultFocusScroll }
1959
- if (newActiveElt) {
1960
- // @ts-ignore
1961
- if (selectionInfo.start && newActiveElt.setSelectionRange) {
1962
- try {
1963
- // @ts-ignore
1964
- newActiveElt.setSelectionRange(selectionInfo.start, selectionInfo.end)
1965
- } catch (e) {
1966
- // the setSelectionRange method is present on fields that don't support it, so just let this fail
1983
+ // settle
1984
+ const doSettle = function() {
1985
+ forEach(settleInfo.tasks, function(task) {
1986
+ task.call()
1987
+ })
1988
+ forEach(settleInfo.elts, function(elt) {
1989
+ if (elt.classList) {
1990
+ elt.classList.remove(htmx.config.settlingClass)
1991
+ }
1992
+ triggerEvent(elt, 'htmx:afterSettle', swapOptions.eventInfo)
1993
+ })
1994
+
1995
+ if (swapOptions.anchor) {
1996
+ const anchorTarget = asElement(resolveTarget('#' + swapOptions.anchor))
1997
+ if (anchorTarget) {
1998
+ anchorTarget.scrollIntoView({ block: 'start', behavior: 'auto' })
1967
1999
  }
1968
2000
  }
1969
- newActiveElt.focus(focusOptions)
2001
+
2002
+ updateScrollState(settleInfo.elts, swapSpec)
2003
+ maybeCall(swapOptions.afterSettleCallback)
2004
+ maybeCall(settleResolve)
1970
2005
  }
1971
- }
1972
2006
 
1973
- target.classList.remove(htmx.config.swappingClass)
1974
- forEach(settleInfo.elts, function(elt) {
1975
- if (elt.classList) {
1976
- elt.classList.add(htmx.config.settlingClass)
2007
+ if (swapSpec.settleDelay > 0) {
2008
+ getWindow().setTimeout(doSettle, swapSpec.settleDelay)
2009
+ } else {
2010
+ doSettle()
1977
2011
  }
1978
- triggerEvent(elt, 'htmx:afterSwap', swapOptions.eventInfo)
1979
- })
1980
- if (swapOptions.afterSwapCallback) {
1981
- swapOptions.afterSwapCallback()
1982
2012
  }
1983
-
1984
- // merge in new title after swap but before settle
1985
- if (!swapSpec.ignoreTitle) {
1986
- handleTitle(settleInfo.title)
2013
+ let shouldTransition = htmx.config.globalViewTransitions
2014
+ if (swapSpec.hasOwnProperty('transition')) {
2015
+ shouldTransition = swapSpec.transition
1987
2016
  }
1988
2017
 
1989
- // settle
1990
- const doSettle = function() {
1991
- forEach(settleInfo.tasks, function(task) {
1992
- task.call()
1993
- })
1994
- forEach(settleInfo.elts, function(elt) {
1995
- if (elt.classList) {
1996
- elt.classList.remove(htmx.config.settlingClass)
1997
- }
1998
- triggerEvent(elt, 'htmx:afterSettle', swapOptions.eventInfo)
1999
- })
2000
-
2001
- if (swapOptions.anchor) {
2002
- const anchorTarget = asElement(resolveTarget('#' + swapOptions.anchor))
2003
- if (anchorTarget) {
2004
- anchorTarget.scrollIntoView({ block: 'start', behavior: 'auto' })
2005
- }
2006
- }
2018
+ const elt = swapOptions.contextElement || getDocument()
2007
2019
 
2008
- updateScrollState(settleInfo.elts, swapSpec)
2009
- if (swapOptions.afterSettleCallback) {
2010
- swapOptions.afterSettleCallback()
2020
+ if (shouldTransition &&
2021
+ triggerEvent(elt, 'htmx:beforeTransition', swapOptions.eventInfo) &&
2022
+ typeof Promise !== 'undefined' &&
2023
+ // @ts-ignore experimental feature atm
2024
+ document.startViewTransition) {
2025
+ const settlePromise = new Promise(function(_resolve, _reject) {
2026
+ settleResolve = _resolve
2027
+ settleReject = _reject
2028
+ })
2029
+ // wrap the original doSwap() in a call to startViewTransition()
2030
+ const innerDoSwap = doSwap
2031
+ doSwap = function() {
2032
+ // @ts-ignore experimental feature atm
2033
+ document.startViewTransition(function() {
2034
+ innerDoSwap()
2035
+ return settlePromise
2036
+ })
2011
2037
  }
2012
2038
  }
2013
2039
 
2014
- if (swapSpec.settleDelay > 0) {
2015
- getWindow().setTimeout(doSettle, swapSpec.settleDelay)
2016
- } else {
2017
- doSettle()
2040
+ try {
2041
+ if (swapSpec?.swapDelay && swapSpec.swapDelay > 0) {
2042
+ getWindow().setTimeout(doSwap, swapSpec.swapDelay)
2043
+ } else {
2044
+ doSwap()
2045
+ }
2046
+ } catch (e) {
2047
+ triggerErrorEvent(elt, 'htmx:swapError', swapOptions.eventInfo)
2048
+ maybeCall(settleReject)
2049
+ throw e
2018
2050
  }
2019
2051
  }
2020
2052
 
@@ -2368,7 +2400,7 @@ var htmx = (function() {
2368
2400
  if (path == null || path === '') {
2369
2401
  // if there is no action attribute on the form set path to current href before the
2370
2402
  // following logic to properly clear parameters on a GET (not on a POST!)
2371
- path = getDocument().location.href
2403
+ path = location.href
2372
2404
  }
2373
2405
  if (verb === 'get' && path.includes('?')) {
2374
2406
  path = path.replace(/\?[^#]+/, '')
@@ -2389,23 +2421,24 @@ var htmx = (function() {
2389
2421
 
2390
2422
  /**
2391
2423
  * @param {Event} evt
2392
- * @param {Node} node
2424
+ * @param {Element} elt
2393
2425
  * @returns {boolean}
2394
2426
  */
2395
- function shouldCancel(evt, node) {
2396
- const elt = asElement(node)
2397
- if (!elt) {
2398
- return false
2399
- }
2427
+ function shouldCancel(evt, elt) {
2400
2428
  if (evt.type === 'submit' || evt.type === 'click') {
2429
+ // use elt from event that was submitted/clicked where possible to determining if default form/link behavior should be canceled
2430
+ elt = asElement(evt.target) || elt
2401
2431
  if (elt.tagName === 'FORM') {
2402
2432
  return true
2403
2433
  }
2404
- if (matches(elt, 'input[type="submit"], button') &&
2405
- (matches(elt, '[form]') || closest(elt, 'form') !== null)) {
2434
+ // @ts-ignore Do not cancel on buttons that 1) don't have a related form or 2) have a type attribute of 'reset'/'button'.
2435
+ // The properties will resolve to undefined for elements that don't define 'type' or 'form', which is fine
2436
+ if (elt.form && elt.type === 'submit') {
2406
2437
  return true
2407
2438
  }
2408
- if (elt instanceof HTMLAnchorElement && elt.href &&
2439
+ elt = elt.closest('a')
2440
+ // @ts-ignore check for a link wrapping the event elt or if elt is a link. elt will be link so href check is fine
2441
+ if (elt && elt.href &&
2409
2442
  (elt.getAttribute('href') === '#' || elt.getAttribute('href').indexOf('#') !== 0)) {
2410
2443
  return true
2411
2444
  }
@@ -2445,7 +2478,7 @@ var htmx = (function() {
2445
2478
  }
2446
2479
 
2447
2480
  /**
2448
- * @param {Node} elt
2481
+ * @param {Element} elt
2449
2482
  * @param {TriggerHandler} handler
2450
2483
  * @param {HtmxNodeInternalData} nodeData
2451
2484
  * @param {HtmxTriggerSpecification} triggerSpec
@@ -2512,7 +2545,7 @@ var htmx = (function() {
2512
2545
  }
2513
2546
  }
2514
2547
  if (triggerSpec.changed) {
2515
- const node = event.target
2548
+ const node = evt.target
2516
2549
  // @ts-ignore value will be undefined for non-input elements, which is fine
2517
2550
  const value = node.value
2518
2551
  const lastValue = elementData.lastValue.get(triggerSpec)
@@ -2635,7 +2668,7 @@ var htmx = (function() {
2635
2668
  triggerSpecs.forEach(function(triggerSpec) {
2636
2669
  addTriggerHandler(elt, triggerSpec, nodeData, function(node, evt) {
2637
2670
  const elt = asElement(node)
2638
- if (closest(elt, htmx.config.disableSelector)) {
2671
+ if (eltIsDisabled(elt)) {
2639
2672
  cleanUpElement(elt)
2640
2673
  return
2641
2674
  }
@@ -2649,12 +2682,12 @@ var htmx = (function() {
2649
2682
 
2650
2683
  /**
2651
2684
  * @callback TriggerHandler
2652
- * @param {Node} elt
2685
+ * @param {Element} elt
2653
2686
  * @param {Event} [evt]
2654
2687
  */
2655
2688
 
2656
2689
  /**
2657
- * @param {Node} elt
2690
+ * @param {Element} elt
2658
2691
  * @param {HtmxTriggerSpecification} triggerSpec
2659
2692
  * @param {HtmxNodeInternalData} nodeData
2660
2693
  * @param {TriggerHandler} handler
@@ -2779,7 +2812,7 @@ var htmx = (function() {
2779
2812
  * @param {Event} evt
2780
2813
  */
2781
2814
  function maybeSetLastButtonClicked(evt) {
2782
- const elt = /** @type {HTMLButtonElement|HTMLInputElement} */ (closest(asElement(evt.target), "button, input[type='submit']"))
2815
+ const elt = getTargetButton(evt.target)
2783
2816
  const internalData = getRelatedFormData(evt)
2784
2817
  if (internalData) {
2785
2818
  internalData.lastButtonClicked = elt
@@ -2796,19 +2829,33 @@ var htmx = (function() {
2796
2829
  }
2797
2830
  }
2798
2831
 
2832
+ /**
2833
+ * @param {EventTarget} target
2834
+ * @returns {HTMLButtonElement|HTMLInputElement|null}
2835
+ */
2836
+ function getTargetButton(target) {
2837
+ return /** @type {HTMLButtonElement|HTMLInputElement|null} */ (closest(asElement(target), "button, input[type='submit']"))
2838
+ }
2839
+
2840
+ /**
2841
+ * @param {Element} elt
2842
+ * @returns {HTMLFormElement|null}
2843
+ */
2844
+ function getRelatedForm(elt) {
2845
+ // @ts-ignore Get the related form if available, else find the closest parent form
2846
+ return elt.form || closest(elt, 'form')
2847
+ }
2848
+
2799
2849
  /**
2800
2850
  * @param {Event} evt
2801
2851
  * @returns {HtmxNodeInternalData|undefined}
2802
2852
  */
2803
2853
  function getRelatedFormData(evt) {
2804
- const elt = closest(asElement(evt.target), "button, input[type='submit']")
2854
+ const elt = getTargetButton(evt.target)
2805
2855
  if (!elt) {
2806
2856
  return
2807
2857
  }
2808
- const form = resolveTarget('#' + getRawAttribute(elt, 'form'), elt.getRootNode()) || closest(elt, 'form')
2809
- if (!form) {
2810
- return
2811
- }
2858
+ const form = getRelatedForm(elt)
2812
2859
  return getInternalData(form)
2813
2860
  }
2814
2861
 
@@ -2885,44 +2932,52 @@ var htmx = (function() {
2885
2932
  * @param {Element|HTMLInputElement} elt
2886
2933
  */
2887
2934
  function initNode(elt) {
2888
- if (closest(elt, htmx.config.disableSelector)) {
2889
- cleanUpElement(elt)
2890
- return
2891
- }
2892
- const nodeData = getInternalData(elt)
2893
- const attrHash = attributeHash(elt)
2894
- if (nodeData.initHash !== attrHash) {
2895
- // clean up any previously processed info
2896
- deInitNode(elt)
2935
+ triggerEvent(elt, 'htmx:beforeProcessNode')
2897
2936
 
2898
- nodeData.initHash = attrHash
2899
-
2900
- triggerEvent(elt, 'htmx:beforeProcessNode')
2901
-
2902
- const triggerSpecs = getTriggerSpecs(elt)
2903
- const hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs)
2937
+ const nodeData = getInternalData(elt)
2938
+ const triggerSpecs = getTriggerSpecs(elt)
2939
+ const hasExplicitHttpAction = processVerbs(elt, nodeData, triggerSpecs)
2904
2940
 
2905
- if (!hasExplicitHttpAction) {
2906
- if (getClosestAttributeValue(elt, 'hx-boost') === 'true') {
2907
- boostElement(elt, nodeData, triggerSpecs)
2908
- } else if (hasAttribute(elt, 'hx-trigger')) {
2909
- triggerSpecs.forEach(function(triggerSpec) {
2910
- // For "naked" triggers, don't do anything at all
2911
- addTriggerHandler(elt, triggerSpec, nodeData, function() {
2912
- })
2941
+ if (!hasExplicitHttpAction) {
2942
+ if (getClosestAttributeValue(elt, 'hx-boost') === 'true') {
2943
+ boostElement(elt, nodeData, triggerSpecs)
2944
+ } else if (hasAttribute(elt, 'hx-trigger')) {
2945
+ triggerSpecs.forEach(function(triggerSpec) {
2946
+ // For "naked" triggers, don't do anything at all
2947
+ addTriggerHandler(elt, triggerSpec, nodeData, function() {
2913
2948
  })
2914
- }
2949
+ })
2915
2950
  }
2951
+ }
2916
2952
 
2917
- // Handle submit buttons/inputs that have the form attribute set
2918
- // see https://developer.mozilla.org/docs/Web/HTML/Element/button
2919
- if (elt.tagName === 'FORM' || (getRawAttribute(elt, 'type') === 'submit' && hasAttribute(elt, 'form'))) {
2920
- initButtonTracking(elt)
2921
- }
2953
+ // Handle submit buttons/inputs that have the form attribute set
2954
+ // see https://developer.mozilla.org/docs/Web/HTML/Element/button
2955
+ if (elt.tagName === 'FORM' || (getRawAttribute(elt, 'type') === 'submit' && hasAttribute(elt, 'form'))) {
2956
+ initButtonTracking(elt)
2957
+ }
2958
+
2959
+ nodeData.firstInitCompleted = true
2960
+ triggerEvent(elt, 'htmx:afterProcessNode')
2961
+ }
2922
2962
 
2923
- nodeData.firstInitCompleted = true
2924
- triggerEvent(elt, 'htmx:afterProcessNode')
2963
+ /**
2964
+ * @param {Element} elt
2965
+ * @returns {boolean}
2966
+ */
2967
+ function maybeDeInitAndHash(elt) {
2968
+ // Ensure only valid Elements and not shadow DOM roots are inited
2969
+ if (!(elt instanceof Element)) {
2970
+ return false
2971
+ }
2972
+
2973
+ const nodeData = getInternalData(elt)
2974
+ const hash = attributeHash(elt)
2975
+ if (nodeData.initHash !== hash) {
2976
+ deInitNode(elt)
2977
+ nodeData.initHash = hash
2978
+ return true
2925
2979
  }
2980
+ return false
2926
2981
  }
2927
2982
 
2928
2983
  /**
@@ -2934,13 +2989,27 @@ var htmx = (function() {
2934
2989
  */
2935
2990
  function processNode(elt) {
2936
2991
  elt = resolveTarget(elt)
2937
- if (closest(elt, htmx.config.disableSelector)) {
2992
+ if (eltIsDisabled(elt)) {
2938
2993
  cleanUpElement(elt)
2939
2994
  return
2940
2995
  }
2941
- initNode(elt)
2942
- forEach(findElementsToProcess(elt), function(child) { initNode(child) })
2996
+
2997
+ const elementsToInit = []
2998
+ if (maybeDeInitAndHash(elt)) {
2999
+ elementsToInit.push(elt)
3000
+ }
3001
+ forEach(findElementsToProcess(elt), function(child) {
3002
+ if (eltIsDisabled(child)) {
3003
+ cleanUpElement(child)
3004
+ return
3005
+ }
3006
+ if (maybeDeInitAndHash(child)) {
3007
+ elementsToInit.push(child)
3008
+ }
3009
+ })
3010
+
2943
3011
  forEach(findHxOnWildcardElements(elt), processHxOnWildcard)
3012
+ forEach(elementsToInit, initNode)
2944
3013
  }
2945
3014
 
2946
3015
  //= ===================================================================
@@ -2961,16 +3030,9 @@ var htmx = (function() {
2961
3030
  * @returns {CustomEvent}
2962
3031
  */
2963
3032
  function makeEvent(eventName, detail) {
2964
- let evt
2965
- if (window.CustomEvent && typeof window.CustomEvent === 'function') {
2966
- // TODO: `composed: true` here is a hack to make global event handlers work with events in shadow DOM
2967
- // This breaks expected encapsulation but needs to be here until decided otherwise by core devs
2968
- evt = new CustomEvent(eventName, { bubbles: true, cancelable: true, composed: true, detail })
2969
- } else {
2970
- evt = getDocument().createEvent('CustomEvent')
2971
- evt.initCustomEvent(eventName, true, true, detail)
2972
- }
2973
- return evt
3033
+ // TODO: `composed: true` here is a hack to make global event handlers work with events in shadow DOM
3034
+ // This breaks expected encapsulation but needs to be here until decided otherwise by core devs
3035
+ return new CustomEvent(eventName, { bubbles: true, cancelable: true, composed: true, detail })
2974
3036
  }
2975
3037
 
2976
3038
  /**
@@ -2992,15 +3054,17 @@ var htmx = (function() {
2992
3054
 
2993
3055
  /**
2994
3056
  * `withExtensions` locates all active extensions for a provided element, then
2995
- * executes the provided function using each of the active extensions. It should
3057
+ * executes the provided function using each of the active extensions. You can filter
3058
+ * the element's extensions by giving it a list of extensions to ignore. It should
2996
3059
  * be called internally at every extendable execution point in htmx.
2997
3060
  *
2998
3061
  * @param {Element} elt
2999
3062
  * @param {(extension:HtmxExtension) => void} toDo
3063
+ * @param {string[]=} extensionsToIgnore
3000
3064
  * @returns void
3001
3065
  */
3002
- function withExtensions(elt, toDo) {
3003
- forEach(getExtensions(elt), function(extension) {
3066
+ function withExtensions(elt, toDo, extensionsToIgnore) {
3067
+ forEach(getExtensions(elt, [], extensionsToIgnore), function(extension) {
3004
3068
  try {
3005
3069
  toDo(extension)
3006
3070
  } catch (e) {
@@ -3010,11 +3074,7 @@ var htmx = (function() {
3010
3074
  }
3011
3075
 
3012
3076
  function logError(msg) {
3013
- if (console.error) {
3014
- console.error(msg)
3015
- } else if (console.log) {
3016
- console.log('ERROR: ', msg)
3017
- }
3077
+ console.error(msg)
3018
3078
  }
3019
3079
 
3020
3080
  /**
@@ -3058,6 +3118,16 @@ var htmx = (function() {
3058
3118
  //= ===================================================================
3059
3119
  let currentPathForHistory = location.pathname + location.search
3060
3120
 
3121
+ /**
3122
+ * @param {string} path
3123
+ */
3124
+ function setCurrentPathForHistory(path) {
3125
+ currentPathForHistory = path
3126
+ if (canAccessLocalStorage()) {
3127
+ sessionStorage.setItem('htmx-current-path-for-history', path)
3128
+ }
3129
+ }
3130
+
3061
3131
  /**
3062
3132
  * @returns {Element}
3063
3133
  */
@@ -3082,13 +3152,13 @@ var htmx = (function() {
3082
3152
 
3083
3153
  if (htmx.config.historyCacheSize <= 0) {
3084
3154
  // make sure that an eventually already existing cache is purged
3085
- localStorage.removeItem('htmx-history-cache')
3155
+ sessionStorage.removeItem('htmx-history-cache')
3086
3156
  return
3087
3157
  }
3088
3158
 
3089
3159
  url = normalizePath(url)
3090
3160
 
3091
- const historyCache = parseJSON(localStorage.getItem('htmx-history-cache')) || []
3161
+ const historyCache = parseJSON(sessionStorage.getItem('htmx-history-cache')) || []
3092
3162
  for (let i = 0; i < historyCache.length; i++) {
3093
3163
  if (historyCache[i].url === url) {
3094
3164
  historyCache.splice(i, 1)
@@ -3109,7 +3179,7 @@ var htmx = (function() {
3109
3179
  // keep trying to save the cache until it succeeds or is empty
3110
3180
  while (historyCache.length > 0) {
3111
3181
  try {
3112
- localStorage.setItem('htmx-history-cache', JSON.stringify(historyCache))
3182
+ sessionStorage.setItem('htmx-history-cache', JSON.stringify(historyCache))
3113
3183
  break
3114
3184
  } catch (e) {
3115
3185
  triggerErrorEvent(getDocument().body, 'htmx:historyCacheError', { cause: e, cache: historyCache })
@@ -3137,7 +3207,7 @@ var htmx = (function() {
3137
3207
 
3138
3208
  url = normalizePath(url)
3139
3209
 
3140
- const historyCache = parseJSON(localStorage.getItem('htmx-history-cache')) || []
3210
+ const historyCache = parseJSON(sessionStorage.getItem('htmx-history-cache')) || []
3141
3211
  for (let i = 0; i < historyCache.length; i++) {
3142
3212
  if (historyCache[i].url === url) {
3143
3213
  return historyCache[i]
@@ -3165,26 +3235,24 @@ var htmx = (function() {
3165
3235
 
3166
3236
  function saveCurrentPageToHistory() {
3167
3237
  const elt = getHistoryElement()
3168
- const path = currentPathForHistory || location.pathname + location.search
3238
+ let path = currentPathForHistory
3239
+ if (canAccessLocalStorage()) {
3240
+ path = sessionStorage.getItem('htmx-current-path-for-history')
3241
+ }
3242
+ path = path || location.pathname + location.search
3169
3243
 
3170
3244
  // Allow history snapshot feature to be disabled where hx-history="false"
3171
3245
  // is present *anywhere* in the current document we're about to save,
3172
3246
  // so we can prevent privileged data entering the cache.
3173
3247
  // The page will still be reachable as a history entry, but htmx will fetch it
3174
- // live from the server onpopstate rather than look in the localStorage cache
3175
- let disableHistoryCache
3176
- try {
3177
- disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]')
3178
- } catch (e) {
3179
- // IE11: insensitive modifier not supported so fallback to case sensitive selector
3180
- disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]')
3181
- }
3248
+ // live from the server onpopstate rather than look in the sessionStorage cache
3249
+ const disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]')
3182
3250
  if (!disableHistoryCache) {
3183
3251
  triggerEvent(getDocument().body, 'htmx:beforeHistorySave', { path, historyElt: elt })
3184
3252
  saveToHistoryCache(path, elt)
3185
3253
  }
3186
3254
 
3187
- if (htmx.config.historyEnabled) history.replaceState({ htmx: true }, getDocument().title, window.location.href)
3255
+ if (htmx.config.historyEnabled) history.replaceState({ htmx: true }, getDocument().title, location.href)
3188
3256
  }
3189
3257
 
3190
3258
  /**
@@ -3201,7 +3269,7 @@ var htmx = (function() {
3201
3269
  if (htmx.config.historyEnabled) {
3202
3270
  history.pushState({ htmx: true }, '', path)
3203
3271
  }
3204
- currentPathForHistory = path
3272
+ setCurrentPathForHistory(path)
3205
3273
  }
3206
3274
 
3207
3275
  /**
@@ -3209,7 +3277,7 @@ var htmx = (function() {
3209
3277
  */
3210
3278
  function replaceUrlInHistory(path) {
3211
3279
  if (htmx.config.historyEnabled) history.replaceState({ htmx: true }, '', path)
3212
- currentPathForHistory = path
3280
+ setCurrentPathForHistory(path)
3213
3281
  }
3214
3282
 
3215
3283
  /**
@@ -3226,33 +3294,31 @@ var htmx = (function() {
3226
3294
  */
3227
3295
  function loadHistoryFromServer(path) {
3228
3296
  const request = new XMLHttpRequest()
3229
- const details = { path, xhr: request }
3230
- triggerEvent(getDocument().body, 'htmx:historyCacheMiss', details)
3297
+ const swapSpec = { swapStyle: 'innerHTML', swapDelay: 0, settleDelay: 0 }
3298
+ const details = { path, xhr: request, historyElt: getHistoryElement(), swapSpec }
3231
3299
  request.open('GET', path, true)
3232
- request.setRequestHeader('HX-Request', 'true')
3300
+ if (htmx.config.historyRestoreAsHxRequest) {
3301
+ request.setRequestHeader('HX-Request', 'true')
3302
+ }
3233
3303
  request.setRequestHeader('HX-History-Restore-Request', 'true')
3234
- request.setRequestHeader('HX-Current-URL', getDocument().location.href)
3304
+ request.setRequestHeader('HX-Current-URL', location.href)
3235
3305
  request.onload = function() {
3236
3306
  if (this.status >= 200 && this.status < 400) {
3307
+ details.response = this.response
3237
3308
  triggerEvent(getDocument().body, 'htmx:historyCacheMissLoad', details)
3238
- const fragment = makeFragment(this.response)
3239
- /** @type ParentNode */
3240
- const content = fragment.querySelector('[hx-history-elt],[data-hx-history-elt]') || fragment
3241
- const historyElement = getHistoryElement()
3242
- const settleInfo = makeSettleInfo(historyElement)
3243
- handleTitle(fragment.title)
3244
-
3245
- handlePreservedElements(fragment)
3246
- swapInnerHTML(historyElement, content, settleInfo)
3247
- restorePreservedElements()
3248
- settleImmediately(settleInfo.tasks)
3249
- currentPathForHistory = path
3250
- triggerEvent(getDocument().body, 'htmx:historyRestore', { path, cacheMiss: true, serverResponse: this.response })
3309
+ swap(details.historyElt, details.response, swapSpec, {
3310
+ contextElement: details.historyElt,
3311
+ historyRequest: true
3312
+ })
3313
+ setCurrentPathForHistory(details.path)
3314
+ triggerEvent(getDocument().body, 'htmx:historyRestore', { path, cacheMiss: true, serverResponse: details.response })
3251
3315
  } else {
3252
3316
  triggerErrorEvent(getDocument().body, 'htmx:historyCacheMissLoadError', details)
3253
3317
  }
3254
3318
  }
3255
- request.send()
3319
+ if (triggerEvent(getDocument().body, 'htmx:historyCacheMiss', details)) {
3320
+ request.send() // only send request if event not prevented
3321
+ }
3256
3322
  }
3257
3323
 
3258
3324
  /**
@@ -3263,24 +3329,21 @@ var htmx = (function() {
3263
3329
  path = path || location.pathname + location.search
3264
3330
  const cached = getCachedHistory(path)
3265
3331
  if (cached) {
3266
- const fragment = makeFragment(cached.content)
3267
- const historyElement = getHistoryElement()
3268
- const settleInfo = makeSettleInfo(historyElement)
3269
- handleTitle(cached.title)
3270
- handlePreservedElements(fragment)
3271
- swapInnerHTML(historyElement, fragment, settleInfo)
3272
- restorePreservedElements()
3273
- settleImmediately(settleInfo.tasks)
3274
- getWindow().setTimeout(function() {
3275
- window.scrollTo(0, cached.scroll)
3276
- }, 0) // next 'tick', so browser has time to render layout
3277
- currentPathForHistory = path
3278
- triggerEvent(getDocument().body, 'htmx:historyRestore', { path, item: cached })
3332
+ const swapSpec = { swapStyle: 'innerHTML', swapDelay: 0, settleDelay: 0, scroll: cached.scroll }
3333
+ const details = { path, item: cached, historyElt: getHistoryElement(), swapSpec }
3334
+ if (triggerEvent(getDocument().body, 'htmx:historyCacheHit', details)) {
3335
+ swap(details.historyElt, cached.content, swapSpec, {
3336
+ contextElement: details.historyElt,
3337
+ title: cached.title
3338
+ })
3339
+ setCurrentPathForHistory(details.path)
3340
+ triggerEvent(getDocument().body, 'htmx:historyRestore', details)
3341
+ }
3279
3342
  } else {
3280
3343
  if (htmx.config.refreshOnHistoryMiss) {
3281
3344
  // @ts-ignore: optional parameter in reload() function throws error
3282
3345
  // noinspection JSUnresolvedReference
3283
- window.location.reload(true)
3346
+ htmx.location.reload(true)
3284
3347
  } else {
3285
3348
  loadHistoryFromServer(path)
3286
3349
  }
@@ -3385,7 +3448,8 @@ var htmx = (function() {
3385
3448
  return true
3386
3449
  }
3387
3450
 
3388
- /** @param {string} name
3451
+ /**
3452
+ * @param {string} name
3389
3453
  * @param {string|Array|FormDataEntryValue} value
3390
3454
  * @param {FormData} formData */
3391
3455
  function addValueToFormData(name, value, formData) {
@@ -3398,7 +3462,8 @@ var htmx = (function() {
3398
3462
  }
3399
3463
  }
3400
3464
 
3401
- /** @param {string} name
3465
+ /**
3466
+ * @param {string} name
3402
3467
  * @param {string|Array} value
3403
3468
  * @param {FormData} formData */
3404
3469
  function removeValueFromFormData(name, value, formData) {
@@ -3414,6 +3479,22 @@ var htmx = (function() {
3414
3479
  }
3415
3480
  }
3416
3481
 
3482
+ /**
3483
+ * @param {Element} elt
3484
+ * @returns {string|Array}
3485
+ */
3486
+ function getValueFromInput(elt) {
3487
+ if (elt instanceof HTMLSelectElement && elt.multiple) {
3488
+ return toArray(elt.querySelectorAll('option:checked')).map(function(e) { return (/** @type HTMLOptionElement */(e)).value })
3489
+ }
3490
+ // include file inputs
3491
+ if (elt instanceof HTMLInputElement && elt.files) {
3492
+ return toArray(elt.files)
3493
+ }
3494
+ // @ts-ignore value will be undefined for non-input elements, which is fine
3495
+ return elt.value
3496
+ }
3497
+
3417
3498
  /**
3418
3499
  * @param {Element[]} processed
3419
3500
  * @param {FormData} formData
@@ -3429,16 +3510,7 @@ var htmx = (function() {
3429
3510
  }
3430
3511
  if (shouldInclude(elt)) {
3431
3512
  const name = getRawAttribute(elt, 'name')
3432
- // @ts-ignore value will be undefined for non-input elements, which is fine
3433
- let value = elt.value
3434
- if (elt instanceof HTMLSelectElement && elt.multiple) {
3435
- value = toArray(elt.querySelectorAll('option:checked')).map(function(e) { return (/** @type HTMLOptionElement */(e)).value })
3436
- }
3437
- // include file inputs
3438
- if (elt instanceof HTMLInputElement && elt.files) {
3439
- value = toArray(elt.files)
3440
- }
3441
- addValueToFormData(name, value, formData)
3513
+ addValueToFormData(name, getValueFromInput(elt), formData)
3442
3514
  if (validate) {
3443
3515
  validateElement(elt, errors)
3444
3516
  }
@@ -3449,7 +3521,7 @@ var htmx = (function() {
3449
3521
  // The input has already been processed and added to the values, but the FormData that will be
3450
3522
  // constructed right after on the form, will include it once again. So remove that input's value
3451
3523
  // now to avoid duplicates
3452
- removeValueFromFormData(input.name, input.value, formData)
3524
+ removeValueFromFormData(input.name, getValueFromInput(input), formData)
3453
3525
  } else {
3454
3526
  processed.push(input)
3455
3527
  }
@@ -3467,7 +3539,6 @@ var htmx = (function() {
3467
3539
  }
3468
3540
 
3469
3541
  /**
3470
- *
3471
3542
  * @param {Element} elt
3472
3543
  * @param {HtmxElementValidationError[]} errors
3473
3544
  */
@@ -3522,9 +3593,9 @@ var htmx = (function() {
3522
3593
  validate = validate && internalData.lastButtonClicked.formNoValidate !== true
3523
3594
  }
3524
3595
 
3525
- // for a non-GET include the closest form
3596
+ // for a non-GET include the related form, which may or may not be a parent element of elt
3526
3597
  if (verb !== 'get') {
3527
- processInputValue(processed, priorityFormData, errors, closest(elt, 'form'), validate)
3598
+ processInputValue(processed, priorityFormData, errors, getRelatedForm(elt), validate)
3528
3599
  }
3529
3600
 
3530
3601
  // include the element itself
@@ -3604,7 +3675,7 @@ var htmx = (function() {
3604
3675
  'HX-Trigger': getRawAttribute(elt, 'id'),
3605
3676
  'HX-Trigger-Name': getRawAttribute(elt, 'name'),
3606
3677
  'HX-Target': getAttributeValue(target, 'id'),
3607
- 'HX-Current-URL': getDocument().location.href
3678
+ 'HX-Current-URL': location.href
3608
3679
  }
3609
3680
  getValuesForElement(elt, 'hx-headers', false, headers)
3610
3681
  if (prompt !== undefined) {
@@ -3782,6 +3853,11 @@ var htmx = (function() {
3782
3853
  target = target || last
3783
3854
  target.scrollTop = target.scrollHeight
3784
3855
  }
3856
+ if (typeof swapSpec.scroll === 'number') {
3857
+ getWindow().setTimeout(function() {
3858
+ window.scrollTo(0, /** @type number */ (swapSpec.scroll))
3859
+ }, 0) // next 'tick', so browser has time to render layout
3860
+ }
3785
3861
  }
3786
3862
  if (swapSpec.show) {
3787
3863
  var target = null
@@ -3810,9 +3886,10 @@ var htmx = (function() {
3810
3886
  * @param {string} attr
3811
3887
  * @param {boolean=} evalAsDefault
3812
3888
  * @param {Object=} values
3889
+ * @param {Event=} event
3813
3890
  * @returns {Object}
3814
3891
  */
3815
- function getValuesForElement(elt, attr, evalAsDefault, values) {
3892
+ function getValuesForElement(elt, attr, evalAsDefault, values, event) {
3816
3893
  if (values == null) {
3817
3894
  values = {}
3818
3895
  }
@@ -3838,7 +3915,13 @@ var htmx = (function() {
3838
3915
  }
3839
3916
  let varsValues
3840
3917
  if (evaluateValue) {
3841
- varsValues = maybeEval(elt, function() { return Function('return (' + str + ')')() }, {})
3918
+ varsValues = maybeEval(elt, function() {
3919
+ if (event) {
3920
+ return Function('event', 'return (' + str + ')').call(elt, event)
3921
+ } else { // allow window.event to be accessible
3922
+ return Function('return (' + str + ')').call(elt)
3923
+ }
3924
+ }, {})
3842
3925
  } else {
3843
3926
  varsValues = parseJSON(str)
3844
3927
  }
@@ -3850,7 +3933,7 @@ var htmx = (function() {
3850
3933
  }
3851
3934
  }
3852
3935
  }
3853
- return getValuesForElement(asElement(parentElt(elt)), attr, evalAsDefault, values)
3936
+ return getValuesForElement(asElement(parentElt(elt)), attr, evalAsDefault, values, event)
3854
3937
  }
3855
3938
 
3856
3939
  /**
@@ -3870,28 +3953,31 @@ var htmx = (function() {
3870
3953
 
3871
3954
  /**
3872
3955
  * @param {Element} elt
3873
- * @param {*?} expressionVars
3956
+ * @param {Event=} event
3957
+ * @param {*?=} expressionVars
3874
3958
  * @returns
3875
3959
  */
3876
- function getHXVarsForElement(elt, expressionVars) {
3877
- return getValuesForElement(elt, 'hx-vars', true, expressionVars)
3960
+ function getHXVarsForElement(elt, event, expressionVars) {
3961
+ return getValuesForElement(elt, 'hx-vars', true, expressionVars, event)
3878
3962
  }
3879
3963
 
3880
3964
  /**
3881
3965
  * @param {Element} elt
3882
- * @param {*?} expressionVars
3966
+ * @param {Event=} event
3967
+ * @param {*?=} expressionVars
3883
3968
  * @returns
3884
3969
  */
3885
- function getHXValsForElement(elt, expressionVars) {
3886
- return getValuesForElement(elt, 'hx-vals', false, expressionVars)
3970
+ function getHXValsForElement(elt, event, expressionVars) {
3971
+ return getValuesForElement(elt, 'hx-vals', false, expressionVars, event)
3887
3972
  }
3888
3973
 
3889
3974
  /**
3890
3975
  * @param {Element} elt
3976
+ * @param {Event=} event
3891
3977
  * @returns {FormData}
3892
3978
  */
3893
- function getExpressionVars(elt) {
3894
- return mergeObjects(getHXVarsForElement(elt), getHXValsForElement(elt))
3979
+ function getExpressionVars(elt, event) {
3980
+ return mergeObjects(getHXVarsForElement(elt, event), getHXValsForElement(elt, event))
3895
3981
  }
3896
3982
 
3897
3983
  /**
@@ -3916,8 +4002,7 @@ var htmx = (function() {
3916
4002
  * @return {string}
3917
4003
  */
3918
4004
  function getPathFromResponse(xhr) {
3919
- // NB: IE11 does not support this stuff
3920
- if (xhr.responseURL && typeof (URL) !== 'undefined') {
4005
+ if (xhr.responseURL) {
3921
4006
  try {
3922
4007
  const url = new URL(xhr.responseURL)
3923
4008
  return url.pathname + url.search
@@ -3999,17 +4084,9 @@ var htmx = (function() {
3999
4084
  * @return {boolean}
4000
4085
  */
4001
4086
  function verifyPath(elt, path, requestConfig) {
4002
- let sameHost
4003
- let url
4004
- if (typeof URL === 'function') {
4005
- url = new URL(path, document.location.href)
4006
- const origin = document.location.origin
4007
- sameHost = origin === url.origin
4008
- } else {
4009
- // IE11 doesn't support URL
4010
- url = path
4011
- sameHost = startsWith(path, document.location.origin)
4012
- }
4087
+ const url = new URL(path, location.protocol !== 'about:' ? location.href : window.origin)
4088
+ const origin = location.protocol !== 'about:' ? location.origin : window.origin
4089
+ const sameHost = origin === url.origin
4013
4090
 
4014
4091
  if (htmx.config.selfRequestsOnly) {
4015
4092
  if (!sameHost) {
@@ -4110,8 +4187,6 @@ var htmx = (function() {
4110
4187
  return function() {
4111
4188
  return formData[name].apply(formData, arguments)
4112
4189
  }
4113
- } else {
4114
- return target[name]
4115
4190
  }
4116
4191
  }
4117
4192
  const array = formData.getAll(name)
@@ -4186,7 +4261,7 @@ var htmx = (function() {
4186
4261
  }
4187
4262
  const target = etc.targetOverride || asElement(getTarget(elt))
4188
4263
  if (target == null || target == DUMMY_ELT) {
4189
- triggerErrorEvent(elt, 'htmx:targetError', { target: getAttributeValue(elt, 'hx-target') })
4264
+ triggerErrorEvent(elt, 'htmx:targetError', { target: getClosestAttributeValue(elt, 'hx-target') })
4190
4265
  maybeCall(reject)
4191
4266
  return promise
4192
4267
  }
@@ -4202,9 +4277,11 @@ var htmx = (function() {
4202
4277
 
4203
4278
  const buttonVerb = getRawAttribute(submitter, 'formmethod')
4204
4279
  if (buttonVerb != null) {
4205
- // ignore buttons with formmethod="dialog"
4206
- if (buttonVerb.toLowerCase() !== 'dialog') {
4280
+ if (VERBS.includes(buttonVerb.toLowerCase())) {
4207
4281
  verb = (/** @type HttpVerb */(buttonVerb))
4282
+ } else {
4283
+ maybeCall(resolve)
4284
+ return promise
4208
4285
  }
4209
4286
  }
4210
4287
  }
@@ -4339,7 +4416,7 @@ var htmx = (function() {
4339
4416
  if (etc.values) {
4340
4417
  overrideFormData(rawFormData, formDataFromObject(etc.values))
4341
4418
  }
4342
- const expressionVars = formDataFromObject(getExpressionVars(elt))
4419
+ const expressionVars = formDataFromObject(getExpressionVars(elt, event))
4343
4420
  const allFormData = overrideFormData(rawFormData, expressionVars)
4344
4421
  let filteredFormData = filterValues(allFormData, elt)
4345
4422
 
@@ -4349,7 +4426,7 @@ var htmx = (function() {
4349
4426
 
4350
4427
  // behavior of anchors w/ empty href is to use the current URL
4351
4428
  if (path == null || path === '') {
4352
- path = getDocument().location.href
4429
+ path = location.href
4353
4430
  }
4354
4431
 
4355
4432
  /**
@@ -4373,6 +4450,7 @@ var htmx = (function() {
4373
4450
  unfilteredFormData: allFormData,
4374
4451
  unfilteredParameters: formDataProxy(allFormData),
4375
4452
  headers,
4453
+ elt,
4376
4454
  target,
4377
4455
  verb,
4378
4456
  errors,
@@ -4427,6 +4505,7 @@ var htmx = (function() {
4427
4505
  if (!verifyPath(elt, finalPath, requestConfig)) {
4428
4506
  triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig)
4429
4507
  maybeCall(reject)
4508
+ endRequestLock()
4430
4509
  return promise
4431
4510
  }
4432
4511
 
@@ -4489,10 +4568,11 @@ var htmx = (function() {
4489
4568
  }
4490
4569
  }
4491
4570
  maybeCall(resolve)
4492
- endRequestLock()
4493
4571
  } catch (e) {
4494
4572
  triggerErrorEvent(elt, 'htmx:onLoadError', mergeObjects({ error: e }, responseInfo))
4495
4573
  throw e
4574
+ } finally {
4575
+ endRequestLock()
4496
4576
  }
4497
4577
  }
4498
4578
  xhr.onerror = function() {
@@ -4667,13 +4747,31 @@ var htmx = (function() {
4667
4747
  if (title) {
4668
4748
  const titleElt = find('title')
4669
4749
  if (titleElt) {
4670
- titleElt.innerHTML = title
4750
+ titleElt.textContent = title
4671
4751
  } else {
4672
4752
  window.document.title = title
4673
4753
  }
4674
4754
  }
4675
4755
  }
4676
4756
 
4757
+ /**
4758
+ * Resove the Retarget selector and throw if not found
4759
+ * @param {Element} elt
4760
+ * @param {String} target
4761
+ * @returns {Element}
4762
+ */
4763
+ function resolveRetarget(elt, target) {
4764
+ if (target === 'this') {
4765
+ return elt
4766
+ }
4767
+ const resolvedTarget = asElement(querySelectorExt(elt, target))
4768
+ if (resolvedTarget == null) {
4769
+ triggerErrorEvent(elt, 'htmx:targetError', { target })
4770
+ throw new Error(`Invalid re-target ${target}`)
4771
+ }
4772
+ return resolvedTarget
4773
+ }
4774
+
4677
4775
  /**
4678
4776
  * @param {Element} elt
4679
4777
  * @param {HtmxResponseInfo} responseInfo
@@ -4711,25 +4809,17 @@ var htmx = (function() {
4711
4809
 
4712
4810
  if (hasHeader(xhr, /HX-Redirect:/i)) {
4713
4811
  responseInfo.keepIndicators = true
4714
- location.href = xhr.getResponseHeader('HX-Redirect')
4715
- shouldRefresh && location.reload()
4812
+ htmx.location.href = xhr.getResponseHeader('HX-Redirect')
4813
+ shouldRefresh && htmx.location.reload()
4716
4814
  return
4717
4815
  }
4718
4816
 
4719
4817
  if (shouldRefresh) {
4720
4818
  responseInfo.keepIndicators = true
4721
- location.reload()
4819
+ htmx.location.reload()
4722
4820
  return
4723
4821
  }
4724
4822
 
4725
- if (hasHeader(xhr, /HX-Retarget:/i)) {
4726
- if (xhr.getResponseHeader('HX-Retarget') === 'this') {
4727
- responseInfo.target = elt
4728
- } else {
4729
- responseInfo.target = asElement(querySelectorExt(elt, xhr.getResponseHeader('HX-Retarget')))
4730
- }
4731
- }
4732
-
4733
4823
  const historyUpdate = determineHistoryUpdates(elt, responseInfo)
4734
4824
 
4735
4825
  const responseHandling = resolveResponseHandling(xhr)
@@ -4738,7 +4828,7 @@ var htmx = (function() {
4738
4828
  let ignoreTitle = htmx.config.ignoreTitle || responseHandling.ignoreTitle
4739
4829
  let selectOverride = responseHandling.select
4740
4830
  if (responseHandling.target) {
4741
- responseInfo.target = asElement(querySelectorExt(elt, responseHandling.target))
4831
+ responseInfo.target = resolveRetarget(elt, responseHandling.target)
4742
4832
  }
4743
4833
  var swapOverride = etc.swapOverride
4744
4834
  if (swapOverride == null && responseHandling.swapOverride) {
@@ -4747,12 +4837,9 @@ var htmx = (function() {
4747
4837
 
4748
4838
  // response headers override response handling config
4749
4839
  if (hasHeader(xhr, /HX-Retarget:/i)) {
4750
- if (xhr.getResponseHeader('HX-Retarget') === 'this') {
4751
- responseInfo.target = elt
4752
- } else {
4753
- responseInfo.target = asElement(querySelectorExt(elt, xhr.getResponseHeader('HX-Retarget')))
4754
- }
4840
+ responseInfo.target = resolveRetarget(elt, xhr.getResponseHeader('HX-Retarget'))
4755
4841
  }
4842
+
4756
4843
  if (hasHeader(xhr, /HX-Reswap:/i)) {
4757
4844
  swapOverride = xhr.getResponseHeader('HX-Reswap')
4758
4845
  }
@@ -4805,10 +4892,6 @@ var htmx = (function() {
4805
4892
 
4806
4893
  target.classList.add(htmx.config.swappingClass)
4807
4894
 
4808
- // optional transition API promise callbacks
4809
- let settleResolve = null
4810
- let settleReject = null
4811
-
4812
4895
  if (responseInfoSelect) {
4813
4896
  selectOverride = responseInfoSelect
4814
4897
  }
@@ -4820,8 +4903,31 @@ var htmx = (function() {
4820
4903
  const selectOOB = getClosestAttributeValue(elt, 'hx-select-oob')
4821
4904
  const select = getClosestAttributeValue(elt, 'hx-select')
4822
4905
 
4823
- let doSwap = function() {
4824
- try {
4906
+ swap(target, serverResponse, swapSpec, {
4907
+ select: selectOverride === 'unset' ? null : selectOverride || select,
4908
+ selectOOB,
4909
+ eventInfo: responseInfo,
4910
+ anchor: responseInfo.pathInfo.anchor,
4911
+ contextElement: elt,
4912
+ afterSwapCallback: function() {
4913
+ if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) {
4914
+ let finalElt = elt
4915
+ if (!bodyContains(elt)) {
4916
+ finalElt = getDocument().body
4917
+ }
4918
+ handleTriggerHeader(xhr, 'HX-Trigger-After-Swap', finalElt)
4919
+ }
4920
+ },
4921
+ afterSettleCallback: function() {
4922
+ if (hasHeader(xhr, /HX-Trigger-After-Settle:/i)) {
4923
+ let finalElt = elt
4924
+ if (!bodyContains(elt)) {
4925
+ finalElt = getDocument().body
4926
+ }
4927
+ handleTriggerHeader(xhr, 'HX-Trigger-After-Settle', finalElt)
4928
+ }
4929
+ },
4930
+ beforeSwapCallback: function() {
4825
4931
  // if we need to save history, do so, before swapping so that relative resources have the correct base URL
4826
4932
  if (historyUpdate.type) {
4827
4933
  triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo))
@@ -4833,70 +4939,8 @@ var htmx = (function() {
4833
4939
  triggerEvent(getDocument().body, 'htmx:replacedInHistory', { path: historyUpdate.path })
4834
4940
  }
4835
4941
  }
4836
-
4837
- swap(target, serverResponse, swapSpec, {
4838
- select: selectOverride || select,
4839
- selectOOB,
4840
- eventInfo: responseInfo,
4841
- anchor: responseInfo.pathInfo.anchor,
4842
- contextElement: elt,
4843
- afterSwapCallback: function() {
4844
- if (hasHeader(xhr, /HX-Trigger-After-Swap:/i)) {
4845
- let finalElt = elt
4846
- if (!bodyContains(elt)) {
4847
- finalElt = getDocument().body
4848
- }
4849
- handleTriggerHeader(xhr, 'HX-Trigger-After-Swap', finalElt)
4850
- }
4851
- },
4852
- afterSettleCallback: function() {
4853
- if (hasHeader(xhr, /HX-Trigger-After-Settle:/i)) {
4854
- let finalElt = elt
4855
- if (!bodyContains(elt)) {
4856
- finalElt = getDocument().body
4857
- }
4858
- handleTriggerHeader(xhr, 'HX-Trigger-After-Settle', finalElt)
4859
- }
4860
- maybeCall(settleResolve)
4861
- }
4862
- })
4863
- } catch (e) {
4864
- triggerErrorEvent(elt, 'htmx:swapError', responseInfo)
4865
- maybeCall(settleReject)
4866
- throw e
4867
4942
  }
4868
- }
4869
-
4870
- let shouldTransition = htmx.config.globalViewTransitions
4871
- if (swapSpec.hasOwnProperty('transition')) {
4872
- shouldTransition = swapSpec.transition
4873
- }
4874
-
4875
- if (shouldTransition &&
4876
- triggerEvent(elt, 'htmx:beforeTransition', responseInfo) &&
4877
- typeof Promise !== 'undefined' &&
4878
- // @ts-ignore experimental feature atm
4879
- document.startViewTransition) {
4880
- const settlePromise = new Promise(function(_resolve, _reject) {
4881
- settleResolve = _resolve
4882
- settleReject = _reject
4883
- })
4884
- // wrap the original doSwap() in a call to startViewTransition()
4885
- const innerDoSwap = doSwap
4886
- doSwap = function() {
4887
- // @ts-ignore experimental feature atm
4888
- document.startViewTransition(function() {
4889
- innerDoSwap()
4890
- return settlePromise
4891
- })
4892
- }
4893
- }
4894
-
4895
- if (swapSpec.swapDelay > 0) {
4896
- getWindow().setTimeout(doSwap, swapSpec.swapDelay)
4897
- } else {
4898
- doSwap()
4899
- }
4943
+ })
4900
4944
  }
4901
4945
  if (isError) {
4902
4946
  triggerErrorEvent(elt, 'htmx:responseError', mergeObjects({ error: 'Response Status Error Code ' + xhr.status + ' from ' + responseInfo.pathInfo.requestPath }, responseInfo))
@@ -5097,6 +5141,9 @@ var htmx = (function() {
5097
5141
  * @property {Element} [contextElement]
5098
5142
  * @property {swapCallback} [afterSwapCallback]
5099
5143
  * @property {swapCallback} [afterSettleCallback]
5144
+ * @property {swapCallback} [beforeSwapCallback]
5145
+ * @property {string} [title]
5146
+ * @property {boolean} [historyRequest]
5100
5147
  */
5101
5148
 
5102
5149
  /**
@@ -5115,7 +5162,7 @@ var htmx = (function() {
5115
5162
  * @property {boolean} [transition]
5116
5163
  * @property {boolean} [ignoreTitle]
5117
5164
  * @property {string} [head]
5118
- * @property {'top' | 'bottom'} [scroll]
5165
+ * @property {'top' | 'bottom' | number } [scroll]
5119
5166
  * @property {string} [scrollTarget]
5120
5167
  * @property {string} [show]
5121
5168
  * @property {string} [showTarget]
@@ -5160,7 +5207,8 @@ var htmx = (function() {
5160
5207
  * @property {'true'} [HX-History-Restore-Request]
5161
5208
  */
5162
5209
 
5163
- /** @typedef HtmxAjaxHelperContext
5210
+ /**
5211
+ * @typedef HtmxAjaxHelperContext
5164
5212
  * @property {Element|string} [source]
5165
5213
  * @property {Event} [event]
5166
5214
  * @property {HtmxAjaxHandler} [handler]
@@ -5180,6 +5228,7 @@ var htmx = (function() {
5180
5228
  * @property {FormData} unfilteredFormData
5181
5229
  * @property {Object} unfilteredParameters unfilteredFormData proxy
5182
5230
  * @property {HtmxHeaderSpecification} headers
5231
+ * @property {Element} elt
5183
5232
  * @property {Element} target
5184
5233
  * @property {HttpVerb} verb
5185
5234
  * @property {HtmxElementValidationError[]} errors
@@ -5253,7 +5302,7 @@ var htmx = (function() {
5253
5302
  * @see https://github.com/bigskysoftware/htmx-extensions/blob/main/README.md
5254
5303
  * @typedef {Object} HtmxExtension
5255
5304
  * @property {(api: any) => void} init
5256
- * @property {(name: string, event: Event|CustomEvent) => boolean} onEvent
5305
+ * @property {(name: string, event: CustomEvent) => boolean} onEvent
5257
5306
  * @property {(text: string, xhr: XMLHttpRequest, elt: Element) => string} transformResponse
5258
5307
  * @property {(swapStyle: HtmxSwapStyle) => boolean} isInlineSwap
5259
5308
  * @property {(swapStyle: HtmxSwapStyle, target: Node, fragment: Node, settleInfo: HtmxSettleInfo) => boolean|Node[]} handleSwap