wai-website-theme 1.3.1 → 1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/_includes/different.html +2 -1
  3. data/_includes/external.html +2 -1
  4. data/_includes/header.html +2 -1
  5. data/_includes/menuitem.html +6 -2
  6. data/_includes/peoplelist.html +21 -0
  7. data/_includes/prevnext-navigation.html +56 -0
  8. data/_includes/{prevnext.html → prevnext-order.html} +9 -0
  9. data/_includes/translation-note-msg.html +5 -3
  10. data/_includes/video-player.html +2 -2
  11. data/_layouts/default.html +8 -1
  12. data/_layouts/news.html +7 -1
  13. data/_layouts/policy.html +7 -1
  14. data/_layouts/sidenav.html +8 -1
  15. data/_layouts/sidenavsidebar.html +8 -1
  16. data/assets/ableplayer/Gruntfile.js +2 -1
  17. data/assets/ableplayer/README.md +158 -85
  18. data/assets/ableplayer/build/ableplayer.dist.js +15445 -13823
  19. data/assets/ableplayer/build/ableplayer.js +15445 -13823
  20. data/assets/ableplayer/build/ableplayer.min.css +1 -2
  21. data/assets/ableplayer/build/ableplayer.min.js +3 -10
  22. data/assets/ableplayer/package-lock.json +944 -346
  23. data/assets/ableplayer/package.json +8 -8
  24. data/assets/ableplayer/scripts/ableplayer-base.js +515 -524
  25. data/assets/ableplayer/scripts/browser.js +158 -158
  26. data/assets/ableplayer/scripts/buildplayer.js +1750 -1682
  27. data/assets/ableplayer/scripts/caption.js +424 -401
  28. data/assets/ableplayer/scripts/chapters.js +259 -259
  29. data/assets/ableplayer/scripts/control.js +1831 -1594
  30. data/assets/ableplayer/scripts/description.js +333 -256
  31. data/assets/ableplayer/scripts/dialog.js +145 -145
  32. data/assets/ableplayer/scripts/dragdrop.js +746 -749
  33. data/assets/ableplayer/scripts/event.js +875 -696
  34. data/assets/ableplayer/scripts/initialize.js +819 -912
  35. data/assets/ableplayer/scripts/langs.js +979 -743
  36. data/assets/ableplayer/scripts/metadata.js +124 -124
  37. data/assets/ableplayer/scripts/misc.js +170 -137
  38. data/assets/ableplayer/scripts/preference.js +904 -904
  39. data/assets/ableplayer/scripts/search.js +172 -172
  40. data/assets/ableplayer/scripts/sign.js +82 -78
  41. data/assets/ableplayer/scripts/slider.js +449 -448
  42. data/assets/ableplayer/scripts/track.js +409 -309
  43. data/assets/ableplayer/scripts/transcript.js +684 -595
  44. data/assets/ableplayer/scripts/translation.js +63 -67
  45. data/assets/ableplayer/scripts/ttml2webvtt.js +85 -85
  46. data/assets/ableplayer/scripts/vimeo.js +448 -0
  47. data/assets/ableplayer/scripts/volume.js +395 -380
  48. data/assets/ableplayer/scripts/vts.js +1077 -1077
  49. data/assets/ableplayer/scripts/webvtt.js +766 -763
  50. data/assets/ableplayer/scripts/youtube.js +695 -478
  51. data/assets/ableplayer/styles/ableplayer.css +54 -46
  52. data/assets/ableplayer/translations/nl.js +54 -54
  53. data/assets/ableplayer/translations/pt-br.js +311 -0
  54. data/assets/ableplayer/translations/tr.js +311 -0
  55. data/assets/ableplayer/translations/zh-tw.js +1 -1
  56. data/assets/css/style.css +1 -1
  57. data/assets/css/style.css.map +1 -1
  58. data/assets/images/icons.svg +5 -5
  59. data/assets/scripts/main.js +7 -0
  60. data/assets/search/tipuesearch.js +3 -3
  61. metadata +8 -3
@@ -1,283 +1,360 @@
1
1
  (function ($) {
2
- AblePlayer.prototype.initDescription = function() {
2
+ AblePlayer.prototype.initDescription = function() {
3
3
 
4
- // set default mode for delivering description (open vs closed)
5
- // based on availability and user preference
4
+ // set default mode for delivering description (open vs closed)
5
+ // based on availability and user preference
6
6
 
7
- // called when player is being built, or when a user
8
- // toggles the Description button or changes a description-related preference
9
- // In the latter two scendarios, this.refreshingDesc == true via control.js > handleDescriptionToggle()
7
+ // called when player is being built, or when a user
8
+ // toggles the Description button or changes a description-related preference
9
+ // In the latter two scendarios, this.refreshingDesc == true via control.js > handleDescriptionToggle()
10
10
 
11
- // The following variables are applicable to delivery of description:
12
- // prefDesc == 1 if user wants description (i.e., Description button is on); else 0
13
- // prefDescFormat == either 'video' or 'text'
14
- // prefDescPause == 1 to pause video when description starts; else 0
15
- // prefVisibleDesc == 1 to visibly show text-based description area; else 0
16
- // hasOpenDesc == true if a described version of video is available via data-desc-src attribute
17
- // hasClosedDesc == true if a description text track is available
18
- // this.useDescFormat == either 'video' or 'text'; the format ultimately delivered
19
- // descOn == true if description of either type is on
20
- if (!this.refreshingDesc) {
21
- // this is the initial build
22
- // first, check to see if there's an open-described version of this video
23
- // checks only the first source since if a described version is provided,
24
- // it must be provided for all sources
25
- this.descFile = this.$sources.first().attr('data-desc-src');
26
- if (typeof this.descFile !== 'undefined') {
27
- this.hasOpenDesc = true;
28
- }
29
- else {
30
- // there's no open-described version via data-desc-src, but what about data-youtube-desc-src?
31
- if (this.youTubeDescId) {
32
- this.hasOpenDesc = true;
33
- }
34
- else { // there are no open-described versions from any source
35
- this.hasOpenDesc = false;
36
- }
37
- }
38
- }
39
- // update this.useDescFormat based on media availability & user preferences
40
- if (this.prefDesc) {
41
- if (this.hasOpenDesc && this.hasClosedDesc) {
42
- // both formats are available. Use whichever one user prefers
43
- this.useDescFormat = this.prefDescFormat;
44
- this.descOn = true;
45
- }
46
- else if (this.hasOpenDesc) {
47
- this.useDescFormat = 'video';
48
- this.descOn = true;
49
- }
50
- else if (this.hasClosedDesc) {
51
- this.useDescFormat = 'text';
52
- this.descOn = true;
53
- }
54
- }
55
- else { // description button is off
56
- if (this.refreshingDesc) { // user just now toggled it off
57
- this.prevDescFormat = this.useDescFormat;
58
- this.useDescFormat = false;
59
- this.descOn = false;
60
- }
61
- else { // desc has always been off
62
- this.useDescFormat = false;
63
- }
64
- }
11
+ // The following variables are applicable to delivery of description:
12
+ // prefDesc == 1 if user wants description (i.e., Description button is on); else 0
13
+ // prefDescFormat == either 'video' or 'text'
14
+ // prefDescPause == 1 to pause video when description starts; else 0
15
+ // prefVisibleDesc == 1 to visibly show text-based description area; else 0
16
+ // hasOpenDesc == true if a described version of video is available via data-desc-src attribute
17
+ // hasClosedDesc == true if a description text track is available
18
+ // this.useDescFormat == either 'video' or 'text'; the format ultimately delivered
19
+ // descOn == true if description of either type is on
65
20
 
66
- if (this.descOn) {
21
+ var thisObj = this;
67
22
 
68
- if (this.useDescFormat === 'video') {
23
+ if (!this.refreshingDesc) {
24
+ // this is the initial build
25
+ // first, check to see if there's an open-described version of this video
26
+ // checks only the first source since if a described version is provided,
27
+ // it must be provided for all sources
28
+ this.descFile = this.$sources.first().attr('data-desc-src');
29
+ if (typeof this.descFile !== 'undefined') {
30
+ this.hasOpenDesc = true;
31
+ }
32
+ else {
33
+ // there's no open-described version via data-desc-src,
34
+ // but what about data-youtube-desc-src or data-vimeo-desc-src?
35
+ if (this.youTubeDescId || this.vimeoDescId) {
36
+ this.hasOpenDesc = true;
37
+ }
38
+ else { // there are no open-described versions from any source
39
+ this.hasOpenDesc = false;
40
+ }
41
+ }
42
+ }
43
+ // update this.useDescFormat based on media availability & user preferences
44
+ if (this.prefDesc) {
45
+ if (this.hasOpenDesc && this.hasClosedDesc) {
46
+ // both formats are available. Use whichever one user prefers
47
+ this.useDescFormat = this.prefDescFormat;
48
+ this.descOn = true;
49
+ }
50
+ else if (this.hasOpenDesc) {
51
+ this.useDescFormat = 'video';
52
+ this.descOn = true;
53
+ }
54
+ else if (this.hasClosedDesc) {
55
+ this.useDescFormat = 'text';
56
+ this.descOn = true;
57
+ }
58
+ }
59
+ else { // description button is off
60
+ if (this.refreshingDesc) { // user just now toggled it off
61
+ this.prevDescFormat = this.useDescFormat;
62
+ this.useDescFormat = false;
63
+ this.descOn = false;
64
+ }
65
+ else { // desc has always been off
66
+ this.useDescFormat = false;
67
+ }
68
+ }
69
69
 
70
- if (!this.usingAudioDescription()) {
71
- // switched from non-described to described version
72
- this.swapDescription();
73
- }
74
- // hide description div
75
- this.$descDiv.hide();
76
- this.$descDiv.removeClass('able-clipped');
77
- }
78
- else if (this.useDescFormat === 'text') {
79
- this.$descDiv.show();
80
- if (this.prefVisibleDesc) { // make it visible to everyone
81
- this.$descDiv.removeClass('able-clipped');
82
- }
83
- else { // keep it visible to screen readers, but hide from everyone else
84
- this.$descDiv.addClass('able-clipped');
85
- }
86
- if (!this.swappingSrc) {
87
- this.showDescription(this.getElapsed());
88
- }
89
- }
90
- }
91
- else { // description is off.
70
+ if (this.useDescFormat === 'text') {
71
+ // check whether browser supports the Web Speech API
72
+ if (window.speechSynthesis) {
73
+ // It does!
74
+ this.synth = window.speechSynthesis;
75
+ this.descVoices = this.synth.getVoices();
76
+ // select the first voice that matches the track language
77
+ // available languages are identified with local suffixes (e.g., en-US)
78
+ // in case no matching voices are found, use the first voice in the voices array
79
+ this.descVoiceIndex = 0;
80
+ for (var i=0; i<this.descVoices.length; i++) {
81
+ if (this.captionLang.length === 2) {
82
+ // match only the first 2 characters of the lang code
83
+ if (this.descVoices[i].lang.substr(0,2).toLowerCase() === this.captionLang.toLowerCase()) {
84
+ this.descVoiceIndex = i;
85
+ break;
86
+ }
87
+ }
88
+ else {
89
+ // match the entire lang code
90
+ if (this.descVoices[i].lang.toLowerCase() === this.captionLang.toLowerCase()) {
91
+ this.descVoiceIndex = i;
92
+ break;
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ if (this.descOn) {
92
99
 
93
- if (this.prevDescFormat === 'video') { // user was previously using description via video
94
- if (this.usingAudioDescription()) {
95
- this.swapDescription();
96
- }
97
- }
98
- else if (this.prevDescFormat === 'text') { // user was previously using text description
99
- // hide description div from everyone, including screen reader users
100
- this.$descDiv.hide();
101
- this.$descDiv.removeClass('able-clipped');
102
- }
103
- }
104
- this.refreshingDesc = false;
105
- };
100
+ if (this.useDescFormat === 'video') {
101
+ if (!this.usingAudioDescription()) {
102
+ // switched from non-described to described version
103
+ this.swapDescription();
104
+ }
105
+ // hide description div
106
+ this.$descDiv.hide();
107
+ this.$descDiv.removeClass('able-clipped');
108
+ }
109
+ else if (this.useDescFormat === 'text') {
110
+ this.$descDiv.show();
111
+ if (this.prefVisibleDesc) { // make it visible to everyone
112
+ this.$descDiv.removeClass('able-clipped');
113
+ }
114
+ else { // keep it visible to screen readers, but hide from everyone else
115
+ this.$descDiv.addClass('able-clipped');
116
+ }
117
+ if (!this.swappingSrc) {
118
+ this.showDescription(this.elapsed);
119
+ }
120
+ }
121
+ }
122
+ else { // description is off.
106
123
 
107
- // Returns true if currently using audio description, false otherwise.
108
- AblePlayer.prototype.usingAudioDescription = function () {
124
+ if (this.prevDescFormat === 'video') { // user was previously using description via video
125
+ if (this.usingAudioDescription()) {
126
+ this.swapDescription();
127
+ }
128
+ }
129
+ else if (this.prevDescFormat === 'text') { // user was previously using text description
130
+ // hide description div from everyone, including screen reader users
131
+ this.$descDiv.hide();
132
+ this.$descDiv.removeClass('able-clipped');
133
+ }
134
+ }
135
+ this.refreshingDesc = false;
136
+ };
109
137
 
110
- if (this.player === 'youtube') {
111
- return (this.activeYouTubeId === this.youTubeDescId);
112
- }
113
- else {
114
- return (this.$sources.first().attr('data-desc-src') === this.$sources.first().attr('src'));
115
- }
116
- };
138
+ // Returns true if currently using audio description, false otherwise.
139
+ AblePlayer.prototype.usingAudioDescription = function () {
117
140
 
118
- AblePlayer.prototype.swapDescription = function() {
119
- // swap described and non-described source media, depending on which is playing
120
- // this function is only called in two circumstances:
121
- // 1. Swapping to described version when initializing player (based on user prefs & availability)
122
- // 2. User is toggling description
123
- var thisObj, i, origSrc, descSrc, srcType, jwSourceIndex, newSource;
141
+ if (this.player === 'youtube') {
142
+ return (this.activeYouTubeId === this.youTubeDescId);
143
+ }
144
+ else if (this.player === 'vimeo') {
145
+ return (this.activeVimeoId === this.vimeoDescId);
146
+ }
147
+ else {
148
+ return (this.$sources.first().attr('data-desc-src') === this.$sources.first().attr('src'));
149
+ }
150
+ };
124
151
 
125
- thisObj = this;
152
+ AblePlayer.prototype.swapDescription = function() {
126
153
 
127
- // get current time, and start new video at the same time
128
- // NOTE: There is some risk in resuming playback at the same start time
129
- // since the described version might include extended audio description (with pauses)
130
- // and might therefore be longer than the non-described version
131
- // The benefits though would seem to outweigh this risk
132
- this.swapTime = this.getElapsed(); // video will scrub to this time after loaded (see event.js)
154
+ // swap described and non-described source media, depending on which is playing
155
+ // this function is only called in two circumstances:
156
+ // 1. Swapping to described version when initializing player (based on user prefs & availability)
157
+ // 2. User is toggling description
158
+ var thisObj, i, origSrc, descSrc, srcType, newSource;
133
159
 
134
- if (this.descOn) {
135
- // user has requested the described version
136
- this.showAlert(this.tt.alertDescribedVersion);
137
- }
138
- else {
139
- // user has requested the non-described version
140
- this.showAlert(this.tt.alertNonDescribedVersion);
141
- }
160
+ thisObj = this;
142
161
 
143
- if (this.player === 'html5') {
162
+ // get current time, and start new video at the same time
163
+ // NOTE: There is some risk in resuming playback at the same start time
164
+ // since the described version might include extended audio description (with pauses)
165
+ // and might therefore be longer than the non-described version
166
+ // The benefits though would seem to outweigh this risk
144
167
 
145
- if (this.usingAudioDescription()) {
146
- // the described version is currently playing. Swap to non-described
147
- for (i=0; i < this.$sources.length; i++) {
148
- // for all <source> elements, replace src with data-orig-src
149
- origSrc = this.$sources[i].getAttribute('data-orig-src');
150
- srcType = this.$sources[i].getAttribute('type');
151
- if (origSrc) {
152
- this.$sources[i].setAttribute('src',origSrc);
153
- }
154
- if (srcType === 'video/mp4') {
155
- jwSourceIndex = i;
156
- }
157
- }
158
- // No need to check for this.initializing
159
- // This function is only called during initialization
160
- // if swapping from non-described to described
161
- this.swappingSrc = true;
162
- }
163
- else {
164
- // the non-described version is currently playing. Swap to described.
165
- for (i=0; i < this.$sources.length; i++) {
166
- // for all <source> elements, replace src with data-desc-src (if one exists)
167
- // then store original source in a new data-orig-src attribute
168
- origSrc = this.$sources[i].getAttribute('src');
169
- descSrc = this.$sources[i].getAttribute('data-desc-src');
170
- srcType = this.$sources[i].getAttribute('type');
171
- if (descSrc) {
172
- this.$sources[i].setAttribute('src',descSrc);
173
- this.$sources[i].setAttribute('data-orig-src',origSrc);
174
- }
175
- if (srcType === 'video/mp4') {
176
- jwSourceIndex = i;
177
- }
178
- }
179
- this.swappingSrc = true;
180
- }
168
+ this.swapTime = this.elapsed; // video will scrub to this time after loaded (see event.js)
169
+ if (this.descOn) {
170
+ // user has requested the described version
171
+ this.showAlert(this.tt.alertDescribedVersion);
172
+ }
173
+ else {
174
+ // user has requested the non-described version
175
+ this.showAlert(this.tt.alertNonDescribedVersion);
176
+ }
177
+ if (this.player === 'html5') {
181
178
 
182
- // now reload the source file.
183
- if (this.player === 'html5') {
184
- this.media.load();
185
- }
186
- else if (this.player === 'youtube') {
187
- // TODO: Load new youTubeId
188
- }
189
- else if (this.player === 'jw' && this.jwPlayer) {
190
- newSource = this.$sources[jwSourceIndex].getAttribute('src');
191
- this.jwPlayer.load({file: newSource});
192
- }
193
- }
194
- else if (this.player === 'youtube') {
179
+ if (this.usingAudioDescription()) {
180
+ // the described version is currently playing. Swap to non-described
181
+ for (i=0; i < this.$sources.length; i++) {
182
+ // for all <source> elements, replace src with data-orig-src
183
+ origSrc = this.$sources[i].getAttribute('data-orig-src');
184
+ srcType = this.$sources[i].getAttribute('type');
185
+ if (origSrc) {
186
+ this.$sources[i].setAttribute('src',origSrc);
187
+ }
188
+ }
189
+ // No need to check for this.initializing
190
+ // This function is only called during initialization
191
+ // if swapping from non-described to described
192
+ this.swappingSrc = true;
193
+ }
194
+ else {
195
+ // the non-described version is currently playing. Swap to described.
196
+ for (i=0; i < this.$sources.length; i++) {
197
+ // for all <source> elements, replace src with data-desc-src (if one exists)
198
+ // then store original source in a new data-orig-src attribute
199
+ origSrc = this.$sources[i].getAttribute('src');
200
+ descSrc = this.$sources[i].getAttribute('data-desc-src');
201
+ srcType = this.$sources[i].getAttribute('type');
202
+ if (descSrc) {
203
+ this.$sources[i].setAttribute('src',descSrc);
204
+ this.$sources[i].setAttribute('data-orig-src',origSrc);
205
+ }
206
+ }
207
+ this.swappingSrc = true;
208
+ }
195
209
 
196
- if (this.usingAudioDescription()) {
197
- // the described version is currently playing. Swap to non-described
198
- this.activeYouTubeId = this.youTubeId;
199
- this.showAlert(this.tt.alertNonDescribedVersion);
200
- }
201
- else {
202
- // the non-described version is currently playing. Swap to described.
203
- this.activeYouTubeId = this.youTubeDescId;
204
- this.showAlert(this.tt.alertDescribedVersion);
205
- }
206
- if (typeof this.youTubePlayer !== 'undefined') {
210
+ // now reload the source file.
211
+ if (this.player === 'html5') {
212
+ this.media.load();
213
+ }
214
+ }
215
+ else if (this.player === 'youtube') {
207
216
 
208
- // retrieve/setup captions for the new video from YouTube
209
- this.setupAltCaptions().then(function() {
217
+ if (this.usingAudioDescription()) {
218
+ // the described version is currently playing. Swap to non-described
219
+ this.activeYouTubeId = this.youTubeId;
220
+ this.showAlert(this.tt.alertNonDescribedVersion);
221
+ }
222
+ else {
223
+ // the non-described version is currently playing. Swap to described.
224
+ this.activeYouTubeId = this.youTubeDescId;
225
+ this.showAlert(this.tt.alertDescribedVersion);
226
+ }
227
+ if (typeof this.youTubePlayer !== 'undefined') {
210
228
 
211
- if (thisObj.playing) {
212
- // loadVideoById() loads and immediately plays the new video at swapTime
213
- thisObj.youTubePlayer.loadVideoById(thisObj.activeYouTubeId,thisObj.swapTime);
214
- }
215
- else {
216
- // cueVideoById() loads the new video and seeks to swapTime, but does not play
217
- thisObj.youTubePlayer.cueVideoById(thisObj.activeYouTubeId,thisObj.swapTime);
218
- }
219
- });
220
- }
221
- }
222
- };
229
+ // retrieve/setup captions for the new video from YouTube
230
+ this.setupAltCaptions().then(function() {
223
231
 
224
- AblePlayer.prototype.showDescription = function(now) {
232
+ if (thisObj.playing) {
233
+ // loadVideoById() loads and immediately plays the new video at swapTime
234
+ thisObj.youTubePlayer.loadVideoById(thisObj.activeYouTubeId,thisObj.swapTime);
235
+ }
236
+ else {
237
+ // cueVideoById() loads the new video and seeks to swapTime, but does not play
238
+ thisObj.youTubePlayer.cueVideoById(thisObj.activeYouTubeId,thisObj.swapTime);
239
+ }
240
+ });
241
+ }
242
+ }
243
+ else if (this.player === 'vimeo') {
244
+ if (this.usingAudioDescription()) {
245
+ // the described version is currently playing. Swap to non-described
246
+ this.activeVimeoId = this.vimeoId;
247
+ this.showAlert(this.tt.alertNonDescribedVersion);
248
+ }
249
+ else {
250
+ // the non-described version is currently playing. Swap to described.
251
+ this.activeVimeoId = this.vimeoDescId;
252
+ this.showAlert(this.tt.alertDescribedVersion);
253
+ }
254
+ // load the new video source
255
+ this.vimeoPlayer.loadVideo(this.activeVimeoId).then(function() {
225
256
 
226
- // there's a lot of redundancy between this function and showCaptions
227
- // Trying to combine them ended up in a mess though. Keeping as is for now.
257
+ if (thisObj.playing) {
258
+ // video was playing when user requested an alternative version
259
+ // seek to swapTime and continue playback (playback happens automatically)
260
+ thisObj.vimeoPlayer.setCurrentTime(thisObj.swapTime);
261
+ }
262
+ else {
263
+ // Vimeo autostarts immediately after video loads
264
+ // The "Described" button should not trigger playback, so stop this before the user notices.
265
+ thisObj.vimeoPlayer.pause();
266
+ }
267
+ });
268
+ }
269
+ };
228
270
 
229
- if (this.swappingSrc) {
230
- return;
231
- }
271
+ AblePlayer.prototype.showDescription = function(now) {
232
272
 
233
- var d, thisDescription;
234
- var flattenComponentForDescription = function (component) {
235
- var result = [];
236
- if (component.type === 'string') {
237
- result.push(component.value);
238
- }
239
- else {
240
- for (var ii = 0; ii < component.children.length; ii++) {
241
- result.push(flattenComponentForDescription(component.children[ii]));
242
- }
243
- }
244
- return result.join('');
245
- };
273
+ // there's a lot of redundancy between this function and showCaptions
274
+ // Trying to combine them ended up in a mess though. Keeping as is for now.
246
275
 
247
- var cues;
248
- if (this.selectedDescriptions) {
249
- cues = this.selectedDescriptions.cues;
250
- }
251
- else if (this.descriptions.length >= 1) {
252
- cues = this.descriptions[0].cues;
253
- }
254
- else {
255
- cues = [];
256
- }
257
- for (d = 0; d < cues.length; d++) {
258
- if ((cues[d].start <= now) && (cues[d].end > now)) {
259
- thisDescription = d;
260
- break;
261
- }
262
- }
263
- if (typeof thisDescription !== 'undefined') {
264
- if (this.currentDescription !== thisDescription) {
265
- // temporarily remove aria-live from $status in order to prevent description from being interrupted
266
- this.$status.removeAttr('aria-live');
267
- // load the new description into the container div
268
- this.$descDiv.html(flattenComponentForDescription(cues[thisDescription].components));
269
- if (this.prefDescPause) {
270
- this.pauseMedia();
271
- }
272
- this.currentDescription = thisDescription;
273
- }
274
- }
275
- else {
276
- this.$descDiv.html('');
277
- this.currentDescription = -1;
278
- // restore aria-live to $status
279
- this.$status.attr('aria-live','polite');
280
- }
281
- };
276
+ if (this.swappingSrc) {
277
+ return;
278
+ }
279
+
280
+ var thisObj, i, cues, d, thisDescription, descText, msg;
281
+ thisObj = this;
282
+
283
+ var flattenComponentForDescription = function (component) {
284
+ var result = [];
285
+ if (component.type === 'string') {
286
+ result.push(component.value);
287
+ }
288
+ else {
289
+ for (var i = 0; i < component.children.length; i++) {
290
+ result.push(flattenComponentForDescription(component.children[i]));
291
+ }
292
+ }
293
+ return result.join('');
294
+ };
295
+
296
+ if (this.selectedDescriptions) {
297
+ cues = this.selectedDescriptions.cues;
298
+ }
299
+ else if (this.descriptions.length >= 1) {
300
+ cues = this.descriptions[0].cues;
301
+ }
302
+ else {
303
+ cues = [];
304
+ }
305
+ for (d = 0; d < cues.length; d++) {
306
+ if ((cues[d].start <= now) && (cues[d].end > now)) {
307
+ thisDescription = d;
308
+ break;
309
+ }
310
+ }
311
+ if (typeof thisDescription !== 'undefined') {
312
+ if (this.currentDescription !== thisDescription) {
313
+ // temporarily remove aria-live from $status in order to prevent description from being interrupted
314
+ this.$status.removeAttr('aria-live');
315
+ descText = flattenComponentForDescription(cues[thisDescription].components);
316
+ if (typeof this.synth !== 'undefined' && typeof this.descVoiceIndex !== 'undefined') {
317
+ // browser supports speech synthesis and a voice has been selected in initDescription()
318
+ // use the web speech API
319
+ msg = new SpeechSynthesisUtterance();
320
+ msg.voice = this.descVoices[this.descVoiceIndex]; // Note: some voices don't support altering params
321
+ msg.voiceURI = 'native';
322
+ msg.volume = 1; // 0 to 1
323
+ msg.rate = 1.5; // 0.1 to 10 (1 is normal human speech; 2 is fast but easily decipherable; anything above 2 is blazing fast)
324
+ msg.pitch = 1; //0 to 2
325
+ msg.text = descText;
326
+ msg.lang = this.captionLang;
327
+ msg.onend = function(e) {
328
+ // NOTE: e.elapsedTime might be useful
329
+ if (thisObj.pausedForDescription) {
330
+ thisObj.playMedia();
331
+ }
332
+ };
333
+ this.synth.speak(msg);
334
+ if (this.prefVisibleDesc) {
335
+ // write description to the screen for sighted users
336
+ // but remove ARIA attributes since it isn't intended to be read by screen readers
337
+ this.$descDiv.html(descText).removeAttr('aria-live aria-atomic');
338
+ }
339
+ }
340
+ else {
341
+ // browser does not support speech synthesis
342
+ // load the new description into the container div for screen readers to read
343
+ this.$descDiv.html(descText);
344
+ }
345
+ if (this.prefDescPause) {
346
+ this.pauseMedia();
347
+ this.pausedForDescription = true;
348
+ }
349
+ this.currentDescription = thisDescription;
350
+ }
351
+ }
352
+ else {
353
+ this.$descDiv.html('');
354
+ this.currentDescription = -1;
355
+ // restore aria-live to $status
356
+ this.$status.attr('aria-live','polite');
357
+ }
358
+ };
282
359
 
283
360
  })(jQuery);