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