3 * A Backbone View that provides the aural view of CKEditor toolbar
7 (function (Drupal, Backbone, $) {
11 Drupal.ckeditor.AuralView = Backbone.View.extend(/** @lends Drupal.ckeditor.AuralView# */{
17 'click .ckeditor-buttons a': 'announceButtonHelp',
18 'click .ckeditor-multiple-buttons a': 'announceSeparatorHelp',
19 'focus .ckeditor-button a': 'onFocus',
20 'focus .ckeditor-button-separator a': 'onFocus',
21 'focus .ckeditor-toolbar-group': 'onFocus'
25 * Backbone View for CKEditor toolbar configuration; aural UX (output only).
29 * @augments Backbone.View
31 initialize: function () {
32 // Announce the button and group positions when the model is no longer
34 this.listenTo(this.model, 'change:isDirty', this.announceMove);
38 * Calls announce on buttons and groups when their position is changed.
40 * @param {Drupal.ckeditor.ConfigurationModel} model
41 * The ckeditor configuration model.
42 * @param {bool} isDirty
43 * A model attribute that indicates if the changed toolbar configuration
44 * has been stored or not.
46 announceMove: function (model, isDirty) {
47 // Announce the position of a button or group after the model has been
50 var item = document.activeElement || null;
53 if ($item.hasClass('ckeditor-toolbar-group')) {
54 this.announceButtonGroupPosition($item);
56 else if ($item.parent().hasClass('ckeditor-button')) {
57 this.announceButtonPosition($item.parent());
64 * Handles the focus event of elements in the active and available toolbars.
66 * @param {jQuery.Event} event
67 * The focus event that was triggered.
69 onFocus: function (event) {
70 event.stopPropagation();
72 var $originalTarget = $(event.target);
73 var $currentTarget = $(event.currentTarget);
74 var $parent = $currentTarget.parent();
75 if ($parent.hasClass('ckeditor-button') || $parent.hasClass('ckeditor-button-separator')) {
76 this.announceButtonPosition($currentTarget.parent());
78 else if ($originalTarget.attr('role') !== 'button' && $currentTarget.hasClass('ckeditor-toolbar-group')) {
79 this.announceButtonGroupPosition($currentTarget);
84 * Announces the current position of a button group.
86 * @param {jQuery} $group
87 * A jQuery set that contains an li element that wraps a group of buttons.
89 announceButtonGroupPosition: function ($group) {
90 var $groups = $group.parent().children();
91 var $row = $group.closest('.ckeditor-row');
92 var $rows = $row.parent().children();
93 var position = $groups.index($group) + 1;
94 var positionCount = $groups.not('.placeholder').length;
95 var row = $rows.index($row) + 1;
96 var rowCount = $rows.not('.placeholder').length;
97 var text = Drupal.t('@groupName button group in position @position of @positionCount in row @row of @rowCount.', {
98 '@groupName': $group.attr('data-drupal-ckeditor-toolbar-group-name'),
99 '@position': position,
100 '@positionCount': positionCount,
102 '@rowCount': rowCount
104 // If this position is the first in the last row then tell the user that
105 // pressing the down arrow key will create a new row.
106 if (position === 1 && row === rowCount) {
108 text += Drupal.t('Press the down arrow key to create a new row.');
110 Drupal.announce(text, 'assertive');
114 * Announces current button position.
116 * @param {jQuery} $button
117 * A jQuery set that contains an li element that wraps a button.
119 announceButtonPosition: function ($button) {
120 var $row = $button.closest('.ckeditor-row');
121 var $rows = $row.parent().children();
122 var $buttons = $button.closest('.ckeditor-buttons').children();
123 var $group = $button.closest('.ckeditor-toolbar-group');
124 var $groups = $group.parent().children();
125 var groupPosition = $groups.index($group) + 1;
126 var groupPositionCount = $groups.not('.placeholder').length;
127 var position = $buttons.index($button) + 1;
128 var positionCount = $buttons.length;
129 var row = $rows.index($row) + 1;
130 var rowCount = $rows.not('.placeholder').length;
131 // The name of the button separator is 'button separator' and its type
132 // is 'separator', so we do not want to print the type of this item,
133 // otherwise the UA will speak 'button separator separator'.
134 var type = ($button.attr('data-drupal-ckeditor-type') === 'separator') ? '' : Drupal.t('button');
136 // The button is located in the available button set.
137 if ($button.closest('.ckeditor-toolbar-disabled').length > 0) {
138 text = Drupal.t('@name @type.', {
139 '@name': $button.children().attr('aria-label'),
142 text += '\n' + Drupal.t('Press the down arrow key to activate.');
144 Drupal.announce(text, 'assertive');
146 // The button is in the active toolbar.
147 else if ($group.not('.placeholder').length === 1) {
148 text = Drupal.t('@name @type in position @position of @positionCount in @groupName button group in row @row of @rowCount.', {
149 '@name': $button.children().attr('aria-label'),
151 '@position': position,
152 '@positionCount': positionCount,
153 '@groupName': $group.attr('data-drupal-ckeditor-toolbar-group-name'),
155 '@rowCount': rowCount
157 // If this position is the first in the last row then tell the user that
158 // pressing the down arrow key will create a new row.
159 if (groupPosition === 1 && position === 1 && row === rowCount) {
161 text += Drupal.t('Press the down arrow key to create a new button group in a new row.');
163 // If this position is the last one in this row then tell the user that
164 // moving the button to the next group will create a new group.
165 if (groupPosition === groupPositionCount && position === positionCount) {
167 text += Drupal.t('This is the last group. Move the button forward to create a new group.');
169 Drupal.announce(text, 'assertive');
174 * Provides help information when a button is clicked.
176 * @param {jQuery.Event} event
177 * The click event for the button click.
179 announceButtonHelp: function (event) {
180 var $link = $(event.currentTarget);
181 var $button = $link.parent();
182 var enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
186 message = Drupal.t('The "@name" button is currently enabled.', {
187 '@name': $link.attr('aria-label')
189 message += '\n' + Drupal.t('Use the keyboard arrow keys to change the position of this button.');
190 message += '\n' + Drupal.t('Press the up arrow key on the top row to disable the button.');
193 message = Drupal.t('The "@name" button is currently disabled.', {
194 '@name': $link.attr('aria-label')
196 message += '\n' + Drupal.t('Use the down arrow key to move this button into the active toolbar.');
198 Drupal.announce(message);
199 event.preventDefault();
203 * Provides help information when a separator is clicked.
205 * @param {jQuery.Event} event
206 * The click event for the separator click.
208 announceSeparatorHelp: function (event) {
209 var $link = $(event.currentTarget);
210 var $button = $link.parent();
211 var enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
215 message = Drupal.t('This @name is currently enabled.', {
216 '@name': $link.attr('aria-label')
218 message += '\n' + Drupal.t('Use the keyboard arrow keys to change the position of this separator.');
221 message = Drupal.t('Separators are used to visually split individual buttons.');
222 message += '\n' + Drupal.t('This @name is currently disabled.', {
223 '@name': $link.attr('aria-label')
225 message += '\n' + Drupal.t('Use the down arrow key to move this separator into the active toolbar.');
226 message += '\n' + Drupal.t('You may add multiple separators to each button group.');
228 Drupal.announce(message);
229 event.preventDefault();
233 })(Drupal, Backbone, jQuery);