@themarioga/grid-editor 8.5.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 +33 -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 +200 -36
  7. package/dist/grideditor.js +200 -36
  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 +54 -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 -2
  83. package/src/js/grideditor.js +242 -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 +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;
@@ -762,31 +762,53 @@ function build(instance, baseElem, optionsOrMethod) {
762
762
  whenConfirmed();
763
763
  return;
764
764
  }
765
- var Modal = modalLibrary();
766
- if (!Modal) {
767
- if (window.confirm(message)) {
765
+ confirmWith(message, { danger: true }, function(confirmed) {
766
+ if (confirmed) {
768
767
  whenConfirmed();
769
768
  }
769
+ });
770
+ }
771
+ function confirmWith(message, options, answer) {
772
+ options = options || {};
773
+ var Modal = modalLibrary();
774
+ if (!Modal) {
775
+ answer(window.confirm(message));
770
776
  return;
771
777
  }
772
778
  var modal = confirmModal();
773
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);
774
797
  one(modal, ".ge-confirm-message").textContent = message;
775
- one(modal, ".ge-confirm-ok").onclick = function() {
798
+ ok.onclick = function() {
776
799
  confirmed = true;
777
800
  Modal.getInstance(modal).hide();
778
801
  };
779
802
  confirmHandlers.hidden = function() {
780
- if (confirmed) {
781
- whenConfirmed();
782
- }
803
+ settle(confirmed);
783
804
  };
784
805
  confirmHandlers.shown = function() {
785
- one(modal, ".ge-confirm-ok").focus();
806
+ ok.focus();
786
807
  };
787
808
  Modal.getOrCreateInstance(modal).show();
788
809
  }
789
810
  var confirmHandlers = { hidden: null, shown: null };
811
+ var pendingAnswer = null;
790
812
  function confirmModal() {
791
813
  if (confirmDialog) {
792
814
  return confirmDialog;
@@ -800,13 +822,14 @@ function build(instance, baseElem, optionsOrMethod) {
800
822
  one(confirmDialog, ".ge-confirm-cancel").textContent = t("confirm.cancel");
801
823
  one(confirmDialog, ".ge-confirm-ok").textContent = t("confirm.ok");
802
824
  trackModal(confirmDialog);
825
+ var asking = confirmDialog;
803
826
  confirmDialog.addEventListener("hidden.bs.modal", function() {
804
- if (confirmHandlers.hidden) {
827
+ if (asking === confirmDialog && confirmHandlers.hidden) {
805
828
  confirmHandlers.hidden();
806
829
  }
807
830
  });
808
831
  confirmDialog.addEventListener("shown.bs.modal", function() {
809
- if (confirmHandlers.shown) {
832
+ if (asking === confirmDialog && confirmHandlers.shown) {
810
833
  confirmHandlers.shown();
811
834
  }
812
835
  });
@@ -820,6 +843,9 @@ function build(instance, baseElem, optionsOrMethod) {
820
843
  confirmHandlers.shown = null;
821
844
  retireModal(confirmDialog);
822
845
  confirmDialog = null;
846
+ if (pendingAnswer) {
847
+ pendingAnswer(null);
848
+ }
823
849
  }
824
850
  function retireModal(panel) {
825
851
  var Modal = modalLibrary();
@@ -1114,7 +1140,7 @@ function build(instance, baseElem, optionsOrMethod) {
1114
1140
  settings.new_row_layouts.forEach(function(layout) {
1115
1141
  var grouped = !Array.isArray(layout);
1116
1142
  var btn = element("a", {
1117
- "class": "btn btn-sm btn-primary",
1143
+ "class": "btn btn-sm btn-light",
1118
1144
  title: grouped ? t("row.add_row_cols", { columns: layout.columns, counts: rowColsText(layout.row_cols) }) : t("row.add", { layout: layout.join("-") }),
1119
1145
  // What this button makes, in the markup rather than in
1120
1146
  // memory: a drag works on a clone of it
@@ -1149,7 +1175,7 @@ function build(instance, baseElem, optionsOrMethod) {
1149
1175
  Object.keys(CONTAINERS).forEach(function(type) {
1150
1176
  var definition = CONTAINERS[type];
1151
1177
  var button = labelButton(
1152
- element("a", { "class": "btn btn-sm btn-primary ge-add-container" }),
1178
+ element("a", { "class": "btn btn-sm btn-light ge-add-container" }),
1153
1179
  t(definition.labelKey),
1154
1180
  definition.iconClass
1155
1181
  );
@@ -1270,7 +1296,7 @@ function build(instance, baseElem, optionsOrMethod) {
1270
1296
  names.forEach(function(name) {
1271
1297
  var tab = tabs.appendChild(element("button", {
1272
1298
  type: "button",
1273
- "class": "btn btn-sm btn-outline-primary",
1299
+ "class": "btn btn-sm btn-outline-secondary",
1274
1300
  role: "tab",
1275
1301
  "data-ge-group": name
1276
1302
  }, groupLabel(name)));
@@ -1319,7 +1345,7 @@ function build(instance, baseElem, optionsOrMethod) {
1319
1345
  mainControls.remove();
1320
1346
  }
1321
1347
  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>');
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>');
1323
1349
  start.parentNode.insertBefore(more, start.nextSibling);
1324
1350
  var toggle2 = one(more, "button");
1325
1351
  var panel = one(more, ".ge-toolbar-overflow");
@@ -1410,7 +1436,8 @@ function build(instance, baseElem, optionsOrMethod) {
1410
1436
  function featureButton(name, item, index) {
1411
1437
  var iconClass = item.iconClass || (item.align === "end" ? "bi bi-plus" : null);
1412
1438
  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);
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);
1414
1441
  addClass(button, item.className || "");
1415
1442
  attr(button, {
1416
1443
  "data-ge-toolbar": "feature",
@@ -1918,22 +1945,122 @@ function build(instance, baseElem, optionsOrMethod) {
1918
1945
  closeSizePicker();
1919
1946
  hideDropMarker();
1920
1947
  closeDialog();
1921
- all(canvas, ".ge-tools-drawer").forEach(function(drawer) {
1922
- drawer.remove();
1923
- });
1948
+ removeDrawers(canvas);
1924
1949
  pluginFields.clear();
1925
- unwrapTexts();
1950
+ unwrapTexts(canvas);
1926
1951
  plugins("onDeinit");
1927
1952
  clearPreviews(canvas);
1928
- all(canvas, "[data-ge-row-cols]").forEach(function(row) {
1929
- row.removeAttribute("data-ge-row-cols");
1930
- });
1953
+ clearRowCols(canvas);
1931
1954
  unmarkContainers();
1932
1955
  removeSortable();
1933
1956
  removeResizable();
1934
- runFilter(false);
1957
+ runFilter(false, canvas);
1935
1958
  }
1936
- function getHtml() {
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
+ };
2005
+ }
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
+ }
1937
2064
  deinit();
1938
2065
  stripPixelWidths(canvas);
1939
2066
  var html = canvas.innerHTML;
@@ -1947,10 +2074,24 @@ function build(instance, baseElem, optionsOrMethod) {
1947
2074
  init();
1948
2075
  return html;
1949
2076
  }
1950
- function getPlainHtml() {
1951
- 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();
1952
2092
  }
1953
2093
  function destroy() {
2094
+ plugins("onDestroy");
1954
2095
  if (sourceOpen) {
1955
2096
  closeSource();
1956
2097
  }
@@ -2063,6 +2204,11 @@ function build(instance, baseElem, optionsOrMethod) {
2063
2204
  closeDialog,
2064
2205
  rowFromLayout: rowFromLayoutValue,
2065
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,
2066
2212
  // A text editor has rewritten a content area, so whatever the
2067
2213
  // editor and its plugins had put in there goes back in
2068
2214
  textReady,
@@ -2585,10 +2731,13 @@ function build(instance, baseElem, optionsOrMethod) {
2585
2731
  if (definition) {
2586
2732
  definition.unmark(container);
2587
2733
  }
2588
- removeClass(container, "ge-container ge-container-" + containerTypeOf(container));
2589
- dropEmptyClass(container);
2734
+ unmarkContainerClasses(container);
2590
2735
  });
2591
2736
  }
2737
+ function unmarkContainerClasses(container) {
2738
+ removeClass(container, "ge-container ge-container-" + containerTypeOf(container));
2739
+ dropEmptyClass(container);
2740
+ }
2592
2741
  function prependDrawer(node, className) {
2593
2742
  var drawer = element("div", { "class": className });
2594
2743
  node.insertBefore(drawer, node.firstChild);
@@ -3989,13 +4138,16 @@ function build(instance, baseElem, optionsOrMethod) {
3989
4138
  on(handle, "pointerup pointercancel", onUp);
3990
4139
  }
3991
4140
  function removeResizable() {
3992
- 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) {
3993
4145
  handle.remove();
3994
4146
  });
3995
- all(canvas, ".ge-resize-size").forEach(function(readout) {
4147
+ all(root, ".ge-resize-size").forEach(function(readout) {
3996
4148
  readout.remove();
3997
4149
  });
3998
- stripPixelWidths(canvas);
4150
+ stripPixelWidths(root);
3999
4151
  }
4000
4152
  function snapUnits(col, pixels) {
4001
4153
  var units = Math.round(pixels / rowContentWidth(col.parentElement) * MAX_COL_SIZE);
@@ -4203,7 +4355,7 @@ function build(instance, baseElem, optionsOrMethod) {
4203
4355
  setOffset(column, tier, wanted.offset);
4204
4356
  return column;
4205
4357
  }
4206
- function runFilter(isInit) {
4358
+ function runFilter(isInit, root) {
4207
4359
  if (!settings.custom_filter || !settings.custom_filter.length) {
4208
4360
  return;
4209
4361
  }
@@ -4212,7 +4364,7 @@ function build(instance, baseElem, optionsOrMethod) {
4212
4364
  if (typeof func == "string") {
4213
4365
  func = window[func];
4214
4366
  }
4215
- func(canvas, isInit);
4367
+ func(root || canvas, isInit);
4216
4368
  });
4217
4369
  }
4218
4370
  function textCutter() {
@@ -4332,8 +4484,8 @@ function build(instance, baseElem, optionsOrMethod) {
4332
4484
  }
4333
4485
  });
4334
4486
  }
4335
- function unwrapTexts() {
4336
- all(canvas, ".ge-text-block").forEach(function(textBlock) {
4487
+ function unwrapTexts(root) {
4488
+ all(root, ".ge-text-block").forEach(function(textBlock) {
4337
4489
  unwrap(textBlock);
4338
4490
  });
4339
4491
  }
@@ -4506,6 +4658,18 @@ function build(instance, baseElem, optionsOrMethod) {
4506
4658
  instances.set(canvas, instance);
4507
4659
  loadPlugins();
4508
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
+ });
4509
4673
  setup();
4510
4674
  init();
4511
4675
  GridEditor._created.forEach(function(hook) {
@@ -4521,7 +4685,7 @@ GridEditor.t = translate;
4521
4685
  GridEditor._plainHtml = plainHtml;
4522
4686
  GridEditor.Sortable = null;
4523
4687
  GridEditor.bootstrap = null;
4524
- GridEditor.version = false ? "dev" : "8.5.0";
4688
+ GridEditor.version = false ? "dev" : "8.6.0";
4525
4689
  GridEditor.locales = {
4526
4690
  en: {
4527
4691
  "tool.move": "Move",