Chris@0
|
1 /**
|
Chris@0
|
2 * @file
|
Chris@0
|
3 * A Backbone View that provides the aural view of CKEditor toolbar
|
Chris@0
|
4 * configuration.
|
Chris@0
|
5 */
|
Chris@0
|
6
|
Chris@17
|
7 (function(Drupal, Backbone, $) {
|
Chris@17
|
8 Drupal.ckeditor.AuralView = Backbone.View.extend(
|
Chris@17
|
9 /** @lends Drupal.ckeditor.AuralView# */ {
|
Chris@17
|
10 /**
|
Chris@17
|
11 * @type {object}
|
Chris@17
|
12 */
|
Chris@17
|
13 events: {
|
Chris@17
|
14 'click .ckeditor-buttons a': 'announceButtonHelp',
|
Chris@17
|
15 'click .ckeditor-multiple-buttons a': 'announceSeparatorHelp',
|
Chris@17
|
16 'focus .ckeditor-button a': 'onFocus',
|
Chris@17
|
17 'focus .ckeditor-button-separator a': 'onFocus',
|
Chris@17
|
18 'focus .ckeditor-toolbar-group': 'onFocus',
|
Chris@17
|
19 },
|
Chris@0
|
20
|
Chris@17
|
21 /**
|
Chris@17
|
22 * Backbone View for CKEditor toolbar configuration; aural UX (output only).
|
Chris@17
|
23 *
|
Chris@17
|
24 * @constructs
|
Chris@17
|
25 *
|
Chris@17
|
26 * @augments Backbone.View
|
Chris@17
|
27 */
|
Chris@17
|
28 initialize() {
|
Chris@17
|
29 // Announce the button and group positions when the model is no longer
|
Chris@17
|
30 // dirty.
|
Chris@17
|
31 this.listenTo(this.model, 'change:isDirty', this.announceMove);
|
Chris@17
|
32 },
|
Chris@0
|
33
|
Chris@17
|
34 /**
|
Chris@17
|
35 * Calls announce on buttons and groups when their position is changed.
|
Chris@17
|
36 *
|
Chris@17
|
37 * @param {Drupal.ckeditor.ConfigurationModel} model
|
Chris@17
|
38 * The ckeditor configuration model.
|
Chris@17
|
39 * @param {bool} isDirty
|
Chris@17
|
40 * A model attribute that indicates if the changed toolbar configuration
|
Chris@17
|
41 * has been stored or not.
|
Chris@17
|
42 */
|
Chris@17
|
43 announceMove(model, isDirty) {
|
Chris@17
|
44 // Announce the position of a button or group after the model has been
|
Chris@17
|
45 // updated.
|
Chris@17
|
46 if (!isDirty) {
|
Chris@17
|
47 const item = document.activeElement || null;
|
Chris@17
|
48 if (item) {
|
Chris@17
|
49 const $item = $(item);
|
Chris@17
|
50 if ($item.hasClass('ckeditor-toolbar-group')) {
|
Chris@17
|
51 this.announceButtonGroupPosition($item);
|
Chris@17
|
52 } else if ($item.parent().hasClass('ckeditor-button')) {
|
Chris@17
|
53 this.announceButtonPosition($item.parent());
|
Chris@17
|
54 }
|
Chris@0
|
55 }
|
Chris@0
|
56 }
|
Chris@17
|
57 },
|
Chris@0
|
58
|
Chris@17
|
59 /**
|
Chris@17
|
60 * Handles the focus event of elements in the active and available toolbars.
|
Chris@17
|
61 *
|
Chris@17
|
62 * @param {jQuery.Event} event
|
Chris@17
|
63 * The focus event that was triggered.
|
Chris@17
|
64 */
|
Chris@17
|
65 onFocus(event) {
|
Chris@17
|
66 event.stopPropagation();
|
Chris@0
|
67
|
Chris@17
|
68 const $originalTarget = $(event.target);
|
Chris@17
|
69 const $currentTarget = $(event.currentTarget);
|
Chris@17
|
70 const $parent = $currentTarget.parent();
|
Chris@17
|
71 if (
|
Chris@17
|
72 $parent.hasClass('ckeditor-button') ||
|
Chris@17
|
73 $parent.hasClass('ckeditor-button-separator')
|
Chris@17
|
74 ) {
|
Chris@17
|
75 this.announceButtonPosition($currentTarget.parent());
|
Chris@17
|
76 } else if (
|
Chris@17
|
77 $originalTarget.attr('role') !== 'button' &&
|
Chris@17
|
78 $currentTarget.hasClass('ckeditor-toolbar-group')
|
Chris@17
|
79 ) {
|
Chris@17
|
80 this.announceButtonGroupPosition($currentTarget);
|
Chris@17
|
81 }
|
Chris@17
|
82 },
|
Chris@0
|
83
|
Chris@17
|
84 /**
|
Chris@17
|
85 * Announces the current position of a button group.
|
Chris@17
|
86 *
|
Chris@17
|
87 * @param {jQuery} $group
|
Chris@17
|
88 * A jQuery set that contains an li element that wraps a group of buttons.
|
Chris@17
|
89 */
|
Chris@17
|
90 announceButtonGroupPosition($group) {
|
Chris@17
|
91 const $groups = $group.parent().children();
|
Chris@17
|
92 const $row = $group.closest('.ckeditor-row');
|
Chris@17
|
93 const $rows = $row.parent().children();
|
Chris@17
|
94 const position = $groups.index($group) + 1;
|
Chris@17
|
95 const positionCount = $groups.not('.placeholder').length;
|
Chris@17
|
96 const row = $rows.index($row) + 1;
|
Chris@17
|
97 const rowCount = $rows.not('.placeholder').length;
|
Chris@17
|
98 let text = Drupal.t(
|
Chris@17
|
99 '@groupName button group in position @position of @positionCount in row @row of @rowCount.',
|
Chris@17
|
100 {
|
Chris@17
|
101 '@groupName': $group.attr(
|
Chris@17
|
102 'data-drupal-ckeditor-toolbar-group-name',
|
Chris@17
|
103 ),
|
Chris@17
|
104 '@position': position,
|
Chris@17
|
105 '@positionCount': positionCount,
|
Chris@17
|
106 '@row': row,
|
Chris@17
|
107 '@rowCount': rowCount,
|
Chris@17
|
108 },
|
Chris@17
|
109 );
|
Chris@0
|
110 // If this position is the first in the last row then tell the user that
|
Chris@0
|
111 // pressing the down arrow key will create a new row.
|
Chris@17
|
112 if (position === 1 && row === rowCount) {
|
Chris@0
|
113 text += '\n';
|
Chris@17
|
114 text += Drupal.t('Press the down arrow key to create a new row.');
|
Chris@0
|
115 }
|
Chris@0
|
116 Drupal.announce(text, 'assertive');
|
Chris@17
|
117 },
|
Chris@17
|
118
|
Chris@17
|
119 /**
|
Chris@17
|
120 * Announces current button position.
|
Chris@17
|
121 *
|
Chris@17
|
122 * @param {jQuery} $button
|
Chris@17
|
123 * A jQuery set that contains an li element that wraps a button.
|
Chris@17
|
124 */
|
Chris@17
|
125 announceButtonPosition($button) {
|
Chris@17
|
126 const $row = $button.closest('.ckeditor-row');
|
Chris@17
|
127 const $rows = $row.parent().children();
|
Chris@17
|
128 const $buttons = $button.closest('.ckeditor-buttons').children();
|
Chris@17
|
129 const $group = $button.closest('.ckeditor-toolbar-group');
|
Chris@17
|
130 const $groups = $group.parent().children();
|
Chris@17
|
131 const groupPosition = $groups.index($group) + 1;
|
Chris@17
|
132 const groupPositionCount = $groups.not('.placeholder').length;
|
Chris@17
|
133 const position = $buttons.index($button) + 1;
|
Chris@17
|
134 const positionCount = $buttons.length;
|
Chris@17
|
135 const row = $rows.index($row) + 1;
|
Chris@17
|
136 const rowCount = $rows.not('.placeholder').length;
|
Chris@17
|
137 // The name of the button separator is 'button separator' and its type
|
Chris@17
|
138 // is 'separator', so we do not want to print the type of this item,
|
Chris@17
|
139 // otherwise the UA will speak 'button separator separator'.
|
Chris@17
|
140 const type =
|
Chris@17
|
141 $button.attr('data-drupal-ckeditor-type') === 'separator'
|
Chris@17
|
142 ? ''
|
Chris@17
|
143 : Drupal.t('button');
|
Chris@17
|
144 let text;
|
Chris@17
|
145 // The button is located in the available button set.
|
Chris@17
|
146 if ($button.closest('.ckeditor-toolbar-disabled').length > 0) {
|
Chris@17
|
147 text = Drupal.t('@name @type.', {
|
Chris@17
|
148 '@name': $button.children().attr('aria-label'),
|
Chris@17
|
149 '@type': type,
|
Chris@17
|
150 });
|
Chris@17
|
151 text += `\n${Drupal.t('Press the down arrow key to activate.')}`;
|
Chris@17
|
152
|
Chris@17
|
153 Drupal.announce(text, 'assertive');
|
Chris@17
|
154 }
|
Chris@17
|
155 // The button is in the active toolbar.
|
Chris@17
|
156 else if ($group.not('.placeholder').length === 1) {
|
Chris@17
|
157 text = Drupal.t(
|
Chris@17
|
158 '@name @type in position @position of @positionCount in @groupName button group in row @row of @rowCount.',
|
Chris@17
|
159 {
|
Chris@17
|
160 '@name': $button.children().attr('aria-label'),
|
Chris@17
|
161 '@type': type,
|
Chris@17
|
162 '@position': position,
|
Chris@17
|
163 '@positionCount': positionCount,
|
Chris@17
|
164 '@groupName': $group.attr(
|
Chris@17
|
165 'data-drupal-ckeditor-toolbar-group-name',
|
Chris@17
|
166 ),
|
Chris@17
|
167 '@row': row,
|
Chris@17
|
168 '@rowCount': rowCount,
|
Chris@17
|
169 },
|
Chris@17
|
170 );
|
Chris@17
|
171 // If this position is the first in the last row then tell the user that
|
Chris@17
|
172 // pressing the down arrow key will create a new row.
|
Chris@17
|
173 if (groupPosition === 1 && position === 1 && row === rowCount) {
|
Chris@17
|
174 text += '\n';
|
Chris@17
|
175 text += Drupal.t(
|
Chris@17
|
176 'Press the down arrow key to create a new button group in a new row.',
|
Chris@17
|
177 );
|
Chris@17
|
178 }
|
Chris@17
|
179 // If this position is the last one in this row then tell the user that
|
Chris@17
|
180 // moving the button to the next group will create a new group.
|
Chris@17
|
181 if (
|
Chris@17
|
182 groupPosition === groupPositionCount &&
|
Chris@17
|
183 position === positionCount
|
Chris@17
|
184 ) {
|
Chris@17
|
185 text += '\n';
|
Chris@17
|
186 text += Drupal.t(
|
Chris@17
|
187 'This is the last group. Move the button forward to create a new group.',
|
Chris@17
|
188 );
|
Chris@17
|
189 }
|
Chris@17
|
190 Drupal.announce(text, 'assertive');
|
Chris@17
|
191 }
|
Chris@17
|
192 },
|
Chris@17
|
193
|
Chris@17
|
194 /**
|
Chris@17
|
195 * Provides help information when a button is clicked.
|
Chris@17
|
196 *
|
Chris@17
|
197 * @param {jQuery.Event} event
|
Chris@17
|
198 * The click event for the button click.
|
Chris@17
|
199 */
|
Chris@17
|
200 announceButtonHelp(event) {
|
Chris@17
|
201 const $link = $(event.currentTarget);
|
Chris@17
|
202 const $button = $link.parent();
|
Chris@17
|
203 const enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
|
Chris@17
|
204 let message;
|
Chris@17
|
205
|
Chris@17
|
206 if (enabled) {
|
Chris@17
|
207 message = Drupal.t('The "@name" button is currently enabled.', {
|
Chris@17
|
208 '@name': $link.attr('aria-label'),
|
Chris@17
|
209 });
|
Chris@17
|
210 message += `\n${Drupal.t(
|
Chris@17
|
211 'Use the keyboard arrow keys to change the position of this button.',
|
Chris@17
|
212 )}`;
|
Chris@17
|
213 message += `\n${Drupal.t(
|
Chris@17
|
214 'Press the up arrow key on the top row to disable the button.',
|
Chris@17
|
215 )}`;
|
Chris@17
|
216 } else {
|
Chris@17
|
217 message = Drupal.t('The "@name" button is currently disabled.', {
|
Chris@17
|
218 '@name': $link.attr('aria-label'),
|
Chris@17
|
219 });
|
Chris@17
|
220 message += `\n${Drupal.t(
|
Chris@17
|
221 'Use the down arrow key to move this button into the active toolbar.',
|
Chris@17
|
222 )}`;
|
Chris@17
|
223 }
|
Chris@17
|
224 Drupal.announce(message);
|
Chris@17
|
225 event.preventDefault();
|
Chris@17
|
226 },
|
Chris@17
|
227
|
Chris@17
|
228 /**
|
Chris@17
|
229 * Provides help information when a separator is clicked.
|
Chris@17
|
230 *
|
Chris@17
|
231 * @param {jQuery.Event} event
|
Chris@17
|
232 * The click event for the separator click.
|
Chris@17
|
233 */
|
Chris@17
|
234 announceSeparatorHelp(event) {
|
Chris@17
|
235 const $link = $(event.currentTarget);
|
Chris@17
|
236 const $button = $link.parent();
|
Chris@17
|
237 const enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
|
Chris@17
|
238 let message;
|
Chris@17
|
239
|
Chris@17
|
240 if (enabled) {
|
Chris@17
|
241 message = Drupal.t('This @name is currently enabled.', {
|
Chris@17
|
242 '@name': $link.attr('aria-label'),
|
Chris@17
|
243 });
|
Chris@17
|
244 message += `\n${Drupal.t(
|
Chris@17
|
245 'Use the keyboard arrow keys to change the position of this separator.',
|
Chris@17
|
246 )}`;
|
Chris@17
|
247 } else {
|
Chris@17
|
248 message = Drupal.t(
|
Chris@17
|
249 'Separators are used to visually split individual buttons.',
|
Chris@17
|
250 );
|
Chris@17
|
251 message += `\n${Drupal.t('This @name is currently disabled.', {
|
Chris@17
|
252 '@name': $link.attr('aria-label'),
|
Chris@17
|
253 })}`;
|
Chris@17
|
254 message += `\n${Drupal.t(
|
Chris@17
|
255 'Use the down arrow key to move this separator into the active toolbar.',
|
Chris@17
|
256 )}`;
|
Chris@17
|
257 message += `\n${Drupal.t(
|
Chris@17
|
258 'You may add multiple separators to each button group.',
|
Chris@17
|
259 )}`;
|
Chris@17
|
260 }
|
Chris@17
|
261 Drupal.announce(message);
|
Chris@17
|
262 event.preventDefault();
|
Chris@17
|
263 },
|
Chris@0
|
264 },
|
Chris@17
|
265 );
|
Chris@17
|
266 })(Drupal, Backbone, jQuery);
|