ckeditor5-phoenix 1.27.1 → 1.28.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 (29) hide show
  1. package/dist/hooks/editable.d.ts.map +1 -1
  2. package/dist/hooks/editor/editor.d.ts.map +1 -1
  3. package/dist/hooks/editor/utils/assign-editor-roots-to-config.d.ts +14 -0
  4. package/dist/hooks/editor/utils/assign-editor-roots-to-config.d.ts.map +1 -0
  5. package/dist/hooks/editor/utils/index.d.ts +1 -2
  6. package/dist/hooks/editor/utils/index.d.ts.map +1 -1
  7. package/dist/hooks/editor/utils/query-all-editor-editables.d.ts +1 -16
  8. package/dist/hooks/editor/utils/query-all-editor-editables.d.ts.map +1 -1
  9. package/dist/index.cjs +2 -2
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.mjs +244 -277
  12. package/dist/index.mjs.map +1 -1
  13. package/package.json +3 -3
  14. package/src/hooks/editable.test.ts +29 -0
  15. package/src/hooks/editable.ts +3 -1
  16. package/src/hooks/editor/editor.test.ts +94 -0
  17. package/src/hooks/editor/editor.ts +22 -42
  18. package/src/hooks/editor/plugins/sync-editor-with-phoenix.ts +1 -1
  19. package/src/hooks/editor/utils/assign-editor-roots-to-config.ts +58 -0
  20. package/src/hooks/editor/utils/index.ts +1 -2
  21. package/src/hooks/editor/utils/query-all-editor-editables.ts +12 -35
  22. package/test-utils/editor/create-editable-html-element.ts +5 -0
  23. package/test-utils/editor/create-editor-html-element.ts +5 -0
  24. package/dist/hooks/editor/utils/assign-initial-data-to-editor-config.d.ts +0 -10
  25. package/dist/hooks/editor/utils/assign-initial-data-to-editor-config.d.ts.map +0 -1
  26. package/dist/hooks/editor/utils/assign-source-elements-to-editor-config.d.ts +0 -12
  27. package/dist/hooks/editor/utils/assign-source-elements-to-editor-config.d.ts.map +0 -1
  28. package/src/hooks/editor/utils/assign-initial-data-to-editor-config.ts +0 -47
  29. package/src/hooks/editor/utils/assign-source-elements-to-editor-config.ts +0 -60
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- function H(r, t) {
1
+ function j(r, t) {
2
2
  if (!r || r.size !== t.size)
3
3
  return !1;
4
4
  for (const [e, a] of r)
@@ -6,7 +6,7 @@ function H(r, t) {
6
6
  return !1;
7
7
  return !0;
8
8
  }
9
- class K {
9
+ class W {
10
10
  /**
11
11
  * Map of registered items.
12
12
  */
@@ -215,7 +215,7 @@ class K {
215
215
  * Immediately dispatches the current state to all watchers if it changed.
216
216
  */
217
217
  flushWatchers() {
218
- H(this.lastNotifiedItems, this.items) && H(this.lastNotifiedErrors, this.initializationErrors) || (this.lastNotifiedItems = new Map(this.items), this.lastNotifiedErrors = new Map(this.initializationErrors), this.watchers.forEach((t) => t(
218
+ j(this.lastNotifiedItems, this.items) && j(this.lastNotifiedErrors, this.initializationErrors) || (this.lastNotifiedItems = new Map(this.items), this.lastNotifiedErrors = new Map(this.initializationErrors), this.watchers.forEach((t) => t(
219
219
  new Map(this.items),
220
220
  new Map(this.initializationErrors)
221
221
  )));
@@ -231,10 +231,10 @@ class K {
231
231
  return e || (e = { success: [], error: [] }, this.pendingCallbacks.set(t, e)), e;
232
232
  }
233
233
  }
234
- function rt(r) {
234
+ function Z(r) {
235
235
  return r.replace(/[-_\s]+(.)?/g, (t, e) => e ? e.toUpperCase() : "").replace(/^./, (t) => t.toLowerCase());
236
236
  }
237
- function I(r, t) {
237
+ function A(r, t) {
238
238
  let e = null;
239
239
  return (...a) => {
240
240
  e && clearTimeout(e), e = setTimeout(() => {
@@ -242,35 +242,31 @@ function I(r, t) {
242
242
  }, r);
243
243
  };
244
244
  }
245
- function at(r) {
245
+ function tt(r) {
246
246
  if (Object.prototype.toString.call(r) !== "[object Object]")
247
247
  return !1;
248
248
  const t = Object.getPrototypeOf(r);
249
249
  return t === Object.prototype || t === null;
250
250
  }
251
- function O(r) {
251
+ function P(r) {
252
252
  if (Array.isArray(r))
253
- return r.map(O);
254
- if (at(r)) {
253
+ return r.map(P);
254
+ if (tt(r)) {
255
255
  const t = /* @__PURE__ */ Object.create(null);
256
256
  for (const [e, a] of Object.entries(r))
257
- t[rt(e)] = O(a);
257
+ t[Z(e)] = P(a);
258
258
  return t;
259
259
  }
260
260
  return r;
261
261
  }
262
- function nt(r, t) {
263
- const e = Object.entries(r).filter(([a, n]) => t(n, a));
264
- return Object.fromEntries(e);
265
- }
266
- function it() {
262
+ function et() {
267
263
  const r = document.querySelector('meta[name="csrf-token"]');
268
264
  if (r)
269
265
  return r.getAttribute("content");
270
266
  const t = document.cookie.match(/(?:^|; )_csrf_token=([^;]*)/);
271
267
  return t ? decodeURIComponent(t[1]) : null;
272
268
  }
273
- class S {
269
+ class x {
274
270
  /**
275
271
  * The current state of the hook.
276
272
  */
@@ -345,7 +341,7 @@ class S {
345
341
  this._beforeDestroyCallbacks = [];
346
342
  }
347
343
  }
348
- function N(r) {
344
+ function D(r) {
349
345
  return {
350
346
  /**
351
347
  * The mounted lifecycle callback for the LiveView hook object.
@@ -390,26 +386,26 @@ function N(r) {
390
386
  }
391
387
  };
392
388
  }
393
- function Y(r) {
389
+ function L(r) {
394
390
  return Object.keys(r).length === 0 && r.constructor === Object;
395
391
  }
396
- function st(r) {
392
+ function rt(r) {
397
393
  return r == null;
398
394
  }
399
- function j(r, t) {
395
+ function at(r, t) {
400
396
  const e = Object.entries(r).map(([a, n]) => [a, t(n, a)]);
401
397
  return Object.fromEntries(e);
402
398
  }
403
- function q(r) {
399
+ function H(r) {
404
400
  if (r === null)
405
401
  return null;
406
402
  const t = Number.parseInt(r, 10);
407
403
  return Number.isNaN(t) ? null : t;
408
404
  }
409
- function ot(r) {
405
+ function nt(r) {
410
406
  return r == null || r.trim() === "" ? null : JSON.parse(r);
411
407
  }
412
- function ct(r, t) {
408
+ function it(r, t) {
413
409
  if (r === t)
414
410
  return !0;
415
411
  const e = Object.keys(r), a = Object.keys(t);
@@ -420,10 +416,10 @@ function ct(r, t) {
420
416
  return !1;
421
417
  return !0;
422
418
  }
423
- function ut() {
419
+ function ot() {
424
420
  return Math.random().toString(36).substring(2);
425
421
  }
426
- function lt(r, {
422
+ function st(r, {
427
423
  timeOutAfter: t = 500,
428
424
  retryAfter: e = 100
429
425
  } = {}) {
@@ -443,60 +439,36 @@ function lt(r, {
443
439
  o();
444
440
  });
445
441
  }
446
- function dt(r, t) {
447
- const e = ht(r), a = /* @__PURE__ */ new Set([
448
- ...Object.keys(e),
449
- ...Object.keys(t.roots ?? {})
450
- ]), n = Array.from(a).reduce((s, c) => ({
451
- ...s,
452
- [c]: {
453
- ...t.roots?.[c],
454
- ...c === "main" ? t.root : {},
455
- /* v8 ignore next 5 */
456
- ...c in e ? {
457
- initialData: e[c]
458
- } : {}
459
- }
460
- }), Object.create(t.roots || {})), i = {
461
- ...t,
462
- roots: n
463
- };
464
- return delete i.root, i;
465
- }
466
- function ht(r) {
467
- return typeof r == "string" ? { main: r } : { ...r };
468
- }
469
- function mt(r, t, e) {
470
- const a = pt(t);
471
- if (!r.editorName || r.editorName === "ClassicEditor")
472
- return {
473
- ...e,
474
- attachTo: a.main
475
- };
476
- const n = /* @__PURE__ */ new Set([
477
- ...Object.keys(a),
442
+ function ct(r, t, e) {
443
+ const a = !r.editorName || r.editorName === "ClassicEditor", n = /* @__PURE__ */ new Set([
444
+ ...Object.keys(t),
478
445
  ...Object.keys(e.roots ?? {})
479
446
  ]), i = Array.from(n).reduce((c, o) => ({
480
447
  ...c,
481
448
  [o]: {
482
- /* v8 ignore next */
449
+ /* v8 ignore next 1 */
483
450
  ...e.roots?.[o],
484
451
  ...o === "main" ? e.root : {},
485
- /* v8 ignore next 5 */
486
- ...o in a ? {
487
- element: a[o]
452
+ /* v8 ignore start */
453
+ ...o in t ? {
454
+ initialData: t[o].initialValue,
455
+ modelElement: t[o].modelElement || "$root",
456
+ ...!a && {
457
+ element: t[o].content
458
+ }
488
459
  } : {}
460
+ /* v8 ignore end */
489
461
  }
490
- }), Object.create(e.roots || {})), s = {
462
+ }), { ...e.roots || {} }), s = {
491
463
  ...e,
492
- roots: i
464
+ roots: i,
465
+ ...a && {
466
+ attachTo: t.main?.content
467
+ }
493
468
  };
494
469
  return delete s.root, s;
495
470
  }
496
- function pt(r) {
497
- return r instanceof HTMLElement ? { main: r } : { ...r };
498
- }
499
- function ft(r) {
471
+ function ut(r) {
500
472
  const t = [
501
473
  r.ui?.element,
502
474
  r.ui?.view?.toolbar?.element,
@@ -523,8 +495,8 @@ function ft(r) {
523
495
  }
524
496
  }
525
497
  const R = /* @__PURE__ */ Symbol.for("context-editor-watchdog");
526
- async function wt({ context: r, creator: t, config: e }) {
527
- const a = ut();
498
+ async function lt({ context: r, creator: t, config: e }) {
499
+ const a = ot();
528
500
  await r.add({
529
501
  creator: t.create.bind(t),
530
502
  id: a,
@@ -543,16 +515,16 @@ async function wt({ context: r, creator: t, config: e }) {
543
515
  editor: n
544
516
  };
545
517
  }
546
- function gt(r) {
518
+ function dt(r) {
547
519
  return R in r ? r[R] : null;
548
520
  }
549
- function B(r) {
521
+ function q(r) {
550
522
  return "addEditable" in r.ui;
551
523
  }
552
- function T(r) {
524
+ function N(r) {
553
525
  return ["inline", "classic", "balloon", "decoupled"].includes(r);
554
526
  }
555
- async function bt(r) {
527
+ async function mt(r) {
556
528
  const t = await import("ckeditor5"), a = {
557
529
  inline: t.InlineEditor,
558
530
  balloon: t.BalloonEditor,
@@ -564,8 +536,8 @@ async function bt(r) {
564
536
  throw new Error(`Unsupported editor type: ${r}`);
565
537
  return a;
566
538
  }
567
- class $ {
568
- static the = new $();
539
+ class U {
540
+ static the = new U();
569
541
  /**
570
542
  * Map of registered custom plugins.
571
543
  */
@@ -624,11 +596,11 @@ class $ {
624
596
  return this.plugins.has(t);
625
597
  }
626
598
  }
627
- async function J(r) {
599
+ async function K(r) {
628
600
  const t = await import("ckeditor5");
629
601
  let e = null;
630
602
  const a = r.map(async (n) => {
631
- const i = await $.the.get(n);
603
+ const i = await U.the.get(n);
632
604
  if (i)
633
605
  return i;
634
606
  const { [n]: s } = t;
@@ -650,25 +622,25 @@ async function J(r) {
650
622
  hasPremium: !!e
651
623
  };
652
624
  }
653
- async function G(r, t) {
625
+ async function Y(r, t) {
654
626
  const e = [r.ui, r.content];
655
627
  return await Promise.all(
656
628
  [
657
- F("ckeditor5", e),
629
+ B("ckeditor5", e),
658
630
  /* v8 ignore next */
659
- t && F("ckeditor5-premium-features", e)
631
+ t && B("ckeditor5-premium-features", e)
660
632
  ].filter((n) => !!n)
661
633
  ).then((n) => n.flat());
662
634
  }
663
- async function F(r, t) {
635
+ async function B(r, t) {
664
636
  return await Promise.all(
665
637
  t.filter((e) => e !== "en").map(async (e) => {
666
- const a = await yt(r, e);
638
+ const a = await ht(r, e);
667
639
  return a?.default ?? a;
668
640
  }).filter(Boolean)
669
641
  );
670
642
  }
671
- async function yt(r, t) {
643
+ async function ht(r, t) {
672
644
  try {
673
645
  if (r === "ckeditor5")
674
646
  switch (t) {
@@ -968,75 +940,73 @@ async function yt(r, t) {
968
940
  return console.error(`Failed to load translation for ${r}/${t}:`, e), null;
969
941
  }
970
942
  }
971
- function X(r) {
972
- return j(r, (t) => ({
943
+ function J(r) {
944
+ return at(r, (t) => ({
973
945
  dictionary: t
974
946
  }));
975
947
  }
976
- function Q(r) {
977
- const t = Z(r);
978
- return j(t, ({ content: e }) => e);
979
- }
980
- function L(r) {
981
- const t = Z(r), e = j(t, ({ initialValue: a }) => a);
982
- return nt(e, (a) => typeof a == "string");
983
- }
984
- function Z(r) {
948
+ function G(r) {
985
949
  const t = document.querySelectorAll(
986
950
  [
987
951
  `[data-cke-editor-id="${r}"][data-cke-editable-root-name]`,
988
952
  "[data-cke-editable-root-name]:not([data-cke-editor-id])"
989
953
  ].join(", ")
990
954
  ), e = Array.from(t).reduce((c, o) => {
991
- const u = o.getAttribute("data-cke-editable-root-name"), l = o.getAttribute("data-cke-editable-initial-value") || "", d = o.querySelector("[data-cke-editable-content]");
992
- return !u || !d ? c : {
955
+ const u = o.getAttribute("data-cke-editable-root-name"), d = o.getAttribute("data-cke-editable-root-model-element-name") || null, l = o.getAttribute("data-cke-editable-initial-value") || "", p = o.querySelector("[data-cke-editable-content]");
956
+ return !u || !p ? c : {
993
957
  ...c,
994
958
  [u]: {
995
- content: d,
996
- initialValue: l
959
+ content: p,
960
+ initialValue: l,
961
+ modelElement: d
997
962
  }
998
963
  };
999
- }, /* @__PURE__ */ Object.create({})), a = document.querySelector(`[phx-hook="CKEditor5"][id="${r}"]`);
964
+ }, /* @__PURE__ */ Object.create(null)), a = document.querySelector(`[phx-hook="CKEditor5"][id="${r}"]`);
1000
965
  if (!a)
1001
966
  return e;
1002
- const n = a.getAttribute("data-cke-initial-value") || "", i = a.querySelector(`#${r}_editor `), s = e.main;
967
+ const n = a.getAttribute("data-cke-root-model-element-name"), i = a.getAttribute("data-cke-initial-value") || "";
968
+ if (e.main)
969
+ return {
970
+ ...e,
971
+ main: {
972
+ ...e.main,
973
+ modelElement: e.main.modelElement || n,
974
+ initialValue: e.main.initialValue || i
975
+ }
976
+ };
977
+ const s = a.querySelector(`#${r}_editor `);
1003
978
  return s ? {
1004
979
  ...e,
1005
980
  main: {
1006
- ...s,
1007
- initialValue: s.initialValue || n
1008
- }
1009
- } : i ? {
1010
- ...e,
1011
- main: {
1012
- content: i,
1013
- initialValue: n
981
+ content: s,
982
+ initialValue: i,
983
+ modelElement: a.getAttribute("data-cke-root-model-element-name") || "$root"
1014
984
  }
1015
985
  } : e;
1016
986
  }
1017
- const W = ["inline", "classic", "balloon", "decoupled", "multiroot"];
1018
- function kt(r) {
987
+ const F = ["inline", "classic", "balloon", "decoupled", "multiroot"];
988
+ function pt(r) {
1019
989
  const t = r.getAttribute("data-cke-preset");
1020
990
  if (!t)
1021
991
  throw new Error('CKEditor5 hook requires a "cke-preset" attribute on the element.');
1022
992
  const { type: e, config: a, license: n, watchdog: i, ...s } = JSON.parse(t);
1023
993
  if (!e || !a || !n)
1024
994
  throw new Error('CKEditor5 hook configuration must include "editor", "config", and "license" properties.');
1025
- if (!W.includes(e))
1026
- throw new Error(`Invalid editor type: ${e}. Must be one of: ${W.join(", ")}.`);
995
+ if (!F.includes(e))
996
+ throw new Error(`Invalid editor type: ${e}. Must be one of: ${F.join(", ")}.`);
1027
997
  return {
1028
998
  type: e,
1029
999
  license: n,
1030
1000
  watchdog: i,
1031
- config: O(a),
1001
+ config: P(a),
1032
1002
  customTranslations: s.customTranslations || s.custom_translations
1033
1003
  };
1034
1004
  }
1035
- function x(r) {
1005
+ function T(r) {
1036
1006
  if (!r || typeof r != "object")
1037
1007
  return r;
1038
1008
  if (Array.isArray(r))
1039
- return r.map((a) => x(a));
1009
+ return r.map((a) => T(a));
1040
1010
  const t = r;
1041
1011
  if (t.$element && typeof t.$element == "string") {
1042
1012
  const a = document.querySelector(t.$element);
@@ -1044,39 +1014,39 @@ function x(r) {
1044
1014
  }
1045
1015
  const e = /* @__PURE__ */ Object.create(null);
1046
1016
  for (const [a, n] of Object.entries(r))
1047
- e[a] = x(n);
1017
+ e[a] = T(n);
1048
1018
  return e;
1049
1019
  }
1050
- function D(r, t, e) {
1020
+ function I(r, t, e) {
1051
1021
  if (!e || typeof e != "object")
1052
1022
  return e;
1053
1023
  if (Array.isArray(e))
1054
- return e.map((i) => D(r, t, i));
1024
+ return e.map((i) => I(r, t, i));
1055
1025
  const a = e;
1056
1026
  if (a.$translation && typeof a.$translation == "string") {
1057
- const i = a.$translation, s = Et(r, i, t);
1027
+ const i = a.$translation, s = ft(r, i, t);
1058
1028
  return s === void 0 && console.warn(`Translation not found for key: ${i}`), s !== void 0 ? s : null;
1059
1029
  }
1060
1030
  const n = /* @__PURE__ */ Object.create(null);
1061
1031
  for (const [i, s] of Object.entries(e))
1062
- n[i] = D(r, t, s);
1032
+ n[i] = I(r, t, s);
1063
1033
  return n;
1064
1034
  }
1065
- function Et(r, t, e) {
1035
+ function ft(r, t, e) {
1066
1036
  for (const a of r) {
1067
1037
  const n = a[e];
1068
1038
  if (n?.dictionary && t in n.dictionary)
1069
1039
  return n.dictionary[t];
1070
1040
  }
1071
1041
  }
1072
- function vt(r, t) {
1042
+ function wt(r, t) {
1073
1043
  const { editing: e } = r;
1074
1044
  e.view.change((a) => {
1075
1045
  a.setStyle("height", `${t}px`, e.view.document.getRoot());
1076
1046
  });
1077
1047
  }
1078
1048
  const V = /* @__PURE__ */ Symbol.for("elixir-editor-watchdog");
1079
- async function Ct(r, t) {
1049
+ async function gt(r, t) {
1080
1050
  const { EditorWatchdog: e } = await import("ckeditor5"), a = new e(null, t ?? {
1081
1051
  crashNumberLimit: 10,
1082
1052
  minimumNonErrorTimePeriod: 5e3
@@ -1086,24 +1056,24 @@ async function Ct(r, t) {
1086
1056
  return n[V] = a, n;
1087
1057
  }), a;
1088
1058
  }
1089
- function Pt(r) {
1059
+ function bt(r) {
1090
1060
  return V in r ? r[V] : null;
1091
1061
  }
1092
- class y extends K {
1093
- static the = new y();
1062
+ class b extends W {
1063
+ static the = new b();
1094
1064
  }
1095
- function At(r) {
1065
+ function yt(r) {
1096
1066
  const t = r.getAttribute("data-cke-context");
1097
1067
  if (!t)
1098
1068
  throw new Error('CKEditor5 hook requires a "data-cke-context" attribute on the element.');
1099
1069
  const { config: e, ...a } = JSON.parse(t);
1100
1070
  return {
1101
- config: O(e),
1071
+ config: P(e),
1102
1072
  customTranslations: a.customTranslations || a.custom_translations,
1103
1073
  watchdogConfig: a.watchdogConfig || a.watchdog_config
1104
1074
  };
1105
1075
  }
1106
- class Tt extends S {
1076
+ class kt extends x {
1107
1077
  /**
1108
1078
  * The promise that resolves to the context instance.
1109
1079
  */
@@ -1114,7 +1084,7 @@ class Tt extends S {
1114
1084
  get attrs() {
1115
1085
  const t = (a) => this.el.getAttribute(a) || null, e = {
1116
1086
  id: this.el.id,
1117
- config: At(this.el),
1087
+ config: yt(this.el),
1118
1088
  language: {
1119
1089
  ui: t("data-cke-language") || "en",
1120
1090
  content: t("data-cke-content-language") || "en"
@@ -1131,29 +1101,29 @@ class Tt extends S {
1131
1101
  * Mounts the context component.
1132
1102
  */
1133
1103
  async mounted() {
1134
- const { id: t, language: e } = this.attrs, { customTranslations: a, watchdogConfig: n, config: { plugins: i, ...s } } = this.attrs.config, { loadedPlugins: c, hasPremium: o } = await J(i ?? []), l = [
1135
- ...await G(e, o),
1136
- X(a?.dictionary || {})
1137
- ].filter((m) => !Y(m));
1138
- let d = x(s);
1139
- d = D([...l].reverse(), e.ui, d), this.contextPromise = (async () => {
1140
- const { ContextWatchdog: m, Context: E } = await import("ckeditor5"), b = new m(E, {
1104
+ const { id: t, language: e } = this.attrs, { customTranslations: a, watchdogConfig: n, config: { plugins: i, ...s } } = this.attrs.config, { loadedPlugins: c, hasPremium: o } = await K(i ?? []), d = [
1105
+ ...await Y(e, o),
1106
+ J(a?.dictionary || {})
1107
+ ].filter((m) => !L(m));
1108
+ let l = T(s);
1109
+ l = I([...d].reverse(), e.ui, l), this.contextPromise = (async () => {
1110
+ const { ContextWatchdog: m, Context: k } = await import("ckeditor5"), g = new m(k, {
1141
1111
  crashNumberLimit: 10,
1142
1112
  ...n
1143
1113
  });
1144
- return await b.create({
1145
- ...d,
1114
+ return await g.create({
1115
+ ...l,
1146
1116
  language: e,
1147
1117
  plugins: c,
1148
- ...l.length && {
1149
- translations: l
1118
+ ...d.length && {
1119
+ translations: d
1150
1120
  }
1151
- }), b.on("itemError", (...P) => {
1152
- console.error("Context item error:", ...P);
1153
- }), b;
1121
+ }), g.on("itemError", (...E) => {
1122
+ console.error("Context item error:", ...E);
1123
+ }), g;
1154
1124
  })();
1155
- const w = await this.contextPromise;
1156
- this.isBeingDestroyed() || y.the.register(t, w);
1125
+ const p = await this.contextPromise;
1126
+ this.isBeingDestroyed() || b.the.register(t, p);
1157
1127
  }
1158
1128
  /**
1159
1129
  * Destroys the context component. Unmounts root from the editor.
@@ -1164,28 +1134,28 @@ class Tt extends S {
1164
1134
  try {
1165
1135
  await (await this.contextPromise)?.destroy();
1166
1136
  } finally {
1167
- this.contextPromise = null, y.the.hasItem(t) && y.the.unregister(t);
1137
+ this.contextPromise = null, b.the.hasItem(t) && b.the.unregister(t);
1168
1138
  }
1169
1139
  }
1170
1140
  }
1171
- function It(r) {
1141
+ function Et(r) {
1172
1142
  return r.hasAttribute("data-cke-context");
1173
1143
  }
1174
- function Ot(r) {
1144
+ function vt(r) {
1175
1145
  let t = r;
1176
1146
  for (; t; ) {
1177
- if (It(t))
1147
+ if (Et(t))
1178
1148
  return t;
1179
1149
  t = t.parentElement;
1180
1150
  }
1181
1151
  return null;
1182
1152
  }
1183
- async function xt(r) {
1184
- const t = Ot(r);
1185
- return t ? y.the.waitFor(t.id) : null;
1153
+ async function Ct(r) {
1154
+ const t = vt(r);
1155
+ return t ? b.the.waitFor(t.id) : null;
1186
1156
  }
1187
- const Dt = N(Tt);
1188
- function St(r, t) {
1157
+ const At = D(kt);
1158
+ function Pt(r, t) {
1189
1159
  const e = /* @__PURE__ */ new Set();
1190
1160
  return (a) => {
1191
1161
  let n = !1;
@@ -1200,7 +1170,7 @@ function St(r, t) {
1200
1170
  }), n;
1201
1171
  };
1202
1172
  }
1203
- async function Nt() {
1173
+ async function Tt() {
1204
1174
  const { Plugin: r, FileRepository: t } = await import("ckeditor5");
1205
1175
  return class extends r {
1206
1176
  /**
@@ -1220,11 +1190,11 @@ async function Nt() {
1220
1190
  if (!s || n.has("SimpleUploadAdapter") || n.has("Base64UploadAdapter") || n.has("CKFinderUploadAdapter"))
1221
1191
  return;
1222
1192
  const c = n.get(t);
1223
- c.createUploadAdapter = (o) => new Mt(o, s);
1193
+ c.createUploadAdapter = (o) => new It(o, s);
1224
1194
  }
1225
1195
  };
1226
1196
  }
1227
- class Mt {
1197
+ class It {
1228
1198
  loader;
1229
1199
  uploadUrl;
1230
1200
  abortController = null;
@@ -1239,7 +1209,7 @@ class Mt {
1239
1209
  this.abortController = new AbortController();
1240
1210
  const e = new FormData();
1241
1211
  e.append("file", t), t.size && (this.loader.uploadTotal = t.size, this.loader.uploaded = 0);
1242
- const a = {}, n = it();
1212
+ const a = {}, n = et();
1243
1213
  n && (a["X-CSRF-Token"] = n);
1244
1214
  try {
1245
1215
  const i = await fetch(this.uploadUrl, {
@@ -1272,7 +1242,7 @@ class Mt {
1272
1242
  this.abortController?.abort(), this.abortController = null;
1273
1243
  }
1274
1244
  }
1275
- async function Rt({
1245
+ async function xt({
1276
1246
  editorId: r,
1277
1247
  saveDebounceMs: t
1278
1248
  }) {
@@ -1297,7 +1267,7 @@ async function Rt({
1297
1267
  */
1298
1268
  afterInit() {
1299
1269
  const { editor: n } = this;
1300
- this.input = document.getElementById(`${r}_input`), this.input && (n.model.document.on("change:data", I(t, () => this.sync())), n.once("ready", this.sync), this.form = this.input.closest("form"), this.form?.addEventListener("submit", this.sync));
1270
+ this.input = document.getElementById(`${r}_input`), this.input && (n.model.document.on("change:data", A(t, () => this.sync())), n.once("ready", this.sync), this.form = this.input.closest("form"), this.form?.addEventListener("submit", this.sync));
1301
1271
  }
1302
1272
  /**
1303
1273
  * Synchronizes the editor's content with the input field.
@@ -1314,8 +1284,8 @@ async function Rt({
1314
1284
  }
1315
1285
  };
1316
1286
  }
1317
- const U = /* @__PURE__ */ Symbol("suppress-phoenix-sync");
1318
- async function Vt(r) {
1287
+ const $ = /* @__PURE__ */ Symbol("suppress-phoenix-sync");
1288
+ async function Dt(r) {
1319
1289
  const { Plugin: t } = await import("ckeditor5"), { editorId: e, saveDebounceMs: a, events: n, pushEvent: i, handleEvent: s } = r;
1320
1290
  return class extends t {
1321
1291
  /**
@@ -1332,10 +1302,10 @@ async function Vt(r) {
1332
1302
  n.change && this.setupTypingContentPush(), n.blur && this.setupEventPush("blur"), n.focus && this.setupEventPush("focus"), n.ready && this.editor.once("ready", () => {
1333
1303
  i("ckeditor5:ready", {
1334
1304
  editorId: e,
1335
- data: M(o)
1305
+ data: S(o)
1336
1306
  });
1337
- }), s("ckeditor5:set-data", ({ editorId: u, data: l }) => {
1338
- (st(u) || u === e) && o.setData(l);
1307
+ }), s("ckeditor5:set-data", ({ editorId: u, data: d }) => {
1308
+ (rt(u) || u === e) && o.setData(d);
1339
1309
  });
1340
1310
  }
1341
1311
  /**
@@ -1343,64 +1313,64 @@ async function Vt(r) {
1343
1313
  */
1344
1314
  setupTypingContentPush() {
1345
1315
  const { editor: o } = this;
1346
- let u = null, l = !1;
1347
- const d = () => {
1348
- if (l)
1316
+ let u = null, d = !1;
1317
+ const l = () => {
1318
+ if (d)
1349
1319
  return;
1350
- const m = M(o);
1351
- (!u || !ct(u, m)) && (i(
1320
+ const m = S(o);
1321
+ (!u || !it(u, m)) && (i(
1352
1322
  "ckeditor5:change",
1353
1323
  {
1354
1324
  editorId: e,
1355
1325
  data: m
1356
1326
  }
1357
1327
  ), u = m);
1358
- }, w = I(a, d);
1359
- o.model.document.on("change:data", I(10, (m) => {
1360
- if (Ut(m)) {
1328
+ }, p = A(a, l);
1329
+ o.model.document.on("change:data", A(10, (m) => {
1330
+ if (Ot(m)) {
1361
1331
  u = null;
1362
1332
  return;
1363
1333
  }
1364
- o.ui.focusTracker.isFocused ? w() : d();
1365
- })), o.once("ready", d), o.once("destroy", () => {
1366
- l = !0;
1334
+ o.ui.focusTracker.isFocused ? p() : l();
1335
+ })), o.once("ready", l), o.once("destroy", () => {
1336
+ d = !0;
1367
1337
  });
1368
1338
  }
1369
1339
  /**
1370
1340
  * Setups the event push for the editor.
1371
1341
  */
1372
1342
  setupEventPush(o) {
1373
- const { editor: u } = this, l = () => {
1374
- const { isFocused: d } = u.ui.focusTracker;
1375
- (d ? "focus" : "blur") === o && i(
1343
+ const { editor: u } = this, d = () => {
1344
+ const { isFocused: l } = u.ui.focusTracker;
1345
+ (l ? "focus" : "blur") === o && i(
1376
1346
  `ckeditor5:${o}`,
1377
1347
  {
1378
1348
  editorId: e,
1379
- data: M(u)
1349
+ data: S(u)
1380
1350
  }
1381
1351
  );
1382
1352
  };
1383
- u.ui.focusTracker.on("change:isFocused", l);
1353
+ u.ui.focusTracker.on("change:isFocused", d);
1384
1354
  }
1385
1355
  };
1386
1356
  }
1387
- function M(r) {
1388
- return r.model.document.getRootNames().reduce((e, a) => (e[a] = r.getData({ rootName: a }), e), /* @__PURE__ */ Object.create({}));
1357
+ function S(r) {
1358
+ return r.model.document.getRootNames().reduce((e, a) => (e[a] = r.getData({ rootName: a }), e), /* @__PURE__ */ Object.create(null));
1389
1359
  }
1390
- function Ut(r) {
1391
- const t = r[U];
1392
- return delete r[U], !!t;
1360
+ function Ot(r) {
1361
+ const t = r[$];
1362
+ return delete r[$], !!t;
1393
1363
  }
1394
- function jt(r) {
1364
+ function Nt(r) {
1395
1365
  let t = !1;
1396
1366
  const e = (a) => {
1397
- t || (a[U] = !0);
1367
+ t || (a[$] = !0);
1398
1368
  };
1399
1369
  return r.model.document.once("change:data", e, { priority: "highest" }), () => {
1400
1370
  t = !0, r.model.document.off("change:data", e);
1401
1371
  };
1402
1372
  }
1403
- class tt {
1373
+ class X {
1404
1374
  /**
1405
1375
  * The DOM element being observed for attribute changes.
1406
1376
  */
@@ -1469,7 +1439,7 @@ class tt {
1469
1439
  */
1470
1440
  get attrs() {
1471
1441
  return {
1472
- rootAttributes: ot(this.el.getAttribute(this.rootAttrsAttrName)),
1442
+ rootAttributes: nt(this.el.getAttribute(this.rootAttrsAttrName)),
1473
1443
  value: this.el.getAttribute(this.valueAttrName)
1474
1444
  };
1475
1445
  }
@@ -1485,7 +1455,7 @@ class tt {
1485
1455
  };
1486
1456
  t.model.enqueueChange({ isUndoable: !1 }, () => {
1487
1457
  let i = this.attrsUpdater?.(a);
1488
- e !== this.previousValue && (this.previousValue = e, t.ui.focusTracker.isFocused ? this.pendingValue = e : (this.setRootValue(t, this.rootName, e), i = !0)), i && (n = jt(t));
1458
+ e !== this.previousValue && (this.previousValue = e, t.ui.focusTracker.isFocused ? this.pendingValue = e : (this.setRootValue(t, this.rootName, e), i = !0)), i && (n = Nt(t));
1489
1459
  }), n();
1490
1460
  }
1491
1461
  /**
@@ -1493,7 +1463,7 @@ class tt {
1493
1463
  * Registers cleanup via onBeforeDestroy.
1494
1464
  */
1495
1465
  setupSyncHandlers(t, e) {
1496
- this.attrsUpdater = St(t, e), this.attrsUpdater(this.attrs.rootAttributes);
1466
+ this.attrsUpdater = Pt(t, e), this.attrsUpdater(this.attrs.rootAttributes);
1497
1467
  const a = () => {
1498
1468
  this.pendingValue = null;
1499
1469
  }, n = () => {
@@ -1517,10 +1487,10 @@ class tt {
1517
1487
  this.isDestroyed = !0, this.cleanupCallbacks.forEach((t) => t()), this.cleanupCallbacks = [];
1518
1488
  }
1519
1489
  }
1520
- class h extends K {
1490
+ class h extends W {
1521
1491
  static the = new h();
1522
1492
  }
1523
- class $t extends S {
1493
+ class St extends x {
1524
1494
  /**
1525
1495
  * The sentinel instance responsible for tracking and updating root values and attributes
1526
1496
  * for single-root editors.
@@ -1533,8 +1503,8 @@ class $t extends S {
1533
1503
  const { el: t } = this, e = t.getAttribute.bind(t), a = t.hasAttribute.bind(t), n = {
1534
1504
  editorId: e("id"),
1535
1505
  contextId: e("data-cke-context-id"),
1536
- preset: kt(t),
1537
- editableHeight: q(e("data-cke-editable-height")),
1506
+ preset: pt(t),
1507
+ editableHeight: H(e("data-cke-editable-height")),
1538
1508
  watchdog: a("data-cke-watchdog"),
1539
1509
  events: {
1540
1510
  change: a("data-cke-change-event"),
@@ -1542,7 +1512,7 @@ class $t extends S {
1542
1512
  focus: a("data-cke-focus-event"),
1543
1513
  ready: a("data-cke-ready-event")
1544
1514
  },
1545
- saveDebounceMs: q(e("data-cke-save-debounce-ms")) ?? 400,
1515
+ saveDebounceMs: H(e("data-cke-save-debounce-ms")) ?? 400,
1546
1516
  language: {
1547
1517
  ui: e("data-cke-language") || "en",
1548
1518
  content: e("data-cke-content-language") || "en"
@@ -1567,7 +1537,7 @@ class $t extends S {
1567
1537
  return;
1568
1538
  const a = h.the.mountEffect(t, (n) => (n.once("destroy", () => {
1569
1539
  h.the.unregister(t, !1);
1570
- }, { priority: "highest" }), this.sentinel = new tt({
1540
+ }, { priority: "highest" }), this.sentinel = new X({
1571
1541
  editor: n,
1572
1542
  el: this.el,
1573
1543
  rootName: "main",
@@ -1578,7 +1548,7 @@ class $t extends S {
1578
1548
  }));
1579
1549
  this.onBeforeDestroy(async () => {
1580
1550
  h.the.unregister(t), a();
1581
- const n = gt(e), i = Pt(e);
1551
+ const n = dt(e), i = bt(e);
1582
1552
  n ? n.state !== "unavailable" && await n.context.remove(n.editorContextId) : i ? await i.destroy() : await e.destroy();
1583
1553
  }), h.the.register(t, e);
1584
1554
  } catch (e) {
@@ -1611,16 +1581,16 @@ class $t extends S {
1611
1581
  saveDebounceMs: s,
1612
1582
  language: c,
1613
1583
  watchdog: o
1614
- } = this.attrs, { customTranslations: u, type: l, license: d, config: { plugins: w, ...m } } = t, E = await bt(l), b = await (a ? y.the.waitFor(a) : xt(this.el)), P = async () => {
1615
- const { loadedPlugins: f, hasPremium: A } = await J(w);
1616
- T(l) && f.push(
1617
- await Rt({
1584
+ } = this.attrs, { customTranslations: u, type: d, license: l, config: { plugins: p, ...m } } = t, k = await mt(d), g = await (a ? b.the.waitFor(a) : Ct(this.el)), E = async () => {
1585
+ const { loadedPlugins: f, hasPremium: v } = await K(p);
1586
+ N(d) && f.push(
1587
+ await xt({
1618
1588
  editorId: e,
1619
1589
  saveDebounceMs: s
1620
1590
  })
1621
1591
  ), f.push(
1622
1592
  ...await Promise.all([
1623
- Vt(
1593
+ Dt(
1624
1594
  {
1625
1595
  editorId: e,
1626
1596
  saveDebounceMs: s,
@@ -1629,61 +1599,56 @@ class $t extends S {
1629
1599
  handleEvent: this.handleEvent.bind(this)
1630
1600
  }
1631
1601
  ),
1632
- Nt()
1602
+ Tt()
1633
1603
  ])
1634
1604
  );
1635
- const k = [
1636
- ...await G(c, A),
1637
- X(u?.dictionary || {})
1638
- ].filter((C) => !Y(C));
1639
- let v = L(e);
1640
- T(l) && (v = v.main || "");
1641
- let g = Q(e);
1642
- if (!(g instanceof HTMLElement) && !("main" in g)) {
1643
- const C = l === "decoupled" ? ["main"] : Object.keys(v);
1644
- et(g, C) || (g = await _t(e, C), v = L(e));
1645
- }
1646
- T(l) && "main" in g && (g = g.main);
1647
- let p = {
1605
+ const y = [
1606
+ ...await Y(c, v),
1607
+ J(u?.dictionary || {})
1608
+ ].filter((z) => !L(z));
1609
+ let C = G(e);
1610
+ const O = Object.keys(C);
1611
+ N(d) && O.push("main"), Q(C, O) || (C = await Rt(e, O));
1612
+ let w = {
1648
1613
  ...m,
1649
- licenseKey: d.key,
1614
+ licenseKey: l.key,
1650
1615
  plugins: f,
1651
1616
  language: c,
1652
- ...k.length && {
1653
- translations: k
1617
+ ...y.length && {
1618
+ translations: y
1654
1619
  }
1655
1620
  };
1656
- p = x(p), p = D([...k].reverse(), c.ui, p), p = mt(E, g, p), p = dt(v, p);
1657
- const z = await (async () => b ? (await wt({
1658
- context: b,
1659
- creator: E,
1660
- config: p
1661
- })).editor : E.create(p))();
1662
- return T(l) && n && vt(z, n), z;
1621
+ w = T(w), w = I([...y].reverse(), c.ui, w), w = ct(k, C, w);
1622
+ const _ = await (async () => g ? (await lt({
1623
+ context: g,
1624
+ creator: k,
1625
+ config: w
1626
+ })).editor : k.create(w))();
1627
+ return N(d) && n && wt(_, n), _;
1663
1628
  };
1664
- if (o && !b) {
1665
- const f = await Ct(P, t.watchdog);
1666
- return f.on("error", (A, { causesRestart: _ }) => {
1667
- if (_) {
1668
- const k = h.the.getItem(e);
1669
- k && (ft(k), h.the.unregister(e));
1629
+ if (o && !g) {
1630
+ const f = await gt(E, t.watchdog);
1631
+ return f.on("error", (v, { causesRestart: M }) => {
1632
+ if (M) {
1633
+ const y = h.the.getItem(e);
1634
+ y && (ut(y), h.the.unregister(e));
1670
1635
  }
1671
1636
  }), f.on("restart", () => {
1672
- const A = f.editor;
1673
- h.the.register(e, A);
1637
+ const v = f.editor;
1638
+ h.the.register(e, v);
1674
1639
  }), await f.create({}), f.editor;
1675
1640
  }
1676
- return P();
1641
+ return E();
1677
1642
  }
1678
1643
  }
1679
- function et(r, t) {
1644
+ function Q(r, t) {
1680
1645
  return t.every((e) => r[e]);
1681
1646
  }
1682
- async function _t(r, t) {
1683
- return lt(
1647
+ async function Rt(r, t) {
1648
+ return st(
1684
1649
  () => {
1685
- const e = Q(r);
1686
- if (!et(e, t))
1650
+ const e = G(r);
1651
+ if (!Q(e, t))
1687
1652
  throw new Error(
1688
1653
  `It looks like not all required root elements are present yet.
1689
1654
  * If you want to wait for them, ensure they are registered before editor initialization.
@@ -1695,8 +1660,8 @@ Missing roots: ${t.filter((a) => !e[a]).join(", ")}.`
1695
1660
  { timeOutAfter: 2e3, retryAfter: 100 }
1696
1661
  );
1697
1662
  }
1698
- const zt = N($t);
1699
- class Ht extends S {
1663
+ const Vt = D(St);
1664
+ class $t extends x {
1700
1665
  /**
1701
1666
  * The sentinel instance responsible for tracking and updating root values and attributes.
1702
1667
  */
@@ -1709,6 +1674,7 @@ class Ht extends S {
1709
1674
  editableId: this.el.getAttribute("id"),
1710
1675
  editorId: this.el.getAttribute("data-cke-editor-id") || null,
1711
1676
  rootName: this.el.getAttribute("data-cke-editable-root-name"),
1677
+ modelElement: this.el.getAttribute("data-cke-editable-root-model-element-name") || null,
1712
1678
  initialValue: this.el.getAttribute("data-cke-editable-initial-value") || ""
1713
1679
  };
1714
1680
  return Object.defineProperty(this, "attrs", {
@@ -1722,37 +1688,38 @@ class Ht extends S {
1722
1688
  * Mounts the editable component.
1723
1689
  */
1724
1690
  mounted() {
1725
- const { editableId: t, editorId: e, rootName: a, initialValue: n } = this.attrs, i = h.the.mountEffect(e, (s) => {
1726
- const c = this.el.querySelector("[data-cke-editable-content]");
1691
+ const { editableId: t, editorId: e, rootName: a, initialValue: n, modelElement: i } = this.attrs, s = h.the.mountEffect(e, (c) => {
1692
+ const o = this.el.querySelector("[data-cke-editable-content]");
1727
1693
  if (this.isBeingDestroyed())
1728
1694
  return;
1729
- const o = this.el.querySelector(`#${t}_input`);
1730
- if (B(s) && !s.model.document.getRoot(a)) {
1731
- const { ui: l, editing: d } = s;
1732
- s.addRoot(a, {
1695
+ const u = this.el.querySelector(`#${t}_input`);
1696
+ if (q(c) && !c.model.document.getRoot(a)) {
1697
+ const { ui: l, editing: p } = c;
1698
+ c.addRoot(a, {
1733
1699
  isUndoable: !1,
1734
- initialData: n
1700
+ initialData: n,
1701
+ modelElement: i || "$root"
1735
1702
  });
1736
- const w = l.view.createEditable(a, c);
1737
- l.addEditable(w), d.view.forceRender();
1703
+ const m = l.view.createEditable(a, o);
1704
+ l.addEditable(m), p.view.forceRender();
1738
1705
  }
1739
- this.sentinel = new tt({
1706
+ this.sentinel = new X({
1740
1707
  el: this.el,
1741
- editor: s,
1708
+ editor: c,
1742
1709
  rootName: a,
1743
1710
  valueAttrName: "data-cke-editable-initial-value",
1744
1711
  rootAttrsAttrName: "data-cke-editable-root-attrs"
1745
1712
  });
1746
- const u = o ? Bt(o, s, a) : null;
1713
+ const d = u ? Mt(u, c, a) : null;
1747
1714
  return () => {
1748
- if (u?.(), this.sentinel?.destroy(), this.sentinel = null, s.state !== "destroyed") {
1749
- const l = s.model.document.getRoot(a);
1750
- l && B(s) && (s.ui.view.editables[a] && s.detachEditable(l), l.isAttached() && s.detachRoot(a, !1));
1715
+ if (d?.(), this.sentinel?.destroy(), this.sentinel = null, c.state !== "destroyed") {
1716
+ const l = c.model.document.getRoot(a);
1717
+ l && q(c) && (c.ui.view.editables[a] && c.detachEditable(l), l.isAttached() && c.detachRoot(a, !1));
1751
1718
  }
1752
1719
  };
1753
1720
  });
1754
1721
  this.onBeforeDestroy(() => {
1755
- this.el.style.display = "none", i();
1722
+ this.el.style.display = "none", s();
1756
1723
  });
1757
1724
  }
1758
1725
  /**
@@ -1762,16 +1729,16 @@ class Ht extends S {
1762
1729
  this.sentinel?.updated();
1763
1730
  }
1764
1731
  }
1765
- const qt = N(Ht);
1766
- function Bt(r, t, e) {
1732
+ const Ut = D($t);
1733
+ function Mt(r, t, e) {
1767
1734
  const a = () => {
1768
1735
  r.value = t.getData({ rootName: e });
1769
- }, n = I(200, a);
1736
+ }, n = A(200, a);
1770
1737
  return t.model.document.on("change:data", n), a(), () => {
1771
1738
  t.model.document.off("change:data", n);
1772
1739
  };
1773
1740
  }
1774
- class Ft extends S {
1741
+ class _t extends x {
1775
1742
  /**
1776
1743
  * Attributes for the editable instance.
1777
1744
  */
@@ -1794,7 +1761,7 @@ class Ft extends S {
1794
1761
  const { editorId: t, name: e } = this.attrs, a = h.the.mountEffect(t, (n) => {
1795
1762
  if (this.isBeingDestroyed())
1796
1763
  return;
1797
- const { ui: i } = n, s = Lt(e), c = i.view[s];
1764
+ const { ui: i } = n, s = zt(e), c = i.view[s];
1798
1765
  if (!c) {
1799
1766
  console.error(`Unknown UI part name: "${e}". Supported names are "toolbar" and "menubar".`);
1800
1767
  return;
@@ -1808,7 +1775,7 @@ class Ft extends S {
1808
1775
  });
1809
1776
  }
1810
1777
  }
1811
- function Lt(r) {
1778
+ function zt(r) {
1812
1779
  switch (r) {
1813
1780
  case "toolbar":
1814
1781
  return "toolbar";
@@ -1818,18 +1785,18 @@ function Lt(r) {
1818
1785
  return null;
1819
1786
  }
1820
1787
  }
1821
- const Wt = N(Ft), Gt = {
1822
- CKEditor5: zt,
1823
- CKEditable: qt,
1824
- CKUIPart: Wt,
1825
- CKContext: Dt
1788
+ const jt = D(_t), Ft = {
1789
+ CKEditor5: Vt,
1790
+ CKEditable: Ut,
1791
+ CKUIPart: jt,
1792
+ CKContext: At
1826
1793
  };
1827
1794
  export {
1828
- y as ContextsRegistry,
1829
- $ as CustomEditorPluginsRegistry,
1795
+ b as ContextsRegistry,
1796
+ U as CustomEditorPluginsRegistry,
1830
1797
  h as EditorsRegistry,
1831
- Gt as Hooks,
1832
- gt as unwrapEditorContext,
1833
- Pt as unwrapEditorWatchdog
1798
+ Ft as Hooks,
1799
+ dt as unwrapEditorContext,
1800
+ bt as unwrapEditorWatchdog
1834
1801
  };
1835
1802
  //# sourceMappingURL=index.mjs.map