@themarioga/grid-editor 3.1.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/AUTO_SAVE.md +30 -0
- package/BUILDING.md +68 -0
- package/CHANGELOG.md +362 -0
- package/LICENSE +23 -0
- package/README.md +521 -0
- package/UPGRADING.md +88 -0
- package/dist/grideditor.css +3875 -0
- package/dist/grideditor.min.css +2 -0
- package/dist/grideditor.min.css.map +1 -0
- package/dist/jquery.grideditor.js +2996 -0
- package/dist/jquery.grideditor.min.js +2 -0
- package/dist/jquery.grideditor.min.js.map +1 -0
- package/dist/locales/grideditor.es.js +74 -0
- package/dist/locales/grideditor.es.min.js +2 -0
- package/dist/locales/grideditor.es.min.js.map +1 -0
- package/dist/plugins/grideditor.accordion.js +185 -0
- package/dist/plugins/grideditor.accordion.min.js +2 -0
- package/dist/plugins/grideditor.accordion.min.js.map +1 -0
- package/dist/plugins/grideditor.elements.js +141 -0
- package/dist/plugins/grideditor.elements.min.js +2 -0
- package/dist/plugins/grideditor.elements.min.js.map +1 -0
- package/dist/plugins/grideditor.popup.js +143 -0
- package/dist/plugins/grideditor.popup.min.js +2 -0
- package/dist/plugins/grideditor.popup.min.js.map +1 -0
- package/dist/plugins/grideditor.tabs.js +140 -0
- package/dist/plugins/grideditor.tabs.min.js +2 -0
- package/dist/plugins/grideditor.tabs.min.js.map +1 -0
- package/docs/events.md +158 -0
- package/docs/locale-keys.md +140 -0
- package/docs/plugins.md +163 -0
- package/example/autosave.html +89 -0
- package/example/basic.html +93 -0
- package/example/breakpoints.html +94 -0
- package/example/ckeditor.html +104 -0
- package/example/containers.html +214 -0
- package/example/elements.html +147 -0
- package/example/index.html +119 -0
- package/example/locale.html +84 -0
- package/example/plugins.html +217 -0
- package/example/summernote.html +106 -0
- package/example/wrap_content.html +44 -0
- package/package.json +66 -0
- package/src/js/jquery.grideditor.ckeditor.js +77 -0
- package/src/js/jquery.grideditor.js +2711 -0
- package/src/js/jquery.grideditor.summernote.js +72 -0
- package/src/js/jquery.grideditor.tinymce.js +135 -0
- package/src/js/locales/grideditor.es.js +74 -0
- package/src/js/plugins/grideditor.accordion.js +185 -0
- package/src/js/plugins/grideditor.elements.js +141 -0
- package/src/js/plugins/grideditor.popup.js +143 -0
- package/src/js/plugins/grideditor.tabs.js +140 -0
- package/src/less/grideditor.less +594 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
(function($) {
|
|
2
|
+
|
|
3
|
+
$.fn.gridEditor.RTEs.summernote = {
|
|
4
|
+
|
|
5
|
+
init: function(settings, contentAreas) {
|
|
6
|
+
|
|
7
|
+
if (!jQuery().summernote) {
|
|
8
|
+
console.error($.fn.gridEditor.t(settings, 'error.summernote_missing'));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
var self = this;
|
|
12
|
+
contentAreas.each(function() {
|
|
13
|
+
var contentArea = $(this);
|
|
14
|
+
if (!contentArea.hasClass('active')) {
|
|
15
|
+
if (contentArea.html() == self.initialContent) {
|
|
16
|
+
contentArea.html('');
|
|
17
|
+
}
|
|
18
|
+
contentArea.addClass('active');
|
|
19
|
+
|
|
20
|
+
var configuration = $.extend(
|
|
21
|
+
true, // deep copy
|
|
22
|
+
{},
|
|
23
|
+
(settings.summernote && settings.summernote.config ? settings.summernote.config : {}),
|
|
24
|
+
{
|
|
25
|
+
tabsize: 2,
|
|
26
|
+
airMode: true,
|
|
27
|
+
// Focus editor on creation
|
|
28
|
+
callbacks: {
|
|
29
|
+
onInit: function() {
|
|
30
|
+
|
|
31
|
+
// Call original oninit function, if one was passed in the config
|
|
32
|
+
var callback;
|
|
33
|
+
try {
|
|
34
|
+
callback = settings.summernote.config.callbacks.onInit;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
// No callback passed
|
|
37
|
+
}
|
|
38
|
+
if (callback) {
|
|
39
|
+
callback.call(this);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// The editor owns what is inside the
|
|
43
|
+
// content area now, so the grid editor is
|
|
44
|
+
// told to put its own furniture back
|
|
45
|
+
contentArea.trigger('ge-rte-ready');
|
|
46
|
+
|
|
47
|
+
contentArea.summernote('focus');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
contentArea.summernote(configuration);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
deinit: function(settings, contentAreas) {
|
|
58
|
+
contentAreas.filter('.active').each(function() {
|
|
59
|
+
var contentArea = $(this);
|
|
60
|
+
contentArea.summernote('destroy');
|
|
61
|
+
contentArea
|
|
62
|
+
.removeClass('active')
|
|
63
|
+
.removeAttr('id')
|
|
64
|
+
.removeAttr('style')
|
|
65
|
+
.removeAttr('spellcheck')
|
|
66
|
+
;
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
initialContent: '<p>Lorem ipsum dolores</p>',
|
|
71
|
+
};
|
|
72
|
+
})(jQuery);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
(function($) {
|
|
2
|
+
|
|
3
|
+
// tinyMCE snapshots the target element's attributes when an inline editor is
|
|
4
|
+
// created and restores them on remove(), so this has to run *after* remove()
|
|
5
|
+
// to keep the grid editor's own class and tinyMCE's leftovers off the element.
|
|
6
|
+
function cleanUp(contentArea) {
|
|
7
|
+
contentArea
|
|
8
|
+
.removeClass('active')
|
|
9
|
+
.removeClass('ge-rte-active')
|
|
10
|
+
.removeAttr('id')
|
|
11
|
+
.removeAttr('style')
|
|
12
|
+
.removeAttr('spellcheck')
|
|
13
|
+
.removeAttr('contenteditable')
|
|
14
|
+
.removeAttr('data-mce-style')
|
|
15
|
+
;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
$.fn.gridEditor.RTEs.tinymce = {
|
|
19
|
+
|
|
20
|
+
init: function(settings, contentAreas) {
|
|
21
|
+
|
|
22
|
+
if (!window.tinymce) {
|
|
23
|
+
console.error($.fn.gridEditor.t(settings, 'error.tinymce_missing'));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var self = this;
|
|
28
|
+
var userConfig = (settings.tinymce && settings.tinymce.config) ? settings.tinymce.config : {};
|
|
29
|
+
|
|
30
|
+
contentAreas.each(function() {
|
|
31
|
+
var contentArea = $(this);
|
|
32
|
+
if (contentArea.hasClass('active')) { return; }
|
|
33
|
+
|
|
34
|
+
if (contentArea.html() == self.initialContent) {
|
|
35
|
+
contentArea.html('');
|
|
36
|
+
}
|
|
37
|
+
contentArea.addClass('active');
|
|
38
|
+
|
|
39
|
+
var configuration = $.extend({
|
|
40
|
+
// tinyMCE's own "Upgrade" badge in the menubar. Off by
|
|
41
|
+
// default because an inline editor here is a column of
|
|
42
|
+
// someone's page, not tinyMCE's own interface; a host that
|
|
43
|
+
// wants it back passes promotion: true.
|
|
44
|
+
promotion: false,
|
|
45
|
+
}, userConfig, {
|
|
46
|
+
target: this,
|
|
47
|
+
inline: true,
|
|
48
|
+
init_instance_callback: function(editor) {
|
|
49
|
+
// deinit ran before this init finished, so tear it down again
|
|
50
|
+
if (contentArea.data('ge-tinymce-pending-remove')) {
|
|
51
|
+
contentArea.removeData('ge-tinymce-pending-remove');
|
|
52
|
+
editor.remove();
|
|
53
|
+
// deinit already ran and cannot clean up after this
|
|
54
|
+
// late remove(), so do it here instead
|
|
55
|
+
cleanUp(contentArea);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
contentArea.data('ge-tinymce', editor);
|
|
60
|
+
|
|
61
|
+
// The editor rewrote what is inside this content area
|
|
62
|
+
// while it took it over, so whatever the grid editor
|
|
63
|
+
// had in there is gone. Saying so lets it put its own
|
|
64
|
+
// furniture back (see RTE_READY in the core).
|
|
65
|
+
contentArea.trigger('ge-rte-ready');
|
|
66
|
+
|
|
67
|
+
// The inline toolbar is laid out against the element's
|
|
68
|
+
// geometry at the moment tinyMCE draws it, and an
|
|
69
|
+
// element that has just appeared - a tab pane, an
|
|
70
|
+
// accordion body, a column whose width is still
|
|
71
|
+
// settling - may not have its own width yet. Asking
|
|
72
|
+
// for the ui again once the browser has laid the frame
|
|
73
|
+
// out measures it as it now is, instead of leaving a
|
|
74
|
+
// toolbar wrapped into a narrow column.
|
|
75
|
+
window.requestAnimationFrame(function() {
|
|
76
|
+
if (editor.removed || !editor.ui || !editor.ui.show) { return; }
|
|
77
|
+
|
|
78
|
+
editor.ui.show();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// And again whenever the user comes back to this
|
|
82
|
+
// editor: by then the element may have been resized,
|
|
83
|
+
// hidden and shown again - a tab switched away from
|
|
84
|
+
// and back, a column made narrower - and the toolbar
|
|
85
|
+
// is only ever as right as its last measurement.
|
|
86
|
+
editor.on('focus', function() {
|
|
87
|
+
window.requestAnimationFrame(function() {
|
|
88
|
+
if (editor.removed || !editor.ui || !editor.ui.show) { return; }
|
|
89
|
+
|
|
90
|
+
editor.ui.show();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Bring focus to text field
|
|
95
|
+
editor.focus();
|
|
96
|
+
|
|
97
|
+
// Call the original callbacks, if any were passed in the config
|
|
98
|
+
if (userConfig.init_instance_callback) {
|
|
99
|
+
userConfig.init_instance_callback.call(this, editor);
|
|
100
|
+
}
|
|
101
|
+
// 'oninit' is the pre-6 name, honoured so existing configs keep working
|
|
102
|
+
if (userConfig.oninit) {
|
|
103
|
+
userConfig.oninit.call(this, editor);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// We always edit the element we were handed, so a selector in the
|
|
108
|
+
// user config would only fight with target
|
|
109
|
+
delete configuration.selector;
|
|
110
|
+
|
|
111
|
+
window.tinymce.init(configuration);
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
deinit: function(settings, contentAreas) {
|
|
116
|
+
contentAreas.filter('.active').each(function() {
|
|
117
|
+
var contentArea = $(this);
|
|
118
|
+
var editor = contentArea.data('ge-tinymce');
|
|
119
|
+
|
|
120
|
+
if (editor) {
|
|
121
|
+
contentArea.removeData('ge-tinymce');
|
|
122
|
+
editor.remove();
|
|
123
|
+
} else {
|
|
124
|
+
// tinymce.init() is asynchronous, so the editor may not exist
|
|
125
|
+
// yet. Leave a note for init_instance_callback to remove it.
|
|
126
|
+
contentArea.data('ge-tinymce-pending-remove', true);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
cleanUp(contentArea);
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
initialContent: '<p>Lorem ipsum dolores</p>',
|
|
134
|
+
};
|
|
135
|
+
})(jQuery);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spanish strings for grid-editor.
|
|
3
|
+
*
|
|
4
|
+
* Load after the plugin and select with `locale: 'es'`:
|
|
5
|
+
*
|
|
6
|
+
* <script src="dist/jquery.grideditor.min.js"></script>
|
|
7
|
+
* <script src="dist/locales/grideditor.es.js"></script>
|
|
8
|
+
* <script>$('#myGrid').gridEditor({ locale: 'es' });</script>
|
|
9
|
+
*
|
|
10
|
+
* This is one of the two locales grid-editor ships, so it is held to complete
|
|
11
|
+
* coverage of `$.fn.gridEditor.locales.en`: test/locales.js fails when a key
|
|
12
|
+
* is missing. Add the Spanish string in the same change that adds the English
|
|
13
|
+
* one, and keep the wording short enough to fit the tooltip or the dropdown
|
|
14
|
+
* item it appears in.
|
|
15
|
+
*/
|
|
16
|
+
(function($) {
|
|
17
|
+
|
|
18
|
+
$.fn.gridEditor.locales.es = {
|
|
19
|
+
'tool.move': 'Mover',
|
|
20
|
+
'tool.settings': 'Configuración',
|
|
21
|
+
'tool.add_row': 'Añadir fila',
|
|
22
|
+
'tool.add_column': 'Añadir columna\n(mantén pulsado para elegir el ancho)',
|
|
23
|
+
'tool.column_size': '{size} de 12',
|
|
24
|
+
'tool.delete_row': 'Borrar fila',
|
|
25
|
+
'tool.delete_column': 'Borrar columna',
|
|
26
|
+
'tool.delete_element': 'Borrar elemento',
|
|
27
|
+
'tool.delete_container': 'Borrar contenedor',
|
|
28
|
+
'tool.delete_pane': 'Borrar panel',
|
|
29
|
+
'tool.rename': 'Haz doble clic para renombrar',
|
|
30
|
+
'tool.toggle_popup': 'Plegar esta ventana mientras editas',
|
|
31
|
+
'tool.element_info': 'Elemento: {name}',
|
|
32
|
+
'tool.column_narrower': 'Estrechar columna\n(Mayús para el mínimo)',
|
|
33
|
+
'tool.column_wider': 'Ensanchar columna\n(Mayús para el máximo)',
|
|
34
|
+
'tool.indent_decrease': 'Reducir sangría\n(Mayús para quitarla)',
|
|
35
|
+
'tool.indent_increase': 'Aumentar sangría\n(Mayús para el máximo)',
|
|
36
|
+
'tool.edit_source': 'Editar el código fuente',
|
|
37
|
+
'tool.preview': 'Vista previa',
|
|
38
|
+
'tool.id_placeholder': 'id',
|
|
39
|
+
'tool.id_title': 'Asignar un identificador único',
|
|
40
|
+
'tool.classes_placeholder': 'clases',
|
|
41
|
+
'tool.classes_title': 'Clases css, separadas por espacios',
|
|
42
|
+
'tool.toggle_class': 'Aplicar o quitar el estilo "{label}"',
|
|
43
|
+
'row.add': 'Añadir fila {layout}',
|
|
44
|
+
'container.add_tabs': 'Pestañas',
|
|
45
|
+
'container.add_tab': 'Añadir pestaña',
|
|
46
|
+
'container.tab_label': 'Pestaña {number}',
|
|
47
|
+
'container.add_accordion': 'Acordeón',
|
|
48
|
+
'container.add_accordion_item': 'Añadir apartado',
|
|
49
|
+
'container.accordion_label': 'Apartado {number}',
|
|
50
|
+
'container.add_popup': 'Ventana emergente',
|
|
51
|
+
'container.popup_title': 'Título',
|
|
52
|
+
'container.popup_trigger': 'Abrir',
|
|
53
|
+
'confirm.title': 'Confirmar',
|
|
54
|
+
'confirm.ok': 'Borrar',
|
|
55
|
+
'confirm.cancel': 'Cancelar',
|
|
56
|
+
'confirm.delete_row': '¿Seguro que quieres borrar esta fila?',
|
|
57
|
+
'confirm.delete_column': '¿Seguro que quieres borrar esta columna?',
|
|
58
|
+
'confirm.delete_element': '¿Seguro que quieres borrar este elemento?',
|
|
59
|
+
'confirm.delete_container': '¿Seguro que quieres borrar este contenedor y todo su contenido?',
|
|
60
|
+
'confirm.delete_tab': '¿Seguro que quieres borrar esta pestaña y todo su contenido?',
|
|
61
|
+
'confirm.delete_accordion_item': '¿Seguro que quieres borrar este apartado y todo su contenido?',
|
|
62
|
+
'view.all': 'Todos los tamaños',
|
|
63
|
+
'view.xs': 'Móvil',
|
|
64
|
+
'view.sm': 'Tablet',
|
|
65
|
+
'view.md': 'Escritorio pequeño',
|
|
66
|
+
'view.lg': 'Escritorio',
|
|
67
|
+
'view.xl': 'Escritorio grande',
|
|
68
|
+
'view.xxl': 'Pantalla panorámica',
|
|
69
|
+
'error.tinymce_missing': '¡tinyMCE no está disponible! Asegúrate de haber cargado el archivo js de tinyMCE.',
|
|
70
|
+
'error.ckeditor_missing': '¡CKEditor no está disponible! Asegúrate de haber cargado los archivos js de ckeditor y del adaptador de jQuery.',
|
|
71
|
+
'error.summernote_missing': '¡Summernote no está disponible! Asegúrate de haber cargado el archivo js de Summernote.',
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
})(jQuery);
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accordions for grid-editor.
|
|
3
|
+
*
|
|
4
|
+
* A container plugin: load this file after the editor and the toolbar offers
|
|
5
|
+
* an accordion. What it can ask the editor for is the handle its factory is
|
|
6
|
+
* called with, described in docs/plugins.md.
|
|
7
|
+
*
|
|
8
|
+
* <script src="dist/jquery.grideditor.min.js"></script>
|
|
9
|
+
* <script src="dist/plugins/grideditor.accordion.min.js"></script>
|
|
10
|
+
*/
|
|
11
|
+
(function($) {
|
|
12
|
+
|
|
13
|
+
$.extend($.fn.gridEditor.locales.en, {
|
|
14
|
+
'container.add_accordion': 'Accordion',
|
|
15
|
+
'container.add_accordion_item': 'Add item',
|
|
16
|
+
'container.accordion_label': 'Item {number}',
|
|
17
|
+
'confirm.delete_accordion_item': 'Delete this item and everything in it?',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
$.fn.gridEditor.containers.accordion = function(ge) {
|
|
21
|
+
|
|
22
|
+
function staysOpen(container, ignore) {
|
|
23
|
+
var items = container.find('> .accordion > .accordion-item > .accordion-collapse');
|
|
24
|
+
|
|
25
|
+
if (ignore) { items = items.not(ignore.find('> .accordion-collapse')); }
|
|
26
|
+
|
|
27
|
+
return items.length > 0 && !items.filter('[data-bs-parent]').length;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function addAccordionItemTo(container, options) {
|
|
31
|
+
options = options || {};
|
|
32
|
+
|
|
33
|
+
var accordion = container.find('> .accordion');
|
|
34
|
+
var id = ge.containerId('acc-item');
|
|
35
|
+
var number = accordion.children('.accordion-item').length + 1;
|
|
36
|
+
var open = options.open === undefined ? number === 1 : !!options.open;
|
|
37
|
+
var stayOpen = options.stay_open === undefined ? staysOpen(container) : !!options.stay_open;
|
|
38
|
+
|
|
39
|
+
var item = $('<div class="accordion-item ge-accordion-item" />').appendTo(accordion);
|
|
40
|
+
|
|
41
|
+
$('<h2 class="accordion-header" />')
|
|
42
|
+
.append($('<button class="accordion-button" type="button" data-bs-toggle="collapse" />')
|
|
43
|
+
.attr('data-bs-target', '#' + id)
|
|
44
|
+
.attr('aria-expanded', open ? 'true' : 'false')
|
|
45
|
+
.toggleClass('collapsed', !open)
|
|
46
|
+
.append($('<span class="ge-pane-label" />')
|
|
47
|
+
.text(options.label || ge.t('container.accordion_label', { number: number }))))
|
|
48
|
+
.appendTo(item)
|
|
49
|
+
;
|
|
50
|
+
|
|
51
|
+
var collapse = $('<div class="accordion-collapse collapse" />')
|
|
52
|
+
.attr('id', id)
|
|
53
|
+
.attr('data-ge-open', open ? 'true' : 'false')
|
|
54
|
+
.toggleClass('show', open)
|
|
55
|
+
.appendTo(item)
|
|
56
|
+
;
|
|
57
|
+
|
|
58
|
+
if (!stayOpen) { collapse.attr('data-bs-parent', '#' + accordion.attr('id')); }
|
|
59
|
+
|
|
60
|
+
return $('<div class="accordion-body" />').append(ge.defaultRegion()).appendTo(collapse);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function toggleAccordionItem(container, item) {
|
|
64
|
+
var collapse = item.find('> .accordion-collapse');
|
|
65
|
+
var opening = collapse.attr('data-ge-open') !== 'true';
|
|
66
|
+
|
|
67
|
+
if (opening && !staysOpen(container)) {
|
|
68
|
+
container.find('> .accordion > .accordion-item').not(item).each(function() {
|
|
69
|
+
setAccordionItemOpen($(this), false);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setAccordionItemOpen(item, opening);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function setAccordionItemOpen(item, open) {
|
|
77
|
+
item.find('> .accordion-collapse')
|
|
78
|
+
.attr('data-ge-open', open ? 'true' : 'false')
|
|
79
|
+
.toggleClass('show', open)
|
|
80
|
+
;
|
|
81
|
+
item.find('> .accordion-header .accordion-button')
|
|
82
|
+
.toggleClass('collapsed', !open)
|
|
83
|
+
.attr('aria-expanded', open ? 'true' : 'false')
|
|
84
|
+
;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function reparentAccordionItem(container, item) {
|
|
88
|
+
var accordion = item.closest('.accordion');
|
|
89
|
+
var collapse = item.find('> .accordion-collapse');
|
|
90
|
+
|
|
91
|
+
// The item that just arrived still carries the parent it had
|
|
92
|
+
// where it came from, so it is not asked what this accordion does
|
|
93
|
+
if (staysOpen(container, item)) {
|
|
94
|
+
collapse.removeAttr('data-bs-parent');
|
|
95
|
+
} else {
|
|
96
|
+
collapse.attr('data-bs-parent', '#' + accordion.attr('id'));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
labelKey: 'container.add_accordion',
|
|
102
|
+
addPaneKey: 'container.add_accordion_item',
|
|
103
|
+
paneKind: 'accordion-item',
|
|
104
|
+
|
|
105
|
+
create: function(options) {
|
|
106
|
+
var container = $('<div />').attr('data-ge-container', 'accordion');
|
|
107
|
+
var labels = options.labels || [];
|
|
108
|
+
var count = options.items || labels.length || 2;
|
|
109
|
+
|
|
110
|
+
$('<div class="accordion" />')
|
|
111
|
+
.attr('id', ge.containerId('accordion'))
|
|
112
|
+
.appendTo(container)
|
|
113
|
+
;
|
|
114
|
+
|
|
115
|
+
for (var i = 0; i < count; i++) {
|
|
116
|
+
addAccordionItemTo(container, {
|
|
117
|
+
label: labels[i],
|
|
118
|
+
open: i === 0,
|
|
119
|
+
stay_open: options.stay_open,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return container;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
addPane: addAccordionItemTo,
|
|
127
|
+
|
|
128
|
+
mark: function(container) {
|
|
129
|
+
container.find('> .accordion > .accordion-item').each(function() {
|
|
130
|
+
var item = $(this).addClass('ge-accordion-item');
|
|
131
|
+
var collapse = item.find('> .accordion-collapse');
|
|
132
|
+
|
|
133
|
+
// What the author wanted, before Bootstrap's own
|
|
134
|
+
// toggles get a chance to change it while editing.
|
|
135
|
+
// Every item is shown while editing (the stylesheet
|
|
136
|
+
// does that), so this is the only record of it.
|
|
137
|
+
if (collapse.attr('data-ge-open') === undefined) {
|
|
138
|
+
collapse.attr('data-ge-open', collapse.hasClass('show') ? 'true' : 'false');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
var button = item.find('> .accordion-header .accordion-button');
|
|
142
|
+
|
|
143
|
+
// Bootstrap's collapse stays out of it, and the editor
|
|
144
|
+
// answers the click itself
|
|
145
|
+
ge.suspendToggles(button);
|
|
146
|
+
ge.makeLabelEditable(ge.labelIn(button));
|
|
147
|
+
|
|
148
|
+
if (!button.data('ge-toggles')) {
|
|
149
|
+
button.data('ge-toggles', true).on('click', function(e) {
|
|
150
|
+
if (ge.labelIn(button).attr('contenteditable') === 'true') { return; }
|
|
151
|
+
|
|
152
|
+
e.preventDefault();
|
|
153
|
+
toggleAccordionItem(container, item);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (item.find('> .ge-tools-drawer').length) { return; }
|
|
158
|
+
|
|
159
|
+
ge.createPaneControls(item, 'accordion-item', ge.settings.accordion_tools,
|
|
160
|
+
ge.t('confirm.delete_accordion_item'), function(removed) {
|
|
161
|
+
item.slideUp(200, removed);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
unmark: function(container) {
|
|
167
|
+
ge.resumeToggles(container);
|
|
168
|
+
|
|
169
|
+
// What the ge.canvas was showing is what the page ships
|
|
170
|
+
container.find('> .accordion > .accordion-item').each(function() {
|
|
171
|
+
setAccordionItemOpen($(this),
|
|
172
|
+
$(this).find('> .accordion-collapse').attr('data-ge-open') === 'true');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
container.find('.ge-accordion-item').removeClass('ge-accordion-item');
|
|
176
|
+
ge.unwrapLabels(container);
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
afterPaneMove: function(container, item) {
|
|
180
|
+
reparentAccordionItem(container, item);
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
})(jQuery);
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element level controls for grid-editor.
|
|
3
|
+
*
|
|
4
|
+
* A feature plugin: load this file after the editor and the nodes a host
|
|
5
|
+
* marked inside a content area become elements - one movable, deletable thing
|
|
6
|
+
* each, instead of rich text. What it can ask the editor for is the handle its
|
|
7
|
+
* factory is called with, described in docs/plugins.md.
|
|
8
|
+
*
|
|
9
|
+
* <script src="dist/jquery.grideditor.min.js"></script>
|
|
10
|
+
* <script src="dist/plugins/grideditor.elements.min.js"></script>
|
|
11
|
+
*/
|
|
12
|
+
(function($) {
|
|
13
|
+
|
|
14
|
+
$.extend($.fn.gridEditor.locales.en, {
|
|
15
|
+
'tool.delete_element': 'Remove element',
|
|
16
|
+
'tool.element_info': 'Element: {name}',
|
|
17
|
+
'confirm.delete_element': 'Delete element?',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
$.fn.gridEditor.features.elements = function(ge) {
|
|
21
|
+
|
|
22
|
+
function elementsEnabled() {
|
|
23
|
+
if (ge.settings.elements.enabled !== 'auto') { return !!ge.settings.elements.enabled; }
|
|
24
|
+
|
|
25
|
+
return ge.settings.elements.auto || ge.canvas.find(ge.settings.elements.selector).length > 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function elementsIn(contentArea) {
|
|
29
|
+
return ge.settings.elements.auto
|
|
30
|
+
? contentArea.children()
|
|
31
|
+
: contentArea.children(ge.settings.elements.selector);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function markElements() {
|
|
35
|
+
if (!elementsEnabled()) { return; }
|
|
36
|
+
|
|
37
|
+
ge.canvas.find('.ge-content').each(function() {
|
|
38
|
+
elementsIn($(this)).each(function() {
|
|
39
|
+
var element = $(this).addClass('ge-element').attr('contenteditable', 'false');
|
|
40
|
+
|
|
41
|
+
if (element.find('> .ge-tools-drawer').length) { return; }
|
|
42
|
+
|
|
43
|
+
createElementControls(element);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function unmarkElements() {
|
|
49
|
+
ge.canvas.find('.ge-element').each(function() {
|
|
50
|
+
var element = $(this).removeClass('ge-element').removeAttr('contenteditable');
|
|
51
|
+
|
|
52
|
+
// A host element that had no class of its own should not come
|
|
53
|
+
// back from getHtml carrying an empty one
|
|
54
|
+
if (!element.attr('class')) { element.removeAttr('class'); }
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function createElementControls(element) {
|
|
59
|
+
// data-mce-bogus="all" is how tinyMCE is told that a node is the
|
|
60
|
+
// editor's furniture rather than content: it leaves the subtree
|
|
61
|
+
// alone and keeps it out of what it serializes. Without it the
|
|
62
|
+
// drawer's tools are inline elements with no text, which is
|
|
63
|
+
// exactly what its cleanup removes, so an element inside an open
|
|
64
|
+
// editor would lose its move and delete tools.
|
|
65
|
+
var drawer = $('<div class="ge-tools-drawer ge-element-drawer" />')
|
|
66
|
+
.attr('data-mce-bogus', 'all')
|
|
67
|
+
.prependTo(element)
|
|
68
|
+
;
|
|
69
|
+
|
|
70
|
+
ge.createMoveTool(drawer);
|
|
71
|
+
ge.createTool(drawer, ge.t('tool.element_info', { name: elementName(element) }),
|
|
72
|
+
'ge-element-info', 'bi bi-info-circle');
|
|
73
|
+
ge.addSettingsTool(drawer, element, ge.settings.element_classes);
|
|
74
|
+
|
|
75
|
+
ge.settings.element_tools.forEach(function(hostTool) {
|
|
76
|
+
ge.createTool(drawer, hostTool.title || '', hostTool.className || '',
|
|
77
|
+
hostTool.iconClass || 'bi bi-wrench', hostTool.on);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
ge.createTool(drawer, ge.t('tool.delete_element'), 'ge-delete-element', 'bi bi-trash', function() {
|
|
81
|
+
ge.deleteNode('element', element, ge.t('confirm.delete_element'), function(removed) {
|
|
82
|
+
element.animate({ opacity: 'hide', height: 'hide' }, 300, removed);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function elementName(element) {
|
|
88
|
+
var type = element.attr('data-ge-element');
|
|
89
|
+
var label = element.attr('data-ge-label');
|
|
90
|
+
|
|
91
|
+
if (label && type) { return label + ' (' + type + ')'; }
|
|
92
|
+
|
|
93
|
+
return label || type || element[0].tagName.toLowerCase();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function apiCreateElement(content, options) {
|
|
97
|
+
options = options || {};
|
|
98
|
+
|
|
99
|
+
var element = $('<div class="ge-element" />')
|
|
100
|
+
.attr('data-ge-element', options.type || 'element')
|
|
101
|
+
.append(content)
|
|
102
|
+
;
|
|
103
|
+
if (options.label !== undefined) {
|
|
104
|
+
element.attr('data-ge-label', options.label);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return ge.place(element, 'element', options);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
methods: {
|
|
112
|
+
createElement: apiCreateElement,
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/** A node this plugin marked is an element, whatever else it is. */
|
|
116
|
+
kindOf: function(node) {
|
|
117
|
+
return node.hasClass('ge-element') ? 'element' : null;
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
onInit: markElements,
|
|
121
|
+
onDeinit: unmarkElements,
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* An editor rewrites the content area as it takes it over, drawers
|
|
125
|
+
* included, so they go back in when it says it is ready.
|
|
126
|
+
*/
|
|
127
|
+
onContentReady: markElements,
|
|
128
|
+
|
|
129
|
+
/** Elements move within a content area and between them. */
|
|
130
|
+
onSortable: function(shared) {
|
|
131
|
+
if (!elementsEnabled()) { return; }
|
|
132
|
+
|
|
133
|
+
ge.canvas.find('.ge-content').sortable($.extend({
|
|
134
|
+
items: '> .ge-element',
|
|
135
|
+
connectWith: '.ge-canvas .ge-content',
|
|
136
|
+
}, shared, ge.settings.sortable_options));
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
})(jQuery);
|