htmx.org 1.9.5 → 1.9.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/htmx.js CHANGED
@@ -74,7 +74,8 @@ return (function () {
74
74
  getCacheBusterParam: false,
75
75
  globalViewTransitions: false,
76
76
  methodsThatUseUrlParams: ["get"],
77
- selfRequestsOnly: false
77
+ selfRequestsOnly: false,
78
+ scrollIntoViewOnBoost: true
78
79
  },
79
80
  parseInterval:parseInterval,
80
81
  _:internalEval,
@@ -86,7 +87,7 @@ return (function () {
86
87
  sock.binaryType = htmx.config.wsBinaryType;
87
88
  return sock;
88
89
  },
89
- version: "1.9.5"
90
+ version: "1.9.7"
90
91
  };
91
92
 
92
93
  /** @type {import("./htmx").HtmxInternalApi} */
@@ -306,6 +307,7 @@ return (function () {
306
307
  case "th":
307
308
  return parseHTML("<table><tbody><tr>" + resp + "</tr></tbody></table>", 3);
308
309
  case "script":
310
+ case "style":
309
311
  return parseHTML("<div>" + resp + "</div>", 1);
310
312
  default:
311
313
  return parseHTML(resp, 0);
@@ -573,9 +575,17 @@ return (function () {
573
575
  }
574
576
  }
575
577
 
578
+ function startsWith(str, prefix) {
579
+ return str.substring(0, prefix.length) === prefix
580
+ }
581
+
582
+ function endsWith(str, suffix) {
583
+ return str.substring(str.length - suffix.length) === suffix
584
+ }
585
+
576
586
  function normalizeSelector(selector) {
577
587
  var trimmedSelector = selector.trim();
578
- if (trimmedSelector.startsWith("<") && trimmedSelector.endsWith("/>")) {
588
+ if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) {
579
589
  return trimmedSelector.substring(1, trimmedSelector.length - 2);
580
590
  } else {
581
591
  return trimmedSelector;
@@ -587,8 +597,12 @@ return (function () {
587
597
  return [closest(elt, normalizeSelector(selector.substr(8)))];
588
598
  } else if (selector.indexOf("find ") === 0) {
589
599
  return [find(elt, normalizeSelector(selector.substr(5)))];
600
+ } else if (selector === "next") {
601
+ return [elt.nextElementSibling]
590
602
  } else if (selector.indexOf("next ") === 0) {
591
603
  return [scanForwardQuery(elt, normalizeSelector(selector.substr(5)))];
604
+ } else if (selector === "previous") {
605
+ return [elt.previousElementSibling]
592
606
  } else if (selector.indexOf("previous ") === 0) {
593
607
  return [scanBackwardsQuery(elt, normalizeSelector(selector.substr(9)))];
594
608
  } else if (selector === 'document') {
@@ -1270,12 +1284,14 @@ return (function () {
1270
1284
  var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA);
1271
1285
  if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") {
1272
1286
  tokens.shift();
1273
- from_arg +=
1274
- " " +
1275
- consumeUntil(
1276
- tokens,
1277
- WHITESPACE_OR_COMMA
1278
- );
1287
+ var selector = consumeUntil(
1288
+ tokens,
1289
+ WHITESPACE_OR_COMMA
1290
+ )
1291
+ // `next` and `previous` allow a selector-less syntax
1292
+ if (selector.length > 0) {
1293
+ from_arg += " " + selector;
1294
+ }
1279
1295
  }
1280
1296
  triggerSpec.from = from_arg;
1281
1297
  } else if (token === "target" && tokens[0] === ":") {
@@ -1348,7 +1364,7 @@ return (function () {
1348
1364
  var verb, path;
1349
1365
  if (elt.tagName === "A") {
1350
1366
  verb = "get";
1351
- path = elt.href; // DOM property gives the fully resolved href of a relative link
1367
+ path = getRawAttribute(elt, 'href')
1352
1368
  } else {
1353
1369
  var rawAttribute = getRawAttribute(elt, "method");
1354
1370
  verb = rawAttribute ? rawAttribute.toLowerCase() : "get";
@@ -1864,12 +1880,25 @@ return (function () {
1864
1880
  }
1865
1881
 
1866
1882
  function findHxOnWildcardElements(elt) {
1867
- if (!document.evaluate) return []
1883
+ var node = null
1884
+ var elements = []
1885
+
1886
+ if (document.evaluate) {
1887
+ var iter = document.evaluate('//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") ]]', elt)
1888
+ while (node = iter.iterateNext()) elements.push(node)
1889
+ } else {
1890
+ var allElements = document.getElementsByTagName("*")
1891
+ for (var i = 0; i < allElements.length; i++) {
1892
+ var attributes = allElements[i].attributes
1893
+ for (var j = 0; j < attributes.length; j++) {
1894
+ var attrName = attributes[j].name
1895
+ if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:")) {
1896
+ elements.push(allElements[i])
1897
+ }
1898
+ }
1899
+ }
1900
+ }
1868
1901
 
1869
- let node = null
1870
- const elements = []
1871
- const iter = document.evaluate('//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") ]]', elt)
1872
- while (node = iter.iterateNext()) elements.push(node)
1873
1902
  return elements
1874
1903
  }
1875
1904
 
@@ -1884,32 +1913,39 @@ return (function () {
1884
1913
  }
1885
1914
  }
1886
1915
 
1887
- function initButtonTracking(elt) {
1888
- // Handle submit buttons/inputs that have the form attribute set
1889
- // see https://developer.mozilla.org/docs/Web/HTML/Element/button
1890
- var form = resolveTarget("#" + getRawAttribute(elt, "form")) || closest(elt, "form")
1891
- if (!form) {
1892
- return
1916
+ // Handle submit buttons/inputs that have the form attribute set
1917
+ // see https://developer.mozilla.org/docs/Web/HTML/Element/button
1918
+ function maybeSetLastButtonClicked(evt) {
1919
+ var elt = closest(evt.target, "button, input[type='submit']");
1920
+ var internalData = getRelatedFormData(evt)
1921
+ if (internalData) {
1922
+ internalData.lastButtonClicked = elt;
1893
1923
  }
1894
-
1895
- var maybeSetLastButtonClicked = function (evt) {
1896
- var elt = closest(evt.target, "button, input[type='submit']");
1897
- if (elt !== null) {
1898
- var internalData = getInternalData(form);
1899
- internalData.lastButtonClicked = elt;
1900
- }
1901
- };
1902
-
1924
+ };
1925
+ function maybeUnsetLastButtonClicked(evt){
1926
+ var internalData = getRelatedFormData(evt)
1927
+ if (internalData) {
1928
+ internalData.lastButtonClicked = null;
1929
+ }
1930
+ }
1931
+ function getRelatedFormData(evt) {
1932
+ var elt = closest(evt.target, "button, input[type='submit']");
1933
+ if (!elt) {
1934
+ return;
1935
+ }
1936
+ var form = resolveTarget('#' + getRawAttribute(elt, 'form')) || closest(elt, 'form');
1937
+ if (!form) {
1938
+ return;
1939
+ }
1940
+ return getInternalData(form);
1941
+ }
1942
+ function initButtonTracking(elt) {
1903
1943
  // need to handle both click and focus in:
1904
1944
  // focusin - in case someone tabs in to a button and hits the space bar
1905
1945
  // click - on OSX buttons do not focus on click see https://bugs.webkit.org/show_bug.cgi?id=13724
1906
-
1907
1946
  elt.addEventListener('click', maybeSetLastButtonClicked)
1908
1947
  elt.addEventListener('focusin', maybeSetLastButtonClicked)
1909
- elt.addEventListener('focusout', function(evt){
1910
- var internalData = getInternalData(form);
1911
- internalData.lastButtonClicked = null;
1912
- })
1948
+ elt.addEventListener('focusout', maybeUnsetLastButtonClicked)
1913
1949
  }
1914
1950
 
1915
1951
  function countCurlies(line) {
@@ -1928,7 +1964,9 @@ return (function () {
1928
1964
 
1929
1965
  function addHxOnEventHandler(elt, eventName, code) {
1930
1966
  var nodeData = getInternalData(elt);
1931
- nodeData.onHandlers = [];
1967
+ if (!Array.isArray(nodeData.onHandlers)) {
1968
+ nodeData.onHandlers = [];
1969
+ }
1932
1970
  var func;
1933
1971
  var listener = function (e) {
1934
1972
  return maybeEval(elt, function() {
@@ -1951,7 +1989,7 @@ return (function () {
1951
1989
  var curlyCount = 0;
1952
1990
  while (lines.length > 0) {
1953
1991
  var line = lines.shift();
1954
- var match = line.match(/^\s*([a-zA-Z:\-]+:)(.*)/);
1992
+ var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);
1955
1993
  if (curlyCount === 0 && match) {
1956
1994
  line.split(":")
1957
1995
  currentEvent = match[1].slice(0, -1); // strip last colon
@@ -1975,10 +2013,10 @@ return (function () {
1975
2013
  for (var i = 0; i < elt.attributes.length; i++) {
1976
2014
  var name = elt.attributes[i].name
1977
2015
  var value = elt.attributes[i].value
1978
- if (name.startsWith("hx-on:") || name.startsWith("data-hx-on:")) {
2016
+ if (startsWith(name, "hx-on:") || startsWith(name, "data-hx-on:")) {
1979
2017
  let eventName = name.slice(name.indexOf(":") + 1)
1980
2018
  // if the eventName starts with a colon, prepend "htmx" for shorthand support
1981
- if (eventName.startsWith(":")) eventName = "htmx" + eventName
2019
+ if (startsWith(eventName, ":")) eventName = "htmx" + eventName
1982
2020
 
1983
2021
  addHxOnEventHandler(elt, eventName, value)
1984
2022
  }
@@ -2127,7 +2165,7 @@ return (function () {
2127
2165
  eventResult = eventResult && elt.dispatchEvent(kebabedEvent)
2128
2166
  }
2129
2167
  withExtensions(elt, function (extension) {
2130
- eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
2168
+ eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented)
2131
2169
  });
2132
2170
  return eventResult;
2133
2171
  }
@@ -2147,6 +2185,12 @@ return (function () {
2147
2185
  return;
2148
2186
  }
2149
2187
 
2188
+ if (htmx.config.historyCacheSize <= 0) {
2189
+ // make sure that an eventually already existing cache is purged
2190
+ localStorage.removeItem("htmx-history-cache");
2191
+ return;
2192
+ }
2193
+
2150
2194
  url = normalizePath(url);
2151
2195
 
2152
2196
  var historyCache = parseJSON(localStorage.getItem("htmx-history-cache")) || [];
@@ -2207,7 +2251,13 @@ return (function () {
2207
2251
  // so we can prevent privileged data entering the cache.
2208
2252
  // The page will still be reachable as a history entry, but htmx will fetch it
2209
2253
  // live from the server onpopstate rather than look in the localStorage cache
2210
- var disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]');
2254
+ var disableHistoryCache
2255
+ try {
2256
+ disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]')
2257
+ } catch (e) {
2258
+ // IE11: insensitive modifier not supported so fallback to case sensitive selector
2259
+ disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]')
2260
+ }
2211
2261
  if (!disableHistoryCache) {
2212
2262
  triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt});
2213
2263
  saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY);
@@ -2220,7 +2270,7 @@ return (function () {
2220
2270
  // remove the cache buster parameter, if any
2221
2271
  if (htmx.config.getCacheBusterParam) {
2222
2272
  path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '')
2223
- if (path.endsWith('&') || path.endsWith("?")) {
2273
+ if (endsWith(path, '&') || endsWith(path, "?")) {
2224
2274
  path = path.slice(0, -1);
2225
2275
  }
2226
2276
  }
@@ -2316,7 +2366,20 @@ return (function () {
2316
2366
  return indicators;
2317
2367
  }
2318
2368
 
2319
- function removeRequestIndicatorClasses(indicators) {
2369
+ function disableElements(elt) {
2370
+ var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt');
2371
+ if (disabledElts == null) {
2372
+ disabledElts = [];
2373
+ }
2374
+ forEach(disabledElts, function (disabledElement) {
2375
+ var internalData = getInternalData(disabledElement);
2376
+ internalData.requestCount = (internalData.requestCount || 0) + 1;
2377
+ disabledElement.setAttribute("disabled", "");
2378
+ });
2379
+ return disabledElts;
2380
+ }
2381
+
2382
+ function removeRequestIndicators(indicators, disabled) {
2320
2383
  forEach(indicators, function (ic) {
2321
2384
  var internalData = getInternalData(ic);
2322
2385
  internalData.requestCount = (internalData.requestCount || 0) - 1;
@@ -2324,6 +2387,13 @@ return (function () {
2324
2387
  ic.classList["remove"].call(ic.classList, htmx.config.requestClass);
2325
2388
  }
2326
2389
  });
2390
+ forEach(disabled, function (disabledElement) {
2391
+ var internalData = getInternalData(disabledElement);
2392
+ internalData.requestCount = (internalData.requestCount || 0) - 1;
2393
+ if (internalData.requestCount === 0) {
2394
+ disabledElement.removeAttribute('disabled');
2395
+ }
2396
+ });
2327
2397
  }
2328
2398
 
2329
2399
  //====================================================================
@@ -2386,7 +2456,7 @@ return (function () {
2386
2456
  if (shouldInclude(elt)) {
2387
2457
  var name = getRawAttribute(elt,"name");
2388
2458
  var value = elt.value;
2389
- if (elt.multiple) {
2459
+ if (elt.multiple && elt.tagName === "SELECT") {
2390
2460
  value = toArray(elt.querySelectorAll("option:checked")).map(function (e) { return e.value });
2391
2461
  }
2392
2462
  // include file inputs
@@ -2426,6 +2496,9 @@ return (function () {
2426
2496
  var formValues = {};
2427
2497
  var errors = [];
2428
2498
  var internalData = getInternalData(elt);
2499
+ if (internalData.lastButtonClicked && !bodyContains(internalData.lastButtonClicked)) {
2500
+ internalData.lastButtonClicked = null
2501
+ }
2429
2502
 
2430
2503
  // only validate when form is directly submitted and novalidate or formnovalidate are not set
2431
2504
  // or if the element has an explicit hx-validate="true" on it
@@ -2593,43 +2666,43 @@ return (function () {
2593
2666
  "swapDelay" : htmx.config.defaultSwapDelay,
2594
2667
  "settleDelay" : htmx.config.defaultSettleDelay
2595
2668
  }
2596
- if (getInternalData(elt).boosted && !isAnchorLink(elt)) {
2669
+ if (htmx.config.scrollIntoViewOnBoost && getInternalData(elt).boosted && !isAnchorLink(elt)) {
2597
2670
  swapSpec["show"] = "top"
2598
2671
  }
2599
2672
  if (swapInfo) {
2600
2673
  var split = splitOnWhitespace(swapInfo);
2601
2674
  if (split.length > 0) {
2602
- swapSpec["swapStyle"] = split[0];
2603
- for (var i = 1; i < split.length; i++) {
2604
- var modifier = split[i];
2605
- if (modifier.indexOf("swap:") === 0) {
2606
- swapSpec["swapDelay"] = parseInterval(modifier.substr(5));
2607
- }
2608
- if (modifier.indexOf("settle:") === 0) {
2609
- swapSpec["settleDelay"] = parseInterval(modifier.substr(7));
2610
- }
2611
- if (modifier.indexOf("transition:") === 0) {
2612
- swapSpec["transition"] = modifier.substr(11) === "true";
2613
- }
2614
- if (modifier.indexOf("scroll:") === 0) {
2615
- var scrollSpec = modifier.substr(7);
2675
+ for (var i = 0; i < split.length; i++) {
2676
+ var value = split[i];
2677
+ if (value.indexOf("swap:") === 0) {
2678
+ swapSpec["swapDelay"] = parseInterval(value.substr(5));
2679
+ } else if (value.indexOf("settle:") === 0) {
2680
+ swapSpec["settleDelay"] = parseInterval(value.substr(7));
2681
+ } else if (value.indexOf("transition:") === 0) {
2682
+ swapSpec["transition"] = value.substr(11) === "true";
2683
+ } else if (value.indexOf("ignoreTitle:") === 0) {
2684
+ swapSpec["ignoreTitle"] = value.substr(12) === "true";
2685
+ } else if (value.indexOf("scroll:") === 0) {
2686
+ var scrollSpec = value.substr(7);
2616
2687
  var splitSpec = scrollSpec.split(":");
2617
2688
  var scrollVal = splitSpec.pop();
2618
2689
  var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null;
2619
2690
  swapSpec["scroll"] = scrollVal;
2620
2691
  swapSpec["scrollTarget"] = selectorVal;
2621
- }
2622
- if (modifier.indexOf("show:") === 0) {
2623
- var showSpec = modifier.substr(5);
2692
+ } else if (value.indexOf("show:") === 0) {
2693
+ var showSpec = value.substr(5);
2624
2694
  var splitSpec = showSpec.split(":");
2625
2695
  var showVal = splitSpec.pop();
2626
2696
  var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null;
2627
2697
  swapSpec["show"] = showVal;
2628
2698
  swapSpec["showTarget"] = selectorVal;
2629
- }
2630
- if (modifier.indexOf("focus-scroll:") === 0) {
2631
- var focusScrollVal = modifier.substr("focus-scroll:".length);
2699
+ } else if (value.indexOf("focus-scroll:") === 0) {
2700
+ var focusScrollVal = value.substr("focus-scroll:".length);
2632
2701
  swapSpec["focusScroll"] = focusScrollVal == "true";
2702
+ } else if (i == 0) {
2703
+ swapSpec["swapStyle"] = value;
2704
+ } else {
2705
+ logError('Unknown modifier in hx-swap: ' + value);
2633
2706
  }
2634
2707
  }
2635
2708
  }
@@ -2853,9 +2926,18 @@ return (function () {
2853
2926
  }
2854
2927
 
2855
2928
  function verifyPath(elt, path, requestConfig) {
2856
- var url = new URL(path, document.location.href);
2857
- var origin = document.location.origin;
2858
- var sameHost = origin === url.origin;
2929
+ var sameHost
2930
+ var url
2931
+ if (typeof URL === "function") {
2932
+ url = new URL(path, document.location.href);
2933
+ var origin = document.location.origin;
2934
+ sameHost = origin === url.origin;
2935
+ } else {
2936
+ // IE11 doesn't support URL
2937
+ url = path
2938
+ sameHost = startsWith(path, document.location.origin)
2939
+ }
2940
+
2859
2941
  if (htmx.config.selfRequestsOnly) {
2860
2942
  if (!sameHost) {
2861
2943
  return false;
@@ -2880,27 +2962,49 @@ return (function () {
2880
2962
  var responseHandler = etc.handler || handleAjaxResponse;
2881
2963
 
2882
2964
  if (!bodyContains(elt)) {
2883
- return; // do not issue requests for elements removed from the DOM
2965
+ // do not issue requests for elements removed from the DOM
2966
+ maybeCall(resolve);
2967
+ return promise;
2884
2968
  }
2885
2969
  var target = etc.targetOverride || getTarget(elt);
2886
2970
  if (target == null || target == DUMMY_ELT) {
2887
2971
  triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")});
2888
- return;
2972
+ maybeCall(reject);
2973
+ return promise;
2889
2974
  }
2890
2975
 
2976
+ var eltData = getInternalData(elt);
2977
+ var submitter = eltData.lastButtonClicked;
2978
+
2979
+ if (submitter) {
2980
+ var buttonPath = getRawAttribute(submitter, "formaction");
2981
+ if (buttonPath != null) {
2982
+ path = buttonPath;
2983
+ }
2984
+
2985
+ var buttonVerb = getRawAttribute(submitter, "formmethod")
2986
+ if (buttonVerb != null) {
2987
+ // ignore buttons with formmethod="dialog"
2988
+ if (buttonVerb.toLowerCase() !== "dialog") {
2989
+ verb = buttonVerb;
2990
+ }
2991
+ }
2992
+ }
2993
+
2994
+ var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm");
2891
2995
  // allow event-based confirmation w/ a callback
2892
- if (!confirmed) {
2893
- var issueRequest = function() {
2894
- return issueAjaxRequest(verb, path, elt, event, etc, true);
2996
+ if (confirmed === undefined) {
2997
+ var issueRequest = function(skipConfirmation) {
2998
+ return issueAjaxRequest(verb, path, elt, event, etc, !!skipConfirmation);
2895
2999
  }
2896
- var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest};
3000
+ var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest, question: confirmQuestion};
2897
3001
  if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) {
2898
- return;
3002
+ maybeCall(resolve);
3003
+ return promise;
2899
3004
  }
2900
3005
  }
2901
3006
 
2902
3007
  var syncElt = elt;
2903
- var eltData = getInternalData(elt);
2904
3008
  var syncStrategy = getClosestAttributeValue(elt, "hx-sync");
2905
3009
  var queueStrategy = null;
2906
3010
  var abortable = false;
@@ -2916,10 +3020,12 @@ return (function () {
2916
3020
  syncStrategy = (syncStrings[1] || 'drop').trim();
2917
3021
  eltData = getInternalData(syncElt);
2918
3022
  if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) {
2919
- return;
3023
+ maybeCall(resolve);
3024
+ return promise;
2920
3025
  } else if (syncStrategy === "abort") {
2921
3026
  if (eltData.xhr) {
2922
- return;
3027
+ maybeCall(resolve);
3028
+ return promise;
2923
3029
  } else {
2924
3030
  abortable = true;
2925
3031
  }
@@ -2963,7 +3069,8 @@ return (function () {
2963
3069
  issueAjaxRequest(verb, path, elt, event, etc)
2964
3070
  });
2965
3071
  }
2966
- return;
3072
+ maybeCall(resolve);
3073
+ return promise;
2967
3074
  }
2968
3075
  }
2969
3076
 
@@ -2991,8 +3098,7 @@ return (function () {
2991
3098
  }
2992
3099
  }
2993
3100
 
2994
- var confirmQuestion = getClosestAttributeValue(elt, "hx-confirm");
2995
- if (confirmQuestion) {
3101
+ if (confirmQuestion && !confirmed) {
2996
3102
  if(!confirm(confirmQuestion)) {
2997
3103
  maybeCall(resolve);
2998
3104
  endRequestLock()
@@ -3094,7 +3200,8 @@ return (function () {
3094
3200
 
3095
3201
  if (!verifyPath(elt, finalPath, requestConfig)) {
3096
3202
  triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig)
3097
- return;
3203
+ maybeCall(reject);
3204
+ return promise;
3098
3205
  };
3099
3206
 
3100
3207
  xhr.open(verb.toUpperCase(), finalPath, true);
@@ -3128,7 +3235,7 @@ return (function () {
3128
3235
  var hierarchy = hierarchyForElt(elt);
3129
3236
  responseInfo.pathInfo.responsePath = getPathFromResponse(xhr);
3130
3237
  responseHandler(elt, responseInfo);
3131
- removeRequestIndicatorClasses(indicators);
3238
+ removeRequestIndicators(indicators, disableElts);
3132
3239
  triggerEvent(elt, 'htmx:afterRequest', responseInfo);
3133
3240
  triggerEvent(elt, 'htmx:afterOnLoad', responseInfo);
3134
3241
  // if the body no longer contains the element, trigger the event on the closest parent
@@ -3154,21 +3261,21 @@ return (function () {
3154
3261
  }
3155
3262
  }
3156
3263
  xhr.onerror = function () {
3157
- removeRequestIndicatorClasses(indicators);
3264
+ removeRequestIndicators(indicators, disableElts);
3158
3265
  triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo);
3159
3266
  triggerErrorEvent(elt, 'htmx:sendError', responseInfo);
3160
3267
  maybeCall(reject);
3161
3268
  endRequestLock();
3162
3269
  }
3163
3270
  xhr.onabort = function() {
3164
- removeRequestIndicatorClasses(indicators);
3271
+ removeRequestIndicators(indicators, disableElts);
3165
3272
  triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo);
3166
3273
  triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo);
3167
3274
  maybeCall(reject);
3168
3275
  endRequestLock();
3169
3276
  }
3170
3277
  xhr.ontimeout = function() {
3171
- removeRequestIndicatorClasses(indicators);
3278
+ removeRequestIndicators(indicators, disableElts);
3172
3279
  triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo);
3173
3280
  triggerErrorEvent(elt, 'htmx:timeout', responseInfo);
3174
3281
  maybeCall(reject);
@@ -3180,6 +3287,7 @@ return (function () {
3180
3287
  return promise
3181
3288
  }
3182
3289
  var indicators = addRequestIndicatorClasses(elt);
3290
+ var disableElts = disableElements(elt);
3183
3291
 
3184
3292
  forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) {
3185
3293
  forEach([xhr, xhr.upload], function (target) {
@@ -3284,6 +3392,7 @@ return (function () {
3284
3392
  var xhr = responseInfo.xhr;
3285
3393
  var target = responseInfo.target;
3286
3394
  var etc = responseInfo.etc;
3395
+ var requestConfig = responseInfo.requestConfig;
3287
3396
 
3288
3397
  if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return;
3289
3398
 
@@ -3307,16 +3416,17 @@ return (function () {
3307
3416
  return;
3308
3417
  }
3309
3418
 
3419
+ var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh");
3420
+
3310
3421
  if (hasHeader(xhr, /HX-Redirect:/i)) {
3311
3422
  location.href = xhr.getResponseHeader("HX-Redirect");
3423
+ shouldRefresh && location.reload();
3312
3424
  return;
3313
3425
  }
3314
3426
 
3315
- if (hasHeader(xhr,/HX-Refresh:/i)) {
3316
- if ("true" === xhr.getResponseHeader("HX-Refresh")) {
3317
- location.reload();
3318
- return;
3319
- }
3427
+ if (shouldRefresh) {
3428
+ location.reload();
3429
+ return;
3320
3430
  }
3321
3431
 
3322
3432
  if (hasHeader(xhr,/HX-Retarget:/i)) {
@@ -3332,12 +3442,14 @@ return (function () {
3332
3442
  var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204;
3333
3443
  var serverResponse = xhr.response;
3334
3444
  var isError = xhr.status >= 400;
3335
- var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo);
3445
+ var ignoreTitle = htmx.config.ignoreTitle
3446
+ var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError, ignoreTitle:ignoreTitle }, responseInfo);
3336
3447
  if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return;
3337
3448
 
3338
3449
  target = beforeSwapDetails.target; // allow re-targeting
3339
3450
  serverResponse = beforeSwapDetails.serverResponse; // allow updating content
3340
3451
  isError = beforeSwapDetails.isError; // allow updating error
3452
+ ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title
3341
3453
 
3342
3454
  responseInfo.target = target; // Make updated target available to response events
3343
3455
  responseInfo.failed = isError; // Make failed property available to response events
@@ -3363,6 +3475,10 @@ return (function () {
3363
3475
  }
3364
3476
  var swapSpec = getSwapSpecification(elt, swapOverride);
3365
3477
 
3478
+ if (swapSpec.hasOwnProperty('ignoreTitle')) {
3479
+ ignoreTitle = swapSpec.ignoreTitle;
3480
+ }
3481
+
3366
3482
  target.classList.add(htmx.config.swappingClass);
3367
3483
 
3368
3484
  // optional transition API promise callbacks
@@ -3441,6 +3557,7 @@ return (function () {
3441
3557
 
3442
3558
  // if we need to save history, do so
3443
3559
  if (historyUpdate.type) {
3560
+ triggerEvent(getDocument().body, 'htmx:beforeHistoryUpdate', mergeObjects({ history: historyUpdate }, responseInfo));
3444
3561
  if (historyUpdate.type === "push") {
3445
3562
  pushUrlIntoHistory(historyUpdate.path);
3446
3563
  triggerEvent(getDocument().body, 'htmx:pushedIntoHistory', {path: historyUpdate.path});
@@ -3450,13 +3567,13 @@ return (function () {
3450
3567
  }
3451
3568
  }
3452
3569
  if (responseInfo.pathInfo.anchor) {
3453
- var anchorTarget = find("#" + responseInfo.pathInfo.anchor);
3570
+ var anchorTarget = getDocument().getElementById(responseInfo.pathInfo.anchor);
3454
3571
  if(anchorTarget) {
3455
3572
  anchorTarget.scrollIntoView({block:'start', behavior: "auto"});
3456
3573
  }
3457
3574
  }
3458
3575
 
3459
- if(settleInfo.title) {
3576
+ if(settleInfo.title && !ignoreTitle) {
3460
3577
  var titleElt = find("title");
3461
3578
  if(titleElt) {
3462
3579
  titleElt.innerHTML = settleInfo.title;