@themarioga/grid-editor 5.1.2 → 5.3.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/BUILDING.md +6 -0
- package/CHANGELOG.md +58 -0
- package/README.md +46 -9
- package/UPGRADING.md +25 -0
- package/dist/grideditor.css +28 -0
- package/dist/grideditor.min.css +1 -1
- package/dist/grideditor.min.css.map +1 -1
- package/dist/jquery.grideditor.bundle.min.js +2 -2
- package/dist/jquery.grideditor.js +760 -245
- package/dist/jquery.grideditor.min.js +1 -1
- package/dist/jquery.grideditor.min.js.map +1 -1
- package/dist/locales/grideditor.es.js +11 -0
- package/dist/locales/grideditor.es.min.js +1 -1
- package/dist/locales/grideditor.es.min.js.map +1 -1
- package/dist/plugins/grideditor.ckeditor.js +120 -0
- package/dist/plugins/grideditor.ckeditor.min.js +2 -0
- package/dist/plugins/grideditor.ckeditor.min.js.map +1 -0
- package/dist/plugins/grideditor.clipboard.js +8 -7
- package/dist/plugins/grideditor.clipboard.min.js +1 -1
- package/dist/plugins/grideditor.clipboard.min.js.map +1 -1
- package/dist/plugins/grideditor.spacing.js +1 -1
- package/dist/plugins/grideditor.spacing.min.js +1 -1
- package/dist/plugins/grideditor.spacing.min.js.map +1 -1
- package/dist/plugins/grideditor.summernote.js +98 -0
- package/dist/plugins/grideditor.summernote.min.js +2 -0
- package/dist/plugins/grideditor.summernote.min.js.map +1 -0
- package/dist/plugins/grideditor.textalign.js +1 -1
- package/dist/plugins/grideditor.textalign.min.js +1 -1
- package/dist/plugins/grideditor.textalign.min.js.map +1 -1
- package/dist/plugins/grideditor.tinymce.js +162 -0
- package/dist/plugins/grideditor.tinymce.min.js +2 -0
- package/dist/plugins/grideditor.tinymce.min.js.map +1 -0
- package/dist/plugins/grideditor.visibility.js +3 -3
- package/dist/plugins/grideditor.visibility.min.js +1 -1
- package/dist/plugins/grideditor.visibility.min.js.map +1 -1
- package/docs/events.md +5 -2
- package/docs/locale-keys.md +23 -1
- package/docs/plugins.md +61 -4
- package/example/attributes.html +4 -2
- package/example/autosave.html +2 -0
- package/example/basic.html +2 -0
- package/example/ckeditor.html +2 -0
- package/example/clipboard.html +2 -0
- package/example/containers.html +2 -0
- package/example/elements.html +2 -0
- package/example/summernote.html +2 -0
- package/example/wrap_content.html +2 -0
- package/package.json +1 -1
- package/src/js/compat/bundled-texts.js +14 -0
- package/src/js/jquery.grideditor.js +444 -33
- package/src/js/locales/grideditor.es.js +11 -0
- package/src/js/plugins/grideditor.ckeditor.js +120 -0
- package/src/js/plugins/grideditor.clipboard.js +8 -7
- package/src/js/plugins/grideditor.spacing.js +1 -1
- package/src/js/plugins/grideditor.summernote.js +98 -0
- package/src/js/plugins/grideditor.textalign.js +1 -1
- package/src/js/plugins/grideditor.tinymce.js +162 -0
- package/src/js/plugins/grideditor.visibility.js +3 -3
- package/src/less/grideditor.less +34 -0
- package/src/js/jquery.grideditor.ckeditor.js +0 -77
- package/src/js/jquery.grideditor.summernote.js +0 -72
- package/src/js/jquery.grideditor.tinymce.js +0 -143
|
@@ -29,6 +29,7 @@ var METHODS = {
|
|
|
29
29
|
createRow: { value: true },
|
|
30
30
|
createColumn: { value: true },
|
|
31
31
|
createElement: { value: true },
|
|
32
|
+
createText: { value: true },
|
|
32
33
|
createSection: { value: true },
|
|
33
34
|
createContainer: { value: true },
|
|
34
35
|
addTab: { value: true },
|
|
@@ -338,6 +339,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
338
339
|
'toolbar_drag' : 'auto', // Drag the toolbar's buttons onto the canvas. 'auto' follows drag_handle
|
|
339
340
|
'element_tools' : [], // Host tools on element drawers, same shape as row_tools
|
|
340
341
|
'element_classes' : [], // Preset class toggles on an element's settings panel
|
|
342
|
+
'text_tools' : [], // Host tools on text block drawers
|
|
343
|
+
'text_classes' : [], // Preset class toggles on a text block's settings panel
|
|
341
344
|
'container_classes' : [], // The same, on a container's panel
|
|
342
345
|
'pane_classes' : [], // And on a tab's or an accordion item's
|
|
343
346
|
'container_tools' : [], // Host tools on container drawers
|
|
@@ -831,7 +834,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
831
834
|
if (node.hasClass('row')) { return 'row'; }
|
|
832
835
|
if (node.hasClass('column')) { return 'column'; }
|
|
833
836
|
if (node.hasClass('ge-element')) { return 'element'; }
|
|
834
|
-
if (node.hasClass('ge-content')) { return '
|
|
837
|
+
if (node.hasClass('ge-content') || node.hasClass('ge-text-block')) { return 'text'; }
|
|
835
838
|
return 'node';
|
|
836
839
|
}
|
|
837
840
|
|
|
@@ -879,6 +882,12 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
879
882
|
// which costs the element drawers inside it. The integrations say
|
|
880
883
|
// when their editor is ready, and the drawers go back in.
|
|
881
884
|
canvas.on('ge-rte-ready', '.ge-content', function() {
|
|
885
|
+
// The first time only: an undo says ready again, and by then
|
|
886
|
+
// the host may have changed the content area itself
|
|
887
|
+
if ($(this).data('ge-text-before') && !$(this).data('ge-text-ready')) {
|
|
888
|
+
$(this).data('ge-text-ready', attributesOf(this));
|
|
889
|
+
}
|
|
890
|
+
|
|
882
891
|
plugins('onContentReady', $(this));
|
|
883
892
|
refreshPreviews($(this));
|
|
884
893
|
});
|
|
@@ -936,6 +945,35 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
936
945
|
|
|
937
946
|
addContainerGroup.appendTo(wrapper);
|
|
938
947
|
|
|
948
|
+
// A text block of each editor offered. Like a container, it goes
|
|
949
|
+
// into a row of its own when clicked, or where it is dropped
|
|
950
|
+
var texts = offeredTexts();
|
|
951
|
+
texts.forEach(function(type) {
|
|
952
|
+
var label = texts.length > 1 ? t('text.add_type', { editor: textLabel(type) }) : t('text.add');
|
|
953
|
+
|
|
954
|
+
$('<a class="btn btn-sm btn-primary ge-add-container ge-add-text-button" />')
|
|
955
|
+
.attr('title', label)
|
|
956
|
+
.attr('data-ge-toolbar', 'text')
|
|
957
|
+
.attr('data-ge-text', type)
|
|
958
|
+
.append('<i class="bi bi-plus"></i>')
|
|
959
|
+
.append($('<span />').text(label))
|
|
960
|
+
.on('click', function() {
|
|
961
|
+
var row = createRow();
|
|
962
|
+
var block = makeText(type);
|
|
963
|
+
createColumn(MAX_COL_SIZE).appendTo(row).find('> .ge-content').replaceWith(block);
|
|
964
|
+
|
|
965
|
+
var added = addNode('text', block, function() {
|
|
966
|
+
row.appendTo(canvas);
|
|
967
|
+
}, { parent: canvas, source: 'tool' });
|
|
968
|
+
|
|
969
|
+
if (added && row[0].scrollIntoView) {
|
|
970
|
+
row[0].scrollIntoView({behavior: 'smooth'});
|
|
971
|
+
}
|
|
972
|
+
})
|
|
973
|
+
.appendTo(addContainerGroup)
|
|
974
|
+
;
|
|
975
|
+
});
|
|
976
|
+
|
|
939
977
|
// A container starts in a row of its own, the way the add row
|
|
940
978
|
// buttons next to these ones do
|
|
941
979
|
$.each(CONTAINERS, function(type, definition) {
|
|
@@ -1226,6 +1264,28 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1226
1264
|
}, { parent: where.region, source: item.source || 'dragdrop' });
|
|
1227
1265
|
}
|
|
1228
1266
|
|
|
1267
|
+
/**
|
|
1268
|
+
* A text block dropped from the toolbar: where it lands in a column,
|
|
1269
|
+
* or in a row and a column of its own anywhere else.
|
|
1270
|
+
*/
|
|
1271
|
+
function insertTextFromToolbar(button, where) {
|
|
1272
|
+
var made = makeText(button.attr('data-ge-text'));
|
|
1273
|
+
var placed = made;
|
|
1274
|
+
|
|
1275
|
+
if (!where.region.is('.column')) {
|
|
1276
|
+
placed = createRow();
|
|
1277
|
+
createColumn(MAX_COL_SIZE).appendTo(placed).find('> .ge-content').replaceWith(made);
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
return addNode('text', made, function() {
|
|
1281
|
+
if (where.before) {
|
|
1282
|
+
placed.insertBefore(where.before);
|
|
1283
|
+
} else {
|
|
1284
|
+
placed.appendTo(where.region);
|
|
1285
|
+
}
|
|
1286
|
+
}, { parent: where.region, source: 'dragdrop' });
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1229
1289
|
/** A line where the block would go, following the pointer. */
|
|
1230
1290
|
function showDropMarker(pageX, pageY) {
|
|
1231
1291
|
var where = dropPlaceAt(pageX, pageY);
|
|
@@ -1255,6 +1315,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1255
1315
|
if (button.attr('data-ge-toolbar') === 'feature') {
|
|
1256
1316
|
return insertFeatureFromToolbar(button, where);
|
|
1257
1317
|
}
|
|
1318
|
+
if (button.attr('data-ge-toolbar') === 'text') {
|
|
1319
|
+
return insertTextFromToolbar(button, where);
|
|
1320
|
+
}
|
|
1258
1321
|
|
|
1259
1322
|
var container = button.attr('data-ge-toolbar') === 'container';
|
|
1260
1323
|
var type = button.attr('data-ge-container-type');
|
|
@@ -1404,11 +1467,28 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1404
1467
|
// its toolbar out against, and nothing anyone can type into
|
|
1405
1468
|
if (!$(this).is(':visible')) { return; }
|
|
1406
1469
|
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1470
|
+
// The attribute, not jQuery's cached copy of it: it is markup, and
|
|
1471
|
+
// the drawer reads it the same way
|
|
1472
|
+
var type = $(this).attr('data-ge-content-type');
|
|
1473
|
+
var text = textFor(type);
|
|
1474
|
+
if (!text) { return; }
|
|
1475
|
+
|
|
1476
|
+
// Not marked active, so the next click tries again: the library
|
|
1477
|
+
// may be loaded by then
|
|
1478
|
+
if (text.available && !text.available()) {
|
|
1479
|
+
if (text.missingKey) { console.error(t(text.missingKey)); }
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
if (text.bundled) {
|
|
1484
|
+
warnOnceHere('text-bundled:' + type, 'the ' + type + ' text editor is the copy in the ' +
|
|
1485
|
+
'main bundle, which 6.0 will not include: load dist/plugins/grideditor.' + type +
|
|
1486
|
+
'.js after the editor');
|
|
1411
1487
|
}
|
|
1488
|
+
|
|
1489
|
+
$(this).data('ge-text-before', attributesOf(this)).removeData('ge-text-ready');
|
|
1490
|
+
$(this).addClass('ge-rte-active');
|
|
1491
|
+
text.start($(this));
|
|
1412
1492
|
}
|
|
1413
1493
|
|
|
1414
1494
|
function reset() {
|
|
@@ -1422,6 +1502,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1422
1502
|
canvas.toggleClass('ge-drag-drawer', settings.drag_handle === 'drawer');
|
|
1423
1503
|
addAllColClasses();
|
|
1424
1504
|
wrapContent();
|
|
1505
|
+
wrapTexts();
|
|
1425
1506
|
createRowControls();
|
|
1426
1507
|
createColControls();
|
|
1427
1508
|
markContainers();
|
|
@@ -1434,21 +1515,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1434
1515
|
|
|
1435
1516
|
function deinit() {
|
|
1436
1517
|
canvas.removeClass('ge-editing ge-drag-drawer ge-dropping');
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
var rte = getRTE(content.data('ge-content-type'));
|
|
1440
|
-
if (rte) {
|
|
1441
|
-
rte.deinit(settings, content);
|
|
1442
|
-
}
|
|
1443
|
-
// Cleared after rte.deinit, not before: an editor can restore the
|
|
1444
|
-
// class attribute it snapshotted when it was created, which would
|
|
1445
|
-
// leave ge-rte-active in place and make initRTE ignore every later
|
|
1446
|
-
// click on this content area.
|
|
1447
|
-
content.removeClass('ge-rte-active');
|
|
1518
|
+
canvas.find('.ge-content').each(function() {
|
|
1519
|
+
closeText($(this));
|
|
1448
1520
|
});
|
|
1449
1521
|
closeSizePicker();
|
|
1450
1522
|
hideDropMarker();
|
|
1451
1523
|
canvas.find('.ge-tools-drawer').remove();
|
|
1524
|
+
unwrapTexts();
|
|
1452
1525
|
plugins('onDeinit');
|
|
1453
1526
|
// After the rich text editors have let go of their content areas:
|
|
1454
1527
|
// one that rebuilt its area's DOM brought the preview styles back
|
|
@@ -1555,6 +1628,23 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1555
1628
|
if (wanted(name)) { FEATURES[name] = factory(api); }
|
|
1556
1629
|
});
|
|
1557
1630
|
|
|
1631
|
+
// Text editors are chosen by content_types rather than by the
|
|
1632
|
+
// plugins setting: a page that names its containers there has
|
|
1633
|
+
// named no editor, and still wants the one it uses
|
|
1634
|
+
$.each($.fn.gridEditor.texts, function(type, factory) {
|
|
1635
|
+
TEXTS[type] = $.extend({ bundled: !!factory.bundled }, factory(api));
|
|
1636
|
+
});
|
|
1637
|
+
|
|
1638
|
+
// 5.x's registry, for an integration a host wrote itself. One the
|
|
1639
|
+
// plugins above already provide is theirs.
|
|
1640
|
+
$.each($.fn.gridEditor.RTEs, function(type, rte) {
|
|
1641
|
+
if (TEXTS[type]) { return; }
|
|
1642
|
+
|
|
1643
|
+
warnOnceHere('rtes:' + type, '$.fn.gridEditor.RTEs is deprecated and will be removed ' +
|
|
1644
|
+
'in 7.0: register "' + type + '" under $.fn.gridEditor.texts, see docs/plugins.md');
|
|
1645
|
+
TEXTS[type] = legacyText(rte);
|
|
1646
|
+
});
|
|
1647
|
+
|
|
1558
1648
|
$.each($.fn.gridEditor.utilities, function(name, factory) {
|
|
1559
1649
|
if (!wanted(name)) { return; }
|
|
1560
1650
|
|
|
@@ -1631,6 +1721,16 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1631
1721
|
bareStyle: bareStyle,
|
|
1632
1722
|
rowFromLayout: rowFromLayoutValue,
|
|
1633
1723
|
nodeHtml: nodeHtml,
|
|
1724
|
+
// A text editor has rewritten a content area, so whatever the
|
|
1725
|
+
// editor and its plugins had put in there goes back in
|
|
1726
|
+
textReady: function(block) { block.trigger('ge-rte-ready'); },
|
|
1727
|
+
// A node's drawer: its first child, or for a content area the
|
|
1728
|
+
// one beside it in its text block
|
|
1729
|
+
drawerOf: function(node) {
|
|
1730
|
+
return node.hasClass('ge-content')
|
|
1731
|
+
? node.parent('.ge-text-block').children('.ge-tools-drawer')
|
|
1732
|
+
: node.children('.ge-tools-drawer');
|
|
1733
|
+
},
|
|
1634
1734
|
toolbarItems: function(name) {
|
|
1635
1735
|
return mainControls
|
|
1636
1736
|
? mainControls.find('[data-ge-toolbar="feature"][data-ge-feature="' + name + '"]')
|
|
@@ -1657,7 +1757,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1657
1757
|
* -------------------------------------------------------------- */
|
|
1658
1758
|
|
|
1659
1759
|
var FAMILIES = {}; // Every family the utility plugins declare, by name
|
|
1660
|
-
var UTILITY_NODES = '.row, .column, .ge-element, [data-ge-container]';
|
|
1760
|
+
var UTILITY_NODES = '.row, .column, .ge-content, .ge-element, [data-ge-container]';
|
|
1661
1761
|
var PREVIEW_ATTR = 'data-ge-preview';
|
|
1662
1762
|
var utilitiesOpen = false; // Whether the panels show their Responsive section unfolded
|
|
1663
1763
|
|
|
@@ -2164,6 +2264,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2164
2264
|
* -------------------------------------------------------------- */
|
|
2165
2265
|
|
|
2166
2266
|
var CONTAINERS = {}; // The container plugins in use, by the type each builds
|
|
2267
|
+
var TEXTS = {}; // The text editor plugins, by the content type each edits
|
|
2167
2268
|
var FEATURES = {}; // The feature plugins in use, by name
|
|
2168
2269
|
var UTILITIES = {}; // The utility plugins in use, by name
|
|
2169
2270
|
var featureMethods = {}; // The methods those features contribute
|
|
@@ -2444,6 +2545,14 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2444
2545
|
function attachSizePicker(tool, row) {
|
|
2445
2546
|
if (!settings.add_column.picker) { return; }
|
|
2446
2547
|
|
|
2548
|
+
attachPicker(tool, function() { openSizePicker(tool, row); });
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
/**
|
|
2552
|
+
* Hold a tool - or rest the pointer on it - and `open` offers a
|
|
2553
|
+
* choice under it. The add text tool offers its editors the same way.
|
|
2554
|
+
*/
|
|
2555
|
+
function attachPicker(tool, open) {
|
|
2447
2556
|
var timer = null;
|
|
2448
2557
|
|
|
2449
2558
|
var cancel = function() {
|
|
@@ -2456,7 +2565,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2456
2565
|
|
|
2457
2566
|
timer = window.setTimeout(function() {
|
|
2458
2567
|
timer = null;
|
|
2459
|
-
|
|
2568
|
+
open();
|
|
2460
2569
|
}, settings.add_column.delay);
|
|
2461
2570
|
});
|
|
2462
2571
|
|
|
@@ -2606,6 +2715,22 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2606
2715
|
});
|
|
2607
2716
|
});
|
|
2608
2717
|
|
|
2718
|
+
// A text block of the first editor offered; held, the tool
|
|
2719
|
+
// offers each of them when there is more than one
|
|
2720
|
+
var texts = offeredTexts();
|
|
2721
|
+
if (texts.length) {
|
|
2722
|
+
createTool(drawer, t('tool.add_text'), 'ge-add-text', 'bi bi-type', function() {
|
|
2723
|
+
if (closeSizePicker()) { return; } // The picker was open: that was the answer
|
|
2724
|
+
|
|
2725
|
+
addTextTo(col, texts[0]);
|
|
2726
|
+
});
|
|
2727
|
+
|
|
2728
|
+
if (texts.length > 1) {
|
|
2729
|
+
var textTool = drawer.find('> .ge-add-text');
|
|
2730
|
+
attachPicker(textTool, function() { openTextPicker(textTool, col); });
|
|
2731
|
+
}
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2609
2734
|
createTool(drawer, t('tool.add_row'), 'ge-add-row', 'bi bi-plus-circle', function() {
|
|
2610
2735
|
// An empty row: the columns in it are the next decision,
|
|
2611
2736
|
// and its drawer's add column tool is where that is made
|
|
@@ -2720,7 +2845,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2720
2845
|
}
|
|
2721
2846
|
|
|
2722
2847
|
function createTool(drawer, title, className, iconClass, eventHandlers) {
|
|
2723
|
-
|
|
2848
|
+
// Through attr rather than spliced into markup: a title is text,
|
|
2849
|
+
// and a quote in it - a type name, a host's own title - would
|
|
2850
|
+
// end the attribute early
|
|
2851
|
+
var tool = $('<a />')
|
|
2852
|
+
.attr('title', title)
|
|
2853
|
+
.attr('class', className)
|
|
2854
|
+
.append($('<i />').attr('class', iconClass))
|
|
2724
2855
|
.appendTo(drawer)
|
|
2725
2856
|
;
|
|
2726
2857
|
if (typeof eventHandlers == 'function') {
|
|
@@ -3154,11 +3285,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3154
3285
|
|
|
3155
3286
|
/**
|
|
3156
3287
|
* What the canvas, the columns and the plugins' regions move: rows,
|
|
3157
|
-
*
|
|
3158
|
-
* the sections plugin's sections.
|
|
3288
|
+
* text blocks and containers, and whatever blocks a feature adds -
|
|
3289
|
+
* the sections plugin's sections. A content area is a block through
|
|
3290
|
+
* the text block around it while editing, and on its own before.
|
|
3159
3291
|
*/
|
|
3160
3292
|
function blockSelector() {
|
|
3161
|
-
return ['.row', '.ge-content', '[data-ge-container]']
|
|
3293
|
+
return ['.row', '.ge-text-block', '.ge-content', '[data-ge-container]']
|
|
3294
|
+
.concat(pluginHooks('blocks')).join(', ');
|
|
3162
3295
|
}
|
|
3163
3296
|
|
|
3164
3297
|
/** A selector every feature plugin may contribute to, under one name. */
|
|
@@ -3324,12 +3457,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3324
3457
|
function sortStart(e) {
|
|
3325
3458
|
var node = $(e.item);
|
|
3326
3459
|
var from = positionOf(node);
|
|
3460
|
+
var subject = moveSubject(node);
|
|
3327
3461
|
|
|
3328
3462
|
node.data('ge-move-from', from);
|
|
3329
3463
|
node.removeData('ge-move-canceled');
|
|
3330
3464
|
|
|
3331
3465
|
operate(function() {
|
|
3332
|
-
var moving = emit('before-move', payloadFor(
|
|
3466
|
+
var moving = emit('before-move', payloadFor(subject.kind, subject.node, {
|
|
3333
3467
|
parent: from.parent,
|
|
3334
3468
|
source: 'dragdrop',
|
|
3335
3469
|
from: from,
|
|
@@ -3339,6 +3473,19 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3339
3473
|
});
|
|
3340
3474
|
}
|
|
3341
3475
|
|
|
3476
|
+
/**
|
|
3477
|
+
* What a drag reports as moving. A text block is the editor's own
|
|
3478
|
+
* wrapper, so what moved is the content area inside it, and until 6.0
|
|
3479
|
+
* a content area moves as kind 'content', as it did in 5.x - adding
|
|
3480
|
+
* and deleting one, which 5.x never announced, already say 'text'.
|
|
3481
|
+
*/
|
|
3482
|
+
function moveSubject(item) {
|
|
3483
|
+
var node = item.hasClass('ge-text-block') ? item.children('.ge-content') : item;
|
|
3484
|
+
var kind = kindOf(node);
|
|
3485
|
+
|
|
3486
|
+
return { kind: kind === 'text' ? 'content' : kind, node: node };
|
|
3487
|
+
}
|
|
3488
|
+
|
|
3342
3489
|
/** Put a node back where a refused drag found it. */
|
|
3343
3490
|
function putBack(node, from) {
|
|
3344
3491
|
var siblings = from.parent.children().not('.ge-tools-drawer').not(node);
|
|
@@ -3376,8 +3523,10 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3376
3523
|
// No init() here, unlike an add: the node brought its drawer
|
|
3377
3524
|
// with it, and the drag is still being finished, so this is the
|
|
3378
3525
|
// wrong moment to rebuild the lists it is using.
|
|
3526
|
+
var subject = moveSubject(node);
|
|
3527
|
+
|
|
3379
3528
|
operate(function() {
|
|
3380
|
-
emit('after-move', payloadFor(
|
|
3529
|
+
emit('after-move', payloadFor(subject.kind, subject.node, {
|
|
3381
3530
|
parent: to.parent,
|
|
3382
3531
|
source: 'dragdrop',
|
|
3383
3532
|
from: from,
|
|
@@ -3770,9 +3919,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3770
3919
|
* the all view. `offset` indents it, within the same 12 unit budget.
|
|
3771
3920
|
*/
|
|
3772
3921
|
function createColumn(size, offset) {
|
|
3773
|
-
var
|
|
3922
|
+
var text = textFor(settings.content_types[0]);
|
|
3774
3923
|
var column = $('<div class="column"/>')
|
|
3775
|
-
.append(createDefaultContentWrapper().html(
|
|
3924
|
+
.append(createDefaultContentWrapper().html(text ? text.initialContent : ''))
|
|
3776
3925
|
;
|
|
3777
3926
|
|
|
3778
3927
|
// The tier being edited, or the base class in the all view
|
|
@@ -3820,7 +3969,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3820
3969
|
// A container sits in the column beside the content
|
|
3821
3970
|
// areas, not inside one, so it ends a run of loose
|
|
3822
3971
|
// content rather than joining it
|
|
3823
|
-
if (child.is('.row, .ge-content, [data-ge-container]')) {
|
|
3972
|
+
if (child.is('.row, .ge-content, .ge-text-block, [data-ge-container]')) {
|
|
3824
3973
|
contents = doWrap(contents);
|
|
3825
3974
|
} else {
|
|
3826
3975
|
contents = contents.add(child);
|
|
@@ -3847,11 +3996,12 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3847
3996
|
}
|
|
3848
3997
|
|
|
3849
3998
|
/**
|
|
3850
|
-
* A content area for the first
|
|
3851
|
-
* it is a content area and nothing more: there is no
|
|
3999
|
+
* A content area for a content type, the first one by default. With
|
|
4000
|
+
* no content types it is a content area and nothing more: there is no
|
|
4001
|
+
* type to name.
|
|
3852
4002
|
*/
|
|
3853
|
-
function createDefaultContentWrapper() {
|
|
3854
|
-
|
|
4003
|
+
function createDefaultContentWrapper(type) {
|
|
4004
|
+
type = type || settings.content_types[0];
|
|
3855
4005
|
var contentArea = $('<div class="ge-content"/>');
|
|
3856
4006
|
|
|
3857
4007
|
if (type) {
|
|
@@ -3861,6 +4011,236 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3861
4011
|
return contentArea;
|
|
3862
4012
|
}
|
|
3863
4013
|
|
|
4014
|
+
/* --------------------------------------------------------------
|
|
4015
|
+
* Text blocks: a content area as a block of its own, with a drawer.
|
|
4016
|
+
*
|
|
4017
|
+
* The drawer cannot go inside the content area, as every other
|
|
4018
|
+
* block's does: what is inside is the text editor's, which would make
|
|
4019
|
+
* the drawer editable text, copy it into its own document or take it
|
|
4020
|
+
* away on an undo. So while editing, each content area in a column
|
|
4021
|
+
* sits in a .ge-text-block beside its drawer, and that wrapper is the
|
|
4022
|
+
* block the column moves. deinit takes it off again: it is never part
|
|
4023
|
+
* of the markup.
|
|
4024
|
+
* -------------------------------------------------------------- */
|
|
4025
|
+
|
|
4026
|
+
/** The text editors this editor offers, in content_types order: the ones loaded. */
|
|
4027
|
+
function offeredTexts() {
|
|
4028
|
+
return settings.content_types.filter(function(type) { return !!TEXTS[type]; });
|
|
4029
|
+
}
|
|
4030
|
+
|
|
4031
|
+
function wrapTexts() {
|
|
4032
|
+
canvas.find('.column > .ge-content').each(function() {
|
|
4033
|
+
$(this).wrap('<div class="ge-text-block" />');
|
|
4034
|
+
});
|
|
4035
|
+
|
|
4036
|
+
canvas.find('.ge-text-block').each(function() {
|
|
4037
|
+
var textBlock = $(this);
|
|
4038
|
+
if (!textBlock.children('.ge-tools-drawer').length) { createTextControls(textBlock); }
|
|
4039
|
+
});
|
|
4040
|
+
}
|
|
4041
|
+
|
|
4042
|
+
/** After the drawers are gone, so a wrapper holds its content area and nothing else. */
|
|
4043
|
+
function unwrapTexts() {
|
|
4044
|
+
canvas.find('.ge-text-block').each(function() {
|
|
4045
|
+
$(this).replaceWith($(this).contents());
|
|
4046
|
+
});
|
|
4047
|
+
}
|
|
4048
|
+
|
|
4049
|
+
function createTextControls(textBlock) {
|
|
4050
|
+
var block = textBlock.children('.ge-content');
|
|
4051
|
+
var type = block.attr('data-ge-content-type');
|
|
4052
|
+
var drawer = $('<div class="ge-tools-drawer ge-text-drawer" />').prependTo(textBlock);
|
|
4053
|
+
|
|
4054
|
+
createMoveTool(drawer);
|
|
4055
|
+
|
|
4056
|
+
// Which editor edits it, or that none can: a type whose plugin is
|
|
4057
|
+
// not loaded is still a block to move and delete, but not to edit
|
|
4058
|
+
if (type && TEXTS[type]) {
|
|
4059
|
+
createTool(drawer, t('tool.text_info', { editor: textLabel(type) }), 'ge-text-info', 'bi bi-fonts');
|
|
4060
|
+
} else if (type) {
|
|
4061
|
+
createTool(drawer, t('text.no_editor', { type: type }), 'ge-text-info ge-text-missing',
|
|
4062
|
+
'bi bi-exclamation-triangle');
|
|
4063
|
+
}
|
|
4064
|
+
|
|
4065
|
+
addSettingsTool(drawer, block, settings.text_classes);
|
|
4066
|
+
|
|
4067
|
+
settings.text_tools.forEach(function(hostTool) {
|
|
4068
|
+
createTool(drawer, hostTool.title || '', hostTool.className || '',
|
|
4069
|
+
hostTool.iconClass || 'bi bi-wrench', hostTool.on);
|
|
4070
|
+
});
|
|
4071
|
+
|
|
4072
|
+
createTool(drawer, t('tool.delete_text'), 'ge-delete-text', 'bi bi-trash', function() {
|
|
4073
|
+
deleteNode('text', block, t('confirm.delete_text'), function(removed) {
|
|
4074
|
+
// Its editor lets go first: removed while open, it would
|
|
4075
|
+
// be left behind, attached to nothing
|
|
4076
|
+
closeText(block);
|
|
4077
|
+
textBlock.slideUp(function() {
|
|
4078
|
+
textBlock.remove();
|
|
4079
|
+
removed();
|
|
4080
|
+
});
|
|
4081
|
+
});
|
|
4082
|
+
});
|
|
4083
|
+
}
|
|
4084
|
+
|
|
4085
|
+
/**
|
|
4086
|
+
* Close the text editor on one content area, and leave the content
|
|
4087
|
+
* area's own attributes as the host left them.
|
|
4088
|
+
*
|
|
4089
|
+
* An editor adds attributes of its own while it is open, and takes
|
|
4090
|
+
* them off again, more or less: CKEditor leaves aria-readonly behind.
|
|
4091
|
+
* tinyMCE does worse, and puts back every attribute as it was when it
|
|
4092
|
+
* opened, so an id, a class or a plugin's attribute given while it was
|
|
4093
|
+
* open is lost. So the attributes are read three times - before the
|
|
4094
|
+
* editor opens, once it has opened, and before it closes - and what
|
|
4095
|
+
* changed between the second and the third, the host's doing, is
|
|
4096
|
+
* played back onto the first, the host's markup. What the editor did
|
|
4097
|
+
* is left out either way.
|
|
4098
|
+
*/
|
|
4099
|
+
function closeText(block) {
|
|
4100
|
+
var text = textFor(block.attr('data-ge-content-type'));
|
|
4101
|
+
var before = block.data('ge-text-before');
|
|
4102
|
+
var open = before ? attributesOf(block[0]) : null;
|
|
4103
|
+
|
|
4104
|
+
// Every content area is told, as in 5.x: one whose editor never
|
|
4105
|
+
// started is the plugin's to ignore
|
|
4106
|
+
if (text) { text.stop(block); }
|
|
4107
|
+
|
|
4108
|
+
// After stop, not before: an editor that restores the class
|
|
4109
|
+
// attribute it snapshotted would put ge-rte-active back
|
|
4110
|
+
block.removeClass('ge-rte-active');
|
|
4111
|
+
|
|
4112
|
+
if (before) {
|
|
4113
|
+
restoreAttributes(block, before, block.data('ge-text-ready') || before, open);
|
|
4114
|
+
block.removeData('ge-text-before').removeData('ge-text-ready');
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
|
|
4118
|
+
/** What a text editor puts on a content area while it is open, whenever it likes. */
|
|
4119
|
+
var EDITOR_CLASS = /^(mce-|cke|note-)|^(active|ge-rte-active)$/;
|
|
4120
|
+
var EDITOR_ATTRIBUTE = /^(data-mce-|contenteditable$|spellcheck$)/;
|
|
4121
|
+
|
|
4122
|
+
function attributesOf(element) {
|
|
4123
|
+
var found = {};
|
|
4124
|
+
$.each($.makeArray(element.attributes), function(i, attribute) {
|
|
4125
|
+
found[attribute.name] = attribute.value;
|
|
4126
|
+
});
|
|
4127
|
+
return found;
|
|
4128
|
+
}
|
|
4129
|
+
|
|
4130
|
+
function classesIn(value) {
|
|
4131
|
+
return (value || '').split(/\s+/).filter(function(name) {
|
|
4132
|
+
return name !== '' && !EDITOR_CLASS.test(name);
|
|
4133
|
+
});
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
/** The host's markup, with what changed from `ready` to `open` played back onto it. */
|
|
4137
|
+
function restoreAttributes(block, before, ready, open) {
|
|
4138
|
+
var result = $.extend({}, before);
|
|
4139
|
+
|
|
4140
|
+
$.each($.extend({}, ready, open), function(name) {
|
|
4141
|
+
if (name === 'class' || EDITOR_ATTRIBUTE.test(name) || ready[name] === open[name]) { return; }
|
|
4142
|
+
|
|
4143
|
+
if (open[name] === undefined) {
|
|
4144
|
+
delete result[name];
|
|
4145
|
+
} else {
|
|
4146
|
+
result[name] = open[name];
|
|
4147
|
+
}
|
|
4148
|
+
});
|
|
4149
|
+
|
|
4150
|
+
var readyClasses = classesIn(ready['class']);
|
|
4151
|
+
var openClasses = classesIn(open['class']);
|
|
4152
|
+
var classes = classesIn(before['class']).filter(function(name) {
|
|
4153
|
+
return openClasses.indexOf(name) !== -1 || readyClasses.indexOf(name) === -1;
|
|
4154
|
+
});
|
|
4155
|
+
openClasses.forEach(function(name) {
|
|
4156
|
+
if (readyClasses.indexOf(name) === -1 && classes.indexOf(name) === -1) { classes.push(name); }
|
|
4157
|
+
});
|
|
4158
|
+
|
|
4159
|
+
// In the host's order, class where it was: getHtml should not
|
|
4160
|
+
// shuffle a node's attributes because it was edited
|
|
4161
|
+
if (classes.length) { result['class'] = classes.join(' '); } else { delete result['class']; }
|
|
4162
|
+
|
|
4163
|
+
$.each(attributesOf(block[0]), function(name) { block.removeAttr(name); });
|
|
4164
|
+
$.each(result, function(name, value) { block.attr(name, value); });
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
/** What a text editor is called: its plugin's label, or its type. */
|
|
4168
|
+
function textLabel(type) {
|
|
4169
|
+
var text = TEXTS[type];
|
|
4170
|
+
return text && text.labelKey ? t(text.labelKey) : type;
|
|
4171
|
+
}
|
|
4172
|
+
|
|
4173
|
+
/** A detached content area of `type`, holding `content` or the editor's initial content. */
|
|
4174
|
+
function makeText(type, content) {
|
|
4175
|
+
var text = TEXTS[type];
|
|
4176
|
+
return createDefaultContentWrapper(type)
|
|
4177
|
+
.html(content !== undefined ? content : (text && text.initialContent) || '');
|
|
4178
|
+
}
|
|
4179
|
+
|
|
4180
|
+
/** A text block at the end of a column, through the add events. */
|
|
4181
|
+
function addTextTo(col, type) {
|
|
4182
|
+
var block = makeText(type);
|
|
4183
|
+
|
|
4184
|
+
return addNode('text', block, function() {
|
|
4185
|
+
col.append(block);
|
|
4186
|
+
}, { parent: col, source: 'tool' });
|
|
4187
|
+
}
|
|
4188
|
+
|
|
4189
|
+
/**
|
|
4190
|
+
* createText(type?, options?): a content area of a text editor's,
|
|
4191
|
+
* detached unless a placement is given. The type is the first one
|
|
4192
|
+
* offered by default. `options.content` is its html.
|
|
4193
|
+
*/
|
|
4194
|
+
function apiCreateText(type, options) {
|
|
4195
|
+
if (type && typeof type === 'object') {
|
|
4196
|
+
options = type;
|
|
4197
|
+
type = undefined;
|
|
4198
|
+
}
|
|
4199
|
+
options = options || {};
|
|
4200
|
+
type = type || offeredTexts()[0];
|
|
4201
|
+
|
|
4202
|
+
if (!type || !TEXTS[type]) {
|
|
4203
|
+
warnOnceHere('createText:' + type, 'createText: no text editor "' + type + '" is loaded; ' +
|
|
4204
|
+
'load its plugin and name it in content_types');
|
|
4205
|
+
return null;
|
|
4206
|
+
}
|
|
4207
|
+
|
|
4208
|
+
return place(makeText(type, options.content), 'text', options);
|
|
4209
|
+
}
|
|
4210
|
+
|
|
4211
|
+
/**
|
|
4212
|
+
* The text editors offered, as a strip under the add text tool, when
|
|
4213
|
+
* there is more than one to choose from.
|
|
4214
|
+
*/
|
|
4215
|
+
function openTextPicker(tool, col) {
|
|
4216
|
+
closeSizePicker();
|
|
4217
|
+
|
|
4218
|
+
tool.closest('.ge-tools-drawer').addClass('ge-picker-open');
|
|
4219
|
+
sizePicker = $('<div class="ge-size-picker ge-text-picker" />').appendTo(tool);
|
|
4220
|
+
|
|
4221
|
+
offeredTexts().forEach(function(type) {
|
|
4222
|
+
$('<a class="ge-size ge-size-flex" />')
|
|
4223
|
+
.attr('data-ge-content-type', type)
|
|
4224
|
+
.attr('title', t('tool.add_text_type', { editor: textLabel(type) }))
|
|
4225
|
+
.text(textLabel(type))
|
|
4226
|
+
.on('click', function(e) {
|
|
4227
|
+
e.preventDefault();
|
|
4228
|
+
e.stopPropagation();
|
|
4229
|
+
|
|
4230
|
+
closeSizePicker();
|
|
4231
|
+
addTextTo(col, type);
|
|
4232
|
+
})
|
|
4233
|
+
.appendTo(sizePicker)
|
|
4234
|
+
;
|
|
4235
|
+
});
|
|
4236
|
+
|
|
4237
|
+
tool.one('mouseleave', function() {
|
|
4238
|
+
window.setTimeout(function() {
|
|
4239
|
+
if (sizePicker && !sizePicker.is(':hover')) { closeSizePicker(); }
|
|
4240
|
+
}, 400);
|
|
4241
|
+
});
|
|
4242
|
+
}
|
|
4243
|
+
|
|
3864
4244
|
/**
|
|
3865
4245
|
* Constrain the canvas to the view's preview width and make that
|
|
3866
4246
|
* tier's classes the effective ones. The all view constrains nothing:
|
|
@@ -3915,8 +4295,21 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3915
4295
|
return curView;
|
|
3916
4296
|
}
|
|
3917
4297
|
|
|
3918
|
-
|
|
3919
|
-
|
|
4298
|
+
/** The text editor plugin for a content type, or null. */
|
|
4299
|
+
function textFor(type) {
|
|
4300
|
+
return TEXTS[type] || null;
|
|
4301
|
+
}
|
|
4302
|
+
|
|
4303
|
+
/**
|
|
4304
|
+
* An integration written to 5.x's contract - init and deinit over a
|
|
4305
|
+
* set of content areas - as a text editor plugin.
|
|
4306
|
+
*/
|
|
4307
|
+
function legacyText(rte) {
|
|
4308
|
+
return {
|
|
4309
|
+
initialContent: rte.initialContent,
|
|
4310
|
+
start: function(block) { rte.init(settings, block); },
|
|
4311
|
+
stop: function(block) { rte.deinit(settings, block); },
|
|
4312
|
+
};
|
|
3920
4313
|
}
|
|
3921
4314
|
|
|
3922
4315
|
/**
|
|
@@ -3938,6 +4331,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3938
4331
|
getView: getView,
|
|
3939
4332
|
createRow: apiCreateRow,
|
|
3940
4333
|
createColumn: apiCreateColumn,
|
|
4334
|
+
createText: apiCreateText,
|
|
3941
4335
|
createElement: function(content, options) {
|
|
3942
4336
|
if (!featureMethods.createElement) {
|
|
3943
4337
|
warnOnceHere('plugin:elements', 'createElement needs the elements plugin: ' +
|
|
@@ -3991,6 +4385,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3991
4385
|
|
|
3992
4386
|
};
|
|
3993
4387
|
|
|
4388
|
+
/**
|
|
4389
|
+
* Text editor plugins: tinyMCE, CKEditor, summernote, and whatever a host
|
|
4390
|
+
* writes. A factory registered under the content type it edits, called once
|
|
4391
|
+
* per editor with the handle described in docs/plugins.md, returning
|
|
4392
|
+
* { labelKey, initialContent, available, missingKey, start, stop }.
|
|
4393
|
+
*/
|
|
4394
|
+
$.fn.gridEditor.texts = {};
|
|
4395
|
+
|
|
4396
|
+
/** 5.x's registry of text editors, adapted with a warning. Removed in 7.0. */
|
|
3994
4397
|
$.fn.gridEditor.RTEs = {};
|
|
3995
4398
|
|
|
3996
4399
|
/**
|
|
@@ -4046,6 +4449,13 @@ $.fn.gridEditor.locales = {
|
|
|
4046
4449
|
'tool.move': 'Move',
|
|
4047
4450
|
'tool.settings': 'Settings',
|
|
4048
4451
|
'tool.add_row': 'Add row',
|
|
4452
|
+
'tool.add_text': 'Add text',
|
|
4453
|
+
'tool.add_text_type': 'Add {editor} text',
|
|
4454
|
+
'tool.text_info': 'Text: {editor}',
|
|
4455
|
+
'tool.delete_text': 'Remove text',
|
|
4456
|
+
'text.add': 'Text',
|
|
4457
|
+
'text.add_type': 'Text ({editor})',
|
|
4458
|
+
'text.no_editor': 'No text editor "{type}" is loaded: this text can be moved and deleted, not edited',
|
|
4049
4459
|
'tool.add_column': 'Add column\n(hold to choose the width)',
|
|
4050
4460
|
'tool.column_size': '{size} of 12',
|
|
4051
4461
|
'tool.column_equal': 'Equal: shares what the row has left',
|
|
@@ -4073,6 +4483,7 @@ $.fn.gridEditor.locales = {
|
|
|
4073
4483
|
'confirm.cancel': 'Cancel',
|
|
4074
4484
|
'confirm.delete_row': 'Delete row?',
|
|
4075
4485
|
'confirm.delete_column': 'Delete column?',
|
|
4486
|
+
'confirm.delete_text': 'Delete this text?',
|
|
4076
4487
|
'confirm.delete_container': 'Delete this container and everything in it?',
|
|
4077
4488
|
'view.all': 'All sizes',
|
|
4078
4489
|
'view.xs': 'Phone',
|