413e69be857bd04c83bdd124b1572d7633bf68a8
[yaffs-website] / web / core / modules / ckeditor / js / views / VisualView.es6.js
1 /**
2  * @file
3  * A Backbone View that provides the visual UX view of CKEditor toolbar
4  *   configuration.
5  */
6
7 (function (Drupal, Backbone, $) {
8   Drupal.ckeditor.VisualView = Backbone.View.extend(/** @lends Drupal.ckeditor.VisualView# */{
9
10     events: {
11       'click .ckeditor-toolbar-group-name': 'onGroupNameClick',
12       'click .ckeditor-groupnames-toggle': 'onGroupNamesToggleClick',
13       'click .ckeditor-add-new-group button': 'onAddGroupButtonClick',
14     },
15
16     /**
17      * Backbone View for CKEditor toolbar configuration; visual UX.
18      *
19      * @constructs
20      *
21      * @augments Backbone.View
22      */
23     initialize() {
24       this.listenTo(this.model, 'change:isDirty change:groupNamesVisible', this.render);
25
26       // Add a toggle for the button group names.
27       $(Drupal.theme('ckeditorButtonGroupNamesToggle'))
28         .prependTo(this.$el.find('#ckeditor-active-toolbar').parent());
29
30       this.render();
31     },
32
33     /**
34      * Render function for rendering the toolbar configuration.
35      *
36      * @param {*} model
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.
42      *
43      * @return {Drupal.ckeditor.VisualView}
44      *   The {@link Drupal.ckeditor.VisualView} object.
45      */
46     render(model, value, changedAttributes) {
47       this.insertPlaceholders();
48       this.applySorting();
49
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;
57       }
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);
62
63       return this;
64     },
65
66     /**
67      * Handles clicks to a button group name.
68      *
69      * @param {jQuery.Event} event
70      *   The click event on the button group.
71      */
72     onGroupNameClick(event) {
73       const $group = $(event.currentTarget).closest('.ckeditor-toolbar-group');
74       Drupal.ckeditor.openGroupNameDialog(this, $group);
75
76       event.stopPropagation();
77       event.preventDefault();
78     },
79
80     /**
81      * Handles clicks on the button group names toggle button.
82      *
83      * @param {jQuery.Event} event
84      *   The click event on the toggle button.
85      */
86     onGroupNamesToggleClick(event) {
87       this.model.set('groupNamesVisible', !this.model.get('groupNamesVisible'));
88       event.preventDefault();
89     },
90
91     /**
92      * Prompts the user to provide a name for a new button group; inserts it.
93      *
94      * @param {jQuery.Event} event
95      *   The event of the button click.
96      */
97     onAddGroupButtonClick(event) {
98       /**
99        * Inserts a new button if the openGroupNameDialog function returns true.
100        *
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.
107        */
108       function insertNewGroup(success, $group) {
109         if (success) {
110           $group.appendTo($(event.currentTarget).closest('.ckeditor-row').children('.ckeditor-toolbar-groups'));
111           // Focus on the new group.
112           $group.trigger('focus');
113         }
114       }
115
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);
119
120       event.preventDefault();
121     },
122
123     /**
124      * Handles jQuery Sortable stop sort of a button group.
125      *
126      * @param {jQuery.Event} event
127      *   The event triggered on the group drag.
128      * @param {object} ui
129      *   A jQuery.ui.sortable argument that contains information about the
130      *   elements involved in the sort action.
131      */
132     endGroupDrag(event, ui) {
133       const view = this;
134       Drupal.ckeditor.registerGroupMove(this, ui.item, (success) => {
135         if (!success) {
136           // Cancel any sorting in the configuration area.
137           view.$el.find('.ckeditor-toolbar-configuration').find('.ui-sortable').sortable('cancel');
138         }
139       });
140     },
141
142     /**
143      * Handles jQuery Sortable start sort of a button.
144      *
145      * @param {jQuery.Event} event
146      *   The event triggered on the group drag.
147      * @param {object} ui
148      *   A jQuery.ui.sortable argument that contains information about the
149      *   elements involved in the sort action.
150      */
151     startButtonDrag(event, ui) {
152       this.$el.find('a:focus').trigger('blur');
153
154       // Show the button group names as soon as the user starts dragging.
155       this.model.set('groupNamesVisible', true);
156     },
157
158     /**
159      * Handles jQuery Sortable stop sort of a button.
160      *
161      * @param {jQuery.Event} event
162      *   The event triggered on the button drag.
163      * @param {object} ui
164      *   A jQuery.ui.sortable argument that contains information about the
165      *   elements involved in the sort action.
166      */
167     endButtonDrag(event, ui) {
168       const view = this;
169       Drupal.ckeditor.registerButtonMove(this, ui.item, (success) => {
170         if (!success) {
171           // Cancel any sorting in the configuration area.
172           view.$el.find('.ui-sortable').sortable('cancel');
173         }
174         // Refocus the target button so that the user can continue from a known
175         // place.
176         ui.item.find('a').trigger('focus');
177       });
178     },
179
180     /**
181      * Invokes jQuery.sortable() on new buttons and groups in a CKEditor config.
182      */
183     applySorting() {
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',
191         cursor: 'move',
192         start: this.startButtonDrag.bind(this),
193         // Sorting within a sortable.
194         stop: this.endButtonDrag.bind(this),
195       }).disableSelection();
196
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,
203         cursor: 'move',
204         stop: this.endGroupDrag.bind(this),
205       });
206
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',
210         helper: 'clone',
211       });
212     },
213
214     /**
215      * Wraps the invocation of methods to insert blank groups and rows.
216      */
217     insertPlaceholders() {
218       this.insertPlaceholderRow();
219       this.insertNewGroupButtons();
220     },
221
222     /**
223      * Inserts a blank row at the bottom of the CKEditor configuration.
224      */
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')) {
229         this.$el
230           .find('.ckeditor-toolbar-active')
231           .children('.ckeditor-active-toolbar-configuration')
232           .append(Drupal.theme('ckeditorRow'));
233       }
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) {
241           return false;
242         }
243         return $(row).find('.ckeditor-toolbar-group').not('.placeholder').length === 0;
244       })
245         // Then get all rows that are placeholders and remove them.
246         .remove();
247     },
248
249     /**
250      * Inserts a button in each row that will add a new CKEditor button group.
251      */
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'));
260         }
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'));
264         }
265       });
266     },
267   });
268 }(Drupal, Backbone, jQuery));