3 * Backbone View providing the aural view of CKEditor keyboard UX configuration.
6 (function ($, Drupal, Backbone, _) {
10 Drupal.ckeditor.KeyboardView = Backbone.View.extend(/** @lends Drupal.ckeditor.KeyboardView# */{
13 * Backbone View for CKEditor toolbar configuration; keyboard UX.
17 * @augments Backbone.View
19 initialize: function () {
20 // Add keyboard arrow support.
21 this.$el.on('keydown.ckeditor', '.ckeditor-buttons a, .ckeditor-multiple-buttons a', this.onPressButton.bind(this));
22 this.$el.on('keydown.ckeditor', '[data-drupal-ckeditor-type="group"]', this.onPressGroup.bind(this));
32 * Handles keypresses on a CKEditor configuration button.
34 * @param {jQuery.Event} event
35 * The keypress event triggered.
37 onPressButton: function (event) {
40 63232, // Safari up arrow.
42 63233 // Safari down arrow.
46 63234, // Safari left arrow.
48 63235 // Safari right arrow.
51 // Respond to an enter key press. Prevent the bubbling of the enter key
52 // press to the button group parent element.
53 if (event.keyCode === 13) {
54 event.stopPropagation();
57 // Only take action when a direction key is pressed.
58 if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
60 var $target = $(event.currentTarget);
61 var $button = $target.parent();
62 var $container = $button.parent();
63 var $group = $button.closest('.ckeditor-toolbar-group');
65 var containerType = $container.data('drupal-ckeditor-button-sorting');
66 var $availableButtons = this.$el.find('[data-drupal-ckeditor-button-sorting="source"]');
67 var $activeButtons = this.$el.find('.ckeditor-toolbar-active');
68 // The current location of the button, just in case it needs to be put
70 var $originalGroup = $group;
73 // Move available buttons between their container and the active
75 if (containerType === 'source') {
76 // Move the button to the active toolbar configuration when the down
77 // or up keys are pressed.
78 if (_.indexOf([40, 63233], event.keyCode) > -1) {
79 // Move the button to the first row, first button group index
81 $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
84 else if (containerType === 'target') {
85 // Move buttons between sibling buttons in a group and between groups.
86 if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
88 var $siblings = $container.children();
89 var index = $siblings.index($button);
90 if (_.indexOf([37, 63234], event.keyCode) > -1) {
91 // Move between sibling buttons.
93 $button.insertBefore($container.children().eq(index - 1));
95 // Move between button groups and rows.
97 // Move between button groups.
98 $group = $container.parent().prev();
99 if ($group.length > 0) {
100 $group.find('.ckeditor-toolbar-group-buttons').append($button);
102 // Wrap between rows.
104 $container.closest('.ckeditor-row').prev().find('.ckeditor-toolbar-group').not('.placeholder').find('.ckeditor-toolbar-group-buttons').eq(-1).append($button);
109 else if (_.indexOf([39, 63235], event.keyCode) > -1) {
110 // Move between sibling buttons.
111 if (index < ($siblings.length - 1)) {
112 $button.insertAfter($container.children().eq(index + 1));
114 // Move between button groups. Moving right at the end of a row
115 // will create a new group.
117 $container.parent().next().find('.ckeditor-toolbar-group-buttons').prepend($button);
121 // Move buttons between rows and the available button set.
122 else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
123 dir = (_.indexOf([38, 63232], event.keyCode) > -1) ? 'prev' : 'next';
124 $row = $container.closest('.ckeditor-row')[dir]();
125 // Move the button back into the available button set.
126 if (dir === 'prev' && $row.length === 0) {
127 // If this is a divider, just destroy it.
128 if ($button.data('drupal-ckeditor-type') === 'separator') {
132 // Focus on the first button in the active toolbar.
133 $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).children().eq(0).children().trigger('focus');
135 // Otherwise, move it.
137 $availableButtons.prepend($button);
141 $row.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
145 // Move dividers between their container and the active toolbar.
146 else if (containerType === 'dividers') {
147 // Move the button to the active toolbar configuration when the down
148 // or up keys are pressed.
149 if (_.indexOf([40, 63233], event.keyCode) > -1) {
150 // Move the button to the first row, first button group index
152 $button = $button.clone(true);
153 $activeButtons.find('.ckeditor-toolbar-group-buttons').eq(0).prepend($button);
154 $target = $button.children();
159 // Attempt to move the button to the new toolbar position.
160 Drupal.ckeditor.registerButtonMove(this, $button, function (result) {
162 // Put the button back if the registration failed.
163 // If the button was in a row, then it was in the active toolbar
164 // configuration. The button was probably placed in a new group, but
165 // that action was canceled.
166 if (!result && $originalGroup) {
167 $originalGroup.find('.ckeditor-buttons').append($button);
169 // Otherwise refresh the sortables to acknowledge the new button
172 view.$el.find('.ui-sortable').sortable('refresh');
174 // Refocus the target button so that the user can continue from a
176 $target.trigger('focus');
179 event.preventDefault();
180 event.stopPropagation();
185 * Handles keypresses on a CKEditor configuration group.
187 * @param {jQuery.Event} event
188 * The keypress event triggered.
190 onPressGroup: function (event) {
193 63232, // Safari up arrow.
195 63233 // Safari down arrow.
197 var leftRightKeys = [
199 63234, // Safari left arrow.
201 63235 // Safari right arrow.
204 // Respond to an enter key press.
205 if (event.keyCode === 13) {
207 // Open the group renaming dialog in the next evaluation cycle so that
208 // this event can be cancelled and the bubbling wiped out. Otherwise,
209 // Firefox has issues because the page focus is shifted to the dialog
210 // along with the keydown event.
211 window.setTimeout(function () {
212 Drupal.ckeditor.openGroupNameDialog(view, $(event.currentTarget));
214 event.preventDefault();
215 event.stopPropagation();
218 // Respond to direction key presses.
219 if (_.indexOf(_.union(upDownKeys, leftRightKeys), event.keyCode) > -1) {
220 var $group = $(event.currentTarget);
221 var $container = $group.parent();
222 var $siblings = $container.children();
225 // Move groups between sibling groups.
226 if (_.indexOf(leftRightKeys, event.keyCode) > -1) {
227 index = $siblings.index($group);
228 // Move left between sibling groups.
229 if ((_.indexOf([37, 63234], event.keyCode) > -1)) {
231 $group.insertBefore($siblings.eq(index - 1));
233 // Wrap between rows. Insert the group before the placeholder group
234 // at the end of the previous row.
236 $group.insertBefore($container.closest('.ckeditor-row').prev().find('.ckeditor-toolbar-groups').children().eq(-1));
239 // Move right between sibling groups.
240 else if (_.indexOf([39, 63235], event.keyCode) > -1) {
241 // Move to the right if the next group is not a placeholder.
242 if (!$siblings.eq(index + 1).hasClass('placeholder')) {
243 $group.insertAfter($container.children().eq(index + 1));
245 // Wrap group between rows.
247 $container.closest('.ckeditor-row').next().find('.ckeditor-toolbar-groups').prepend($group);
252 // Move groups between rows.
253 else if (_.indexOf(upDownKeys, event.keyCode) > -1) {
254 dir = (_.indexOf([38, 63232], event.keyCode) > -1) ? 'prev' : 'next';
255 $group.closest('.ckeditor-row')[dir]().find('.ckeditor-toolbar-groups').eq(0).prepend($group);
258 Drupal.ckeditor.registerGroupMove(this, $group);
259 $group.trigger('focus');
260 event.preventDefault();
261 event.stopPropagation();
266 })(jQuery, Drupal, Backbone, _);