phoenix_live_view 1.2.0-rc.2 → 1.2.0

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.
Files changed (27) hide show
  1. package/README.md +5 -5
  2. package/assets/js/phoenix_live_view/README.md +3 -0
  3. package/assets/js/phoenix_live_view/{aria.js → aria.ts} +18 -10
  4. package/assets/js/phoenix_live_view/{browser.js → browser.ts} +12 -8
  5. package/assets/js/phoenix_live_view/{dom.js → dom.ts} +107 -34
  6. package/assets/js/phoenix_live_view/{dom_patch.js → dom_patch.ts} +187 -124
  7. package/assets/js/phoenix_live_view/{dom_post_morph_restorer.js → dom_post_morph_restorer.ts} +17 -2
  8. package/assets/js/phoenix_live_view/{element_ref.js → element_ref.ts} +17 -11
  9. package/assets/js/phoenix_live_view/entry_uploader.js +4 -4
  10. package/assets/js/phoenix_live_view/{hooks.js → hooks.ts} +108 -91
  11. package/assets/js/phoenix_live_view/index.ts +14 -301
  12. package/assets/js/phoenix_live_view/js.js +2 -1
  13. package/assets/js/phoenix_live_view/js_commands.ts +12 -9
  14. package/assets/js/phoenix_live_view/{live_socket.js → live_socket.ts} +582 -114
  15. package/assets/js/phoenix_live_view/live_uploader.js +1 -1
  16. package/assets/js/phoenix_live_view/rendered.js +3 -0
  17. package/assets/js/phoenix_live_view/{utils.js → utils.ts} +35 -6
  18. package/assets/js/phoenix_live_view/{view.js → view.ts} +221 -110
  19. package/assets/js/phoenix_live_view/view_hook.ts +92 -32
  20. package/package.json +5 -2
  21. package/priv/static/phoenix_live_view.cjs.js +577 -314
  22. package/priv/static/phoenix_live_view.cjs.js.map +4 -4
  23. package/priv/static/phoenix_live_view.esm.js +577 -314
  24. package/priv/static/phoenix_live_view.esm.js.map +4 -4
  25. package/priv/static/phoenix_live_view.js +584 -314
  26. package/priv/static/phoenix_live_view.min.js +7 -7
  27. /package/assets/js/phoenix_live_view/{constants.js → constants.ts} +0 -0
@@ -19,7 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  // js/phoenix_live_view/index.ts
20
20
  var phoenix_live_view_exports = {};
21
21
  __export(phoenix_live_view_exports, {
22
- LiveSocket: () => LiveSocket2,
22
+ LiveSocket: () => LiveSocket,
23
23
  ViewHook: () => ViewHook,
24
24
  createHook: () => createHook,
25
25
  getFileURLForUpload: () => getFileURLForUpload,
@@ -27,7 +27,7 @@ __export(phoenix_live_view_exports, {
27
27
  });
28
28
  module.exports = __toCommonJS(phoenix_live_view_exports);
29
29
 
30
- // js/phoenix_live_view/constants.js
30
+ // js/phoenix_live_view/constants.ts
31
31
  var CONSECUTIVE_RELOADS = "consecutive-reloads";
32
32
  var MAX_RELOADS = 10;
33
33
  var RELOAD_JITTER_MIN = 5e3;
@@ -169,12 +169,12 @@ var EntryUploader = class {
169
169
  }
170
170
  this.uploadChannel.leave();
171
171
  this.errored = true;
172
- clearTimeout(this.chunkTimer);
172
+ this.chunkTimer != null && clearTimeout(this.chunkTimer);
173
173
  this.entry.error(reason);
174
174
  }
175
175
  upload() {
176
176
  this.uploadChannel.onError((reason) => this.error(reason));
177
- this.uploadChannel.join().receive("ok", (_data) => this.readNextChunk()).receive("error", (reason) => this.error(reason));
177
+ this.uploadChannel.join().receive("ok", (_data) => this.readNextChunk()).receive("error", ({ reason }) => this.error(reason));
178
178
  }
179
179
  isDone() {
180
180
  return this.offset >= this.entry.file.size;
@@ -186,7 +186,7 @@ var EntryUploader = class {
186
186
  this.chunkSize + this.offset
187
187
  );
188
188
  reader.onload = (e) => {
189
- if (e.target.error === null) {
189
+ if (e.target?.error === null) {
190
190
  this.offset += /** @type {ArrayBuffer} */
191
191
  e.target.result.byteLength;
192
192
  this.pushChunk(
@@ -194,7 +194,7 @@ var EntryUploader = class {
194
194
  e.target.result
195
195
  );
196
196
  } else {
197
- return logError("Read error: " + e.target.error);
197
+ return logError("Read error: " + e.target?.error);
198
198
  }
199
199
  };
200
200
  reader.readAsArrayBuffer(blob);
@@ -215,8 +215,23 @@ var EntryUploader = class {
215
215
  }
216
216
  };
217
217
 
218
- // js/phoenix_live_view/utils.js
218
+ // js/phoenix_live_view/utils.ts
219
219
  var logError = (msg, obj) => console.error && console.error(msg, obj);
220
+ var ensureSameOrigin = (href, kind) => {
221
+ let url;
222
+ try {
223
+ url = new URL(href, window.location.href);
224
+ } catch {
225
+ throw new Error(
226
+ `expected ${kind} destination to be a valid URL, got: ${href}`
227
+ );
228
+ }
229
+ if (url.origin !== window.location.origin) {
230
+ throw new Error(
231
+ `cannot ${kind} to "${href}" because its origin does not match the current origin "${window.location.origin}". Use window.location directly for cross-origin navigation.`
232
+ );
233
+ }
234
+ };
220
235
  var isCid = (cid) => {
221
236
  const type = typeof cid;
222
237
  return type === "number" || type === "string" && /^(0|[1-9]\d*)$/.test(cid);
@@ -257,12 +272,13 @@ var closure = (val) => typeof val === "function" ? val : function() {
257
272
  var clone = (obj) => {
258
273
  return JSON.parse(JSON.stringify(obj));
259
274
  };
260
- var closestPhxBinding = (el, binding, borderEl) => {
275
+ var closestPhxBinding = (startEl, binding, borderEl) => {
276
+ let el = startEl;
261
277
  do {
262
- if (el.matches(`[${binding}]`) && !el.disabled) {
278
+ if (el.matches(`[${binding}]`) && !("disabled" in el && el.disabled)) {
263
279
  return el;
264
280
  }
265
- el = el.parentElement || el.parentNode;
281
+ el = el.parentElement;
266
282
  } while (el !== null && el.nodeType === 1 && !(borderEl && borderEl.isSameNode(el) || el.matches(PHX_VIEW_SELECTOR)));
267
283
  return null;
268
284
  };
@@ -294,7 +310,7 @@ var eventContainsFiles = (e) => {
294
310
  return false;
295
311
  };
296
312
 
297
- // js/phoenix_live_view/browser.js
313
+ // js/phoenix_live_view/browser.ts
298
314
  var Browser = {
299
315
  canPushState() {
300
316
  return typeof history.pushState !== "undefined";
@@ -341,7 +357,7 @@ var Browser = {
341
357
  }
342
358
  });
343
359
  }
344
- } else {
360
+ } else if (to) {
345
361
  this.redirect(to);
346
362
  }
347
363
  },
@@ -358,7 +374,7 @@ var Browser = {
358
374
  deleteCookie(name) {
359
375
  document.cookie = `${name}=; max-age=-1; path=/`;
360
376
  },
361
- redirect(toURL, flash, navigate = (url) => {
377
+ redirect(toURL, flash = null, navigate = (url) => {
362
378
  window.location.href = url;
363
379
  }) {
364
380
  if (flash) {
@@ -379,11 +395,21 @@ var Browser = {
379
395
  };
380
396
  var browser_default = Browser;
381
397
 
382
- // js/phoenix_live_view/dom.js
398
+ // js/phoenix_live_view/dom.ts
383
399
  var DOM = {
384
400
  byId(id) {
385
401
  return document.getElementById(id) || logError(`no id found for ${id}`);
386
402
  },
403
+ elementFromTarget(target) {
404
+ if (!(target instanceof Node)) {
405
+ return null;
406
+ }
407
+ if (target.nodeType === Node.ELEMENT_NODE) {
408
+ return target;
409
+ } else {
410
+ return target.parentElement;
411
+ }
412
+ },
387
413
  removeClass(el, className) {
388
414
  el.classList.remove(className);
389
415
  if (el.classList.length === 0) {
@@ -421,12 +447,20 @@ var DOM = {
421
447
  inputsOutsideForm
422
448
  );
423
449
  },
424
- findComponentNodeList(viewId, cid, doc2 = document) {
425
- return this.all(
426
- doc2,
450
+ findComponent(viewId, cid, doc2 = document) {
451
+ return doc2.querySelector(
427
452
  `[${PHX_VIEW_REF}="${viewId}"][${PHX_COMPONENT}="${cid}"]`
428
453
  );
429
454
  },
455
+ getComponent(viewId, cid, doc2 = document) {
456
+ const el = this.findComponent(viewId, cid, doc2);
457
+ if (!el) {
458
+ throw new Error(
459
+ `no component found matching viewId ${viewId} and cid ${cid}`
460
+ );
461
+ }
462
+ return el;
463
+ },
430
464
  isPhxDestroyed(node) {
431
465
  return node.id && DOM.private(node, "destroyed") ? true : false;
432
466
  },
@@ -818,11 +852,23 @@ var DOM = {
818
852
  focused.setSelectionRange(selectionStart, selectionEnd);
819
853
  }
820
854
  },
821
- isFormInput(el) {
822
- if (el.localName && customElements.get(el.localName)) {
823
- return customElements.get(el.localName)[`formAssociated`];
855
+ /**
856
+ * Returns true if the element is an input that can be focused and edited by the user,
857
+ * so we can skip patching it if it has focus.
858
+ */
859
+ isEditableInput(el) {
860
+ return this.isFormAssociated(el) && !(el instanceof HTMLButtonElement) && !(el instanceof HTMLInputElement && el.type === "button");
861
+ },
862
+ isFormAssociated(el) {
863
+ if (!(el instanceof HTMLElement))
864
+ return false;
865
+ if (el.localName) {
866
+ const customEl = customElements.get(el.localName);
867
+ if (customEl) {
868
+ return customEl.formAssociated === true;
869
+ }
824
870
  }
825
- return /^(?:input|select|textarea)$/i.test(el.tagName) && el.type !== "button";
871
+ return el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement || el instanceof HTMLButtonElement;
826
872
  },
827
873
  syncAttrsToProps(el) {
828
874
  if (el instanceof HTMLInputElement && CHECKABLE_INPUTS.indexOf(el.type.toLocaleLowerCase()) >= 0) {
@@ -839,13 +885,13 @@ var DOM = {
839
885
  if (DOM.isPhxUpdate(container, phxUpdate, ["append", "prepend", PHX_STREAM])) {
840
886
  const toRemove = [];
841
887
  container.childNodes.forEach((childNode) => {
842
- if (!childNode.id) {
843
- const isEmptyTextNode = childNode.nodeType === Node.TEXT_NODE && childNode.nodeValue.trim() === "";
888
+ if (!("id" in childNode) || !childNode.id) {
889
+ const isEmptyTextNode = childNode.nodeType === Node.TEXT_NODE && childNode.nodeValue && childNode.nodeValue.trim() === "";
844
890
  if (!isEmptyTextNode && childNode.nodeType !== Node.COMMENT_NODE) {
845
891
  logError(
846
892
  `only HTML element tags with an id are allowed inside containers with phx-update.
847
893
 
848
- removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
894
+ removing illegal node: "${("outerHTML" in childNode && childNode.outerHTML || childNode.nodeValue || "").trim()}"
849
895
 
850
896
  `
851
897
  );
@@ -873,9 +919,12 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
873
919
  Object.keys(attrs).forEach(
874
920
  (attr) => newContainer.setAttribute(attr, attrs[attr])
875
921
  );
876
- retainedAttrs.forEach(
877
- (attr) => newContainer.setAttribute(attr, container.getAttribute(attr))
878
- );
922
+ retainedAttrs.forEach((attr) => {
923
+ const value = container.getAttribute(attr);
924
+ if (value !== null) {
925
+ newContainer.setAttribute(attr, value);
926
+ }
927
+ });
879
928
  newContainer.innerHTML = container.innerHTML;
880
929
  container.replaceWith(newContainer);
881
930
  return newContainer;
@@ -1127,7 +1176,7 @@ var LiveUploader = class _LiveUploader {
1127
1176
  [],
1128
1177
  (existing) => existing.concat(newFiles)
1129
1178
  );
1130
- inputEl.value = null;
1179
+ inputEl.value = "";
1131
1180
  } else {
1132
1181
  if (dataTransfer && dataTransfer.files.length > 0) {
1133
1182
  inputEl.files = dataTransfer.files;
@@ -1210,20 +1259,20 @@ var LiveUploader = class _LiveUploader {
1210
1259
  }
1211
1260
  };
1212
1261
 
1213
- // js/phoenix_live_view/aria.js
1262
+ // js/phoenix_live_view/aria.ts
1214
1263
  var ARIA = {
1215
1264
  anyOf(instance, classes) {
1216
- return classes.find((name) => instance instanceof name);
1265
+ return classes.some((name) => instance instanceof name);
1217
1266
  },
1218
- isFocusable(el, interactiveOnly) {
1219
- return el instanceof HTMLAnchorElement && el.rel !== "ignore" || el instanceof HTMLAreaElement && el.href !== void 0 || !el.disabled && this.anyOf(el, [
1267
+ isFocusable(el, interactiveOnly = false) {
1268
+ return el instanceof HTMLAnchorElement && el.rel !== "ignore" || el instanceof HTMLAreaElement && el.href !== void 0 || !("disabled" in el && el.disabled) && this.anyOf(el, [
1220
1269
  HTMLInputElement,
1221
1270
  HTMLSelectElement,
1222
1271
  HTMLTextAreaElement,
1223
1272
  HTMLButtonElement
1224
- ]) || el instanceof HTMLIFrameElement || el.tabIndex >= 0 && el.getAttribute("aria-hidden") !== "true" || !interactiveOnly && el.getAttribute("tabindex") !== null && el.getAttribute("aria-hidden") !== "true";
1273
+ ]) || el instanceof HTMLIFrameElement || el instanceof HTMLElement && el.tabIndex >= 0 && el.getAttribute("aria-hidden") !== "true" || !interactiveOnly && el.getAttribute("tabindex") !== null && el.getAttribute("aria-hidden") !== "true";
1225
1274
  },
1226
- attemptFocus(el, interactiveOnly) {
1275
+ attemptFocus(el, interactiveOnly = false) {
1227
1276
  if (this.isFocusable(el, interactiveOnly)) {
1228
1277
  try {
1229
1278
  el.focus();
@@ -1240,6 +1289,7 @@ var ARIA = {
1240
1289
  }
1241
1290
  child = child.nextElementSibling;
1242
1291
  }
1292
+ return false;
1243
1293
  },
1244
1294
  focusFirst(el) {
1245
1295
  let child = el.firstElementChild;
@@ -1249,6 +1299,7 @@ var ARIA = {
1249
1299
  }
1250
1300
  child = child.nextElementSibling;
1251
1301
  }
1302
+ return false;
1252
1303
  },
1253
1304
  focusLast(el) {
1254
1305
  let child = el.lastElementChild;
@@ -1258,79 +1309,12 @@ var ARIA = {
1258
1309
  }
1259
1310
  child = child.previousElementSibling;
1260
1311
  }
1312
+ return false;
1261
1313
  }
1262
1314
  };
1263
1315
  var aria_default = ARIA;
1264
1316
 
1265
- // js/phoenix_live_view/hooks.js
1266
- var Hooks = {
1267
- LiveFileUpload: {
1268
- activeRefs() {
1269
- return this.el.getAttribute(PHX_ACTIVE_ENTRY_REFS);
1270
- },
1271
- preflightedRefs() {
1272
- return this.el.getAttribute(PHX_PREFLIGHTED_REFS);
1273
- },
1274
- mounted() {
1275
- this.js().ignoreAttributes(this.el, ["value"]);
1276
- this.preflightedWas = this.preflightedRefs();
1277
- },
1278
- updated() {
1279
- const newPreflights = this.preflightedRefs();
1280
- if (this.preflightedWas !== newPreflights) {
1281
- this.preflightedWas = newPreflights;
1282
- if (newPreflights === "") {
1283
- this.__view().cancelSubmit(this.el.form);
1284
- }
1285
- }
1286
- if (this.activeRefs() === "") {
1287
- this.el.value = null;
1288
- }
1289
- this.el.dispatchEvent(new CustomEvent(PHX_LIVE_FILE_UPDATED));
1290
- }
1291
- },
1292
- LiveImgPreview: {
1293
- mounted() {
1294
- this.ref = this.el.getAttribute("data-phx-entry-ref");
1295
- this.inputEl = document.getElementById(
1296
- this.el.getAttribute(PHX_UPLOAD_REF)
1297
- );
1298
- this.url = LiveUploader.getEntryDataURL(this.inputEl, this.ref);
1299
- this.el.src = this.url;
1300
- },
1301
- destroyed() {
1302
- URL.revokeObjectURL(this.url);
1303
- }
1304
- },
1305
- FocusWrap: {
1306
- mounted() {
1307
- this.focusStart = this.el.firstElementChild;
1308
- this.focusEnd = this.el.lastElementChild;
1309
- this.focusStart.addEventListener("focus", (e) => {
1310
- if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
1311
- const nextFocus = e.target.nextElementSibling;
1312
- aria_default.attemptFocus(nextFocus) || aria_default.focusFirst(nextFocus);
1313
- } else {
1314
- aria_default.focusLast(this.el);
1315
- }
1316
- });
1317
- this.focusEnd.addEventListener("focus", (e) => {
1318
- if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
1319
- const nextFocus = e.target.previousElementSibling;
1320
- aria_default.attemptFocus(nextFocus) || aria_default.focusLast(nextFocus);
1321
- } else {
1322
- aria_default.focusFirst(this.el);
1323
- }
1324
- });
1325
- if (!this.el.contains(document.activeElement)) {
1326
- this.el.addEventListener("phx:show-end", () => this.el.focus());
1327
- if (window.getComputedStyle(this.el).display !== "none") {
1328
- aria_default.focusFirst(this.el);
1329
- }
1330
- }
1331
- }
1332
- }
1333
- };
1317
+ // js/phoenix_live_view/hooks.ts
1334
1318
  var findScrollContainer = (el) => {
1335
1319
  if (["HTML", "BODY"].indexOf(el.nodeName.toUpperCase()) >= 0)
1336
1320
  return null;
@@ -1371,7 +1355,7 @@ var isWithinViewport = (el, scrollContainer) => {
1371
1355
  const rect = el.getBoundingClientRect();
1372
1356
  return Math.ceil(rect.top) >= top(scrollContainer) && Math.floor(rect.top) <= bottom(scrollContainer);
1373
1357
  };
1374
- Hooks.InfiniteScroll = {
1358
+ var InfiniteScroll = {
1375
1359
  mounted() {
1376
1360
  this.scrollContainer = findScrollContainer(this.el);
1377
1361
  let scrollBefore = scrollTop(this.scrollContainer);
@@ -1447,9 +1431,9 @@ Hooks.InfiniteScroll = {
1447
1431
  } else if (isScrollingDown && topOverran && rect.top <= 0) {
1448
1432
  topOverran = false;
1449
1433
  }
1450
- if (topEvent && isScrollingUp && isAtViewportTop(firstChild, this.scrollContainer)) {
1434
+ if (topEvent && isScrollingUp && firstChild && isAtViewportTop(firstChild, this.scrollContainer)) {
1451
1435
  onFirstChildAtTop(topEvent, firstChild);
1452
- } else if (bottomEvent && isScrollingDown && isAtViewportBottom(lastChild, this.scrollContainer)) {
1436
+ } else if (bottomEvent && isScrollingDown && lastChild && isAtViewportBottom(lastChild, this.scrollContainer)) {
1453
1437
  onLastChildAtBottom(bottomEvent, lastChild);
1454
1438
  }
1455
1439
  scrollBefore = scrollNow;
@@ -1513,9 +1497,80 @@ Hooks.InfiniteScroll = {
1513
1497
  return rect;
1514
1498
  }
1515
1499
  };
1500
+ var LiveFileUpload = {
1501
+ activeRefs() {
1502
+ return this.el.getAttribute(PHX_ACTIVE_ENTRY_REFS);
1503
+ },
1504
+ preflightedRefs() {
1505
+ return this.el.getAttribute(PHX_PREFLIGHTED_REFS);
1506
+ },
1507
+ mounted() {
1508
+ this.js().ignoreAttributes(this.el, ["value"]);
1509
+ this.preflightedWas = this.preflightedRefs();
1510
+ },
1511
+ updated() {
1512
+ const newPreflights = this.preflightedRefs();
1513
+ if (this.preflightedWas !== newPreflights) {
1514
+ this.preflightedWas = newPreflights;
1515
+ if (newPreflights === "") {
1516
+ this.__view().cancelSubmit(this.el.form);
1517
+ }
1518
+ }
1519
+ if (this.activeRefs() === "") {
1520
+ this.el.value = "";
1521
+ }
1522
+ this.el.dispatchEvent(new CustomEvent(PHX_LIVE_FILE_UPDATED));
1523
+ }
1524
+ };
1525
+ var LiveImgPreview = {
1526
+ mounted() {
1527
+ this.ref = this.el.getAttribute("data-phx-entry-ref");
1528
+ this.inputEl = document.getElementById(
1529
+ this.el.getAttribute(PHX_UPLOAD_REF)
1530
+ );
1531
+ this.url = LiveUploader.getEntryDataURL(this.inputEl, this.ref);
1532
+ this.el.src = this.url;
1533
+ },
1534
+ destroyed() {
1535
+ URL.revokeObjectURL(this.url);
1536
+ }
1537
+ };
1538
+ var Hooks = {
1539
+ LiveFileUpload,
1540
+ LiveImgPreview,
1541
+ FocusWrap: {
1542
+ mounted() {
1543
+ this.focusStart = this.el.firstElementChild;
1544
+ this.focusEnd = this.el.lastElementChild;
1545
+ this.focusStart.addEventListener("focus", (e) => {
1546
+ if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
1547
+ const nextFocus = e.target.nextElementSibling;
1548
+ aria_default.attemptFocus(nextFocus) || aria_default.focusFirst(nextFocus);
1549
+ } else {
1550
+ aria_default.focusLast(this.el);
1551
+ }
1552
+ });
1553
+ this.focusEnd.addEventListener("focus", (e) => {
1554
+ if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
1555
+ const nextFocus = e.target.previousElementSibling;
1556
+ aria_default.attemptFocus(nextFocus) || aria_default.focusLast(nextFocus);
1557
+ } else {
1558
+ aria_default.focusFirst(this.el);
1559
+ }
1560
+ });
1561
+ if (!this.el.contains(document.activeElement)) {
1562
+ this.el.addEventListener("phx:show-end", () => this.el.focus());
1563
+ if (window.getComputedStyle(this.el).display !== "none") {
1564
+ aria_default.focusFirst(this.el);
1565
+ }
1566
+ }
1567
+ }
1568
+ },
1569
+ InfiniteScroll
1570
+ };
1516
1571
  var hooks_default = Hooks;
1517
1572
 
1518
- // js/phoenix_live_view/element_ref.js
1573
+ // js/phoenix_live_view/element_ref.ts
1519
1574
  var ElementRef = class {
1520
1575
  static onUnlock(el, callback) {
1521
1576
  if (!dom_default.isLocked(el) && !el.closest(`[${PHX_REF_LOCK}]`)) {
@@ -1611,11 +1666,11 @@ var ElementRef = class {
1611
1666
  this.el.removeAttribute(PHX_REF_LOADING);
1612
1667
  const disabledVal = this.el.getAttribute(PHX_DISABLED);
1613
1668
  const readOnlyVal = this.el.getAttribute(PHX_READONLY);
1614
- if (readOnlyVal !== null) {
1669
+ if (readOnlyVal !== null && "readOnly" in this.el) {
1615
1670
  this.el.readOnly = readOnlyVal === "true" ? true : false;
1616
1671
  this.el.removeAttribute(PHX_READONLY);
1617
1672
  }
1618
- if (disabledVal !== null) {
1673
+ if (disabledVal !== null && "disabled" in this.el) {
1619
1674
  this.el.disabled = disabledVal === "true" ? true : false;
1620
1675
  this.el.removeAttribute(PHX_DISABLED);
1621
1676
  }
@@ -1642,6 +1697,7 @@ var ElementRef = class {
1642
1697
  isLoadingUndoneBy(ref) {
1643
1698
  return this.loadingRef === null ? false : this.loadingRef <= ref;
1644
1699
  }
1700
+ /** @internal */
1645
1701
  isLockUndoneBy(ref) {
1646
1702
  return this.lockRef === null ? false : this.lockRef <= ref;
1647
1703
  }
@@ -1654,7 +1710,7 @@ var ElementRef = class {
1654
1710
  }
1655
1711
  };
1656
1712
 
1657
- // js/phoenix_live_view/dom_post_morph_restorer.js
1713
+ // js/phoenix_live_view/dom_post_morph_restorer.ts
1658
1714
  var DOMPostMorphRestorer = class {
1659
1715
  constructor(containerBefore, containerAfter, updateType) {
1660
1716
  const idsBefore = /* @__PURE__ */ new Set();
@@ -2245,47 +2301,47 @@ function morphdomFactory(morphAttrs2) {
2245
2301
  var morphdom = morphdomFactory(morphAttrs);
2246
2302
  var morphdom_esm_default = morphdom;
2247
2303
 
2248
- // js/phoenix_live_view/dom_patch.js
2304
+ // js/phoenix_live_view/dom_patch.ts
2249
2305
  var DOMPatch = class {
2250
- constructor(view, container, id, html, streams, targetCID, opts = {}) {
2306
+ constructor(view, container, html, streams, targetCID, opts = {}) {
2251
2307
  this.view = view;
2252
2308
  this.liveSocket = view.liveSocket;
2253
2309
  this.container = container;
2254
- this.id = id;
2255
2310
  this.rootID = view.root.id;
2256
2311
  this.html = html;
2257
2312
  this.streams = streams;
2258
2313
  this.streamInserts = {};
2259
2314
  this.streamComponentRestore = {};
2260
2315
  this.targetCID = targetCID;
2261
- this.cidPatch = isCid(this.targetCID);
2262
2316
  this.pendingRemoves = [];
2263
2317
  this.phxRemove = this.liveSocket.binding("remove");
2264
- this.targetContainer = this.isCIDPatch() ? this.targetCIDContainer(html) : container;
2265
- this.callbacks = {
2266
- beforeadded: [],
2267
- beforeupdated: [],
2268
- beforephxChildAdded: [],
2269
- afteradded: [],
2270
- afterupdated: [],
2271
- afterdiscarded: [],
2272
- afterphxChildAdded: [],
2273
- aftertransitionsDiscarded: []
2274
- };
2318
+ this.targetContainer = targetCID ? dom_default.getComponent(this.view.id, targetCID) : container;
2319
+ this.beforeUpdatedCallbacks = [];
2320
+ this.afterAddedCallbacks = [];
2321
+ this.afterUpdatedCallbacks = [];
2322
+ this.afterPhxChildAddedCallbacks = [];
2323
+ this.afterDiscardedCallbacks = [];
2324
+ this.afterTransitionsDiscardedCallbacks = [];
2275
2325
  this.withChildren = opts.withChildren || opts.undoRef !== void 0 || false;
2276
- this.undoRef = opts.undoRef;
2326
+ this.undoRef = opts.undoRef ?? null;
2277
2327
  }
2278
- before(kind, callback) {
2279
- this.callbacks[`before${kind}`].push(callback);
2328
+ beforeUpdated(callback) {
2329
+ this.beforeUpdatedCallbacks.push(callback);
2280
2330
  }
2281
- after(kind, callback) {
2282
- this.callbacks[`after${kind}`].push(callback);
2331
+ afterAdded(callback) {
2332
+ this.afterAddedCallbacks.push(callback);
2283
2333
  }
2284
- trackBefore(kind, ...args) {
2285
- this.callbacks[`before${kind}`].forEach((callback) => callback(...args));
2334
+ afterUpdated(callback) {
2335
+ this.afterUpdatedCallbacks.push(callback);
2286
2336
  }
2287
- trackAfter(kind, ...args) {
2288
- this.callbacks[`after${kind}`].forEach((callback) => callback(...args));
2337
+ afterPhxChildAdded(callback) {
2338
+ this.afterPhxChildAddedCallbacks.push(callback);
2339
+ }
2340
+ afterDiscarded(callback) {
2341
+ this.afterDiscardedCallbacks.push(callback);
2342
+ }
2343
+ afterTransitionsDiscarded(callback) {
2344
+ this.afterTransitionsDiscardedCallbacks.push(callback);
2289
2345
  }
2290
2346
  markPrunableContentForRemoval() {
2291
2347
  const phxUpdate = this.liveSocket.binding(PHX_UPDATE);
@@ -2300,10 +2356,7 @@ var DOMPatch = class {
2300
2356
  perform(isJoinPatch) {
2301
2357
  const { view, liveSocket, html, container } = this;
2302
2358
  let targetContainer = this.targetContainer;
2303
- if (this.isCIDPatch() && !this.targetContainer) {
2304
- return;
2305
- }
2306
- if (this.isCIDPatch()) {
2359
+ if (this.targetCID) {
2307
2360
  const closestLock = targetContainer.closest(`[${PHX_REF_LOCK}]`);
2308
2361
  if (closestLock && !closestLock.isSameNode(targetContainer)) {
2309
2362
  const clonedTree = dom_default.private(closestLock, PHX_REF_LOCK);
@@ -2335,6 +2388,8 @@ var DOMPatch = class {
2335
2388
  // another case is the recursive patch of a stream item that was kept on reset (-> onBeforeNodeAdded)
2336
2389
  childrenOnly: targetContainer2.getAttribute(PHX_COMPONENT) === null && !withChildren,
2337
2390
  getNodeKey: (node) => {
2391
+ if (!(node instanceof Element))
2392
+ return null;
2338
2393
  if (dom_default.isPhxDestroyed(node)) {
2339
2394
  return null;
2340
2395
  }
@@ -2342,9 +2397,9 @@ var DOMPatch = class {
2342
2397
  return node.id;
2343
2398
  }
2344
2399
  if (dom_default.private(node, "clientsideIdAttribute")) {
2345
- return node.getAttribute && node.getAttribute(PHX_MAGIC_ID);
2400
+ return node.getAttribute(PHX_MAGIC_ID);
2346
2401
  }
2347
- return node.id || node.getAttribute && node.getAttribute(PHX_MAGIC_ID);
2402
+ return node.id || node.getAttribute(PHX_MAGIC_ID);
2348
2403
  },
2349
2404
  // skip indexing from children when container is stream
2350
2405
  skipFromChildren: (from) => {
@@ -2365,7 +2420,7 @@ var DOMPatch = class {
2365
2420
  const nonStreamChild = Array.from(parent.children).find(
2366
2421
  (c) => !c.hasAttribute(PHX_STREAM_REF)
2367
2422
  );
2368
- parent.insertBefore(child, nonStreamChild);
2423
+ parent.insertBefore(child, nonStreamChild ?? null);
2369
2424
  } else {
2370
2425
  parent.appendChild(child);
2371
2426
  }
@@ -2375,11 +2430,13 @@ var DOMPatch = class {
2375
2430
  }
2376
2431
  },
2377
2432
  onBeforeNodeAdded: (el) => {
2433
+ if (!(el instanceof Element)) {
2434
+ return el;
2435
+ }
2378
2436
  if (this.getStreamInsert(el)?.updateOnly && !this.streamComponentRestore[el.id]) {
2379
2437
  return false;
2380
2438
  }
2381
2439
  dom_default.maintainPrivateHooks(el, el, phxViewportTop, phxViewportBottom);
2382
- this.trackBefore("added", el);
2383
2440
  let morphedEl = el;
2384
2441
  if (this.streamComponentRestore[el.id]) {
2385
2442
  morphedEl = this.streamComponentRestore[el.id];
@@ -2389,9 +2446,11 @@ var DOMPatch = class {
2389
2446
  return morphedEl;
2390
2447
  },
2391
2448
  onNodeAdded: (el) => {
2392
- if (el.getAttribute) {
2393
- this.maybeReOrderStream(el, true);
2449
+ if (!(el instanceof Element)) {
2450
+ added.push(el);
2451
+ return;
2394
2452
  }
2453
+ this.maybeReOrderStream(el, true);
2395
2454
  if (dom_default.isPortalTemplate(el)) {
2396
2455
  portalCallbacks.push(() => this.teleport(el, morph));
2397
2456
  }
@@ -2404,7 +2463,7 @@ var DOMPatch = class {
2404
2463
  externalFormTriggered = el;
2405
2464
  }
2406
2465
  if (dom_default.isPhxChild(el) && view.ownsElement(el) || dom_default.isPhxSticky(el) && view.ownsElement(el.parentNode)) {
2407
- this.trackAfter("phxChildAdded", el);
2466
+ this.trackAfterPhxChildAdded(el);
2408
2467
  }
2409
2468
  if (el.nodeName === "SCRIPT" && el.hasAttribute(PHX_RUNTIME_HOOK)) {
2410
2469
  this.handleRuntimeHook(el, source);
@@ -2413,7 +2472,10 @@ var DOMPatch = class {
2413
2472
  },
2414
2473
  onNodeDiscarded: (el) => this.onNodeDiscarded(el),
2415
2474
  onBeforeNodeDiscarded: (el) => {
2416
- if (el.getAttribute && el.getAttribute(PHX_PRUNE) !== null) {
2475
+ if (!(el instanceof Element)) {
2476
+ return true;
2477
+ }
2478
+ if (el.getAttribute(PHX_PRUNE) !== null) {
2417
2479
  return true;
2418
2480
  }
2419
2481
  if (el.parentElement !== null && el.id && dom_default.isPhxUpdate(el.parentElement, phxUpdate, [
@@ -2423,7 +2485,7 @@ var DOMPatch = class {
2423
2485
  ])) {
2424
2486
  return false;
2425
2487
  }
2426
- if (el.getAttribute && el.getAttribute(PHX_TELEPORTED_REF)) {
2488
+ if (el.getAttribute(PHX_TELEPORTED_REF)) {
2427
2489
  return false;
2428
2490
  }
2429
2491
  if (this.maybePendingRemove(el)) {
@@ -2434,7 +2496,7 @@ var DOMPatch = class {
2434
2496
  }
2435
2497
  if (dom_default.isPortalTemplate(el)) {
2436
2498
  const teleportedEl = document.getElementById(
2437
- el.content.firstElementChild.id
2499
+ el.content.firstElementChild?.id || ""
2438
2500
  );
2439
2501
  if (teleportedEl) {
2440
2502
  teleportedEl.remove();
@@ -2465,7 +2527,7 @@ var DOMPatch = class {
2465
2527
  phxViewportBottom
2466
2528
  );
2467
2529
  dom_default.cleanChildNodes(toEl, phxUpdate);
2468
- const isFocusedFormEl = focused && fromEl.isSameNode(focused) && dom_default.isFormInput(fromEl);
2530
+ const isFocusedFormEl = focused && fromEl.isSameNode(focused) && dom_default.isEditableInput(fromEl);
2469
2531
  const focusedSelectChanged = isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
2470
2532
  if (this.skipCIDSibling(toEl)) {
2471
2533
  this.maybeCloneLockedElement(fromEl, isFocusedFormEl);
@@ -2486,7 +2548,7 @@ var DOMPatch = class {
2486
2548
  return false;
2487
2549
  }
2488
2550
  if (dom_default.isIgnored(fromEl, phxUpdate) || fromEl.form && fromEl.form.isSameNode(externalFormTriggered)) {
2489
- this.trackBefore("updated", fromEl, toEl);
2551
+ this.trackBeforeUpdated(fromEl, toEl);
2490
2552
  dom_default.mergeAttrs(fromEl, toEl, {
2491
2553
  isIgnored: dom_default.isIgnored(fromEl, phxUpdate)
2492
2554
  });
@@ -2516,7 +2578,7 @@ var DOMPatch = class {
2516
2578
  return false;
2517
2579
  }
2518
2580
  if (isFocusedFormEl && fromEl.type !== "hidden" && !focusedSelectChanged) {
2519
- this.trackBefore("updated", fromEl, toEl);
2581
+ this.trackBeforeUpdated(fromEl, toEl);
2520
2582
  dom_default.mergeFocusedInput(fromEl, toEl);
2521
2583
  dom_default.syncAttrsToProps(fromEl);
2522
2584
  updates.push(fromEl);
@@ -2537,15 +2599,14 @@ var DOMPatch = class {
2537
2599
  }
2538
2600
  dom_default.syncAttrsToProps(toEl);
2539
2601
  dom_default.applyStickyOperations(toEl);
2540
- this.trackBefore("updated", fromEl, toEl);
2602
+ this.trackBeforeUpdated(fromEl, toEl);
2541
2603
  return fromEl;
2542
2604
  }
2543
2605
  }
2544
2606
  };
2545
2607
  morphdom_esm_default(targetContainer2, source, morphCallbacks);
2546
2608
  };
2547
- this.trackBefore("added", container);
2548
- this.trackBefore("updated", container, container);
2609
+ this.trackBeforeUpdated(container, container);
2549
2610
  liveSocket.time("morphdom", () => {
2550
2611
  this.streams.forEach(([ref, inserts, deleteIds, reset]) => {
2551
2612
  inserts.forEach(([key, streamAt, limit, updateOnly]) => {
@@ -2581,13 +2642,14 @@ var DOMPatch = class {
2581
2642
  this.view.portalElementIds.forEach((id) => {
2582
2643
  const el = document.getElementById(id);
2583
2644
  if (el) {
2584
- const source = document.getElementById(
2585
- el.getAttribute(PHX_TELEPORTED_SRC)
2586
- );
2587
- if (!source) {
2588
- el.remove();
2589
- this.onNodeDiscarded(el);
2590
- this.view.dropPortalElementId(id);
2645
+ const srcId = el.getAttribute(PHX_TELEPORTED_SRC);
2646
+ if (srcId) {
2647
+ const source = document.getElementById(srcId);
2648
+ if (!source) {
2649
+ el.remove();
2650
+ this.onNodeDiscarded(el);
2651
+ this.view.dropPortalElementId(id);
2652
+ }
2591
2653
  }
2592
2654
  }
2593
2655
  });
@@ -2615,8 +2677,8 @@ var DOMPatch = class {
2615
2677
  () => dom_default.restoreFocus(focused, selectionStart, selectionEnd)
2616
2678
  );
2617
2679
  dom_default.dispatchEvent(document, "phx:update");
2618
- added.forEach((el) => this.trackAfter("added", el));
2619
- updates.forEach((el) => this.trackAfter("updated", el));
2680
+ added.forEach((el) => this.trackAfterAdded(el));
2681
+ updates.forEach((el) => this.trackAfterUpdated(el));
2620
2682
  this.transitionPendingRemoves();
2621
2683
  if (externalFormTriggered) {
2622
2684
  liveSocket.unload();
@@ -2638,11 +2700,29 @@ var DOMPatch = class {
2638
2700
  }
2639
2701
  return true;
2640
2702
  }
2703
+ trackBeforeUpdated(fromEl, toEl) {
2704
+ this.beforeUpdatedCallbacks.forEach((cb) => cb(fromEl, toEl));
2705
+ }
2706
+ trackAfterAdded(el) {
2707
+ this.afterAddedCallbacks.forEach((cb) => cb(el));
2708
+ }
2709
+ trackAfterUpdated(el) {
2710
+ this.afterUpdatedCallbacks.forEach((cb) => cb(el));
2711
+ }
2712
+ trackAfterPhxChildAdded(el) {
2713
+ this.afterPhxChildAddedCallbacks.forEach((cb) => cb(el));
2714
+ }
2715
+ trackAfterDiscarded(el) {
2716
+ this.afterDiscardedCallbacks.forEach((cb) => cb(el));
2717
+ }
2718
+ trackAfterTransitionsDiscarded(els) {
2719
+ this.afterTransitionsDiscardedCallbacks.forEach((cb) => cb(els));
2720
+ }
2641
2721
  onNodeDiscarded(el) {
2642
2722
  if (dom_default.isPhxChild(el) || dom_default.isPhxSticky(el)) {
2643
2723
  this.liveSocket.destroyViewByEl(el);
2644
2724
  }
2645
- this.trackAfter("discarded", el);
2725
+ this.trackAfterDiscarded(el);
2646
2726
  }
2647
2727
  maybePendingRemove(node) {
2648
2728
  if (node.getAttribute && node.getAttribute(this.phxRemove) !== null) {
@@ -2677,7 +2757,7 @@ var DOMPatch = class {
2677
2757
  (el2) => el2.setAttribute(PHX_STREAM_REF, ref)
2678
2758
  );
2679
2759
  }
2680
- maybeReOrderStream(el, isNew) {
2760
+ maybeReOrderStream(el, isNew = false) {
2681
2761
  const { ref, streamAt, reset } = this.getStreamInsert(el);
2682
2762
  if (streamAt === void 0) {
2683
2763
  return;
@@ -2733,11 +2813,13 @@ var DOMPatch = class {
2733
2813
  }
2734
2814
  maybeLimitStream(el) {
2735
2815
  const { limit } = this.getStreamInsert(el);
2736
- const children = limit !== null && Array.from(el.parentElement.children);
2737
- if (limit && limit < 0 && children.length > limit * -1) {
2738
- children.slice(0, children.length + limit).forEach((child) => this.removeStreamChildElement(child));
2739
- } else if (limit && limit >= 0 && children.length > limit) {
2740
- children.slice(limit).forEach((child) => this.removeStreamChildElement(child));
2816
+ if (limit !== null) {
2817
+ const children = Array.from(el.parentElement.children);
2818
+ if (limit < 0 && children.length > limit * -1) {
2819
+ children.slice(0, children.length + limit).forEach((child) => this.removeStreamChildElement(child));
2820
+ } else if (limit >= 0 && children.length > limit) {
2821
+ children.slice(limit).forEach((child) => this.removeStreamChildElement(child));
2822
+ }
2741
2823
  }
2742
2824
  }
2743
2825
  transitionPendingRemoves() {
@@ -2751,7 +2833,7 @@ var DOMPatch = class {
2751
2833
  }
2752
2834
  el.remove();
2753
2835
  });
2754
- this.trackAfter("transitionsDiscarded", pendingRemoves);
2836
+ this.trackAfterTransitionsDiscarded(pendingRemoves);
2755
2837
  });
2756
2838
  }
2757
2839
  }
@@ -2765,9 +2847,6 @@ var DOMPatch = class {
2765
2847
  toEl.value = fromEl.value;
2766
2848
  return !fromEl.isEqualNode(toEl);
2767
2849
  }
2768
- isCIDPatch() {
2769
- return this.cidPatch;
2770
- }
2771
2850
  skipCIDSibling(el) {
2772
2851
  return el.nodeType === Node.ELEMENT_NODE && el.hasAttribute(PHX_SKIP);
2773
2852
  }
@@ -2775,7 +2854,7 @@ var DOMPatch = class {
2775
2854
  if (!fromEl.hasAttribute(PHX_REF_SRC))
2776
2855
  return fromEl;
2777
2856
  const ref = new ElementRef(fromEl);
2778
- if (ref.lockRef === null || this.undoRef !== void 0 && ref.isLockUndoneBy(this.undoRef)) {
2857
+ if (!fromEl.hasAttribute(PHX_REF_LOCK) || this.undoRef !== null && ref.isLockUndoneBy(this.undoRef)) {
2779
2858
  return fromEl;
2780
2859
  }
2781
2860
  dom_default.applyStickyOperations(fromEl);
@@ -2786,24 +2865,10 @@ var DOMPatch = class {
2786
2865
  return isFocusedFormEl ? fromEl : clone2;
2787
2866
  }
2788
2867
  copyNestedPrivateLock(fromEl, toEl) {
2789
- if (this.undoRef === void 0 || !dom_default.private(toEl, PHX_REF_LOCK))
2868
+ if (this.undoRef === null || !dom_default.private(toEl, PHX_REF_LOCK))
2790
2869
  return;
2791
2870
  dom_default.putPrivate(fromEl, PHX_REF_LOCK, dom_default.private(toEl, PHX_REF_LOCK));
2792
2871
  }
2793
- targetCIDContainer(html) {
2794
- if (!this.isCIDPatch()) {
2795
- return;
2796
- }
2797
- const [first, ...rest] = dom_default.findComponentNodeList(
2798
- this.view.id,
2799
- this.targetCID
2800
- );
2801
- if (rest.length === 0 && dom_default.childNodeLength(html) === 1) {
2802
- return first;
2803
- } else {
2804
- return first && first.parentNode;
2805
- }
2806
- }
2807
2872
  indexOf(parent, child) {
2808
2873
  return Array.from(parent.children).indexOf(child);
2809
2874
  }
@@ -2848,7 +2913,7 @@ var DOMPatch = class {
2848
2913
  if (el.hasAttribute("nonce")) {
2849
2914
  const template = document.createElement("template");
2850
2915
  template.innerHTML = source;
2851
- nonce = template.content.querySelector(`script[${PHX_RUNTIME_HOOK}="${CSS.escape(name)}"]`).getAttribute("nonce");
2916
+ nonce = template.content.querySelector(`script[${PHX_RUNTIME_HOOK}="${CSS.escape(name)}"]`)?.getAttribute("nonce") ?? null;
2852
2917
  }
2853
2918
  const script = document.createElement("script");
2854
2919
  script.textContent = el.textContent;
@@ -3370,7 +3435,7 @@ var JS = {
3370
3435
  }
3371
3436
  if (eventType === "change") {
3372
3437
  let { newCid, _target } = args;
3373
- _target = _target || (dom_default.isFormInput(sourceEl) ? sourceEl.name : void 0);
3438
+ _target = _target || (dom_default.isFormAssociated(sourceEl) ? sourceEl.name : void 0);
3374
3439
  if (_target) {
3375
3440
  pushOpts._target = _target;
3376
3441
  }
@@ -3884,6 +3949,7 @@ var js_commands_default = (liveSocket, eventType) => {
3884
3949
  });
3885
3950
  },
3886
3951
  navigate(href, opts = {}) {
3952
+ ensureSameOrigin(href, "navigate");
3887
3953
  const customEvent = new CustomEvent("phx:exec");
3888
3954
  liveSocket.historyRedirect(
3889
3955
  customEvent,
@@ -3894,6 +3960,7 @@ var js_commands_default = (liveSocket, eventType) => {
3894
3960
  );
3895
3961
  },
3896
3962
  patch(href, opts = {}) {
3963
+ ensureSameOrigin(href, "patch");
3897
3964
  const customEvent = new CustomEvent("phx:exec");
3898
3965
  liveSocket.pushHistoryPatch(
3899
3966
  customEvent,
@@ -3916,15 +3983,19 @@ var ViewHook = class _ViewHook {
3916
3983
  get liveSocket() {
3917
3984
  return this.__liveSocket();
3918
3985
  }
3986
+ /** @internal */
3919
3987
  static makeID() {
3920
3988
  return viewHookID++;
3921
3989
  }
3990
+ /** @internal */
3922
3991
  static elementID(el) {
3923
3992
  return dom_default.private(el, HOOK_ID);
3924
3993
  }
3994
+ /** @internal */
3925
3995
  static deadHook(el) {
3926
3996
  return dom_default.private(el, DEAD_HOOK) === true;
3927
3997
  }
3998
+ /** @internal */
3928
3999
  constructor(view, el, callbacks) {
3929
4000
  this.el = el;
3930
4001
  this.__attachView(view);
@@ -4079,7 +4150,12 @@ var ViewHook = class _ViewHook {
4079
4150
  }
4080
4151
  );
4081
4152
  const promises = targetPair.map(({ view, targetCtx }) => {
4082
- return view.pushHookEvent(this.el, targetCtx, event, payload || {});
4153
+ return view.pushHookEvent(
4154
+ this.el,
4155
+ targetCtx,
4156
+ event,
4157
+ payload || {}
4158
+ );
4083
4159
  });
4084
4160
  return Promise.allSettled(promises);
4085
4161
  }
@@ -4131,7 +4207,7 @@ var ViewHook = class _ViewHook {
4131
4207
  }
4132
4208
  };
4133
4209
 
4134
- // js/phoenix_live_view/view.js
4210
+ // js/phoenix_live_view/view.ts
4135
4211
  var prependFormDataKey = (key, prefix) => {
4136
4212
  const isArray = key.endsWith("[]");
4137
4213
  let baseKey = isArray ? key.slice(0, -2) : key;
@@ -4146,7 +4222,8 @@ var View = class _View {
4146
4222
  const liveViewEl = el.closest(PHX_VIEW_SELECTOR);
4147
4223
  return liveViewEl ? dom_default.private(liveViewEl, "view") : null;
4148
4224
  }
4149
- constructor(el, liveSocket, parentView, flash, liveReferer) {
4225
+ constructor(el, liveSocket, parentView, flash = null, liveReferer = null) {
4226
+ this.rendered = null;
4150
4227
  this.isDead = false;
4151
4228
  this.liveSocket = liveSocket;
4152
4229
  this.flash = flash;
@@ -4202,7 +4279,7 @@ var View = class _View {
4202
4279
  params: this.connectParams(liveReferer),
4203
4280
  session: this.getSession(),
4204
4281
  static: this.getStatic(),
4205
- flash: this.flash,
4282
+ flash: this.flash ?? void 0,
4206
4283
  sticky: this.el.hasAttribute(PHX_STICKY)
4207
4284
  };
4208
4285
  });
@@ -4220,13 +4297,15 @@ var View = class _View {
4220
4297
  }
4221
4298
  connectParams(liveReferer) {
4222
4299
  const params = this.liveSocket.params(this.el);
4223
- const manifest = dom_default.all(document, `[${this.binding(PHX_TRACK_STATIC)}]`).map((node) => node.src || node.href).filter((url) => typeof url === "string");
4300
+ const manifest = dom_default.all(document, `[${this.binding(PHX_TRACK_STATIC)}]`).map(
4301
+ (node) => "src" in node && node.src || "href" in node && node.href
4302
+ ).filter((url) => typeof url === "string");
4224
4303
  if (manifest.length > 0) {
4225
4304
  params["_track_static"] = manifest;
4226
4305
  }
4227
4306
  params["_mounts"] = this.joinCount;
4228
4307
  params["_mount_attempts"] = this.joinAttempts;
4229
- params["_live_referer"] = liveReferer;
4308
+ params["_live_referer"] = liveReferer ?? void 0;
4230
4309
  this.joinAttempts++;
4231
4310
  return params;
4232
4311
  }
@@ -4250,7 +4329,7 @@ var View = class _View {
4250
4329
  if (this.parent) {
4251
4330
  delete this.root.children[this.parent.id][this.id];
4252
4331
  }
4253
- clearTimeout(this.loaderTimer);
4332
+ this.loaderTimer != null && clearTimeout(this.loaderTimer);
4254
4333
  const onFinished = () => {
4255
4334
  callback();
4256
4335
  for (const id in this.viewHooks) {
@@ -4272,7 +4351,7 @@ var View = class _View {
4272
4351
  this.el.classList.add(...classes);
4273
4352
  }
4274
4353
  showLoader(timeout) {
4275
- clearTimeout(this.loaderTimer);
4354
+ this.loaderTimer != null && clearTimeout(this.loaderTimer);
4276
4355
  if (timeout) {
4277
4356
  this.loaderTimer = setTimeout(() => this.showLoader(), timeout);
4278
4357
  } else {
@@ -4290,8 +4369,8 @@ var View = class _View {
4290
4369
  );
4291
4370
  }
4292
4371
  hideLoader() {
4293
- clearTimeout(this.loaderTimer);
4294
- clearTimeout(this.disconnectedTimer);
4372
+ this.loaderTimer != null && clearTimeout(this.loaderTimer);
4373
+ this.disconnectedTimer != null && clearTimeout(this.disconnectedTimer);
4295
4374
  this.setContainerClasses(PHX_CONNECTED_CLASS);
4296
4375
  this.execAll(this.binding("connected"));
4297
4376
  }
@@ -4321,11 +4400,14 @@ var View = class _View {
4321
4400
  );
4322
4401
  }
4323
4402
  if (isCid(phxTarget)) {
4324
- const targets = dom_default.findComponentNodeList(this.id, phxTarget, dom);
4325
- if (targets.length === 0) {
4403
+ const target = dom_default.findComponent(this.id, phxTarget, dom);
4404
+ if (!target) {
4326
4405
  logError(`no component found matching phx-target of ${phxTarget}`);
4327
4406
  } else {
4328
- callback(this, parseInt(phxTarget));
4407
+ callback(
4408
+ this,
4409
+ typeof phxTarget === "number" ? phxTarget : parseInt(phxTarget)
4410
+ );
4329
4411
  }
4330
4412
  } else {
4331
4413
  const targets = Array.from(dom.querySelectorAll(phxTarget));
@@ -4452,7 +4534,11 @@ var View = class _View {
4452
4534
  }
4453
4535
  }
4454
4536
  attachTrueDocEl() {
4455
- this.el = dom_default.byId(this.id);
4537
+ const el = dom_default.byId(this.id);
4538
+ if (!el) {
4539
+ throw new Error("unable to find root element for view");
4540
+ }
4541
+ this.el = el;
4456
4542
  this.el.setAttribute(PHX_ROOT_ID, this.root.id);
4457
4543
  }
4458
4544
  // this is invoked for dead and live views, so we must filter by
@@ -4501,7 +4587,7 @@ var View = class _View {
4501
4587
  }
4502
4588
  }
4503
4589
  this.attachTrueDocEl();
4504
- const patch = new DOMPatch(this, this.el, this.id, html, streams, null);
4590
+ const patch = new DOMPatch(this, this.el, html, streams, null);
4505
4591
  patch.markPrunableContentForRemoval();
4506
4592
  this.performPatch(patch, false, true);
4507
4593
  this.joinNewChildren();
@@ -4547,7 +4633,7 @@ var View = class _View {
4547
4633
  let phxChildrenAdded = false;
4548
4634
  const updatedHookIds = /* @__PURE__ */ new Set();
4549
4635
  this.liveSocket.triggerDOM("onPatchStart", [patch.targetContainer]);
4550
- patch.after("added", (el) => {
4636
+ patch.afterAdded((el) => {
4551
4637
  this.liveSocket.triggerDOM("onNodeAdded", [el]);
4552
4638
  const phxViewportTop = this.binding(PHX_VIEWPORT_TOP);
4553
4639
  const phxViewportBottom = this.binding(PHX_VIEWPORT_BOTTOM);
@@ -4557,33 +4643,32 @@ var View = class _View {
4557
4643
  this.maybeMounted(el);
4558
4644
  }
4559
4645
  });
4560
- patch.after("phxChildAdded", (el) => {
4646
+ patch.afterPhxChildAdded((el) => {
4561
4647
  if (dom_default.isPhxSticky(el)) {
4562
4648
  this.liveSocket.joinRootViews();
4563
4649
  } else {
4564
4650
  phxChildrenAdded = true;
4565
4651
  }
4566
4652
  });
4567
- patch.before("updated", (fromEl, toEl) => {
4653
+ patch.beforeUpdated((fromEl, toEl) => {
4568
4654
  const hook = this.triggerBeforeUpdateHook(fromEl, toEl);
4569
4655
  if (hook) {
4570
4656
  updatedHookIds.add(fromEl.id);
4571
4657
  }
4572
4658
  js_default.onBeforeElUpdated(fromEl, toEl);
4573
4659
  });
4574
- patch.after("updated", (el) => {
4660
+ patch.afterUpdated((el) => {
4575
4661
  if (updatedHookIds.has(el.id)) {
4576
4662
  const hook = this.getHook(el);
4577
4663
  hook && hook.__updated();
4578
4664
  }
4579
4665
  });
4580
- patch.after("discarded", (el) => {
4666
+ patch.afterDiscarded((el) => {
4581
4667
  if (el.nodeType === Node.ELEMENT_NODE) {
4582
4668
  removedEls.push(el);
4583
4669
  }
4584
4670
  });
4585
- patch.after(
4586
- "transitionsDiscarded",
4671
+ patch.afterTransitionsDiscarded(
4587
4672
  (els) => this.afterElementsRemoved(els, pruneCids)
4588
4673
  );
4589
4674
  patch.perform(isJoinPatch);
@@ -4625,8 +4710,14 @@ var View = class _View {
4625
4710
  const oldForms = this.root.formsForRecovery;
4626
4711
  const template = document.createElement("template");
4627
4712
  template.innerHTML = html;
4713
+ if (!template.content.firstElementChild) {
4714
+ return;
4715
+ }
4628
4716
  dom_default.all(template.content, `[${PHX_PORTAL}]`).forEach((portalTemplate) => {
4629
- template.content.firstElementChild.appendChild(
4717
+ if (!(portalTemplate instanceof HTMLTemplateElement)) {
4718
+ return;
4719
+ }
4720
+ template.content.firstElementChild?.appendChild(
4630
4721
  portalTemplate.content.firstElementChild
4631
4722
  );
4632
4723
  });
@@ -4634,8 +4725,8 @@ var View = class _View {
4634
4725
  rootEl.id = this.id;
4635
4726
  rootEl.setAttribute(PHX_ROOT_ID, this.root.id);
4636
4727
  rootEl.setAttribute(PHX_SESSION, this.getSession());
4637
- rootEl.setAttribute(PHX_STATIC, this.getStatic());
4638
- rootEl.setAttribute(PHX_PARENT_ID, this.parent ? this.parent.id : null);
4728
+ rootEl.setAttribute(PHX_STATIC, this.getStatic() ?? "");
4729
+ this.parent && rootEl.setAttribute(PHX_PARENT_ID, this.parent.id);
4639
4730
  const formsToRecover = (
4640
4731
  // we go over all forms in the new DOM; because this is only the HTML for the current
4641
4732
  // view, we can be sure that all forms are owned by this view:
@@ -4670,7 +4761,7 @@ var View = class _View {
4670
4761
  if (el.id === this.id) {
4671
4762
  return this;
4672
4763
  } else {
4673
- return this.children[el.getAttribute(PHX_PARENT_ID)]?.[el.id];
4764
+ return this.children && this.children[el.getAttribute(PHX_PARENT_ID)]?.[el.id];
4674
4765
  }
4675
4766
  }
4676
4767
  destroyDescendent(id) {
@@ -4744,7 +4835,7 @@ var View = class _View {
4744
4835
  } else if (!isEmpty(diff)) {
4745
4836
  this.liveSocket.time("full patch complete", () => {
4746
4837
  const [html, streams] = this.renderContainer(diff, "update");
4747
- const patch = new DOMPatch(this, this.el, this.id, html, streams, null);
4838
+ const patch = new DOMPatch(this, this.el, html, streams, null);
4748
4839
  phxChildrenAdded = this.performPatch(patch, true);
4749
4840
  });
4750
4841
  }
@@ -4766,7 +4857,7 @@ var View = class _View {
4766
4857
  if (isEmpty(diff))
4767
4858
  return false;
4768
4859
  const { buffer: html, streams } = this.rendered.componentToString(cid);
4769
- const patch = new DOMPatch(this, this.el, this.id, html, streams, cid);
4860
+ const patch = new DOMPatch(this, this.el, html, streams, cid);
4770
4861
  const childrenAdded = this.performPatch(patch, true);
4771
4862
  return childrenAdded;
4772
4863
  }
@@ -4896,11 +4987,12 @@ var View = class _View {
4896
4987
  expandURL(to) {
4897
4988
  return to.startsWith("/") ? `${window.location.protocol}//${window.location.host}${to}` : to;
4898
4989
  }
4899
- /**
4900
- * @param {{to: string, flash?: string, reloadToken?: string}} redirect
4901
- */
4902
- onRedirect({ to, flash, reloadToken }) {
4903
- this.liveSocket.redirect(to, flash, reloadToken);
4990
+ onRedirect({
4991
+ to,
4992
+ flash,
4993
+ reloadToken
4994
+ }) {
4995
+ this.liveSocket.redirect(to, flash ?? null, reloadToken ?? null);
4904
4996
  }
4905
4997
  isDestroyed() {
4906
4998
  return this.destroyed;
@@ -4908,10 +5000,6 @@ var View = class _View {
4908
5000
  joinDead() {
4909
5001
  this.isDead = true;
4910
5002
  }
4911
- joinPush() {
4912
- this.joinPush = this.joinPush || this.channel.join();
4913
- return this.joinPush;
4914
- }
4915
5003
  join(callback) {
4916
5004
  this.showLoader(this.liveSocket.loaderTimeout);
4917
5005
  this.bindChannel();
@@ -4933,6 +5021,9 @@ var View = class _View {
4933
5021
  });
4934
5022
  }
4935
5023
  onJoinError(resp) {
5024
+ if (resp.events) {
5025
+ this.liveSocket.dispatchEvents(resp.events);
5026
+ }
4936
5027
  if (resp.reason === "reload") {
4937
5028
  this.log("error", () => [
4938
5029
  `failed mount with ${resp.status}. Falling back to page reload`,
@@ -5150,7 +5241,7 @@ var View = class _View {
5150
5241
  undoElRef(el, ref, phxEvent) {
5151
5242
  const elRef = new ElementRef(el);
5152
5243
  elRef.maybeUndo(ref, phxEvent, (clonedTree) => {
5153
- const patch = new DOMPatch(this, el, this.id, clonedTree, [], null, {
5244
+ const patch = new DOMPatch(this, el, clonedTree, /* @__PURE__ */ new Set(), null, {
5154
5245
  undoRef: ref
5155
5246
  });
5156
5247
  const phxChildrenAdded = this.performPatch(patch, true);
@@ -5182,10 +5273,10 @@ var View = class _View {
5182
5273
  }
5183
5274
  el.setAttribute(PHX_REF_SRC, this.refSrc());
5184
5275
  if (loading) {
5185
- el.setAttribute(PHX_REF_LOADING, newRef);
5276
+ el.setAttribute(PHX_REF_LOADING, newRef.toString());
5186
5277
  }
5187
5278
  if (lock) {
5188
- el.setAttribute(PHX_REF_LOCK, newRef);
5279
+ el.setAttribute(PHX_REF_LOCK, newRef.toString());
5189
5280
  }
5190
5281
  if (!loading || opts.submitter && !(el === opts.submitter || el === opts.form)) {
5191
5282
  continue;
@@ -5206,14 +5297,14 @@ var View = class _View {
5206
5297
  const disableText = el.getAttribute(disableWith);
5207
5298
  if (disableText !== null) {
5208
5299
  if (!el.getAttribute(PHX_DISABLE_WITH_RESTORE)) {
5209
- el.setAttribute(PHX_DISABLE_WITH_RESTORE, el.textContent);
5300
+ el.setAttribute(PHX_DISABLE_WITH_RESTORE, el.textContent || "");
5210
5301
  }
5211
5302
  if (disableText !== "") {
5212
5303
  el.textContent = disableText;
5213
5304
  }
5214
5305
  el.setAttribute(
5215
5306
  PHX_DISABLED,
5216
- el.getAttribute(PHX_DISABLED) || el.disabled
5307
+ el.getAttribute(PHX_DISABLED) || ("disabled" in el ? String(el.disabled) : "")
5217
5308
  );
5218
5309
  el.setAttribute("disabled", "");
5219
5310
  }
@@ -5287,7 +5378,7 @@ var View = class _View {
5287
5378
  }
5288
5379
  const cidOrSelector = opts.target || target.getAttribute(this.binding("target"));
5289
5380
  if (isCid(cidOrSelector)) {
5290
- return parseInt(cidOrSelector);
5381
+ return typeof cidOrSelector === "number" ? cidOrSelector : parseInt(cidOrSelector);
5291
5382
  } else if (targetCtx && (cidOrSelector !== null || opts.target)) {
5292
5383
  return this.closestComponentID(targetCtx);
5293
5384
  } else {
@@ -5337,7 +5428,9 @@ var View = class _View {
5337
5428
  event,
5338
5429
  value: payload,
5339
5430
  cid: this.closestComponentID(targetCtx)
5340
- }).then(({ resp: _resp, reply, ref }) => ({ reply, ref }));
5431
+ }).then(
5432
+ ({ resp: _resp, reply, ref }) => ({ reply, ref })
5433
+ );
5341
5434
  }
5342
5435
  extractMeta(el, meta, value) {
5343
5436
  const prefix = this.binding("value-");
@@ -5369,7 +5462,7 @@ var View = class _View {
5369
5462
  }
5370
5463
  return meta;
5371
5464
  }
5372
- serializeForm(form, opts, onlyNames = []) {
5465
+ serializeForm(form, opts = {}, onlyNames = []) {
5373
5466
  const { submitter } = opts;
5374
5467
  let injectedElement;
5375
5468
  if (submitter && submitter.name) {
@@ -5395,6 +5488,9 @@ var View = class _View {
5395
5488
  const params = new URLSearchParams();
5396
5489
  const { inputsUnused, onlyHiddenInputs } = Array.from(form.elements).reduce(
5397
5490
  (acc, input) => {
5491
+ if (!dom_default.isFormAssociated(input)) {
5492
+ return acc;
5493
+ }
5398
5494
  const { inputsUnused: inputsUnused2, onlyHiddenInputs: onlyHiddenInputs2 } = acc;
5399
5495
  const key = input.name;
5400
5496
  if (!key) {
@@ -5583,27 +5679,30 @@ var View = class _View {
5583
5679
  return el.hasAttribute(this.binding(PHX_DISABLE_WITH));
5584
5680
  };
5585
5681
  const filterButton = (el) => el.tagName == "BUTTON";
5586
- const filterInput = (el) => ["INPUT", "TEXTAREA", "SELECT"].includes(el.tagName);
5682
+ const filterInput = (el) => ["INPUT", "TEXTAREA"].includes(el.tagName);
5587
5683
  const formElements = Array.from(formEl.elements);
5588
5684
  const disables = formElements.filter(filterDisables);
5589
5685
  const buttons = formElements.filter(filterButton).filter(filterIgnored);
5590
5686
  const inputs = formElements.filter(filterInput).filter(filterIgnored);
5591
5687
  buttons.forEach((button) => {
5592
- button.setAttribute(PHX_DISABLED, button.disabled);
5688
+ button.setAttribute(PHX_DISABLED, button.disabled.toString());
5593
5689
  button.disabled = true;
5594
5690
  });
5595
5691
  inputs.forEach((input) => {
5596
- input.setAttribute(PHX_READONLY, input.readOnly);
5692
+ input.setAttribute(PHX_READONLY, input.readOnly.toString());
5597
5693
  input.readOnly = true;
5598
- if (input.files) {
5599
- input.setAttribute(PHX_DISABLED, input.disabled);
5694
+ if (input instanceof HTMLInputElement && input.files) {
5695
+ input.setAttribute(PHX_DISABLED, input.disabled.toString());
5600
5696
  input.disabled = true;
5601
5697
  }
5602
5698
  });
5603
5699
  const formEls = disables.concat(buttons).concat(inputs).map((el) => {
5604
5700
  return { el, loading: true, lock: true };
5605
5701
  });
5606
- const els = [{ el: formEl, loading: true, lock: false }].concat(formEls).reverse();
5702
+ const els = [
5703
+ { el: formEl, loading: true, lock: false },
5704
+ ...formEls
5705
+ ].reverse();
5607
5706
  return this.putRef(els, phxEvent, "submit", opts);
5608
5707
  }
5609
5708
  pushFormSubmit(formEl, targetCtx, phxEvent, submitter, opts, onReply) {
@@ -5735,7 +5834,7 @@ var View = class _View {
5735
5834
  }
5736
5835
  targetCtxElement(targetCtx) {
5737
5836
  if (isCid(targetCtx)) {
5738
- const [target] = dom_default.findComponentNodeList(this.id, targetCtx);
5837
+ const target = dom_default.findComponent(this.id, targetCtx);
5739
5838
  return target;
5740
5839
  } else if (targetCtx) {
5741
5840
  return targetCtx;
@@ -5748,7 +5847,7 @@ var View = class _View {
5748
5847
  const phxTarget = newForm.getAttribute(this.binding("target")) || newForm;
5749
5848
  const phxEvent = newForm.getAttribute(this.binding(PHX_AUTO_RECOVER)) || newForm.getAttribute(this.binding("change"));
5750
5849
  const inputs = Array.from(oldForm.elements).filter(
5751
- (el) => dom_default.isFormInput(el) && el.name && !el.hasAttribute(phxChange)
5850
+ (el) => dom_default.isFormAssociated(el) && el.name && !el.hasAttribute(phxChange)
5752
5851
  );
5753
5852
  if (inputs.length === 0) {
5754
5853
  callback();
@@ -5794,7 +5893,7 @@ var View = class _View {
5794
5893
  null,
5795
5894
  "click"
5796
5895
  ) : null;
5797
- const fallback = () => this.liveSocket.redirect(window.location.href);
5896
+ const fallback = () => this.liveSocket.redirect(window.location.href, null, null);
5798
5897
  const url = href.startsWith("/") ? `${location.protocol}//${location.host}${href}` : href;
5799
5898
  this.pushWithReply(refGen, "live_patch", { url }).then(
5800
5899
  ({ resp }) => {
@@ -5823,14 +5922,14 @@ var View = class _View {
5823
5922
  return dom_default.all(
5824
5923
  document,
5825
5924
  `#${CSS.escape(this.id)} form[${phxChange}], [${PHX_TELEPORTED_REF}="${CSS.escape(this.id)}"] form[${phxChange}]`
5826
- ).filter((form) => form.id).filter((form) => form.elements.length > 0).filter(
5925
+ ).filter((form) => form instanceof HTMLFormElement).filter((form) => form.id).filter((form) => form.elements.length > 0).filter(
5827
5926
  (form) => form.getAttribute(this.binding(PHX_AUTO_RECOVER)) !== "ignore"
5828
5927
  ).map((form) => {
5829
5928
  const clonedForm = form.cloneNode(true);
5830
5929
  morphdom_esm_default(clonedForm, form, {
5831
5930
  onBeforeElUpdated: (fromEl, toEl) => {
5832
5931
  dom_default.copyPrivates(fromEl, toEl);
5833
- if (fromEl.getAttribute("form") === form.id) {
5932
+ if (fromEl.getAttribute("form") === form.id && fromEl.parentNode) {
5834
5933
  fromEl.parentNode.removeChild(fromEl);
5835
5934
  return false;
5836
5935
  }
@@ -5841,10 +5940,7 @@ var View = class _View {
5841
5940
  `[form="${CSS.escape(form.id)}"]`
5842
5941
  );
5843
5942
  Array.from(externalElements).forEach((el) => {
5844
- const clonedEl = (
5845
- /** @type {HTMLElement} */
5846
- el.cloneNode(true)
5847
- );
5943
+ const clonedEl = el.cloneNode(true);
5848
5944
  morphdom_esm_default(clonedEl, el);
5849
5945
  dom_default.copyPrivates(clonedEl, el);
5850
5946
  clonedEl.removeAttribute("form");
@@ -5858,7 +5954,7 @@ var View = class _View {
5858
5954
  }
5859
5955
  maybePushComponentsDestroyed(destroyedCIDs) {
5860
5956
  let willDestroyCIDs = destroyedCIDs.filter((cid) => {
5861
- return dom_default.findComponentNodeList(this.id, cid).length === 0;
5957
+ return dom_default.findComponent(this.id, cid) === null;
5862
5958
  });
5863
5959
  const onError = (error) => {
5864
5960
  if (!this.isDestroyed()) {
@@ -5870,7 +5966,7 @@ var View = class _View {
5870
5966
  this.pushWithReply(null, "cids_will_destroy", { cids: willDestroyCIDs }).then(() => {
5871
5967
  this.liveSocket.requestDOMUpdate(() => {
5872
5968
  let completelyDestroyCIDs = willDestroyCIDs.filter((cid) => {
5873
- return dom_default.findComponentNodeList(this.id, cid).length === 0;
5969
+ return dom_default.findComponent(this.id, cid) === null;
5874
5970
  });
5875
5971
  if (completelyDestroyCIDs.length > 0) {
5876
5972
  this.pushWithReply(null, "cids_destroyed", {
@@ -5891,7 +5987,7 @@ var View = class _View {
5891
5987
  dom_default.putPrivate(form, PHX_HAS_SUBMITTED, true);
5892
5988
  const inputs = Array.from(form.elements);
5893
5989
  inputs.forEach((input) => dom_default.putPrivate(input, PHX_HAS_SUBMITTED, true));
5894
- this.liveSocket.blurActiveElement(this);
5990
+ this.liveSocket.blurActiveElement();
5895
5991
  this.pushFormSubmit(form, targetCtx, phxEvent, submitter, opts, () => {
5896
5992
  this.liveSocket.restorePreviouslyActiveFocus();
5897
5993
  });
@@ -5918,10 +6014,14 @@ var View = class _View {
5918
6014
  }
5919
6015
  };
5920
6016
 
5921
- // js/phoenix_live_view/live_socket.js
6017
+ // js/phoenix_live_view/live_socket.ts
5922
6018
  var isUsedInput = (el) => dom_default.isUsedInput(el);
5923
6019
  var LiveSocket = class {
6020
+ /**
6021
+ * Creates a new LiveSocket instance.
6022
+ */
5924
6023
  constructor(url, phxSocket, opts = {}) {
6024
+ /** @internal */
5925
6025
  this.unloaded = false;
5926
6026
  if (!phxSocket || phxSocket.constructor.name === "Object") {
5927
6027
  throw new Error(`
@@ -5934,7 +6034,6 @@ var LiveSocket = class {
5934
6034
  }
5935
6035
  this.socket = new phxSocket(url, opts);
5936
6036
  this.bindingPrefix = opts.bindingPrefix || BINDING_PREFIX;
5937
- this.opts = opts;
5938
6037
  this.params = closure(opts.params || {});
5939
6038
  this.viewLogger = opts.viewLogger;
5940
6039
  this.metadataCallbacks = opts.metadata || {};
@@ -5975,7 +6074,7 @@ var LiveSocket = class {
5975
6074
  opts.dom || {}
5976
6075
  );
5977
6076
  this.transitions = new TransitionSet();
5978
- this.currentHistoryPosition = parseInt(this.sessionStorage.getItem(PHX_LV_HISTORY_POSITION)) || 0;
6077
+ this.currentHistoryPosition = parseInt(this.sessionStorage.getItem(PHX_LV_HISTORY_POSITION) || "0") || 0;
5979
6078
  window.addEventListener("pagehide", (_e) => {
5980
6079
  this.unloaded = true;
5981
6080
  });
@@ -5986,47 +6085,94 @@ var LiveSocket = class {
5986
6085
  });
5987
6086
  }
5988
6087
  // public
6088
+ /**
6089
+ * Returns the version of the LiveView client.
6090
+ */
5989
6091
  version() {
5990
- return "1.2.0-rc.2";
6092
+ return "1.2.0";
5991
6093
  }
6094
+ /**
6095
+ * Returns true if profiling is enabled. See {@link enableProfiling} and {@link disableProfiling}.
6096
+ */
5992
6097
  isProfileEnabled() {
5993
6098
  return this.sessionStorage.getItem(PHX_LV_PROFILE) === "true";
5994
6099
  }
6100
+ /**
6101
+ * Returns true if debugging is enabled. See {@link enableDebug} and {@link disableDebug}.
6102
+ */
5995
6103
  isDebugEnabled() {
5996
6104
  return this.sessionStorage.getItem(PHX_LV_DEBUG) === "true";
5997
6105
  }
6106
+ /**
6107
+ * Returns true if debugging is disabled. See {@link enableDebug} and {@link disableDebug}.
6108
+ */
5998
6109
  isDebugDisabled() {
5999
6110
  return this.sessionStorage.getItem(PHX_LV_DEBUG) === "false";
6000
6111
  }
6112
+ /**
6113
+ * Enables debugging.
6114
+ *
6115
+ * When debugging is enabled, the LiveView client will log debug information to the console.
6116
+ * See [Debugging client events](https://phoenix-live-view.hexdocs.pm/js-interop.html#debugging-client-events) for more information.
6117
+ */
6001
6118
  enableDebug() {
6002
6119
  this.sessionStorage.setItem(PHX_LV_DEBUG, "true");
6003
6120
  }
6121
+ /**
6122
+ * Enables profiling.
6123
+ *
6124
+ * When profiling is enabled, the LiveView client will log profiling information to the console.
6125
+ */
6004
6126
  enableProfiling() {
6005
6127
  this.sessionStorage.setItem(PHX_LV_PROFILE, "true");
6006
6128
  }
6129
+ /**
6130
+ * Disables debugging.
6131
+ */
6007
6132
  disableDebug() {
6008
6133
  this.sessionStorage.setItem(PHX_LV_DEBUG, "false");
6009
6134
  }
6135
+ /**
6136
+ * Disables profiling.
6137
+ */
6010
6138
  disableProfiling() {
6011
6139
  this.sessionStorage.removeItem(PHX_LV_PROFILE);
6012
6140
  }
6141
+ /**
6142
+ * Enables latency simulation.
6143
+ *
6144
+ * When latency simulation is enabled, the LiveView client will add a delay to requests and responses from the server.
6145
+ * See [Simulating Latency](https://phoenix-live-view.hexdocs.pm/js-interop.html#simulating-latency) for more information.
6146
+ */
6013
6147
  enableLatencySim(upperBoundMs) {
6014
6148
  this.enableDebug();
6015
6149
  console.log(
6016
6150
  "latency simulator enabled for the duration of this browser session. Call disableLatencySim() to disable"
6017
6151
  );
6018
- this.sessionStorage.setItem(PHX_LV_LATENCY_SIM, upperBoundMs);
6152
+ this.sessionStorage.setItem(PHX_LV_LATENCY_SIM, upperBoundMs.toString());
6019
6153
  }
6154
+ /**
6155
+ * Disables latency simulation.
6156
+ */
6020
6157
  disableLatencySim() {
6021
6158
  this.sessionStorage.removeItem(PHX_LV_LATENCY_SIM);
6022
6159
  }
6160
+ /**
6161
+ * Returns the current latency simulation upper bound.
6162
+ */
6023
6163
  getLatencySim() {
6024
6164
  const str = this.sessionStorage.getItem(PHX_LV_LATENCY_SIM);
6025
6165
  return str ? parseInt(str) : null;
6026
6166
  }
6167
+ /**
6168
+ * Returns the Phoenix Socket instance.
6169
+ */
6027
6170
  getSocket() {
6028
6171
  return this.socket;
6029
6172
  }
6173
+ /**
6174
+ * Connects to the LiveView server.
6175
+ */
6030
6176
  connect() {
6031
6177
  if (window.location.hostname === "localhost" && !this.isDebugDisabled()) {
6032
6178
  this.enableDebug();
@@ -6049,23 +6195,29 @@ var LiveSocket = class {
6049
6195
  document.addEventListener("DOMContentLoaded", () => doConnect());
6050
6196
  }
6051
6197
  }
6198
+ /**
6199
+ * Disconnects from the LiveView server.
6200
+ */
6052
6201
  disconnect(callback) {
6053
- clearTimeout(this.reloadWithJitterTimer);
6202
+ this.reloadWithJitterTimer != null && clearTimeout(this.reloadWithJitterTimer);
6054
6203
  if (this.serverCloseRef) {
6055
- this.socket.off(this.serverCloseRef);
6204
+ this.socket.off([this.serverCloseRef]);
6056
6205
  this.serverCloseRef = null;
6057
6206
  }
6058
6207
  this.socket.disconnect(callback);
6059
6208
  }
6209
+ /**
6210
+ * Can be used to replace the transport used by the underlying Phoenix Socket.
6211
+ */
6060
6212
  replaceTransport(transport) {
6061
- clearTimeout(this.reloadWithJitterTimer);
6213
+ this.reloadWithJitterTimer != null && clearTimeout(this.reloadWithJitterTimer);
6062
6214
  this.socket.replaceTransport(transport);
6063
6215
  this.connect();
6064
6216
  }
6065
6217
  /**
6066
- * @param {HTMLElement} el
6067
- * @param {import("./js_commands").EncodedJS} encodedJS
6068
- * @param {string | null} [eventType]
6218
+ * Executes an encoded JS command, targeting the given element.
6219
+ *
6220
+ * See [`Phoenix.LiveView.JS`](https://phoenix-live-view.hexdocs.pm/Phoenix.LiveView.JS.html) for more information.
6069
6221
  */
6070
6222
  execJS(el, encodedJS, eventType = null) {
6071
6223
  const e = new CustomEvent("phx:exec", { detail: { sourceElement: el } });
@@ -6075,12 +6227,13 @@ var LiveSocket = class {
6075
6227
  * Returns an object with methods to manipulate the DOM and execute JavaScript.
6076
6228
  * The applied changes integrate with server DOM patching.
6077
6229
  *
6078
- * @returns {import("./js_commands").LiveSocketJSCommands}
6230
+ * See [JavaScript interoperability](https://phoenix-live-view.hexdocs.pm/js-interop.html) for more information.
6079
6231
  */
6080
6232
  js() {
6081
6233
  return js_commands_default(this, "js");
6082
6234
  }
6083
6235
  // private
6236
+ /** @internal */
6084
6237
  unload() {
6085
6238
  if (this.unloaded) {
6086
6239
  return;
@@ -6092,9 +6245,11 @@ var LiveSocket = class {
6092
6245
  this.destroyAllViews();
6093
6246
  this.disconnect();
6094
6247
  }
6248
+ /** @internal */
6095
6249
  triggerDOM(kind, args) {
6096
6250
  this.domCallbacks[kind](...args);
6097
6251
  }
6252
+ /** @internal */
6098
6253
  time(name, func) {
6099
6254
  if (!this.isProfileEnabled() || !console.time) {
6100
6255
  return func();
@@ -6104,6 +6259,7 @@ var LiveSocket = class {
6104
6259
  console.timeEnd(name);
6105
6260
  return result;
6106
6261
  }
6262
+ /** @internal */
6107
6263
  log(view, kind, msgCallback) {
6108
6264
  if (this.viewLogger) {
6109
6265
  const [msg, obj] = msgCallback();
@@ -6113,16 +6269,20 @@ var LiveSocket = class {
6113
6269
  debug(view, kind, msg, obj);
6114
6270
  }
6115
6271
  }
6272
+ /** @internal */
6116
6273
  requestDOMUpdate(callback) {
6117
6274
  this.transitions.after(callback);
6118
6275
  }
6276
+ /** @internal */
6119
6277
  asyncTransition(promise) {
6120
6278
  this.transitions.addAsyncTransition(promise);
6121
6279
  }
6280
+ /** @internal */
6122
6281
  transition(time, onStart, onDone = function() {
6123
6282
  }) {
6124
6283
  this.transitions.addTransition(time, onStart, onDone);
6125
6284
  }
6285
+ /** @internal */
6126
6286
  onChannel(channel, event, cb) {
6127
6287
  channel.on(event, (data) => {
6128
6288
  const latency = this.getLatencySim();
@@ -6133,8 +6293,9 @@ var LiveSocket = class {
6133
6293
  }
6134
6294
  });
6135
6295
  }
6296
+ /** @internal */
6136
6297
  reloadWithJitter(view, log) {
6137
- clearTimeout(this.reloadWithJitterTimer);
6298
+ this.reloadWithJitterTimer != null && clearTimeout(this.reloadWithJitterTimer);
6138
6299
  this.disconnect();
6139
6300
  const minMs = this.reloadJitterMin;
6140
6301
  const maxMs = this.reloadJitterMax;
@@ -6162,22 +6323,25 @@ var LiveSocket = class {
6162
6323
  `exceeded ${this.maxReloads} consecutive reloads. Entering failsafe mode`
6163
6324
  ]);
6164
6325
  }
6165
- if (this.hasPendingLink()) {
6166
- window.location = this.pendingLink;
6326
+ if (this.pendingLink !== null) {
6327
+ window.location.href = this.pendingLink;
6167
6328
  } else {
6168
6329
  window.location.reload();
6169
6330
  }
6170
6331
  }, afterMs);
6171
6332
  }
6333
+ /** @internal */
6172
6334
  getHookDefinition(name) {
6173
6335
  if (!name) {
6174
6336
  return;
6175
6337
  }
6176
6338
  return this.maybeInternalHook(name) || this.hooks[name] || this.maybeRuntimeHook(name);
6177
6339
  }
6340
+ /** @internal */
6178
6341
  maybeInternalHook(name) {
6179
6342
  return name && name.startsWith("Phoenix.") && hooks_default[name.split(".")[1]];
6180
6343
  }
6344
+ /** @internal */
6181
6345
  maybeRuntimeHook(name) {
6182
6346
  const runtimeHook = document.querySelector(
6183
6347
  `script[${PHX_RUNTIME_HOOK}="${CSS.escape(name)}"]`
@@ -6199,21 +6363,27 @@ var LiveSocket = class {
6199
6363
  runtimeHook
6200
6364
  );
6201
6365
  }
6366
+ /** @internal */
6202
6367
  isUnloaded() {
6203
6368
  return this.unloaded;
6204
6369
  }
6370
+ /** @internal */
6205
6371
  isConnected() {
6206
6372
  return this.socket.isConnected();
6207
6373
  }
6374
+ /** @internal */
6208
6375
  getBindingPrefix() {
6209
6376
  return this.bindingPrefix;
6210
6377
  }
6378
+ /** @internal */
6211
6379
  binding(kind) {
6212
6380
  return `${this.getBindingPrefix()}${kind}`;
6213
6381
  }
6382
+ /** @internal */
6214
6383
  channel(topic, params) {
6215
6384
  return this.socket.channel(topic, params);
6216
6385
  }
6386
+ /** @internal */
6217
6387
  joinDeadView() {
6218
6388
  const body = document.body;
6219
6389
  if (body && !this.isPhxView(body) && !this.isPhxView(document.firstElementChild)) {
@@ -6229,6 +6399,7 @@ var LiveSocket = class {
6229
6399
  });
6230
6400
  }
6231
6401
  }
6402
+ /** @internal */
6232
6403
  joinRootViews() {
6233
6404
  let rootsFound = false;
6234
6405
  dom_default.all(
@@ -6250,6 +6421,7 @@ var LiveSocket = class {
6250
6421
  );
6251
6422
  return rootsFound;
6252
6423
  }
6424
+ /** @internal */
6253
6425
  redirect(to, flash, reloadToken) {
6254
6426
  if (reloadToken) {
6255
6427
  browser_default.setCookie(PHX_RELOAD_STATUS, reloadToken, 60);
@@ -6257,7 +6429,11 @@ var LiveSocket = class {
6257
6429
  this.unload();
6258
6430
  browser_default.redirect(to, flash);
6259
6431
  }
6432
+ /** @internal */
6260
6433
  replaceMain(href, flash, callback = null, linkRef = this.setPendingLink(href)) {
6434
+ if (!this.main) {
6435
+ return;
6436
+ }
6261
6437
  const liveReferer = this.currentLocation.href;
6262
6438
  this.outgoingMainEl = this.outgoingMainEl || this.main.el;
6263
6439
  const stickies = dom_default.findPhxSticky(document) || [];
@@ -6284,6 +6460,7 @@ var LiveSocket = class {
6284
6460
  }
6285
6461
  });
6286
6462
  }
6463
+ /** @internal */
6287
6464
  transitionRemoves(elements, callback) {
6288
6465
  const removeAttr = this.binding("remove");
6289
6466
  const silenceEvents = (e) => {
@@ -6305,14 +6482,17 @@ var LiveSocket = class {
6305
6482
  callback && callback();
6306
6483
  });
6307
6484
  }
6485
+ /** @internal */
6308
6486
  isPhxView(el) {
6309
6487
  return el.getAttribute && el.getAttribute(PHX_SESSION) !== null;
6310
6488
  }
6489
+ /** @internal */
6311
6490
  newRootView(el, flash, liveReferer) {
6312
6491
  const view = new View(el, this, null, flash, liveReferer);
6313
6492
  this.roots[view.id] = view;
6314
6493
  return view;
6315
6494
  }
6495
+ /** @internal */
6316
6496
  owner(childEl, callback) {
6317
6497
  let view;
6318
6498
  const viewEl = dom_default.closestViewEl(childEl);
@@ -6326,9 +6506,11 @@ var LiveSocket = class {
6326
6506
  }
6327
6507
  return view && callback ? callback(view) : view;
6328
6508
  }
6509
+ /** @internal */
6329
6510
  withinOwners(childEl, callback) {
6330
6511
  this.owner(childEl, (view) => callback(view, childEl));
6331
6512
  }
6513
+ /** @internal */
6332
6514
  getViewByEl(el) {
6333
6515
  const rootId = el.getAttribute(PHX_ROOT_ID);
6334
6516
  return maybe(
@@ -6336,9 +6518,11 @@ var LiveSocket = class {
6336
6518
  (root) => root.getDescendentByEl(el)
6337
6519
  );
6338
6520
  }
6521
+ /** @internal */
6339
6522
  getRootById(id) {
6340
6523
  return this.roots[id];
6341
6524
  }
6525
+ /** @internal */
6342
6526
  destroyAllViews() {
6343
6527
  for (const id in this.roots) {
6344
6528
  this.roots[id].destroy();
@@ -6346,6 +6530,7 @@ var LiveSocket = class {
6346
6530
  }
6347
6531
  this.main = null;
6348
6532
  }
6533
+ /** @internal */
6349
6534
  destroyViewByEl(el) {
6350
6535
  const root = this.getRootById(el.getAttribute(PHX_ROOT_ID));
6351
6536
  if (root && root.id === el.id) {
@@ -6355,28 +6540,30 @@ var LiveSocket = class {
6355
6540
  root.destroyDescendent(el.id);
6356
6541
  }
6357
6542
  }
6543
+ /** @internal */
6358
6544
  getActiveElement() {
6359
6545
  return document.activeElement;
6360
6546
  }
6547
+ /** @internal */
6361
6548
  dropActiveElement(view) {
6362
6549
  if (this.prevActive && view.ownsElement(this.prevActive)) {
6363
6550
  this.prevActive = null;
6364
6551
  }
6365
6552
  }
6553
+ /** @internal */
6366
6554
  restorePreviouslyActiveFocus() {
6367
6555
  if (this.prevActive && this.prevActive !== document.body && this.prevActive instanceof HTMLElement) {
6368
6556
  this.prevActive.focus();
6369
6557
  }
6370
6558
  }
6559
+ /** @internal */
6371
6560
  blurActiveElement() {
6372
6561
  this.prevActive = this.getActiveElement();
6373
6562
  if (this.prevActive !== document.body && this.prevActive instanceof HTMLElement) {
6374
6563
  this.prevActive.blur();
6375
6564
  }
6376
6565
  }
6377
- /**
6378
- * @param {{dead?: boolean}} [options={}]
6379
- */
6566
+ /** @internal */
6380
6567
  bindTopLevelEvents({ dead } = {}) {
6381
6568
  if (this.boundTopLevelEvents) {
6382
6569
  return;
@@ -6423,7 +6610,7 @@ var LiveSocket = class {
6423
6610
  { blur: "focusout", focus: "focusin" },
6424
6611
  (e, type, view, targetEl, phxEvent, phxTarget) => {
6425
6612
  if (!phxTarget) {
6426
- const data = { key: e.key, ...this.eventMeta(type, e, targetEl) };
6613
+ const data = { ...this.eventMeta(type, e, targetEl) };
6427
6614
  js_default.exec(e, type, phxEvent, view, targetEl, ["push", { data }]);
6428
6615
  }
6429
6616
  }
@@ -6439,10 +6626,11 @@ var LiveSocket = class {
6439
6626
  );
6440
6627
  this.on("dragover", (e) => e.preventDefault());
6441
6628
  this.on("dragenter", (e) => {
6442
- const dropzone = closestPhxBinding(
6443
- e.target,
6444
- this.binding(PHX_DROP_TARGET)
6445
- );
6629
+ let target = e.target && dom_default.elementFromTarget(e.target);
6630
+ if (!target) {
6631
+ return;
6632
+ }
6633
+ const dropzone = closestPhxBinding(target, this.binding(PHX_DROP_TARGET));
6446
6634
  if (!dropzone || !(dropzone instanceof HTMLElement)) {
6447
6635
  return;
6448
6636
  }
@@ -6451,10 +6639,11 @@ var LiveSocket = class {
6451
6639
  }
6452
6640
  });
6453
6641
  this.on("dragleave", (e) => {
6454
- const dropzone = closestPhxBinding(
6455
- e.target,
6456
- this.binding(PHX_DROP_TARGET)
6457
- );
6642
+ let target = e.target && dom_default.elementFromTarget(e.target);
6643
+ if (!target) {
6644
+ return;
6645
+ }
6646
+ const dropzone = closestPhxBinding(target, this.binding(PHX_DROP_TARGET));
6458
6647
  if (!dropzone || !(dropzone instanceof HTMLElement)) {
6459
6648
  return;
6460
6649
  }
@@ -6464,15 +6653,19 @@ var LiveSocket = class {
6464
6653
  }
6465
6654
  });
6466
6655
  this.on("drop", (e) => {
6656
+ let target = e.target && dom_default.elementFromTarget(e.target);
6657
+ if (!target) {
6658
+ return;
6659
+ }
6467
6660
  e.preventDefault();
6468
- const dropzone = closestPhxBinding(
6469
- e.target,
6470
- this.binding(PHX_DROP_TARGET)
6471
- );
6661
+ const dropzone = closestPhxBinding(target, this.binding(PHX_DROP_TARGET));
6472
6662
  if (!dropzone || !(dropzone instanceof HTMLElement)) {
6473
6663
  return;
6474
6664
  }
6475
6665
  this.js().removeClass(dropzone, PHX_DROP_TARGET_ACTIVE_CLASS);
6666
+ if (!e.dataTransfer) {
6667
+ return;
6668
+ }
6476
6669
  const dropTargetId = dropzone.getAttribute(this.binding(PHX_DROP_TARGET));
6477
6670
  const dropTarget = dropTargetId && document.getElementById(dropTargetId);
6478
6671
  const files = Array.from(e.dataTransfer.files || []);
@@ -6483,7 +6676,7 @@ var LiveSocket = class {
6483
6676
  dropTarget.dispatchEvent(new Event("input", { bubbles: true }));
6484
6677
  });
6485
6678
  this.on(PHX_TRACK_UPLOADS, (e) => {
6486
- const uploadTarget = e.target;
6679
+ const uploadTarget = e.target && dom_default.elementFromTarget(e.target);
6487
6680
  if (!dom_default.isUploadInput(uploadTarget)) {
6488
6681
  return;
6489
6682
  }
@@ -6494,47 +6687,67 @@ var LiveSocket = class {
6494
6687
  uploadTarget.dispatchEvent(new Event("input", { bubbles: true }));
6495
6688
  });
6496
6689
  }
6690
+ /** @internal */
6497
6691
  eventMeta(eventName, e, targetEl) {
6498
6692
  const callback = this.metadataCallbacks[eventName];
6499
6693
  return callback ? callback(e, targetEl) : {};
6500
6694
  }
6695
+ /** @internal */
6501
6696
  setPendingLink(href) {
6502
6697
  this.linkRef++;
6503
6698
  this.pendingLink = href;
6504
6699
  this.resetReloadStatus();
6505
6700
  return this.linkRef;
6506
6701
  }
6507
- // anytime we are navigating or connecting, drop reload cookie in case
6508
- // we issue the cookie but the next request was interrupted and the server never dropped it
6702
+ /**
6703
+ * @internal
6704
+ * anytime we are navigating or connecting, drop reload cookie in case
6705
+ * we issue the cookie but the next request was interrupted and the server never dropped it
6706
+ */
6509
6707
  resetReloadStatus() {
6510
6708
  browser_default.deleteCookie(PHX_RELOAD_STATUS);
6511
6709
  }
6710
+ /** @internal */
6512
6711
  commitPendingLink(linkRef) {
6513
6712
  if (this.linkRef !== linkRef) {
6514
6713
  return false;
6515
- } else {
6714
+ }
6715
+ if (this.pendingLink !== null) {
6516
6716
  this.href = this.pendingLink;
6517
6717
  this.pendingLink = null;
6518
- return true;
6519
6718
  }
6719
+ return true;
6520
6720
  }
6721
+ /** @internal */
6521
6722
  getHref() {
6522
6723
  return this.href;
6523
6724
  }
6725
+ /** @internal */
6524
6726
  hasPendingLink() {
6525
6727
  return !!this.pendingLink;
6526
6728
  }
6729
+ /** @internal */
6527
6730
  bind(events, callback) {
6528
6731
  for (const event in events) {
6529
6732
  const browserEventName = events[event];
6530
6733
  this.on(browserEventName, (e) => {
6531
6734
  const binding = this.binding(event);
6532
6735
  const windowBinding = this.binding(`window-${event}`);
6533
- const targetPhxEvent = e.target.getAttribute && e.target.getAttribute(binding);
6736
+ const targetPhxEvent = e.target instanceof Element && e.target.getAttribute(binding);
6737
+ if (!(e.target instanceof Element)) {
6738
+ return;
6739
+ }
6534
6740
  if (targetPhxEvent) {
6535
6741
  this.debounce(e.target, e, browserEventName, () => {
6536
6742
  this.withinOwners(e.target, (view) => {
6537
- callback(e, event, view, e.target, targetPhxEvent, null);
6743
+ callback(
6744
+ e,
6745
+ event,
6746
+ view,
6747
+ e.target,
6748
+ targetPhxEvent,
6749
+ null
6750
+ );
6538
6751
  });
6539
6752
  });
6540
6753
  } else {
@@ -6542,7 +6755,14 @@ var LiveSocket = class {
6542
6755
  const phxEvent = el.getAttribute(windowBinding);
6543
6756
  this.debounce(el, e, browserEventName, () => {
6544
6757
  this.withinOwners(el, (view) => {
6545
- callback(e, event, view, el, phxEvent, "window");
6758
+ callback(
6759
+ e,
6760
+ event,
6761
+ view,
6762
+ el,
6763
+ phxEvent,
6764
+ "window"
6765
+ );
6546
6766
  });
6547
6767
  });
6548
6768
  });
@@ -6550,23 +6770,31 @@ var LiveSocket = class {
6550
6770
  });
6551
6771
  }
6552
6772
  }
6773
+ /** @internal */
6553
6774
  bindClicks() {
6554
6775
  this.on("mousedown", (e) => this.clickStartedAtTarget = e.target);
6555
- this.bindClick("click", "click");
6776
+ this.bindClick();
6556
6777
  }
6557
- bindClick(eventName, bindingName) {
6558
- const click = this.binding(bindingName);
6778
+ /** @internal */
6779
+ bindClick() {
6780
+ const click = this.binding("click");
6559
6781
  window.addEventListener(
6560
- eventName,
6782
+ "click",
6561
6783
  (e) => {
6562
- let target = null;
6784
+ let target = e.target && dom_default.elementFromTarget(e.target);
6785
+ if (!target) {
6786
+ return;
6787
+ }
6563
6788
  if (e.detail === 0)
6564
- this.clickStartedAtTarget = e.target;
6565
- const clickStartedAtTarget = this.clickStartedAtTarget || e.target;
6566
- target = closestPhxBinding(e.target, click);
6789
+ this.clickStartedAtTarget = target;
6790
+ const clickStartedAtTarget = this.clickStartedAtTarget || target;
6791
+ target = closestPhxBinding(target, click);
6567
6792
  this.dispatchClickAway(e, clickStartedAtTarget);
6568
6793
  this.clickStartedAtTarget = null;
6569
- const phxEvent = target && target.getAttribute(click);
6794
+ if (!target) {
6795
+ return;
6796
+ }
6797
+ const phxEvent = target.getAttribute(click);
6570
6798
  if (!phxEvent) {
6571
6799
  if (dom_default.isNewPageClick(e, window.location)) {
6572
6800
  this.unload();
@@ -6591,6 +6819,7 @@ var LiveSocket = class {
6591
6819
  false
6592
6820
  );
6593
6821
  }
6822
+ /** @internal */
6594
6823
  dispatchClickAway(e, clickStartedAt) {
6595
6824
  const phxClickAway = this.binding("click-away");
6596
6825
  const portal = clickStartedAt.closest(`[${PHX_TELEPORTED_SRC}]`);
@@ -6621,6 +6850,7 @@ var LiveSocket = class {
6621
6850
  }
6622
6851
  });
6623
6852
  }
6853
+ /** @internal */
6624
6854
  bindNav() {
6625
6855
  if (!browser_default.canPushState()) {
6626
6856
  return;
@@ -6630,7 +6860,7 @@ var LiveSocket = class {
6630
6860
  }
6631
6861
  let scrollTimer = null;
6632
6862
  window.addEventListener("scroll", (_e) => {
6633
- clearTimeout(scrollTimer);
6863
+ scrollTimer != null && clearTimeout(scrollTimer);
6634
6864
  scrollTimer = setTimeout(() => {
6635
6865
  browser_default.updateCurrentState(
6636
6866
  (state) => Object.assign(state, { scroll: window.scrollY })
@@ -6664,7 +6894,7 @@ var LiveSocket = class {
6664
6894
  const callback = () => {
6665
6895
  this.maybeScroll(scroll);
6666
6896
  };
6667
- if (this.main.isConnected() && navType === "patch" && id === this.main.id) {
6897
+ if (this.main && this.main.isConnected() && navType === "patch" && id === this.main.id) {
6668
6898
  this.main.pushLinkPatch(event, href, null, callback);
6669
6899
  } else {
6670
6900
  this.replaceMain(href, null, callback);
@@ -6676,13 +6906,25 @@ var LiveSocket = class {
6676
6906
  window.addEventListener(
6677
6907
  "click",
6678
6908
  (e) => {
6679
- const target = closestPhxBinding(e.target, PHX_LIVE_LINK);
6909
+ let el = e.target && dom_default.elementFromTarget(e.target);
6910
+ if (!el) {
6911
+ return;
6912
+ }
6913
+ const target = closestPhxBinding(
6914
+ el,
6915
+ PHX_LIVE_LINK
6916
+ );
6680
6917
  const type = target && target.getAttribute(PHX_LIVE_LINK);
6681
6918
  if (!type || !this.isConnected() || !this.main || dom_default.wantsNewTab(e)) {
6682
6919
  return;
6683
6920
  }
6684
6921
  const href = target.href instanceof SVGAnimatedString ? target.href.baseVal : target.href;
6685
6922
  const linkState = target.getAttribute(PHX_LINK_STATE);
6923
+ if (linkState !== "replace" && linkState !== "push") {
6924
+ throw new Error(
6925
+ `expected ${PHX_LINK_STATE} to be "replace" or "push", got: ${linkState}`
6926
+ );
6927
+ }
6686
6928
  e.preventDefault();
6687
6929
  e.stopImmediatePropagation();
6688
6930
  if (this.pendingLink === href) {
@@ -6707,6 +6949,7 @@ var LiveSocket = class {
6707
6949
  false
6708
6950
  );
6709
6951
  }
6952
+ /** @internal */
6710
6953
  maybeScroll(scroll) {
6711
6954
  if (typeof scroll === "number") {
6712
6955
  requestAnimationFrame(() => {
@@ -6714,19 +6957,23 @@ var LiveSocket = class {
6714
6957
  });
6715
6958
  }
6716
6959
  }
6960
+ /** @internal */
6717
6961
  dispatchEvent(event, payload = {}) {
6718
6962
  dom_default.dispatchEvent(window, `phx:${event}`, { detail: payload });
6719
6963
  }
6964
+ /** @internal */
6720
6965
  dispatchEvents(events) {
6721
6966
  events.forEach(([event, payload]) => this.dispatchEvent(event, payload));
6722
6967
  }
6968
+ /** @internal */
6723
6969
  withPageLoading(info, callback) {
6724
6970
  dom_default.dispatchEvent(window, "phx:page-loading-start", { detail: info });
6725
6971
  const done = () => dom_default.dispatchEvent(window, "phx:page-loading-stop", { detail: info });
6726
6972
  return callback ? callback(done) : done;
6727
6973
  }
6974
+ /** @internal */
6728
6975
  pushHistoryPatch(e, href, linkState, targetEl) {
6729
- if (!this.isConnected() || !this.main.isMain()) {
6976
+ if (!this.isConnected() || !(this.main && this.main.isMain())) {
6730
6977
  return browser_default.redirect(href);
6731
6978
  }
6732
6979
  this.withPageLoading({ to: href, kind: "patch" }, (done) => {
@@ -6736,6 +6983,7 @@ var LiveSocket = class {
6736
6983
  });
6737
6984
  });
6738
6985
  }
6986
+ /** @internal */
6739
6987
  historyPatch(href, linkState, linkRef = this.setPendingLink(href)) {
6740
6988
  if (!this.commitPendingLink(linkRef)) {
6741
6989
  return;
@@ -6760,12 +7008,13 @@ var LiveSocket = class {
6760
7008
  });
6761
7009
  this.registerNewLocation(window.location);
6762
7010
  }
7011
+ /** @internal */
6763
7012
  historyRedirect(e, href, linkState, flash, targetEl) {
6764
7013
  const clickLoading = targetEl && e.isTrusted && e.type !== "popstate";
6765
7014
  if (clickLoading) {
6766
7015
  targetEl.classList.add("phx-click-loading");
6767
7016
  }
6768
- if (!this.isConnected() || !this.main.isMain()) {
7017
+ if (!this.isConnected() || !(this.main && this.main.isMain())) {
6769
7018
  return browser_default.redirect(href, flash);
6770
7019
  }
6771
7020
  if (/^\/$|^\/[^\/]+.*$/.test(href)) {
@@ -6807,6 +7056,7 @@ var LiveSocket = class {
6807
7056
  });
6808
7057
  });
6809
7058
  }
7059
+ /** @internal */
6810
7060
  registerNewLocation(newLocation) {
6811
7061
  const { pathname, search } = this.currentLocation;
6812
7062
  if (pathname + search === newLocation.pathname + newLocation.search) {
@@ -6816,10 +7066,13 @@ var LiveSocket = class {
6816
7066
  return true;
6817
7067
  }
6818
7068
  }
7069
+ /** @internal */
6819
7070
  bindForms() {
6820
7071
  let iterations = 0;
6821
7072
  let externalFormSubmitted = false;
6822
7073
  this.on("submit", (e) => {
7074
+ if (!(e.target instanceof HTMLFormElement))
7075
+ return;
6823
7076
  const phxSubmit = e.target.getAttribute(this.binding("submit"));
6824
7077
  const phxChange = e.target.getAttribute(this.binding("change"));
6825
7078
  if (!externalFormSubmitted && phxChange && !phxSubmit) {
@@ -6837,6 +7090,8 @@ var LiveSocket = class {
6837
7090
  }
6838
7091
  });
6839
7092
  this.on("submit", (e) => {
7093
+ if (!(e.target instanceof HTMLFormElement))
7094
+ return;
6840
7095
  const phxEvent = e.target.getAttribute(this.binding("submit"));
6841
7096
  if (!phxEvent) {
6842
7097
  if (dom_default.isUnloadableFormSubmit(e)) {
@@ -6855,7 +7110,10 @@ var LiveSocket = class {
6855
7110
  });
6856
7111
  for (const type of ["change", "input"]) {
6857
7112
  this.on(type, (e) => {
6858
- if (e instanceof CustomEvent && (e.target instanceof HTMLInputElement || e.target instanceof HTMLSelectElement || e.target instanceof HTMLTextAreaElement) && e.target.form === void 0) {
7113
+ if (!dom_default.isFormAssociated(e.target)) {
7114
+ return;
7115
+ }
7116
+ if (e instanceof CustomEvent && e.target.form === void 0) {
6859
7117
  if (e.detail && e.detail.dispatcher) {
6860
7118
  throw new Error(
6861
7119
  `dispatching a custom ${type} event is only supported on input elements inside a form`
@@ -6863,9 +7121,9 @@ var LiveSocket = class {
6863
7121
  }
6864
7122
  return;
6865
7123
  }
6866
- const phxChange = this.binding("change");
6867
7124
  const input = e.target;
6868
- if (this.blockPhxChangeWhileComposing && e.isComposing) {
7125
+ const phxChange = this.binding("change");
7126
+ if (this.blockPhxChangeWhileComposing && e instanceof InputEvent && e.isComposing) {
6869
7127
  const key = `composition-listener-${type}`;
6870
7128
  if (!dom_default.private(input, key)) {
6871
7129
  dom_default.putPrivate(input, key, true);
@@ -6905,7 +7163,7 @@ var LiveSocket = class {
6905
7163
  dom_default.putPrivate(input, PHX_HAS_FOCUSED, true);
6906
7164
  js_default.exec(e, "change", phxEvent, view, input, [
6907
7165
  "push",
6908
- { _target: e.target.name, dispatcher }
7166
+ { _target: input.name, dispatcher }
6909
7167
  ]);
6910
7168
  });
6911
7169
  });
@@ -6914,7 +7172,9 @@ var LiveSocket = class {
6914
7172
  this.on("reset", (e) => {
6915
7173
  const form = e.target;
6916
7174
  dom_default.resetForm(form);
6917
- const input = Array.from(form.elements).find((el) => el.type === "reset");
7175
+ const input = Array.from(form.elements).find(
7176
+ (el) => "type" in el && el.type === "reset"
7177
+ );
6918
7178
  if (input) {
6919
7179
  window.requestAnimationFrame(() => {
6920
7180
  input.dispatchEvent(
@@ -6924,6 +7184,7 @@ var LiveSocket = class {
6924
7184
  }
6925
7185
  });
6926
7186
  }
7187
+ /** @internal */
6927
7188
  debounce(el, event, eventType, callback) {
6928
7189
  if (eventType === "blur" || eventType === "focusout") {
6929
7190
  return callback();
@@ -6948,11 +7209,13 @@ var LiveSocket = class {
6948
7209
  );
6949
7210
  });
6950
7211
  }
7212
+ /** @internal */
6951
7213
  silenceEvents(callback) {
6952
7214
  this.silenced = true;
6953
7215
  callback();
6954
7216
  this.silenced = false;
6955
7217
  }
7218
+ /** @internal */
6956
7219
  on(event, callback) {
6957
7220
  this.boundEventNames.add(event);
6958
7221
  window.addEventListener(event, (e) => {
@@ -6961,6 +7224,7 @@ var LiveSocket = class {
6961
7224
  }
6962
7225
  });
6963
7226
  }
7227
+ /** @internal */
6964
7228
  jsQuerySelectorAll(sourceEl, query, defaultQuery) {
6965
7229
  const all = this.domCallbacks.jsQuerySelectorAll;
6966
7230
  return all ? all(sourceEl, query, defaultQuery) : defaultQuery();
@@ -7022,7 +7286,6 @@ var TransitionSet = class {
7022
7286
  };
7023
7287
 
7024
7288
  // js/phoenix_live_view/index.ts
7025
- var LiveSocket2 = LiveSocket;
7026
7289
  function createHook(el, callbacks) {
7027
7290
  let existingHook = dom_default.getCustomElHook(el);
7028
7291
  if (existingHook) {