@themarioga/grid-editor 5.2.0 → 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/CHANGELOG.md +33 -0
- package/README.md +29 -1
- 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 +395 -26
- 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 +5 -0
- package/dist/plugins/grideditor.ckeditor.min.js +1 -1
- package/dist/plugins/grideditor.ckeditor.min.js.map +1 -1
- 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 +5 -0
- package/dist/plugins/grideditor.summernote.min.js +1 -1
- package/dist/plugins/grideditor.summernote.min.js.map +1 -1
- 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 +5 -0
- package/dist/plugins/grideditor.tinymce.min.js +1 -1
- package/dist/plugins/grideditor.tinymce.min.js.map +1 -1
- 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 +7 -2
- package/example/attributes.html +2 -2
- package/package.json +1 -1
- package/src/js/jquery.grideditor.js +380 -26
- package/src/js/locales/grideditor.es.js +11 -0
- package/src/js/plugins/grideditor.ckeditor.js +5 -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 +5 -0
- package/src/js/plugins/grideditor.textalign.js +1 -1
- package/src/js/plugins/grideditor.tinymce.js +5 -0
- package/src/js/plugins/grideditor.visibility.js +3 -3
- package/src/less/grideditor.less +34 -0
|
@@ -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,7 +1467,9 @@ $.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
|
-
|
|
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');
|
|
1408
1473
|
var text = textFor(type);
|
|
1409
1474
|
if (!text) { return; }
|
|
1410
1475
|
|
|
@@ -1421,6 +1486,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1421
1486
|
'.js after the editor');
|
|
1422
1487
|
}
|
|
1423
1488
|
|
|
1489
|
+
$(this).data('ge-text-before', attributesOf(this)).removeData('ge-text-ready');
|
|
1424
1490
|
$(this).addClass('ge-rte-active');
|
|
1425
1491
|
text.start($(this));
|
|
1426
1492
|
}
|
|
@@ -1436,6 +1502,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1436
1502
|
canvas.toggleClass('ge-drag-drawer', settings.drag_handle === 'drawer');
|
|
1437
1503
|
addAllColClasses();
|
|
1438
1504
|
wrapContent();
|
|
1505
|
+
wrapTexts();
|
|
1439
1506
|
createRowControls();
|
|
1440
1507
|
createColControls();
|
|
1441
1508
|
markContainers();
|
|
@@ -1448,21 +1515,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1448
1515
|
|
|
1449
1516
|
function deinit() {
|
|
1450
1517
|
canvas.removeClass('ge-editing ge-drag-drawer ge-dropping');
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
var text = textFor(content.data('ge-content-type'));
|
|
1454
|
-
if (text) {
|
|
1455
|
-
text.stop(content);
|
|
1456
|
-
}
|
|
1457
|
-
// Cleared after rte.deinit, not before: an editor can restore the
|
|
1458
|
-
// class attribute it snapshotted when it was created, which would
|
|
1459
|
-
// leave ge-rte-active in place and make initRTE ignore every later
|
|
1460
|
-
// click on this content area.
|
|
1461
|
-
content.removeClass('ge-rte-active');
|
|
1518
|
+
canvas.find('.ge-content').each(function() {
|
|
1519
|
+
closeText($(this));
|
|
1462
1520
|
});
|
|
1463
1521
|
closeSizePicker();
|
|
1464
1522
|
hideDropMarker();
|
|
1465
1523
|
canvas.find('.ge-tools-drawer').remove();
|
|
1524
|
+
unwrapTexts();
|
|
1466
1525
|
plugins('onDeinit');
|
|
1467
1526
|
// After the rich text editors have let go of their content areas:
|
|
1468
1527
|
// one that rebuilt its area's DOM brought the preview styles back
|
|
@@ -1665,6 +1724,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1665
1724
|
// A text editor has rewritten a content area, so whatever the
|
|
1666
1725
|
// editor and its plugins had put in there goes back in
|
|
1667
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
|
+
},
|
|
1668
1734
|
toolbarItems: function(name) {
|
|
1669
1735
|
return mainControls
|
|
1670
1736
|
? mainControls.find('[data-ge-toolbar="feature"][data-ge-feature="' + name + '"]')
|
|
@@ -1691,7 +1757,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1691
1757
|
* -------------------------------------------------------------- */
|
|
1692
1758
|
|
|
1693
1759
|
var FAMILIES = {}; // Every family the utility plugins declare, by name
|
|
1694
|
-
var UTILITY_NODES = '.row, .column, .ge-element, [data-ge-container]';
|
|
1760
|
+
var UTILITY_NODES = '.row, .column, .ge-content, .ge-element, [data-ge-container]';
|
|
1695
1761
|
var PREVIEW_ATTR = 'data-ge-preview';
|
|
1696
1762
|
var utilitiesOpen = false; // Whether the panels show their Responsive section unfolded
|
|
1697
1763
|
|
|
@@ -2479,6 +2545,14 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2479
2545
|
function attachSizePicker(tool, row) {
|
|
2480
2546
|
if (!settings.add_column.picker) { return; }
|
|
2481
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) {
|
|
2482
2556
|
var timer = null;
|
|
2483
2557
|
|
|
2484
2558
|
var cancel = function() {
|
|
@@ -2491,7 +2565,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2491
2565
|
|
|
2492
2566
|
timer = window.setTimeout(function() {
|
|
2493
2567
|
timer = null;
|
|
2494
|
-
|
|
2568
|
+
open();
|
|
2495
2569
|
}, settings.add_column.delay);
|
|
2496
2570
|
});
|
|
2497
2571
|
|
|
@@ -2641,6 +2715,22 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2641
2715
|
});
|
|
2642
2716
|
});
|
|
2643
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
|
+
|
|
2644
2734
|
createTool(drawer, t('tool.add_row'), 'ge-add-row', 'bi bi-plus-circle', function() {
|
|
2645
2735
|
// An empty row: the columns in it are the next decision,
|
|
2646
2736
|
// and its drawer's add column tool is where that is made
|
|
@@ -2755,7 +2845,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2755
2845
|
}
|
|
2756
2846
|
|
|
2757
2847
|
function createTool(drawer, title, className, iconClass, eventHandlers) {
|
|
2758
|
-
|
|
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))
|
|
2759
2855
|
.appendTo(drawer)
|
|
2760
2856
|
;
|
|
2761
2857
|
if (typeof eventHandlers == 'function') {
|
|
@@ -3189,11 +3285,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3189
3285
|
|
|
3190
3286
|
/**
|
|
3191
3287
|
* What the canvas, the columns and the plugins' regions move: rows,
|
|
3192
|
-
*
|
|
3193
|
-
* 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.
|
|
3194
3291
|
*/
|
|
3195
3292
|
function blockSelector() {
|
|
3196
|
-
return ['.row', '.ge-content', '[data-ge-container]']
|
|
3293
|
+
return ['.row', '.ge-text-block', '.ge-content', '[data-ge-container]']
|
|
3294
|
+
.concat(pluginHooks('blocks')).join(', ');
|
|
3197
3295
|
}
|
|
3198
3296
|
|
|
3199
3297
|
/** A selector every feature plugin may contribute to, under one name. */
|
|
@@ -3359,12 +3457,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3359
3457
|
function sortStart(e) {
|
|
3360
3458
|
var node = $(e.item);
|
|
3361
3459
|
var from = positionOf(node);
|
|
3460
|
+
var subject = moveSubject(node);
|
|
3362
3461
|
|
|
3363
3462
|
node.data('ge-move-from', from);
|
|
3364
3463
|
node.removeData('ge-move-canceled');
|
|
3365
3464
|
|
|
3366
3465
|
operate(function() {
|
|
3367
|
-
var moving = emit('before-move', payloadFor(
|
|
3466
|
+
var moving = emit('before-move', payloadFor(subject.kind, subject.node, {
|
|
3368
3467
|
parent: from.parent,
|
|
3369
3468
|
source: 'dragdrop',
|
|
3370
3469
|
from: from,
|
|
@@ -3374,6 +3473,19 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3374
3473
|
});
|
|
3375
3474
|
}
|
|
3376
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
|
+
|
|
3377
3489
|
/** Put a node back where a refused drag found it. */
|
|
3378
3490
|
function putBack(node, from) {
|
|
3379
3491
|
var siblings = from.parent.children().not('.ge-tools-drawer').not(node);
|
|
@@ -3411,8 +3523,10 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3411
3523
|
// No init() here, unlike an add: the node brought its drawer
|
|
3412
3524
|
// with it, and the drag is still being finished, so this is the
|
|
3413
3525
|
// wrong moment to rebuild the lists it is using.
|
|
3526
|
+
var subject = moveSubject(node);
|
|
3527
|
+
|
|
3414
3528
|
operate(function() {
|
|
3415
|
-
emit('after-move', payloadFor(
|
|
3529
|
+
emit('after-move', payloadFor(subject.kind, subject.node, {
|
|
3416
3530
|
parent: to.parent,
|
|
3417
3531
|
source: 'dragdrop',
|
|
3418
3532
|
from: from,
|
|
@@ -3855,7 +3969,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3855
3969
|
// A container sits in the column beside the content
|
|
3856
3970
|
// areas, not inside one, so it ends a run of loose
|
|
3857
3971
|
// content rather than joining it
|
|
3858
|
-
if (child.is('.row, .ge-content, [data-ge-container]')) {
|
|
3972
|
+
if (child.is('.row, .ge-content, .ge-text-block, [data-ge-container]')) {
|
|
3859
3973
|
contents = doWrap(contents);
|
|
3860
3974
|
} else {
|
|
3861
3975
|
contents = contents.add(child);
|
|
@@ -3882,11 +3996,12 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3882
3996
|
}
|
|
3883
3997
|
|
|
3884
3998
|
/**
|
|
3885
|
-
* A content area for the first
|
|
3886
|
-
* 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.
|
|
3887
4002
|
*/
|
|
3888
|
-
function createDefaultContentWrapper() {
|
|
3889
|
-
|
|
4003
|
+
function createDefaultContentWrapper(type) {
|
|
4004
|
+
type = type || settings.content_types[0];
|
|
3890
4005
|
var contentArea = $('<div class="ge-content"/>');
|
|
3891
4006
|
|
|
3892
4007
|
if (type) {
|
|
@@ -3896,6 +4011,236 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3896
4011
|
return contentArea;
|
|
3897
4012
|
}
|
|
3898
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
|
+
|
|
3899
4244
|
/**
|
|
3900
4245
|
* Constrain the canvas to the view's preview width and make that
|
|
3901
4246
|
* tier's classes the effective ones. The all view constrains nothing:
|
|
@@ -3986,6 +4331,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
3986
4331
|
getView: getView,
|
|
3987
4332
|
createRow: apiCreateRow,
|
|
3988
4333
|
createColumn: apiCreateColumn,
|
|
4334
|
+
createText: apiCreateText,
|
|
3989
4335
|
createElement: function(content, options) {
|
|
3990
4336
|
if (!featureMethods.createElement) {
|
|
3991
4337
|
warnOnceHere('plugin:elements', 'createElement needs the elements plugin: ' +
|
|
@@ -4103,6 +4449,13 @@ $.fn.gridEditor.locales = {
|
|
|
4103
4449
|
'tool.move': 'Move',
|
|
4104
4450
|
'tool.settings': 'Settings',
|
|
4105
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',
|
|
4106
4459
|
'tool.add_column': 'Add column\n(hold to choose the width)',
|
|
4107
4460
|
'tool.column_size': '{size} of 12',
|
|
4108
4461
|
'tool.column_equal': 'Equal: shares what the row has left',
|
|
@@ -4130,6 +4483,7 @@ $.fn.gridEditor.locales = {
|
|
|
4130
4483
|
'confirm.cancel': 'Cancel',
|
|
4131
4484
|
'confirm.delete_row': 'Delete row?',
|
|
4132
4485
|
'confirm.delete_column': 'Delete column?',
|
|
4486
|
+
'confirm.delete_text': 'Delete this text?',
|
|
4133
4487
|
'confirm.delete_container': 'Delete this container and everything in it?',
|
|
4134
4488
|
'view.all': 'All sizes',
|
|
4135
4489
|
'view.xs': 'Phone',
|
|
@@ -19,6 +19,16 @@
|
|
|
19
19
|
'tool.move': 'Mover',
|
|
20
20
|
'tool.settings': 'Configuración',
|
|
21
21
|
'tool.add_row': 'Añadir fila',
|
|
22
|
+
'tool.add_text': 'Añadir texto',
|
|
23
|
+
'tool.add_text_type': 'Añadir texto con {editor}',
|
|
24
|
+
'tool.text_info': 'Texto: {editor}',
|
|
25
|
+
'tool.delete_text': 'Quitar texto',
|
|
26
|
+
'text.add': 'Texto',
|
|
27
|
+
'text.add_type': 'Texto ({editor})',
|
|
28
|
+
'text.no_editor': 'No hay cargado ningún editor de texto "{type}": este texto se puede mover y borrar, pero no editar',
|
|
29
|
+
'text.tinymce': 'tinyMCE',
|
|
30
|
+
'text.ckeditor': 'CKEditor',
|
|
31
|
+
'text.summernote': 'Summernote',
|
|
22
32
|
'tool.add_column': 'Añadir columna\n(mantén pulsado para elegir el ancho)',
|
|
23
33
|
'tool.column_size': '{size} de 12',
|
|
24
34
|
'tool.column_equal': 'Igual: reparte lo que queda de la fila',
|
|
@@ -61,6 +71,7 @@
|
|
|
61
71
|
'confirm.cancel': 'Cancelar',
|
|
62
72
|
'confirm.delete_row': '¿Seguro que quieres borrar esta fila?',
|
|
63
73
|
'confirm.delete_column': '¿Seguro que quieres borrar esta columna?',
|
|
74
|
+
'confirm.delete_text': '¿Seguro que quieres borrar este texto?',
|
|
64
75
|
'confirm.delete_element': '¿Seguro que quieres borrar este elemento?',
|
|
65
76
|
'confirm.delete_container': '¿Seguro que quieres borrar este contenedor y todo su contenido?',
|
|
66
77
|
'confirm.delete_tab': '¿Seguro que quieres borrar esta pestaña y todo su contenido?',
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
*/
|
|
15
15
|
(function($) {
|
|
16
16
|
|
|
17
|
+
$.extend($.fn.gridEditor.locales.en, {
|
|
18
|
+
'text.ckeditor': 'CKEditor',
|
|
19
|
+
});
|
|
20
|
+
|
|
17
21
|
var INITIAL_CONTENT = '<p>Lorem initius... </p>';
|
|
18
22
|
|
|
19
23
|
$.fn.gridEditor.texts.ckeditor = function(ge) {
|
|
@@ -36,6 +40,7 @@
|
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
return {
|
|
43
|
+
labelKey: 'text.ckeditor',
|
|
39
44
|
initialContent: INITIAL_CONTENT,
|
|
40
45
|
missingKey: 'error.ckeditor_missing',
|
|
41
46
|
|