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