Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / misc / announce.es6.js
1 /**
2  * @file
3  * Adds an HTML element and method to trigger audio UAs to read system messages.
4  *
5  * Use {@link Drupal.announce} to indicate to screen reader users that an
6  * element on the page has changed state. For instance, if clicking a link
7  * loads 10 more items into a list, one might announce the change like this.
8  *
9  * @example
10  * $('#search-list')
11  *   .on('itemInsert', function (event, data) {
12  *     // Insert the new items.
13  *     $(data.container.el).append(data.items.el);
14  *     // Announce the change to the page contents.
15  *     Drupal.announce(Drupal.t('@count items added to @container',
16  *       {'@count': data.items.length, '@container': data.container.title}
17  *     ));
18  *   });
19  */
20
21 (function(Drupal, debounce) {
22   let liveElement;
23   const announcements = [];
24
25   /**
26    * Builds a div element with the aria-live attribute and add it to the DOM.
27    *
28    * @type {Drupal~behavior}
29    *
30    * @prop {Drupal~behaviorAttach} attach
31    *   Attaches the behavior for drupalAnnounce.
32    */
33   Drupal.behaviors.drupalAnnounce = {
34     attach(context) {
35       // Create only one aria-live element.
36       if (!liveElement) {
37         liveElement = document.createElement('div');
38         liveElement.id = 'drupal-live-announce';
39         liveElement.className = 'visually-hidden';
40         liveElement.setAttribute('aria-live', 'polite');
41         liveElement.setAttribute('aria-busy', 'false');
42         document.body.appendChild(liveElement);
43       }
44     },
45   };
46
47   /**
48    * Concatenates announcements to a single string; appends to the live region.
49    */
50   function announce() {
51     const text = [];
52     let priority = 'polite';
53     let announcement;
54
55     // Create an array of announcement strings to be joined and appended to the
56     // aria live region.
57     const il = announcements.length;
58     for (let i = 0; i < il; i++) {
59       announcement = announcements.pop();
60       text.unshift(announcement.text);
61       // If any of the announcements has a priority of assertive then the group
62       // of joined announcements will have this priority.
63       if (announcement.priority === 'assertive') {
64         priority = 'assertive';
65       }
66     }
67
68     if (text.length) {
69       // Clear the liveElement so that repeated strings will be read.
70       liveElement.innerHTML = '';
71       // Set the busy state to true until the node changes are complete.
72       liveElement.setAttribute('aria-busy', 'true');
73       // Set the priority to assertive, or default to polite.
74       liveElement.setAttribute('aria-live', priority);
75       // Print the text to the live region. Text should be run through
76       // Drupal.t() before being passed to Drupal.announce().
77       liveElement.innerHTML = text.join('\n');
78       // The live text area is updated. Allow the AT to announce the text.
79       liveElement.setAttribute('aria-busy', 'false');
80     }
81   }
82
83   /**
84    * Triggers audio UAs to read the supplied text.
85    *
86    * The aria-live region will only read the text that currently populates its
87    * text node. Replacing text quickly in rapid calls to announce results in
88    * only the text from the most recent call to {@link Drupal.announce} being
89    * read. By wrapping the call to announce in a debounce function, we allow for
90    * time for multiple calls to {@link Drupal.announce} to queue up their
91    * messages. These messages are then joined and append to the aria-live region
92    * as one text node.
93    *
94    * @param {string} text
95    *   A string to be read by the UA.
96    * @param {string} [priority='polite']
97    *   A string to indicate the priority of the message. Can be either
98    *   'polite' or 'assertive'.
99    *
100    * @return {function}
101    *   The return of the call to debounce.
102    *
103    * @see http://www.w3.org/WAI/PF/aria-practices/#liveprops
104    */
105   Drupal.announce = function(text, priority) {
106     // Save the text and priority into a closure variable. Multiple simultaneous
107     // announcements will be concatenated and read in sequence.
108     announcements.push({
109       text,
110       priority,
111     });
112     // Immediately invoke the function that debounce returns. 200 ms is right at
113     // the cusp where humans notice a pause, so we will wait
114     // at most this much time before the set of queued announcements is read.
115     return debounce(announce, 200)();
116   };
117 })(Drupal, Drupal.debounce);