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