@themarioga/grid-editor 8.5.0 → 8.7.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 +46 -0
  2. package/README.md +78 -4
  3. package/dist/grideditor.bundle.min.js +3 -3
  4. package/dist/grideditor.css +15 -4
  5. package/dist/grideditor.d.ts +59 -3
  6. package/dist/grideditor.esm.js +226 -38
  7. package/dist/grideditor.js +226 -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 +4 -0
  13. package/dist/locales/grideditor.es.js +4 -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 +29 -0
  29. package/dist/plugins/grideditor.ckeditor.js +29 -0
  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 +6 -3
  41. package/dist/plugins/grideditor.elements.js +6 -3
  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 +39 -0
  65. package/dist/plugins/grideditor.summernote.js +39 -0
  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 +31 -0
  73. package/dist/plugins/grideditor.tinymce.js +31 -0
  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 +14 -0
  78. package/docs/plugins.md +86 -2
  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 -3
  83. package/src/js/grideditor.js +280 -30
  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 +4 -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 +4 -3
  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 +26 -0
  103. package/src/less/grideditor.less +16 -8
  104. package/types/grideditor.d.ts +59 -3
  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 */
@@ -126,6 +126,8 @@ export interface GridEditorOptions {
126
126
  /** The tabs plugin: the variant of the tabs containers made new. */
127
127
  tabs?: TabsOptions;
128
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. */
@@ -362,10 +411,10 @@ export declare class GridEditor {
362
411
  /** A read-only copy of the settings the editor runs with. */
363
412
  readonly settings: Readonly<GridEditorOptions>;
364
413
 
365
- /** The html to save: no drawers, no editor classes, no inline styles. */
366
- 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;
367
416
  /** getHtml without grid-editor's marking, for publishing. It cannot be edited again as it was. */
368
- getPlainHtml(): string;
417
+ getPlainHtml(options?: GetHtmlOptions): string;
369
418
  /** Run the editing pass over the canvas again, after markup was put in. */
370
419
  init(): this;
371
420
  /** Take the editing furniture off, leaving the markup. */
@@ -395,6 +444,13 @@ export declare class GridEditor {
395
444
  getUtility(node: Target, family: string, view?: View): string | null;
396
445
  /** Write one through the events; null is inherit. False when canceled or nothing changed. */
397
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;
398
454
  }
399
455
 
400
456
  export default GridEditor;
@@ -652,6 +652,7 @@ function build(instance, baseElem, optionsOrMethod) {
652
652
  var refitToolbar = function() {
653
653
  };
654
654
  var addButtons = [];
655
+ var shownToolbar = {};
655
656
  var toolbarGroup = null;
656
657
  var warnedHere = {};
657
658
  var sortables = [];
@@ -762,31 +763,53 @@ function build(instance, baseElem, optionsOrMethod) {
762
763
  whenConfirmed();
763
764
  return;
764
765
  }
765
- var Modal = modalLibrary();
766
- if (!Modal) {
767
- if (window.confirm(message)) {
766
+ confirmWith(message, { danger: true }, function(confirmed) {
767
+ if (confirmed) {
768
768
  whenConfirmed();
769
769
  }
770
+ });
771
+ }
772
+ function confirmWith(message, options, answer) {
773
+ options = options || {};
774
+ var Modal = modalLibrary();
775
+ if (!Modal) {
776
+ answer(window.confirm(message));
770
777
  return;
771
778
  }
772
779
  var modal = confirmModal();
773
780
  var confirmed = false;
781
+ var ok = one(modal, ".ge-confirm-ok");
782
+ if (pendingAnswer) {
783
+ pendingAnswer(null);
784
+ }
785
+ var settle = function(value) {
786
+ if (pendingAnswer !== settle) {
787
+ return;
788
+ }
789
+ pendingAnswer = null;
790
+ answer(value);
791
+ };
792
+ pendingAnswer = settle;
793
+ one(modal, ".modal-title").textContent = options.title || t("confirm.title");
794
+ one(modal, ".ge-confirm-cancel").textContent = options.cancel || t("confirm.cancel");
795
+ ok.textContent = options.ok || t("confirm.ok");
796
+ toggleClass(ok, "btn-danger", !!options.danger);
797
+ toggleClass(ok, "btn-primary", !options.danger);
774
798
  one(modal, ".ge-confirm-message").textContent = message;
775
- one(modal, ".ge-confirm-ok").onclick = function() {
799
+ ok.onclick = function() {
776
800
  confirmed = true;
777
801
  Modal.getInstance(modal).hide();
778
802
  };
779
803
  confirmHandlers.hidden = function() {
780
- if (confirmed) {
781
- whenConfirmed();
782
- }
804
+ settle(confirmed);
783
805
  };
784
806
  confirmHandlers.shown = function() {
785
- one(modal, ".ge-confirm-ok").focus();
807
+ ok.focus();
786
808
  };
787
809
  Modal.getOrCreateInstance(modal).show();
788
810
  }
789
811
  var confirmHandlers = { hidden: null, shown: null };
812
+ var pendingAnswer = null;
790
813
  function confirmModal() {
791
814
  if (confirmDialog) {
792
815
  return confirmDialog;
@@ -800,13 +823,14 @@ function build(instance, baseElem, optionsOrMethod) {
800
823
  one(confirmDialog, ".ge-confirm-cancel").textContent = t("confirm.cancel");
801
824
  one(confirmDialog, ".ge-confirm-ok").textContent = t("confirm.ok");
802
825
  trackModal(confirmDialog);
826
+ var asking = confirmDialog;
803
827
  confirmDialog.addEventListener("hidden.bs.modal", function() {
804
- if (confirmHandlers.hidden) {
828
+ if (asking === confirmDialog && confirmHandlers.hidden) {
805
829
  confirmHandlers.hidden();
806
830
  }
807
831
  });
808
832
  confirmDialog.addEventListener("shown.bs.modal", function() {
809
- if (confirmHandlers.shown) {
833
+ if (asking === confirmDialog && confirmHandlers.shown) {
810
834
  confirmHandlers.shown();
811
835
  }
812
836
  });
@@ -820,6 +844,9 @@ function build(instance, baseElem, optionsOrMethod) {
820
844
  confirmHandlers.shown = null;
821
845
  retireModal(confirmDialog);
822
846
  confirmDialog = null;
847
+ if (pendingAnswer) {
848
+ pendingAnswer(null);
849
+ }
823
850
  }
824
851
  function retireModal(panel) {
825
852
  var Modal = modalLibrary();
@@ -1114,7 +1141,7 @@ function build(instance, baseElem, optionsOrMethod) {
1114
1141
  settings.new_row_layouts.forEach(function(layout) {
1115
1142
  var grouped = !Array.isArray(layout);
1116
1143
  var btn = element("a", {
1117
- "class": "btn btn-sm btn-primary",
1144
+ "class": "btn btn-sm btn-light",
1118
1145
  title: grouped ? t("row.add_row_cols", { columns: layout.columns, counts: rowColsText(layout.row_cols) }) : t("row.add", { layout: layout.join("-") }),
1119
1146
  // What this button makes, in the markup rather than in
1120
1147
  // memory: a drag works on a clone of it
@@ -1149,7 +1176,7 @@ function build(instance, baseElem, optionsOrMethod) {
1149
1176
  Object.keys(CONTAINERS).forEach(function(type) {
1150
1177
  var definition = CONTAINERS[type];
1151
1178
  var button = labelButton(
1152
- element("a", { "class": "btn btn-sm btn-primary ge-add-container" }),
1179
+ element("a", { "class": "btn btn-sm btn-light ge-add-container" }),
1153
1180
  t(definition.labelKey),
1154
1181
  definition.iconClass
1155
1182
  );
@@ -1170,8 +1197,10 @@ function build(instance, baseElem, optionsOrMethod) {
1170
1197
  addButtons.push({ button, home: addContainerGroup, group: toolbarGroupOf(definition.group) || "content" });
1171
1198
  });
1172
1199
  var endItems = [];
1200
+ shownToolbar = {};
1173
1201
  Object.keys(FEATURES).forEach(function(name) {
1174
- (FEATURES[name].toolbar || []).forEach(function(item, index) {
1202
+ shownToolbar[name] = toolbarOf(FEATURES[name]);
1203
+ shownToolbar[name].forEach(function(item, index) {
1175
1204
  var button = featureButton(name, item, index);
1176
1205
  if (item.align === "end") {
1177
1206
  endItems.push(button);
@@ -1270,7 +1299,7 @@ function build(instance, baseElem, optionsOrMethod) {
1270
1299
  names.forEach(function(name) {
1271
1300
  var tab = tabs.appendChild(element("button", {
1272
1301
  type: "button",
1273
- "class": "btn btn-sm btn-outline-primary",
1302
+ "class": "btn btn-sm btn-outline-secondary",
1274
1303
  role: "tab",
1275
1304
  "data-ge-group": name
1276
1305
  }, groupLabel(name)));
@@ -1307,6 +1336,22 @@ function build(instance, baseElem, optionsOrMethod) {
1307
1336
  return !closest(entry.button, ".ge-toolbar-stash");
1308
1337
  });
1309
1338
  }
1339
+ function toolbarOf(feature) {
1340
+ var items = typeof feature.toolbar === "function" ? feature.toolbar() : feature.toolbar;
1341
+ return Array.isArray(items) ? items : [];
1342
+ }
1343
+ function refreshToolbar() {
1344
+ if (!mainControls || destroyed) {
1345
+ return;
1346
+ }
1347
+ var previewing = !!one(mainControls, ".gm-preview.active");
1348
+ removeMainControls();
1349
+ createMainControls();
1350
+ if (previewing) {
1351
+ addClass(one(mainControls, ".gm-preview"), "active btn-danger");
1352
+ }
1353
+ onScroll();
1354
+ }
1310
1355
  function removeMainControls() {
1311
1356
  if (toolbarLifetime) {
1312
1357
  toolbarLifetime.abort();
@@ -1319,7 +1364,7 @@ function build(instance, baseElem, optionsOrMethod) {
1319
1364
  mainControls.remove();
1320
1365
  }
1321
1366
  function createOverflowMenu(start) {
1322
- 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>');
1367
+ 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>');
1323
1368
  start.parentNode.insertBefore(more, start.nextSibling);
1324
1369
  var toggle2 = one(more, "button");
1325
1370
  var panel = one(more, ".ge-toolbar-overflow");
@@ -1410,7 +1455,8 @@ function build(instance, baseElem, optionsOrMethod) {
1410
1455
  function featureButton(name, item, index) {
1411
1456
  var iconClass = item.iconClass || (item.align === "end" ? "bi bi-plus" : null);
1412
1457
  var label = typeof item.label === "function" ? item.label() : item.label || t(item.labelKey);
1413
- var button = labelButton(element("a", { "class": "btn btn-sm btn-primary ge-add-container ge-add-feature" }), label, iconClass);
1458
+ var colour = item.align === "end" ? "btn-primary" : "btn-light";
1459
+ var button = labelButton(element("a", { "class": "btn btn-sm " + colour + " ge-add-container ge-add-feature" }), label, iconClass);
1414
1460
  addClass(button, item.className || "");
1415
1461
  attr(button, {
1416
1462
  "data-ge-toolbar": "feature",
@@ -1532,7 +1578,10 @@ function build(instance, baseElem, optionsOrMethod) {
1532
1578
  return { region, before };
1533
1579
  }
1534
1580
  function insertFeatureFromToolbar(button, where, source) {
1535
- var item = FEATURES[button.getAttribute("data-ge-feature")].toolbar[parseInt(button.getAttribute("data-ge-item"), 10)];
1581
+ var item = (shownToolbar[button.getAttribute("data-ge-feature")] || [])[parseInt(button.getAttribute("data-ge-item"), 10)];
1582
+ if (!item) {
1583
+ return null;
1584
+ }
1536
1585
  var made = item.create();
1537
1586
  if (!made) {
1538
1587
  return null;
@@ -1918,22 +1967,122 @@ function build(instance, baseElem, optionsOrMethod) {
1918
1967
  closeSizePicker();
1919
1968
  hideDropMarker();
1920
1969
  closeDialog();
1921
- all(canvas, ".ge-tools-drawer").forEach(function(drawer) {
1922
- drawer.remove();
1923
- });
1970
+ removeDrawers(canvas);
1924
1971
  pluginFields.clear();
1925
- unwrapTexts();
1972
+ unwrapTexts(canvas);
1926
1973
  plugins("onDeinit");
1927
1974
  clearPreviews(canvas);
1928
- all(canvas, "[data-ge-row-cols]").forEach(function(row) {
1929
- row.removeAttribute("data-ge-row-cols");
1930
- });
1975
+ clearRowCols(canvas);
1931
1976
  unmarkContainers();
1932
1977
  removeSortable();
1933
1978
  removeResizable();
1934
- runFilter(false);
1979
+ runFilter(false, canvas);
1980
+ }
1981
+ function removeDrawers(root) {
1982
+ all(root, ".ge-tools-drawer").forEach(function(drawer) {
1983
+ drawer.remove();
1984
+ });
1985
+ }
1986
+ function clearRowCols(root) {
1987
+ all(root, "[data-ge-row-cols]").forEach(function(row) {
1988
+ row.removeAttribute("data-ge-row-cols");
1989
+ });
1990
+ }
1991
+ function pluginsWithoutCleanMarkup() {
1992
+ var names = [];
1993
+ [CONTAINERS, FEATURES, UTILITIES].forEach(function(registry) {
1994
+ Object.keys(registry).forEach(function(name) {
1995
+ var plugin = registry[name];
1996
+ var marks = plugin.unmark || plugin.onDeinit || plugin.onBeforeDeinit;
1997
+ if (marks && !plugin.cleanMarkup) {
1998
+ names.push(name);
1999
+ }
2000
+ });
2001
+ });
2002
+ return names;
2003
+ }
2004
+ function copyCanvas() {
2005
+ var copy = canvas.cloneNode(true);
2006
+ var toLive = /* @__PURE__ */ new Map();
2007
+ var toCopy = /* @__PURE__ */ new Map();
2008
+ var live = document.createTreeWalker(canvas, NodeFilter.SHOW_ELEMENT);
2009
+ var copied = document.createTreeWalker(copy, NodeFilter.SHOW_ELEMENT);
2010
+ var a = live.currentNode;
2011
+ var b = copied.currentNode;
2012
+ while (a && b) {
2013
+ toLive.set(b, a);
2014
+ toCopy.set(a, b);
2015
+ a = live.nextNode();
2016
+ b = copied.nextNode();
2017
+ }
2018
+ return {
2019
+ root: copy,
2020
+ liveOf: function(node) {
2021
+ return toLive.get(node) || null;
2022
+ },
2023
+ copyOf: function(node) {
2024
+ return toCopy.get(node) || null;
2025
+ }
2026
+ };
2027
+ }
2028
+ function snapshotHtml() {
2029
+ if (sourceOpen) {
2030
+ return getHtml();
2031
+ }
2032
+ var missing = pluginsWithoutCleanMarkup();
2033
+ if (missing.length) {
2034
+ missing.forEach(function(name) {
2035
+ warnOnceHere("snapshot:" + name, 'the "' + name + '" plugin has no cleanMarkup, so the canvas leaves editing to be read: open texts and settings panels close');
2036
+ });
2037
+ return getHtml();
2038
+ }
2039
+ var copy = copyCanvas();
2040
+ var root = copy.root;
2041
+ [root].concat(all(root, ".ge-active-target, .ge-settings-target")).forEach(function(node) {
2042
+ removeClass(node, "ge-active-target ge-settings-target");
2043
+ dropEmptyClass(node);
2044
+ });
2045
+ all(root, ".ge-drop-marker").forEach(function(marker) {
2046
+ marker.remove();
2047
+ });
2048
+ removeDrawers(root);
2049
+ unwrapTexts(root);
2050
+ var refused = null;
2051
+ [CONTAINERS, FEATURES, UTILITIES].forEach(function(registry) {
2052
+ Object.keys(registry).forEach(function(name) {
2053
+ var plugin = registry[name];
2054
+ if (refused || !plugin.cleanMarkup) {
2055
+ return;
2056
+ }
2057
+ if (plugin.cleanMarkup(root, copy.liveOf) === false) {
2058
+ refused = name;
2059
+ }
2060
+ });
2061
+ });
2062
+ if (refused) {
2063
+ warnOnceHere("snapshot-refused:" + refused, 'the "' + refused + '" plugin cannot take its marking off a copy right now, so the canvas leaves editing to be read');
2064
+ return getHtml();
2065
+ }
2066
+ clearPreviews(root);
2067
+ clearRowCols(root);
2068
+ all(root, "[data-ge-container]").forEach(unmarkContainerClasses);
2069
+ sortables.forEach(function(sortableInstance) {
2070
+ var list = copy.copyOf(sortableInstance.el);
2071
+ if (list) {
2072
+ all(list, "[draggable]").forEach(function(node) {
2073
+ node.removeAttribute("draggable");
2074
+ });
2075
+ }
2076
+ });
2077
+ removeResizeFurniture(root);
2078
+ runFilter(false, root);
2079
+ stripPixelWidths(root);
2080
+ return root.innerHTML;
1935
2081
  }
1936
- function getHtml() {
2082
+ function getHtml(options) {
2083
+ if (options && options.keepEditing) {
2084
+ return snapshotHtml();
2085
+ }
1937
2086
  deinit();
1938
2087
  stripPixelWidths(canvas);
1939
2088
  var html = canvas.innerHTML;
@@ -1947,10 +2096,24 @@ function build(instance, baseElem, optionsOrMethod) {
1947
2096
  init();
1948
2097
  return html;
1949
2098
  }
1950
- function getPlainHtml() {
1951
- return plainHtml(getHtml());
2099
+ function getPlainHtml(options) {
2100
+ return plainHtml(getHtml(options));
2101
+ }
2102
+ function setHtml2(html) {
2103
+ if (sourceOpen) {
2104
+ closeSource();
2105
+ if (mainControls) {
2106
+ all(mainControls, ".gm-edit-mode").forEach(function(button) {
2107
+ removeClass(button, "active btn-danger");
2108
+ });
2109
+ }
2110
+ }
2111
+ deinit();
2112
+ setHtml(canvas, html);
2113
+ init();
1952
2114
  }
1953
2115
  function destroy() {
2116
+ plugins("onDestroy");
1954
2117
  if (sourceOpen) {
1955
2118
  closeSource();
1956
2119
  }
@@ -2063,6 +2226,11 @@ function build(instance, baseElem, optionsOrMethod) {
2063
2226
  closeDialog,
2064
2227
  rowFromLayout: rowFromLayoutValue,
2065
2228
  nodeHtml,
2229
+ // The markup as getHtml gives it, with the canvas left editing
2230
+ snapshotHtml,
2231
+ setHtml: setHtml2,
2232
+ // A question in the editor's confirm modal
2233
+ confirm: confirmWith,
2066
2234
  // A text editor has rewritten a content area, so whatever the
2067
2235
  // editor and its plugins had put in there goes back in
2068
2236
  textReady,
@@ -2075,6 +2243,8 @@ function build(instance, baseElem, optionsOrMethod) {
2075
2243
  // A node's drawer: its first child, or for a content area the
2076
2244
  // one beside it in its text block
2077
2245
  drawerOf,
2246
+ // The toolbar built again, from the plugins' toolbars as they are now
2247
+ refreshToolbar,
2078
2248
  toolbarItems: function(name) {
2079
2249
  return mainControls ? all(mainControls, '[data-ge-toolbar="feature"][data-ge-feature="' + name + '"]') : [];
2080
2250
  }
@@ -2585,10 +2755,13 @@ function build(instance, baseElem, optionsOrMethod) {
2585
2755
  if (definition) {
2586
2756
  definition.unmark(container);
2587
2757
  }
2588
- removeClass(container, "ge-container ge-container-" + containerTypeOf(container));
2589
- dropEmptyClass(container);
2758
+ unmarkContainerClasses(container);
2590
2759
  });
2591
2760
  }
2761
+ function unmarkContainerClasses(container) {
2762
+ removeClass(container, "ge-container ge-container-" + containerTypeOf(container));
2763
+ dropEmptyClass(container);
2764
+ }
2592
2765
  function prependDrawer(node, className) {
2593
2766
  var drawer = element("div", { "class": className });
2594
2767
  node.insertBefore(drawer, node.firstChild);
@@ -3989,13 +4162,16 @@ function build(instance, baseElem, optionsOrMethod) {
3989
4162
  on(handle, "pointerup pointercancel", onUp);
3990
4163
  }
3991
4164
  function removeResizable() {
3992
- all(canvas, ".ge-resize-handle").forEach(function(handle) {
4165
+ removeResizeFurniture(canvas);
4166
+ }
4167
+ function removeResizeFurniture(root) {
4168
+ all(root, ".ge-resize-handle").forEach(function(handle) {
3993
4169
  handle.remove();
3994
4170
  });
3995
- all(canvas, ".ge-resize-size").forEach(function(readout) {
4171
+ all(root, ".ge-resize-size").forEach(function(readout) {
3996
4172
  readout.remove();
3997
4173
  });
3998
- stripPixelWidths(canvas);
4174
+ stripPixelWidths(root);
3999
4175
  }
4000
4176
  function snapUnits(col, pixels) {
4001
4177
  var units = Math.round(pixels / rowContentWidth(col.parentElement) * MAX_COL_SIZE);
@@ -4203,7 +4379,7 @@ function build(instance, baseElem, optionsOrMethod) {
4203
4379
  setOffset(column, tier, wanted.offset);
4204
4380
  return column;
4205
4381
  }
4206
- function runFilter(isInit) {
4382
+ function runFilter(isInit, root) {
4207
4383
  if (!settings.custom_filter || !settings.custom_filter.length) {
4208
4384
  return;
4209
4385
  }
@@ -4212,7 +4388,7 @@ function build(instance, baseElem, optionsOrMethod) {
4212
4388
  if (typeof func == "string") {
4213
4389
  func = window[func];
4214
4390
  }
4215
- func(canvas, isInit);
4391
+ func(root || canvas, isInit);
4216
4392
  });
4217
4393
  }
4218
4394
  function textCutter() {
@@ -4332,8 +4508,8 @@ function build(instance, baseElem, optionsOrMethod) {
4332
4508
  }
4333
4509
  });
4334
4510
  }
4335
- function unwrapTexts() {
4336
- all(canvas, ".ge-text-block").forEach(function(textBlock) {
4511
+ function unwrapTexts(root) {
4512
+ all(root, ".ge-text-block").forEach(function(textBlock) {
4337
4513
  unwrap(textBlock);
4338
4514
  });
4339
4515
  }
@@ -4506,6 +4682,18 @@ function build(instance, baseElem, optionsOrMethod) {
4506
4682
  instances.set(canvas, instance);
4507
4683
  loadPlugins();
4508
4684
  instance.settings = settingsCopy();
4685
+ Object.keys(featureMethods).forEach(function(name) {
4686
+ if (Object.prototype.hasOwnProperty.call(own, name) || name in instance) {
4687
+ return;
4688
+ }
4689
+ instance[name] = function() {
4690
+ if (destroyed) {
4691
+ warnOnceHere("destroyed:" + name, t("warning.destroyed", { method: name }));
4692
+ return null;
4693
+ }
4694
+ return featureMethods[name].apply(null, arguments);
4695
+ };
4696
+ });
4509
4697
  setup();
4510
4698
  init();
4511
4699
  GridEditor._created.forEach(function(hook) {
@@ -4521,7 +4709,7 @@ GridEditor.t = translate;
4521
4709
  GridEditor._plainHtml = plainHtml;
4522
4710
  GridEditor.Sortable = null;
4523
4711
  GridEditor.bootstrap = null;
4524
- GridEditor.version = false ? "dev" : "8.5.0";
4712
+ GridEditor.version = false ? "dev" : "8.7.0";
4525
4713
  GridEditor.locales = {
4526
4714
  en: {
4527
4715
  "tool.move": "Move",