Version 1
[yaffs-website] / web / core / modules / ckeditor / js / views / AuralView.js
1 /**
2  * @file
3  * A Backbone View that provides the aural view of CKEditor toolbar
4  * configuration.
5  */
6
7 (function (Drupal, Backbone, $) {
8
9   'use strict';
10
11   Drupal.ckeditor.AuralView = Backbone.View.extend(/** @lends Drupal.ckeditor.AuralView# */{
12
13     /**
14      * @type {object}
15      */
16     events: {
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'
22     },
23
24     /**
25      * Backbone View for CKEditor toolbar configuration; aural UX (output only).
26      *
27      * @constructs
28      *
29      * @augments Backbone.View
30      */
31     initialize: function () {
32       // Announce the button and group positions when the model is no longer
33       // dirty.
34       this.listenTo(this.model, 'change:isDirty', this.announceMove);
35     },
36
37     /**
38      * Calls announce on buttons and groups when their position is changed.
39      *
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.
45      */
46     announceMove: function (model, isDirty) {
47       // Announce the position of a button or group after the model has been
48       // updated.
49       if (!isDirty) {
50         var item = document.activeElement || null;
51         if (item) {
52           var $item = $(item);
53           if ($item.hasClass('ckeditor-toolbar-group')) {
54             this.announceButtonGroupPosition($item);
55           }
56           else if ($item.parent().hasClass('ckeditor-button')) {
57             this.announceButtonPosition($item.parent());
58           }
59         }
60       }
61     },
62
63     /**
64      * Handles the focus event of elements in the active and available toolbars.
65      *
66      * @param {jQuery.Event} event
67      *   The focus event that was triggered.
68      */
69     onFocus: function (event) {
70       event.stopPropagation();
71
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());
77       }
78       else if ($originalTarget.attr('role') !== 'button' && $currentTarget.hasClass('ckeditor-toolbar-group')) {
79         this.announceButtonGroupPosition($currentTarget);
80       }
81     },
82
83     /**
84      * Announces the current position of a button group.
85      *
86      * @param {jQuery} $group
87      *   A jQuery set that contains an li element that wraps a group of buttons.
88      */
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,
101         '@row': row,
102         '@rowCount': rowCount
103       });
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) {
107         text += '\n';
108         text += Drupal.t('Press the down arrow key to create a new row.');
109       }
110       Drupal.announce(text, 'assertive');
111     },
112
113     /**
114      * Announces current button position.
115      *
116      * @param {jQuery} $button
117      *   A jQuery set that contains an li element that wraps a button.
118      */
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');
135       var text;
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'),
140           '@type': type
141         });
142         text += '\n' + Drupal.t('Press the down arrow key to activate.');
143
144         Drupal.announce(text, 'assertive');
145       }
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'),
150           '@type': type,
151           '@position': position,
152           '@positionCount': positionCount,
153           '@groupName': $group.attr('data-drupal-ckeditor-toolbar-group-name'),
154           '@row': row,
155           '@rowCount': rowCount
156         });
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) {
160           text += '\n';
161           text += Drupal.t('Press the down arrow key to create a new button group in a new row.');
162         }
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) {
166           text += '\n';
167           text += Drupal.t('This is the last group. Move the button forward to create a new group.');
168         }
169         Drupal.announce(text, 'assertive');
170       }
171     },
172
173     /**
174      * Provides help information when a button is clicked.
175      *
176      * @param {jQuery.Event} event
177      *   The click event for the button click.
178      */
179     announceButtonHelp: function (event) {
180       var $link = $(event.currentTarget);
181       var $button = $link.parent();
182       var enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
183       var message;
184
185       if (enabled) {
186         message = Drupal.t('The "@name" button is currently enabled.', {
187           '@name': $link.attr('aria-label')
188         });
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.');
191       }
192       else {
193         message = Drupal.t('The "@name" button is currently disabled.', {
194           '@name': $link.attr('aria-label')
195         });
196         message += '\n' + Drupal.t('Use the down arrow key to move this button into the active toolbar.');
197       }
198       Drupal.announce(message);
199       event.preventDefault();
200     },
201
202     /**
203      * Provides help information when a separator is clicked.
204      *
205      * @param {jQuery.Event} event
206      *   The click event for the separator click.
207      */
208     announceSeparatorHelp: function (event) {
209       var $link = $(event.currentTarget);
210       var $button = $link.parent();
211       var enabled = $button.closest('.ckeditor-toolbar-active').length > 0;
212       var message;
213
214       if (enabled) {
215         message = Drupal.t('This @name is currently enabled.', {
216           '@name': $link.attr('aria-label')
217         });
218         message += '\n' + Drupal.t('Use the keyboard arrow keys to change the position of this separator.');
219       }
220       else {
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')
224         });
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.');
227       }
228       Drupal.announce(message);
229       event.preventDefault();
230     }
231   });
232
233 })(Drupal, Backbone, jQuery);