094f390cd0dfe0fab94129ace77a6972976b41d8
[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.closest('.ckeditor-row').prev().find('.ckeditor-toolbar-group').not('.placeholder').find('.ckeditor-toolbar-group-buttons').eq(-1).append($button);
102                 }
103               }
104             }
105             // Move right.
106             else if (_.indexOf([39, 63235], event.keyCode) > -1) {
107               // Move between sibling buttons.
108               if (index < ($siblings.length - 1)) {
109                 $button.insertAfter($container.children().eq(index + 1));
110               }
111               // Move between button groups. Moving right at the end of a row
112               // will create a new group.
113               else {
114                 $container.parent().next().find('.ckeditor-toolbar-group-buttons').prepend($button);
115               }
116             }
117           }
118           // Move buttons between rows and the available button set.
119           else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
120             dir = (_.indexOf([38, 63232], event.keyCode) > -1) ? 'prev' : 'next';
121             $row = $container.closest('.ckeditor-row')[dir]();
122             // Move the button back into the available button set.
123             if (dir === 'prev' && $row.length === 0) {
124               // If this is a divider, just destroy it.
125               if ($button.data('drupal-ckeditor-type') === 'separator') {
126                 $button
127                   .off()
128                   .remove();
129                 // Focus on the first button in the active toolbar.
130                 $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).children().eq(0).children().trigger('focus');
131               }
132               // Otherwise, move it.
133               else {
134                 $availableButtons.prepend($button);
135               }
136             }
137             else {
138               $row.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
139             }
140           }
141         }
142         // Move dividers between their container and the active toolbar.
143         else if (containerType === 'dividers') {
144           // Move the button to the active toolbar configuration when the down
145           // or up keys are pressed.
146           if (_.indexOf([40, 63233], event.keyCode) > -1) {
147             // Move the button to the first row, first button group index
148             // position.
149             $button = $button.clone(true);
150             $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
151             $target = $button.children();
152           }
153         }
154
155         view = this;
156         // Attempt to move the button to the new toolbar position.
157         Drupal.ckeditor.registerButtonMove(this, $button, (result) => {
158           // Put the button back if the registration failed.
159           // If the button was in a row, then it was in the active toolbar
160           // configuration. The button was probably placed in a new group, but
161           // that action was canceled.
162           if (!result && $originalGroup) {
163             $originalGroup.find('.ckeditor-buttons').append($button);
164           }
165           // Otherwise refresh the sortables to acknowledge the new button
166           // positions.
167           else {
168             view.$el.find('.ui-sortable').sortable('refresh');
169           }
170           // Refocus the target button so that the user can continue from a
171           // known place.
172           $target.trigger('focus');
173         });
174
175         event.preventDefault();
176         event.stopPropagation();
177       }
178     },
179
180     /**
181      * Handles keypresses on a CKEditor configuration group.
182      *
183      * @param {jQuery.Event} event
184      *   The keypress event triggered.
185      */
186     onPressGroup(event) {
187       const upDownKeys = [
188         38, // Up arrow.
189         63232, // Safari up arrow.
190         40, // Down arrow.
191         63233, // Safari down arrow.
192       ];
193       const leftRightKeys = [
194         37, // Left arrow.
195         63234, // Safari left arrow.
196         39, // Right arrow.
197         63235, // Safari right arrow.
198       ];
199
200       // Respond to an enter key press.
201       if (event.keyCode === 13) {
202         const view = this;
203         // Open the group renaming dialog in the next evaluation cycle so that
204         // this event can be cancelled and the bubbling wiped out. Otherwise,
205         // Firefox has issues because the page focus is shifted to the dialog
206         // along with the keydown event.
207         window.setTimeout(() => {
208           Drupal.ckeditor.openGroupNameDialog(view, $(event.currentTarget));
209         }, 0);
210         event.preventDefault();
211         event.stopPropagation();
212       }
213
214       // Respond to direction key presses.
215       if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
216         const $group = $(event.currentTarget);
217         const $container = $group.parent();
218         const $siblings = $container.children();
219         let index;
220         let dir;
221         // Move groups between sibling groups.
222         if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
223           index = $siblings.index($group);
224           // Move left between sibling groups.
225           if ((_.indexOf([37, 63234], event.keyCode) > -1)) {
226             if (index > 0) {
227               $group.insertBefore($siblings.eq(index - 1));
228             }
229             // Wrap between rows. Insert the group before the placeholder group
230             // at the end of the previous row.
231             else {
232               $group.insertBefore($container.closest('.ckeditor-row').prev().find('.ckeditor-toolbar-groups').children().eq(-1));
233             }
234           }
235           // Move right between sibling groups.
236           else if (_.indexOf([39, 63235], event.keyCode) > -1) {
237             // Move to the right if the next group is not a placeholder.
238             if (!$siblings.eq(index + 1).hasClass('placeholder')) {
239               $group.insertAfter($container.children().eq(index + 1));
240             }
241             // Wrap group between rows.
242             else {
243               $container.closest('.ckeditor-row').next().find('.ckeditor-toolbar-groups').prepend($group);
244             }
245           }
246         }
247         // Move groups between rows.
248         else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
249           dir = (_.indexOf([38, 63232], event.keyCode) > -1) ? 'prev' : 'next';
250           $group.closest('.ckeditor-row')[dir]().find('.ckeditor-toolbar-groups').eq(0).prepend($group);
251         }
252
253         Drupal.ckeditor.registerGroupMove(this, $group);
254         $group.trigger('focus');
255         event.preventDefault();
256         event.stopPropagation();
257       }
258     },
259   });
260 }(jQuery, Drupal, Backbone, _));