@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.
- package/CHANGELOG.md +46 -0
- package/README.md +78 -4
- package/dist/grideditor.bundle.min.js +3 -3
- package/dist/grideditor.css +15 -4
- package/dist/grideditor.d.ts +59 -3
- package/dist/grideditor.esm.js +226 -38
- package/dist/grideditor.js +226 -38
- package/dist/grideditor.min.css +1 -1
- package/dist/grideditor.min.css.map +1 -1
- package/dist/grideditor.min.js +2 -2
- package/dist/grideditor.min.js.map +3 -3
- package/dist/locales/grideditor.es.esm.js +4 -0
- package/dist/locales/grideditor.es.js +4 -0
- package/dist/locales/grideditor.es.min.js +1 -1
- package/dist/locales/grideditor.es.min.js.map +2 -2
- package/dist/plugins/grideditor.accordion.esm.js +7 -0
- package/dist/plugins/grideditor.accordion.js +7 -0
- package/dist/plugins/grideditor.accordion.min.js +1 -1
- package/dist/plugins/grideditor.accordion.min.js.map +3 -3
- package/dist/plugins/grideditor.autosave.esm.js +309 -0
- package/dist/plugins/grideditor.autosave.js +316 -0
- package/dist/plugins/grideditor.autosave.min.js +1 -0
- package/dist/plugins/grideditor.autosave.min.js.map +7 -0
- package/dist/plugins/grideditor.card.esm.js +10 -0
- package/dist/plugins/grideditor.card.js +10 -0
- package/dist/plugins/grideditor.card.min.js +1 -1
- package/dist/plugins/grideditor.card.min.js.map +3 -3
- package/dist/plugins/grideditor.ckeditor.esm.js +29 -0
- package/dist/plugins/grideditor.ckeditor.js +29 -0
- package/dist/plugins/grideditor.ckeditor.min.js +1 -1
- package/dist/plugins/grideditor.ckeditor.min.js.map +3 -3
- package/dist/plugins/grideditor.clipboard.esm.js +3 -0
- package/dist/plugins/grideditor.clipboard.js +3 -0
- package/dist/plugins/grideditor.clipboard.min.js +1 -1
- package/dist/plugins/grideditor.clipboard.min.js.map +2 -2
- package/dist/plugins/grideditor.codemirror-inline.esm.js +11 -0
- package/dist/plugins/grideditor.codemirror-inline.js +11 -0
- package/dist/plugins/grideditor.codemirror-inline.min.js +1 -1
- package/dist/plugins/grideditor.codemirror-inline.min.js.map +3 -3
- package/dist/plugins/grideditor.elements.esm.js +6 -3
- package/dist/plugins/grideditor.elements.js +6 -3
- package/dist/plugins/grideditor.elements.min.js +1 -1
- package/dist/plugins/grideditor.elements.min.js.map +3 -3
- package/dist/plugins/grideditor.gutters.esm.js +5 -0
- package/dist/plugins/grideditor.gutters.js +5 -0
- package/dist/plugins/grideditor.gutters.min.js +1 -1
- package/dist/plugins/grideditor.gutters.min.js.map +3 -3
- package/dist/plugins/grideditor.inline-style.esm.js +18 -10
- package/dist/plugins/grideditor.inline-style.js +18 -10
- package/dist/plugins/grideditor.inline-style.min.js +3 -3
- package/dist/plugins/grideditor.inline-style.min.js.map +3 -3
- package/dist/plugins/grideditor.order.esm.js +5 -0
- package/dist/plugins/grideditor.order.js +5 -0
- package/dist/plugins/grideditor.order.min.js +1 -1
- package/dist/plugins/grideditor.order.min.js.map +3 -3
- package/dist/plugins/grideditor.popup.esm.js +17 -6
- package/dist/plugins/grideditor.popup.js +17 -6
- package/dist/plugins/grideditor.popup.min.js +1 -1
- package/dist/plugins/grideditor.popup.min.js.map +3 -3
- package/dist/plugins/grideditor.sections.esm.js +6 -3
- package/dist/plugins/grideditor.sections.js +6 -3
- package/dist/plugins/grideditor.sections.min.js +1 -1
- package/dist/plugins/grideditor.sections.min.js.map +3 -3
- package/dist/plugins/grideditor.summernote.esm.js +39 -0
- package/dist/plugins/grideditor.summernote.js +39 -0
- package/dist/plugins/grideditor.summernote.min.js +1 -1
- package/dist/plugins/grideditor.summernote.min.js.map +3 -3
- package/dist/plugins/grideditor.tabs.esm.js +7 -0
- package/dist/plugins/grideditor.tabs.js +7 -0
- package/dist/plugins/grideditor.tabs.min.js +1 -1
- package/dist/plugins/grideditor.tabs.min.js.map +3 -3
- package/dist/plugins/grideditor.tinymce.esm.js +31 -0
- package/dist/plugins/grideditor.tinymce.js +31 -0
- package/dist/plugins/grideditor.tinymce.min.js +1 -1
- package/dist/plugins/grideditor.tinymce.min.js.map +3 -3
- package/docs/events.md +8 -0
- package/docs/locale-keys.md +14 -0
- package/docs/plugins.md +86 -2
- package/example/autosave.html +39 -22
- package/example/index.html +1 -1
- package/example/plugins.html +10 -0
- package/package.json +1 -3
- package/src/js/grideditor.js +280 -30
- package/src/js/inline-style/display.js +4 -3
- package/src/js/inline-style/drawerflow.js +10 -5
- package/src/js/locales/grideditor.es.js +4 -0
- package/src/js/plugins/grideditor.accordion.js +6 -0
- package/src/js/plugins/grideditor.autosave.js +349 -0
- package/src/js/plugins/grideditor.card.js +6 -0
- package/src/js/plugins/grideditor.ckeditor.js +6 -0
- package/src/js/plugins/grideditor.clipboard.js +3 -0
- package/src/js/plugins/grideditor.codemirror-inline.js +7 -0
- package/src/js/plugins/grideditor.elements.js +4 -3
- package/src/js/plugins/grideditor.gutters.js +4 -0
- package/src/js/plugins/grideditor.inline-style.js +2 -0
- package/src/js/plugins/grideditor.order.js +4 -0
- package/src/js/plugins/grideditor.popup.js +15 -6
- package/src/js/plugins/grideditor.sections.js +4 -3
- package/src/js/plugins/grideditor.summernote.js +14 -0
- package/src/js/plugins/grideditor.tabs.js +6 -0
- package/src/js/plugins/grideditor.tinymce.js +8 -0
- package/src/js/text/grideditor.text.js +26 -0
- package/src/less/grideditor.less +16 -8
- package/types/grideditor.d.ts +59 -3
- package/AUTO_SAVE.md +0 -30
package/src/js/grideditor.js
CHANGED
|
@@ -437,6 +437,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
437
437
|
var closeOverflowMenu = function() {}; // Folds the toolbar's overflow menu away, when it has one
|
|
438
438
|
var refitToolbar = function() {}; // Works out again what the toolbar's overflow menu holds, when it has one
|
|
439
439
|
var addButtons = []; // The toolbar's add buttons, in toolbar order: { button, home, group }
|
|
440
|
+
var shownToolbar = {}; // Each feature plugin's toolbar items, as the toolbar was last built with them
|
|
440
441
|
var toolbarGroup = null; // The category shown, with toolbar_groups. Kept when the toolbar is rebuilt
|
|
441
442
|
var warnedHere = {}; // Some warnings are worth saying once per instance, not once per call
|
|
442
443
|
var sortables = []; // Every list made sortable, so deinit destroys exactly those
|
|
@@ -628,30 +629,61 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
628
629
|
return;
|
|
629
630
|
}
|
|
630
631
|
|
|
632
|
+
confirmWith(message, { danger: true }, function(confirmed) {
|
|
633
|
+
if (confirmed) { whenConfirmed(); }
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* ge.confirm(): a question of a plugin's own, in the same modal.
|
|
639
|
+
* `answer` is called once: true for the ok button, false for cancel,
|
|
640
|
+
* the close button or Escape, and null when the question goes
|
|
641
|
+
* unanswered because the modal is taken away - setLocale, destroy, or
|
|
642
|
+
* another question asked over it.
|
|
643
|
+
*/
|
|
644
|
+
function confirmWith(message, options, answer) {
|
|
645
|
+
options = options || {};
|
|
631
646
|
var Modal = modalLibrary();
|
|
632
647
|
|
|
633
648
|
if (!Modal) {
|
|
634
|
-
|
|
649
|
+
answer(window.confirm(message));
|
|
635
650
|
return;
|
|
636
651
|
}
|
|
637
652
|
|
|
638
653
|
var modal = confirmModal();
|
|
639
654
|
var confirmed = false;
|
|
655
|
+
var ok = dom.one(modal, '.ge-confirm-ok');
|
|
656
|
+
|
|
657
|
+
// The question before this one, if it is still up, goes unanswered
|
|
658
|
+
if (pendingAnswer) { pendingAnswer(null); }
|
|
659
|
+
|
|
660
|
+
var settle = function(value) {
|
|
661
|
+
if (pendingAnswer !== settle) { return; }
|
|
662
|
+
pendingAnswer = null;
|
|
663
|
+
answer(value);
|
|
664
|
+
};
|
|
665
|
+
pendingAnswer = settle;
|
|
666
|
+
|
|
667
|
+
dom.one(modal, '.modal-title').textContent = options.title || t('confirm.title');
|
|
668
|
+
dom.one(modal, '.ge-confirm-cancel').textContent = options.cancel || t('confirm.cancel');
|
|
669
|
+
ok.textContent = options.ok || t('confirm.ok');
|
|
670
|
+
dom.toggleClass(ok, 'btn-danger', !!options.danger);
|
|
671
|
+
dom.toggleClass(ok, 'btn-primary', !options.danger);
|
|
640
672
|
|
|
641
673
|
dom.one(modal, '.ge-confirm-message').textContent = message;
|
|
642
|
-
|
|
674
|
+
ok.onclick = function() {
|
|
643
675
|
confirmed = true;
|
|
644
676
|
Modal.getInstance(modal).hide();
|
|
645
677
|
};
|
|
646
678
|
|
|
647
679
|
confirmHandlers.hidden = function() {
|
|
648
680
|
// After the modal is out of the way, so the backdrop is not
|
|
649
|
-
// sitting over the animation
|
|
650
|
-
|
|
681
|
+
// sitting over the animation a delete runs
|
|
682
|
+
settle(confirmed);
|
|
651
683
|
};
|
|
652
684
|
|
|
653
685
|
confirmHandlers.shown = function() {
|
|
654
|
-
|
|
686
|
+
ok.focus();
|
|
655
687
|
};
|
|
656
688
|
|
|
657
689
|
Modal.getOrCreateInstance(modal).show();
|
|
@@ -660,6 +692,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
660
692
|
// What the confirm modal does when Bootstrap says it is shown or
|
|
661
693
|
// hidden: this question's answer, replaced by the next question's
|
|
662
694
|
var confirmHandlers = { hidden: null, shown: null };
|
|
695
|
+
var pendingAnswer = null; // The open question's answer, until it is given
|
|
663
696
|
|
|
664
697
|
/** Built once per instance, and taken away again by destroy(). */
|
|
665
698
|
function confirmModal() {
|
|
@@ -690,11 +723,15 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
690
723
|
dom.one(confirmDialog, '.ge-confirm-ok').textContent = t('confirm.ok');
|
|
691
724
|
|
|
692
725
|
trackModal(confirmDialog);
|
|
726
|
+
// Only while it is the modal asking: one being taken away -
|
|
727
|
+
// setLocale builds another - finishes hiding after the next
|
|
728
|
+
// question is up, and that answer is not its to give
|
|
729
|
+
var asking = confirmDialog;
|
|
693
730
|
confirmDialog.addEventListener('hidden.bs.modal', function() {
|
|
694
|
-
if (confirmHandlers.hidden) { confirmHandlers.hidden(); }
|
|
731
|
+
if (asking === confirmDialog && confirmHandlers.hidden) { confirmHandlers.hidden(); }
|
|
695
732
|
});
|
|
696
733
|
confirmDialog.addEventListener('shown.bs.modal', function() {
|
|
697
|
-
if (confirmHandlers.shown) { confirmHandlers.shown(); }
|
|
734
|
+
if (asking === confirmDialog && confirmHandlers.shown) { confirmHandlers.shown(); }
|
|
698
735
|
});
|
|
699
736
|
|
|
700
737
|
return confirmDialog;
|
|
@@ -709,6 +746,9 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
709
746
|
confirmHandlers.shown = null;
|
|
710
747
|
retireModal(confirmDialog);
|
|
711
748
|
confirmDialog = null;
|
|
749
|
+
|
|
750
|
+
// A plugin's question goes unanswered, and the plugin is told so
|
|
751
|
+
if (pendingAnswer) { pendingAnswer(null); }
|
|
712
752
|
}
|
|
713
753
|
|
|
714
754
|
/**
|
|
@@ -1081,7 +1121,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1081
1121
|
settings.new_row_layouts.forEach(function(layout) {
|
|
1082
1122
|
var grouped = !Array.isArray(layout);
|
|
1083
1123
|
var btn = dom.element('a', {
|
|
1084
|
-
'class': 'btn btn-sm btn-
|
|
1124
|
+
'class': 'btn btn-sm btn-light',
|
|
1085
1125
|
title: grouped
|
|
1086
1126
|
? t('row.add_row_cols', { columns: layout.columns, counts: rowColsText(layout.row_cols) })
|
|
1087
1127
|
: t('row.add', { layout: layout.join('-') }),
|
|
@@ -1126,7 +1166,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1126
1166
|
// buttons next to these ones do
|
|
1127
1167
|
Object.keys(CONTAINERS).forEach(function(type) {
|
|
1128
1168
|
var definition = CONTAINERS[type];
|
|
1129
|
-
var button = labelButton(dom.element('a', { 'class': 'btn btn-sm btn-
|
|
1169
|
+
var button = labelButton(dom.element('a', { 'class': 'btn btn-sm btn-light ge-add-container' }),
|
|
1130
1170
|
t(definition.labelKey), definition.iconClass);
|
|
1131
1171
|
|
|
1132
1172
|
dom.attr(button, { 'data-ge-toolbar': 'container', 'data-ge-container-type': type });
|
|
@@ -1150,8 +1190,10 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1150
1190
|
// One with align 'end' goes on the right instead, as an icon,
|
|
1151
1191
|
// beside the source and preview buttons.
|
|
1152
1192
|
var endItems = [];
|
|
1193
|
+
shownToolbar = {};
|
|
1153
1194
|
Object.keys(FEATURES).forEach(function(name) {
|
|
1154
|
-
|
|
1195
|
+
shownToolbar[name] = toolbarOf(FEATURES[name]);
|
|
1196
|
+
shownToolbar[name].forEach(function(item, index) {
|
|
1155
1197
|
var button = featureButton(name, item, index);
|
|
1156
1198
|
|
|
1157
1199
|
if (item.align === 'end') {
|
|
@@ -1274,7 +1316,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1274
1316
|
names.forEach(function(name) {
|
|
1275
1317
|
var tab = tabs.appendChild(dom.element('button', {
|
|
1276
1318
|
type: 'button',
|
|
1277
|
-
'class': 'btn btn-sm btn-outline-
|
|
1319
|
+
'class': 'btn btn-sm btn-outline-secondary',
|
|
1278
1320
|
role: 'tab',
|
|
1279
1321
|
'data-ge-group': name,
|
|
1280
1322
|
}, groupLabel(name)));
|
|
@@ -1316,6 +1358,34 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1316
1358
|
}
|
|
1317
1359
|
|
|
1318
1360
|
/** The toolbar off the page, with what keeps its overflow menu up to date. */
|
|
1361
|
+
/** A feature plugin's toolbar items: its array, or what its function returns now. */
|
|
1362
|
+
function toolbarOf(feature) {
|
|
1363
|
+
var items = typeof feature.toolbar === 'function' ? feature.toolbar() : feature.toolbar;
|
|
1364
|
+
return Array.isArray(items) ? items : [];
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/**
|
|
1368
|
+
* ge.refreshToolbar(): the toolbar built again, from what the plugins'
|
|
1369
|
+
* toolbars hold now - a plugin whose buttons come and go calls it when
|
|
1370
|
+
* they have. The category shown, the preview and the source view stay
|
|
1371
|
+
* as they are; an open overflow menu folds away.
|
|
1372
|
+
*/
|
|
1373
|
+
function refreshToolbar() {
|
|
1374
|
+
// Not built yet - the first one reads the toolbars as they are
|
|
1375
|
+
// then - or taken away for good
|
|
1376
|
+
if (!mainControls || destroyed) { return; }
|
|
1377
|
+
|
|
1378
|
+
var previewing = !!dom.one(mainControls, '.gm-preview.active');
|
|
1379
|
+
|
|
1380
|
+
removeMainControls();
|
|
1381
|
+
createMainControls();
|
|
1382
|
+
|
|
1383
|
+
if (previewing) {
|
|
1384
|
+
dom.addClass(dom.one(mainControls, '.gm-preview'), 'active btn-danger');
|
|
1385
|
+
}
|
|
1386
|
+
onScroll();
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1319
1389
|
function removeMainControls() {
|
|
1320
1390
|
if (toolbarLifetime) { toolbarLifetime.abort(); }
|
|
1321
1391
|
toolbarLifetime = null;
|
|
@@ -1333,7 +1403,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1333
1403
|
*/
|
|
1334
1404
|
function createOverflowMenu(start) {
|
|
1335
1405
|
var more = dom.create('<div class="ge-toolbar-more">' +
|
|
1336
|
-
'<button type="button" class="btn btn-sm btn-
|
|
1406
|
+
'<button type="button" class="btn btn-sm btn-light"><i class="bi bi-three-dots"></i></button>' +
|
|
1337
1407
|
'<div class="ge-toolbar-overflow">' +
|
|
1338
1408
|
'<div class="ge-addRowGroup btn-group"></div>' +
|
|
1339
1409
|
'<div class="ge-addContainerGroup btn-group"></div>' +
|
|
@@ -1442,7 +1512,10 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1442
1512
|
// A label of its own, worked out when the toolbar is built - and
|
|
1443
1513
|
// built again by setLocale - or the string its labelKey names
|
|
1444
1514
|
var label = typeof item.label === 'function' ? item.label() : (item.label || t(item.labelKey));
|
|
1445
|
-
|
|
1515
|
+
// Grey with the add buttons on the left, blue with the source and
|
|
1516
|
+
// preview buttons on the right
|
|
1517
|
+
var colour = item.align === 'end' ? 'btn-primary' : 'btn-light';
|
|
1518
|
+
var button = labelButton(dom.element('a', { 'class': 'btn btn-sm ' + colour + ' ge-add-container ge-add-feature' }), label, iconClass);
|
|
1446
1519
|
|
|
1447
1520
|
dom.addClass(button, item.className || '');
|
|
1448
1521
|
dom.attr(button, {
|
|
@@ -1613,7 +1686,10 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
1613
1686
|
* to the canvas, just after the top level block the pointer was in.
|
|
1614
1687
|
*/
|
|
1615
1688
|
function insertFeatureFromToolbar(button, where, source) {
|
|
1616
|
-
|
|
1689
|
+
// The item the button was built from, whatever the plugin's list
|
|
1690
|
+
// says now: it may have changed since, and the toolbar not yet
|
|
1691
|
+
var item = (shownToolbar[button.getAttribute('data-ge-feature')] || [])[parseInt(button.getAttribute('data-ge-item'), 10)];
|
|
1692
|
+
if (!item) { return null; }
|
|
1617
1693
|
var made = item.create();
|
|
1618
1694
|
if (!made) { return null; }
|
|
1619
1695
|
var placed = made;
|
|
@@ -2090,26 +2166,144 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
2090
2166
|
hideDropMarker();
|
|
2091
2167
|
// In case a dialog was opened with no settings panel open
|
|
2092
2168
|
closeDialog();
|
|
2093
|
-
|
|
2169
|
+
removeDrawers(canvas);
|
|
2094
2170
|
pluginFields.clear();
|
|
2095
|
-
unwrapTexts();
|
|
2171
|
+
unwrapTexts(canvas);
|
|
2096
2172
|
plugins('onDeinit');
|
|
2097
2173
|
// After the rich text editors have let go of their content areas:
|
|
2098
2174
|
// one that rebuilt its area's DOM brought the preview styles back
|
|
2099
2175
|
// with it, and the attribute recording them came back too
|
|
2100
2176
|
clearPreviews(canvas);
|
|
2101
|
-
|
|
2177
|
+
clearRowCols(canvas);
|
|
2102
2178
|
unmarkContainers();
|
|
2103
2179
|
removeSortable();
|
|
2104
2180
|
removeResizable();
|
|
2105
|
-
runFilter(false);
|
|
2181
|
+
runFilter(false, canvas);
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
function removeDrawers(root) {
|
|
2185
|
+
dom.all(root, '.ge-tools-drawer').forEach(function(drawer) { drawer.remove(); });
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
function clearRowCols(root) {
|
|
2189
|
+
dom.all(root, '[data-ge-row-cols]').forEach(function(row) { row.removeAttribute('data-ge-row-cols'); });
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
/**
|
|
2193
|
+
* The plugins that cannot be read without the canvas leaving editing:
|
|
2194
|
+
* the ones that put something on the canvas - an unmark, an onDeinit
|
|
2195
|
+
* or an onBeforeDeinit takes it off - and say nothing of how to take
|
|
2196
|
+
* it off a copy.
|
|
2197
|
+
*/
|
|
2198
|
+
function pluginsWithoutCleanMarkup() {
|
|
2199
|
+
var names = [];
|
|
2200
|
+
|
|
2201
|
+
[CONTAINERS, FEATURES, UTILITIES].forEach(function(registry) {
|
|
2202
|
+
Object.keys(registry).forEach(function(name) {
|
|
2203
|
+
var plugin = registry[name];
|
|
2204
|
+
var marks = plugin.unmark || plugin.onDeinit || plugin.onBeforeDeinit;
|
|
2205
|
+
if (marks && !plugin.cleanMarkup) { names.push(name); }
|
|
2206
|
+
});
|
|
2207
|
+
});
|
|
2208
|
+
|
|
2209
|
+
return names;
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
/**
|
|
2213
|
+
* A copy of the canvas, and the way from each of its elements back to
|
|
2214
|
+
* the one it was copied from. The two trees are walked side by side,
|
|
2215
|
+
* which works because a deep clone has the same shape.
|
|
2216
|
+
*/
|
|
2217
|
+
function copyCanvas() {
|
|
2218
|
+
var copy = canvas.cloneNode(true);
|
|
2219
|
+
var toLive = new Map();
|
|
2220
|
+
var toCopy = new Map();
|
|
2221
|
+
var live = document.createTreeWalker(canvas, NodeFilter.SHOW_ELEMENT);
|
|
2222
|
+
var copied = document.createTreeWalker(copy, NodeFilter.SHOW_ELEMENT);
|
|
2223
|
+
var a = live.currentNode;
|
|
2224
|
+
var b = copied.currentNode;
|
|
2225
|
+
|
|
2226
|
+
while (a && b) {
|
|
2227
|
+
toLive.set(b, a);
|
|
2228
|
+
toCopy.set(a, b);
|
|
2229
|
+
a = live.nextNode();
|
|
2230
|
+
b = copied.nextNode();
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
return {
|
|
2234
|
+
root: copy,
|
|
2235
|
+
liveOf: function(node) { return toLive.get(node) || null; },
|
|
2236
|
+
copyOf: function(node) { return toCopy.get(node) || null; },
|
|
2237
|
+
};
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
/**
|
|
2241
|
+
* getHtml without the canvas leaving editing: what deinit takes off,
|
|
2242
|
+
* taken off a copy, so the text being typed into, the settings panel
|
|
2243
|
+
* and the focus are where they were. The same markup getHtml gives,
|
|
2244
|
+
* or getHtml itself - with a word, once - when a plugin can only take
|
|
2245
|
+
* its marking off the canvas, or will not this time.
|
|
2246
|
+
*/
|
|
2247
|
+
function snapshotHtml() {
|
|
2248
|
+
// Nothing is being edited to keep: the canvas is out of editing
|
|
2249
|
+
if (sourceOpen) { return getHtml(); }
|
|
2250
|
+
|
|
2251
|
+
var missing = pluginsWithoutCleanMarkup();
|
|
2252
|
+
if (missing.length) {
|
|
2253
|
+
missing.forEach(function(name) {
|
|
2254
|
+
warnOnceHere('snapshot:' + name, 'the "' + name + '" plugin has no cleanMarkup, so the canvas ' +
|
|
2255
|
+
'leaves editing to be read: open texts and settings panels close');
|
|
2256
|
+
});
|
|
2257
|
+
return getHtml();
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
var copy = copyCanvas();
|
|
2261
|
+
var root = copy.root;
|
|
2262
|
+
|
|
2263
|
+
[root].concat(dom.all(root, '.ge-active-target, .ge-settings-target')).forEach(function(node) {
|
|
2264
|
+
dom.removeClass(node, 'ge-active-target ge-settings-target');
|
|
2265
|
+
dom.dropEmptyClass(node);
|
|
2266
|
+
});
|
|
2267
|
+
dom.all(root, '.ge-drop-marker').forEach(function(marker) { marker.remove(); });
|
|
2268
|
+
removeDrawers(root);
|
|
2269
|
+
unwrapTexts(root);
|
|
2270
|
+
|
|
2271
|
+
var refused = null;
|
|
2272
|
+
[CONTAINERS, FEATURES, UTILITIES].forEach(function(registry) {
|
|
2273
|
+
Object.keys(registry).forEach(function(name) {
|
|
2274
|
+
var plugin = registry[name];
|
|
2275
|
+
if (refused || !plugin.cleanMarkup) { return; }
|
|
2276
|
+
if (plugin.cleanMarkup(root, copy.liveOf) === false) { refused = name; }
|
|
2277
|
+
});
|
|
2278
|
+
});
|
|
2279
|
+
|
|
2280
|
+
if (refused) {
|
|
2281
|
+
warnOnceHere('snapshot-refused:' + refused, 'the "' + refused + '" plugin cannot take its marking off ' +
|
|
2282
|
+
'a copy right now, so the canvas leaves editing to be read');
|
|
2283
|
+
return getHtml();
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
clearPreviews(root);
|
|
2287
|
+
clearRowCols(root);
|
|
2288
|
+
dom.all(root, '[data-ge-container]').forEach(unmarkContainerClasses);
|
|
2289
|
+
sortables.forEach(function(sortableInstance) {
|
|
2290
|
+
var list = copy.copyOf(sortableInstance.el);
|
|
2291
|
+
if (list) { dom.all(list, '[draggable]').forEach(function(node) { node.removeAttribute('draggable'); }); }
|
|
2292
|
+
});
|
|
2293
|
+
removeResizeFurniture(root);
|
|
2294
|
+
runFilter(false, root);
|
|
2295
|
+
stripPixelWidths(root);
|
|
2296
|
+
|
|
2297
|
+
return root.innerHTML;
|
|
2106
2298
|
}
|
|
2107
2299
|
|
|
2108
2300
|
/**
|
|
2109
2301
|
* The markup as a host would save it: no drawers, no editor, no
|
|
2110
2302
|
* sortables. The canvas goes back to editing afterwards.
|
|
2111
2303
|
*/
|
|
2112
|
-
function getHtml() {
|
|
2304
|
+
function getHtml(options) {
|
|
2305
|
+
if (options && options.keepEditing) { return snapshotHtml(); }
|
|
2306
|
+
|
|
2113
2307
|
deinit();
|
|
2114
2308
|
stripPixelWidths(canvas);
|
|
2115
2309
|
var html = canvas.innerHTML;
|
|
@@ -2130,8 +2324,31 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
2130
2324
|
}
|
|
2131
2325
|
|
|
2132
2326
|
/** getHtml with grid-editor's own classes and attributes taken off. */
|
|
2133
|
-
function getPlainHtml() {
|
|
2134
|
-
return plainHtml(getHtml());
|
|
2327
|
+
function getPlainHtml(options) {
|
|
2328
|
+
return plainHtml(getHtml(options));
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
/**
|
|
2332
|
+
* ge.setHtml(): the canvas made of other markup, and edited again. Not
|
|
2333
|
+
* an add or a delete of anything, so no events: what was there is
|
|
2334
|
+
* replaced as a whole. innerHTML, as the source view does, so a
|
|
2335
|
+
* <script> in it is markup and does not run in the editor.
|
|
2336
|
+
*/
|
|
2337
|
+
function setHtml(html) {
|
|
2338
|
+
// What is in the textarea is not wanted - html replaces it - but
|
|
2339
|
+
// the source view closes the one way it always does
|
|
2340
|
+
if (sourceOpen) {
|
|
2341
|
+
closeSource();
|
|
2342
|
+
if (mainControls) {
|
|
2343
|
+
dom.all(mainControls, '.gm-edit-mode').forEach(function(button) {
|
|
2344
|
+
dom.removeClass(button, 'active btn-danger');
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
deinit();
|
|
2350
|
+
dom.setHtml(canvas, html);
|
|
2351
|
+
init();
|
|
2135
2352
|
}
|
|
2136
2353
|
|
|
2137
2354
|
/**
|
|
@@ -2141,6 +2358,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
2141
2358
|
* as a framework tearing a component down does.
|
|
2142
2359
|
*/
|
|
2143
2360
|
function destroy() {
|
|
2361
|
+
// First, while the editor is whole: a plugin may still read it
|
|
2362
|
+
plugins('onDestroy');
|
|
2144
2363
|
// The html being edited is what the canvas is left with
|
|
2145
2364
|
if (sourceOpen) { closeSource(); }
|
|
2146
2365
|
deinit();
|
|
@@ -2268,6 +2487,11 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
2268
2487
|
closeDialog: closeDialog,
|
|
2269
2488
|
rowFromLayout: rowFromLayoutValue,
|
|
2270
2489
|
nodeHtml: nodeHtml,
|
|
2490
|
+
// The markup as getHtml gives it, with the canvas left editing
|
|
2491
|
+
snapshotHtml: snapshotHtml,
|
|
2492
|
+
setHtml: setHtml,
|
|
2493
|
+
// A question in the editor's confirm modal
|
|
2494
|
+
confirm: confirmWith,
|
|
2271
2495
|
// A text editor has rewritten a content area, so whatever the
|
|
2272
2496
|
// editor and its plugins had put in there goes back in
|
|
2273
2497
|
textReady: textReady,
|
|
@@ -2280,6 +2504,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
2280
2504
|
// A node's drawer: its first child, or for a content area the
|
|
2281
2505
|
// one beside it in its text block
|
|
2282
2506
|
drawerOf: drawerOf,
|
|
2507
|
+
// The toolbar built again, from the plugins' toolbars as they are now
|
|
2508
|
+
refreshToolbar: refreshToolbar,
|
|
2283
2509
|
toolbarItems: function(name) {
|
|
2284
2510
|
return mainControls
|
|
2285
2511
|
? dom.all(mainControls, '[data-ge-toolbar="feature"][data-ge-feature="' + name + '"]')
|
|
@@ -2990,11 +3216,15 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
2990
3216
|
|
|
2991
3217
|
if (definition) { definition.unmark(container); }
|
|
2992
3218
|
|
|
2993
|
-
|
|
2994
|
-
dom.dropEmptyClass(container);
|
|
3219
|
+
unmarkContainerClasses(container);
|
|
2995
3220
|
});
|
|
2996
3221
|
}
|
|
2997
3222
|
|
|
3223
|
+
function unmarkContainerClasses(container) {
|
|
3224
|
+
dom.removeClass(container, 'ge-container ge-container-' + containerTypeOf(container));
|
|
3225
|
+
dom.dropEmptyClass(container);
|
|
3226
|
+
}
|
|
3227
|
+
|
|
2998
3228
|
/** A new drawer as the first child of `node`. */
|
|
2999
3229
|
function prependDrawer(node, className) {
|
|
3000
3230
|
var drawer = dom.element('div', { 'class': className });
|
|
@@ -4923,10 +5153,14 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
4923
5153
|
}
|
|
4924
5154
|
|
|
4925
5155
|
function removeResizable() {
|
|
4926
|
-
|
|
5156
|
+
removeResizeFurniture(canvas);
|
|
5157
|
+
}
|
|
4927
5158
|
|
|
4928
|
-
|
|
4929
|
-
|
|
5159
|
+
function removeResizeFurniture(root) {
|
|
5160
|
+
dom.all(root, '.ge-resize-handle').forEach(function(handle) { handle.remove(); });
|
|
5161
|
+
|
|
5162
|
+
dom.all(root, '.ge-resize-size').forEach(function(readout) { readout.remove(); });
|
|
5163
|
+
stripPixelWidths(root);
|
|
4930
5164
|
}
|
|
4931
5165
|
|
|
4932
5166
|
/**
|
|
@@ -5252,7 +5486,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
5252
5486
|
* function, or the name of one on window, and gets the canvas and
|
|
5253
5487
|
* whether this is init.
|
|
5254
5488
|
*/
|
|
5255
|
-
function runFilter(isInit) {
|
|
5489
|
+
function runFilter(isInit, root) {
|
|
5256
5490
|
if (!settings.custom_filter || !settings.custom_filter.length) { return; }
|
|
5257
5491
|
|
|
5258
5492
|
var filters = typeof settings.custom_filter === 'string' || typeof settings.custom_filter === 'function'
|
|
@@ -5264,7 +5498,7 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
5264
5498
|
func = window[func];
|
|
5265
5499
|
}
|
|
5266
5500
|
|
|
5267
|
-
func(canvas, isInit);
|
|
5501
|
+
func(root || canvas, isInit);
|
|
5268
5502
|
});
|
|
5269
5503
|
}
|
|
5270
5504
|
|
|
@@ -5457,8 +5691,8 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
5457
5691
|
}
|
|
5458
5692
|
|
|
5459
5693
|
/** After the drawers are gone, so a wrapper holds its content area and nothing else. */
|
|
5460
|
-
function unwrapTexts() {
|
|
5461
|
-
dom.all(
|
|
5694
|
+
function unwrapTexts(root) {
|
|
5695
|
+
dom.all(root, '.ge-text-block').forEach(function(textBlock) {
|
|
5462
5696
|
dom.unwrap(textBlock);
|
|
5463
5697
|
});
|
|
5464
5698
|
}
|
|
@@ -5666,6 +5900,22 @@ function build(instance, baseElem, optionsOrMethod) {
|
|
|
5666
5900
|
// Again, with what the plugins filled in: the text editors'
|
|
5667
5901
|
// content_types when the host gave none
|
|
5668
5902
|
instance.settings = settingsCopy();
|
|
5903
|
+
|
|
5904
|
+
// A feature plugin's methods that are not the editor's own documented
|
|
5905
|
+
// ones - the autosave plugin's - are the instance's too, while the
|
|
5906
|
+
// plugin is there
|
|
5907
|
+
Object.keys(featureMethods).forEach(function(name) {
|
|
5908
|
+
if (Object.prototype.hasOwnProperty.call(own, name) || name in instance) { return; }
|
|
5909
|
+
|
|
5910
|
+
instance[name] = function() {
|
|
5911
|
+
if (destroyed) {
|
|
5912
|
+
warnOnceHere('destroyed:' + name, t('warning.destroyed', { method: name }));
|
|
5913
|
+
return null;
|
|
5914
|
+
}
|
|
5915
|
+
|
|
5916
|
+
return featureMethods[name].apply(null, arguments);
|
|
5917
|
+
};
|
|
5918
|
+
});
|
|
5669
5919
|
setup();
|
|
5670
5920
|
init();
|
|
5671
5921
|
|
|
@@ -177,8 +177,8 @@ export function displayPart(ge, given) {
|
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
function unmark() {
|
|
181
|
-
dom.all(ge.canvas, '.ge-hidden-in-view, [data-ge-hidden-in], [' + SHOWN_ATTR + ']').forEach(function(node) {
|
|
180
|
+
function unmark(root) {
|
|
181
|
+
dom.all(root || ge.canvas, '.ge-hidden-in-view, [data-ge-hidden-in], [' + SHOWN_ATTR + ']').forEach(function(node) {
|
|
182
182
|
dom.removeClass(node, 'ge-hidden-in-view');
|
|
183
183
|
node.removeAttribute('data-ge-hidden-in');
|
|
184
184
|
node.removeAttribute(SHOWN_ATTR);
|
|
@@ -224,6 +224,7 @@ export function displayPart(ge, given) {
|
|
|
224
224
|
},
|
|
225
225
|
|
|
226
226
|
onRefresh: mark,
|
|
227
|
-
onDeinit: unmark,
|
|
227
|
+
onDeinit: function() { unmark(); },
|
|
228
|
+
cleanMarkup: unmark,
|
|
228
229
|
};
|
|
229
230
|
}
|
|
@@ -95,15 +95,19 @@ export function drawerflowPart(ge) {
|
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
function
|
|
99
|
-
|
|
100
|
-
observer = null;
|
|
101
|
-
|
|
102
|
-
dom.all(ge.canvas, '.' + OUT_CLASS).forEach(function(node) {
|
|
98
|
+
function unmarkNodes(root) {
|
|
99
|
+
dom.all(root, '.' + OUT_CLASS).forEach(function(node) {
|
|
103
100
|
dom.removeClass(node, OUT_CLASS);
|
|
104
101
|
node.removeAttribute(OUT_ATTR);
|
|
105
102
|
dom.dropEmptyClass(node);
|
|
106
103
|
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function unmark() {
|
|
107
|
+
if (observer) { observer.disconnect(); }
|
|
108
|
+
observer = null;
|
|
109
|
+
|
|
110
|
+
unmarkNodes(ge.canvas);
|
|
107
111
|
|
|
108
112
|
if (sheet) { sheet.remove(); }
|
|
109
113
|
sheet = null;
|
|
@@ -114,5 +118,6 @@ export function drawerflowPart(ge) {
|
|
|
114
118
|
families: [],
|
|
115
119
|
onRefresh: mark,
|
|
116
120
|
onDeinit: unmark,
|
|
121
|
+
cleanMarkup: unmarkNodes,
|
|
117
122
|
};
|
|
118
123
|
}
|
|
@@ -140,6 +140,10 @@ GridEditor.locales.es = {
|
|
|
140
140
|
'tool.paste': 'Pegar',
|
|
141
141
|
'clipboard.paste_row': 'Pegar fila',
|
|
142
142
|
'clipboard.paste_section': 'Pegar sección',
|
|
143
|
+
'autosave.restore_title': '¿Recuperar el borrador?',
|
|
144
|
+
'autosave.restore_message': 'Hay un borrador de esta página guardado el {date}, con cambios que no se publicaron. ¿Quieres recuperarlo?',
|
|
145
|
+
'autosave.restore': 'Recuperar',
|
|
146
|
+
'autosave.discard': 'Descartar',
|
|
143
147
|
'utility.col_from_row': 'De la fila: {count}',
|
|
144
148
|
'utility.row_cols': 'Columnas por fila',
|
|
145
149
|
'badge.row_cols': '{count} por fila',
|
|
@@ -206,6 +206,12 @@ GridEditor.containers.accordion = function(ge) {
|
|
|
206
206
|
ge.unwrapLabels(container);
|
|
207
207
|
},
|
|
208
208
|
|
|
209
|
+
// The same, on a copy of the canvas: unmark only touches markup
|
|
210
|
+
cleanMarkup: function(root) {
|
|
211
|
+
var definition = this;
|
|
212
|
+
dom.all(root, '[data-ge-container="accordion"]').forEach(function(container) { definition.unmark(container); });
|
|
213
|
+
},
|
|
214
|
+
|
|
209
215
|
afterPaneMove: function(container, item) {
|
|
210
216
|
reparentAccordionItem(container, item);
|
|
211
217
|
},
|