Chris@0
|
1 /**
|
Chris@0
|
2 * @file
|
Chris@0
|
3 * CKEditor button and group configuration user interface.
|
Chris@0
|
4 */
|
Chris@0
|
5
|
Chris@17
|
6 (function($, Drupal, drupalSettings, _) {
|
Chris@0
|
7 Drupal.ckeditor = Drupal.ckeditor || {};
|
Chris@0
|
8
|
Chris@0
|
9 /**
|
Chris@0
|
10 * Sets config behaviour and creates config views for the CKEditor toolbar.
|
Chris@0
|
11 *
|
Chris@0
|
12 * @type {Drupal~behavior}
|
Chris@0
|
13 *
|
Chris@0
|
14 * @prop {Drupal~behaviorAttach} attach
|
Chris@0
|
15 * Attaches admin behaviour to the CKEditor buttons.
|
Chris@0
|
16 * @prop {Drupal~behaviorDetach} detach
|
Chris@0
|
17 * Detaches admin behaviour from the CKEditor buttons on 'unload'.
|
Chris@0
|
18 */
|
Chris@0
|
19 Drupal.behaviors.ckeditorAdmin = {
|
Chris@0
|
20 attach(context) {
|
Chris@0
|
21 // Process the CKEditor configuration fragment once.
|
Chris@17
|
22 const $configurationForm = $(context)
|
Chris@17
|
23 .find('.ckeditor-toolbar-configuration')
|
Chris@17
|
24 .once('ckeditor-configuration');
|
Chris@0
|
25 if ($configurationForm.length) {
|
Chris@0
|
26 const $textarea = $configurationForm
|
Chris@0
|
27 // Hide the textarea that contains the serialized representation of the
|
Chris@0
|
28 // CKEditor configuration.
|
Chris@0
|
29 .find('.js-form-item-editor-settings-toolbar-button-groups')
|
Chris@0
|
30 .hide()
|
Chris@0
|
31 // Return the textarea child node from this expression.
|
Chris@0
|
32 .find('textarea');
|
Chris@0
|
33
|
Chris@0
|
34 // The HTML for the CKEditor configuration is assembled on the server
|
Chris@0
|
35 // and sent to the client as a serialized DOM fragment.
|
Chris@0
|
36 $configurationForm.append(drupalSettings.ckeditor.toolbarAdmin);
|
Chris@0
|
37
|
Chris@0
|
38 // Create a configuration model.
|
Chris@14
|
39 Drupal.ckeditor.models.Model = new Drupal.ckeditor.Model({
|
Chris@0
|
40 $textarea,
|
Chris@0
|
41 activeEditorConfig: JSON.parse($textarea.val()),
|
Chris@0
|
42 hiddenEditorConfig: drupalSettings.ckeditor.hiddenCKEditorConfig,
|
Chris@0
|
43 });
|
Chris@0
|
44
|
Chris@0
|
45 // Create the configuration Views.
|
Chris@0
|
46 const viewDefaults = {
|
Chris@14
|
47 model: Drupal.ckeditor.models.Model,
|
Chris@0
|
48 el: $('.ckeditor-toolbar-configuration'),
|
Chris@0
|
49 };
|
Chris@0
|
50 Drupal.ckeditor.views = {
|
Chris@0
|
51 controller: new Drupal.ckeditor.ControllerView(viewDefaults),
|
Chris@0
|
52 visualView: new Drupal.ckeditor.VisualView(viewDefaults),
|
Chris@0
|
53 keyboardView: new Drupal.ckeditor.KeyboardView(viewDefaults),
|
Chris@0
|
54 auralView: new Drupal.ckeditor.AuralView(viewDefaults),
|
Chris@0
|
55 };
|
Chris@0
|
56 }
|
Chris@0
|
57 },
|
Chris@0
|
58 detach(context, settings, trigger) {
|
Chris@0
|
59 // Early-return if the trigger for detachment is something else than
|
Chris@0
|
60 // unload.
|
Chris@0
|
61 if (trigger !== 'unload') {
|
Chris@0
|
62 return;
|
Chris@0
|
63 }
|
Chris@0
|
64
|
Chris@0
|
65 // We're detaching because CKEditor as text editor has been disabled; this
|
Chris@0
|
66 // really means that all CKEditor toolbar buttons have been removed.
|
Chris@0
|
67 // Hence,all editor features will be removed, so any reactions from
|
Chris@0
|
68 // filters will be undone.
|
Chris@17
|
69 const $configurationForm = $(context)
|
Chris@17
|
70 .find('.ckeditor-toolbar-configuration')
|
Chris@17
|
71 .findOnce('ckeditor-configuration');
|
Chris@17
|
72 if (
|
Chris@17
|
73 $configurationForm.length &&
|
Chris@17
|
74 Drupal.ckeditor.models &&
|
Chris@17
|
75 Drupal.ckeditor.models.Model
|
Chris@17
|
76 ) {
|
Chris@0
|
77 const config = Drupal.ckeditor.models.Model.toJSON().activeEditorConfig;
|
Chris@0
|
78 const buttons = Drupal.ckeditor.views.controller.getButtonList(config);
|
Chris@17
|
79 const $activeToolbar = $('.ckeditor-toolbar-configuration').find(
|
Chris@17
|
80 '.ckeditor-toolbar-active',
|
Chris@17
|
81 );
|
Chris@0
|
82 for (let i = 0; i < buttons.length; i++) {
|
Chris@17
|
83 $activeToolbar.trigger('CKEditorToolbarChanged', [
|
Chris@17
|
84 'removed',
|
Chris@17
|
85 buttons[i],
|
Chris@17
|
86 ]);
|
Chris@0
|
87 }
|
Chris@0
|
88 }
|
Chris@0
|
89 },
|
Chris@0
|
90 };
|
Chris@0
|
91
|
Chris@0
|
92 /**
|
Chris@0
|
93 * CKEditor configuration UI methods of Backbone objects.
|
Chris@0
|
94 *
|
Chris@0
|
95 * @namespace
|
Chris@0
|
96 */
|
Chris@0
|
97 Drupal.ckeditor = {
|
Chris@0
|
98 /**
|
Chris@0
|
99 * A hash of View instances.
|
Chris@0
|
100 *
|
Chris@0
|
101 * @type {object}
|
Chris@0
|
102 */
|
Chris@0
|
103 views: {},
|
Chris@0
|
104
|
Chris@0
|
105 /**
|
Chris@0
|
106 * A hash of Model instances.
|
Chris@0
|
107 *
|
Chris@0
|
108 * @type {object}
|
Chris@0
|
109 */
|
Chris@0
|
110 models: {},
|
Chris@0
|
111
|
Chris@0
|
112 /**
|
Chris@0
|
113 * Translates changes in CKEditor config DOM structure to the config model.
|
Chris@0
|
114 *
|
Chris@0
|
115 * If the button is moved within an existing group, the DOM structure is
|
Chris@0
|
116 * simply translated to a configuration model. If the button is moved into a
|
Chris@0
|
117 * new group placeholder, then a process is launched to name that group
|
Chris@0
|
118 * before the button move is translated into configuration.
|
Chris@0
|
119 *
|
Chris@0
|
120 * @param {Backbone.View} view
|
Chris@0
|
121 * The Backbone View that invoked this function.
|
Chris@0
|
122 * @param {jQuery} $button
|
Chris@0
|
123 * A jQuery set that contains an li element that wraps a button element.
|
Chris@0
|
124 * @param {function} callback
|
Chris@0
|
125 * A callback to invoke after the button group naming modal dialog has
|
Chris@0
|
126 * been closed.
|
Chris@0
|
127 *
|
Chris@0
|
128 */
|
Chris@0
|
129 registerButtonMove(view, $button, callback) {
|
Chris@0
|
130 const $group = $button.closest('.ckeditor-toolbar-group');
|
Chris@0
|
131
|
Chris@0
|
132 // If dropped in a placeholder button group, the user must name it.
|
Chris@0
|
133 if ($group.hasClass('placeholder')) {
|
Chris@0
|
134 if (view.isProcessing) {
|
Chris@0
|
135 return;
|
Chris@0
|
136 }
|
Chris@0
|
137 view.isProcessing = true;
|
Chris@0
|
138
|
Chris@0
|
139 Drupal.ckeditor.openGroupNameDialog(view, $group, callback);
|
Chris@17
|
140 } else {
|
Chris@0
|
141 view.model.set('isDirty', true);
|
Chris@0
|
142 callback(true);
|
Chris@0
|
143 }
|
Chris@0
|
144 },
|
Chris@0
|
145
|
Chris@0
|
146 /**
|
Chris@0
|
147 * Translates changes in CKEditor config DOM structure to the config model.
|
Chris@0
|
148 *
|
Chris@0
|
149 * Each row has a placeholder group at the end of the row. A user may not
|
Chris@0
|
150 * move an existing button group past the placeholder group at the end of a
|
Chris@0
|
151 * row.
|
Chris@0
|
152 *
|
Chris@0
|
153 * @param {Backbone.View} view
|
Chris@0
|
154 * The Backbone View that invoked this function.
|
Chris@0
|
155 * @param {jQuery} $group
|
Chris@0
|
156 * A jQuery set that contains an li element that wraps a group of buttons.
|
Chris@0
|
157 */
|
Chris@0
|
158 registerGroupMove(view, $group) {
|
Chris@0
|
159 // Remove placeholder classes if necessary.
|
Chris@0
|
160 let $row = $group.closest('.ckeditor-row');
|
Chris@0
|
161 if ($row.hasClass('placeholder')) {
|
Chris@0
|
162 $row.removeClass('placeholder');
|
Chris@0
|
163 }
|
Chris@0
|
164 // If there are any rows with just a placeholder group, mark the row as a
|
Chris@0
|
165 // placeholder.
|
Chris@17
|
166 $row
|
Chris@17
|
167 .parent()
|
Chris@17
|
168 .children()
|
Chris@17
|
169 .each(function() {
|
Chris@17
|
170 $row = $(this);
|
Chris@17
|
171 if (
|
Chris@17
|
172 $row.find('.ckeditor-toolbar-group').not('.placeholder').length ===
|
Chris@17
|
173 0
|
Chris@17
|
174 ) {
|
Chris@17
|
175 $row.addClass('placeholder');
|
Chris@17
|
176 }
|
Chris@17
|
177 });
|
Chris@0
|
178 view.model.set('isDirty', true);
|
Chris@0
|
179 },
|
Chris@0
|
180
|
Chris@0
|
181 /**
|
Chris@0
|
182 * Opens a dialog with a form for changing the title of a button group.
|
Chris@0
|
183 *
|
Chris@0
|
184 * @param {Backbone.View} view
|
Chris@0
|
185 * The Backbone View that invoked this function.
|
Chris@0
|
186 * @param {jQuery} $group
|
Chris@0
|
187 * A jQuery set that contains an li element that wraps a group of buttons.
|
Chris@0
|
188 * @param {function} callback
|
Chris@0
|
189 * A callback to invoke after the button group naming modal dialog has
|
Chris@0
|
190 * been closed.
|
Chris@0
|
191 */
|
Chris@0
|
192 openGroupNameDialog(view, $group, callback) {
|
Chris@17
|
193 callback = callback || function() {};
|
Chris@0
|
194
|
Chris@0
|
195 /**
|
Chris@0
|
196 * Validates the string provided as a button group title.
|
Chris@0
|
197 *
|
Chris@0
|
198 * @param {HTMLElement} form
|
Chris@0
|
199 * The form DOM element that contains the input with the new button
|
Chris@0
|
200 * group title string.
|
Chris@0
|
201 *
|
Chris@0
|
202 * @return {bool}
|
Chris@0
|
203 * Returns true when an error exists, otherwise returns false.
|
Chris@0
|
204 */
|
Chris@0
|
205 function validateForm(form) {
|
Chris@0
|
206 if (form.elements[0].value.length === 0) {
|
Chris@0
|
207 const $form = $(form);
|
Chris@0
|
208 if (!$form.hasClass('errors')) {
|
Chris@0
|
209 $form
|
Chris@0
|
210 .addClass('errors')
|
Chris@0
|
211 .find('input')
|
Chris@0
|
212 .addClass('error')
|
Chris@0
|
213 .attr('aria-invalid', 'true');
|
Chris@17
|
214 $(
|
Chris@17
|
215 `<div class="description" >${Drupal.t(
|
Chris@17
|
216 'Please provide a name for the button group.',
|
Chris@17
|
217 )}</div>`,
|
Chris@17
|
218 ).insertAfter(form.elements[0]);
|
Chris@0
|
219 }
|
Chris@0
|
220 return true;
|
Chris@0
|
221 }
|
Chris@0
|
222 return false;
|
Chris@0
|
223 }
|
Chris@0
|
224
|
Chris@0
|
225 /**
|
Chris@0
|
226 * Attempts to close the dialog; Validates user input.
|
Chris@0
|
227 *
|
Chris@0
|
228 * @param {string} action
|
Chris@0
|
229 * The dialog action chosen by the user: 'apply' or 'cancel'.
|
Chris@0
|
230 * @param {HTMLElement} form
|
Chris@0
|
231 * The form DOM element that contains the input with the new button
|
Chris@0
|
232 * group title string.
|
Chris@0
|
233 */
|
Chris@0
|
234 function closeDialog(action, form) {
|
Chris@0
|
235 /**
|
Chris@0
|
236 * Closes the dialog when the user cancels or supplies valid data.
|
Chris@0
|
237 */
|
Chris@0
|
238 function shutdown() {
|
Chris@17
|
239 // eslint-disable-next-line no-use-before-define
|
Chris@0
|
240 dialog.close(action);
|
Chris@0
|
241
|
Chris@0
|
242 // The processing marker can be deleted since the dialog has been
|
Chris@0
|
243 // closed.
|
Chris@0
|
244 delete view.isProcessing;
|
Chris@0
|
245 }
|
Chris@0
|
246
|
Chris@0
|
247 /**
|
Chris@0
|
248 * Applies a string as the name of a CKEditor button group.
|
Chris@0
|
249 *
|
Chris@0
|
250 * @param {jQuery} $group
|
Chris@0
|
251 * A jQuery set that contains an li element that wraps a group of
|
Chris@0
|
252 * buttons.
|
Chris@0
|
253 * @param {string} name
|
Chris@0
|
254 * The new name of the CKEditor button group.
|
Chris@0
|
255 */
|
Chris@0
|
256 function namePlaceholderGroup($group, name) {
|
Chris@0
|
257 // If it's currently still a placeholder, then that means we're
|
Chris@0
|
258 // creating a new group, and we must do some extra work.
|
Chris@0
|
259 if ($group.hasClass('placeholder')) {
|
Chris@0
|
260 // Remove all whitespace from the name, lowercase it and ensure
|
Chris@0
|
261 // HTML-safe encoding, then use this as the group ID for CKEditor
|
Chris@0
|
262 // configuration UI accessibility purposes only.
|
Chris@17
|
263 const groupID = `ckeditor-toolbar-group-aria-label-for-${Drupal.checkPlain(
|
Chris@17
|
264 name.toLowerCase().replace(/\s/g, '-'),
|
Chris@17
|
265 )}`;
|
Chris@0
|
266 $group
|
Chris@0
|
267 // Update the group container.
|
Chris@0
|
268 .removeAttr('aria-label')
|
Chris@0
|
269 .attr('data-drupal-ckeditor-type', 'group')
|
Chris@0
|
270 .attr('tabindex', 0)
|
Chris@0
|
271 // Update the group heading.
|
Chris@0
|
272 .children('.ckeditor-toolbar-group-name')
|
Chris@0
|
273 .attr('id', groupID)
|
Chris@0
|
274 .end()
|
Chris@0
|
275 // Update the group items.
|
Chris@0
|
276 .children('.ckeditor-toolbar-group-buttons')
|
Chris@0
|
277 .attr('aria-labelledby', groupID);
|
Chris@0
|
278 }
|
Chris@0
|
279
|
Chris@0
|
280 $group
|
Chris@0
|
281 .attr('data-drupal-ckeditor-toolbar-group-name', name)
|
Chris@0
|
282 .children('.ckeditor-toolbar-group-name')
|
Chris@0
|
283 .text(name);
|
Chris@0
|
284 }
|
Chris@0
|
285
|
Chris@0
|
286 // Invoke a user-provided callback and indicate failure.
|
Chris@0
|
287 if (action === 'cancel') {
|
Chris@0
|
288 shutdown();
|
Chris@0
|
289 callback(false, $group);
|
Chris@0
|
290 return;
|
Chris@0
|
291 }
|
Chris@0
|
292
|
Chris@0
|
293 // Validate that a group name was provided.
|
Chris@0
|
294 if (form && validateForm(form)) {
|
Chris@0
|
295 return;
|
Chris@0
|
296 }
|
Chris@0
|
297
|
Chris@0
|
298 // React to application of a valid group name.
|
Chris@0
|
299 if (action === 'apply') {
|
Chris@0
|
300 shutdown();
|
Chris@0
|
301 // Apply the provided name to the button group label.
|
Chris@17
|
302 namePlaceholderGroup(
|
Chris@17
|
303 $group,
|
Chris@17
|
304 Drupal.checkPlain(form.elements[0].value),
|
Chris@17
|
305 );
|
Chris@0
|
306 // Remove placeholder classes so that new placeholders will be
|
Chris@0
|
307 // inserted.
|
Chris@17
|
308 $group
|
Chris@17
|
309 .closest('.ckeditor-row.placeholder')
|
Chris@17
|
310 .addBack()
|
Chris@17
|
311 .removeClass('placeholder');
|
Chris@0
|
312
|
Chris@0
|
313 // Invoke a user-provided callback and indicate success.
|
Chris@0
|
314 callback(true, $group);
|
Chris@0
|
315
|
Chris@0
|
316 // Signal that the active toolbar DOM structure has changed.
|
Chris@0
|
317 view.model.set('isDirty', true);
|
Chris@0
|
318 }
|
Chris@0
|
319 }
|
Chris@0
|
320
|
Chris@0
|
321 // Create a Drupal dialog that will get a button group name from the user.
|
Chris@17
|
322 const $ckeditorButtonGroupNameForm = $(
|
Chris@17
|
323 Drupal.theme('ckeditorButtonGroupNameForm'),
|
Chris@17
|
324 );
|
Chris@14
|
325 const dialog = Drupal.dialog($ckeditorButtonGroupNameForm.get(0), {
|
Chris@0
|
326 title: Drupal.t('Button group name'),
|
Chris@0
|
327 dialogClass: 'ckeditor-name-toolbar-group',
|
Chris@0
|
328 resizable: false,
|
Chris@0
|
329 buttons: [
|
Chris@0
|
330 {
|
Chris@0
|
331 text: Drupal.t('Apply'),
|
Chris@0
|
332 click() {
|
Chris@0
|
333 closeDialog('apply', this);
|
Chris@0
|
334 },
|
Chris@0
|
335 primary: true,
|
Chris@0
|
336 },
|
Chris@0
|
337 {
|
Chris@0
|
338 text: Drupal.t('Cancel'),
|
Chris@0
|
339 click() {
|
Chris@0
|
340 closeDialog('cancel');
|
Chris@0
|
341 },
|
Chris@0
|
342 },
|
Chris@0
|
343 ],
|
Chris@0
|
344 open() {
|
Chris@0
|
345 const form = this;
|
Chris@0
|
346 const $form = $(this);
|
Chris@0
|
347 const $widget = $form.parent();
|
Chris@0
|
348 $widget.find('.ui-dialog-titlebar-close').remove();
|
Chris@0
|
349 // Set a click handler on the input and button in the form.
|
Chris@17
|
350 $widget.on('keypress.ckeditor', 'input, button', event => {
|
Chris@0
|
351 // React to enter key press.
|
Chris@0
|
352 if (event.keyCode === 13) {
|
Chris@0
|
353 const $target = $(event.currentTarget);
|
Chris@0
|
354 const data = $target.data('ui-button');
|
Chris@0
|
355 let action = 'apply';
|
Chris@0
|
356 // Assume 'apply', but take into account that the user might have
|
Chris@0
|
357 // pressed the enter key on the dialog buttons.
|
Chris@0
|
358 if (data && data.options && data.options.label) {
|
Chris@0
|
359 action = data.options.label.toLowerCase();
|
Chris@0
|
360 }
|
Chris@0
|
361 closeDialog(action, form);
|
Chris@0
|
362 event.stopPropagation();
|
Chris@0
|
363 event.stopImmediatePropagation();
|
Chris@0
|
364 event.preventDefault();
|
Chris@0
|
365 }
|
Chris@0
|
366 });
|
Chris@0
|
367 // Announce to the user that a modal dialog is open.
|
Chris@17
|
368 let text = Drupal.t(
|
Chris@17
|
369 'Editing the name of the new button group in a dialog.',
|
Chris@17
|
370 );
|
Chris@17
|
371 if (
|
Chris@17
|
372 typeof $group.attr('data-drupal-ckeditor-toolbar-group-name') !==
|
Chris@17
|
373 'undefined'
|
Chris@17
|
374 ) {
|
Chris@17
|
375 text = Drupal.t(
|
Chris@17
|
376 'Editing the name of the "@groupName" button group in a dialog.',
|
Chris@17
|
377 {
|
Chris@17
|
378 '@groupName': $group.attr(
|
Chris@17
|
379 'data-drupal-ckeditor-toolbar-group-name',
|
Chris@17
|
380 ),
|
Chris@17
|
381 },
|
Chris@17
|
382 );
|
Chris@0
|
383 }
|
Chris@0
|
384 Drupal.announce(text);
|
Chris@0
|
385 },
|
Chris@0
|
386 close(event) {
|
Chris@0
|
387 // Automatically destroy the DOM element that was used for the dialog.
|
Chris@0
|
388 $(event.target).remove();
|
Chris@0
|
389 },
|
Chris@0
|
390 });
|
Chris@17
|
391
|
Chris@0
|
392 // A modal dialog is used because the user must provide a button group
|
Chris@0
|
393 // name or cancel the button placement before taking any other action.
|
Chris@0
|
394 dialog.showModal();
|
Chris@0
|
395
|
Chris@17
|
396 $(
|
Chris@17
|
397 document
|
Chris@17
|
398 .querySelector('.ckeditor-name-toolbar-group')
|
Chris@17
|
399 .querySelector('input'),
|
Chris@17
|
400 )
|
Chris@0
|
401 // When editing, set the "group name" input in the form to the current
|
Chris@0
|
402 // value.
|
Chris@0
|
403 .attr('value', $group.attr('data-drupal-ckeditor-toolbar-group-name'))
|
Chris@0
|
404 // Focus on the "group name" input in the form.
|
Chris@0
|
405 .trigger('focus');
|
Chris@0
|
406 },
|
Chris@0
|
407 };
|
Chris@0
|
408
|
Chris@0
|
409 /**
|
Chris@0
|
410 * Automatically shows/hides settings of buttons-only CKEditor plugins.
|
Chris@0
|
411 *
|
Chris@0
|
412 * @type {Drupal~behavior}
|
Chris@0
|
413 *
|
Chris@0
|
414 * @prop {Drupal~behaviorAttach} attach
|
Chris@0
|
415 * Attaches show/hide behaviour to Plugin Settings buttons.
|
Chris@0
|
416 */
|
Chris@0
|
417 Drupal.behaviors.ckeditorAdminButtonPluginSettings = {
|
Chris@0
|
418 attach(context) {
|
Chris@0
|
419 const $context = $(context);
|
Chris@17
|
420 const $ckeditorPluginSettings = $context
|
Chris@17
|
421 .find('#ckeditor-plugin-settings')
|
Chris@17
|
422 .once('ckeditor-plugin-settings');
|
Chris@0
|
423 if ($ckeditorPluginSettings.length) {
|
Chris@0
|
424 // Hide all button-dependent plugin settings initially.
|
Chris@17
|
425 $ckeditorPluginSettings
|
Chris@17
|
426 .find('[data-ckeditor-buttons]')
|
Chris@17
|
427 .each(function() {
|
Chris@17
|
428 const $this = $(this);
|
Chris@17
|
429 if ($this.data('verticalTab')) {
|
Chris@17
|
430 $this.data('verticalTab').tabHide();
|
Chris@17
|
431 } else {
|
Chris@17
|
432 // On very narrow viewports, Vertical Tabs are disabled.
|
Chris@17
|
433 $this.hide();
|
Chris@17
|
434 }
|
Chris@17
|
435 $this.data('ckeditorButtonPluginSettingsActiveButtons', []);
|
Chris@17
|
436 });
|
Chris@0
|
437
|
Chris@0
|
438 // Whenever a button is added or removed, check if we should show or
|
Chris@0
|
439 // hide the corresponding plugin settings. (Note that upon
|
Chris@0
|
440 // initialization, each button that already is part of the toolbar still
|
Chris@0
|
441 // is considered "added", hence it also works correctly for buttons that
|
Chris@0
|
442 // were added previously.)
|
Chris@0
|
443 $context
|
Chris@0
|
444 .find('.ckeditor-toolbar-active')
|
Chris@0
|
445 .off('CKEditorToolbarChanged.ckeditorAdminPluginSettings')
|
Chris@17
|
446 .on(
|
Chris@17
|
447 'CKEditorToolbarChanged.ckeditorAdminPluginSettings',
|
Chris@17
|
448 (event, action, button) => {
|
Chris@17
|
449 const $pluginSettings = $ckeditorPluginSettings.find(
|
Chris@17
|
450 `[data-ckeditor-buttons~=${button}]`,
|
Chris@17
|
451 );
|
Chris@0
|
452
|
Chris@17
|
453 // No settings for this button.
|
Chris@17
|
454 if ($pluginSettings.length === 0) {
|
Chris@17
|
455 return;
|
Chris@17
|
456 }
|
Chris@0
|
457
|
Chris@17
|
458 const verticalTab = $pluginSettings.data('verticalTab');
|
Chris@17
|
459 const activeButtons = $pluginSettings.data(
|
Chris@17
|
460 'ckeditorButtonPluginSettingsActiveButtons',
|
Chris@17
|
461 );
|
Chris@17
|
462 if (action === 'added') {
|
Chris@17
|
463 activeButtons.push(button);
|
Chris@17
|
464 // Show this plugin's settings if >=1 of its buttons are active.
|
Chris@0
|
465 if (verticalTab) {
|
Chris@17
|
466 verticalTab.tabShow();
|
Chris@17
|
467 } else {
|
Chris@17
|
468 // On very narrow viewports, Vertical Tabs remain fieldsets.
|
Chris@17
|
469 $pluginSettings.show();
|
Chris@0
|
470 }
|
Chris@17
|
471 } else {
|
Chris@17
|
472 // Remove this button from the list of active buttons.
|
Chris@17
|
473 activeButtons.splice(activeButtons.indexOf(button), 1);
|
Chris@17
|
474 // Show this plugin's settings 0 of its buttons are active.
|
Chris@17
|
475 if (activeButtons.length === 0) {
|
Chris@17
|
476 if (verticalTab) {
|
Chris@17
|
477 verticalTab.tabHide();
|
Chris@17
|
478 } else {
|
Chris@17
|
479 // On very narrow viewports, Vertical Tabs are disabled.
|
Chris@17
|
480 $pluginSettings.hide();
|
Chris@17
|
481 }
|
Chris@0
|
482 }
|
Chris@0
|
483 }
|
Chris@17
|
484 $pluginSettings.data(
|
Chris@17
|
485 'ckeditorButtonPluginSettingsActiveButtons',
|
Chris@17
|
486 activeButtons,
|
Chris@17
|
487 );
|
Chris@17
|
488 },
|
Chris@17
|
489 );
|
Chris@0
|
490 }
|
Chris@0
|
491 },
|
Chris@0
|
492 };
|
Chris@0
|
493
|
Chris@0
|
494 /**
|
Chris@0
|
495 * Themes a blank CKEditor row.
|
Chris@0
|
496 *
|
Chris@0
|
497 * @return {string}
|
Chris@0
|
498 * A HTML string for a CKEditor row.
|
Chris@0
|
499 */
|
Chris@17
|
500 Drupal.theme.ckeditorRow = function() {
|
Chris@0
|
501 return '<li class="ckeditor-row placeholder" role="group"><ul class="ckeditor-toolbar-groups clearfix"></ul></li>';
|
Chris@0
|
502 };
|
Chris@0
|
503
|
Chris@0
|
504 /**
|
Chris@0
|
505 * Themes a blank CKEditor button group.
|
Chris@0
|
506 *
|
Chris@0
|
507 * @return {string}
|
Chris@0
|
508 * A HTML string for a CKEditor button group.
|
Chris@0
|
509 */
|
Chris@17
|
510 Drupal.theme.ckeditorToolbarGroup = function() {
|
Chris@0
|
511 let group = '';
|
Chris@17
|
512 group += `<li class="ckeditor-toolbar-group placeholder" role="presentation" aria-label="${Drupal.t(
|
Chris@17
|
513 'Place a button to create a new button group.',
|
Chris@17
|
514 )}">`;
|
Chris@17
|
515 group += `<h3 class="ckeditor-toolbar-group-name">${Drupal.t(
|
Chris@17
|
516 'New group',
|
Chris@17
|
517 )}</h3>`;
|
Chris@17
|
518 group +=
|
Chris@17
|
519 '<ul class="ckeditor-buttons ckeditor-toolbar-group-buttons" role="toolbar" data-drupal-ckeditor-button-sorting="target"></ul>';
|
Chris@0
|
520 group += '</li>';
|
Chris@0
|
521 return group;
|
Chris@0
|
522 };
|
Chris@0
|
523
|
Chris@0
|
524 /**
|
Chris@0
|
525 * Themes a form for changing the title of a CKEditor button group.
|
Chris@0
|
526 *
|
Chris@0
|
527 * @return {string}
|
Chris@0
|
528 * A HTML string for the form for the title of a CKEditor button group.
|
Chris@0
|
529 */
|
Chris@17
|
530 Drupal.theme.ckeditorButtonGroupNameForm = function() {
|
Chris@0
|
531 return '<form><input name="group-name" required="required"></form>';
|
Chris@0
|
532 };
|
Chris@0
|
533
|
Chris@0
|
534 /**
|
Chris@0
|
535 * Themes a button that will toggle the button group names in active config.
|
Chris@0
|
536 *
|
Chris@0
|
537 * @return {string}
|
Chris@0
|
538 * A HTML string for the button to toggle group names.
|
Chris@0
|
539 */
|
Chris@17
|
540 Drupal.theme.ckeditorButtonGroupNamesToggle = function() {
|
Chris@0
|
541 return '<button class="link ckeditor-groupnames-toggle" aria-pressed="false"></button>';
|
Chris@0
|
542 };
|
Chris@0
|
543
|
Chris@0
|
544 /**
|
Chris@0
|
545 * Themes a button that will prompt the user to name a new button group.
|
Chris@0
|
546 *
|
Chris@0
|
547 * @return {string}
|
Chris@0
|
548 * A HTML string for the button to create a name for a new button group.
|
Chris@0
|
549 */
|
Chris@17
|
550 Drupal.theme.ckeditorNewButtonGroup = function() {
|
Chris@17
|
551 return `<li class="ckeditor-add-new-group"><button aria-label="${Drupal.t(
|
Chris@17
|
552 'Add a CKEditor button group to the end of this row.',
|
Chris@17
|
553 )}">${Drupal.t('Add group')}</button></li>`;
|
Chris@0
|
554 };
|
Chris@17
|
555 })(jQuery, Drupal, drupalSettings, _);
|