cf2ead9937b69a21ce67edd0b066e2ba713f9c4f
[yaffs-website] / web / core / modules / ckeditor / js / views / KeyboardView.es6.js
1 /**
2  * @file
3  * Backbone View providing the aural view of CKEditor keyboard UX configuration.
4  */
5
6 (function ($, Drupal, Backbone, _) {
7   Drupal.ckeditor.KeyboardView = Backbone.View.extend(/** @lends Drupal.ckeditor.KeyboardView# */{
8
9     /**
10      * Backbone View for CKEditor toolbar configuration; keyboard UX.
11      *
12      * @constructs
13      *
14      * @augments Backbone.View
15      */
16     initialize() {
17       // Add keyboard arrow support.
18       this.$el.on('keydown.ckeditor', '.ckeditor-buttons a, .ckeditor-multiple-buttons a', this.onPressButton.bind(this));
19       this.$el.on('keydown.ckeditor', '[data-drupal-ckeditor-type="group"]', this.onPressGroup.bind(this));
20     },
21
22     /**
23      * @inheritdoc
24      */
25     render() {
26     },
27
28     /**
29      * Handles keypresses on a CKEditor configuration button.
30      *
31      * @param {jQuery.Event} event
32      *   The keypress event triggered.
33      */
34     onPressButton(event) {
35       const upDownKeys = [
36         38, // Up arrow.
37         63232, // Safari up arrow.
38         40, // Down arrow.
39         63233, // Safari down arrow.
40       ];
41       const leftRightKeys = [
42         37, // Left arrow.
43         63234, // Safari left arrow.
44         39, // Right arrow.
45         63235, // Safari right arrow.
46       ];
47
48       // Respond to an enter key press. Prevent the bubbling of the enter key
49       // press to the button group parent element.
50       if (event.keyCode === 13) {
51         event.stopPropagation();
52       }
53
54       // Only take action when a direction key is pressed.
55       if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
56         let view = this;
57         let $target = $(event.currentTarget);
58         let $button = $target.parent();
59         const $container = $button.parent();
60         let $group = $button.closest('.ckeditor-toolbar-group');
61         let $row;
62         const containerType = $container.data('drupal-ckeditor-button-sorting');
63         const $availableButtons = this.$el.find('[data-drupal-ckeditor-button-sorting="source"]');
64         const $activeButtons = this.$el.find('.ckeditor-toolbar-active');
65         // The current location of the button, just in case it needs to be put
66         // back.
67         const $originalGroup = $group;
68         let dir;
69
70         // Move available buttons between their container and the active
71         // toolbar.
72         if (containerType === 'source') {
73           // Move the button to the active toolbar configuration when the down
74           // or up keys are pressed.
75           if (_.indexOf([40, 63233], event.keyCode) > -1) {
76             // Move the button to the first row, first button group index
77             // position.
78             $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
79           }
80         }
81         else if (containerType === 'target') {
82           // Move buttons between sibling buttons in a group and between groups.
83           if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
84             // Move left.
85             const $siblings = $container.children();
86             const index = $siblings.index($button);
87             if (_.indexOf([37, 63234], event.keyCode) > -1) {
88               // Move between sibling buttons.
89               if (index > 0) {
90                 $button.insertBefore($container.children().eq(index - 1));
91               }
92               // Move between button groups and rows.
93               else {
94                 // Move between button groups.
95                 $group = $container.parent().prev();
96                 if ($group.length > 0) {
97                   $group.find('.ckeditor-toolbar-group-buttons').append($button);
98                 }
99                 // Wrap between rows.
100                 else {
101                   $container
102                     .closest('.ckeditor-row')
103                     .prev()
104                     .find('.ckeditor-toolbar-group')
105                     .not('.placeholder')
106                     .find('.ckeditor-toolbar-group-buttons')
107                     .eq(-1)
108                     .append($button);
109                 }
110               }
111             }
112             // Move right.
113             else if (_.indexOf([39, 63235], event.keyCode) > -1) {
114               // Move between sibling buttons.
115               if (index < ($siblings.length - 1)) {
116                 $button.insertAfter($container.children().eq(index + 1));
117               }
118               // Move between button groups. Moving right at the end of a row
119               // will create a new group.
120               else {
121                 $container.parent().next().find('.ckeditor-toolbar-group-buttons').prepend($button);
122               }
123             }
124           }
125           // Move buttons between rows and the available button set.
126           else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
127             dir = (_.indexOf([38, 63232], event.keyCode) > -1) ? 'prev' : 'next';
128             $row = $container.closest('.ckeditor-row')[dir]();
129             // Move the button back into the available button set.
130             if (dir === 'prev' && $row.length === 0) {
131               // If this is a divider, just destroy it.
132               if ($button.data('drupal-ckeditor-type') === 'separator') {
133                 $button
134                   .off()
135                   .remove();
136                 // Focus on the first button in the active toolbar.
137                 $activeButtons
138                   .find('.ckeditor-toolbar-group-buttons')
139                   .eq(0)
140                   .children()
141                   .eq(0)
142                   .children()
143                   .trigger('focus');
144               }
145               // Otherwise, move it.
146               else {
147                 $availableButtons.prepend($button);
148               }
149             }
150             else {
151               $row.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
152             }
153           }
154         }
155         // Move dividers between their container and the active toolbar.
156         else if (containerType === 'dividers') {
157           // Move the button to the active toolbar configuration when the down
158           // or up keys are pressed.
159           if (_.indexOf([40, 63233], event.keyCode) > -1) {
160             // Move the button to the first row, first button group index
161             // position.
162             $button = $button.clone(true);
163             $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
164             $target = $button.children();
165           }
166         }
167
168         view = this;
169         // Attempt to move the button to the new toolbar position.
170         Drupal.ckeditor.registerButtonMove(this, $button, (result) => {
171           // Put the button back if the registration failed.
172           // If the button was in a row, then it was in the active toolbar
173           // configuration. The button was probably placed in a new group, but
174           // that action was canceled.
175           if (!result && $originalGroup) {
176             $originalGroup.find('.ckeditor-buttons').append($button);
177           }
178           // Otherwise refresh the sortables to acknowledge the new button
179           // positions.
180           else {
181             view.$el.find('.ui-sortable').sortable('refresh');
182           }
183           // Refocus the target button so that the user can continue from a
184           // known place.
185           $target.trigger('focus');
186         });
187
188         event.preventDefault();
189         event.stopPropagation();
190       }
191     },
192
193     /**
194      * Handles keypresses on a CKEditor configuration group.
195      *
196      * @param {jQuery.Event} event
197      *   The keypress event triggered.
198      */
199     onPressGroup(event) {
200       const upDownKeys = [
201         38, // Up arrow.
202         63232, // Safari up arrow.
203         40, // Down arrow.
204         63233, // Safari down arrow.
205       ];
206       const leftRightKeys = [
207         37, // Left arrow.
208         63234, // Safari left arrow.
209         39, // Right arrow.
210         63235, // Safari right arrow.
211       ];
212
213       // Respond to an enter key press.
214       if (event.keyCode === 13) {
215         const view = this;
216         // Open the group renaming dialog in the next evaluation cycle so that
217         // this event can be cancelled and the bubbling wiped out. Otherwise,
218         // Firefox has issues because the page focus is shifted to the dialog
219         // along with the keydown event.
220         window.setTimeout(() => {
221           Drupal.ckeditor.openGroupNameDialog(view, $(event.currentTarget));
222         }, 0);
223         event.preventDefault();
224         event.stopPropagation();
225       }
226
227       // Respond to direction key presses.
228       if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
229         const $group = $(event.currentTarget);
230         const $container = $group.parent();
231         const $siblings = $container.children();
232         let index;
233         let dir;
234         // Move groups between sibling groups.
235         if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
236           index = $siblings.index($group);
237           // Move left between sibling groups.
238           if ((_.indexOf([37, 63234], event.keyCode) > -1)) {
239             if (index > 0) {
240               $group.insertBefore($siblings.eq(index - 1));
241             }
242             // Wrap between rows. Insert the group before the placeholder group
243             // at the end of the previous row.
244             else {
245               const $rowChildElement = $container
246                 .closest('.ckeditor-row')
247                 .prev()
248                 .find('.ckeditor-toolbar-groups')
249                 .children()
250                 .eq(-1);
251               $group.insertBefore($rowChildElement);
252             }
253           }
254           // Move right between sibling groups.
255           else if (_.indexOf([39, 63235], event.keyCode) > -1) {
256             // Move to the right if the next group is not a placeholder.
257             if (!$siblings.eq(index + 1).hasClass('placeholder')) {
258               $group.insertAfter($container.children().eq(index + 1));
259             }
260             // Wrap group between rows.
261             else {
262               $container.closest('.ckeditor-row').next().find('.ckeditor-toolbar-groups').prepend($group);
263             }
264           }
265         }
266         // Move groups between rows.
267         else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
268           dir = (_.indexOf([38, 63232], event.keyCode) > -1) ? 'prev' : 'next';
269           $group
270             .closest('.ckeditor-row')[dir]()
271             .find('.ckeditor-toolbar-groups')
272             .eq(0)
273             .prepend($group);
274         }
275
276         Drupal.ckeditor.registerGroupMove(this, $group);
277         $group.trigger('focus');
278         event.preventDefault();
279         event.stopPropagation();
280       }
281     },
282   });
283 }(jQuery, Drupal, Backbone, _));