@themarioga/grid-editor 8.4.0 → 8.6.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 (105) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +105 -5
  3. package/dist/grideditor.bundle.min.js +3 -3
  4. package/dist/grideditor.css +15 -4
  5. package/dist/grideditor.d.ts +76 -4
  6. package/dist/grideditor.esm.js +212 -38
  7. package/dist/grideditor.js +212 -38
  8. package/dist/grideditor.min.css +1 -1
  9. package/dist/grideditor.min.css.map +1 -1
  10. package/dist/grideditor.min.js +2 -2
  11. package/dist/grideditor.min.js.map +3 -3
  12. package/dist/locales/grideditor.es.esm.js +5 -0
  13. package/dist/locales/grideditor.es.js +5 -0
  14. package/dist/locales/grideditor.es.min.js +1 -1
  15. package/dist/locales/grideditor.es.min.js.map +2 -2
  16. package/dist/plugins/grideditor.accordion.esm.js +7 -0
  17. package/dist/plugins/grideditor.accordion.js +7 -0
  18. package/dist/plugins/grideditor.accordion.min.js +1 -1
  19. package/dist/plugins/grideditor.accordion.min.js.map +3 -3
  20. package/dist/plugins/grideditor.autosave.esm.js +309 -0
  21. package/dist/plugins/grideditor.autosave.js +316 -0
  22. package/dist/plugins/grideditor.autosave.min.js +1 -0
  23. package/dist/plugins/grideditor.autosave.min.js.map +7 -0
  24. package/dist/plugins/grideditor.card.esm.js +10 -0
  25. package/dist/plugins/grideditor.card.js +10 -0
  26. package/dist/plugins/grideditor.card.min.js +1 -1
  27. package/dist/plugins/grideditor.card.min.js.map +3 -3
  28. package/dist/plugins/grideditor.ckeditor.esm.js +30 -1
  29. package/dist/plugins/grideditor.ckeditor.js +30 -1
  30. package/dist/plugins/grideditor.ckeditor.min.js +1 -1
  31. package/dist/plugins/grideditor.ckeditor.min.js.map +3 -3
  32. package/dist/plugins/grideditor.clipboard.esm.js +3 -0
  33. package/dist/plugins/grideditor.clipboard.js +3 -0
  34. package/dist/plugins/grideditor.clipboard.min.js +1 -1
  35. package/dist/plugins/grideditor.clipboard.min.js.map +2 -2
  36. package/dist/plugins/grideditor.codemirror-inline.esm.js +11 -0
  37. package/dist/plugins/grideditor.codemirror-inline.js +11 -0
  38. package/dist/plugins/grideditor.codemirror-inline.min.js +1 -1
  39. package/dist/plugins/grideditor.codemirror-inline.min.js.map +3 -3
  40. package/dist/plugins/grideditor.elements.esm.js +78 -4
  41. package/dist/plugins/grideditor.elements.js +78 -4
  42. package/dist/plugins/grideditor.elements.min.js +1 -1
  43. package/dist/plugins/grideditor.elements.min.js.map +3 -3
  44. package/dist/plugins/grideditor.gutters.esm.js +5 -0
  45. package/dist/plugins/grideditor.gutters.js +5 -0
  46. package/dist/plugins/grideditor.gutters.min.js +1 -1
  47. package/dist/plugins/grideditor.gutters.min.js.map +3 -3
  48. package/dist/plugins/grideditor.inline-style.esm.js +18 -10
  49. package/dist/plugins/grideditor.inline-style.js +18 -10
  50. package/dist/plugins/grideditor.inline-style.min.js +3 -3
  51. package/dist/plugins/grideditor.inline-style.min.js.map +3 -3
  52. package/dist/plugins/grideditor.order.esm.js +5 -0
  53. package/dist/plugins/grideditor.order.js +5 -0
  54. package/dist/plugins/grideditor.order.min.js +1 -1
  55. package/dist/plugins/grideditor.order.min.js.map +3 -3
  56. package/dist/plugins/grideditor.popup.esm.js +17 -6
  57. package/dist/plugins/grideditor.popup.js +17 -6
  58. package/dist/plugins/grideditor.popup.min.js +1 -1
  59. package/dist/plugins/grideditor.popup.min.js.map +3 -3
  60. package/dist/plugins/grideditor.sections.esm.js +6 -3
  61. package/dist/plugins/grideditor.sections.js +6 -3
  62. package/dist/plugins/grideditor.sections.min.js +1 -1
  63. package/dist/plugins/grideditor.sections.min.js.map +3 -3
  64. package/dist/plugins/grideditor.summernote.esm.js +40 -1
  65. package/dist/plugins/grideditor.summernote.js +40 -1
  66. package/dist/plugins/grideditor.summernote.min.js +1 -1
  67. package/dist/plugins/grideditor.summernote.min.js.map +3 -3
  68. package/dist/plugins/grideditor.tabs.esm.js +7 -0
  69. package/dist/plugins/grideditor.tabs.js +7 -0
  70. package/dist/plugins/grideditor.tabs.min.js +1 -1
  71. package/dist/plugins/grideditor.tabs.min.js.map +3 -3
  72. package/dist/plugins/grideditor.tinymce.esm.js +32 -1
  73. package/dist/plugins/grideditor.tinymce.js +32 -1
  74. package/dist/plugins/grideditor.tinymce.min.js +1 -1
  75. package/dist/plugins/grideditor.tinymce.min.js.map +3 -3
  76. package/docs/events.md +8 -0
  77. package/docs/locale-keys.md +16 -1
  78. package/docs/plugins.md +60 -7
  79. package/example/autosave.html +39 -22
  80. package/example/index.html +1 -1
  81. package/example/plugins.html +10 -0
  82. package/package.json +1 -2
  83. package/src/js/grideditor.js +248 -28
  84. package/src/js/inline-style/display.js +4 -3
  85. package/src/js/inline-style/drawerflow.js +10 -5
  86. package/src/js/locales/grideditor.es.js +5 -0
  87. package/src/js/plugins/grideditor.accordion.js +6 -0
  88. package/src/js/plugins/grideditor.autosave.js +349 -0
  89. package/src/js/plugins/grideditor.card.js +6 -0
  90. package/src/js/plugins/grideditor.ckeditor.js +6 -0
  91. package/src/js/plugins/grideditor.clipboard.js +3 -0
  92. package/src/js/plugins/grideditor.codemirror-inline.js +7 -0
  93. package/src/js/plugins/grideditor.elements.js +98 -4
  94. package/src/js/plugins/grideditor.gutters.js +4 -0
  95. package/src/js/plugins/grideditor.inline-style.js +2 -0
  96. package/src/js/plugins/grideditor.order.js +4 -0
  97. package/src/js/plugins/grideditor.popup.js +15 -6
  98. package/src/js/plugins/grideditor.sections.js +4 -3
  99. package/src/js/plugins/grideditor.summernote.js +14 -0
  100. package/src/js/plugins/grideditor.tabs.js +6 -0
  101. package/src/js/plugins/grideditor.tinymce.js +8 -0
  102. package/src/js/text/grideditor.text.js +27 -1
  103. package/src/less/grideditor.less +16 -8
  104. package/types/grideditor.d.ts +76 -4
  105. package/AUTO_SAVE.md +0 -30
@@ -2,6 +2,8 @@
2
2
  position: relative;
3
3
  min-height: 22px;
4
4
  margin-bottom: 10px;
5
+ /* The add buttons on the left are grey, so the blue is left to the
6
+ source, preview and layout buttons on the right */
5
7
  /* toolbar_groups: the tabs choosing which category of add buttons
6
8
  shows, at the start of the line. Never squeezed, and never behind the
7
9
  more button */
@@ -23,16 +25,25 @@
23
25
  z-index: 20;
24
26
  top: 0;
25
27
  }
26
- .ge-mainControls .ge-addRowGroup .btn.btn-primary {
28
+ .ge-mainControls .ge-addRowGroup .btn.btn-light,
29
+ .ge-mainControls .ge-addContainerGroup .btn.btn-light,
30
+ .ge-mainControls .ge-toolbar-more > .btn.btn-light {
27
31
  background: #f4f4f4;
28
32
  color: #666;
29
33
  border-color: #999;
30
- display: flex;
31
- align-items: center;
32
34
  }
33
- .ge-mainControls .ge-addRowGroup .btn.btn-primary:hover {
35
+ .ge-mainControls .ge-addRowGroup .btn.btn-light:hover,
36
+ .ge-mainControls .ge-addContainerGroup .btn.btn-light:hover,
37
+ .ge-mainControls .ge-toolbar-more > .btn.btn-light:hover,
38
+ .ge-mainControls .ge-addRowGroup .btn.btn-light:active,
39
+ .ge-mainControls .ge-addContainerGroup .btn.btn-light:active,
40
+ .ge-mainControls .ge-toolbar-more > .btn.btn-light:active {
34
41
  background: #E3E3E3;
35
42
  }
43
+ .ge-mainControls .ge-addRowGroup .btn.btn-light {
44
+ display: flex;
45
+ align-items: center;
46
+ }
36
47
  .ge-mainControls .ge-addRowGroup .ge-row-icon {
37
48
  width: 48px;
38
49
  /* A button shrinks to its content, and an equal column has none */
@@ -125,7 +125,9 @@ export interface GridEditorOptions {
125
125
  inline_style?: InlineStyleOptions;
126
126
  /** The tabs plugin: the variant of the tabs containers made new. */
127
127
  tabs?: TabsOptions;
128
- elements?: { enabled?: boolean | 'auto'; selector?: string; auto?: boolean };
128
+ elements?: { enabled?: boolean | 'auto'; selector?: string; auto?: boolean; types?: ElementType[] };
129
+ /** The autosave plugin: what is edited, kept in the browser's storage as it changes. */
130
+ autosave?: AutosaveOptions;
129
131
  custom_filter?: CustomFilter | CustomFilter[] | '';
130
132
  valid_col_sizes?: ColumnSize[];
131
133
  valid_col_offsets?: number[];
@@ -151,6 +153,50 @@ export interface GridEditorOptions {
151
153
  [plugin: string]: unknown;
152
154
  }
153
155
 
156
+ /** The autosave plugin's options. */
157
+ export interface AutosaveOptions {
158
+ /** Saving from the start; the methods turn it on and off. Default true. */
159
+ enabled?: boolean;
160
+ /** localStorage or sessionStorage. Default 'local'. */
161
+ storage?: 'local' | 'session';
162
+ /** Where the draft is kept. Default 'grideditor.autosave:' + the page's path + '#' + the canvas's id. One per document on a page that loads several. */
163
+ key?: string | null;
164
+ /** Milliseconds after the last change. Default 1000. */
165
+ delay?: number;
166
+ /** Milliseconds a draft is offered for; null, the default, for ever. */
167
+ maxAge?: number | null;
168
+ }
169
+
170
+ /** A draft the autosave plugin kept. */
171
+ export interface AutosaveDraft {
172
+ html: string;
173
+ /** When, as an ISO 8601 date. */
174
+ savedAt: string;
175
+ }
176
+
177
+ export interface GetHtmlOptions {
178
+ /** Read the canvas without it leaving editing: open texts and settings panels stay open. */
179
+ keepEditing?: boolean;
180
+ }
181
+
182
+ export interface AutosavePayload {
183
+ canvas: HTMLElement;
184
+ html: string;
185
+ savedAt: string;
186
+ source: 'change' | 'pagehide' | 'api' | 'destroy';
187
+ }
188
+
189
+ export interface RestoreDraftPayload {
190
+ canvas: HTMLElement;
191
+ html: string;
192
+ savedAt: string;
193
+ }
194
+
195
+ export interface AutosaveErrorPayload {
196
+ canvas: HTMLElement;
197
+ error: unknown;
198
+ }
199
+
154
200
  /** Where a node was, or went: its parent and its index among the blocks there. */
155
201
  export interface Position {
156
202
  parent: HTMLElement;
@@ -271,6 +317,9 @@ export interface GridEditorEventMap {
271
317
  'grideditor:popup-orphan': PopupOrphanPayload;
272
318
  'grideditor:view-change': ViewChangePayload;
273
319
  'grideditor:target-change': TargetChangePayload;
320
+ 'grideditor:after-autosave': AutosavePayload;
321
+ 'grideditor:after-restore-draft': RestoreDraftPayload;
322
+ 'grideditor:autosave-error': AutosaveErrorPayload;
274
323
  }
275
324
 
276
325
  /** An event the editor dispatches on its canvas: a CustomEvent whose detail is the payload. */
@@ -286,6 +335,22 @@ export type GridEditorCallbacks = {
286
335
  };
287
336
 
288
337
  /** A plugin's factory: called once per editor with the handle in docs/plugins.md. */
338
+ /** An element the toolbar offers, from elements.types. */
339
+ export interface ElementType {
340
+ /** What it is: its data-ge-element. */
341
+ type: string;
342
+ /** Its markup: html, or a function called for each one made that returns html or a node. */
343
+ html: string | (() => string | Node);
344
+ /** Its button's label, as it is. */
345
+ label?: string;
346
+ /** Its button's label, a locale key: wins over label, and follows setLocale. */
347
+ labelKey?: string;
348
+ /** The button shows this icon alone, with the label as its title. */
349
+ iconClass?: string;
350
+ /** Its toolbar_groups category. Default 'elements'. */
351
+ group?: string;
352
+ }
353
+
289
354
  export type PluginFactory = ((ge: any) => Record<string, unknown>) & { always?: boolean };
290
355
 
291
356
  export interface CreateColumnOptions extends Placement {
@@ -346,10 +411,10 @@ export declare class GridEditor {
346
411
  /** A read-only copy of the settings the editor runs with. */
347
412
  readonly settings: Readonly<GridEditorOptions>;
348
413
 
349
- /** The html to save: no drawers, no editor classes, no inline styles. */
350
- getHtml(): string;
414
+ /** The html to save: no drawers, no editor classes, no inline styles. keepEditing reads it without the canvas leaving editing. */
415
+ getHtml(options?: GetHtmlOptions): string;
351
416
  /** getHtml without grid-editor's marking, for publishing. It cannot be edited again as it was. */
352
- getPlainHtml(): string;
417
+ getPlainHtml(options?: GetHtmlOptions): string;
353
418
  /** Run the editing pass over the canvas again, after markup was put in. */
354
419
  init(): this;
355
420
  /** Take the editing furniture off, leaving the markup. */
@@ -379,6 +444,13 @@ export declare class GridEditor {
379
444
  getUtility(node: Target, family: string, view?: View): string | null;
380
445
  /** Write one through the events; null is inherit. False when canceled or nothing changed. */
381
446
  setUtility(node: Target, family: string, value: string | number | null, view?: View): boolean;
447
+ /** The autosave plugin's, when it is loaded: false when the browser gives it no storage. */
448
+ enableAutosave?(): boolean;
449
+ disableAutosave?(): boolean;
450
+ /** Save now, if it changed: true when it was written. */
451
+ saveDraft?(): boolean;
452
+ getDraft?(): AutosaveDraft | null;
453
+ clearDraft?(): boolean;
382
454
  }
383
455
 
384
456
  export default GridEditor;
@@ -392,8 +392,10 @@ var NESTED_SETTINGS = {
392
392
  // 'auto' turns them on when the page has any
393
393
  selector: "[data-ge-element]",
394
394
  // What the host marks an element with
395
- auto: false
395
+ auto: false,
396
396
  // Treat every child of a content area as an element
397
+ types: []
398
+ // The elements the toolbar offers: { type, html, label | labelKey, iconClass, group }
397
399
  },
398
400
  resize: {
399
401
  enabled: true,
@@ -760,31 +762,53 @@ function build(instance, baseElem, optionsOrMethod) {
760
762
  whenConfirmed();
761
763
  return;
762
764
  }
763
- var Modal = modalLibrary();
764
- if (!Modal) {
765
- if (window.confirm(message)) {
765
+ confirmWith(message, { danger: true }, function(confirmed) {
766
+ if (confirmed) {
766
767
  whenConfirmed();
767
768
  }
769
+ });
770
+ }
771
+ function confirmWith(message, options, answer) {
772
+ options = options || {};
773
+ var Modal = modalLibrary();
774
+ if (!Modal) {
775
+ answer(window.confirm(message));
768
776
  return;
769
777
  }
770
778
  var modal = confirmModal();
771
779
  var confirmed = false;
780
+ var ok = one(modal, ".ge-confirm-ok");
781
+ if (pendingAnswer) {
782
+ pendingAnswer(null);
783
+ }
784
+ var settle = function(value) {
785
+ if (pendingAnswer !== settle) {
786
+ return;
787
+ }
788
+ pendingAnswer = null;
789
+ answer(value);
790
+ };
791
+ pendingAnswer = settle;
792
+ one(modal, ".modal-title").textContent = options.title || t("confirm.title");
793
+ one(modal, ".ge-confirm-cancel").textContent = options.cancel || t("confirm.cancel");
794
+ ok.textContent = options.ok || t("confirm.ok");
795
+ toggleClass(ok, "btn-danger", !!options.danger);
796
+ toggleClass(ok, "btn-primary", !options.danger);
772
797
  one(modal, ".ge-confirm-message").textContent = message;
773
- one(modal, ".ge-confirm-ok").onclick = function() {
798
+ ok.onclick = function() {
774
799
  confirmed = true;
775
800
  Modal.getInstance(modal).hide();
776
801
  };
777
802
  confirmHandlers.hidden = function() {
778
- if (confirmed) {
779
- whenConfirmed();
780
- }
803
+ settle(confirmed);
781
804
  };
782
805
  confirmHandlers.shown = function() {
783
- one(modal, ".ge-confirm-ok").focus();
806
+ ok.focus();
784
807
  };
785
808
  Modal.getOrCreateInstance(modal).show();
786
809
  }
787
810
  var confirmHandlers = { hidden: null, shown: null };
811
+ var pendingAnswer = null;
788
812
  function confirmModal() {
789
813
  if (confirmDialog) {
790
814
  return confirmDialog;
@@ -798,13 +822,14 @@ function build(instance, baseElem, optionsOrMethod) {
798
822
  one(confirmDialog, ".ge-confirm-cancel").textContent = t("confirm.cancel");
799
823
  one(confirmDialog, ".ge-confirm-ok").textContent = t("confirm.ok");
800
824
  trackModal(confirmDialog);
825
+ var asking = confirmDialog;
801
826
  confirmDialog.addEventListener("hidden.bs.modal", function() {
802
- if (confirmHandlers.hidden) {
827
+ if (asking === confirmDialog && confirmHandlers.hidden) {
803
828
  confirmHandlers.hidden();
804
829
  }
805
830
  });
806
831
  confirmDialog.addEventListener("shown.bs.modal", function() {
807
- if (confirmHandlers.shown) {
832
+ if (asking === confirmDialog && confirmHandlers.shown) {
808
833
  confirmHandlers.shown();
809
834
  }
810
835
  });
@@ -818,6 +843,9 @@ function build(instance, baseElem, optionsOrMethod) {
818
843
  confirmHandlers.shown = null;
819
844
  retireModal(confirmDialog);
820
845
  confirmDialog = null;
846
+ if (pendingAnswer) {
847
+ pendingAnswer(null);
848
+ }
821
849
  }
822
850
  function retireModal(panel) {
823
851
  var Modal = modalLibrary();
@@ -1112,7 +1140,7 @@ function build(instance, baseElem, optionsOrMethod) {
1112
1140
  settings.new_row_layouts.forEach(function(layout) {
1113
1141
  var grouped = !Array.isArray(layout);
1114
1142
  var btn = element("a", {
1115
- "class": "btn btn-sm btn-primary",
1143
+ "class": "btn btn-sm btn-light",
1116
1144
  title: grouped ? t("row.add_row_cols", { columns: layout.columns, counts: rowColsText(layout.row_cols) }) : t("row.add", { layout: layout.join("-") }),
1117
1145
  // What this button makes, in the markup rather than in
1118
1146
  // memory: a drag works on a clone of it
@@ -1147,7 +1175,7 @@ function build(instance, baseElem, optionsOrMethod) {
1147
1175
  Object.keys(CONTAINERS).forEach(function(type) {
1148
1176
  var definition = CONTAINERS[type];
1149
1177
  var button = labelButton(
1150
- element("a", { "class": "btn btn-sm btn-primary ge-add-container" }),
1178
+ element("a", { "class": "btn btn-sm btn-light ge-add-container" }),
1151
1179
  t(definition.labelKey),
1152
1180
  definition.iconClass
1153
1181
  );
@@ -1239,7 +1267,8 @@ function build(instance, baseElem, optionsOrMethod) {
1239
1267
  }
1240
1268
  var CORE_GROUPS = [
1241
1269
  { name: "rows", labelKey: "group.rows" },
1242
- { name: "content", labelKey: "group.content" }
1270
+ { name: "content", labelKey: "group.content" },
1271
+ { name: "elements", labelKey: "group.elements" }
1243
1272
  ];
1244
1273
  function toolbarGroupOf(value) {
1245
1274
  return typeof value === "string" && value.trim() !== "" ? value : null;
@@ -1267,7 +1296,7 @@ function build(instance, baseElem, optionsOrMethod) {
1267
1296
  names.forEach(function(name) {
1268
1297
  var tab = tabs.appendChild(element("button", {
1269
1298
  type: "button",
1270
- "class": "btn btn-sm btn-outline-primary",
1299
+ "class": "btn btn-sm btn-outline-secondary",
1271
1300
  role: "tab",
1272
1301
  "data-ge-group": name
1273
1302
  }, groupLabel(name)));
@@ -1316,7 +1345,7 @@ function build(instance, baseElem, optionsOrMethod) {
1316
1345
  mainControls.remove();
1317
1346
  }
1318
1347
  function createOverflowMenu(start) {
1319
- var more = create('<div class="ge-toolbar-more"><button type="button" class="btn btn-sm btn-primary"><i class="bi bi-three-dots"></i></button><div class="ge-toolbar-overflow"><div class="ge-addRowGroup btn-group"></div><div class="ge-addContainerGroup btn-group"></div></div></div>');
1348
+ var more = create('<div class="ge-toolbar-more"><button type="button" class="btn btn-sm btn-light"><i class="bi bi-three-dots"></i></button><div class="ge-toolbar-overflow"><div class="ge-addRowGroup btn-group"></div><div class="ge-addContainerGroup btn-group"></div></div></div>');
1320
1349
  start.parentNode.insertBefore(more, start.nextSibling);
1321
1350
  var toggle2 = one(more, "button");
1322
1351
  var panel = one(more, ".ge-toolbar-overflow");
@@ -1407,7 +1436,8 @@ function build(instance, baseElem, optionsOrMethod) {
1407
1436
  function featureButton(name, item, index) {
1408
1437
  var iconClass = item.iconClass || (item.align === "end" ? "bi bi-plus" : null);
1409
1438
  var label = typeof item.label === "function" ? item.label() : item.label || t(item.labelKey);
1410
- var button = labelButton(element("a", { "class": "btn btn-sm btn-primary ge-add-container ge-add-feature" }), label, iconClass);
1439
+ var colour = item.align === "end" ? "btn-primary" : "btn-light";
1440
+ var button = labelButton(element("a", { "class": "btn btn-sm " + colour + " ge-add-container ge-add-feature" }), label, iconClass);
1411
1441
  addClass(button, item.className || "");
1412
1442
  attr(button, {
1413
1443
  "data-ge-toolbar": "feature",
@@ -1420,6 +1450,9 @@ function build(instance, baseElem, optionsOrMethod) {
1420
1450
  return;
1421
1451
  }
1422
1452
  var made = item.create();
1453
+ if (!made) {
1454
+ return;
1455
+ }
1423
1456
  var placed = item.inColumn ? inRowOfItsOwn(made) : made;
1424
1457
  var added = addNode(item.kind, made, function() {
1425
1458
  canvas.appendChild(placed);
@@ -1528,6 +1561,9 @@ function build(instance, baseElem, optionsOrMethod) {
1528
1561
  function insertFeatureFromToolbar(button, where, source) {
1529
1562
  var item = FEATURES[button.getAttribute("data-ge-feature")].toolbar[parseInt(button.getAttribute("data-ge-item"), 10)];
1530
1563
  var made = item.create();
1564
+ if (!made) {
1565
+ return null;
1566
+ }
1531
1567
  var placed = made;
1532
1568
  if (item.inColumn) {
1533
1569
  if (!is(where.region, ".column")) {
@@ -1909,22 +1945,122 @@ function build(instance, baseElem, optionsOrMethod) {
1909
1945
  closeSizePicker();
1910
1946
  hideDropMarker();
1911
1947
  closeDialog();
1912
- all(canvas, ".ge-tools-drawer").forEach(function(drawer) {
1913
- drawer.remove();
1914
- });
1948
+ removeDrawers(canvas);
1915
1949
  pluginFields.clear();
1916
- unwrapTexts();
1950
+ unwrapTexts(canvas);
1917
1951
  plugins("onDeinit");
1918
1952
  clearPreviews(canvas);
1919
- all(canvas, "[data-ge-row-cols]").forEach(function(row) {
1920
- row.removeAttribute("data-ge-row-cols");
1921
- });
1953
+ clearRowCols(canvas);
1922
1954
  unmarkContainers();
1923
1955
  removeSortable();
1924
1956
  removeResizable();
1925
- runFilter(false);
1957
+ runFilter(false, canvas);
1958
+ }
1959
+ function removeDrawers(root) {
1960
+ all(root, ".ge-tools-drawer").forEach(function(drawer) {
1961
+ drawer.remove();
1962
+ });
1963
+ }
1964
+ function clearRowCols(root) {
1965
+ all(root, "[data-ge-row-cols]").forEach(function(row) {
1966
+ row.removeAttribute("data-ge-row-cols");
1967
+ });
1968
+ }
1969
+ function pluginsWithoutCleanMarkup() {
1970
+ var names = [];
1971
+ [CONTAINERS, FEATURES, UTILITIES].forEach(function(registry) {
1972
+ Object.keys(registry).forEach(function(name) {
1973
+ var plugin = registry[name];
1974
+ var marks = plugin.unmark || plugin.onDeinit || plugin.onBeforeDeinit;
1975
+ if (marks && !plugin.cleanMarkup) {
1976
+ names.push(name);
1977
+ }
1978
+ });
1979
+ });
1980
+ return names;
1981
+ }
1982
+ function copyCanvas() {
1983
+ var copy = canvas.cloneNode(true);
1984
+ var toLive = /* @__PURE__ */ new Map();
1985
+ var toCopy = /* @__PURE__ */ new Map();
1986
+ var live = document.createTreeWalker(canvas, NodeFilter.SHOW_ELEMENT);
1987
+ var copied = document.createTreeWalker(copy, NodeFilter.SHOW_ELEMENT);
1988
+ var a = live.currentNode;
1989
+ var b = copied.currentNode;
1990
+ while (a && b) {
1991
+ toLive.set(b, a);
1992
+ toCopy.set(a, b);
1993
+ a = live.nextNode();
1994
+ b = copied.nextNode();
1995
+ }
1996
+ return {
1997
+ root: copy,
1998
+ liveOf: function(node) {
1999
+ return toLive.get(node) || null;
2000
+ },
2001
+ copyOf: function(node) {
2002
+ return toCopy.get(node) || null;
2003
+ }
2004
+ };
1926
2005
  }
1927
- function getHtml() {
2006
+ function snapshotHtml() {
2007
+ if (sourceOpen) {
2008
+ return getHtml();
2009
+ }
2010
+ var missing = pluginsWithoutCleanMarkup();
2011
+ if (missing.length) {
2012
+ missing.forEach(function(name) {
2013
+ warnOnceHere("snapshot:" + name, 'the "' + name + '" plugin has no cleanMarkup, so the canvas leaves editing to be read: open texts and settings panels close');
2014
+ });
2015
+ return getHtml();
2016
+ }
2017
+ var copy = copyCanvas();
2018
+ var root = copy.root;
2019
+ [root].concat(all(root, ".ge-active-target, .ge-settings-target")).forEach(function(node) {
2020
+ removeClass(node, "ge-active-target ge-settings-target");
2021
+ dropEmptyClass(node);
2022
+ });
2023
+ all(root, ".ge-drop-marker").forEach(function(marker) {
2024
+ marker.remove();
2025
+ });
2026
+ removeDrawers(root);
2027
+ unwrapTexts(root);
2028
+ var refused = null;
2029
+ [CONTAINERS, FEATURES, UTILITIES].forEach(function(registry) {
2030
+ Object.keys(registry).forEach(function(name) {
2031
+ var plugin = registry[name];
2032
+ if (refused || !plugin.cleanMarkup) {
2033
+ return;
2034
+ }
2035
+ if (plugin.cleanMarkup(root, copy.liveOf) === false) {
2036
+ refused = name;
2037
+ }
2038
+ });
2039
+ });
2040
+ if (refused) {
2041
+ warnOnceHere("snapshot-refused:" + refused, 'the "' + refused + '" plugin cannot take its marking off a copy right now, so the canvas leaves editing to be read');
2042
+ return getHtml();
2043
+ }
2044
+ clearPreviews(root);
2045
+ clearRowCols(root);
2046
+ all(root, "[data-ge-container]").forEach(unmarkContainerClasses);
2047
+ sortables.forEach(function(sortableInstance) {
2048
+ var list = copy.copyOf(sortableInstance.el);
2049
+ if (list) {
2050
+ all(list, "[draggable]").forEach(function(node) {
2051
+ node.removeAttribute("draggable");
2052
+ });
2053
+ }
2054
+ });
2055
+ removeResizeFurniture(root);
2056
+ runFilter(false, root);
2057
+ stripPixelWidths(root);
2058
+ return root.innerHTML;
2059
+ }
2060
+ function getHtml(options) {
2061
+ if (options && options.keepEditing) {
2062
+ return snapshotHtml();
2063
+ }
1928
2064
  deinit();
1929
2065
  stripPixelWidths(canvas);
1930
2066
  var html = canvas.innerHTML;
@@ -1938,10 +2074,24 @@ function build(instance, baseElem, optionsOrMethod) {
1938
2074
  init();
1939
2075
  return html;
1940
2076
  }
1941
- function getPlainHtml() {
1942
- return plainHtml(getHtml());
2077
+ function getPlainHtml(options) {
2078
+ return plainHtml(getHtml(options));
2079
+ }
2080
+ function setHtml2(html) {
2081
+ if (sourceOpen) {
2082
+ closeSource();
2083
+ if (mainControls) {
2084
+ all(mainControls, ".gm-edit-mode").forEach(function(button) {
2085
+ removeClass(button, "active btn-danger");
2086
+ });
2087
+ }
2088
+ }
2089
+ deinit();
2090
+ setHtml(canvas, html);
2091
+ init();
1943
2092
  }
1944
2093
  function destroy() {
2094
+ plugins("onDestroy");
1945
2095
  if (sourceOpen) {
1946
2096
  closeSource();
1947
2097
  }
@@ -2054,6 +2204,11 @@ function build(instance, baseElem, optionsOrMethod) {
2054
2204
  closeDialog,
2055
2205
  rowFromLayout: rowFromLayoutValue,
2056
2206
  nodeHtml,
2207
+ // The markup as getHtml gives it, with the canvas left editing
2208
+ snapshotHtml,
2209
+ setHtml: setHtml2,
2210
+ // A question in the editor's confirm modal
2211
+ confirm: confirmWith,
2057
2212
  // A text editor has rewritten a content area, so whatever the
2058
2213
  // editor and its plugins had put in there goes back in
2059
2214
  textReady,
@@ -2576,10 +2731,13 @@ function build(instance, baseElem, optionsOrMethod) {
2576
2731
  if (definition) {
2577
2732
  definition.unmark(container);
2578
2733
  }
2579
- removeClass(container, "ge-container ge-container-" + containerTypeOf(container));
2580
- dropEmptyClass(container);
2734
+ unmarkContainerClasses(container);
2581
2735
  });
2582
2736
  }
2737
+ function unmarkContainerClasses(container) {
2738
+ removeClass(container, "ge-container ge-container-" + containerTypeOf(container));
2739
+ dropEmptyClass(container);
2740
+ }
2583
2741
  function prependDrawer(node, className) {
2584
2742
  var drawer = element("div", { "class": className });
2585
2743
  node.insertBefore(drawer, node.firstChild);
@@ -3980,13 +4138,16 @@ function build(instance, baseElem, optionsOrMethod) {
3980
4138
  on(handle, "pointerup pointercancel", onUp);
3981
4139
  }
3982
4140
  function removeResizable() {
3983
- all(canvas, ".ge-resize-handle").forEach(function(handle) {
4141
+ removeResizeFurniture(canvas);
4142
+ }
4143
+ function removeResizeFurniture(root) {
4144
+ all(root, ".ge-resize-handle").forEach(function(handle) {
3984
4145
  handle.remove();
3985
4146
  });
3986
- all(canvas, ".ge-resize-size").forEach(function(readout) {
4147
+ all(root, ".ge-resize-size").forEach(function(readout) {
3987
4148
  readout.remove();
3988
4149
  });
3989
- stripPixelWidths(canvas);
4150
+ stripPixelWidths(root);
3990
4151
  }
3991
4152
  function snapUnits(col, pixels) {
3992
4153
  var units = Math.round(pixels / rowContentWidth(col.parentElement) * MAX_COL_SIZE);
@@ -4194,7 +4355,7 @@ function build(instance, baseElem, optionsOrMethod) {
4194
4355
  setOffset(column, tier, wanted.offset);
4195
4356
  return column;
4196
4357
  }
4197
- function runFilter(isInit) {
4358
+ function runFilter(isInit, root) {
4198
4359
  if (!settings.custom_filter || !settings.custom_filter.length) {
4199
4360
  return;
4200
4361
  }
@@ -4203,7 +4364,7 @@ function build(instance, baseElem, optionsOrMethod) {
4203
4364
  if (typeof func == "string") {
4204
4365
  func = window[func];
4205
4366
  }
4206
- func(canvas, isInit);
4367
+ func(root || canvas, isInit);
4207
4368
  });
4208
4369
  }
4209
4370
  function textCutter() {
@@ -4323,8 +4484,8 @@ function build(instance, baseElem, optionsOrMethod) {
4323
4484
  }
4324
4485
  });
4325
4486
  }
4326
- function unwrapTexts() {
4327
- all(canvas, ".ge-text-block").forEach(function(textBlock) {
4487
+ function unwrapTexts(root) {
4488
+ all(root, ".ge-text-block").forEach(function(textBlock) {
4328
4489
  unwrap(textBlock);
4329
4490
  });
4330
4491
  }
@@ -4497,6 +4658,18 @@ function build(instance, baseElem, optionsOrMethod) {
4497
4658
  instances.set(canvas, instance);
4498
4659
  loadPlugins();
4499
4660
  instance.settings = settingsCopy();
4661
+ Object.keys(featureMethods).forEach(function(name) {
4662
+ if (Object.prototype.hasOwnProperty.call(own, name) || name in instance) {
4663
+ return;
4664
+ }
4665
+ instance[name] = function() {
4666
+ if (destroyed) {
4667
+ warnOnceHere("destroyed:" + name, t("warning.destroyed", { method: name }));
4668
+ return null;
4669
+ }
4670
+ return featureMethods[name].apply(null, arguments);
4671
+ };
4672
+ });
4500
4673
  setup();
4501
4674
  init();
4502
4675
  GridEditor._created.forEach(function(hook) {
@@ -4512,7 +4685,7 @@ GridEditor.t = translate;
4512
4685
  GridEditor._plainHtml = plainHtml;
4513
4686
  GridEditor.Sortable = null;
4514
4687
  GridEditor.bootstrap = null;
4515
- GridEditor.version = false ? "dev" : "8.4.0";
4688
+ GridEditor.version = false ? "dev" : "8.6.0";
4516
4689
  GridEditor.locales = {
4517
4690
  en: {
4518
4691
  "tool.move": "Move",
@@ -4540,6 +4713,7 @@ GridEditor.locales = {
4540
4713
  "tool.more": "More",
4541
4714
  "group.rows": "Rows",
4542
4715
  "group.content": "Content",
4716
+ "group.elements": "Elements",
4543
4717
  "group.select": "Add",
4544
4718
  "panel.title": "{kind} settings",
4545
4719
  "panel.close": "Close",