@themarioga/grid-editor 6.0.0-beta.3 → 6.0.0-beta.5
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 +3 -1
- package/CHANGELOG.md +74 -1
- package/README.md +71 -5
- package/UPGRADING.md +36 -7
- package/dist/grideditor.css +60 -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 +353 -402
- package/dist/jquery.grideditor.min.js +1 -1
- package/dist/jquery.grideditor.min.js.map +1 -1
- package/dist/locales/grideditor.es.js +7 -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 +397 -3
- package/dist/plugins/grideditor.ckeditor.min.js +1 -1
- package/dist/plugins/grideditor.ckeditor.min.js.map +1 -1
- package/dist/plugins/grideditor.codemirror-inline.js +190 -0
- package/dist/plugins/grideditor.codemirror-inline.min.js +2 -0
- package/dist/plugins/grideditor.codemirror-inline.min.js.map +1 -0
- package/dist/plugins/grideditor.codemirror.js +79 -0
- package/dist/plugins/grideditor.codemirror.min.js +2 -0
- package/dist/plugins/grideditor.codemirror.min.js.map +1 -0
- package/dist/plugins/grideditor.summernote.js +397 -3
- package/dist/plugins/grideditor.summernote.min.js +1 -1
- package/dist/plugins/grideditor.summernote.min.js.map +1 -1
- package/dist/plugins/grideditor.tinymce.js +397 -3
- 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 +1 -1
- package/dist/plugins/grideditor.visibility.min.js +1 -1
- package/dist/plugins/grideditor.visibility.min.js.map +1 -1
- package/docs/events.md +17 -2
- package/docs/locale-keys.md +25 -3
- package/docs/plugins.md +124 -46
- package/example/codemirror.html +103 -0
- package/example/custom_editor.html +193 -0
- package/example/index.html +16 -0
- package/example/wrap_content.html +5 -3
- package/package.json +1 -1
- package/src/js/jquery.grideditor.js +353 -402
- package/src/js/locales/grideditor.es.js +7 -0
- package/src/js/plugins/grideditor.ckeditor.js +7 -3
- package/src/js/plugins/grideditor.codemirror-inline.js +190 -0
- package/src/js/plugins/grideditor.codemirror.js +79 -0
- package/src/js/plugins/grideditor.summernote.js +7 -3
- package/src/js/plugins/grideditor.tinymce.js +7 -3
- package/src/js/plugins/grideditor.visibility.js +1 -1
- package/src/js/text/grideditor.text.js +389 -0
- package/src/less/grideditor.less +71 -0
|
@@ -339,8 +339,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
339
339
|
'toolbar_drag' : 'auto', // Drag the toolbar's buttons onto the canvas. 'auto' follows drag_handle
|
|
340
340
|
'element_tools' : [], // Host tools on element drawers, same shape as row_tools
|
|
341
341
|
'element_classes' : [], // Preset class toggles on an element's settings panel
|
|
342
|
-
|
|
343
|
-
|
|
342
|
+
// content_types, text_tools and text_classes are the text editor
|
|
343
|
+
// plugins' settings since 6.0: see grideditor.text.js
|
|
344
344
|
'container_classes' : [], // The same, on a container's panel
|
|
345
345
|
'pane_classes' : [], // And on a tab's or an accordion item's
|
|
346
346
|
'container_tools' : [], // Host tools on container drawers
|
|
@@ -351,7 +351,6 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
351
351
|
'utilities' : {}, // Options for the utility plugins, by plugin name
|
|
352
352
|
'elements' : NESTED_SETTINGS.elements, // Element level controls, below the column
|
|
353
353
|
'custom_filter' : '',
|
|
354
|
-
'content_types' : ['tinymce'],
|
|
355
354
|
'valid_col_sizes' : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 'equal', 'auto'],
|
|
356
355
|
'valid_col_offsets' : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
|
|
357
356
|
'add_column' : NESTED_SETTINGS.add_column, // The add column tool
|
|
@@ -359,6 +358,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
359
358
|
'default_view' : ALL_VIEW,
|
|
360
359
|
'resize' : NESTED_SETTINGS.resize, // Resizing a column by dragging its edge
|
|
361
360
|
'source_textarea' : '',
|
|
361
|
+
'edit_source' : true, // The toolbar's button to edit the canvas as html
|
|
362
362
|
'locale' : 'en', // Code of a locale in $.fn.gridEditor.locales
|
|
363
363
|
'locale_strings' : {}, // Overrides for individual keys
|
|
364
364
|
'callbacks' : {}, // before_*/after_* functions, the events by another route
|
|
@@ -398,6 +398,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
398
398
|
var curView = settings.default_view; // Breakpoint key, or 'all'
|
|
399
399
|
var confirmDialog = null; // The delete confirmation, built when first needed
|
|
400
400
|
var sizePicker = null; // The open column size picker, if there is one
|
|
401
|
+
var sourceOpen = false; // Whether the canvas is being edited as html
|
|
401
402
|
var dropMarker = null; // The line showing where a dragged toolbar button would land
|
|
402
403
|
var warnedHere = {}; // Deprecations are worth saying once per instance, not once per call
|
|
403
404
|
var sortables = []; // Every list made sortable, so deinit destroys exactly those
|
|
@@ -836,19 +837,21 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
836
837
|
if (node.hasClass('row')) { return 'row'; }
|
|
837
838
|
if (node.hasClass('column')) { return 'column'; }
|
|
838
839
|
if (node.hasClass('ge-element')) { return 'element'; }
|
|
839
|
-
|
|
840
|
+
// A content area is a text when an editor's type is on it, and the
|
|
841
|
+
// host's plain content when there is none
|
|
842
|
+
if (node.hasClass('ge-text-block')) { node = node.children('.ge-content'); }
|
|
843
|
+
if (node.hasClass('ge-content')) { return node.attr('data-ge-content-type') ? 'text' : 'plain'; }
|
|
840
844
|
return 'node';
|
|
841
845
|
}
|
|
842
846
|
|
|
843
847
|
// Copy html to sourceElement if a source textarea is given
|
|
844
848
|
if (settings.source_textarea) {
|
|
845
849
|
var sourceHtml = $(settings.source_textarea).val();
|
|
850
|
+
// Html with no grid in it goes into a row and a full width column
|
|
851
|
+
// of its own, where init wraps it as the host's plain content
|
|
846
852
|
if (sourceHtml.length > 0 && $('<div>' + sourceHtml + '</div>').find('.row').addBack('.row').length == 0) {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
sourceColumn.find('.ge-content').html(sourceHtml);
|
|
850
|
-
sourceHtml = sourceColumn.html();
|
|
851
|
-
}
|
|
853
|
+
sourceHtml = '<div class="row"><div class="col-lg-12">' + sourceHtml + '</div></div>';
|
|
854
|
+
}
|
|
852
855
|
baseElem.html(sourceHtml);
|
|
853
856
|
}
|
|
854
857
|
|
|
@@ -871,8 +874,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
871
874
|
// Make controls fixed on scroll
|
|
872
875
|
$(window).on('scroll', onScroll);
|
|
873
876
|
|
|
874
|
-
/*
|
|
875
|
-
canvas.on('click', '.ge-content',
|
|
877
|
+
/* A click on the host's plain content offers to make it a text */
|
|
878
|
+
canvas.on('click', '.ge-content', onContentClick);
|
|
876
879
|
|
|
877
880
|
/* A trigger is often a link, and a link still navigates even
|
|
878
881
|
with its Bootstrap attributes suspended */
|
|
@@ -884,12 +887,6 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
884
887
|
// which costs the element drawers inside it. The integrations say
|
|
885
888
|
// when their editor is ready, and the drawers go back in.
|
|
886
889
|
canvas.on('ge-rte-ready', '.ge-content', function() {
|
|
887
|
-
// The first time only: an undo says ready again, and by then
|
|
888
|
-
// the host may have changed the content area itself
|
|
889
|
-
if ($(this).data('ge-text-before') && !$(this).data('ge-text-ready')) {
|
|
890
|
-
$(this).data('ge-text-ready', attributesOf(this));
|
|
891
|
-
}
|
|
892
|
-
|
|
893
890
|
plugins('onContentReady', $(this));
|
|
894
891
|
refreshPreviews($(this));
|
|
895
892
|
});
|
|
@@ -947,50 +944,16 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
947
944
|
|
|
948
945
|
addContainerGroup.appendTo(wrapper);
|
|
949
946
|
|
|
950
|
-
// A text block of each editor offered. Like a container, it goes
|
|
951
|
-
// into a row of its own when clicked, or where it is dropped
|
|
952
|
-
var texts = offeredTexts();
|
|
953
|
-
texts.forEach(function(type) {
|
|
954
|
-
var label = texts.length > 1 ? t('text.add_type', { editor: textLabel(type) }) : t('text.add');
|
|
955
|
-
|
|
956
|
-
$('<a class="btn btn-sm btn-primary ge-add-container ge-add-text-button" />')
|
|
957
|
-
.attr('title', label)
|
|
958
|
-
.attr('data-ge-toolbar', 'text')
|
|
959
|
-
.attr('data-ge-text', type)
|
|
960
|
-
.append('<i class="bi bi-plus"></i>')
|
|
961
|
-
.append($('<span />').text(label))
|
|
962
|
-
.on('click', function() {
|
|
963
|
-
var row = createRow();
|
|
964
|
-
var block = makeText(type);
|
|
965
|
-
createColumn(MAX_COL_SIZE).appendTo(row).find('> .ge-content').replaceWith(block);
|
|
966
|
-
|
|
967
|
-
var added = addNode('text', block, function() {
|
|
968
|
-
row.appendTo(canvas);
|
|
969
|
-
}, { parent: canvas, source: 'tool' });
|
|
970
|
-
|
|
971
|
-
if (added && row[0].scrollIntoView) {
|
|
972
|
-
row[0].scrollIntoView({behavior: 'smooth'});
|
|
973
|
-
}
|
|
974
|
-
})
|
|
975
|
-
.appendTo(addContainerGroup)
|
|
976
|
-
;
|
|
977
|
-
});
|
|
978
|
-
|
|
979
947
|
// A container starts in a row of its own, the way the add row
|
|
980
948
|
// buttons next to these ones do
|
|
981
949
|
$.each(CONTAINERS, function(type, definition) {
|
|
982
|
-
$('<a class="btn btn-sm btn-primary ge-add-container" />')
|
|
983
|
-
.attr('title', t(definition.labelKey))
|
|
950
|
+
labelButton($('<a class="btn btn-sm btn-primary ge-add-container" />'), t(definition.labelKey), definition.iconClass)
|
|
984
951
|
.attr('data-ge-toolbar', 'container')
|
|
985
952
|
.attr('data-ge-container-type', type)
|
|
986
|
-
.append('<i class="bi bi-plus"></i>')
|
|
987
|
-
.append($('<span />').text(t(definition.labelKey)))
|
|
988
953
|
.on('click', function() {
|
|
989
954
|
var row = createRow();
|
|
990
955
|
var column = createColumn(MAX_COL_SIZE).appendTo(row);
|
|
991
|
-
var container = definition.create({});
|
|
992
|
-
|
|
993
|
-
column.find('> .ge-content').replaceWith(container);
|
|
956
|
+
var container = definition.create({}).appendTo(column);
|
|
994
957
|
|
|
995
958
|
addNode(type, container, function() {
|
|
996
959
|
row.appendTo(canvas);
|
|
@@ -1042,27 +1005,23 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1042
1005
|
var btnGroup = $('<div class="btn-group pull-right"/>')
|
|
1043
1006
|
.appendTo(wrapper)
|
|
1044
1007
|
;
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
.val(canvas.html())
|
|
1057
|
-
.show()
|
|
1058
|
-
;
|
|
1059
|
-
canvas.hide();
|
|
1060
|
-
}
|
|
1008
|
+
if (settings.edit_source) {
|
|
1009
|
+
// Built again by setLocale, maybe with the source open
|
|
1010
|
+
var htmlButton = $('<button type="button" class="btn btn-sm btn-primary gm-edit-mode"><i class="bi bi-code-slash"></i></button>')
|
|
1011
|
+
.attr('title', t('tool.edit_source'))
|
|
1012
|
+
.toggleClass('active btn-danger', sourceOpen)
|
|
1013
|
+
.on('click', function() {
|
|
1014
|
+
if (sourceOpen) {
|
|
1015
|
+
closeSource();
|
|
1016
|
+
} else {
|
|
1017
|
+
openSource();
|
|
1018
|
+
}
|
|
1061
1019
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1020
|
+
htmlButton.toggleClass('active btn-danger', sourceOpen);
|
|
1021
|
+
})
|
|
1022
|
+
.appendTo(btnGroup)
|
|
1023
|
+
;
|
|
1024
|
+
}
|
|
1066
1025
|
var previewButton = $('<button type="button" class="btn btn-sm btn-primary gm-preview"><i class="bi bi-eye-fill"></i></button>')
|
|
1067
1026
|
.attr('title', t('tool.preview'))
|
|
1068
1027
|
.on('mouseenter', function() {
|
|
@@ -1093,25 +1052,49 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1093
1052
|
makeToolbarDraggable();
|
|
1094
1053
|
}
|
|
1095
1054
|
|
|
1096
|
-
/**
|
|
1055
|
+
/**
|
|
1056
|
+
* A toolbar button's face: the plugin's icon alone when it has one,
|
|
1057
|
+
* with its label as the title, or a plus and the label otherwise.
|
|
1058
|
+
*/
|
|
1059
|
+
function labelButton(button, label, iconClass) {
|
|
1060
|
+
button.attr('title', label);
|
|
1061
|
+
|
|
1062
|
+
if (iconClass) { return button.append($('<i />').addClass(iconClass)); }
|
|
1063
|
+
|
|
1064
|
+
return button
|
|
1065
|
+
.append('<i class="bi bi-plus"></i>')
|
|
1066
|
+
.append($('<span />').text(label))
|
|
1067
|
+
;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
/** A feature plugin's toolbar button. At the end it is always an icon, `bi bi-plus` if it has none of its own. */
|
|
1097
1071
|
function featureButton(name, item, index) {
|
|
1098
|
-
var
|
|
1099
|
-
|
|
1072
|
+
var iconClass = item.iconClass || (item.align === 'end' ? 'bi bi-plus' : null);
|
|
1073
|
+
// A label of its own, worked out when the toolbar is built - and
|
|
1074
|
+
// built again by setLocale - or the string its labelKey names
|
|
1075
|
+
var label = typeof item.label === 'function' ? item.label() : (item.label || t(item.labelKey));
|
|
1076
|
+
var button = labelButton($('<a class="btn btn-sm btn-primary ge-add-container ge-add-feature" />'), label, iconClass)
|
|
1077
|
+
.addClass(item.className || '')
|
|
1100
1078
|
.attr('data-ge-toolbar', 'feature')
|
|
1101
1079
|
.attr('data-ge-feature', name)
|
|
1102
1080
|
.attr('data-ge-item', index)
|
|
1103
|
-
.append($('<i />').addClass(item.iconClass || 'bi bi-plus'))
|
|
1104
1081
|
.on('click', function() {
|
|
1105
1082
|
var made = item.create();
|
|
1106
1083
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1084
|
+
// One that belongs in a column brings a row and a column
|
|
1085
|
+
// of its own, the way a container does
|
|
1086
|
+
var placed = item.inColumn ? inRowOfItsOwn(made) : made;
|
|
1087
|
+
|
|
1088
|
+
var added = addNode(item.kind, made, function() {
|
|
1089
|
+
placed.appendTo(canvas);
|
|
1109
1090
|
}, { parent: canvas, source: item.source || 'tool' });
|
|
1091
|
+
|
|
1092
|
+
if (added && item.inColumn && placed[0].scrollIntoView) {
|
|
1093
|
+
placed[0].scrollIntoView({behavior: 'smooth'});
|
|
1094
|
+
}
|
|
1110
1095
|
})
|
|
1111
1096
|
;
|
|
1112
1097
|
|
|
1113
|
-
if (item.align !== 'end') { button.append($('<span />').text(t(item.labelKey))); }
|
|
1114
|
-
|
|
1115
1098
|
return button;
|
|
1116
1099
|
}
|
|
1117
1100
|
|
|
@@ -1250,8 +1233,13 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1250
1233
|
function insertFeatureFromToolbar(button, where) {
|
|
1251
1234
|
var item = FEATURES[button.attr('data-ge-feature')].toolbar[parseInt(button.attr('data-ge-item'), 10)];
|
|
1252
1235
|
var made = item.create();
|
|
1236
|
+
var placed = made;
|
|
1253
1237
|
|
|
1254
|
-
if (
|
|
1238
|
+
if (item.inColumn) {
|
|
1239
|
+
// Where it lands in a column, or in a row and a column of its
|
|
1240
|
+
// own anywhere else
|
|
1241
|
+
if (!where.region.is('.column')) { placed = inRowOfItsOwn(made); }
|
|
1242
|
+
} else if (!acceptsBlock(where.region, made)) {
|
|
1255
1243
|
var top = where.region.parentsUntil(canvas).addBack().first();
|
|
1256
1244
|
where = { region: canvas, before: top.length ? top.next() : null };
|
|
1257
1245
|
if (where.before && !where.before.length) { where.before = null; }
|
|
@@ -1259,33 +1247,19 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1259
1247
|
|
|
1260
1248
|
return addNode(item.kind, made, function() {
|
|
1261
1249
|
if (where.before) {
|
|
1262
|
-
|
|
1250
|
+
placed.insertBefore(where.before);
|
|
1263
1251
|
} else {
|
|
1264
|
-
|
|
1252
|
+
placed.appendTo(where.region);
|
|
1265
1253
|
}
|
|
1266
1254
|
}, { parent: where.region, source: item.source || 'dragdrop' });
|
|
1267
1255
|
}
|
|
1268
1256
|
|
|
1269
|
-
/**
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
function insertTextFromToolbar(button, where) {
|
|
1274
|
-
var made = makeText(button.attr('data-ge-text'));
|
|
1275
|
-
var placed = made;
|
|
1276
|
-
|
|
1277
|
-
if (!where.region.is('.column')) {
|
|
1278
|
-
placed = createRow();
|
|
1279
|
-
createColumn(MAX_COL_SIZE).appendTo(placed).find('> .ge-content').replaceWith(made);
|
|
1280
|
-
}
|
|
1257
|
+
/** A detached row with one full width column holding `node`. */
|
|
1258
|
+
function inRowOfItsOwn(node) {
|
|
1259
|
+
var row = createRow();
|
|
1260
|
+
createColumn(MAX_COL_SIZE).appendTo(row).append(node);
|
|
1281
1261
|
|
|
1282
|
-
return
|
|
1283
|
-
if (where.before) {
|
|
1284
|
-
placed.insertBefore(where.before);
|
|
1285
|
-
} else {
|
|
1286
|
-
placed.appendTo(where.region);
|
|
1287
|
-
}
|
|
1288
|
-
}, { parent: where.region, source: 'dragdrop' });
|
|
1262
|
+
return row;
|
|
1289
1263
|
}
|
|
1290
1264
|
|
|
1291
1265
|
/** A line where the block would go, following the pointer. */
|
|
@@ -1317,9 +1291,6 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1317
1291
|
if (button.attr('data-ge-toolbar') === 'feature') {
|
|
1318
1292
|
return insertFeatureFromToolbar(button, where);
|
|
1319
1293
|
}
|
|
1320
|
-
if (button.attr('data-ge-toolbar') === 'text') {
|
|
1321
|
-
return insertTextFromToolbar(button, where);
|
|
1322
|
-
}
|
|
1323
1294
|
|
|
1324
1295
|
var container = button.attr('data-ge-toolbar') === 'container';
|
|
1325
1296
|
var type = button.attr('data-ge-container-type');
|
|
@@ -1330,11 +1301,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1330
1301
|
// A container belongs in a column: dropped straight onto the
|
|
1331
1302
|
// canvas it brings a row and a column of its own
|
|
1332
1303
|
var placed = made;
|
|
1333
|
-
if (container && !where.region.is('.column')) {
|
|
1334
|
-
placed = createRow();
|
|
1335
|
-
createColumn(MAX_COL_SIZE).appendTo(placed)
|
|
1336
|
-
.find('> .ge-content').replaceWith(made);
|
|
1337
|
-
}
|
|
1304
|
+
if (container && !where.region.is('.column')) { placed = inRowOfItsOwn(made); }
|
|
1338
1305
|
|
|
1339
1306
|
return addNode(container ? type : 'row', made, function() {
|
|
1340
1307
|
if (where.before) {
|
|
@@ -1461,30 +1428,153 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1461
1428
|
}
|
|
1462
1429
|
}
|
|
1463
1430
|
|
|
1464
|
-
|
|
1465
|
-
|
|
1431
|
+
/**
|
|
1432
|
+
* A click on the host's plain content makes it a text: of the one
|
|
1433
|
+
* editor offered, or of the one chosen when there are several. What
|
|
1434
|
+
* a click on a text does is its editor plugin's business.
|
|
1435
|
+
*/
|
|
1436
|
+
function onContentClick() {
|
|
1437
|
+
var block = $(this);
|
|
1438
|
+
if (block.attr('data-ge-content-type')) { return; }
|
|
1439
|
+
|
|
1440
|
+
// A second click, with the choice still open, withdraws it
|
|
1441
|
+
if (closeSizePicker()) { return; }
|
|
1466
1442
|
|
|
1467
|
-
//
|
|
1468
|
-
//
|
|
1443
|
+
// Content nobody can see - a tab that is not the open one, a
|
|
1444
|
+
// closed accordion item - has no geometry for an editor to lay
|
|
1469
1445
|
// its toolbar out against, and nothing anyone can type into
|
|
1470
|
-
if (
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1446
|
+
if (!block.is(':visible')) { return; }
|
|
1447
|
+
|
|
1448
|
+
var offers = textOffers();
|
|
1449
|
+
if (!offers.length) { return; }
|
|
1450
|
+
|
|
1451
|
+
if (offers.length === 1) {
|
|
1452
|
+
convertPlain(block, offers[0]);
|
|
1453
|
+
} else {
|
|
1454
|
+
openConvertPicker(block, offers);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
/**
|
|
1459
|
+
* Plain content made a text of `offer`'s type, through the convert
|
|
1460
|
+
* events: the type goes on, and the plugin that declared it takes the
|
|
1461
|
+
* block over and opens its editor. Nothing else about the node
|
|
1462
|
+
* changes. False when the library is missing or a handler canceled.
|
|
1463
|
+
*/
|
|
1464
|
+
function convertPlain(block, offer) {
|
|
1465
|
+
// Not converted, so the next click tries again: the library may
|
|
1466
|
+
// be loaded by then
|
|
1467
|
+
if (offer.available && !offer.available()) {
|
|
1468
|
+
if (offer.missingKey) { console.error(t(offer.missingKey)); }
|
|
1469
|
+
return false;
|
|
1483
1470
|
}
|
|
1484
1471
|
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1472
|
+
return operate(function() {
|
|
1473
|
+
var payload = payloadFor('plain', block, { source: 'tool', from: 'plain', to: offer.type });
|
|
1474
|
+
|
|
1475
|
+
if (!emit('before-convert', payload)) { return false; }
|
|
1476
|
+
|
|
1477
|
+
block.addClass('ge-content-type-' + offer.type).attr('data-ge-content-type', offer.type);
|
|
1478
|
+
|
|
1479
|
+
// Its plain drawer goes: the block is the plugin's now, and
|
|
1480
|
+
// the plugin gives it the drawer a text has
|
|
1481
|
+
block.parent('.ge-text-block').removeClass('ge-plain-block').children('.ge-tools-drawer').remove();
|
|
1482
|
+
|
|
1483
|
+
emit('after-convert', payload);
|
|
1484
|
+
offer.edit(block);
|
|
1485
|
+
|
|
1486
|
+
return true;
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
/**
|
|
1491
|
+
* The text types offered, as a strip under the plain content's
|
|
1492
|
+
* drawer, when there is more than one to choose from.
|
|
1493
|
+
*/
|
|
1494
|
+
function openConvertPicker(block, offers) {
|
|
1495
|
+
var textBlock = block.parent('.ge-text-block');
|
|
1496
|
+
|
|
1497
|
+
openPicker(textBlock.children('.ge-tools-drawer'), offers.map(function(offer) {
|
|
1498
|
+
return {
|
|
1499
|
+
label: offer.label,
|
|
1500
|
+
title: t('tool.convert_type', { editor: offer.label }),
|
|
1501
|
+
attributes: { 'data-ge-content-type': offer.type },
|
|
1502
|
+
choose: function() { convertPlain(block, offer); },
|
|
1503
|
+
};
|
|
1504
|
+
}), 'ge-text-picker ge-convert-picker', textBlock);
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
/**
|
|
1508
|
+
* A choice as a strip under `anchor` - a tool, or a drawer - one
|
|
1509
|
+
* button per `{ label, title, attributes, choose }`. Leaving `hover`
|
|
1510
|
+
* (the anchor by default) withdraws it, and so does closeSizePicker.
|
|
1511
|
+
*/
|
|
1512
|
+
function openPicker(anchor, choices, className, hover) {
|
|
1513
|
+
closeSizePicker();
|
|
1514
|
+
|
|
1515
|
+
anchor.closest('.ge-tools-drawer').addClass('ge-picker-open');
|
|
1516
|
+
sizePicker = $('<div class="ge-size-picker" />').addClass(className || '').appendTo(anchor);
|
|
1517
|
+
|
|
1518
|
+
choices.forEach(function(choice) {
|
|
1519
|
+
$('<a class="ge-size ge-size-flex" />')
|
|
1520
|
+
.attr(choice.attributes || {})
|
|
1521
|
+
.attr('title', choice.title || choice.label)
|
|
1522
|
+
.text(choice.label)
|
|
1523
|
+
.on('click', function(e) {
|
|
1524
|
+
e.preventDefault();
|
|
1525
|
+
e.stopPropagation();
|
|
1526
|
+
|
|
1527
|
+
closeSizePicker();
|
|
1528
|
+
choice.choose();
|
|
1529
|
+
})
|
|
1530
|
+
.appendTo(sizePicker)
|
|
1531
|
+
;
|
|
1532
|
+
});
|
|
1533
|
+
|
|
1534
|
+
(hover || anchor).one('mouseleave', function() {
|
|
1535
|
+
window.setTimeout(function() {
|
|
1536
|
+
if (sizePicker && !sizePicker.is(':hover')) { closeSizePicker(); }
|
|
1537
|
+
}, 400);
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
/**
|
|
1542
|
+
* Every text type the feature plugins declare, in the order they
|
|
1543
|
+
* were registered: the first plugin to declare a type owns it.
|
|
1544
|
+
*/
|
|
1545
|
+
function textTypes() {
|
|
1546
|
+
var owners = {};
|
|
1547
|
+
var entries = [];
|
|
1548
|
+
|
|
1549
|
+
$.each(FEATURES, function(name, feature) {
|
|
1550
|
+
if (!feature.textTypes) { return; }
|
|
1551
|
+
|
|
1552
|
+
(feature.textTypes() || []).forEach(function(entry) {
|
|
1553
|
+
if (owners[entry.type]) {
|
|
1554
|
+
if (owners[entry.type] !== name) {
|
|
1555
|
+
warnOnceHere('text-type:' + entry.type, 'the "' + name + '" plugin declares the ' +
|
|
1556
|
+
'text type "' + entry.type + '", which the "' + owners[entry.type] +
|
|
1557
|
+
'" plugin already declares: ignored');
|
|
1558
|
+
}
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
owners[entry.type] = name;
|
|
1563
|
+
entries.push(entry);
|
|
1564
|
+
});
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
return entries;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
/** The types plain content can be made, in order: the ones not marked `offered: false`. */
|
|
1571
|
+
function textOffers() {
|
|
1572
|
+
return textTypes().filter(function(entry) { return entry.offered !== false; });
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
/** Whether a plugin declares this text type. */
|
|
1576
|
+
function hasTextOwner(type) {
|
|
1577
|
+
return textTypes().some(function(entry) { return entry.type === type; });
|
|
1488
1578
|
}
|
|
1489
1579
|
|
|
1490
1580
|
function reset() {
|
|
@@ -1492,6 +1582,32 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1492
1582
|
init();
|
|
1493
1583
|
}
|
|
1494
1584
|
|
|
1585
|
+
/**
|
|
1586
|
+
* The canvas as html, in the textarea in its place, to edit by hand.
|
|
1587
|
+
* A feature plugin's onSourceOpen may put a code editor over the
|
|
1588
|
+
* textarea; its onSourceClose puts what it holds back in it, which is
|
|
1589
|
+
* what the canvas is made of again.
|
|
1590
|
+
*/
|
|
1591
|
+
function openSource() {
|
|
1592
|
+
deinit();
|
|
1593
|
+
htmlTextArea
|
|
1594
|
+
.height(0.8 * $(window).height())
|
|
1595
|
+
.val(canvas.html())
|
|
1596
|
+
.show()
|
|
1597
|
+
;
|
|
1598
|
+
canvas.hide();
|
|
1599
|
+
sourceOpen = true;
|
|
1600
|
+
plugins('onSourceOpen', htmlTextArea);
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
function closeSource() {
|
|
1604
|
+
plugins('onSourceClose', htmlTextArea);
|
|
1605
|
+
sourceOpen = false;
|
|
1606
|
+
canvas.empty().html(htmlTextArea.val()).show();
|
|
1607
|
+
init();
|
|
1608
|
+
htmlTextArea.hide();
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1495
1611
|
function init() {
|
|
1496
1612
|
// The node whose settings were open is gone - deleted, say - and
|
|
1497
1613
|
// its panel with it
|
|
@@ -1521,9 +1637,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1521
1637
|
// Its panel goes home to its drawer first, and both go together
|
|
1522
1638
|
closeSettings();
|
|
1523
1639
|
canvas.removeClass('ge-editing ge-drag-drawer ge-dropping');
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1640
|
+
// Before the drawers and the text blocks come off: an editor
|
|
1641
|
+
// plugin closes its editors here, and finds them where it left them
|
|
1642
|
+
plugins('onBeforeDeinit');
|
|
1527
1643
|
closeSizePicker();
|
|
1528
1644
|
hideDropMarker();
|
|
1529
1645
|
canvas.find('.ge-tools-drawer').remove();
|
|
@@ -1570,13 +1686,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1570
1686
|
}
|
|
1571
1687
|
|
|
1572
1688
|
function destroy() {
|
|
1689
|
+
// The html being edited is what the canvas is left with
|
|
1690
|
+
if (sourceOpen) { closeSource(); }
|
|
1573
1691
|
deinit();
|
|
1574
1692
|
removeConfirmModal();
|
|
1575
1693
|
removeSettingsPanels();
|
|
1576
1694
|
mainControls.remove();
|
|
1577
1695
|
htmlTextArea.remove();
|
|
1578
1696
|
$(window).off('scroll', onScroll);
|
|
1579
|
-
canvas.off('click', '.ge-content',
|
|
1697
|
+
canvas.off('click', '.ge-content', onContentClick);
|
|
1580
1698
|
canvas.off('ge-rte-ready', '.ge-content');
|
|
1581
1699
|
canvas.removeData('grideditor');
|
|
1582
1700
|
}
|
|
@@ -1627,30 +1745,27 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1627
1745
|
return !settings.plugins || settings.plugins.indexOf(name) !== -1;
|
|
1628
1746
|
};
|
|
1629
1747
|
|
|
1748
|
+
// What the plugins setting does not choose: the text editors,
|
|
1749
|
+
// which content_types chooses. A page that names its containers
|
|
1750
|
+
// there has named no editor, and still wants the one it uses.
|
|
1751
|
+
var featureWanted = function(name, factory) {
|
|
1752
|
+
return factory.always === true || wanted(name);
|
|
1753
|
+
};
|
|
1754
|
+
|
|
1630
1755
|
$.each($.fn.gridEditor.containers, function(type, factory) {
|
|
1631
1756
|
if (wanted(type)) { CONTAINERS[type] = factory(api); }
|
|
1632
1757
|
});
|
|
1633
1758
|
|
|
1634
1759
|
$.each($.fn.gridEditor.features, function(name, factory) {
|
|
1635
|
-
if (
|
|
1760
|
+
if (featureWanted(name, factory)) { FEATURES[name] = factory(api); }
|
|
1636
1761
|
});
|
|
1637
1762
|
|
|
1638
|
-
//
|
|
1639
|
-
//
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
|
-
// 5.x's registry, for an integration a host wrote itself. One the
|
|
1646
|
-
// plugins above already provide is theirs.
|
|
1647
|
-
$.each($.fn.gridEditor.RTEs, function(type, rte) {
|
|
1648
|
-
if (TEXTS[type]) { return; }
|
|
1649
|
-
|
|
1650
|
-
warnOnceHere('rtes:' + type, '$.fn.gridEditor.RTEs is deprecated and will be removed ' +
|
|
1651
|
-
'in 7.0: register "' + type + '" under $.fn.gridEditor.texts, see docs/plugins.md');
|
|
1652
|
-
TEXTS[type] = legacyText(rte);
|
|
1653
|
-
});
|
|
1763
|
+
// 5.x's registry of text editors is gone: an editor of a host's
|
|
1764
|
+
// own is a plugin of its own since 6.0
|
|
1765
|
+
if (!$.isEmptyObject($.fn.gridEditor.RTEs || {})) {
|
|
1766
|
+
warnOnceHere('rtes', '$.fn.gridEditor.RTEs was removed in 6.0 and what is registered there ' +
|
|
1767
|
+
'is ignored: write the editor as a plugin, see docs/plugins.md');
|
|
1768
|
+
}
|
|
1654
1769
|
|
|
1655
1770
|
$.each($.fn.gridEditor.utilities, function(name, factory) {
|
|
1656
1771
|
if (!wanted(name)) { return; }
|
|
@@ -1731,6 +1846,10 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1731
1846
|
// A text editor has rewritten a content area, so whatever the
|
|
1732
1847
|
// editor and its plugins had put in there goes back in
|
|
1733
1848
|
textReady: function(block) { block.trigger('ge-rte-ready'); },
|
|
1849
|
+
// A choice under a tool, offered by holding it
|
|
1850
|
+
attachPicker: attachPicker,
|
|
1851
|
+
openPicker: openPicker,
|
|
1852
|
+
closePicker: closeSizePicker,
|
|
1734
1853
|
// A node's settings panel, in its drawer or open outside it
|
|
1735
1854
|
detailsOf: detailsOf,
|
|
1736
1855
|
// A node's drawer: its first child, or for a content area the
|
|
@@ -1804,6 +1923,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1804
1923
|
* name the ones it means.
|
|
1805
1924
|
*/
|
|
1806
1925
|
function appliesTo(family, kind) {
|
|
1926
|
+
// The host's plain content reads and previews its utilities as a
|
|
1927
|
+
// text does; with no gear, nothing in its drawer writes them
|
|
1928
|
+
if (kind === 'plain') { kind = 'text'; }
|
|
1807
1929
|
if (family.appliesTo.indexOf(kind) !== -1) { return true; }
|
|
1808
1930
|
|
|
1809
1931
|
return !!CONTAINERS[kind] && family.appliesTo.indexOf('container') !== -1;
|
|
@@ -2273,7 +2395,6 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2273
2395
|
* -------------------------------------------------------------- */
|
|
2274
2396
|
|
|
2275
2397
|
var CONTAINERS = {}; // The container plugins in use, by the type each builds
|
|
2276
|
-
var TEXTS = {}; // The text editor plugins, by the content type each edits
|
|
2277
2398
|
var FEATURES = {}; // The feature plugins in use, by name
|
|
2278
2399
|
var UTILITIES = {}; // The utility plugins in use, by name
|
|
2279
2400
|
var featureMethods = {}; // The methods those features contribute
|
|
@@ -2724,22 +2845,6 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
2724
2845
|
});
|
|
2725
2846
|
});
|
|
2726
2847
|
|
|
2727
|
-
// A text block of the first editor offered; held, the tool
|
|
2728
|
-
// offers each of them when there is more than one
|
|
2729
|
-
var texts = offeredTexts();
|
|
2730
|
-
if (texts.length) {
|
|
2731
|
-
createTool(drawer, t('tool.add_text'), 'ge-add-text', 'bi bi-type', function() {
|
|
2732
|
-
if (closeSizePicker()) { return; } // The picker was open: that was the answer
|
|
2733
|
-
|
|
2734
|
-
addTextTo(col, texts[0]);
|
|
2735
|
-
});
|
|
2736
|
-
|
|
2737
|
-
if (texts.length > 1) {
|
|
2738
|
-
var textTool = drawer.find('> .ge-add-text');
|
|
2739
|
-
attachPicker(textTool, function() { openTextPicker(textTool, col); });
|
|
2740
|
-
}
|
|
2741
|
-
}
|
|
2742
|
-
|
|
2743
2848
|
createTool(drawer, t('tool.add_row'), 'ge-add-row', 'bi bi-plus-circle', function() {
|
|
2744
2849
|
// An empty row: the columns in it are the next decision,
|
|
2745
2850
|
// and its drawer's add column tool is where that is made
|
|
@@ -4202,7 +4307,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4202
4307
|
|
|
4203
4308
|
var column = createColumn(size, options.offset);
|
|
4204
4309
|
if (options.content !== undefined) {
|
|
4205
|
-
column.
|
|
4310
|
+
column.append(contentFor(options.content));
|
|
4206
4311
|
}
|
|
4207
4312
|
|
|
4208
4313
|
return place(column, 'column', options);
|
|
@@ -4270,10 +4375,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4270
4375
|
* the all view. `offset` indents it, within the same 12 unit budget.
|
|
4271
4376
|
*/
|
|
4272
4377
|
function createColumn(size, offset) {
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
;
|
|
4378
|
+
// Empty: what goes in it is the next decision, and the tools of
|
|
4379
|
+
// the column and the toolbar are where it is made
|
|
4380
|
+
var column = $('<div class="column"/>');
|
|
4277
4381
|
|
|
4278
4382
|
// The tier being edited, or the base class in the all view
|
|
4279
4383
|
var tier = tiersFor(curView)[0];
|
|
@@ -4367,8 +4471,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4367
4471
|
return;
|
|
4368
4472
|
} else if (kept) {
|
|
4369
4473
|
// Of the same type, or of none if it had none
|
|
4370
|
-
placed =
|
|
4371
|
-
.append(piece.text);
|
|
4474
|
+
placed = createDefaultContentWrapper(type).append(piece.text);
|
|
4372
4475
|
} else {
|
|
4373
4476
|
// The content area itself, moved along if an
|
|
4374
4477
|
// element came before the first text
|
|
@@ -4424,7 +4527,8 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4424
4527
|
}
|
|
4425
4528
|
|
|
4426
4529
|
/**
|
|
4427
|
-
* Wrap a run of loose column content in a content area
|
|
4530
|
+
* Wrap a run of loose column content in a content area of no type -
|
|
4531
|
+
* the host's plain content - and hand back
|
|
4428
4532
|
* an empty set: the caller has to forget what it just wrapped, or the
|
|
4429
4533
|
* next boundary wraps the same nodes again and leaves the first
|
|
4430
4534
|
* wrapper behind, empty.
|
|
@@ -4439,12 +4543,10 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4439
4543
|
}
|
|
4440
4544
|
|
|
4441
4545
|
/**
|
|
4442
|
-
* A content area
|
|
4443
|
-
*
|
|
4444
|
-
* type to name.
|
|
4546
|
+
* A content area of a text type, or with none the host's plain
|
|
4547
|
+
* content: a content area and nothing more.
|
|
4445
4548
|
*/
|
|
4446
4549
|
function createDefaultContentWrapper(type) {
|
|
4447
|
-
type = type || settings.content_types[0];
|
|
4448
4550
|
var contentArea = $('<div class="ge-content"/>');
|
|
4449
4551
|
|
|
4450
4552
|
if (type) {
|
|
@@ -4455,22 +4557,23 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4455
4557
|
}
|
|
4456
4558
|
|
|
4457
4559
|
/* --------------------------------------------------------------
|
|
4458
|
-
*
|
|
4560
|
+
* Content blocks: a content area as a block of its own, with a drawer.
|
|
4459
4561
|
*
|
|
4460
4562
|
* The drawer cannot go inside the content area, as every other
|
|
4461
|
-
* block's does: what is inside
|
|
4462
|
-
* the drawer editable text, copy it into its own document or
|
|
4463
|
-
* away on an undo. So while editing, each content area in a
|
|
4464
|
-
* sits in a .ge-text-block beside its drawer, and that wrapper
|
|
4465
|
-
* block the column moves. deinit takes it off again: it is
|
|
4466
|
-
* of the markup.
|
|
4563
|
+
* block's does: what is inside may be a text editor's, which would
|
|
4564
|
+
* make the drawer editable text, copy it into its own document or
|
|
4565
|
+
* take it away on an undo. So while editing, each content area in a
|
|
4566
|
+
* column sits in a .ge-text-block beside its drawer, and that wrapper
|
|
4567
|
+
* is the block the column moves. deinit takes it off again: it is
|
|
4568
|
+
* never part of the markup.
|
|
4569
|
+
*
|
|
4570
|
+
* A content area with no type is the host's plain content, and its
|
|
4571
|
+
* drawer is the editor's: move and delete, nothing else. One whose
|
|
4572
|
+
* type a plugin declares is that plugin's text, and the plugin gives
|
|
4573
|
+
* it its drawer. One whose type nobody declares is a text whose
|
|
4574
|
+
* editor is not loaded: a block to move and delete, not to edit.
|
|
4467
4575
|
* -------------------------------------------------------------- */
|
|
4468
4576
|
|
|
4469
|
-
/** The text editors this editor offers, in content_types order: the ones loaded. */
|
|
4470
|
-
function offeredTexts() {
|
|
4471
|
-
return settings.content_types.filter(function(type) { return !!TEXTS[type]; });
|
|
4472
|
-
}
|
|
4473
|
-
|
|
4474
4577
|
function wrapTexts() {
|
|
4475
4578
|
canvas.find('.column > .ge-content').each(function() {
|
|
4476
4579
|
$(this).wrap('<div class="ge-text-block" />');
|
|
@@ -4478,7 +4581,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4478
4581
|
|
|
4479
4582
|
canvas.find('.ge-text-block').each(function() {
|
|
4480
4583
|
var textBlock = $(this);
|
|
4481
|
-
if (
|
|
4584
|
+
if (textBlock.children('.ge-tools-drawer').length) { return; }
|
|
4585
|
+
|
|
4586
|
+
var type = textBlock.children('.ge-content').attr('data-ge-content-type');
|
|
4587
|
+
|
|
4588
|
+
if (!type) {
|
|
4589
|
+
createPlainControls(textBlock);
|
|
4590
|
+
} else if (!hasTextOwner(type)) {
|
|
4591
|
+
createOrphanControls(textBlock, type);
|
|
4592
|
+
}
|
|
4482
4593
|
});
|
|
4483
4594
|
}
|
|
4484
4595
|
|
|
@@ -4489,41 +4600,22 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4489
4600
|
});
|
|
4490
4601
|
}
|
|
4491
4602
|
|
|
4492
|
-
|
|
4603
|
+
/** The host's plain content: moved and deleted here, edited in the source. */
|
|
4604
|
+
function createPlainControls(textBlock) {
|
|
4493
4605
|
var block = textBlock.children('.ge-content');
|
|
4494
|
-
var
|
|
4495
|
-
|
|
4606
|
+
var drawer = $('<div class="ge-tools-drawer ge-text-drawer ge-plain-drawer" />').prependTo(textBlock);
|
|
4607
|
+
textBlock.addClass('ge-plain-block');
|
|
4496
4608
|
|
|
4497
4609
|
createMoveTool(drawer);
|
|
4498
4610
|
|
|
4499
|
-
//
|
|
4500
|
-
//
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
'bi bi-exclamation-triangle');
|
|
4504
|
-
}
|
|
4505
|
-
|
|
4506
|
-
var details = addSettingsTool(drawer, block, settings.text_classes);
|
|
4507
|
-
|
|
4508
|
-
// Which editor edits it, first in its settings: something to
|
|
4509
|
-
// know about it, not something to do with it
|
|
4510
|
-
if (type && TEXTS[type]) {
|
|
4511
|
-
$('<div class="ge-field ge-text-editor" />')
|
|
4512
|
-
.append($('<span class="ge-field-label" />').text(t('panel.editor')))
|
|
4513
|
-
.append($('<span class="ge-field-value" />').text(textLabel(type)))
|
|
4514
|
-
.prependTo(details.children('.ge-details-general'));
|
|
4515
|
-
}
|
|
4516
|
-
|
|
4517
|
-
settings.text_tools.forEach(function(hostTool) {
|
|
4518
|
-
createTool(drawer, hostTool.title || '', hostTool.className || '',
|
|
4519
|
-
hostTool.iconClass || 'bi bi-wrench', hostTool.on);
|
|
4611
|
+
// The tools a feature plugin gives plain content, which is not
|
|
4612
|
+
// every plugin's drawerTools: no gear, no utilities, no copy
|
|
4613
|
+
$.each(FEATURES, function(name, feature) {
|
|
4614
|
+
if (feature.plainTools) { feature.plainTools(drawer, block); }
|
|
4520
4615
|
});
|
|
4521
4616
|
|
|
4522
|
-
createTool(drawer, t('tool.
|
|
4523
|
-
deleteNode('
|
|
4524
|
-
// Its editor lets go first: removed while open, it would
|
|
4525
|
-
// be left behind, attached to nothing
|
|
4526
|
-
closeText(block);
|
|
4617
|
+
createTool(drawer, t('tool.delete_plain'), 'ge-delete-plain', 'bi bi-trash', function() {
|
|
4618
|
+
deleteNode('plain', block, t('confirm.delete_plain'), function(removed) {
|
|
4527
4619
|
textBlock.slideUp(function() {
|
|
4528
4620
|
textBlock.remove();
|
|
4529
4621
|
removed();
|
|
@@ -4533,162 +4625,34 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4533
4625
|
}
|
|
4534
4626
|
|
|
4535
4627
|
/**
|
|
4536
|
-
*
|
|
4537
|
-
*
|
|
4538
|
-
*
|
|
4539
|
-
* An editor adds attributes of its own while it is open, and takes
|
|
4540
|
-
* them off again, more or less: CKEditor leaves aria-readonly behind.
|
|
4541
|
-
* tinyMCE does worse, and puts back every attribute as it was when it
|
|
4542
|
-
* opened, so an id, a class or a plugin's attribute given while it was
|
|
4543
|
-
* open is lost. So the attributes are read three times - before the
|
|
4544
|
-
* editor opens, once it has opened, and before it closes - and what
|
|
4545
|
-
* changed between the second and the third, the host's doing, is
|
|
4546
|
-
* played back onto the first, the host's markup. What the editor did
|
|
4547
|
-
* is left out either way.
|
|
4628
|
+
* A text whose editor is not loaded: still a block to move and delete,
|
|
4629
|
+
* and its drawer says why it cannot be edited.
|
|
4548
4630
|
*/
|
|
4549
|
-
function
|
|
4550
|
-
var
|
|
4551
|
-
var
|
|
4552
|
-
var open = before ? attributesOf(block[0]) : null;
|
|
4553
|
-
|
|
4554
|
-
// Every content area is told, as in 5.x: one whose editor never
|
|
4555
|
-
// started is the plugin's to ignore
|
|
4556
|
-
if (text) { text.stop(block); }
|
|
4557
|
-
|
|
4558
|
-
// After stop, not before: an editor that restores the class
|
|
4559
|
-
// attribute it snapshotted would put ge-rte-active back
|
|
4560
|
-
block.removeClass('ge-rte-active');
|
|
4561
|
-
|
|
4562
|
-
if (before) {
|
|
4563
|
-
restoreAttributes(block, before, block.data('ge-text-ready') || before, open);
|
|
4564
|
-
block.removeData('ge-text-before').removeData('ge-text-ready');
|
|
4565
|
-
}
|
|
4566
|
-
}
|
|
4567
|
-
|
|
4568
|
-
/** What a text editor puts on a content area while it is open, whenever it likes. */
|
|
4569
|
-
var EDITOR_CLASS = /^(mce-|cke|note-)|^(active|ge-rte-active)$/;
|
|
4570
|
-
var EDITOR_ATTRIBUTE = /^(data-mce-|contenteditable$|spellcheck$)/;
|
|
4571
|
-
|
|
4572
|
-
function attributesOf(element) {
|
|
4573
|
-
var found = {};
|
|
4574
|
-
$.each($.makeArray(element.attributes), function(i, attribute) {
|
|
4575
|
-
found[attribute.name] = attribute.value;
|
|
4576
|
-
});
|
|
4577
|
-
return found;
|
|
4578
|
-
}
|
|
4579
|
-
|
|
4580
|
-
function classesIn(value) {
|
|
4581
|
-
return (value || '').split(/\s+/).filter(function(name) {
|
|
4582
|
-
return name !== '' && !EDITOR_CLASS.test(name);
|
|
4583
|
-
});
|
|
4584
|
-
}
|
|
4585
|
-
|
|
4586
|
-
/** The host's markup, with what changed from `ready` to `open` played back onto it. */
|
|
4587
|
-
function restoreAttributes(block, before, ready, open) {
|
|
4588
|
-
var result = $.extend({}, before);
|
|
4589
|
-
|
|
4590
|
-
$.each($.extend({}, ready, open), function(name) {
|
|
4591
|
-
if (name === 'class' || EDITOR_ATTRIBUTE.test(name) || ready[name] === open[name]) { return; }
|
|
4592
|
-
|
|
4593
|
-
if (open[name] === undefined) {
|
|
4594
|
-
delete result[name];
|
|
4595
|
-
} else {
|
|
4596
|
-
result[name] = open[name];
|
|
4597
|
-
}
|
|
4598
|
-
});
|
|
4631
|
+
function createOrphanControls(textBlock, type) {
|
|
4632
|
+
var block = textBlock.children('.ge-content');
|
|
4633
|
+
var drawer = $('<div class="ge-tools-drawer ge-text-drawer" />').prependTo(textBlock);
|
|
4599
4634
|
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4635
|
+
createMoveTool(drawer);
|
|
4636
|
+
createTool(drawer, t('text.no_editor', { type: type }), 'ge-text-info ge-text-missing',
|
|
4637
|
+
'bi bi-exclamation-triangle');
|
|
4638
|
+
createTool(drawer, t('tool.delete_text'), 'ge-delete-text', 'bi bi-trash', function() {
|
|
4639
|
+
deleteNode('text', block, t('confirm.delete_text'), function(removed) {
|
|
4640
|
+
textBlock.slideUp(function() {
|
|
4641
|
+
textBlock.remove();
|
|
4642
|
+
removed();
|
|
4643
|
+
});
|
|
4644
|
+
});
|
|
4607
4645
|
});
|
|
4608
|
-
|
|
4609
|
-
// In the host's order, class where it was: getHtml should not
|
|
4610
|
-
// shuffle a node's attributes because it was edited
|
|
4611
|
-
if (classes.length) { result['class'] = classes.join(' '); } else { delete result['class']; }
|
|
4612
|
-
|
|
4613
|
-
$.each(attributesOf(block[0]), function(name) { block.removeAttr(name); });
|
|
4614
|
-
$.each(result, function(name, value) { block.attr(name, value); });
|
|
4615
|
-
}
|
|
4616
|
-
|
|
4617
|
-
/** What a text editor is called: its plugin's label, or its type. */
|
|
4618
|
-
function textLabel(type) {
|
|
4619
|
-
var text = TEXTS[type];
|
|
4620
|
-
return text && text.labelKey ? t(text.labelKey) : type;
|
|
4621
|
-
}
|
|
4622
|
-
|
|
4623
|
-
/** A detached content area of `type`, holding `content` or the editor's initial content. */
|
|
4624
|
-
function makeText(type, content) {
|
|
4625
|
-
var text = TEXTS[type];
|
|
4626
|
-
return createDefaultContentWrapper(type)
|
|
4627
|
-
.html(content !== undefined ? content : (text && text.initialContent) || '');
|
|
4628
|
-
}
|
|
4629
|
-
|
|
4630
|
-
/** A text block at the end of a column, through the add events. */
|
|
4631
|
-
function addTextTo(col, type) {
|
|
4632
|
-
var block = makeText(type);
|
|
4633
|
-
|
|
4634
|
-
return addNode('text', block, function() {
|
|
4635
|
-
col.append(block);
|
|
4636
|
-
}, { parent: col, source: 'tool' });
|
|
4637
4646
|
}
|
|
4638
4647
|
|
|
4639
4648
|
/**
|
|
4640
|
-
*
|
|
4641
|
-
*
|
|
4642
|
-
* offered by default. `options.content` is its html.
|
|
4649
|
+
* A detached content area holding `content`: a text of the first type
|
|
4650
|
+
* offered, or the host's plain content when no editor is offered.
|
|
4643
4651
|
*/
|
|
4644
|
-
function
|
|
4645
|
-
|
|
4646
|
-
options = type;
|
|
4647
|
-
type = undefined;
|
|
4648
|
-
}
|
|
4649
|
-
options = options || {};
|
|
4650
|
-
type = type || offeredTexts()[0];
|
|
4651
|
-
|
|
4652
|
-
if (!type || !TEXTS[type]) {
|
|
4653
|
-
warnOnceHere('createText:' + type, 'createText: no text editor "' + type + '" is loaded; ' +
|
|
4654
|
-
'load its plugin and name it in content_types');
|
|
4655
|
-
return null;
|
|
4656
|
-
}
|
|
4652
|
+
function contentFor(content) {
|
|
4653
|
+
var offers = textOffers();
|
|
4657
4654
|
|
|
4658
|
-
return
|
|
4659
|
-
}
|
|
4660
|
-
|
|
4661
|
-
/**
|
|
4662
|
-
* The text editors offered, as a strip under the add text tool, when
|
|
4663
|
-
* there is more than one to choose from.
|
|
4664
|
-
*/
|
|
4665
|
-
function openTextPicker(tool, col) {
|
|
4666
|
-
closeSizePicker();
|
|
4667
|
-
|
|
4668
|
-
tool.closest('.ge-tools-drawer').addClass('ge-picker-open');
|
|
4669
|
-
sizePicker = $('<div class="ge-size-picker ge-text-picker" />').appendTo(tool);
|
|
4670
|
-
|
|
4671
|
-
offeredTexts().forEach(function(type) {
|
|
4672
|
-
$('<a class="ge-size ge-size-flex" />')
|
|
4673
|
-
.attr('data-ge-content-type', type)
|
|
4674
|
-
.attr('title', t('tool.add_text_type', { editor: textLabel(type) }))
|
|
4675
|
-
.text(textLabel(type))
|
|
4676
|
-
.on('click', function(e) {
|
|
4677
|
-
e.preventDefault();
|
|
4678
|
-
e.stopPropagation();
|
|
4679
|
-
|
|
4680
|
-
closeSizePicker();
|
|
4681
|
-
addTextTo(col, type);
|
|
4682
|
-
})
|
|
4683
|
-
.appendTo(sizePicker)
|
|
4684
|
-
;
|
|
4685
|
-
});
|
|
4686
|
-
|
|
4687
|
-
tool.one('mouseleave', function() {
|
|
4688
|
-
window.setTimeout(function() {
|
|
4689
|
-
if (sizePicker && !sizePicker.is(':hover')) { closeSizePicker(); }
|
|
4690
|
-
}, 400);
|
|
4691
|
-
});
|
|
4655
|
+
return createDefaultContentWrapper(offers.length ? offers[0].type : null).html(content);
|
|
4692
4656
|
}
|
|
4693
4657
|
|
|
4694
4658
|
/**
|
|
@@ -4745,23 +4709,6 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4745
4709
|
return curView;
|
|
4746
4710
|
}
|
|
4747
4711
|
|
|
4748
|
-
/** The text editor plugin for a content type, or null. */
|
|
4749
|
-
function textFor(type) {
|
|
4750
|
-
return TEXTS[type] || null;
|
|
4751
|
-
}
|
|
4752
|
-
|
|
4753
|
-
/**
|
|
4754
|
-
* An integration written to 5.x's contract - init and deinit over a
|
|
4755
|
-
* set of content areas - as a text editor plugin.
|
|
4756
|
-
*/
|
|
4757
|
-
function legacyText(rte) {
|
|
4758
|
-
return {
|
|
4759
|
-
initialContent: rte.initialContent,
|
|
4760
|
-
start: function(block) { rte.init(settings, block); },
|
|
4761
|
-
stop: function(block) { rte.deinit(settings, block); },
|
|
4762
|
-
};
|
|
4763
|
-
}
|
|
4764
|
-
|
|
4765
4712
|
/**
|
|
4766
4713
|
* The instance handle, documented API as of 3.0: the methods the
|
|
4767
4714
|
* plugin dispatches, plus the settings and the canvas. A host holding
|
|
@@ -4781,7 +4728,15 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4781
4728
|
getView: getView,
|
|
4782
4729
|
createRow: apiCreateRow,
|
|
4783
4730
|
createColumn: apiCreateColumn,
|
|
4784
|
-
createText:
|
|
4731
|
+
createText: function(type, options) {
|
|
4732
|
+
if (!featureMethods.createText) {
|
|
4733
|
+
warnOnceHere('plugin:text', 'createText needs a text editor plugin: include ' +
|
|
4734
|
+
'dist/plugins/grideditor.tinymce.js, or another editor\'s, after the editor');
|
|
4735
|
+
return null;
|
|
4736
|
+
}
|
|
4737
|
+
|
|
4738
|
+
return featureMethods.createText(type, options);
|
|
4739
|
+
},
|
|
4785
4740
|
createElement: function(content, options) {
|
|
4786
4741
|
if (!featureMethods.createElement) {
|
|
4787
4742
|
warnOnceHere('plugin:elements', 'createElement needs the elements plugin: ' +
|
|
@@ -4826,6 +4781,9 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4826
4781
|
baseElem.data('grideditor', handle);
|
|
4827
4782
|
|
|
4828
4783
|
loadPlugins();
|
|
4784
|
+
// Again, with what the plugins filled in: the text editors'
|
|
4785
|
+
// content_types when the host gave none
|
|
4786
|
+
handle.settings = settingsCopy();
|
|
4829
4787
|
setup();
|
|
4830
4788
|
init();
|
|
4831
4789
|
|
|
@@ -4836,14 +4794,10 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
4836
4794
|
};
|
|
4837
4795
|
|
|
4838
4796
|
/**
|
|
4839
|
-
*
|
|
4840
|
-
*
|
|
4841
|
-
*
|
|
4842
|
-
* { labelKey, initialContent, available, missingKey, start, stop }.
|
|
4797
|
+
* 5.x's registry of text editors, removed in 6.0: still there to register
|
|
4798
|
+
* into, so a page written for 5.x does not throw, but what it holds is
|
|
4799
|
+
* ignored with a warning. The text editors are plugins since 6.0.
|
|
4843
4800
|
*/
|
|
4844
|
-
$.fn.gridEditor.texts = {};
|
|
4845
|
-
|
|
4846
|
-
/** 5.x's registry of text editors, adapted with a warning. Removed in 7.0. */
|
|
4847
4801
|
$.fn.gridEditor.RTEs = {};
|
|
4848
4802
|
|
|
4849
4803
|
/**
|
|
@@ -4899,11 +4853,9 @@ $.fn.gridEditor.locales = {
|
|
|
4899
4853
|
'tool.move': 'Move',
|
|
4900
4854
|
'tool.settings': 'Settings',
|
|
4901
4855
|
'tool.add_row': 'Add row',
|
|
4902
|
-
'tool.add_text': 'Add text',
|
|
4903
|
-
'tool.add_text_type': 'Add {editor} text',
|
|
4904
4856
|
'tool.delete_text': 'Remove text',
|
|
4905
|
-
'
|
|
4906
|
-
'
|
|
4857
|
+
'tool.delete_plain': 'Remove content',
|
|
4858
|
+
'tool.convert_type': 'Edit as {editor} text',
|
|
4907
4859
|
'text.no_editor': 'No text editor "{type}" is loaded: this text can be moved and deleted, not edited',
|
|
4908
4860
|
'tool.add_column': 'Add column\n(hold to choose the width)',
|
|
4909
4861
|
'tool.column_size': '{size} of 12',
|
|
@@ -4925,10 +4877,8 @@ $.fn.gridEditor.locales = {
|
|
|
4925
4877
|
'panel.done': 'Done',
|
|
4926
4878
|
'panel.id': 'Id',
|
|
4927
4879
|
'panel.classes': 'Classes',
|
|
4928
|
-
'panel.editor': 'Editor',
|
|
4929
4880
|
'panel.kind_row': 'Row',
|
|
4930
4881
|
'panel.kind_column': 'Column',
|
|
4931
|
-
'panel.kind_text': 'Text',
|
|
4932
4882
|
'panel.kind_element': 'Element',
|
|
4933
4883
|
'panel.kind_section': 'Section',
|
|
4934
4884
|
'panel.kind_tab': 'Tab',
|
|
@@ -4946,6 +4896,7 @@ $.fn.gridEditor.locales = {
|
|
|
4946
4896
|
'confirm.delete_row': 'Delete row?',
|
|
4947
4897
|
'confirm.delete_column': 'Delete column?',
|
|
4948
4898
|
'confirm.delete_text': 'Delete this text?',
|
|
4899
|
+
'confirm.delete_plain': 'Delete this content?',
|
|
4949
4900
|
'confirm.delete_container': 'Delete this container and everything in it?',
|
|
4950
4901
|
'view.all': 'All sizes',
|
|
4951
4902
|
'view.xs': 'Phone',
|