@themarioga/grid-editor 5.1.1 → 5.1.2
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 +20 -0
- package/README.md +1 -0
- package/dist/grideditor.css +3 -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 +13 -3
- package/dist/jquery.grideditor.min.js +1 -1
- package/dist/jquery.grideditor.min.js.map +1 -1
- package/docs/plugins.md +31 -0
- package/example/attributes.html +362 -0
- package/example/index.html +8 -0
- package/package.json +1 -1
- package/src/js/jquery.grideditor.js +5 -3
- package/src/js/jquery.grideditor.tinymce.js +8 -0
- package/src/less/grideditor.less +4 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
5
|
+
|
|
6
|
+
<!-- CSS dependencies -->
|
|
7
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
|
|
8
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.1/font/bootstrap-icons.css">
|
|
9
|
+
|
|
10
|
+
<!-- Javascript dependencies -->
|
|
11
|
+
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
|
|
12
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
|
|
13
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tinymce/6.8.6/tinymce.min.js"></script>
|
|
14
|
+
|
|
15
|
+
<!-- Grid editor CSS -->
|
|
16
|
+
<link rel="stylesheet" type="text/css" href="../dist/grideditor.css" />
|
|
17
|
+
<!-- Grid editor javascript -->
|
|
18
|
+
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
|
|
19
|
+
<script src="../dist/jquery.grideditor.min.js"></script>
|
|
20
|
+
<script src="../dist/plugins/grideditor.card.min.js"></script>
|
|
21
|
+
<script src="../dist/plugins/grideditor.elements.min.js"></script>
|
|
22
|
+
|
|
23
|
+
<style>
|
|
24
|
+
/* The drawer tool of a node that has an animation set. Under the
|
|
25
|
+
canvas's id, to outweigh the colour the editor gives each kind of
|
|
26
|
+
drawer's tools */
|
|
27
|
+
#myGrid .ge-tools-drawer > a.my-animation-tool.my-animation-set {
|
|
28
|
+
color: #d63384;
|
|
29
|
+
}
|
|
30
|
+
</style>
|
|
31
|
+
|
|
32
|
+
<!-- A plugin of this page's own: a drawer tool that opens a modal, and
|
|
33
|
+
what the user chooses there saved as an attribute on the node. -->
|
|
34
|
+
<script id="animation-plugin">
|
|
35
|
+
(function($) {
|
|
36
|
+
|
|
37
|
+
$.extend($.fn.gridEditor.locales.en, {
|
|
38
|
+
'animation.tool': 'Animation',
|
|
39
|
+
'animation.title': 'Animate on scroll',
|
|
40
|
+
'animation.effect': 'Effect',
|
|
41
|
+
'animation.none': 'None',
|
|
42
|
+
'animation.fade': 'Fade in',
|
|
43
|
+
'animation.slide': 'Slide up',
|
|
44
|
+
'animation.zoom': 'Zoom in',
|
|
45
|
+
'animation.duration': 'Duration (ms)',
|
|
46
|
+
'animation.delay': 'Delay (ms)',
|
|
47
|
+
'animation.once': 'Only the first time it scrolls into view',
|
|
48
|
+
'animation.cancel': 'Cancel',
|
|
49
|
+
'animation.save': 'Save',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Not data-ge-*: getPlainHtml takes those off, and this has to
|
|
53
|
+
// reach the published page
|
|
54
|
+
var ATTRIBUTE = 'data-animation';
|
|
55
|
+
var EFFECTS = ['fade', 'slide', 'zoom'];
|
|
56
|
+
|
|
57
|
+
$.fn.gridEditor.features.animation = function(ge) {
|
|
58
|
+
|
|
59
|
+
var modal = null;
|
|
60
|
+
|
|
61
|
+
function read(node) {
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(node.attr(ATTRIBUTE) || 'null');
|
|
64
|
+
} catch (error) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* An element lives inside a content area, and while a rich
|
|
71
|
+
* text editor is open there, a change it does not know about
|
|
72
|
+
* is one its undo would take back: undoing the next thing the
|
|
73
|
+
* user types restores a snapshot from before the attribute.
|
|
74
|
+
* Made through tinyMCE's undo manager, it is a step of its
|
|
75
|
+
* own, which undo and redo treat like any other.
|
|
76
|
+
*/
|
|
77
|
+
function write(node, value) {
|
|
78
|
+
var apply = function() {
|
|
79
|
+
if (value) {
|
|
80
|
+
node.attr(ATTRIBUTE, JSON.stringify(value));
|
|
81
|
+
} else {
|
|
82
|
+
node.removeAttr(ATTRIBUTE);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
var editor = editorAround(node);
|
|
86
|
+
|
|
87
|
+
if (editor) {
|
|
88
|
+
editor.undoManager.transact(apply);
|
|
89
|
+
} else {
|
|
90
|
+
apply();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The tinyMCE editor open on the content area around node, if there is one. */
|
|
95
|
+
function editorAround(node) {
|
|
96
|
+
var area = node.closest('.ge-content');
|
|
97
|
+
if (!area.length || !window.tinymce) { return null; }
|
|
98
|
+
|
|
99
|
+
return window.tinymce.get().filter(function(editor) {
|
|
100
|
+
return editor.getBody && editor.getBody() === area[0];
|
|
101
|
+
})[0] || null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Built once, outside the canvas, so it is never part of getHtml. */
|
|
105
|
+
function dialog() {
|
|
106
|
+
if (modal) { return modal; }
|
|
107
|
+
|
|
108
|
+
var effects = $('<select class="form-select" name="effect" />')
|
|
109
|
+
.append($('<option value="" />').text(ge.t('animation.none')));
|
|
110
|
+
EFFECTS.forEach(function(effect) {
|
|
111
|
+
effects.append($('<option />').val(effect).text(ge.t('animation.' + effect)));
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
var field = function(label, input) {
|
|
115
|
+
return $('<div class="mb-3" />')
|
|
116
|
+
.append($('<label class="form-label" />').text(ge.t(label)))
|
|
117
|
+
.append(input);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
modal = $(
|
|
121
|
+
'<div class="modal fade my-animation-modal" tabindex="-1" aria-hidden="true">' +
|
|
122
|
+
'<div class="modal-dialog modal-dialog-centered"><form class="modal-content">' +
|
|
123
|
+
'<div class="modal-header">' +
|
|
124
|
+
'<h5 class="modal-title"></h5>' +
|
|
125
|
+
'<button type="button" class="btn-close" data-bs-dismiss="modal"></button>' +
|
|
126
|
+
'</div>' +
|
|
127
|
+
'<div class="modal-body"></div>' +
|
|
128
|
+
'<div class="modal-footer">' +
|
|
129
|
+
'<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"></button>' +
|
|
130
|
+
'<button type="submit" class="btn btn-primary"></button>' +
|
|
131
|
+
'</div>' +
|
|
132
|
+
'</form></div>' +
|
|
133
|
+
'</div>'
|
|
134
|
+
).appendTo('body');
|
|
135
|
+
|
|
136
|
+
modal.find('.modal-title').text(ge.t('animation.title'));
|
|
137
|
+
modal.find('.modal-footer .btn-secondary').text(ge.t('animation.cancel'));
|
|
138
|
+
modal.find('.modal-footer .btn-primary').text(ge.t('animation.save'));
|
|
139
|
+
modal.find('.modal-body')
|
|
140
|
+
.append(field('animation.effect', effects))
|
|
141
|
+
.append(field('animation.duration', '<input class="form-control" type="number" name="duration" min="0" step="100">'))
|
|
142
|
+
.append(field('animation.delay', '<input class="form-control" type="number" name="delay" min="0" step="100">'))
|
|
143
|
+
.append($('<div class="form-check" />')
|
|
144
|
+
.append('<input class="form-check-input" type="checkbox" name="once" id="my-animation-once">')
|
|
145
|
+
.append($('<label class="form-check-label" for="my-animation-once" />').text(ge.t('animation.once'))));
|
|
146
|
+
|
|
147
|
+
return modal;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function open(node, kind, tool) {
|
|
151
|
+
var form = dialog().find('form');
|
|
152
|
+
var current = read(node) || {};
|
|
153
|
+
|
|
154
|
+
form.find('[name=effect]').val(current.effect || '');
|
|
155
|
+
form.find('[name=duration]').val(current.duration || 600);
|
|
156
|
+
form.find('[name=delay]').val(current.delay || 0);
|
|
157
|
+
form.find('[name=once]').prop('checked', current.once !== false);
|
|
158
|
+
|
|
159
|
+
form.off('submit').on('submit', function(e) {
|
|
160
|
+
e.preventDefault();
|
|
161
|
+
|
|
162
|
+
var effect = form.find('[name=effect]').val();
|
|
163
|
+
var value = effect ? {
|
|
164
|
+
effect: effect,
|
|
165
|
+
duration: parseInt(form.find('[name=duration]').val(), 10) || 0,
|
|
166
|
+
delay: parseInt(form.find('[name=delay]').val(), 10) || 0,
|
|
167
|
+
once: form.find('[name=once]').prop('checked'),
|
|
168
|
+
} : null;
|
|
169
|
+
|
|
170
|
+
var from = read(node);
|
|
171
|
+
write(node, value);
|
|
172
|
+
tool.toggleClass('my-animation-set', !!value);
|
|
173
|
+
|
|
174
|
+
// Announced like the editor's own changes, for a host
|
|
175
|
+
// that listens: grideditor:animation-change
|
|
176
|
+
ge.emit('animation-change', ge.payloadFor(kind, node, {
|
|
177
|
+
source: 'tool', from: from, to: value,
|
|
178
|
+
}));
|
|
179
|
+
|
|
180
|
+
window.bootstrap.Modal.getInstance(modal[0]).hide();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
window.bootstrap.Modal.getOrCreateInstance(modal[0]).show();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
// Every drawer that has a gear, and the kind of its node:
|
|
188
|
+
// a row, a column, an element, a container's type, a pane
|
|
189
|
+
drawerTools: function(drawer, node, kind) {
|
|
190
|
+
var animated = ['row', 'column', 'section', 'element'].indexOf(kind) !== -1 ||
|
|
191
|
+
!!$.fn.gridEditor.containers[kind];
|
|
192
|
+
if (!animated) { return; }
|
|
193
|
+
|
|
194
|
+
ge.createTool(drawer, ge.t('animation.tool'), 'my-animation-tool', 'bi bi-magic', function() {
|
|
195
|
+
open(node, kind, tool);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Drawers are built again on every init, so the tool
|
|
199
|
+
// reads the node rather than remembering anything
|
|
200
|
+
var tool = drawer.children('.my-animation-tool').last()
|
|
201
|
+
.toggleClass('my-animation-set', !!read(node));
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
})(jQuery);
|
|
207
|
+
</script>
|
|
208
|
+
|
|
209
|
+
<script>
|
|
210
|
+
$(function() {
|
|
211
|
+
$('#animation-source').text($('#animation-plugin').text().trim().replace(/^ {8}/gm, ''));
|
|
212
|
+
|
|
213
|
+
$('#myGrid').gridEditor({
|
|
214
|
+
new_row_layouts: [[12], [6, 6], [4, 4, 4]],
|
|
215
|
+
content_types: ['tinymce'],
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
$('#myGrid').on('grideditor:animation-change', function(e, payload) {
|
|
219
|
+
$('#log').text('The ' + payload.kind + ' now animates: ' +
|
|
220
|
+
(payload.to ? payload.to.effect : 'no'));
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
$('#output').on('click', function() {
|
|
224
|
+
$('#html').text($('#myGrid').gridEditor('getPlainHtml'));
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// The published page: the plain html, and a few lines of the
|
|
228
|
+
// host's own that read data-animation. Nothing of grid-editor.
|
|
229
|
+
$('#published').on('click', function() {
|
|
230
|
+
var html = $('#myGrid').gridEditor('getPlainHtml');
|
|
231
|
+
var page = window.open('', '_blank');
|
|
232
|
+
|
|
233
|
+
page.document.write(
|
|
234
|
+
'<!DOCTYPE html><html><head>' +
|
|
235
|
+
'<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">' +
|
|
236
|
+
'<style>' +
|
|
237
|
+
'[data-animation] { transition-property: opacity, transform; }' +
|
|
238
|
+
'.animate-waiting { opacity: 0; }' +
|
|
239
|
+
'.animate-waiting.animate-slide { transform: translateY(40px); }' +
|
|
240
|
+
'.animate-waiting.animate-zoom { transform: scale(.85); }' +
|
|
241
|
+
'body { padding-bottom: 60vh; }' +
|
|
242
|
+
'.scroll-hint { height: 100vh; }' +
|
|
243
|
+
'</style>' +
|
|
244
|
+
'</head><body>' +
|
|
245
|
+
'<div class="container scroll-hint d-flex align-items-center justify-content-center text-body-secondary">' +
|
|
246
|
+
'Scroll down: each node animates as it comes into view' +
|
|
247
|
+
'</div>' +
|
|
248
|
+
'<div class="container">' + html + '</div>' +
|
|
249
|
+
'<script>' + $('#runtime').text() + '<\/script>' +
|
|
250
|
+
'</body></html>'
|
|
251
|
+
);
|
|
252
|
+
page.document.close();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
</script>
|
|
256
|
+
|
|
257
|
+
<!-- What the published page runs: not grid-editor's, the host's -->
|
|
258
|
+
<script type="text/plain" id="runtime">
|
|
259
|
+
document.querySelectorAll('[data-animation]').forEach(function(node) {
|
|
260
|
+
var options = JSON.parse(node.getAttribute('data-animation'));
|
|
261
|
+
node.style.transitionDuration = options.duration + 'ms';
|
|
262
|
+
node.style.transitionDelay = options.delay + 'ms';
|
|
263
|
+
// Prefixed: Bootstrap has a .fade class of its own
|
|
264
|
+
node.classList.add('animate-waiting', 'animate-' + options.effect);
|
|
265
|
+
|
|
266
|
+
new IntersectionObserver(function(entries, observer) {
|
|
267
|
+
entries.forEach(function(entry) {
|
|
268
|
+
node.classList.toggle('animate-waiting', !entry.isIntersecting);
|
|
269
|
+
if (entry.isIntersecting && options.once) { observer.disconnect(); }
|
|
270
|
+
});
|
|
271
|
+
}, { threshold: 0.2 }).observe(node);
|
|
272
|
+
});
|
|
273
|
+
</script>
|
|
274
|
+
|
|
275
|
+
<title>Grid Editor example - a plugin with a modal</title>
|
|
276
|
+
</head>
|
|
277
|
+
<body>
|
|
278
|
+
<!-- Back to the demo index -->
|
|
279
|
+
<div class="container pt-3">
|
|
280
|
+
<a class="link-secondary" href="index.html"><i class="bi bi-arrow-left"></i> All demos</a>
|
|
281
|
+
</div>
|
|
282
|
+
|
|
283
|
+
<div class="container">
|
|
284
|
+
|
|
285
|
+
<div class="row my-3">
|
|
286
|
+
<div class="col-lg-9">
|
|
287
|
+
<h1 class="h4">A plugin with a modal</h1>
|
|
288
|
+
<p class="form-text">
|
|
289
|
+
The <i class="bi bi-magic"></i> tool in the drawer of a row,
|
|
290
|
+
a column, a card or an element is a plugin written on this
|
|
291
|
+
page. It opens a modal, and what you choose there is saved
|
|
292
|
+
on the node as a <code>data-animation</code> attribute, which
|
|
293
|
+
<code>getPlainHtml</code> keeps: only <code>data-ge-*</code>
|
|
294
|
+
attributes are the editor’s to take off. A node with
|
|
295
|
+
an animation has its tool highlighted.
|
|
296
|
+
</p>
|
|
297
|
+
<p class="form-text">
|
|
298
|
+
The quote is an element, inside text tinyMCE edits. Open the
|
|
299
|
+
editor there, set an animation on the quote, keep typing
|
|
300
|
+
and press <kbd>Ctrl</kbd>+<kbd>Z</kbd>: the text goes, the
|
|
301
|
+
animation stays, because the plugin wrote it through
|
|
302
|
+
tinyMCE’s undo manager. Written behind its back,
|
|
303
|
+
undoing the typing would undo the attribute with it.
|
|
304
|
+
</p>
|
|
305
|
+
<p class="form-text">
|
|
306
|
+
<em>Open as a published page</em> puts the plain html in a
|
|
307
|
+
page of its own, with a few lines that read the attribute
|
|
308
|
+
and animate each node as it scrolls into view.
|
|
309
|
+
<span id="log" class="fw-semibold"></span>
|
|
310
|
+
</p>
|
|
311
|
+
<button class="btn btn-sm btn-secondary" id="output" type="button">Show getPlainHtml</button>
|
|
312
|
+
<button class="btn btn-sm btn-primary" id="published" type="button">Open as a published page</button>
|
|
313
|
+
<pre id="html" class="form-text"></pre>
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<div id="myGrid">
|
|
318
|
+
<div class="row" data-animation='{"effect":"fade","duration":800,"delay":0,"once":true}'>
|
|
319
|
+
<div class="col-lg-12">
|
|
320
|
+
<h2>This row fades in</h2>
|
|
321
|
+
<p>It came with its animation already set, in the markup the editor was started on.</p>
|
|
322
|
+
</div>
|
|
323
|
+
</div>
|
|
324
|
+
<div class="row">
|
|
325
|
+
<div class="col-lg-6">
|
|
326
|
+
<p>Ordinary text, edited by tinyMCE.</p>
|
|
327
|
+
<blockquote class="blockquote" data-ge-element="quote" data-ge-label="Quote">
|
|
328
|
+
<p>An element: give it an animation while you edit the text around it.</p>
|
|
329
|
+
</blockquote>
|
|
330
|
+
<p>More text, after the element.</p>
|
|
331
|
+
</div>
|
|
332
|
+
<div class="col-lg-6">
|
|
333
|
+
<div data-ge-container="card">
|
|
334
|
+
<div class="card">
|
|
335
|
+
<div class="card-header">A card</div>
|
|
336
|
+
<div class="card-body">
|
|
337
|
+
<div class="row"><div class="col-lg-12">
|
|
338
|
+
<p>Containers take the tool too.</p>
|
|
339
|
+
</div></div>
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<div class="row my-4">
|
|
348
|
+
<div class="col-lg-10">
|
|
349
|
+
<h2 class="h5">The plugin, in full</h2>
|
|
350
|
+
<p class="form-text">
|
|
351
|
+
A feature plugin: <code>drawerTools</code> puts a tool in
|
|
352
|
+
every drawer that has a gear, and the kind says whose drawer
|
|
353
|
+
it is. See <a href="../docs/plugins.md">docs/plugins.md</a>.
|
|
354
|
+
</p>
|
|
355
|
+
<pre class="border rounded p-3 bg-light"><code id="animation-source"></code></pre>
|
|
356
|
+
</div>
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
</body>
|
|
362
|
+
</html>
|
package/example/index.html
CHANGED
|
@@ -61,6 +61,14 @@
|
|
|
61
61
|
<span class="text-body-secondary">The plugin model, with a container plugin written in the page itself</span>
|
|
62
62
|
</a>
|
|
63
63
|
|
|
64
|
+
<a class="list-group-item list-group-item-action" href="attributes.html">
|
|
65
|
+
<div class="d-flex justify-content-between">
|
|
66
|
+
<strong>A plugin with a modal</strong>
|
|
67
|
+
<code class="text-body-secondary">attributes.html</code>
|
|
68
|
+
</div>
|
|
69
|
+
<span class="text-body-secondary">A drawer tool that opens a modal and saves its settings on the node as an attribute, with tinyMCE’s undo in mind</span>
|
|
70
|
+
</a>
|
|
71
|
+
|
|
64
72
|
<a class="list-group-item list-group-item-action" href="containers.html">
|
|
65
73
|
<div class="d-flex justify-content-between">
|
|
66
74
|
<strong>Containers</strong>
|
package/package.json
CHANGED
|
@@ -894,7 +894,7 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
894
894
|
|
|
895
895
|
// Add row
|
|
896
896
|
addRowGroup = $('<div class="ge-addRowGroup btn-group" />').appendTo(wrapper);
|
|
897
|
-
addContainerGroup = $('<div class="ge-addContainerGroup btn-group" />');
|
|
897
|
+
addContainerGroup = $('<div class="ge-addContainerGroup btn-group ms-1" />');
|
|
898
898
|
$.each(settings.new_row_layouts, function(j, layout) {
|
|
899
899
|
var grouped = !Array.isArray(layout);
|
|
900
900
|
var btn = $('<a class="btn btn-sm btn-primary" />')
|
|
@@ -1040,9 +1040,11 @@ $.fn.gridEditor = function( optionsOrMethod ) {
|
|
|
1040
1040
|
;
|
|
1041
1041
|
|
|
1042
1042
|
// Floated right after the source and preview buttons, so it
|
|
1043
|
-
// stands to their left
|
|
1043
|
+
// stands to their left. Not a btn-group: its buttons show one at
|
|
1044
|
+
// a time, and a hidden one still counts as a neighbour to
|
|
1045
|
+
// Bootstrap, which squares off the corners they would share.
|
|
1044
1046
|
if (endItems.length) {
|
|
1045
|
-
$('<div class="
|
|
1047
|
+
$('<div class="pull-right ge-toolbar-end" />')
|
|
1046
1048
|
.append(endItems)
|
|
1047
1049
|
.appendTo(wrapper)
|
|
1048
1050
|
;
|
|
@@ -64,6 +64,14 @@
|
|
|
64
64
|
// furniture back (see RTE_READY in the core).
|
|
65
65
|
contentArea.trigger('ge-rte-ready');
|
|
66
66
|
|
|
67
|
+
// Undo and redo rewrite it too, from snapshots that
|
|
68
|
+
// leave out whatever is marked data-mce-bogus - an
|
|
69
|
+
// element's drawer among them - so it goes back in
|
|
70
|
+
// after each of those as well
|
|
71
|
+
editor.on('Undo Redo', function() {
|
|
72
|
+
contentArea.trigger('ge-rte-ready');
|
|
73
|
+
});
|
|
74
|
+
|
|
67
75
|
// The inline toolbar is laid out against the element's
|
|
68
76
|
// geometry at the moment tinyMCE draws it, and an
|
|
69
77
|
// element that has just appeared - a tab pane, an
|