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/README.md +3 -5
- package/dist/ext/response-targets.js +1 -1
- package/dist/htmx.amd.js +485 -436
- package/dist/htmx.cjs.js +485 -436
- package/dist/htmx.esm.d.ts +8 -2
- package/dist/htmx.esm.js +485 -436
- package/dist/htmx.js +485 -436
- package/dist/htmx.min.js +1 -1
- package/dist/htmx.min.js.gz +0 -0
- package/editors/jetbrains/htmx.web-types.json +398 -85
- package/package.json +32 -15
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 **
|
|
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.
|
|
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
|
-
|
|
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:
|
|
823
|
+
const test = 'htmx:sessionStorageTest'
|
|
815
824
|
try {
|
|
816
|
-
|
|
817
|
-
|
|
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
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
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
|
|
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.
|
|
1161
|
+
item = closest(asElement(elt), normalizeSelector(selector.slice(8)))
|
|
1165
1162
|
} else if (selector.indexOf('find ') === 0) {
|
|
1166
|
-
item = find(asParentNode(elt), normalizeSelector(selector.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
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
|
|
1683
|
-
|
|
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 {
|
|
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
|
|
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
|
-
|
|
1723
|
-
target.remove()
|
|
1724
|
-
} else {
|
|
1725
|
-
target.parentNode.removeChild(target)
|
|
1726
|
-
}
|
|
1716
|
+
target.remove()
|
|
1727
1717
|
}
|
|
1728
1718
|
|
|
1729
1719
|
/**
|
|
1730
|
-
* @param {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
1888
|
-
|
|
1880
|
+
let doSwap = function() {
|
|
1881
|
+
maybeCall(swapOptions.beforeSwapCallback)
|
|
1889
1882
|
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
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
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
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
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
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
|
-
//
|
|
1941
|
-
if (
|
|
1942
|
-
|
|
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
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
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
|
-
|
|
2001
|
+
|
|
2002
|
+
updateScrollState(settleInfo.elts, swapSpec)
|
|
2003
|
+
maybeCall(swapOptions.afterSettleCallback)
|
|
2004
|
+
maybeCall(settleResolve)
|
|
1970
2005
|
}
|
|
1971
|
-
}
|
|
1972
2006
|
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
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
|
-
|
|
1985
|
-
|
|
1986
|
-
handleTitle(settleInfo.title)
|
|
2013
|
+
let shouldTransition = htmx.config.globalViewTransitions
|
|
2014
|
+
if (swapSpec.hasOwnProperty('transition')) {
|
|
2015
|
+
shouldTransition = swapSpec.transition
|
|
1987
2016
|
}
|
|
1988
2017
|
|
|
1989
|
-
|
|
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
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
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
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
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 =
|
|
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 {
|
|
2424
|
+
* @param {Element} elt
|
|
2393
2425
|
* @returns {boolean}
|
|
2394
2426
|
*/
|
|
2395
|
-
function shouldCancel(evt,
|
|
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
|
-
|
|
2405
|
-
|
|
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
|
-
|
|
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 {
|
|
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 =
|
|
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 (
|
|
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 {
|
|
2685
|
+
* @param {Element} elt
|
|
2653
2686
|
* @param {Event} [evt]
|
|
2654
2687
|
*/
|
|
2655
2688
|
|
|
2656
2689
|
/**
|
|
2657
|
-
* @param {
|
|
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 =
|
|
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 =
|
|
2854
|
+
const elt = getTargetButton(evt.target)
|
|
2805
2855
|
if (!elt) {
|
|
2806
2856
|
return
|
|
2807
2857
|
}
|
|
2808
|
-
const 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
|
-
|
|
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
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
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
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
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
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
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
|
-
|
|
2924
|
-
|
|
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 (
|
|
2992
|
+
if (eltIsDisabled(elt)) {
|
|
2938
2993
|
cleanUpElement(elt)
|
|
2939
2994
|
return
|
|
2940
2995
|
}
|
|
2941
|
-
|
|
2942
|
-
|
|
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
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
3155
|
+
sessionStorage.removeItem('htmx-history-cache')
|
|
3086
3156
|
return
|
|
3087
3157
|
}
|
|
3088
3158
|
|
|
3089
3159
|
url = normalizePath(url)
|
|
3090
3160
|
|
|
3091
|
-
const historyCache = parseJSON(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
3175
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3230
|
-
|
|
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
|
-
|
|
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',
|
|
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
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
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
|
-
|
|
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
|
|
3267
|
-
const
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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':
|
|
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() {
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
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:
|
|
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
|
-
|
|
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 =
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
4824
|
-
|
|
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
|
-
/**
|
|
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:
|
|
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
|