wai-website-theme 1.3.1 → 1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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);