3 * A Backbone View that provides the visual UX view of CKEditor toolbar
7 (function (Drupal, Backbone, $) {
8 Drupal.ckeditor.VisualView = Backbone.View.extend(/** @lends Drupal.ckeditor.VisualView# */{
11 'click .ckeditor-toolbar-group-name': 'onGroupNameClick',
12 'click .ckeditor-groupnames-toggle': 'onGroupNamesToggleClick',
13 'click .ckeditor-add-new-group button': 'onAddGroupButtonClick',
17 * Backbone View for CKEditor toolbar configuration; visual UX.
21 * @augments Backbone.View
24 this.listenTo(this.model, 'change:isDirty change:groupNamesVisible', this.render);
26 // Add a toggle for the button group names.
27 $(Drupal.theme('ckeditorButtonGroupNamesToggle'))
28 .prependTo(this.$el.find('#ckeditor-active-toolbar').parent());
34 * Render function for rendering the toolbar configuration.
37 * Model used for the view.
38 * @param {string} [value]
39 * The value that was changed.
40 * @param {object} changedAttributes
41 * The attributes that was changed.
43 * @return {Drupal.ckeditor.VisualView}
44 * The {@link Drupal.ckeditor.VisualView} object.
46 render(model, value, changedAttributes) {
47 this.insertPlaceholders();
50 // Toggle button group names.
51 let groupNamesVisible = this.model.get('groupNamesVisible');
52 // If a button was just placed in the active toolbar, ensure that the
53 // button group names are visible.
54 if (changedAttributes && changedAttributes.changes && changedAttributes.changes.isDirty) {
55 this.model.set({ groupNamesVisible: true }, { silent: true });
56 groupNamesVisible = true;
58 this.$el.find('[data-toolbar="active"]').toggleClass('ckeditor-group-names-are-visible', groupNamesVisible);
59 this.$el.find('.ckeditor-groupnames-toggle')
60 .text((groupNamesVisible) ? Drupal.t('Hide group names') : Drupal.t('Show group names'))
61 .attr('aria-pressed', groupNamesVisible);
67 * Handles clicks to a button group name.
69 * @param {jQuery.Event} event
70 * The click event on the button group.
72 onGroupNameClick(event) {
73 const $group = $(event.currentTarget).closest('.ckeditor-toolbar-group');
74 Drupal.ckeditor.openGroupNameDialog(this, $group);
76 event.stopPropagation();
77 event.preventDefault();
81 * Handles clicks on the button group names toggle button.
83 * @param {jQuery.Event} event
84 * The click event on the toggle button.
86 onGroupNamesToggleClick(event) {
87 this.model.set('groupNamesVisible', !this.model.get('groupNamesVisible'));
88 event.preventDefault();
92 * Prompts the user to provide a name for a new button group; inserts it.
94 * @param {jQuery.Event} event
95 * The event of the button click.
97 onAddGroupButtonClick(event) {
99 * Inserts a new button if the openGroupNameDialog function returns true.
101 * @param {bool} success
102 * A flag that indicates if the user created a new group (true) or
103 * canceled out of the dialog (false).
104 * @param {jQuery} $group
105 * A jQuery DOM fragment that represents the new button group. It has
106 * not been added to the DOM yet.
108 function insertNewGroup(success, $group) {
110 $group.appendTo($(event.currentTarget).closest('.ckeditor-row').children('.ckeditor-toolbar-groups'));
111 // Focus on the new group.
112 $group.trigger('focus');
116 // Pass in a DOM fragment of a placeholder group so that the new group
117 // name can be applied to it.
118 Drupal.ckeditor.openGroupNameDialog(this, $(Drupal.theme('ckeditorToolbarGroup')), insertNewGroup);
120 event.preventDefault();
124 * Handles jQuery Sortable stop sort of a button group.
126 * @param {jQuery.Event} event
127 * The event triggered on the group drag.
129 * A jQuery.ui.sortable argument that contains information about the
130 * elements involved in the sort action.
132 endGroupDrag(event, ui) {
134 Drupal.ckeditor.registerGroupMove(this, ui.item, (success) => {
136 // Cancel any sorting in the configuration area.
137 view.$el.find('.ckeditor-toolbar-configuration').find('.ui-sortable').sortable('cancel');
143 * Handles jQuery Sortable start sort of a button.
145 * @param {jQuery.Event} event
146 * The event triggered on the group drag.
148 * A jQuery.ui.sortable argument that contains information about the
149 * elements involved in the sort action.
151 startButtonDrag(event, ui) {
152 this.$el.find('a:focus').trigger('blur');
154 // Show the button group names as soon as the user starts dragging.
155 this.model.set('groupNamesVisible', true);
159 * Handles jQuery Sortable stop sort of a button.
161 * @param {jQuery.Event} event
162 * The event triggered on the button drag.
164 * A jQuery.ui.sortable argument that contains information about the
165 * elements involved in the sort action.
167 endButtonDrag(event, ui) {
169 Drupal.ckeditor.registerButtonMove(this, ui.item, (success) => {
171 // Cancel any sorting in the configuration area.
172 view.$el.find('.ui-sortable').sortable('cancel');
174 // Refocus the target button so that the user can continue from a known
176 ui.item.find('a').trigger('focus');
181 * Invokes jQuery.sortable() on new buttons and groups in a CKEditor config.
184 // Make the buttons sortable.
185 this.$el.find('.ckeditor-buttons').not('.ui-sortable').sortable({
186 // Change this to .ckeditor-toolbar-group-buttons.
187 connectWith: '.ckeditor-buttons',
188 placeholder: 'ckeditor-button-placeholder',
189 forcePlaceholderSize: true,
190 tolerance: 'pointer',
192 start: this.startButtonDrag.bind(this),
193 // Sorting within a sortable.
194 stop: this.endButtonDrag.bind(this),
195 }).disableSelection();
197 // Add the drag and drop functionality to button groups.
198 this.$el.find('.ckeditor-toolbar-groups').not('.ui-sortable').sortable({
199 connectWith: '.ckeditor-toolbar-groups',
200 cancel: '.ckeditor-add-new-group',
201 placeholder: 'ckeditor-toolbar-group-placeholder',
202 forcePlaceholderSize: true,
204 stop: this.endGroupDrag.bind(this),
207 // Add the drag and drop functionality to buttons.
208 this.$el.find('.ckeditor-multiple-buttons li').draggable({
209 connectToSortable: '.ckeditor-toolbar-active .ckeditor-buttons',
215 * Wraps the invocation of methods to insert blank groups and rows.
217 insertPlaceholders() {
218 this.insertPlaceholderRow();
219 this.insertNewGroupButtons();
223 * Inserts a blank row at the bottom of the CKEditor configuration.
225 insertPlaceholderRow() {
226 let $rows = this.$el.find('.ckeditor-row');
227 // Add a placeholder row. to the end of the list if one does not exist.
228 if (!$rows.eq(-1).hasClass('placeholder')) {
230 .find('.ckeditor-toolbar-active')
231 .children('.ckeditor-active-toolbar-configuration')
232 .append(Drupal.theme('ckeditorRow'));
234 // Update the $rows variable to include the new row.
235 $rows = this.$el.find('.ckeditor-row');
236 // Remove blank rows except the last one.
237 const len = $rows.length;
238 $rows.filter((index, row) => {
239 // Do not remove the last row.
240 if (index + 1 === len) {
243 return $(row).find('.ckeditor-toolbar-group').not('.placeholder').length === 0;
245 // Then get all rows that are placeholders and remove them.
250 * Inserts a button in each row that will add a new CKEditor button group.
252 insertNewGroupButtons() {
253 // Insert an add group button to each row.
254 this.$el.find('.ckeditor-row').each(function () {
255 const $row = $(this);
256 const $groups = $row.find('.ckeditor-toolbar-group');
257 const $button = $row.find('.ckeditor-add-new-group');
258 if ($button.length === 0) {
259 $row.children('.ckeditor-toolbar-groups').append(Drupal.theme('ckeditorNewButtonGroup'));
261 // If a placeholder group exists, make sure it's at the end of the row.
262 else if (!$groups.eq(-1).hasClass('ckeditor-add-new-group')) {
263 $button.appendTo($row.children('.ckeditor-toolbar-groups'));
268 }(Drupal, Backbone, jQuery));