htmx.org 1.9.9 → 1.9.10

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
@@ -76,7 +76,8 @@ return (function () {
76
76
  methodsThatUseUrlParams: ["get"],
77
77
  selfRequestsOnly: false,
78
78
  ignoreTitle: false,
79
- scrollIntoViewOnBoost: true
79
+ scrollIntoViewOnBoost: true,
80
+ triggerSpecsCache: null,
80
81
  },
81
82
  parseInterval:parseInterval,
82
83
  _:internalEval,
@@ -88,7 +89,7 @@ return (function () {
88
89
  sock.binaryType = htmx.config.wsBinaryType;
89
90
  return sock;
90
91
  },
91
- version: "1.9.9"
92
+ version: "1.9.10"
92
93
  };
93
94
 
94
95
  /** @type {import("./htmx").HtmxInternalApi} */
@@ -127,24 +128,40 @@ return (function () {
127
128
  return "[hx-" + verb + "], [data-hx-" + verb + "]"
128
129
  }).join(", ");
129
130
 
131
+ var HEAD_TAG_REGEX = makeTagRegEx('head'),
132
+ TITLE_TAG_REGEX = makeTagRegEx('title'),
133
+ SVG_TAGS_REGEX = makeTagRegEx('svg', true);
134
+
130
135
  //====================================================================
131
136
  // Utilities
132
137
  //====================================================================
133
138
 
139
+ /**
140
+ * @param {string} tag
141
+ * @param {boolean} global
142
+ * @returns {RegExp}
143
+ */
144
+ function makeTagRegEx(tag, global = false) {
145
+ return new RegExp(`<${tag}(\\s[^>]*>|>)([\\s\\S]*?)<\\/${tag}>`,
146
+ global ? 'gim' : 'im');
147
+ }
148
+
134
149
  function parseInterval(str) {
135
150
  if (str == undefined) {
136
- return undefined
151
+ return undefined;
137
152
  }
153
+
154
+ let interval = NaN;
138
155
  if (str.slice(-2) == "ms") {
139
- return parseFloat(str.slice(0,-2)) || undefined
140
- }
141
- if (str.slice(-1) == "s") {
142
- return (parseFloat(str.slice(0,-1)) * 1000) || undefined
143
- }
144
- if (str.slice(-1) == "m") {
145
- return (parseFloat(str.slice(0,-1)) * 1000 * 60) || undefined
156
+ interval = parseFloat(str.slice(0, -2));
157
+ } else if (str.slice(-1) == "s") {
158
+ interval = parseFloat(str.slice(0, -1)) * 1000;
159
+ } else if (str.slice(-1) == "m") {
160
+ interval = parseFloat(str.slice(0, -1)) * 1000 * 60;
161
+ } else {
162
+ interval = parseFloat(str);
146
163
  }
147
- return parseFloat(str) || undefined
164
+ return isNaN(interval) ? undefined : interval;
148
165
  }
149
166
 
150
167
  /**
@@ -276,43 +293,46 @@ return (function () {
276
293
  }
277
294
 
278
295
  function aFullPageResponse(resp) {
279
- return resp.match(/<body/);
296
+ return /<body/.test(resp)
280
297
  }
281
298
 
282
299
  /**
283
300
  *
284
- * @param {string} resp
301
+ * @param {string} response
285
302
  * @returns {Element}
286
303
  */
287
- function makeFragment(resp) {
288
- var partialResponse = !aFullPageResponse(resp);
304
+ function makeFragment(response) {
305
+ var partialResponse = !aFullPageResponse(response);
306
+ var startTag = getStartTag(response);
307
+ var content = response;
308
+ if (startTag === 'head') {
309
+ content = content.replace(HEAD_TAG_REGEX, '');
310
+ }
289
311
  if (htmx.config.useTemplateFragments && partialResponse) {
290
- var documentFragment = parseHTML("<body><template>" + resp + "</template></body>", 0);
312
+ var documentFragment = parseHTML("<body><template>" + content + "</template></body>", 0);
291
313
  // @ts-ignore type mismatch between DocumentFragment and Element.
292
314
  // TODO: Are these close enough for htmx to use interchangeably?
293
315
  return documentFragment.querySelector('template').content;
294
- } else {
295
- var startTag = getStartTag(resp);
296
- switch (startTag) {
297
- case "thead":
298
- case "tbody":
299
- case "tfoot":
300
- case "colgroup":
301
- case "caption":
302
- return parseHTML("<table>" + resp + "</table>", 1);
303
- case "col":
304
- return parseHTML("<table><colgroup>" + resp + "</colgroup></table>", 2);
305
- case "tr":
306
- return parseHTML("<table><tbody>" + resp + "</tbody></table>", 2);
307
- case "td":
308
- case "th":
309
- return parseHTML("<table><tbody><tr>" + resp + "</tr></tbody></table>", 3);
310
- case "script":
311
- case "style":
312
- return parseHTML("<div>" + resp + "</div>", 1);
313
- default:
314
- return parseHTML(resp, 0);
315
- }
316
+ }
317
+ switch (startTag) {
318
+ case "thead":
319
+ case "tbody":
320
+ case "tfoot":
321
+ case "colgroup":
322
+ case "caption":
323
+ return parseHTML("<table>" + content + "</table>", 1);
324
+ case "col":
325
+ return parseHTML("<table><colgroup>" + content + "</colgroup></table>", 2);
326
+ case "tr":
327
+ return parseHTML("<table><tbody>" + content + "</tbody></table>", 2);
328
+ case "td":
329
+ case "th":
330
+ return parseHTML("<table><tbody><tr>" + content + "</tr></tbody></table>", 3);
331
+ case "script":
332
+ case "style":
333
+ return parseHTML("<div>" + content + "</div>", 1);
334
+ default:
335
+ return parseHTML(content, 0);
316
336
  }
317
337
  }
318
338
 
@@ -450,7 +470,7 @@ return (function () {
450
470
  path = url.pathname + url.search;
451
471
  }
452
472
  // remove trailing slash, unless index page
453
- if (!path.match('^/$')) {
473
+ if (!(/^\/$/.test(path))) {
454
474
  path = path.replace(/\/+$/, '');
455
475
  }
456
476
  return path;
@@ -827,7 +847,7 @@ return (function () {
827
847
  var oobSelects = getClosestAttributeValue(elt, "hx-select-oob");
828
848
  if (oobSelects) {
829
849
  var oobSelectValues = oobSelects.split(",");
830
- for (let i = 0; i < oobSelectValues.length; i++) {
850
+ for (var i = 0; i < oobSelectValues.length; i++) {
831
851
  var oobSelectValue = oobSelectValues[i].split(":", 2);
832
852
  var id = oobSelectValue[0].trim();
833
853
  if (id.indexOf("#") === 0) {
@@ -934,7 +954,7 @@ return (function () {
934
954
  function deInitOnHandlers(elt) {
935
955
  var internalData = getInternalData(elt);
936
956
  if (internalData.onHandlers) {
937
- for (let i = 0; i < internalData.onHandlers.length; i++) {
957
+ for (var i = 0; i < internalData.onHandlers.length; i++) {
938
958
  const handlerInfo = internalData.onHandlers[i];
939
959
  elt.removeEventListener(handlerInfo.event, handlerInfo.listener);
940
960
  }
@@ -960,10 +980,8 @@ return (function () {
960
980
  }
961
981
  });
962
982
  }
963
- if (internalData.initHash) {
964
- internalData.initHash = null
965
- }
966
983
  deInitOnHandlers(element);
984
+ forEach(Object.keys(internalData), function(key) { delete internalData[key] });
967
985
  }
968
986
 
969
987
  function cleanUpElement(element) {
@@ -987,7 +1005,6 @@ return (function () {
987
1005
  } else {
988
1006
  newElt = eltBeforeNewContent.nextSibling;
989
1007
  }
990
- getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later
991
1008
  settleInfo.elts = settleInfo.elts.filter(function(e) { return e != target });
992
1009
  while(newElt && newElt !== target) {
993
1010
  if (newElt.nodeType === Node.ELEMENT_NODE) {
@@ -1099,9 +1116,8 @@ return (function () {
1099
1116
 
1100
1117
  function findTitle(content) {
1101
1118
  if (content.indexOf('<title') > -1) {
1102
- var contentWithSvgsRemoved = content.replace(/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim, '');
1103
- var result = contentWithSvgsRemoved.match(/<title(\s[^>]*>|>)([\s\S]*?)<\/title>/im);
1104
-
1119
+ var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, '');
1120
+ var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX);
1105
1121
  if (result) {
1106
1122
  return result[2];
1107
1123
  }
@@ -1230,7 +1246,7 @@ return (function () {
1230
1246
 
1231
1247
  function consumeUntil(tokens, match) {
1232
1248
  var result = "";
1233
- while (tokens.length > 0 && !tokens[0].match(match)) {
1249
+ while (tokens.length > 0 && !match.test(tokens[0])) {
1234
1250
  result += tokens.shift();
1235
1251
  }
1236
1252
  return result;
@@ -1252,91 +1268,107 @@ return (function () {
1252
1268
 
1253
1269
  /**
1254
1270
  * @param {HTMLElement} elt
1271
+ * @param {string} explicitTrigger
1272
+ * @param {cache} cache for trigger specs
1255
1273
  * @returns {import("./htmx").HtmxTriggerSpecification[]}
1256
1274
  */
1257
- function getTriggerSpecs(elt) {
1258
- var explicitTrigger = getAttributeValue(elt, 'hx-trigger');
1275
+ function parseAndCacheTrigger(elt, explicitTrigger, cache) {
1259
1276
  var triggerSpecs = [];
1260
- if (explicitTrigger) {
1261
- var tokens = tokenizeString(explicitTrigger);
1262
- do {
1263
- consumeUntil(tokens, NOT_WHITESPACE);
1264
- var initialLength = tokens.length;
1265
- var trigger = consumeUntil(tokens, /[,\[\s]/);
1266
- if (trigger !== "") {
1267
- if (trigger === "every") {
1268
- var every = {trigger: 'every'};
1269
- consumeUntil(tokens, NOT_WHITESPACE);
1270
- every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/));
1271
- consumeUntil(tokens, NOT_WHITESPACE);
1272
- var eventFilter = maybeGenerateConditional(elt, tokens, "event");
1273
- if (eventFilter) {
1274
- every.eventFilter = eventFilter;
1275
- }
1276
- triggerSpecs.push(every);
1277
- } else if (trigger.indexOf("sse:") === 0) {
1278
- triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)});
1279
- } else {
1280
- var triggerSpec = {trigger: trigger};
1281
- var eventFilter = maybeGenerateConditional(elt, tokens, "event");
1282
- if (eventFilter) {
1283
- triggerSpec.eventFilter = eventFilter;
1284
- }
1285
- while (tokens.length > 0 && tokens[0] !== ",") {
1286
- consumeUntil(tokens, NOT_WHITESPACE)
1287
- var token = tokens.shift();
1288
- if (token === "changed") {
1289
- triggerSpec.changed = true;
1290
- } else if (token === "once") {
1291
- triggerSpec.once = true;
1292
- } else if (token === "consume") {
1293
- triggerSpec.consume = true;
1294
- } else if (token === "delay" && tokens[0] === ":") {
1295
- tokens.shift();
1296
- triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA));
1297
- } else if (token === "from" && tokens[0] === ":") {
1298
- tokens.shift();
1299
- if (COMBINED_SELECTOR_START.test(tokens[0])) {
1300
- var from_arg = consumeCSSSelector(tokens);
1301
- } else {
1302
- var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA);
1303
- if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") {
1304
- tokens.shift();
1305
- var selector = consumeCSSSelector(tokens);
1306
- // `next` and `previous` allow a selector-less syntax
1307
- if (selector.length > 0) {
1308
- from_arg += " " + selector;
1309
- }
1277
+ var tokens = tokenizeString(explicitTrigger);
1278
+ do {
1279
+ consumeUntil(tokens, NOT_WHITESPACE);
1280
+ var initialLength = tokens.length;
1281
+ var trigger = consumeUntil(tokens, /[,\[\s]/);
1282
+ if (trigger !== "") {
1283
+ if (trigger === "every") {
1284
+ var every = {trigger: 'every'};
1285
+ consumeUntil(tokens, NOT_WHITESPACE);
1286
+ every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/));
1287
+ consumeUntil(tokens, NOT_WHITESPACE);
1288
+ var eventFilter = maybeGenerateConditional(elt, tokens, "event");
1289
+ if (eventFilter) {
1290
+ every.eventFilter = eventFilter;
1291
+ }
1292
+ triggerSpecs.push(every);
1293
+ } else if (trigger.indexOf("sse:") === 0) {
1294
+ triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)});
1295
+ } else {
1296
+ var triggerSpec = {trigger: trigger};
1297
+ var eventFilter = maybeGenerateConditional(elt, tokens, "event");
1298
+ if (eventFilter) {
1299
+ triggerSpec.eventFilter = eventFilter;
1300
+ }
1301
+ while (tokens.length > 0 && tokens[0] !== ",") {
1302
+ consumeUntil(tokens, NOT_WHITESPACE)
1303
+ var token = tokens.shift();
1304
+ if (token === "changed") {
1305
+ triggerSpec.changed = true;
1306
+ } else if (token === "once") {
1307
+ triggerSpec.once = true;
1308
+ } else if (token === "consume") {
1309
+ triggerSpec.consume = true;
1310
+ } else if (token === "delay" && tokens[0] === ":") {
1311
+ tokens.shift();
1312
+ triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA));
1313
+ } else if (token === "from" && tokens[0] === ":") {
1314
+ tokens.shift();
1315
+ if (COMBINED_SELECTOR_START.test(tokens[0])) {
1316
+ var from_arg = consumeCSSSelector(tokens);
1317
+ } else {
1318
+ var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA);
1319
+ if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") {
1320
+ tokens.shift();
1321
+ var selector = consumeCSSSelector(tokens);
1322
+ // `next` and `previous` allow a selector-less syntax
1323
+ if (selector.length > 0) {
1324
+ from_arg += " " + selector;
1310
1325
  }
1311
1326
  }
1312
- triggerSpec.from = from_arg;
1313
- } else if (token === "target" && tokens[0] === ":") {
1314
- tokens.shift();
1315
- triggerSpec.target = consumeCSSSelector(tokens);
1316
- } else if (token === "throttle" && tokens[0] === ":") {
1317
- tokens.shift();
1318
- triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA));
1319
- } else if (token === "queue" && tokens[0] === ":") {
1320
- tokens.shift();
1321
- triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA);
1322
- } else if (token === "root" && tokens[0] === ":") {
1323
- tokens.shift();
1324
- triggerSpec[token] = consumeCSSSelector(tokens);
1325
- } else if (token === "threshold" && tokens[0] === ":") {
1326
- tokens.shift();
1327
- triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA);
1328
- } else {
1329
- triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()});
1330
1327
  }
1328
+ triggerSpec.from = from_arg;
1329
+ } else if (token === "target" && tokens[0] === ":") {
1330
+ tokens.shift();
1331
+ triggerSpec.target = consumeCSSSelector(tokens);
1332
+ } else if (token === "throttle" && tokens[0] === ":") {
1333
+ tokens.shift();
1334
+ triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA));
1335
+ } else if (token === "queue" && tokens[0] === ":") {
1336
+ tokens.shift();
1337
+ triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA);
1338
+ } else if (token === "root" && tokens[0] === ":") {
1339
+ tokens.shift();
1340
+ triggerSpec[token] = consumeCSSSelector(tokens);
1341
+ } else if (token === "threshold" && tokens[0] === ":") {
1342
+ tokens.shift();
1343
+ triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA);
1344
+ } else {
1345
+ triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()});
1331
1346
  }
1332
- triggerSpecs.push(triggerSpec);
1333
1347
  }
1348
+ triggerSpecs.push(triggerSpec);
1334
1349
  }
1335
- if (tokens.length === initialLength) {
1336
- triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()});
1337
- }
1338
- consumeUntil(tokens, NOT_WHITESPACE);
1339
- } while (tokens[0] === "," && tokens.shift())
1350
+ }
1351
+ if (tokens.length === initialLength) {
1352
+ triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()});
1353
+ }
1354
+ consumeUntil(tokens, NOT_WHITESPACE);
1355
+ } while (tokens[0] === "," && tokens.shift())
1356
+ if (cache) {
1357
+ cache[explicitTrigger] = triggerSpecs
1358
+ }
1359
+ return triggerSpecs
1360
+ }
1361
+
1362
+ /**
1363
+ * @param {HTMLElement} elt
1364
+ * @returns {import("./htmx").HtmxTriggerSpecification[]}
1365
+ */
1366
+ function getTriggerSpecs(elt) {
1367
+ var explicitTrigger = getAttributeValue(elt, 'hx-trigger');
1368
+ var triggerSpecs = [];
1369
+ if (explicitTrigger) {
1370
+ var cache = htmx.config.triggerSpecsCache
1371
+ triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache)
1340
1372
  }
1341
1373
 
1342
1374
  if (triggerSpecs.length > 0) {
@@ -1508,14 +1540,14 @@ return (function () {
1508
1540
  return;
1509
1541
  }
1510
1542
 
1511
- if (triggerSpec.throttle) {
1543
+ if (triggerSpec.throttle > 0) {
1512
1544
  if (!elementData.throttle) {
1513
1545
  handler(elt, evt);
1514
1546
  elementData.throttle = setTimeout(function () {
1515
1547
  elementData.throttle = null;
1516
1548
  }, triggerSpec.throttle);
1517
1549
  }
1518
- } else if (triggerSpec.delay) {
1550
+ } else if (triggerSpec.delay > 0) {
1519
1551
  elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay);
1520
1552
  } else {
1521
1553
  triggerEvent(elt, 'htmx:trigger')
@@ -1792,7 +1824,7 @@ return (function () {
1792
1824
  handler(elt);
1793
1825
  }
1794
1826
  }
1795
- if (delay) {
1827
+ if (delay > 0) {
1796
1828
  setTimeout(load, delay);
1797
1829
  } else {
1798
1830
  load();
@@ -1851,7 +1883,7 @@ return (function () {
1851
1883
  if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) {
1852
1884
  loadImmediately(elt, handler, nodeData, triggerSpec.delay);
1853
1885
  }
1854
- } else if (triggerSpec.pollInterval) {
1886
+ } else if (triggerSpec.pollInterval > 0) {
1855
1887
  nodeData.polling = true;
1856
1888
  processPolling(elt, handler, triggerSpec);
1857
1889
  } else {
@@ -1894,26 +1926,35 @@ return (function () {
1894
1926
  });
1895
1927
  }
1896
1928
 
1897
- function hasChanceOfBeingBoosted() {
1898
- return document.querySelector("[hx-boost], [data-hx-boost]");
1929
+ function shouldProcessHxOn(elt) {
1930
+ var attributes = elt.attributes
1931
+ for (var j = 0; j < attributes.length; j++) {
1932
+ var attrName = attributes[j].name
1933
+ if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") ||
1934
+ startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) {
1935
+ return true
1936
+ }
1937
+ }
1938
+ return false
1899
1939
  }
1900
1940
 
1901
1941
  function findHxOnWildcardElements(elt) {
1902
1942
  var node = null
1903
1943
  var elements = []
1904
1944
 
1945
+ if (shouldProcessHxOn(elt)) {
1946
+ elements.push(elt)
1947
+ }
1948
+
1905
1949
  if (document.evaluate) {
1906
- var iter = document.evaluate('//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") ]]', elt)
1950
+ var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' +
1951
+ ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt)
1907
1952
  while (node = iter.iterateNext()) elements.push(node)
1908
1953
  } else {
1909
- var allElements = document.getElementsByTagName("*")
1954
+ var allElements = elt.getElementsByTagName("*")
1910
1955
  for (var i = 0; i < allElements.length; i++) {
1911
- var attributes = allElements[i].attributes
1912
- for (var j = 0; j < attributes.length; j++) {
1913
- var attrName = attributes[j].name
1914
- if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:")) {
1915
- elements.push(allElements[i])
1916
- }
1956
+ if (shouldProcessHxOn(allElements[i])) {
1957
+ elements.push(allElements[i])
1917
1958
  }
1918
1959
  }
1919
1960
  }
@@ -1923,8 +1964,8 @@ return (function () {
1923
1964
 
1924
1965
  function findElementsToProcess(elt) {
1925
1966
  if (elt.querySelectorAll) {
1926
- var boostedElts = hasChanceOfBeingBoosted() ? ", a" : "";
1927
- var results = elt.querySelectorAll(VERB_SELECTOR + boostedElts + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," +
1967
+ var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]";
1968
+ var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," +
1928
1969
  " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]");
1929
1970
  return results;
1930
1971
  } else {
@@ -1970,7 +2011,7 @@ return (function () {
1970
2011
  function countCurlies(line) {
1971
2012
  var tokens = tokenizeString(line);
1972
2013
  var netCurlies = 0;
1973
- for (let i = 0; i < tokens.length; i++) {
2014
+ for (var i = 0; i < tokens.length; i++) {
1974
2015
  const token = tokens[i];
1975
2016
  if (token === "{") {
1976
2017
  netCurlies++;
@@ -2032,12 +2073,22 @@ return (function () {
2032
2073
  for (var i = 0; i < elt.attributes.length; i++) {
2033
2074
  var name = elt.attributes[i].name
2034
2075
  var value = elt.attributes[i].value
2035
- if (startsWith(name, "hx-on:") || startsWith(name, "data-hx-on:")) {
2036
- let eventName = name.slice(name.indexOf(":") + 1)
2037
- // if the eventName starts with a colon, prepend "htmx" for shorthand support
2038
- if (startsWith(eventName, ":")) eventName = "htmx" + eventName
2076
+ if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) {
2077
+ var afterOnPosition = name.indexOf("-on") + 3;
2078
+ var nextChar = name.slice(afterOnPosition, afterOnPosition + 1);
2079
+ if (nextChar === "-" || nextChar === ":") {
2080
+ var eventName = name.slice(afterOnPosition + 1);
2081
+ // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support
2082
+ if (startsWith(eventName, ":")) {
2083
+ eventName = "htmx" + eventName
2084
+ } else if (startsWith(eventName, "-")) {
2085
+ eventName = "htmx:" + eventName.slice(1);
2086
+ } else if (startsWith(eventName, "htmx-")) {
2087
+ eventName = "htmx:" + eventName.slice(5);
2088
+ }
2039
2089
 
2040
- addHxOnEventHandler(elt, eventName, value)
2090
+ addHxOnEventHandler(elt, eventName, value)
2091
+ }
2041
2092
  }
2042
2093
  }
2043
2094
  }
@@ -2315,7 +2366,9 @@ return (function () {
2315
2366
  var details = {path: path, xhr:request};
2316
2367
  triggerEvent(getDocument().body, "htmx:historyCacheMiss", details);
2317
2368
  request.open('GET', path, true);
2369
+ request.setRequestHeader("HX-Request", "true");
2318
2370
  request.setRequestHeader("HX-History-Restore-Request", "true");
2371
+ request.setRequestHeader("HX-Current-URL", getDocument().location.href);
2319
2372
  request.onload = function () {
2320
2373
  if (this.status >= 200 && this.status < 400) {
2321
2374
  triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details);
@@ -2430,7 +2483,7 @@ return (function () {
2430
2483
  }
2431
2484
 
2432
2485
  function shouldInclude(elt) {
2433
- if(elt.name === "" || elt.name == null || elt.disabled) {
2486
+ if(elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) {
2434
2487
  return false;
2435
2488
  }
2436
2489
  // ignore "submitter" types (see jQuery src/serialize.js)
@@ -2906,7 +2959,7 @@ return (function () {
2906
2959
  }
2907
2960
 
2908
2961
  function hasHeader(xhr, regexp) {
2909
- return xhr.getAllResponseHeaders().match(regexp);
2962
+ return regexp.test(xhr.getAllResponseHeaders())
2910
2963
  }
2911
2964
 
2912
2965
  function ajaxHelper(verb, path, context) {
@@ -3453,7 +3506,11 @@ return (function () {
3453
3506
  }
3454
3507
 
3455
3508
  if (hasHeader(xhr,/HX-Retarget:/i)) {
3456
- responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget"));
3509
+ if (xhr.getResponseHeader("HX-Retarget") === "this") {
3510
+ responseInfo.target = elt;
3511
+ } else {
3512
+ responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget"));
3513
+ }
3457
3514
  }
3458
3515
 
3459
3516
  var historyUpdate = determineHistoryUpdates(elt, responseInfo);
@@ -3752,34 +3809,25 @@ return (function () {
3752
3809
  //====================================================================
3753
3810
  // Initialization
3754
3811
  //====================================================================
3812
+ var isReady = false
3813
+ getDocument().addEventListener('DOMContentLoaded', function() {
3814
+ isReady = true
3815
+ })
3816
+
3755
3817
  /**
3756
- * We want to initialize the page elements after DOMContentLoaded
3757
- * fires, but there isn't always a good way to tell whether
3758
- * it has already fired when we get here or not.
3818
+ * Execute a function now if DOMContentLoaded has fired, otherwise listen for it.
3819
+ *
3820
+ * This function uses isReady because there is no realiable way to ask the browswer whether
3821
+ * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded
3822
+ * firing and readystate=complete.
3759
3823
  */
3760
- function ready(functionToCall) {
3761
- // call the function exactly once no matter how many times this is called
3762
- var callReadyFunction = function() {
3763
- if (!functionToCall) return;
3764
- functionToCall();
3765
- functionToCall = null;
3766
- };
3767
-
3768
- if (getDocument().readyState === "complete") {
3769
- // DOMContentLoaded definitely fired, we can initialize the page
3770
- callReadyFunction();
3771
- }
3772
- else {
3773
- /* DOMContentLoaded *maybe* already fired, wait for
3774
- * the next DOMContentLoaded or readystatechange event
3775
- */
3776
- getDocument().addEventListener("DOMContentLoaded", function() {
3777
- callReadyFunction();
3778
- });
3779
- getDocument().addEventListener("readystatechange", function() {
3780
- if (getDocument().readyState !== "complete") return;
3781
- callReadyFunction();
3782
- });
3824
+ function ready(fn) {
3825
+ // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by
3826
+ // some means other than the initial page load.
3827
+ if (isReady || getDocument().readyState === 'complete') {
3828
+ fn();
3829
+ } else {
3830
+ getDocument().addEventListener('DOMContentLoaded', fn);
3783
3831
  }
3784
3832
  }
3785
3833
 
@@ -3827,7 +3875,9 @@ return (function () {
3827
3875
  internalData.xhr.abort();
3828
3876
  }
3829
3877
  });
3830
- var originalPopstate = window.onpopstate;
3878
+ /** @type {(ev: PopStateEvent) => any} */
3879
+ const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null;
3880
+ /** @type {(ev: PopStateEvent) => any} */
3831
3881
  window.onpopstate = function (event) {
3832
3882
  if (event.state && event.state.htmx) {
3833
3883
  restoreHistory();