htmx.org 1.9.9 → 1.9.11

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.11"
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,62 @@ 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 fragment = 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
- 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
+ var fragmentContent = fragment.querySelector('template').content;
316
+ if (htmx.config.allowScriptTags) {
317
+ // if there is a nonce set up, set it on the new script tags
318
+ forEach(fragmentContent.querySelectorAll("script"), function (script) {
319
+ if (htmx.config.inlineScriptNonce) {
320
+ script.nonce = htmx.config.inlineScriptNonce;
321
+ }
322
+ // mark as executed due to template insertion semantics on all browsers except firefox fml
323
+ script.htmxExecuted = navigator.userAgent.indexOf("Firefox") === -1;
324
+ })
325
+ } else {
326
+ forEach(fragmentContent.querySelectorAll("script"), function (script) {
327
+ // remove all script tags if scripts are disabled
328
+ removeElement(script);
329
+ })
315
330
  }
331
+ return fragmentContent;
332
+ }
333
+ switch (startTag) {
334
+ case "thead":
335
+ case "tbody":
336
+ case "tfoot":
337
+ case "colgroup":
338
+ case "caption":
339
+ return parseHTML("<table>" + content + "</table>", 1);
340
+ case "col":
341
+ return parseHTML("<table><colgroup>" + content + "</colgroup></table>", 2);
342
+ case "tr":
343
+ return parseHTML("<table><tbody>" + content + "</tbody></table>", 2);
344
+ case "td":
345
+ case "th":
346
+ return parseHTML("<table><tbody><tr>" + content + "</tr></tbody></table>", 3);
347
+ case "script":
348
+ case "style":
349
+ return parseHTML("<div>" + content + "</div>", 1);
350
+ default:
351
+ return parseHTML(content, 0);
316
352
  }
317
353
  }
318
354
 
@@ -450,7 +486,7 @@ return (function () {
450
486
  path = url.pathname + url.search;
451
487
  }
452
488
  // remove trailing slash, unless index page
453
- if (!path.match('^/$')) {
489
+ if (!(/^\/$/.test(path))) {
454
490
  path = path.replace(/\/+$/, '');
455
491
  }
456
492
  return path;
@@ -827,7 +863,7 @@ return (function () {
827
863
  var oobSelects = getClosestAttributeValue(elt, "hx-select-oob");
828
864
  if (oobSelects) {
829
865
  var oobSelectValues = oobSelects.split(",");
830
- for (let i = 0; i < oobSelectValues.length; i++) {
866
+ for (var i = 0; i < oobSelectValues.length; i++) {
831
867
  var oobSelectValue = oobSelectValues[i].split(":", 2);
832
868
  var id = oobSelectValue[0].trim();
833
869
  if (id.indexOf("#") === 0) {
@@ -934,7 +970,7 @@ return (function () {
934
970
  function deInitOnHandlers(elt) {
935
971
  var internalData = getInternalData(elt);
936
972
  if (internalData.onHandlers) {
937
- for (let i = 0; i < internalData.onHandlers.length; i++) {
973
+ for (var i = 0; i < internalData.onHandlers.length; i++) {
938
974
  const handlerInfo = internalData.onHandlers[i];
939
975
  elt.removeEventListener(handlerInfo.event, handlerInfo.listener);
940
976
  }
@@ -960,10 +996,8 @@ return (function () {
960
996
  }
961
997
  });
962
998
  }
963
- if (internalData.initHash) {
964
- internalData.initHash = null
965
- }
966
999
  deInitOnHandlers(element);
1000
+ forEach(Object.keys(internalData), function(key) { delete internalData[key] });
967
1001
  }
968
1002
 
969
1003
  function cleanUpElement(element) {
@@ -987,7 +1021,6 @@ return (function () {
987
1021
  } else {
988
1022
  newElt = eltBeforeNewContent.nextSibling;
989
1023
  }
990
- getInternalData(target).replacedWith = newElt; // tuck away so we can fire events on it later
991
1024
  settleInfo.elts = settleInfo.elts.filter(function(e) { return e != target });
992
1025
  while(newElt && newElt !== target) {
993
1026
  if (newElt.nodeType === Node.ELEMENT_NODE) {
@@ -1099,9 +1132,8 @@ return (function () {
1099
1132
 
1100
1133
  function findTitle(content) {
1101
1134
  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
-
1135
+ var contentWithSvgsRemoved = content.replace(SVG_TAGS_REGEX, '');
1136
+ var result = contentWithSvgsRemoved.match(TITLE_TAG_REGEX);
1105
1137
  if (result) {
1106
1138
  return result[2];
1107
1139
  }
@@ -1230,7 +1262,7 @@ return (function () {
1230
1262
 
1231
1263
  function consumeUntil(tokens, match) {
1232
1264
  var result = "";
1233
- while (tokens.length > 0 && !tokens[0].match(match)) {
1265
+ while (tokens.length > 0 && !match.test(tokens[0])) {
1234
1266
  result += tokens.shift();
1235
1267
  }
1236
1268
  return result;
@@ -1252,91 +1284,107 @@ return (function () {
1252
1284
 
1253
1285
  /**
1254
1286
  * @param {HTMLElement} elt
1287
+ * @param {string} explicitTrigger
1288
+ * @param {cache} cache for trigger specs
1255
1289
  * @returns {import("./htmx").HtmxTriggerSpecification[]}
1256
1290
  */
1257
- function getTriggerSpecs(elt) {
1258
- var explicitTrigger = getAttributeValue(elt, 'hx-trigger');
1291
+ function parseAndCacheTrigger(elt, explicitTrigger, cache) {
1259
1292
  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
- }
1293
+ var tokens = tokenizeString(explicitTrigger);
1294
+ do {
1295
+ consumeUntil(tokens, NOT_WHITESPACE);
1296
+ var initialLength = tokens.length;
1297
+ var trigger = consumeUntil(tokens, /[,\[\s]/);
1298
+ if (trigger !== "") {
1299
+ if (trigger === "every") {
1300
+ var every = {trigger: 'every'};
1301
+ consumeUntil(tokens, NOT_WHITESPACE);
1302
+ every.pollInterval = parseInterval(consumeUntil(tokens, /[,\[\s]/));
1303
+ consumeUntil(tokens, NOT_WHITESPACE);
1304
+ var eventFilter = maybeGenerateConditional(elt, tokens, "event");
1305
+ if (eventFilter) {
1306
+ every.eventFilter = eventFilter;
1307
+ }
1308
+ triggerSpecs.push(every);
1309
+ } else if (trigger.indexOf("sse:") === 0) {
1310
+ triggerSpecs.push({trigger: 'sse', sseEvent: trigger.substr(4)});
1311
+ } else {
1312
+ var triggerSpec = {trigger: trigger};
1313
+ var eventFilter = maybeGenerateConditional(elt, tokens, "event");
1314
+ if (eventFilter) {
1315
+ triggerSpec.eventFilter = eventFilter;
1316
+ }
1317
+ while (tokens.length > 0 && tokens[0] !== ",") {
1318
+ consumeUntil(tokens, NOT_WHITESPACE)
1319
+ var token = tokens.shift();
1320
+ if (token === "changed") {
1321
+ triggerSpec.changed = true;
1322
+ } else if (token === "once") {
1323
+ triggerSpec.once = true;
1324
+ } else if (token === "consume") {
1325
+ triggerSpec.consume = true;
1326
+ } else if (token === "delay" && tokens[0] === ":") {
1327
+ tokens.shift();
1328
+ triggerSpec.delay = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA));
1329
+ } else if (token === "from" && tokens[0] === ":") {
1330
+ tokens.shift();
1331
+ if (COMBINED_SELECTOR_START.test(tokens[0])) {
1332
+ var from_arg = consumeCSSSelector(tokens);
1333
+ } else {
1334
+ var from_arg = consumeUntil(tokens, WHITESPACE_OR_COMMA);
1335
+ if (from_arg === "closest" || from_arg === "find" || from_arg === "next" || from_arg === "previous") {
1336
+ tokens.shift();
1337
+ var selector = consumeCSSSelector(tokens);
1338
+ // `next` and `previous` allow a selector-less syntax
1339
+ if (selector.length > 0) {
1340
+ from_arg += " " + selector;
1310
1341
  }
1311
1342
  }
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
1343
  }
1344
+ triggerSpec.from = from_arg;
1345
+ } else if (token === "target" && tokens[0] === ":") {
1346
+ tokens.shift();
1347
+ triggerSpec.target = consumeCSSSelector(tokens);
1348
+ } else if (token === "throttle" && tokens[0] === ":") {
1349
+ tokens.shift();
1350
+ triggerSpec.throttle = parseInterval(consumeUntil(tokens, WHITESPACE_OR_COMMA));
1351
+ } else if (token === "queue" && tokens[0] === ":") {
1352
+ tokens.shift();
1353
+ triggerSpec.queue = consumeUntil(tokens, WHITESPACE_OR_COMMA);
1354
+ } else if (token === "root" && tokens[0] === ":") {
1355
+ tokens.shift();
1356
+ triggerSpec[token] = consumeCSSSelector(tokens);
1357
+ } else if (token === "threshold" && tokens[0] === ":") {
1358
+ tokens.shift();
1359
+ triggerSpec[token] = consumeUntil(tokens, WHITESPACE_OR_COMMA);
1360
+ } else {
1361
+ triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()});
1331
1362
  }
1332
- triggerSpecs.push(triggerSpec);
1333
1363
  }
1364
+ triggerSpecs.push(triggerSpec);
1334
1365
  }
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())
1366
+ }
1367
+ if (tokens.length === initialLength) {
1368
+ triggerErrorEvent(elt, "htmx:syntax:error", {token:tokens.shift()});
1369
+ }
1370
+ consumeUntil(tokens, NOT_WHITESPACE);
1371
+ } while (tokens[0] === "," && tokens.shift())
1372
+ if (cache) {
1373
+ cache[explicitTrigger] = triggerSpecs
1374
+ }
1375
+ return triggerSpecs
1376
+ }
1377
+
1378
+ /**
1379
+ * @param {HTMLElement} elt
1380
+ * @returns {import("./htmx").HtmxTriggerSpecification[]}
1381
+ */
1382
+ function getTriggerSpecs(elt) {
1383
+ var explicitTrigger = getAttributeValue(elt, 'hx-trigger');
1384
+ var triggerSpecs = [];
1385
+ if (explicitTrigger) {
1386
+ var cache = htmx.config.triggerSpecsCache
1387
+ triggerSpecs = (cache && cache[explicitTrigger]) || parseAndCacheTrigger(elt, explicitTrigger, cache)
1340
1388
  }
1341
1389
 
1342
1390
  if (triggerSpecs.length > 0) {
@@ -1508,14 +1556,14 @@ return (function () {
1508
1556
  return;
1509
1557
  }
1510
1558
 
1511
- if (triggerSpec.throttle) {
1559
+ if (triggerSpec.throttle > 0) {
1512
1560
  if (!elementData.throttle) {
1513
1561
  handler(elt, evt);
1514
1562
  elementData.throttle = setTimeout(function () {
1515
1563
  elementData.throttle = null;
1516
1564
  }, triggerSpec.throttle);
1517
1565
  }
1518
- } else if (triggerSpec.delay) {
1566
+ } else if (triggerSpec.delay > 0) {
1519
1567
  elementData.delayed = setTimeout(function() { handler(elt, evt) }, triggerSpec.delay);
1520
1568
  } else {
1521
1569
  triggerEvent(elt, 'htmx:trigger')
@@ -1792,7 +1840,7 @@ return (function () {
1792
1840
  handler(elt);
1793
1841
  }
1794
1842
  }
1795
- if (delay) {
1843
+ if (delay > 0) {
1796
1844
  setTimeout(load, delay);
1797
1845
  } else {
1798
1846
  load();
@@ -1851,7 +1899,7 @@ return (function () {
1851
1899
  if (!maybeFilterEvent(triggerSpec, elt, makeEvent("load", {elt: elt}))) {
1852
1900
  loadImmediately(elt, handler, nodeData, triggerSpec.delay);
1853
1901
  }
1854
- } else if (triggerSpec.pollInterval) {
1902
+ } else if (triggerSpec.pollInterval > 0) {
1855
1903
  nodeData.polling = true;
1856
1904
  processPolling(elt, handler, triggerSpec);
1857
1905
  } else {
@@ -1860,7 +1908,8 @@ return (function () {
1860
1908
  }
1861
1909
 
1862
1910
  function evalScript(script) {
1863
- if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) {
1911
+ if (!script.htmxExecuted && htmx.config.allowScriptTags &&
1912
+ (script.type === "text/javascript" || script.type === "module" || script.type === "") ) {
1864
1913
  var newScript = getDocument().createElement("script");
1865
1914
  forEach(script.attributes, function (attr) {
1866
1915
  newScript.setAttribute(attr.name, attr.value);
@@ -1894,26 +1943,35 @@ return (function () {
1894
1943
  });
1895
1944
  }
1896
1945
 
1897
- function hasChanceOfBeingBoosted() {
1898
- return document.querySelector("[hx-boost], [data-hx-boost]");
1946
+ function shouldProcessHxOn(elt) {
1947
+ var attributes = elt.attributes
1948
+ for (var j = 0; j < attributes.length; j++) {
1949
+ var attrName = attributes[j].name
1950
+ if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:") ||
1951
+ startsWith(attrName, "hx-on-") || startsWith(attrName, "data-hx-on-")) {
1952
+ return true
1953
+ }
1954
+ }
1955
+ return false
1899
1956
  }
1900
1957
 
1901
1958
  function findHxOnWildcardElements(elt) {
1902
1959
  var node = null
1903
1960
  var elements = []
1904
1961
 
1962
+ if (shouldProcessHxOn(elt)) {
1963
+ elements.push(elt)
1964
+ }
1965
+
1905
1966
  if (document.evaluate) {
1906
- var iter = document.evaluate('//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") ]]', elt)
1967
+ var iter = document.evaluate('.//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") or' +
1968
+ ' starts-with(name(), "hx-on-") or starts-with(name(), "data-hx-on-") ]]', elt)
1907
1969
  while (node = iter.iterateNext()) elements.push(node)
1908
1970
  } else {
1909
- var allElements = document.getElementsByTagName("*")
1971
+ var allElements = elt.getElementsByTagName("*")
1910
1972
  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
- }
1973
+ if (shouldProcessHxOn(allElements[i])) {
1974
+ elements.push(allElements[i])
1917
1975
  }
1918
1976
  }
1919
1977
  }
@@ -1923,8 +1981,8 @@ return (function () {
1923
1981
 
1924
1982
  function findElementsToProcess(elt) {
1925
1983
  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]," +
1984
+ var boostedSelector = ", [hx-boost] a, [data-hx-boost] a, a[hx-boost], a[data-hx-boost]";
1985
+ var results = elt.querySelectorAll(VERB_SELECTOR + boostedSelector + ", form, [type='submit'], [hx-sse], [data-hx-sse], [hx-ws]," +
1928
1986
  " [data-hx-ws], [hx-ext], [data-hx-ext], [hx-trigger], [data-hx-trigger], [hx-on], [data-hx-on]");
1929
1987
  return results;
1930
1988
  } else {
@@ -1970,7 +2028,7 @@ return (function () {
1970
2028
  function countCurlies(line) {
1971
2029
  var tokens = tokenizeString(line);
1972
2030
  var netCurlies = 0;
1973
- for (let i = 0; i < tokens.length; i++) {
2031
+ for (var i = 0; i < tokens.length; i++) {
1974
2032
  const token = tokens[i];
1975
2033
  if (token === "{") {
1976
2034
  netCurlies++;
@@ -2032,12 +2090,22 @@ return (function () {
2032
2090
  for (var i = 0; i < elt.attributes.length; i++) {
2033
2091
  var name = elt.attributes[i].name
2034
2092
  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
2093
+ if (startsWith(name, "hx-on") || startsWith(name, "data-hx-on")) {
2094
+ var afterOnPosition = name.indexOf("-on") + 3;
2095
+ var nextChar = name.slice(afterOnPosition, afterOnPosition + 1);
2096
+ if (nextChar === "-" || nextChar === ":") {
2097
+ var eventName = name.slice(afterOnPosition + 1);
2098
+ // if the eventName starts with a colon or dash, prepend "htmx" for shorthand support
2099
+ if (startsWith(eventName, ":")) {
2100
+ eventName = "htmx" + eventName
2101
+ } else if (startsWith(eventName, "-")) {
2102
+ eventName = "htmx:" + eventName.slice(1);
2103
+ } else if (startsWith(eventName, "htmx-")) {
2104
+ eventName = "htmx:" + eventName.slice(5);
2105
+ }
2039
2106
 
2040
- addHxOnEventHandler(elt, eventName, value)
2107
+ addHxOnEventHandler(elt, eventName, value)
2108
+ }
2041
2109
  }
2042
2110
  }
2043
2111
  }
@@ -2315,7 +2383,9 @@ return (function () {
2315
2383
  var details = {path: path, xhr:request};
2316
2384
  triggerEvent(getDocument().body, "htmx:historyCacheMiss", details);
2317
2385
  request.open('GET', path, true);
2386
+ request.setRequestHeader("HX-Request", "true");
2318
2387
  request.setRequestHeader("HX-History-Restore-Request", "true");
2388
+ request.setRequestHeader("HX-Current-URL", getDocument().location.href);
2319
2389
  request.onload = function () {
2320
2390
  if (this.status >= 200 && this.status < 400) {
2321
2391
  triggerEvent(getDocument().body, "htmx:historyCacheMissLoad", details);
@@ -2430,7 +2500,7 @@ return (function () {
2430
2500
  }
2431
2501
 
2432
2502
  function shouldInclude(elt) {
2433
- if(elt.name === "" || elt.name == null || elt.disabled) {
2503
+ if(elt.name === "" || elt.name == null || elt.disabled || closest(elt, "fieldset[disabled]")) {
2434
2504
  return false;
2435
2505
  }
2436
2506
  // ignore "submitter" types (see jQuery src/serialize.js)
@@ -2906,7 +2976,7 @@ return (function () {
2906
2976
  }
2907
2977
 
2908
2978
  function hasHeader(xhr, regexp) {
2909
- return xhr.getAllResponseHeaders().match(regexp);
2979
+ return regexp.test(xhr.getAllResponseHeaders())
2910
2980
  }
2911
2981
 
2912
2982
  function ajaxHelper(verb, path, context) {
@@ -3453,7 +3523,11 @@ return (function () {
3453
3523
  }
3454
3524
 
3455
3525
  if (hasHeader(xhr,/HX-Retarget:/i)) {
3456
- responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget"));
3526
+ if (xhr.getResponseHeader("HX-Retarget") === "this") {
3527
+ responseInfo.target = elt;
3528
+ } else {
3529
+ responseInfo.target = querySelectorExt(elt, xhr.getResponseHeader("HX-Retarget"));
3530
+ }
3457
3531
  }
3458
3532
 
3459
3533
  var historyUpdate = determineHistoryUpdates(elt, responseInfo);
@@ -3752,34 +3826,25 @@ return (function () {
3752
3826
  //====================================================================
3753
3827
  // Initialization
3754
3828
  //====================================================================
3829
+ var isReady = false
3830
+ getDocument().addEventListener('DOMContentLoaded', function() {
3831
+ isReady = true
3832
+ })
3833
+
3755
3834
  /**
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.
3835
+ * Execute a function now if DOMContentLoaded has fired, otherwise listen for it.
3836
+ *
3837
+ * This function uses isReady because there is no realiable way to ask the browswer whether
3838
+ * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded
3839
+ * firing and readystate=complete.
3759
3840
  */
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
- });
3841
+ function ready(fn) {
3842
+ // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by
3843
+ // some means other than the initial page load.
3844
+ if (isReady || getDocument().readyState === 'complete') {
3845
+ fn();
3846
+ } else {
3847
+ getDocument().addEventListener('DOMContentLoaded', fn);
3783
3848
  }
3784
3849
  }
3785
3850
 
@@ -3827,7 +3892,9 @@ return (function () {
3827
3892
  internalData.xhr.abort();
3828
3893
  }
3829
3894
  });
3830
- var originalPopstate = window.onpopstate;
3895
+ /** @type {(ev: PopStateEvent) => any} */
3896
+ const originalPopstate = window.onpopstate ? window.onpopstate.bind(window) : null;
3897
+ /** @type {(ev: PopStateEvent) => any} */
3831
3898
  window.onpopstate = function (event) {
3832
3899
  if (event.state && event.state.htmx) {
3833
3900
  restoreHistory();