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
@@ -1,4 +1,4 @@
1
- // js/phoenix_live_view/constants.js
1
+ // js/phoenix_live_view/constants.ts
2
2
  var CONSECUTIVE_RELOADS = "consecutive-reloads";
3
3
  var MAX_RELOADS = 10;
4
4
  var RELOAD_JITTER_MIN = 5e3;
@@ -140,12 +140,12 @@ var EntryUploader = class {
140
140
  }
141
141
  this.uploadChannel.leave();
142
142
  this.errored = true;
143
- clearTimeout(this.chunkTimer);
143
+ this.chunkTimer != null && clearTimeout(this.chunkTimer);
144
144
  this.entry.error(reason);
145
145
  }
146
146
  upload() {
147
147
  this.uploadChannel.onError((reason) => this.error(reason));
148
- this.uploadChannel.join().receive("ok", (_data) => this.readNextChunk()).receive("error", (reason) => this.error(reason));
148
+ this.uploadChannel.join().receive("ok", (_data) => this.readNextChunk()).receive("error", ({ reason }) => this.error(reason));
149
149
  }
150
150
  isDone() {
151
151
  return this.offset >= this.entry.file.size;
@@ -157,7 +157,7 @@ var EntryUploader = class {
157
157
  this.chunkSize + this.offset
158
158
  );
159
159
  reader.onload = (e) => {
160
- if (e.target.error === null) {
160
+ if (e.target?.error === null) {
161
161
  this.offset += /** @type {ArrayBuffer} */
162
162
  e.target.result.byteLength;
163
163
  this.pushChunk(
@@ -165,7 +165,7 @@ var EntryUploader = class {
165
165
  e.target.result
166
166
  );
167
167
  } else {
168
- return logError("Read error: " + e.target.error);
168
+ return logError("Read error: " + e.target?.error);
169
169
  }
170
170
  };
171
171
  reader.readAsArrayBuffer(blob);
@@ -186,8 +186,23 @@ var EntryUploader = class {
186
186
  }
187
187
  };
188
188
 
189
- // js/phoenix_live_view/utils.js
189
+ // js/phoenix_live_view/utils.ts
190
190
  var logError = (msg, obj) => console.error && console.error(msg, obj);
191
+ var ensureSameOrigin = (href, kind) => {
192
+ let url;
193
+ try {
194
+ url = new URL(href, window.location.href);
195
+ } catch {
196
+ throw new Error(
197
+ `expected ${kind} destination to be a valid URL, got: ${href}`
198
+ );
199
+ }
200
+ if (url.origin !== window.location.origin) {
201
+ throw new Error(
202
+ `cannot ${kind} to "${href}" because its origin does not match the current origin "${window.location.origin}". Use window.location directly for cross-origin navigation.`
203
+ );
204
+ }
205
+ };
191
206
  var isCid = (cid) => {
192
207
  const type = typeof cid;
193
208
  return type === "number" || type === "string" && /^(0|[1-9]\d*)$/.test(cid);
@@ -228,12 +243,13 @@ var closure = (val) => typeof val === "function" ? val : function() {
228
243
  var clone = (obj) => {
229
244
  return JSON.parse(JSON.stringify(obj));
230
245
  };
231
- var closestPhxBinding = (el, binding, borderEl) => {
246
+ var closestPhxBinding = (startEl, binding, borderEl) => {
247
+ let el = startEl;
232
248
  do {
233
- if (el.matches(`[${binding}]`) && !el.disabled) {
249
+ if (el.matches(`[${binding}]`) && !("disabled" in el && el.disabled)) {
234
250
  return el;
235
251
  }
236
- el = el.parentElement || el.parentNode;
252
+ el = el.parentElement;
237
253
  } while (el !== null && el.nodeType === 1 && !(borderEl && borderEl.isSameNode(el) || el.matches(PHX_VIEW_SELECTOR)));
238
254
  return null;
239
255
  };
@@ -265,7 +281,7 @@ var eventContainsFiles = (e) => {
265
281
  return false;
266
282
  };
267
283
 
268
- // js/phoenix_live_view/browser.js
284
+ // js/phoenix_live_view/browser.ts
269
285
  var Browser = {
270
286
  canPushState() {
271
287
  return typeof history.pushState !== "undefined";
@@ -312,7 +328,7 @@ var Browser = {
312
328
  }
313
329
  });
314
330
  }
315
- } else {
331
+ } else if (to) {
316
332
  this.redirect(to);
317
333
  }
318
334
  },
@@ -329,7 +345,7 @@ var Browser = {
329
345
  deleteCookie(name) {
330
346
  document.cookie = `${name}=; max-age=-1; path=/`;
331
347
  },
332
- redirect(toURL, flash, navigate = (url) => {
348
+ redirect(toURL, flash = null, navigate = (url) => {
333
349
  window.location.href = url;
334
350
  }) {
335
351
  if (flash) {
@@ -350,11 +366,21 @@ var Browser = {
350
366
  };
351
367
  var browser_default = Browser;
352
368
 
353
- // js/phoenix_live_view/dom.js
369
+ // js/phoenix_live_view/dom.ts
354
370
  var DOM = {
355
371
  byId(id) {
356
372
  return document.getElementById(id) || logError(`no id found for ${id}`);
357
373
  },
374
+ elementFromTarget(target) {
375
+ if (!(target instanceof Node)) {
376
+ return null;
377
+ }
378
+ if (target.nodeType === Node.ELEMENT_NODE) {
379
+ return target;
380
+ } else {
381
+ return target.parentElement;
382
+ }
383
+ },
358
384
  removeClass(el, className) {
359
385
  el.classList.remove(className);
360
386
  if (el.classList.length === 0) {
@@ -392,12 +418,20 @@ var DOM = {
392
418
  inputsOutsideForm
393
419
  );
394
420
  },
395
- findComponentNodeList(viewId, cid, doc2 = document) {
396
- return this.all(
397
- doc2,
421
+ findComponent(viewId, cid, doc2 = document) {
422
+ return doc2.querySelector(
398
423
  `[${PHX_VIEW_REF}="${viewId}"][${PHX_COMPONENT}="${cid}"]`
399
424
  );
400
425
  },
426
+ getComponent(viewId, cid, doc2 = document) {
427
+ const el = this.findComponent(viewId, cid, doc2);
428
+ if (!el) {
429
+ throw new Error(
430
+ `no component found matching viewId ${viewId} and cid ${cid}`
431
+ );
432
+ }
433
+ return el;
434
+ },
401
435
  isPhxDestroyed(node) {
402
436
  return node.id && DOM.private(node, "destroyed") ? true : false;
403
437
  },
@@ -789,11 +823,23 @@ var DOM = {
789
823
  focused.setSelectionRange(selectionStart, selectionEnd);
790
824
  }
791
825
  },
792
- isFormInput(el) {
793
- if (el.localName && customElements.get(el.localName)) {
794
- return customElements.get(el.localName)[`formAssociated`];
826
+ /**
827
+ * Returns true if the element is an input that can be focused and edited by the user,
828
+ * so we can skip patching it if it has focus.
829
+ */
830
+ isEditableInput(el) {
831
+ return this.isFormAssociated(el) && !(el instanceof HTMLButtonElement) && !(el instanceof HTMLInputElement && el.type === "button");
832
+ },
833
+ isFormAssociated(el) {
834
+ if (!(el instanceof HTMLElement))
835
+ return false;
836
+ if (el.localName) {
837
+ const customEl = customElements.get(el.localName);
838
+ if (customEl) {
839
+ return customEl.formAssociated === true;
840
+ }
795
841
  }
796
- return /^(?:input|select|textarea)$/i.test(el.tagName) && el.type !== "button";
842
+ return el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement || el instanceof HTMLButtonElement;
797
843
  },
798
844
  syncAttrsToProps(el) {
799
845
  if (el instanceof HTMLInputElement && CHECKABLE_INPUTS.indexOf(el.type.toLocaleLowerCase()) >= 0) {
@@ -810,13 +856,13 @@ var DOM = {
810
856
  if (DOM.isPhxUpdate(container, phxUpdate, ["append", "prepend", PHX_STREAM])) {
811
857
  const toRemove = [];
812
858
  container.childNodes.forEach((childNode) => {
813
- if (!childNode.id) {
814
- const isEmptyTextNode = childNode.nodeType === Node.TEXT_NODE && childNode.nodeValue.trim() === "";
859
+ if (!("id" in childNode) || !childNode.id) {
860
+ const isEmptyTextNode = childNode.nodeType === Node.TEXT_NODE && childNode.nodeValue && childNode.nodeValue.trim() === "";
815
861
  if (!isEmptyTextNode && childNode.nodeType !== Node.COMMENT_NODE) {
816
862
  logError(
817
863
  `only HTML element tags with an id are allowed inside containers with phx-update.
818
864
 
819
- removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
865
+ removing illegal node: "${("outerHTML" in childNode && childNode.outerHTML || childNode.nodeValue || "").trim()}"
820
866
 
821
867
  `
822
868
  );
@@ -844,9 +890,12 @@ removing illegal node: "${(childNode.outerHTML || childNode.nodeValue).trim()}"
844
890
  Object.keys(attrs).forEach(
845
891
  (attr) => newContainer.setAttribute(attr, attrs[attr])
846
892
  );
847
- retainedAttrs.forEach(
848
- (attr) => newContainer.setAttribute(attr, container.getAttribute(attr))
849
- );
893
+ retainedAttrs.forEach((attr) => {
894
+ const value = container.getAttribute(attr);
895
+ if (value !== null) {
896
+ newContainer.setAttribute(attr, value);
897
+ }
898
+ });
850
899
  newContainer.innerHTML = container.innerHTML;
851
900
  container.replaceWith(newContainer);
852
901
  return newContainer;
@@ -1098,7 +1147,7 @@ var LiveUploader = class _LiveUploader {
1098
1147
  [],
1099
1148
  (existing) => existing.concat(newFiles)
1100
1149
  );
1101
- inputEl.value = null;
1150
+ inputEl.value = "";
1102
1151
  } else {
1103
1152
  if (dataTransfer && dataTransfer.files.length > 0) {
1104
1153
  inputEl.files = dataTransfer.files;
@@ -1181,20 +1230,20 @@ var LiveUploader = class _LiveUploader {
1181
1230
  }
1182
1231
  };
1183
1232
 
1184
- // js/phoenix_live_view/aria.js
1233
+ // js/phoenix_live_view/aria.ts
1185
1234
  var ARIA = {
1186
1235
  anyOf(instance, classes) {
1187
- return classes.find((name) => instance instanceof name);
1236
+ return classes.some((name) => instance instanceof name);
1188
1237
  },
1189
- isFocusable(el, interactiveOnly) {
1190
- return el instanceof HTMLAnchorElement && el.rel !== "ignore" || el instanceof HTMLAreaElement && el.href !== void 0 || !el.disabled && this.anyOf(el, [
1238
+ isFocusable(el, interactiveOnly = false) {
1239
+ return el instanceof HTMLAnchorElement && el.rel !== "ignore" || el instanceof HTMLAreaElement && el.href !== void 0 || !("disabled" in el && el.disabled) && this.anyOf(el, [
1191
1240
  HTMLInputElement,
1192
1241
  HTMLSelectElement,
1193
1242
  HTMLTextAreaElement,
1194
1243
  HTMLButtonElement
1195
- ]) || el instanceof HTMLIFrameElement || el.tabIndex >= 0 && el.getAttribute("aria-hidden") !== "true" || !interactiveOnly && el.getAttribute("tabindex") !== null && el.getAttribute("aria-hidden") !== "true";
1244
+ ]) || el instanceof HTMLIFrameElement || el instanceof HTMLElement && el.tabIndex >= 0 && el.getAttribute("aria-hidden") !== "true" || !interactiveOnly && el.getAttribute("tabindex") !== null && el.getAttribute("aria-hidden") !== "true";
1196
1245
  },
1197
- attemptFocus(el, interactiveOnly) {
1246
+ attemptFocus(el, interactiveOnly = false) {
1198
1247
  if (this.isFocusable(el, interactiveOnly)) {
1199
1248
  try {
1200
1249
  el.focus();
@@ -1211,6 +1260,7 @@ var ARIA = {
1211
1260
  }
1212
1261
  child = child.nextElementSibling;
1213
1262
  }
1263
+ return false;
1214
1264
  },
1215
1265
  focusFirst(el) {
1216
1266
  let child = el.firstElementChild;
@@ -1220,6 +1270,7 @@ var ARIA = {
1220
1270
  }
1221
1271
  child = child.nextElementSibling;
1222
1272
  }
1273
+ return false;
1223
1274
  },
1224
1275
  focusLast(el) {
1225
1276
  let child = el.lastElementChild;
@@ -1229,79 +1280,12 @@ var ARIA = {
1229
1280
  }
1230
1281
  child = child.previousElementSibling;
1231
1282
  }
1283
+ return false;
1232
1284
  }
1233
1285
  };
1234
1286
  var aria_default = ARIA;
1235
1287
 
1236
- // js/phoenix_live_view/hooks.js
1237
- var Hooks = {
1238
- LiveFileUpload: {
1239
- activeRefs() {
1240
- return this.el.getAttribute(PHX_ACTIVE_ENTRY_REFS);
1241
- },
1242
- preflightedRefs() {
1243
- return this.el.getAttribute(PHX_PREFLIGHTED_REFS);
1244
- },
1245
- mounted() {
1246
- this.js().ignoreAttributes(this.el, ["value"]);
1247
- this.preflightedWas = this.preflightedRefs();
1248
- },
1249
- updated() {
1250
- const newPreflights = this.preflightedRefs();
1251
- if (this.preflightedWas !== newPreflights) {
1252
- this.preflightedWas = newPreflights;
1253
- if (newPreflights === "") {
1254
- this.__view().cancelSubmit(this.el.form);
1255
- }
1256
- }
1257
- if (this.activeRefs() === "") {
1258
- this.el.value = null;
1259
- }
1260
- this.el.dispatchEvent(new CustomEvent(PHX_LIVE_FILE_UPDATED));
1261
- }
1262
- },
1263
- LiveImgPreview: {
1264
- mounted() {
1265
- this.ref = this.el.getAttribute("data-phx-entry-ref");
1266
- this.inputEl = document.getElementById(
1267
- this.el.getAttribute(PHX_UPLOAD_REF)
1268
- );
1269
- this.url = LiveUploader.getEntryDataURL(this.inputEl, this.ref);
1270
- this.el.src = this.url;
1271
- },
1272
- destroyed() {
1273
- URL.revokeObjectURL(this.url);
1274
- }
1275
- },
1276
- FocusWrap: {
1277
- mounted() {
1278
- this.focusStart = this.el.firstElementChild;
1279
- this.focusEnd = this.el.lastElementChild;
1280
- this.focusStart.addEventListener("focus", (e) => {
1281
- if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
1282
- const nextFocus = e.target.nextElementSibling;
1283
- aria_default.attemptFocus(nextFocus) || aria_default.focusFirst(nextFocus);
1284
- } else {
1285
- aria_default.focusLast(this.el);
1286
- }
1287
- });
1288
- this.focusEnd.addEventListener("focus", (e) => {
1289
- if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
1290
- const nextFocus = e.target.previousElementSibling;
1291
- aria_default.attemptFocus(nextFocus) || aria_default.focusLast(nextFocus);
1292
- } else {
1293
- aria_default.focusFirst(this.el);
1294
- }
1295
- });
1296
- if (!this.el.contains(document.activeElement)) {
1297
- this.el.addEventListener("phx:show-end", () => this.el.focus());
1298
- if (window.getComputedStyle(this.el).display !== "none") {
1299
- aria_default.focusFirst(this.el);
1300
- }
1301
- }
1302
- }
1303
- }
1304
- };
1288
+ // js/phoenix_live_view/hooks.ts
1305
1289
  var findScrollContainer = (el) => {
1306
1290
  if (["HTML", "BODY"].indexOf(el.nodeName.toUpperCase()) >= 0)
1307
1291
  return null;
@@ -1342,7 +1326,7 @@ var isWithinViewport = (el, scrollContainer) => {
1342
1326
  const rect = el.getBoundingClientRect();
1343
1327
  return Math.ceil(rect.top) >= top(scrollContainer) && Math.floor(rect.top) <= bottom(scrollContainer);
1344
1328
  };
1345
- Hooks.InfiniteScroll = {
1329
+ var InfiniteScroll = {
1346
1330
  mounted() {
1347
1331
  this.scrollContainer = findScrollContainer(this.el);
1348
1332
  let scrollBefore = scrollTop(this.scrollContainer);
@@ -1418,9 +1402,9 @@ Hooks.InfiniteScroll = {
1418
1402
  } else if (isScrollingDown && topOverran && rect.top <= 0) {
1419
1403
  topOverran = false;
1420
1404
  }
1421
- if (topEvent && isScrollingUp && isAtViewportTop(firstChild, this.scrollContainer)) {
1405
+ if (topEvent && isScrollingUp && firstChild && isAtViewportTop(firstChild, this.scrollContainer)) {
1422
1406
  onFirstChildAtTop(topEvent, firstChild);
1423
- } else if (bottomEvent && isScrollingDown && isAtViewportBottom(lastChild, this.scrollContainer)) {
1407
+ } else if (bottomEvent && isScrollingDown && lastChild && isAtViewportBottom(lastChild, this.scrollContainer)) {
1424
1408
  onLastChildAtBottom(bottomEvent, lastChild);
1425
1409
  }
1426
1410
  scrollBefore = scrollNow;
@@ -1484,9 +1468,80 @@ Hooks.InfiniteScroll = {
1484
1468
  return rect;
1485
1469
  }
1486
1470
  };
1471
+ var LiveFileUpload = {
1472
+ activeRefs() {
1473
+ return this.el.getAttribute(PHX_ACTIVE_ENTRY_REFS);
1474
+ },
1475
+ preflightedRefs() {
1476
+ return this.el.getAttribute(PHX_PREFLIGHTED_REFS);
1477
+ },
1478
+ mounted() {
1479
+ this.js().ignoreAttributes(this.el, ["value"]);
1480
+ this.preflightedWas = this.preflightedRefs();
1481
+ },
1482
+ updated() {
1483
+ const newPreflights = this.preflightedRefs();
1484
+ if (this.preflightedWas !== newPreflights) {
1485
+ this.preflightedWas = newPreflights;
1486
+ if (newPreflights === "") {
1487
+ this.__view().cancelSubmit(this.el.form);
1488
+ }
1489
+ }
1490
+ if (this.activeRefs() === "") {
1491
+ this.el.value = "";
1492
+ }
1493
+ this.el.dispatchEvent(new CustomEvent(PHX_LIVE_FILE_UPDATED));
1494
+ }
1495
+ };
1496
+ var LiveImgPreview = {
1497
+ mounted() {
1498
+ this.ref = this.el.getAttribute("data-phx-entry-ref");
1499
+ this.inputEl = document.getElementById(
1500
+ this.el.getAttribute(PHX_UPLOAD_REF)
1501
+ );
1502
+ this.url = LiveUploader.getEntryDataURL(this.inputEl, this.ref);
1503
+ this.el.src = this.url;
1504
+ },
1505
+ destroyed() {
1506
+ URL.revokeObjectURL(this.url);
1507
+ }
1508
+ };
1509
+ var Hooks = {
1510
+ LiveFileUpload,
1511
+ LiveImgPreview,
1512
+ FocusWrap: {
1513
+ mounted() {
1514
+ this.focusStart = this.el.firstElementChild;
1515
+ this.focusEnd = this.el.lastElementChild;
1516
+ this.focusStart.addEventListener("focus", (e) => {
1517
+ if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
1518
+ const nextFocus = e.target.nextElementSibling;
1519
+ aria_default.attemptFocus(nextFocus) || aria_default.focusFirst(nextFocus);
1520
+ } else {
1521
+ aria_default.focusLast(this.el);
1522
+ }
1523
+ });
1524
+ this.focusEnd.addEventListener("focus", (e) => {
1525
+ if (!e.relatedTarget || !this.el.contains(e.relatedTarget)) {
1526
+ const nextFocus = e.target.previousElementSibling;
1527
+ aria_default.attemptFocus(nextFocus) || aria_default.focusLast(nextFocus);
1528
+ } else {
1529
+ aria_default.focusFirst(this.el);
1530
+ }
1531
+ });
1532
+ if (!this.el.contains(document.activeElement)) {
1533
+ this.el.addEventListener("phx:show-end", () => this.el.focus());
1534
+ if (window.getComputedStyle(this.el).display !== "none") {
1535
+ aria_default.focusFirst(this.el);
1536
+ }
1537
+ }
1538
+ }
1539
+ },
1540
+ InfiniteScroll
1541
+ };
1487
1542
  var hooks_default = Hooks;
1488
1543
 
1489
- // js/phoenix_live_view/element_ref.js
1544
+ // js/phoenix_live_view/element_ref.ts
1490
1545
  var ElementRef = class {
1491
1546
  static onUnlock(el, callback) {
1492
1547
  if (!dom_default.isLocked(el) && !el.closest(`[${PHX_REF_LOCK}]`)) {
@@ -1582,11 +1637,11 @@ var ElementRef = class {
1582
1637
  this.el.removeAttribute(PHX_REF_LOADING);
1583
1638
  const disabledVal = this.el.getAttribute(PHX_DISABLED);
1584
1639
  const readOnlyVal = this.el.getAttribute(PHX_READONLY);
1585
- if (readOnlyVal !== null) {
1640
+ if (readOnlyVal !== null && "readOnly" in this.el) {
1586
1641
  this.el.readOnly = readOnlyVal === "true" ? true : false;
1587
1642
  this.el.removeAttribute(PHX_READONLY);
1588
1643
  }
1589
- if (disabledVal !== null) {
1644
+ if (disabledVal !== null && "disabled" in this.el) {
1590
1645
  this.el.disabled = disabledVal === "true" ? true : false;
1591
1646
  this.el.removeAttribute(PHX_DISABLED);
1592
1647
  }
@@ -1613,6 +1668,7 @@ var ElementRef = class {
1613
1668
  isLoadingUndoneBy(ref) {
1614
1669
  return this.loadingRef === null ? false : this.loadingRef <= ref;
1615
1670
  }
1671
+ /** @internal */
1616
1672
  isLockUndoneBy(ref) {
1617
1673
  return this.lockRef === null ? false : this.lockRef <= ref;
1618
1674
  }
@@ -1625,7 +1681,7 @@ var ElementRef = class {
1625
1681
  }
1626
1682
  };
1627
1683
 
1628
- // js/phoenix_live_view/dom_post_morph_restorer.js
1684
+ // js/phoenix_live_view/dom_post_morph_restorer.ts
1629
1685
  var DOMPostMorphRestorer = class {
1630
1686
  constructor(containerBefore, containerAfter, updateType) {
1631
1687
  const idsBefore = /* @__PURE__ */ new Set();
@@ -2216,47 +2272,47 @@ function morphdomFactory(morphAttrs2) {
2216
2272
  var morphdom = morphdomFactory(morphAttrs);
2217
2273
  var morphdom_esm_default = morphdom;
2218
2274
 
2219
- // js/phoenix_live_view/dom_patch.js
2275
+ // js/phoenix_live_view/dom_patch.ts
2220
2276
  var DOMPatch = class {
2221
- constructor(view, container, id, html, streams, targetCID, opts = {}) {
2277
+ constructor(view, container, html, streams, targetCID, opts = {}) {
2222
2278
  this.view = view;
2223
2279
  this.liveSocket = view.liveSocket;
2224
2280
  this.container = container;
2225
- this.id = id;
2226
2281
  this.rootID = view.root.id;
2227
2282
  this.html = html;
2228
2283
  this.streams = streams;
2229
2284
  this.streamInserts = {};
2230
2285
  this.streamComponentRestore = {};
2231
2286
  this.targetCID = targetCID;
2232
- this.cidPatch = isCid(this.targetCID);
2233
2287
  this.pendingRemoves = [];
2234
2288
  this.phxRemove = this.liveSocket.binding("remove");
2235
- this.targetContainer = this.isCIDPatch() ? this.targetCIDContainer(html) : container;
2236
- this.callbacks = {
2237
- beforeadded: [],
2238
- beforeupdated: [],
2239
- beforephxChildAdded: [],
2240
- afteradded: [],
2241
- afterupdated: [],
2242
- afterdiscarded: [],
2243
- afterphxChildAdded: [],
2244
- aftertransitionsDiscarded: []
2245
- };
2289
+ this.targetContainer = targetCID ? dom_default.getComponent(this.view.id, targetCID) : container;
2290
+ this.beforeUpdatedCallbacks = [];
2291
+ this.afterAddedCallbacks = [];
2292
+ this.afterUpdatedCallbacks = [];
2293
+ this.afterPhxChildAddedCallbacks = [];
2294
+ this.afterDiscardedCallbacks = [];
2295
+ this.afterTransitionsDiscardedCallbacks = [];
2246
2296
  this.withChildren = opts.withChildren || opts.undoRef !== void 0 || false;
2247
- this.undoRef = opts.undoRef;
2297
+ this.undoRef = opts.undoRef ?? null;
2248
2298
  }
2249
- before(kind, callback) {
2250
- this.callbacks[`before${kind}`].push(callback);
2299
+ beforeUpdated(callback) {
2300
+ this.beforeUpdatedCallbacks.push(callback);
2251
2301
  }
2252
- after(kind, callback) {
2253
- this.callbacks[`after${kind}`].push(callback);
2302
+ afterAdded(callback) {
2303
+ this.afterAddedCallbacks.push(callback);
2254
2304
  }
2255
- trackBefore(kind, ...args) {
2256
- this.callbacks[`before${kind}`].forEach((callback) => callback(...args));
2305
+ afterUpdated(callback) {
2306
+ this.afterUpdatedCallbacks.push(callback);
2257
2307
  }
2258
- trackAfter(kind, ...args) {
2259
- this.callbacks[`after${kind}`].forEach((callback) => callback(...args));
2308
+ afterPhxChildAdded(callback) {
2309
+ this.afterPhxChildAddedCallbacks.push(callback);
2310
+ }
2311
+ afterDiscarded(callback) {
2312
+ this.afterDiscardedCallbacks.push(callback);
2313
+ }
2314
+ afterTransitionsDiscarded(callback) {
2315
+ this.afterTransitionsDiscardedCallbacks.push(callback);
2260
2316
  }
2261
2317
  markPrunableContentForRemoval() {
2262
2318
  const phxUpdate = this.liveSocket.binding(PHX_UPDATE);
@@ -2271,10 +2327,7 @@ var DOMPatch = class {
2271
2327
  perform(isJoinPatch) {
2272
2328
  const { view, liveSocket, html, container } = this;
2273
2329
  let targetContainer = this.targetContainer;
2274
- if (this.isCIDPatch() && !this.targetContainer) {
2275
- return;
2276
- }
2277
- if (this.isCIDPatch()) {
2330
+ if (this.targetCID) {
2278
2331
  const closestLock = targetContainer.closest(`[${PHX_REF_LOCK}]`);
2279
2332
  if (closestLock && !closestLock.isSameNode(targetContainer)) {
2280
2333
  const clonedTree = dom_default.private(closestLock, PHX_REF_LOCK);
@@ -2306,6 +2359,8 @@ var DOMPatch = class {
2306
2359
  // another case is the recursive patch of a stream item that was kept on reset (-> onBeforeNodeAdded)
2307
2360
  childrenOnly: targetContainer2.getAttribute(PHX_COMPONENT) === null && !withChildren,
2308
2361
  getNodeKey: (node) => {
2362
+ if (!(node instanceof Element))
2363
+ return null;
2309
2364
  if (dom_default.isPhxDestroyed(node)) {
2310
2365
  return null;
2311
2366
  }
@@ -2313,9 +2368,9 @@ var DOMPatch = class {
2313
2368
  return node.id;
2314
2369
  }
2315
2370
  if (dom_default.private(node, "clientsideIdAttribute")) {
2316
- return node.getAttribute && node.getAttribute(PHX_MAGIC_ID);
2371
+ return node.getAttribute(PHX_MAGIC_ID);
2317
2372
  }
2318
- return node.id || node.getAttribute && node.getAttribute(PHX_MAGIC_ID);
2373
+ return node.id || node.getAttribute(PHX_MAGIC_ID);
2319
2374
  },
2320
2375
  // skip indexing from children when container is stream
2321
2376
  skipFromChildren: (from) => {
@@ -2336,7 +2391,7 @@ var DOMPatch = class {
2336
2391
  const nonStreamChild = Array.from(parent.children).find(
2337
2392
  (c) => !c.hasAttribute(PHX_STREAM_REF)
2338
2393
  );
2339
- parent.insertBefore(child, nonStreamChild);
2394
+ parent.insertBefore(child, nonStreamChild ?? null);
2340
2395
  } else {
2341
2396
  parent.appendChild(child);
2342
2397
  }
@@ -2346,11 +2401,13 @@ var DOMPatch = class {
2346
2401
  }
2347
2402
  },
2348
2403
  onBeforeNodeAdded: (el) => {
2404
+ if (!(el instanceof Element)) {
2405
+ return el;
2406
+ }
2349
2407
  if (this.getStreamInsert(el)?.updateOnly && !this.streamComponentRestore[el.id]) {
2350
2408
  return false;
2351
2409
  }
2352
2410
  dom_default.maintainPrivateHooks(el, el, phxViewportTop, phxViewportBottom);
2353
- this.trackBefore("added", el);
2354
2411
  let morphedEl = el;
2355
2412
  if (this.streamComponentRestore[el.id]) {
2356
2413
  morphedEl = this.streamComponentRestore[el.id];
@@ -2360,9 +2417,11 @@ var DOMPatch = class {
2360
2417
  return morphedEl;
2361
2418
  },
2362
2419
  onNodeAdded: (el) => {
2363
- if (el.getAttribute) {
2364
- this.maybeReOrderStream(el, true);
2420
+ if (!(el instanceof Element)) {
2421
+ added.push(el);
2422
+ return;
2365
2423
  }
2424
+ this.maybeReOrderStream(el, true);
2366
2425
  if (dom_default.isPortalTemplate(el)) {
2367
2426
  portalCallbacks.push(() => this.teleport(el, morph));
2368
2427
  }
@@ -2375,7 +2434,7 @@ var DOMPatch = class {
2375
2434
  externalFormTriggered = el;
2376
2435
  }
2377
2436
  if (dom_default.isPhxChild(el) && view.ownsElement(el) || dom_default.isPhxSticky(el) && view.ownsElement(el.parentNode)) {
2378
- this.trackAfter("phxChildAdded", el);
2437
+ this.trackAfterPhxChildAdded(el);
2379
2438
  }
2380
2439
  if (el.nodeName === "SCRIPT" && el.hasAttribute(PHX_RUNTIME_HOOK)) {
2381
2440
  this.handleRuntimeHook(el, source);
@@ -2384,7 +2443,10 @@ var DOMPatch = class {
2384
2443
  },
2385
2444
  onNodeDiscarded: (el) => this.onNodeDiscarded(el),
2386
2445
  onBeforeNodeDiscarded: (el) => {
2387
- if (el.getAttribute && el.getAttribute(PHX_PRUNE) !== null) {
2446
+ if (!(el instanceof Element)) {
2447
+ return true;
2448
+ }
2449
+ if (el.getAttribute(PHX_PRUNE) !== null) {
2388
2450
  return true;
2389
2451
  }
2390
2452
  if (el.parentElement !== null && el.id && dom_default.isPhxUpdate(el.parentElement, phxUpdate, [
@@ -2394,7 +2456,7 @@ var DOMPatch = class {
2394
2456
  ])) {
2395
2457
  return false;
2396
2458
  }
2397
- if (el.getAttribute && el.getAttribute(PHX_TELEPORTED_REF)) {
2459
+ if (el.getAttribute(PHX_TELEPORTED_REF)) {
2398
2460
  return false;
2399
2461
  }
2400
2462
  if (this.maybePendingRemove(el)) {
@@ -2405,7 +2467,7 @@ var DOMPatch = class {
2405
2467
  }
2406
2468
  if (dom_default.isPortalTemplate(el)) {
2407
2469
  const teleportedEl = document.getElementById(
2408
- el.content.firstElementChild.id
2470
+ el.content.firstElementChild?.id || ""
2409
2471
  );
2410
2472
  if (teleportedEl) {
2411
2473
  teleportedEl.remove();
@@ -2436,7 +2498,7 @@ var DOMPatch = class {
2436
2498
  phxViewportBottom
2437
2499
  );
2438
2500
  dom_default.cleanChildNodes(toEl, phxUpdate);
2439
- const isFocusedFormEl = focused && fromEl.isSameNode(focused) && dom_default.isFormInput(fromEl);
2501
+ const isFocusedFormEl = focused && fromEl.isSameNode(focused) && dom_default.isEditableInput(fromEl);
2440
2502
  const focusedSelectChanged = isFocusedFormEl && this.isChangedSelect(fromEl, toEl);
2441
2503
  if (this.skipCIDSibling(toEl)) {
2442
2504
  this.maybeCloneLockedElement(fromEl, isFocusedFormEl);
@@ -2457,7 +2519,7 @@ var DOMPatch = class {
2457
2519
  return false;
2458
2520
  }
2459
2521
  if (dom_default.isIgnored(fromEl, phxUpdate) || fromEl.form && fromEl.form.isSameNode(externalFormTriggered)) {
2460
- this.trackBefore("updated", fromEl, toEl);
2522
+ this.trackBeforeUpdated(fromEl, toEl);
2461
2523
  dom_default.mergeAttrs(fromEl, toEl, {
2462
2524
  isIgnored: dom_default.isIgnored(fromEl, phxUpdate)
2463
2525
  });
@@ -2487,7 +2549,7 @@ var DOMPatch = class {
2487
2549
  return false;
2488
2550
  }
2489
2551
  if (isFocusedFormEl && fromEl.type !== "hidden" && !focusedSelectChanged) {
2490
- this.trackBefore("updated", fromEl, toEl);
2552
+ this.trackBeforeUpdated(fromEl, toEl);
2491
2553
  dom_default.mergeFocusedInput(fromEl, toEl);
2492
2554
  dom_default.syncAttrsToProps(fromEl);
2493
2555
  updates.push(fromEl);
@@ -2508,15 +2570,14 @@ var DOMPatch = class {
2508
2570
  }
2509
2571
  dom_default.syncAttrsToProps(toEl);
2510
2572
  dom_default.applyStickyOperations(toEl);
2511
- this.trackBefore("updated", fromEl, toEl);
2573
+ this.trackBeforeUpdated(fromEl, toEl);
2512
2574
  return fromEl;
2513
2575
  }
2514
2576
  }
2515
2577
  };
2516
2578
  morphdom_esm_default(targetContainer2, source, morphCallbacks);
2517
2579
  };
2518
- this.trackBefore("added", container);
2519
- this.trackBefore("updated", container, container);
2580
+ this.trackBeforeUpdated(container, container);
2520
2581
  liveSocket.time("morphdom", () => {
2521
2582
  this.streams.forEach(([ref, inserts, deleteIds, reset]) => {
2522
2583
  inserts.forEach(([key, streamAt, limit, updateOnly]) => {
@@ -2552,13 +2613,14 @@ var DOMPatch = class {
2552
2613
  this.view.portalElementIds.forEach((id) => {
2553
2614
  const el = document.getElementById(id);
2554
2615
  if (el) {
2555
- const source = document.getElementById(
2556
- el.getAttribute(PHX_TELEPORTED_SRC)
2557
- );
2558
- if (!source) {
2559
- el.remove();
2560
- this.onNodeDiscarded(el);
2561
- this.view.dropPortalElementId(id);
2616
+ const srcId = el.getAttribute(PHX_TELEPORTED_SRC);
2617
+ if (srcId) {
2618
+ const source = document.getElementById(srcId);
2619
+ if (!source) {
2620
+ el.remove();
2621
+ this.onNodeDiscarded(el);
2622
+ this.view.dropPortalElementId(id);
2623
+ }
2562
2624
  }
2563
2625
  }
2564
2626
  });
@@ -2586,8 +2648,8 @@ var DOMPatch = class {
2586
2648
  () => dom_default.restoreFocus(focused, selectionStart, selectionEnd)
2587
2649
  );
2588
2650
  dom_default.dispatchEvent(document, "phx:update");
2589
- added.forEach((el) => this.trackAfter("added", el));
2590
- updates.forEach((el) => this.trackAfter("updated", el));
2651
+ added.forEach((el) => this.trackAfterAdded(el));
2652
+ updates.forEach((el) => this.trackAfterUpdated(el));
2591
2653
  this.transitionPendingRemoves();
2592
2654
  if (externalFormTriggered) {
2593
2655
  liveSocket.unload();
@@ -2609,11 +2671,29 @@ var DOMPatch = class {
2609
2671
  }
2610
2672
  return true;
2611
2673
  }
2674
+ trackBeforeUpdated(fromEl, toEl) {
2675
+ this.beforeUpdatedCallbacks.forEach((cb) => cb(fromEl, toEl));
2676
+ }
2677
+ trackAfterAdded(el) {
2678
+ this.afterAddedCallbacks.forEach((cb) => cb(el));
2679
+ }
2680
+ trackAfterUpdated(el) {
2681
+ this.afterUpdatedCallbacks.forEach((cb) => cb(el));
2682
+ }
2683
+ trackAfterPhxChildAdded(el) {
2684
+ this.afterPhxChildAddedCallbacks.forEach((cb) => cb(el));
2685
+ }
2686
+ trackAfterDiscarded(el) {
2687
+ this.afterDiscardedCallbacks.forEach((cb) => cb(el));
2688
+ }
2689
+ trackAfterTransitionsDiscarded(els) {
2690
+ this.afterTransitionsDiscardedCallbacks.forEach((cb) => cb(els));
2691
+ }
2612
2692
  onNodeDiscarded(el) {
2613
2693
  if (dom_default.isPhxChild(el) || dom_default.isPhxSticky(el)) {
2614
2694
  this.liveSocket.destroyViewByEl(el);
2615
2695
  }
2616
- this.trackAfter("discarded", el);
2696
+ this.trackAfterDiscarded(el);
2617
2697
  }
2618
2698
  maybePendingRemove(node) {
2619
2699
  if (node.getAttribute && node.getAttribute(this.phxRemove) !== null) {
@@ -2648,7 +2728,7 @@ var DOMPatch = class {
2648
2728
  (el2) => el2.setAttribute(PHX_STREAM_REF, ref)
2649
2729
  );
2650
2730
  }
2651
- maybeReOrderStream(el, isNew) {
2731
+ maybeReOrderStream(el, isNew = false) {
2652
2732
  const { ref, streamAt, reset } = this.getStreamInsert(el);
2653
2733
  if (streamAt === void 0) {
2654
2734
  return;
@@ -2704,11 +2784,13 @@ var DOMPatch = class {
2704
2784
  }
2705
2785
  maybeLimitStream(el) {
2706
2786
  const { limit } = this.getStreamInsert(el);
2707
- const children = limit !== null && Array.from(el.parentElement.children);
2708
- if (limit && limit < 0 && children.length > limit * -1) {
2709
- children.slice(0, children.length + limit).forEach((child) => this.removeStreamChildElement(child));
2710
- } else if (limit && limit >= 0 && children.length > limit) {
2711
- children.slice(limit).forEach((child) => this.removeStreamChildElement(child));
2787
+ if (limit !== null) {
2788
+ const children = Array.from(el.parentElement.children);
2789
+ if (limit < 0 && children.length > limit * -1) {
2790
+ children.slice(0, children.length + limit).forEach((child) => this.removeStreamChildElement(child));
2791
+ } else if (limit >= 0 && children.length > limit) {
2792
+ children.slice(limit).forEach((child) => this.removeStreamChildElement(child));
2793
+ }
2712
2794
  }
2713
2795
  }
2714
2796
  transitionPendingRemoves() {
@@ -2722,7 +2804,7 @@ var DOMPatch = class {
2722
2804
  }
2723
2805
  el.remove();
2724
2806
  });
2725
- this.trackAfter("transitionsDiscarded", pendingRemoves);
2807
+ this.trackAfterTransitionsDiscarded(pendingRemoves);
2726
2808
  });
2727
2809
  }
2728
2810
  }
@@ -2736,9 +2818,6 @@ var DOMPatch = class {
2736
2818
  toEl.value = fromEl.value;
2737
2819
  return !fromEl.isEqualNode(toEl);
2738
2820
  }
2739
- isCIDPatch() {
2740
- return this.cidPatch;
2741
- }
2742
2821
  skipCIDSibling(el) {
2743
2822
  return el.nodeType === Node.ELEMENT_NODE && el.hasAttribute(PHX_SKIP);
2744
2823
  }
@@ -2746,7 +2825,7 @@ var DOMPatch = class {
2746
2825
  if (!fromEl.hasAttribute(PHX_REF_SRC))
2747
2826
  return fromEl;
2748
2827
  const ref = new ElementRef(fromEl);
2749
- if (ref.lockRef === null || this.undoRef !== void 0 && ref.isLockUndoneBy(this.undoRef)) {
2828
+ if (!fromEl.hasAttribute(PHX_REF_LOCK) || this.undoRef !== null && ref.isLockUndoneBy(this.undoRef)) {
2750
2829
  return fromEl;
2751
2830
  }
2752
2831
  dom_default.applyStickyOperations(fromEl);
@@ -2757,24 +2836,10 @@ var DOMPatch = class {
2757
2836
  return isFocusedFormEl ? fromEl : clone2;
2758
2837
  }
2759
2838
  copyNestedPrivateLock(fromEl, toEl) {
2760
- if (this.undoRef === void 0 || !dom_default.private(toEl, PHX_REF_LOCK))
2839
+ if (this.undoRef === null || !dom_default.private(toEl, PHX_REF_LOCK))
2761
2840
  return;
2762
2841
  dom_default.putPrivate(fromEl, PHX_REF_LOCK, dom_default.private(toEl, PHX_REF_LOCK));
2763
2842
  }
2764
- targetCIDContainer(html) {
2765
- if (!this.isCIDPatch()) {
2766
- return;
2767
- }
2768
- const [first, ...rest] = dom_default.findComponentNodeList(
2769
- this.view.id,
2770
- this.targetCID
2771
- );
2772
- if (rest.length === 0 && dom_default.childNodeLength(html) === 1) {
2773
- return first;
2774
- } else {
2775
- return first && first.parentNode;
2776
- }
2777
- }
2778
2843
  indexOf(parent, child) {
2779
2844
  return Array.from(parent.children).indexOf(child);
2780
2845
  }
@@ -2819,7 +2884,7 @@ var DOMPatch = class {
2819
2884
  if (el.hasAttribute("nonce")) {
2820
2885
  const template = document.createElement("template");
2821
2886
  template.innerHTML = source;
2822
- nonce = template.content.querySelector(`script[${PHX_RUNTIME_HOOK}="${CSS.escape(name)}"]`).getAttribute("nonce");
2887
+ nonce = template.content.querySelector(`script[${PHX_RUNTIME_HOOK}="${CSS.escape(name)}"]`)?.getAttribute("nonce") ?? null;
2823
2888
  }
2824
2889
  const script = document.createElement("script");
2825
2890
  script.textContent = el.textContent;
@@ -3341,7 +3406,7 @@ var JS = {
3341
3406
  }
3342
3407
  if (eventType === "change") {
3343
3408
  let { newCid, _target } = args;
3344
- _target = _target || (dom_default.isFormInput(sourceEl) ? sourceEl.name : void 0);
3409
+ _target = _target || (dom_default.isFormAssociated(sourceEl) ? sourceEl.name : void 0);
3345
3410
  if (_target) {
3346
3411
  pushOpts._target = _target;
3347
3412
  }
@@ -3855,6 +3920,7 @@ var js_commands_default = (liveSocket, eventType) => {
3855
3920
  });
3856
3921
  },
3857
3922
  navigate(href, opts = {}) {
3923
+ ensureSameOrigin(href, "navigate");
3858
3924
  const customEvent = new CustomEvent("phx:exec");
3859
3925
  liveSocket.historyRedirect(
3860
3926
  customEvent,
@@ -3865,6 +3931,7 @@ var js_commands_default = (liveSocket, eventType) => {
3865
3931
  );
3866
3932
  },
3867
3933
  patch(href, opts = {}) {
3934
+ ensureSameOrigin(href, "patch");
3868
3935
  const customEvent = new CustomEvent("phx:exec");
3869
3936
  liveSocket.pushHistoryPatch(
3870
3937
  customEvent,
@@ -3887,15 +3954,19 @@ var ViewHook = class _ViewHook {
3887
3954
  get liveSocket() {
3888
3955
  return this.__liveSocket();
3889
3956
  }
3957
+ /** @internal */
3890
3958
  static makeID() {
3891
3959
  return viewHookID++;
3892
3960
  }
3961
+ /** @internal */
3893
3962
  static elementID(el) {
3894
3963
  return dom_default.private(el, HOOK_ID);
3895
3964
  }
3965
+ /** @internal */
3896
3966
  static deadHook(el) {
3897
3967
  return dom_default.private(el, DEAD_HOOK) === true;
3898
3968
  }
3969
+ /** @internal */
3899
3970
  constructor(view, el, callbacks) {
3900
3971
  this.el = el;
3901
3972
  this.__attachView(view);
@@ -4050,7 +4121,12 @@ var ViewHook = class _ViewHook {
4050
4121
  }
4051
4122
  );
4052
4123
  const promises = targetPair.map(({ view, targetCtx }) => {
4053
- return view.pushHookEvent(this.el, targetCtx, event, payload || {});
4124
+ return view.pushHookEvent(
4125
+ this.el,
4126
+ targetCtx,
4127
+ event,
4128
+ payload || {}
4129
+ );
4054
4130
  });
4055
4131
  return Promise.allSettled(promises);
4056
4132
  }
@@ -4102,7 +4178,7 @@ var ViewHook = class _ViewHook {
4102
4178
  }
4103
4179
  };
4104
4180
 
4105
- // js/phoenix_live_view/view.js
4181
+ // js/phoenix_live_view/view.ts
4106
4182
  var prependFormDataKey = (key, prefix) => {
4107
4183
  const isArray = key.endsWith("[]");
4108
4184
  let baseKey = isArray ? key.slice(0, -2) : key;
@@ -4117,7 +4193,8 @@ var View = class _View {
4117
4193
  const liveViewEl = el.closest(PHX_VIEW_SELECTOR);
4118
4194
  return liveViewEl ? dom_default.private(liveViewEl, "view") : null;
4119
4195
  }
4120
- constructor(el, liveSocket, parentView, flash, liveReferer) {
4196
+ constructor(el, liveSocket, parentView, flash = null, liveReferer = null) {
4197
+ this.rendered = null;
4121
4198
  this.isDead = false;
4122
4199
  this.liveSocket = liveSocket;
4123
4200
  this.flash = flash;
@@ -4173,7 +4250,7 @@ var View = class _View {
4173
4250
  params: this.connectParams(liveReferer),
4174
4251
  session: this.getSession(),
4175
4252
  static: this.getStatic(),
4176
- flash: this.flash,
4253
+ flash: this.flash ?? void 0,
4177
4254
  sticky: this.el.hasAttribute(PHX_STICKY)
4178
4255
  };
4179
4256
  });
@@ -4191,13 +4268,15 @@ var View = class _View {
4191
4268
  }
4192
4269
  connectParams(liveReferer) {
4193
4270
  const params = this.liveSocket.params(this.el);
4194
- const manifest = dom_default.all(document, `[${this.binding(PHX_TRACK_STATIC)}]`).map((node) => node.src || node.href).filter((url) => typeof url === "string");
4271
+ const manifest = dom_default.all(document, `[${this.binding(PHX_TRACK_STATIC)}]`).map(
4272
+ (node) => "src" in node && node.src || "href" in node && node.href
4273
+ ).filter((url) => typeof url === "string");
4195
4274
  if (manifest.length > 0) {
4196
4275
  params["_track_static"] = manifest;
4197
4276
  }
4198
4277
  params["_mounts"] = this.joinCount;
4199
4278
  params["_mount_attempts"] = this.joinAttempts;
4200
- params["_live_referer"] = liveReferer;
4279
+ params["_live_referer"] = liveReferer ?? void 0;
4201
4280
  this.joinAttempts++;
4202
4281
  return params;
4203
4282
  }
@@ -4221,7 +4300,7 @@ var View = class _View {
4221
4300
  if (this.parent) {
4222
4301
  delete this.root.children[this.parent.id][this.id];
4223
4302
  }
4224
- clearTimeout(this.loaderTimer);
4303
+ this.loaderTimer != null && clearTimeout(this.loaderTimer);
4225
4304
  const onFinished = () => {
4226
4305
  callback();
4227
4306
  for (const id in this.viewHooks) {
@@ -4243,7 +4322,7 @@ var View = class _View {
4243
4322
  this.el.classList.add(...classes);
4244
4323
  }
4245
4324
  showLoader(timeout) {
4246
- clearTimeout(this.loaderTimer);
4325
+ this.loaderTimer != null && clearTimeout(this.loaderTimer);
4247
4326
  if (timeout) {
4248
4327
  this.loaderTimer = setTimeout(() => this.showLoader(), timeout);
4249
4328
  } else {
@@ -4261,8 +4340,8 @@ var View = class _View {
4261
4340
  );
4262
4341
  }
4263
4342
  hideLoader() {
4264
- clearTimeout(this.loaderTimer);
4265
- clearTimeout(this.disconnectedTimer);
4343
+ this.loaderTimer != null && clearTimeout(this.loaderTimer);
4344
+ this.disconnectedTimer != null && clearTimeout(this.disconnectedTimer);
4266
4345
  this.setContainerClasses(PHX_CONNECTED_CLASS);
4267
4346
  this.execAll(this.binding("connected"));
4268
4347
  }
@@ -4292,11 +4371,14 @@ var View = class _View {
4292
4371
  );
4293
4372
  }
4294
4373
  if (isCid(phxTarget)) {
4295
- const targets = dom_default.findComponentNodeList(this.id, phxTarget, dom);
4296
- if (targets.length === 0) {
4374
+ const target = dom_default.findComponent(this.id, phxTarget, dom);
4375
+ if (!target) {
4297
4376
  logError(`no component found matching phx-target of ${phxTarget}`);
4298
4377
  } else {
4299
- callback(this, parseInt(phxTarget));
4378
+ callback(
4379
+ this,
4380
+ typeof phxTarget === "number" ? phxTarget : parseInt(phxTarget)
4381
+ );
4300
4382
  }
4301
4383
  } else {
4302
4384
  const targets = Array.from(dom.querySelectorAll(phxTarget));
@@ -4423,7 +4505,11 @@ var View = class _View {
4423
4505
  }
4424
4506
  }
4425
4507
  attachTrueDocEl() {
4426
- this.el = dom_default.byId(this.id);
4508
+ const el = dom_default.byId(this.id);
4509
+ if (!el) {
4510
+ throw new Error("unable to find root element for view");
4511
+ }
4512
+ this.el = el;
4427
4513
  this.el.setAttribute(PHX_ROOT_ID, this.root.id);
4428
4514
  }
4429
4515
  // this is invoked for dead and live views, so we must filter by
@@ -4472,7 +4558,7 @@ var View = class _View {
4472
4558
  }
4473
4559
  }
4474
4560
  this.attachTrueDocEl();
4475
- const patch = new DOMPatch(this, this.el, this.id, html, streams, null);
4561
+ const patch = new DOMPatch(this, this.el, html, streams, null);
4476
4562
  patch.markPrunableContentForRemoval();
4477
4563
  this.performPatch(patch, false, true);
4478
4564
  this.joinNewChildren();
@@ -4518,7 +4604,7 @@ var View = class _View {
4518
4604
  let phxChildrenAdded = false;
4519
4605
  const updatedHookIds = /* @__PURE__ */ new Set();
4520
4606
  this.liveSocket.triggerDOM("onPatchStart", [patch.targetContainer]);
4521
- patch.after("added", (el) => {
4607
+ patch.afterAdded((el) => {
4522
4608
  this.liveSocket.triggerDOM("onNodeAdded", [el]);
4523
4609
  const phxViewportTop = this.binding(PHX_VIEWPORT_TOP);
4524
4610
  const phxViewportBottom = this.binding(PHX_VIEWPORT_BOTTOM);
@@ -4528,33 +4614,32 @@ var View = class _View {
4528
4614
  this.maybeMounted(el);
4529
4615
  }
4530
4616
  });
4531
- patch.after("phxChildAdded", (el) => {
4617
+ patch.afterPhxChildAdded((el) => {
4532
4618
  if (dom_default.isPhxSticky(el)) {
4533
4619
  this.liveSocket.joinRootViews();
4534
4620
  } else {
4535
4621
  phxChildrenAdded = true;
4536
4622
  }
4537
4623
  });
4538
- patch.before("updated", (fromEl, toEl) => {
4624
+ patch.beforeUpdated((fromEl, toEl) => {
4539
4625
  const hook = this.triggerBeforeUpdateHook(fromEl, toEl);
4540
4626
  if (hook) {
4541
4627
  updatedHookIds.add(fromEl.id);
4542
4628
  }
4543
4629
  js_default.onBeforeElUpdated(fromEl, toEl);
4544
4630
  });
4545
- patch.after("updated", (el) => {
4631
+ patch.afterUpdated((el) => {
4546
4632
  if (updatedHookIds.has(el.id)) {
4547
4633
  const hook = this.getHook(el);
4548
4634
  hook && hook.__updated();
4549
4635
  }
4550
4636
  });
4551
- patch.after("discarded", (el) => {
4637
+ patch.afterDiscarded((el) => {
4552
4638
  if (el.nodeType === Node.ELEMENT_NODE) {
4553
4639
  removedEls.push(el);
4554
4640
  }
4555
4641
  });
4556
- patch.after(
4557
- "transitionsDiscarded",
4642
+ patch.afterTransitionsDiscarded(
4558
4643
  (els) => this.afterElementsRemoved(els, pruneCids)
4559
4644
  );
4560
4645
  patch.perform(isJoinPatch);
@@ -4596,8 +4681,14 @@ var View = class _View {
4596
4681
  const oldForms = this.root.formsForRecovery;
4597
4682
  const template = document.createElement("template");
4598
4683
  template.innerHTML = html;
4684
+ if (!template.content.firstElementChild) {
4685
+ return;
4686
+ }
4599
4687
  dom_default.all(template.content, `[${PHX_PORTAL}]`).forEach((portalTemplate) => {
4600
- template.content.firstElementChild.appendChild(
4688
+ if (!(portalTemplate instanceof HTMLTemplateElement)) {
4689
+ return;
4690
+ }
4691
+ template.content.firstElementChild?.appendChild(
4601
4692
  portalTemplate.content.firstElementChild
4602
4693
  );
4603
4694
  });
@@ -4605,8 +4696,8 @@ var View = class _View {
4605
4696
  rootEl.id = this.id;
4606
4697
  rootEl.setAttribute(PHX_ROOT_ID, this.root.id);
4607
4698
  rootEl.setAttribute(PHX_SESSION, this.getSession());
4608
- rootEl.setAttribute(PHX_STATIC, this.getStatic());
4609
- rootEl.setAttribute(PHX_PARENT_ID, this.parent ? this.parent.id : null);
4699
+ rootEl.setAttribute(PHX_STATIC, this.getStatic() ?? "");
4700
+ this.parent && rootEl.setAttribute(PHX_PARENT_ID, this.parent.id);
4610
4701
  const formsToRecover = (
4611
4702
  // we go over all forms in the new DOM; because this is only the HTML for the current
4612
4703
  // view, we can be sure that all forms are owned by this view:
@@ -4641,7 +4732,7 @@ var View = class _View {
4641
4732
  if (el.id === this.id) {
4642
4733
  return this;
4643
4734
  } else {
4644
- return this.children[el.getAttribute(PHX_PARENT_ID)]?.[el.id];
4735
+ return this.children && this.children[el.getAttribute(PHX_PARENT_ID)]?.[el.id];
4645
4736
  }
4646
4737
  }
4647
4738
  destroyDescendent(id) {
@@ -4715,7 +4806,7 @@ var View = class _View {
4715
4806
  } else if (!isEmpty(diff)) {
4716
4807
  this.liveSocket.time("full patch complete", () => {
4717
4808
  const [html, streams] = this.renderContainer(diff, "update");
4718
- const patch = new DOMPatch(this, this.el, this.id, html, streams, null);
4809
+ const patch = new DOMPatch(this, this.el, html, streams, null);
4719
4810
  phxChildrenAdded = this.performPatch(patch, true);
4720
4811
  });
4721
4812
  }
@@ -4737,7 +4828,7 @@ var View = class _View {
4737
4828
  if (isEmpty(diff))
4738
4829
  return false;
4739
4830
  const { buffer: html, streams } = this.rendered.componentToString(cid);
4740
- const patch = new DOMPatch(this, this.el, this.id, html, streams, cid);
4831
+ const patch = new DOMPatch(this, this.el, html, streams, cid);
4741
4832
  const childrenAdded = this.performPatch(patch, true);
4742
4833
  return childrenAdded;
4743
4834
  }
@@ -4867,11 +4958,12 @@ var View = class _View {
4867
4958
  expandURL(to) {
4868
4959
  return to.startsWith("/") ? `${window.location.protocol}//${window.location.host}${to}` : to;
4869
4960
  }
4870
- /**
4871
- * @param {{to: string, flash?: string, reloadToken?: string}} redirect
4872
- */
4873
- onRedirect({ to, flash, reloadToken }) {
4874
- this.liveSocket.redirect(to, flash, reloadToken);
4961
+ onRedirect({
4962
+ to,
4963
+ flash,
4964
+ reloadToken
4965
+ }) {
4966
+ this.liveSocket.redirect(to, flash ?? null, reloadToken ?? null);
4875
4967
  }
4876
4968
  isDestroyed() {
4877
4969
  return this.destroyed;
@@ -4879,10 +4971,6 @@ var View = class _View {
4879
4971
  joinDead() {
4880
4972
  this.isDead = true;
4881
4973
  }
4882
- joinPush() {
4883
- this.joinPush = this.joinPush || this.channel.join();
4884
- return this.joinPush;
4885
- }
4886
4974
  join(callback) {
4887
4975
  this.showLoader(this.liveSocket.loaderTimeout);
4888
4976
  this.bindChannel();
@@ -4904,6 +4992,9 @@ var View = class _View {
4904
4992
  });
4905
4993
  }
4906
4994
  onJoinError(resp) {
4995
+ if (resp.events) {
4996
+ this.liveSocket.dispatchEvents(resp.events);
4997
+ }
4907
4998
  if (resp.reason === "reload") {
4908
4999
  this.log("error", () => [
4909
5000
  `failed mount with ${resp.status}. Falling back to page reload`,
@@ -5121,7 +5212,7 @@ var View = class _View {
5121
5212
  undoElRef(el, ref, phxEvent) {
5122
5213
  const elRef = new ElementRef(el);
5123
5214
  elRef.maybeUndo(ref, phxEvent, (clonedTree) => {
5124
- const patch = new DOMPatch(this, el, this.id, clonedTree, [], null, {
5215
+ const patch = new DOMPatch(this, el, clonedTree, /* @__PURE__ */ new Set(), null, {
5125
5216
  undoRef: ref
5126
5217
  });
5127
5218
  const phxChildrenAdded = this.performPatch(patch, true);
@@ -5153,10 +5244,10 @@ var View = class _View {
5153
5244
  }
5154
5245
  el.setAttribute(PHX_REF_SRC, this.refSrc());
5155
5246
  if (loading) {
5156
- el.setAttribute(PHX_REF_LOADING, newRef);
5247
+ el.setAttribute(PHX_REF_LOADING, newRef.toString());
5157
5248
  }
5158
5249
  if (lock) {
5159
- el.setAttribute(PHX_REF_LOCK, newRef);
5250
+ el.setAttribute(PHX_REF_LOCK, newRef.toString());
5160
5251
  }
5161
5252
  if (!loading || opts.submitter && !(el === opts.submitter || el === opts.form)) {
5162
5253
  continue;
@@ -5177,14 +5268,14 @@ var View = class _View {
5177
5268
  const disableText = el.getAttribute(disableWith);
5178
5269
  if (disableText !== null) {
5179
5270
  if (!el.getAttribute(PHX_DISABLE_WITH_RESTORE)) {
5180
- el.setAttribute(PHX_DISABLE_WITH_RESTORE, el.textContent);
5271
+ el.setAttribute(PHX_DISABLE_WITH_RESTORE, el.textContent || "");
5181
5272
  }
5182
5273
  if (disableText !== "") {
5183
5274
  el.textContent = disableText;
5184
5275
  }
5185
5276
  el.setAttribute(
5186
5277
  PHX_DISABLED,
5187
- el.getAttribute(PHX_DISABLED) || el.disabled
5278
+ el.getAttribute(PHX_DISABLED) || ("disabled" in el ? String(el.disabled) : "")
5188
5279
  );
5189
5280
  el.setAttribute("disabled", "");
5190
5281
  }
@@ -5258,7 +5349,7 @@ var View = class _View {
5258
5349
  }
5259
5350
  const cidOrSelector = opts.target || target.getAttribute(this.binding("target"));
5260
5351
  if (isCid(cidOrSelector)) {
5261
- return parseInt(cidOrSelector);
5352
+ return typeof cidOrSelector === "number" ? cidOrSelector : parseInt(cidOrSelector);
5262
5353
  } else if (targetCtx && (cidOrSelector !== null || opts.target)) {
5263
5354
  return this.closestComponentID(targetCtx);
5264
5355
  } else {
@@ -5308,7 +5399,9 @@ var View = class _View {
5308
5399
  event,
5309
5400
  value: payload,
5310
5401
  cid: this.closestComponentID(targetCtx)
5311
- }).then(({ resp: _resp, reply, ref }) => ({ reply, ref }));
5402
+ }).then(
5403
+ ({ resp: _resp, reply, ref }) => ({ reply, ref })
5404
+ );
5312
5405
  }
5313
5406
  extractMeta(el, meta, value) {
5314
5407
  const prefix = this.binding("value-");
@@ -5340,7 +5433,7 @@ var View = class _View {
5340
5433
  }
5341
5434
  return meta;
5342
5435
  }
5343
- serializeForm(form, opts, onlyNames = []) {
5436
+ serializeForm(form, opts = {}, onlyNames = []) {
5344
5437
  const { submitter } = opts;
5345
5438
  let injectedElement;
5346
5439
  if (submitter && submitter.name) {
@@ -5366,6 +5459,9 @@ var View = class _View {
5366
5459
  const params = new URLSearchParams();
5367
5460
  const { inputsUnused, onlyHiddenInputs } = Array.from(form.elements).reduce(
5368
5461
  (acc, input) => {
5462
+ if (!dom_default.isFormAssociated(input)) {
5463
+ return acc;
5464
+ }
5369
5465
  const { inputsUnused: inputsUnused2, onlyHiddenInputs: onlyHiddenInputs2 } = acc;
5370
5466
  const key = input.name;
5371
5467
  if (!key) {
@@ -5554,27 +5650,30 @@ var View = class _View {
5554
5650
  return el.hasAttribute(this.binding(PHX_DISABLE_WITH));
5555
5651
  };
5556
5652
  const filterButton = (el) => el.tagName == "BUTTON";
5557
- const filterInput = (el) => ["INPUT", "TEXTAREA", "SELECT"].includes(el.tagName);
5653
+ const filterInput = (el) => ["INPUT", "TEXTAREA"].includes(el.tagName);
5558
5654
  const formElements = Array.from(formEl.elements);
5559
5655
  const disables = formElements.filter(filterDisables);
5560
5656
  const buttons = formElements.filter(filterButton).filter(filterIgnored);
5561
5657
  const inputs = formElements.filter(filterInput).filter(filterIgnored);
5562
5658
  buttons.forEach((button) => {
5563
- button.setAttribute(PHX_DISABLED, button.disabled);
5659
+ button.setAttribute(PHX_DISABLED, button.disabled.toString());
5564
5660
  button.disabled = true;
5565
5661
  });
5566
5662
  inputs.forEach((input) => {
5567
- input.setAttribute(PHX_READONLY, input.readOnly);
5663
+ input.setAttribute(PHX_READONLY, input.readOnly.toString());
5568
5664
  input.readOnly = true;
5569
- if (input.files) {
5570
- input.setAttribute(PHX_DISABLED, input.disabled);
5665
+ if (input instanceof HTMLInputElement && input.files) {
5666
+ input.setAttribute(PHX_DISABLED, input.disabled.toString());
5571
5667
  input.disabled = true;
5572
5668
  }
5573
5669
  });
5574
5670
  const formEls = disables.concat(buttons).concat(inputs).map((el) => {
5575
5671
  return { el, loading: true, lock: true };
5576
5672
  });
5577
- const els = [{ el: formEl, loading: true, lock: false }].concat(formEls).reverse();
5673
+ const els = [
5674
+ { el: formEl, loading: true, lock: false },
5675
+ ...formEls
5676
+ ].reverse();
5578
5677
  return this.putRef(els, phxEvent, "submit", opts);
5579
5678
  }
5580
5679
  pushFormSubmit(formEl, targetCtx, phxEvent, submitter, opts, onReply) {
@@ -5706,7 +5805,7 @@ var View = class _View {
5706
5805
  }
5707
5806
  targetCtxElement(targetCtx) {
5708
5807
  if (isCid(targetCtx)) {
5709
- const [target] = dom_default.findComponentNodeList(this.id, targetCtx);
5808
+ const target = dom_default.findComponent(this.id, targetCtx);
5710
5809
  return target;
5711
5810
  } else if (targetCtx) {
5712
5811
  return targetCtx;
@@ -5719,7 +5818,7 @@ var View = class _View {
5719
5818
  const phxTarget = newForm.getAttribute(this.binding("target")) || newForm;
5720
5819
  const phxEvent = newForm.getAttribute(this.binding(PHX_AUTO_RECOVER)) || newForm.getAttribute(this.binding("change"));
5721
5820
  const inputs = Array.from(oldForm.elements).filter(
5722
- (el) => dom_default.isFormInput(el) && el.name && !el.hasAttribute(phxChange)
5821
+ (el) => dom_default.isFormAssociated(el) && el.name && !el.hasAttribute(phxChange)
5723
5822
  );
5724
5823
  if (inputs.length === 0) {
5725
5824
  callback();
@@ -5765,7 +5864,7 @@ var View = class _View {
5765
5864
  null,
5766
5865
  "click"
5767
5866
  ) : null;
5768
- const fallback = () => this.liveSocket.redirect(window.location.href);
5867
+ const fallback = () => this.liveSocket.redirect(window.location.href, null, null);
5769
5868
  const url = href.startsWith("/") ? `${location.protocol}//${location.host}${href}` : href;
5770
5869
  this.pushWithReply(refGen, "live_patch", { url }).then(
5771
5870
  ({ resp }) => {
@@ -5794,14 +5893,14 @@ var View = class _View {
5794
5893
  return dom_default.all(
5795
5894
  document,
5796
5895
  `#${CSS.escape(this.id)} form[${phxChange}], [${PHX_TELEPORTED_REF}="${CSS.escape(this.id)}"] form[${phxChange}]`
5797
- ).filter((form) => form.id).filter((form) => form.elements.length > 0).filter(
5896
+ ).filter((form) => form instanceof HTMLFormElement).filter((form) => form.id).filter((form) => form.elements.length > 0).filter(
5798
5897
  (form) => form.getAttribute(this.binding(PHX_AUTO_RECOVER)) !== "ignore"
5799
5898
  ).map((form) => {
5800
5899
  const clonedForm = form.cloneNode(true);
5801
5900
  morphdom_esm_default(clonedForm, form, {
5802
5901
  onBeforeElUpdated: (fromEl, toEl) => {
5803
5902
  dom_default.copyPrivates(fromEl, toEl);
5804
- if (fromEl.getAttribute("form") === form.id) {
5903
+ if (fromEl.getAttribute("form") === form.id && fromEl.parentNode) {
5805
5904
  fromEl.parentNode.removeChild(fromEl);
5806
5905
  return false;
5807
5906
  }
@@ -5812,10 +5911,7 @@ var View = class _View {
5812
5911
  `[form="${CSS.escape(form.id)}"]`
5813
5912
  );
5814
5913
  Array.from(externalElements).forEach((el) => {
5815
- const clonedEl = (
5816
- /** @type {HTMLElement} */
5817
- el.cloneNode(true)
5818
- );
5914
+ const clonedEl = el.cloneNode(true);
5819
5915
  morphdom_esm_default(clonedEl, el);
5820
5916
  dom_default.copyPrivates(clonedEl, el);
5821
5917
  clonedEl.removeAttribute("form");
@@ -5829,7 +5925,7 @@ var View = class _View {
5829
5925
  }
5830
5926
  maybePushComponentsDestroyed(destroyedCIDs) {
5831
5927
  let willDestroyCIDs = destroyedCIDs.filter((cid) => {
5832
- return dom_default.findComponentNodeList(this.id, cid).length === 0;
5928
+ return dom_default.findComponent(this.id, cid) === null;
5833
5929
  });
5834
5930
  const onError = (error) => {
5835
5931
  if (!this.isDestroyed()) {
@@ -5841,7 +5937,7 @@ var View = class _View {
5841
5937
  this.pushWithReply(null, "cids_will_destroy", { cids: willDestroyCIDs }).then(() => {
5842
5938
  this.liveSocket.requestDOMUpdate(() => {
5843
5939
  let completelyDestroyCIDs = willDestroyCIDs.filter((cid) => {
5844
- return dom_default.findComponentNodeList(this.id, cid).length === 0;
5940
+ return dom_default.findComponent(this.id, cid) === null;
5845
5941
  });
5846
5942
  if (completelyDestroyCIDs.length > 0) {
5847
5943
  this.pushWithReply(null, "cids_destroyed", {
@@ -5862,7 +5958,7 @@ var View = class _View {
5862
5958
  dom_default.putPrivate(form, PHX_HAS_SUBMITTED, true);
5863
5959
  const inputs = Array.from(form.elements);
5864
5960
  inputs.forEach((input) => dom_default.putPrivate(input, PHX_HAS_SUBMITTED, true));
5865
- this.liveSocket.blurActiveElement(this);
5961
+ this.liveSocket.blurActiveElement();
5866
5962
  this.pushFormSubmit(form, targetCtx, phxEvent, submitter, opts, () => {
5867
5963
  this.liveSocket.restorePreviouslyActiveFocus();
5868
5964
  });
@@ -5889,10 +5985,14 @@ var View = class _View {
5889
5985
  }
5890
5986
  };
5891
5987
 
5892
- // js/phoenix_live_view/live_socket.js
5988
+ // js/phoenix_live_view/live_socket.ts
5893
5989
  var isUsedInput = (el) => dom_default.isUsedInput(el);
5894
5990
  var LiveSocket = class {
5991
+ /**
5992
+ * Creates a new LiveSocket instance.
5993
+ */
5895
5994
  constructor(url, phxSocket, opts = {}) {
5995
+ /** @internal */
5896
5996
  this.unloaded = false;
5897
5997
  if (!phxSocket || phxSocket.constructor.name === "Object") {
5898
5998
  throw new Error(`
@@ -5905,7 +6005,6 @@ var LiveSocket = class {
5905
6005
  }
5906
6006
  this.socket = new phxSocket(url, opts);
5907
6007
  this.bindingPrefix = opts.bindingPrefix || BINDING_PREFIX;
5908
- this.opts = opts;
5909
6008
  this.params = closure(opts.params || {});
5910
6009
  this.viewLogger = opts.viewLogger;
5911
6010
  this.metadataCallbacks = opts.metadata || {};
@@ -5946,7 +6045,7 @@ var LiveSocket = class {
5946
6045
  opts.dom || {}
5947
6046
  );
5948
6047
  this.transitions = new TransitionSet();
5949
- this.currentHistoryPosition = parseInt(this.sessionStorage.getItem(PHX_LV_HISTORY_POSITION)) || 0;
6048
+ this.currentHistoryPosition = parseInt(this.sessionStorage.getItem(PHX_LV_HISTORY_POSITION) || "0") || 0;
5950
6049
  window.addEventListener("pagehide", (_e) => {
5951
6050
  this.unloaded = true;
5952
6051
  });
@@ -5957,47 +6056,94 @@ var LiveSocket = class {
5957
6056
  });
5958
6057
  }
5959
6058
  // public
6059
+ /**
6060
+ * Returns the version of the LiveView client.
6061
+ */
5960
6062
  version() {
5961
- return "1.2.0-rc.2";
6063
+ return "1.2.0";
5962
6064
  }
6065
+ /**
6066
+ * Returns true if profiling is enabled. See {@link enableProfiling} and {@link disableProfiling}.
6067
+ */
5963
6068
  isProfileEnabled() {
5964
6069
  return this.sessionStorage.getItem(PHX_LV_PROFILE) === "true";
5965
6070
  }
6071
+ /**
6072
+ * Returns true if debugging is enabled. See {@link enableDebug} and {@link disableDebug}.
6073
+ */
5966
6074
  isDebugEnabled() {
5967
6075
  return this.sessionStorage.getItem(PHX_LV_DEBUG) === "true";
5968
6076
  }
6077
+ /**
6078
+ * Returns true if debugging is disabled. See {@link enableDebug} and {@link disableDebug}.
6079
+ */
5969
6080
  isDebugDisabled() {
5970
6081
  return this.sessionStorage.getItem(PHX_LV_DEBUG) === "false";
5971
6082
  }
6083
+ /**
6084
+ * Enables debugging.
6085
+ *
6086
+ * When debugging is enabled, the LiveView client will log debug information to the console.
6087
+ * See [Debugging client events](https://phoenix-live-view.hexdocs.pm/js-interop.html#debugging-client-events) for more information.
6088
+ */
5972
6089
  enableDebug() {
5973
6090
  this.sessionStorage.setItem(PHX_LV_DEBUG, "true");
5974
6091
  }
6092
+ /**
6093
+ * Enables profiling.
6094
+ *
6095
+ * When profiling is enabled, the LiveView client will log profiling information to the console.
6096
+ */
5975
6097
  enableProfiling() {
5976
6098
  this.sessionStorage.setItem(PHX_LV_PROFILE, "true");
5977
6099
  }
6100
+ /**
6101
+ * Disables debugging.
6102
+ */
5978
6103
  disableDebug() {
5979
6104
  this.sessionStorage.setItem(PHX_LV_DEBUG, "false");
5980
6105
  }
6106
+ /**
6107
+ * Disables profiling.
6108
+ */
5981
6109
  disableProfiling() {
5982
6110
  this.sessionStorage.removeItem(PHX_LV_PROFILE);
5983
6111
  }
6112
+ /**
6113
+ * Enables latency simulation.
6114
+ *
6115
+ * When latency simulation is enabled, the LiveView client will add a delay to requests and responses from the server.
6116
+ * See [Simulating Latency](https://phoenix-live-view.hexdocs.pm/js-interop.html#simulating-latency) for more information.
6117
+ */
5984
6118
  enableLatencySim(upperBoundMs) {
5985
6119
  this.enableDebug();
5986
6120
  console.log(
5987
6121
  "latency simulator enabled for the duration of this browser session. Call disableLatencySim() to disable"
5988
6122
  );
5989
- this.sessionStorage.setItem(PHX_LV_LATENCY_SIM, upperBoundMs);
6123
+ this.sessionStorage.setItem(PHX_LV_LATENCY_SIM, upperBoundMs.toString());
5990
6124
  }
6125
+ /**
6126
+ * Disables latency simulation.
6127
+ */
5991
6128
  disableLatencySim() {
5992
6129
  this.sessionStorage.removeItem(PHX_LV_LATENCY_SIM);
5993
6130
  }
6131
+ /**
6132
+ * Returns the current latency simulation upper bound.
6133
+ */
5994
6134
  getLatencySim() {
5995
6135
  const str = this.sessionStorage.getItem(PHX_LV_LATENCY_SIM);
5996
6136
  return str ? parseInt(str) : null;
5997
6137
  }
6138
+ /**
6139
+ * Returns the Phoenix Socket instance.
6140
+ */
5998
6141
  getSocket() {
5999
6142
  return this.socket;
6000
6143
  }
6144
+ /**
6145
+ * Connects to the LiveView server.
6146
+ */
6001
6147
  connect() {
6002
6148
  if (window.location.hostname === "localhost" && !this.isDebugDisabled()) {
6003
6149
  this.enableDebug();
@@ -6020,23 +6166,29 @@ var LiveSocket = class {
6020
6166
  document.addEventListener("DOMContentLoaded", () => doConnect());
6021
6167
  }
6022
6168
  }
6169
+ /**
6170
+ * Disconnects from the LiveView server.
6171
+ */
6023
6172
  disconnect(callback) {
6024
- clearTimeout(this.reloadWithJitterTimer);
6173
+ this.reloadWithJitterTimer != null && clearTimeout(this.reloadWithJitterTimer);
6025
6174
  if (this.serverCloseRef) {
6026
- this.socket.off(this.serverCloseRef);
6175
+ this.socket.off([this.serverCloseRef]);
6027
6176
  this.serverCloseRef = null;
6028
6177
  }
6029
6178
  this.socket.disconnect(callback);
6030
6179
  }
6180
+ /**
6181
+ * Can be used to replace the transport used by the underlying Phoenix Socket.
6182
+ */
6031
6183
  replaceTransport(transport) {
6032
- clearTimeout(this.reloadWithJitterTimer);
6184
+ this.reloadWithJitterTimer != null && clearTimeout(this.reloadWithJitterTimer);
6033
6185
  this.socket.replaceTransport(transport);
6034
6186
  this.connect();
6035
6187
  }
6036
6188
  /**
6037
- * @param {HTMLElement} el
6038
- * @param {import("./js_commands").EncodedJS} encodedJS
6039
- * @param {string | null} [eventType]
6189
+ * Executes an encoded JS command, targeting the given element.
6190
+ *
6191
+ * See [`Phoenix.LiveView.JS`](https://phoenix-live-view.hexdocs.pm/Phoenix.LiveView.JS.html) for more information.
6040
6192
  */
6041
6193
  execJS(el, encodedJS, eventType = null) {
6042
6194
  const e = new CustomEvent("phx:exec", { detail: { sourceElement: el } });
@@ -6046,12 +6198,13 @@ var LiveSocket = class {
6046
6198
  * Returns an object with methods to manipulate the DOM and execute JavaScript.
6047
6199
  * The applied changes integrate with server DOM patching.
6048
6200
  *
6049
- * @returns {import("./js_commands").LiveSocketJSCommands}
6201
+ * See [JavaScript interoperability](https://phoenix-live-view.hexdocs.pm/js-interop.html) for more information.
6050
6202
  */
6051
6203
  js() {
6052
6204
  return js_commands_default(this, "js");
6053
6205
  }
6054
6206
  // private
6207
+ /** @internal */
6055
6208
  unload() {
6056
6209
  if (this.unloaded) {
6057
6210
  return;
@@ -6063,9 +6216,11 @@ var LiveSocket = class {
6063
6216
  this.destroyAllViews();
6064
6217
  this.disconnect();
6065
6218
  }
6219
+ /** @internal */
6066
6220
  triggerDOM(kind, args) {
6067
6221
  this.domCallbacks[kind](...args);
6068
6222
  }
6223
+ /** @internal */
6069
6224
  time(name, func) {
6070
6225
  if (!this.isProfileEnabled() || !console.time) {
6071
6226
  return func();
@@ -6075,6 +6230,7 @@ var LiveSocket = class {
6075
6230
  console.timeEnd(name);
6076
6231
  return result;
6077
6232
  }
6233
+ /** @internal */
6078
6234
  log(view, kind, msgCallback) {
6079
6235
  if (this.viewLogger) {
6080
6236
  const [msg, obj] = msgCallback();
@@ -6084,16 +6240,20 @@ var LiveSocket = class {
6084
6240
  debug(view, kind, msg, obj);
6085
6241
  }
6086
6242
  }
6243
+ /** @internal */
6087
6244
  requestDOMUpdate(callback) {
6088
6245
  this.transitions.after(callback);
6089
6246
  }
6247
+ /** @internal */
6090
6248
  asyncTransition(promise) {
6091
6249
  this.transitions.addAsyncTransition(promise);
6092
6250
  }
6251
+ /** @internal */
6093
6252
  transition(time, onStart, onDone = function() {
6094
6253
  }) {
6095
6254
  this.transitions.addTransition(time, onStart, onDone);
6096
6255
  }
6256
+ /** @internal */
6097
6257
  onChannel(channel, event, cb) {
6098
6258
  channel.on(event, (data) => {
6099
6259
  const latency = this.getLatencySim();
@@ -6104,8 +6264,9 @@ var LiveSocket = class {
6104
6264
  }
6105
6265
  });
6106
6266
  }
6267
+ /** @internal */
6107
6268
  reloadWithJitter(view, log) {
6108
- clearTimeout(this.reloadWithJitterTimer);
6269
+ this.reloadWithJitterTimer != null && clearTimeout(this.reloadWithJitterTimer);
6109
6270
  this.disconnect();
6110
6271
  const minMs = this.reloadJitterMin;
6111
6272
  const maxMs = this.reloadJitterMax;
@@ -6133,22 +6294,25 @@ var LiveSocket = class {
6133
6294
  `exceeded ${this.maxReloads} consecutive reloads. Entering failsafe mode`
6134
6295
  ]);
6135
6296
  }
6136
- if (this.hasPendingLink()) {
6137
- window.location = this.pendingLink;
6297
+ if (this.pendingLink !== null) {
6298
+ window.location.href = this.pendingLink;
6138
6299
  } else {
6139
6300
  window.location.reload();
6140
6301
  }
6141
6302
  }, afterMs);
6142
6303
  }
6304
+ /** @internal */
6143
6305
  getHookDefinition(name) {
6144
6306
  if (!name) {
6145
6307
  return;
6146
6308
  }
6147
6309
  return this.maybeInternalHook(name) || this.hooks[name] || this.maybeRuntimeHook(name);
6148
6310
  }
6311
+ /** @internal */
6149
6312
  maybeInternalHook(name) {
6150
6313
  return name && name.startsWith("Phoenix.") && hooks_default[name.split(".")[1]];
6151
6314
  }
6315
+ /** @internal */
6152
6316
  maybeRuntimeHook(name) {
6153
6317
  const runtimeHook = document.querySelector(
6154
6318
  `script[${PHX_RUNTIME_HOOK}="${CSS.escape(name)}"]`
@@ -6170,21 +6334,27 @@ var LiveSocket = class {
6170
6334
  runtimeHook
6171
6335
  );
6172
6336
  }
6337
+ /** @internal */
6173
6338
  isUnloaded() {
6174
6339
  return this.unloaded;
6175
6340
  }
6341
+ /** @internal */
6176
6342
  isConnected() {
6177
6343
  return this.socket.isConnected();
6178
6344
  }
6345
+ /** @internal */
6179
6346
  getBindingPrefix() {
6180
6347
  return this.bindingPrefix;
6181
6348
  }
6349
+ /** @internal */
6182
6350
  binding(kind) {
6183
6351
  return `${this.getBindingPrefix()}${kind}`;
6184
6352
  }
6353
+ /** @internal */
6185
6354
  channel(topic, params) {
6186
6355
  return this.socket.channel(topic, params);
6187
6356
  }
6357
+ /** @internal */
6188
6358
  joinDeadView() {
6189
6359
  const body = document.body;
6190
6360
  if (body && !this.isPhxView(body) && !this.isPhxView(document.firstElementChild)) {
@@ -6200,6 +6370,7 @@ var LiveSocket = class {
6200
6370
  });
6201
6371
  }
6202
6372
  }
6373
+ /** @internal */
6203
6374
  joinRootViews() {
6204
6375
  let rootsFound = false;
6205
6376
  dom_default.all(
@@ -6221,6 +6392,7 @@ var LiveSocket = class {
6221
6392
  );
6222
6393
  return rootsFound;
6223
6394
  }
6395
+ /** @internal */
6224
6396
  redirect(to, flash, reloadToken) {
6225
6397
  if (reloadToken) {
6226
6398
  browser_default.setCookie(PHX_RELOAD_STATUS, reloadToken, 60);
@@ -6228,7 +6400,11 @@ var LiveSocket = class {
6228
6400
  this.unload();
6229
6401
  browser_default.redirect(to, flash);
6230
6402
  }
6403
+ /** @internal */
6231
6404
  replaceMain(href, flash, callback = null, linkRef = this.setPendingLink(href)) {
6405
+ if (!this.main) {
6406
+ return;
6407
+ }
6232
6408
  const liveReferer = this.currentLocation.href;
6233
6409
  this.outgoingMainEl = this.outgoingMainEl || this.main.el;
6234
6410
  const stickies = dom_default.findPhxSticky(document) || [];
@@ -6255,6 +6431,7 @@ var LiveSocket = class {
6255
6431
  }
6256
6432
  });
6257
6433
  }
6434
+ /** @internal */
6258
6435
  transitionRemoves(elements, callback) {
6259
6436
  const removeAttr = this.binding("remove");
6260
6437
  const silenceEvents = (e) => {
@@ -6276,14 +6453,17 @@ var LiveSocket = class {
6276
6453
  callback && callback();
6277
6454
  });
6278
6455
  }
6456
+ /** @internal */
6279
6457
  isPhxView(el) {
6280
6458
  return el.getAttribute && el.getAttribute(PHX_SESSION) !== null;
6281
6459
  }
6460
+ /** @internal */
6282
6461
  newRootView(el, flash, liveReferer) {
6283
6462
  const view = new View(el, this, null, flash, liveReferer);
6284
6463
  this.roots[view.id] = view;
6285
6464
  return view;
6286
6465
  }
6466
+ /** @internal */
6287
6467
  owner(childEl, callback) {
6288
6468
  let view;
6289
6469
  const viewEl = dom_default.closestViewEl(childEl);
@@ -6297,9 +6477,11 @@ var LiveSocket = class {
6297
6477
  }
6298
6478
  return view && callback ? callback(view) : view;
6299
6479
  }
6480
+ /** @internal */
6300
6481
  withinOwners(childEl, callback) {
6301
6482
  this.owner(childEl, (view) => callback(view, childEl));
6302
6483
  }
6484
+ /** @internal */
6303
6485
  getViewByEl(el) {
6304
6486
  const rootId = el.getAttribute(PHX_ROOT_ID);
6305
6487
  return maybe(
@@ -6307,9 +6489,11 @@ var LiveSocket = class {
6307
6489
  (root) => root.getDescendentByEl(el)
6308
6490
  );
6309
6491
  }
6492
+ /** @internal */
6310
6493
  getRootById(id) {
6311
6494
  return this.roots[id];
6312
6495
  }
6496
+ /** @internal */
6313
6497
  destroyAllViews() {
6314
6498
  for (const id in this.roots) {
6315
6499
  this.roots[id].destroy();
@@ -6317,6 +6501,7 @@ var LiveSocket = class {
6317
6501
  }
6318
6502
  this.main = null;
6319
6503
  }
6504
+ /** @internal */
6320
6505
  destroyViewByEl(el) {
6321
6506
  const root = this.getRootById(el.getAttribute(PHX_ROOT_ID));
6322
6507
  if (root && root.id === el.id) {
@@ -6326,28 +6511,30 @@ var LiveSocket = class {
6326
6511
  root.destroyDescendent(el.id);
6327
6512
  }
6328
6513
  }
6514
+ /** @internal */
6329
6515
  getActiveElement() {
6330
6516
  return document.activeElement;
6331
6517
  }
6518
+ /** @internal */
6332
6519
  dropActiveElement(view) {
6333
6520
  if (this.prevActive && view.ownsElement(this.prevActive)) {
6334
6521
  this.prevActive = null;
6335
6522
  }
6336
6523
  }
6524
+ /** @internal */
6337
6525
  restorePreviouslyActiveFocus() {
6338
6526
  if (this.prevActive && this.prevActive !== document.body && this.prevActive instanceof HTMLElement) {
6339
6527
  this.prevActive.focus();
6340
6528
  }
6341
6529
  }
6530
+ /** @internal */
6342
6531
  blurActiveElement() {
6343
6532
  this.prevActive = this.getActiveElement();
6344
6533
  if (this.prevActive !== document.body && this.prevActive instanceof HTMLElement) {
6345
6534
  this.prevActive.blur();
6346
6535
  }
6347
6536
  }
6348
- /**
6349
- * @param {{dead?: boolean}} [options={}]
6350
- */
6537
+ /** @internal */
6351
6538
  bindTopLevelEvents({ dead } = {}) {
6352
6539
  if (this.boundTopLevelEvents) {
6353
6540
  return;
@@ -6394,7 +6581,7 @@ var LiveSocket = class {
6394
6581
  { blur: "focusout", focus: "focusin" },
6395
6582
  (e, type, view, targetEl, phxEvent, phxTarget) => {
6396
6583
  if (!phxTarget) {
6397
- const data = { key: e.key, ...this.eventMeta(type, e, targetEl) };
6584
+ const data = { ...this.eventMeta(type, e, targetEl) };
6398
6585
  js_default.exec(e, type, phxEvent, view, targetEl, ["push", { data }]);
6399
6586
  }
6400
6587
  }
@@ -6410,10 +6597,11 @@ var LiveSocket = class {
6410
6597
  );
6411
6598
  this.on("dragover", (e) => e.preventDefault());
6412
6599
  this.on("dragenter", (e) => {
6413
- const dropzone = closestPhxBinding(
6414
- e.target,
6415
- this.binding(PHX_DROP_TARGET)
6416
- );
6600
+ let target = e.target && dom_default.elementFromTarget(e.target);
6601
+ if (!target) {
6602
+ return;
6603
+ }
6604
+ const dropzone = closestPhxBinding(target, this.binding(PHX_DROP_TARGET));
6417
6605
  if (!dropzone || !(dropzone instanceof HTMLElement)) {
6418
6606
  return;
6419
6607
  }
@@ -6422,10 +6610,11 @@ var LiveSocket = class {
6422
6610
  }
6423
6611
  });
6424
6612
  this.on("dragleave", (e) => {
6425
- const dropzone = closestPhxBinding(
6426
- e.target,
6427
- this.binding(PHX_DROP_TARGET)
6428
- );
6613
+ let target = e.target && dom_default.elementFromTarget(e.target);
6614
+ if (!target) {
6615
+ return;
6616
+ }
6617
+ const dropzone = closestPhxBinding(target, this.binding(PHX_DROP_TARGET));
6429
6618
  if (!dropzone || !(dropzone instanceof HTMLElement)) {
6430
6619
  return;
6431
6620
  }
@@ -6435,15 +6624,19 @@ var LiveSocket = class {
6435
6624
  }
6436
6625
  });
6437
6626
  this.on("drop", (e) => {
6627
+ let target = e.target && dom_default.elementFromTarget(e.target);
6628
+ if (!target) {
6629
+ return;
6630
+ }
6438
6631
  e.preventDefault();
6439
- const dropzone = closestPhxBinding(
6440
- e.target,
6441
- this.binding(PHX_DROP_TARGET)
6442
- );
6632
+ const dropzone = closestPhxBinding(target, this.binding(PHX_DROP_TARGET));
6443
6633
  if (!dropzone || !(dropzone instanceof HTMLElement)) {
6444
6634
  return;
6445
6635
  }
6446
6636
  this.js().removeClass(dropzone, PHX_DROP_TARGET_ACTIVE_CLASS);
6637
+ if (!e.dataTransfer) {
6638
+ return;
6639
+ }
6447
6640
  const dropTargetId = dropzone.getAttribute(this.binding(PHX_DROP_TARGET));
6448
6641
  const dropTarget = dropTargetId && document.getElementById(dropTargetId);
6449
6642
  const files = Array.from(e.dataTransfer.files || []);
@@ -6454,7 +6647,7 @@ var LiveSocket = class {
6454
6647
  dropTarget.dispatchEvent(new Event("input", { bubbles: true }));
6455
6648
  });
6456
6649
  this.on(PHX_TRACK_UPLOADS, (e) => {
6457
- const uploadTarget = e.target;
6650
+ const uploadTarget = e.target && dom_default.elementFromTarget(e.target);
6458
6651
  if (!dom_default.isUploadInput(uploadTarget)) {
6459
6652
  return;
6460
6653
  }
@@ -6465,47 +6658,67 @@ var LiveSocket = class {
6465
6658
  uploadTarget.dispatchEvent(new Event("input", { bubbles: true }));
6466
6659
  });
6467
6660
  }
6661
+ /** @internal */
6468
6662
  eventMeta(eventName, e, targetEl) {
6469
6663
  const callback = this.metadataCallbacks[eventName];
6470
6664
  return callback ? callback(e, targetEl) : {};
6471
6665
  }
6666
+ /** @internal */
6472
6667
  setPendingLink(href) {
6473
6668
  this.linkRef++;
6474
6669
  this.pendingLink = href;
6475
6670
  this.resetReloadStatus();
6476
6671
  return this.linkRef;
6477
6672
  }
6478
- // anytime we are navigating or connecting, drop reload cookie in case
6479
- // we issue the cookie but the next request was interrupted and the server never dropped it
6673
+ /**
6674
+ * @internal
6675
+ * anytime we are navigating or connecting, drop reload cookie in case
6676
+ * we issue the cookie but the next request was interrupted and the server never dropped it
6677
+ */
6480
6678
  resetReloadStatus() {
6481
6679
  browser_default.deleteCookie(PHX_RELOAD_STATUS);
6482
6680
  }
6681
+ /** @internal */
6483
6682
  commitPendingLink(linkRef) {
6484
6683
  if (this.linkRef !== linkRef) {
6485
6684
  return false;
6486
- } else {
6685
+ }
6686
+ if (this.pendingLink !== null) {
6487
6687
  this.href = this.pendingLink;
6488
6688
  this.pendingLink = null;
6489
- return true;
6490
6689
  }
6690
+ return true;
6491
6691
  }
6692
+ /** @internal */
6492
6693
  getHref() {
6493
6694
  return this.href;
6494
6695
  }
6696
+ /** @internal */
6495
6697
  hasPendingLink() {
6496
6698
  return !!this.pendingLink;
6497
6699
  }
6700
+ /** @internal */
6498
6701
  bind(events, callback) {
6499
6702
  for (const event in events) {
6500
6703
  const browserEventName = events[event];
6501
6704
  this.on(browserEventName, (e) => {
6502
6705
  const binding = this.binding(event);
6503
6706
  const windowBinding = this.binding(`window-${event}`);
6504
- const targetPhxEvent = e.target.getAttribute && e.target.getAttribute(binding);
6707
+ const targetPhxEvent = e.target instanceof Element && e.target.getAttribute(binding);
6708
+ if (!(e.target instanceof Element)) {
6709
+ return;
6710
+ }
6505
6711
  if (targetPhxEvent) {
6506
6712
  this.debounce(e.target, e, browserEventName, () => {
6507
6713
  this.withinOwners(e.target, (view) => {
6508
- callback(e, event, view, e.target, targetPhxEvent, null);
6714
+ callback(
6715
+ e,
6716
+ event,
6717
+ view,
6718
+ e.target,
6719
+ targetPhxEvent,
6720
+ null
6721
+ );
6509
6722
  });
6510
6723
  });
6511
6724
  } else {
@@ -6513,7 +6726,14 @@ var LiveSocket = class {
6513
6726
  const phxEvent = el.getAttribute(windowBinding);
6514
6727
  this.debounce(el, e, browserEventName, () => {
6515
6728
  this.withinOwners(el, (view) => {
6516
- callback(e, event, view, el, phxEvent, "window");
6729
+ callback(
6730
+ e,
6731
+ event,
6732
+ view,
6733
+ el,
6734
+ phxEvent,
6735
+ "window"
6736
+ );
6517
6737
  });
6518
6738
  });
6519
6739
  });
@@ -6521,23 +6741,31 @@ var LiveSocket = class {
6521
6741
  });
6522
6742
  }
6523
6743
  }
6744
+ /** @internal */
6524
6745
  bindClicks() {
6525
6746
  this.on("mousedown", (e) => this.clickStartedAtTarget = e.target);
6526
- this.bindClick("click", "click");
6747
+ this.bindClick();
6527
6748
  }
6528
- bindClick(eventName, bindingName) {
6529
- const click = this.binding(bindingName);
6749
+ /** @internal */
6750
+ bindClick() {
6751
+ const click = this.binding("click");
6530
6752
  window.addEventListener(
6531
- eventName,
6753
+ "click",
6532
6754
  (e) => {
6533
- let target = null;
6755
+ let target = e.target && dom_default.elementFromTarget(e.target);
6756
+ if (!target) {
6757
+ return;
6758
+ }
6534
6759
  if (e.detail === 0)
6535
- this.clickStartedAtTarget = e.target;
6536
- const clickStartedAtTarget = this.clickStartedAtTarget || e.target;
6537
- target = closestPhxBinding(e.target, click);
6760
+ this.clickStartedAtTarget = target;
6761
+ const clickStartedAtTarget = this.clickStartedAtTarget || target;
6762
+ target = closestPhxBinding(target, click);
6538
6763
  this.dispatchClickAway(e, clickStartedAtTarget);
6539
6764
  this.clickStartedAtTarget = null;
6540
- const phxEvent = target && target.getAttribute(click);
6765
+ if (!target) {
6766
+ return;
6767
+ }
6768
+ const phxEvent = target.getAttribute(click);
6541
6769
  if (!phxEvent) {
6542
6770
  if (dom_default.isNewPageClick(e, window.location)) {
6543
6771
  this.unload();
@@ -6562,6 +6790,7 @@ var LiveSocket = class {
6562
6790
  false
6563
6791
  );
6564
6792
  }
6793
+ /** @internal */
6565
6794
  dispatchClickAway(e, clickStartedAt) {
6566
6795
  const phxClickAway = this.binding("click-away");
6567
6796
  const portal = clickStartedAt.closest(`[${PHX_TELEPORTED_SRC}]`);
@@ -6592,6 +6821,7 @@ var LiveSocket = class {
6592
6821
  }
6593
6822
  });
6594
6823
  }
6824
+ /** @internal */
6595
6825
  bindNav() {
6596
6826
  if (!browser_default.canPushState()) {
6597
6827
  return;
@@ -6601,7 +6831,7 @@ var LiveSocket = class {
6601
6831
  }
6602
6832
  let scrollTimer = null;
6603
6833
  window.addEventListener("scroll", (_e) => {
6604
- clearTimeout(scrollTimer);
6834
+ scrollTimer != null && clearTimeout(scrollTimer);
6605
6835
  scrollTimer = setTimeout(() => {
6606
6836
  browser_default.updateCurrentState(
6607
6837
  (state) => Object.assign(state, { scroll: window.scrollY })
@@ -6635,7 +6865,7 @@ var LiveSocket = class {
6635
6865
  const callback = () => {
6636
6866
  this.maybeScroll(scroll);
6637
6867
  };
6638
- if (this.main.isConnected() && navType === "patch" && id === this.main.id) {
6868
+ if (this.main && this.main.isConnected() && navType === "patch" && id === this.main.id) {
6639
6869
  this.main.pushLinkPatch(event, href, null, callback);
6640
6870
  } else {
6641
6871
  this.replaceMain(href, null, callback);
@@ -6647,13 +6877,25 @@ var LiveSocket = class {
6647
6877
  window.addEventListener(
6648
6878
  "click",
6649
6879
  (e) => {
6650
- const target = closestPhxBinding(e.target, PHX_LIVE_LINK);
6880
+ let el = e.target && dom_default.elementFromTarget(e.target);
6881
+ if (!el) {
6882
+ return;
6883
+ }
6884
+ const target = closestPhxBinding(
6885
+ el,
6886
+ PHX_LIVE_LINK
6887
+ );
6651
6888
  const type = target && target.getAttribute(PHX_LIVE_LINK);
6652
6889
  if (!type || !this.isConnected() || !this.main || dom_default.wantsNewTab(e)) {
6653
6890
  return;
6654
6891
  }
6655
6892
  const href = target.href instanceof SVGAnimatedString ? target.href.baseVal : target.href;
6656
6893
  const linkState = target.getAttribute(PHX_LINK_STATE);
6894
+ if (linkState !== "replace" && linkState !== "push") {
6895
+ throw new Error(
6896
+ `expected ${PHX_LINK_STATE} to be "replace" or "push", got: ${linkState}`
6897
+ );
6898
+ }
6657
6899
  e.preventDefault();
6658
6900
  e.stopImmediatePropagation();
6659
6901
  if (this.pendingLink === href) {
@@ -6678,6 +6920,7 @@ var LiveSocket = class {
6678
6920
  false
6679
6921
  );
6680
6922
  }
6923
+ /** @internal */
6681
6924
  maybeScroll(scroll) {
6682
6925
  if (typeof scroll === "number") {
6683
6926
  requestAnimationFrame(() => {
@@ -6685,19 +6928,23 @@ var LiveSocket = class {
6685
6928
  });
6686
6929
  }
6687
6930
  }
6931
+ /** @internal */
6688
6932
  dispatchEvent(event, payload = {}) {
6689
6933
  dom_default.dispatchEvent(window, `phx:${event}`, { detail: payload });
6690
6934
  }
6935
+ /** @internal */
6691
6936
  dispatchEvents(events) {
6692
6937
  events.forEach(([event, payload]) => this.dispatchEvent(event, payload));
6693
6938
  }
6939
+ /** @internal */
6694
6940
  withPageLoading(info, callback) {
6695
6941
  dom_default.dispatchEvent(window, "phx:page-loading-start", { detail: info });
6696
6942
  const done = () => dom_default.dispatchEvent(window, "phx:page-loading-stop", { detail: info });
6697
6943
  return callback ? callback(done) : done;
6698
6944
  }
6945
+ /** @internal */
6699
6946
  pushHistoryPatch(e, href, linkState, targetEl) {
6700
- if (!this.isConnected() || !this.main.isMain()) {
6947
+ if (!this.isConnected() || !(this.main && this.main.isMain())) {
6701
6948
  return browser_default.redirect(href);
6702
6949
  }
6703
6950
  this.withPageLoading({ to: href, kind: "patch" }, (done) => {
@@ -6707,6 +6954,7 @@ var LiveSocket = class {
6707
6954
  });
6708
6955
  });
6709
6956
  }
6957
+ /** @internal */
6710
6958
  historyPatch(href, linkState, linkRef = this.setPendingLink(href)) {
6711
6959
  if (!this.commitPendingLink(linkRef)) {
6712
6960
  return;
@@ -6731,12 +6979,13 @@ var LiveSocket = class {
6731
6979
  });
6732
6980
  this.registerNewLocation(window.location);
6733
6981
  }
6982
+ /** @internal */
6734
6983
  historyRedirect(e, href, linkState, flash, targetEl) {
6735
6984
  const clickLoading = targetEl && e.isTrusted && e.type !== "popstate";
6736
6985
  if (clickLoading) {
6737
6986
  targetEl.classList.add("phx-click-loading");
6738
6987
  }
6739
- if (!this.isConnected() || !this.main.isMain()) {
6988
+ if (!this.isConnected() || !(this.main && this.main.isMain())) {
6740
6989
  return browser_default.redirect(href, flash);
6741
6990
  }
6742
6991
  if (/^\/$|^\/[^\/]+.*$/.test(href)) {
@@ -6778,6 +7027,7 @@ var LiveSocket = class {
6778
7027
  });
6779
7028
  });
6780
7029
  }
7030
+ /** @internal */
6781
7031
  registerNewLocation(newLocation) {
6782
7032
  const { pathname, search } = this.currentLocation;
6783
7033
  if (pathname + search === newLocation.pathname + newLocation.search) {
@@ -6787,10 +7037,13 @@ var LiveSocket = class {
6787
7037
  return true;
6788
7038
  }
6789
7039
  }
7040
+ /** @internal */
6790
7041
  bindForms() {
6791
7042
  let iterations = 0;
6792
7043
  let externalFormSubmitted = false;
6793
7044
  this.on("submit", (e) => {
7045
+ if (!(e.target instanceof HTMLFormElement))
7046
+ return;
6794
7047
  const phxSubmit = e.target.getAttribute(this.binding("submit"));
6795
7048
  const phxChange = e.target.getAttribute(this.binding("change"));
6796
7049
  if (!externalFormSubmitted && phxChange && !phxSubmit) {
@@ -6808,6 +7061,8 @@ var LiveSocket = class {
6808
7061
  }
6809
7062
  });
6810
7063
  this.on("submit", (e) => {
7064
+ if (!(e.target instanceof HTMLFormElement))
7065
+ return;
6811
7066
  const phxEvent = e.target.getAttribute(this.binding("submit"));
6812
7067
  if (!phxEvent) {
6813
7068
  if (dom_default.isUnloadableFormSubmit(e)) {
@@ -6826,7 +7081,10 @@ var LiveSocket = class {
6826
7081
  });
6827
7082
  for (const type of ["change", "input"]) {
6828
7083
  this.on(type, (e) => {
6829
- if (e instanceof CustomEvent && (e.target instanceof HTMLInputElement || e.target instanceof HTMLSelectElement || e.target instanceof HTMLTextAreaElement) && e.target.form === void 0) {
7084
+ if (!dom_default.isFormAssociated(e.target)) {
7085
+ return;
7086
+ }
7087
+ if (e instanceof CustomEvent && e.target.form === void 0) {
6830
7088
  if (e.detail && e.detail.dispatcher) {
6831
7089
  throw new Error(
6832
7090
  `dispatching a custom ${type} event is only supported on input elements inside a form`
@@ -6834,9 +7092,9 @@ var LiveSocket = class {
6834
7092
  }
6835
7093
  return;
6836
7094
  }
6837
- const phxChange = this.binding("change");
6838
7095
  const input = e.target;
6839
- if (this.blockPhxChangeWhileComposing && e.isComposing) {
7096
+ const phxChange = this.binding("change");
7097
+ if (this.blockPhxChangeWhileComposing && e instanceof InputEvent && e.isComposing) {
6840
7098
  const key = `composition-listener-${type}`;
6841
7099
  if (!dom_default.private(input, key)) {
6842
7100
  dom_default.putPrivate(input, key, true);
@@ -6876,7 +7134,7 @@ var LiveSocket = class {
6876
7134
  dom_default.putPrivate(input, PHX_HAS_FOCUSED, true);
6877
7135
  js_default.exec(e, "change", phxEvent, view, input, [
6878
7136
  "push",
6879
- { _target: e.target.name, dispatcher }
7137
+ { _target: input.name, dispatcher }
6880
7138
  ]);
6881
7139
  });
6882
7140
  });
@@ -6885,7 +7143,9 @@ var LiveSocket = class {
6885
7143
  this.on("reset", (e) => {
6886
7144
  const form = e.target;
6887
7145
  dom_default.resetForm(form);
6888
- const input = Array.from(form.elements).find((el) => el.type === "reset");
7146
+ const input = Array.from(form.elements).find(
7147
+ (el) => "type" in el && el.type === "reset"
7148
+ );
6889
7149
  if (input) {
6890
7150
  window.requestAnimationFrame(() => {
6891
7151
  input.dispatchEvent(
@@ -6895,6 +7155,7 @@ var LiveSocket = class {
6895
7155
  }
6896
7156
  });
6897
7157
  }
7158
+ /** @internal */
6898
7159
  debounce(el, event, eventType, callback) {
6899
7160
  if (eventType === "blur" || eventType === "focusout") {
6900
7161
  return callback();
@@ -6919,11 +7180,13 @@ var LiveSocket = class {
6919
7180
  );
6920
7181
  });
6921
7182
  }
7183
+ /** @internal */
6922
7184
  silenceEvents(callback) {
6923
7185
  this.silenced = true;
6924
7186
  callback();
6925
7187
  this.silenced = false;
6926
7188
  }
7189
+ /** @internal */
6927
7190
  on(event, callback) {
6928
7191
  this.boundEventNames.add(event);
6929
7192
  window.addEventListener(event, (e) => {
@@ -6932,6 +7195,7 @@ var LiveSocket = class {
6932
7195
  }
6933
7196
  });
6934
7197
  }
7198
+ /** @internal */
6935
7199
  jsQuerySelectorAll(sourceEl, query, defaultQuery) {
6936
7200
  const all = this.domCallbacks.jsQuerySelectorAll;
6937
7201
  return all ? all(sourceEl, query, defaultQuery) : defaultQuery();
@@ -6993,7 +7257,6 @@ var TransitionSet = class {
6993
7257
  };
6994
7258
 
6995
7259
  // js/phoenix_live_view/index.ts
6996
- var LiveSocket2 = LiveSocket;
6997
7260
  function createHook(el, callbacks) {
6998
7261
  let existingHook = dom_default.getCustomElHook(el);
6999
7262
  if (existingHook) {
@@ -7013,7 +7276,7 @@ function getFileURLForUpload(input, uploadRef) {
7013
7276
  return LiveUploader.getEntryDataURL(input, uploadRef);
7014
7277
  }
7015
7278
  export {
7016
- LiveSocket2 as LiveSocket,
7279
+ LiveSocket,
7017
7280
  ViewHook,
7018
7281
  createHook,
7019
7282
  getFileURLForUpload,