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.
- checksums.yaml +4 -4
- data/_includes/different.html +2 -1
- data/_includes/external.html +2 -1
- data/_includes/header.html +2 -1
- data/_includes/menuitem.html +6 -2
- data/_includes/peoplelist.html +21 -0
- data/_includes/prevnext-navigation.html +56 -0
- data/_includes/{prevnext.html → prevnext-order.html} +9 -0
- data/_includes/translation-note-msg.html +5 -3
- data/_includes/video-player.html +2 -2
- data/_layouts/default.html +8 -1
- data/_layouts/news.html +7 -1
- data/_layouts/policy.html +7 -1
- data/_layouts/sidenav.html +8 -1
- data/_layouts/sidenavsidebar.html +8 -1
- data/assets/ableplayer/Gruntfile.js +2 -1
- data/assets/ableplayer/README.md +158 -85
- data/assets/ableplayer/build/ableplayer.dist.js +15445 -13823
- data/assets/ableplayer/build/ableplayer.js +15445 -13823
- data/assets/ableplayer/build/ableplayer.min.css +1 -2
- data/assets/ableplayer/build/ableplayer.min.js +3 -10
- data/assets/ableplayer/package-lock.json +944 -346
- data/assets/ableplayer/package.json +8 -8
- data/assets/ableplayer/scripts/ableplayer-base.js +515 -524
- data/assets/ableplayer/scripts/browser.js +158 -158
- data/assets/ableplayer/scripts/buildplayer.js +1750 -1682
- data/assets/ableplayer/scripts/caption.js +424 -401
- data/assets/ableplayer/scripts/chapters.js +259 -259
- data/assets/ableplayer/scripts/control.js +1831 -1594
- data/assets/ableplayer/scripts/description.js +333 -256
- data/assets/ableplayer/scripts/dialog.js +145 -145
- data/assets/ableplayer/scripts/dragdrop.js +746 -749
- data/assets/ableplayer/scripts/event.js +875 -696
- data/assets/ableplayer/scripts/initialize.js +819 -912
- data/assets/ableplayer/scripts/langs.js +979 -743
- data/assets/ableplayer/scripts/metadata.js +124 -124
- data/assets/ableplayer/scripts/misc.js +170 -137
- data/assets/ableplayer/scripts/preference.js +904 -904
- data/assets/ableplayer/scripts/search.js +172 -172
- data/assets/ableplayer/scripts/sign.js +82 -78
- data/assets/ableplayer/scripts/slider.js +449 -448
- data/assets/ableplayer/scripts/track.js +409 -309
- data/assets/ableplayer/scripts/transcript.js +684 -595
- data/assets/ableplayer/scripts/translation.js +63 -67
- data/assets/ableplayer/scripts/ttml2webvtt.js +85 -85
- data/assets/ableplayer/scripts/vimeo.js +448 -0
- data/assets/ableplayer/scripts/volume.js +395 -380
- data/assets/ableplayer/scripts/vts.js +1077 -1077
- data/assets/ableplayer/scripts/webvtt.js +766 -763
- data/assets/ableplayer/scripts/youtube.js +695 -478
- data/assets/ableplayer/styles/ableplayer.css +54 -46
- data/assets/ableplayer/translations/nl.js +54 -54
- data/assets/ableplayer/translations/pt-br.js +311 -0
- data/assets/ableplayer/translations/tr.js +311 -0
- data/assets/ableplayer/translations/zh-tw.js +1 -1
- data/assets/css/style.css +1 -1
- data/assets/css/style.css.map +1 -1
- data/assets/images/icons.svg +5 -5
- data/assets/scripts/main.js +7 -0
- data/assets/search/tipuesearch.js +3 -3
- metadata +8 -3
@@ -1,162 +1,162 @@
|
|
1
1
|
(function ($) {
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
3
|
+
AblePlayer.prototype.getUserAgent = function() {
|
4
|
+
|
5
|
+
// Whenever possible we avoid browser sniffing. Better to do feature detection.
|
6
|
+
// However, in case it's needed...
|
7
|
+
// this function defines a userAgent array that can be used to query for common browsers and OSs
|
8
|
+
// NOTE: This would be much simpler with jQuery.browser but that was removed from jQuery 1.9
|
9
|
+
// http://api.jquery.com/jQuery.browser/
|
10
|
+
this.userAgent = {};
|
11
|
+
this.userAgent.browser = {};
|
12
|
+
|
13
|
+
// Test for common browsers
|
14
|
+
if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent)){ //test for Firefox/x.x or Firefox x.x (ignoring remaining digits);
|
15
|
+
this.userAgent.browser.name = 'Firefox';
|
16
|
+
this.userAgent.browser.version = RegExp.$1; // capture x.x portion
|
17
|
+
}
|
18
|
+
else if (/MSIE (\d+\.\d+);/.test(navigator.userAgent)) { //test for MSIE x.x (IE10 or lower)
|
19
|
+
this.userAgent.browser.name = 'Internet Explorer';
|
20
|
+
this.userAgent.browser.version = RegExp.$1;
|
21
|
+
}
|
22
|
+
else if (/Trident.*rv[ :]*(\d+\.\d+)/.test(navigator.userAgent)) { // test for IE11 or higher
|
23
|
+
this.userAgent.browser.name = 'Internet Explorer';
|
24
|
+
this.userAgent.browser.version = RegExp.$1;
|
25
|
+
}
|
26
|
+
else if (/Edge[\/\s](\d+\.\d+)/.test(navigator.userAgent)) { // test for MS Edge
|
27
|
+
this.userAgent.browser.name = 'Edge';
|
28
|
+
this.userAgent.browser.version = RegExp.$1;
|
29
|
+
}
|
30
|
+
else if (/OPR\/(\d+\.\d+)/i.test(navigator.userAgent)) { // Opera 15 or over
|
31
|
+
this.userAgent.browser.name = 'Opera';
|
32
|
+
this.userAgent.browser.version = RegExp.$1;
|
33
|
+
}
|
34
|
+
else if (/Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor)) {
|
35
|
+
this.userAgent.browser.name = 'Chrome';
|
36
|
+
if (/Chrome[\/\s](\d+\.\d+)/.test(navigator.userAgent)) {
|
37
|
+
this.userAgent.browser.version = RegExp.$1;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
else if (/Safari/.test(navigator.userAgent) && /Apple Computer/.test(navigator.vendor)) {
|
41
|
+
this.userAgent.browser.name = 'Safari';
|
42
|
+
if (/Version[\/\s](\d+\.\d+)/.test(navigator.userAgent)) {
|
43
|
+
this.userAgent.browser.version = RegExp.$1;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
else {
|
47
|
+
this.userAgent.browser.name = 'Unknown';
|
48
|
+
this.userAgent.browser.version = 'Unknown';
|
49
|
+
}
|
50
|
+
|
51
|
+
// Now test for common operating systems
|
52
|
+
if (window.navigator.userAgent.indexOf("Windows NT 6.2") != -1) {
|
53
|
+
this.userAgent.os = "Windows 8";
|
54
|
+
}
|
55
|
+
else if (window.navigator.userAgent.indexOf("Windows NT 6.1") != -1) {
|
56
|
+
this.userAgent.os = "Windows 7";
|
57
|
+
}
|
58
|
+
else if (window.navigator.userAgent.indexOf("Windows NT 6.0") != -1) {
|
59
|
+
this.userAgent.os = "Windows Vista";
|
60
|
+
}
|
61
|
+
else if (window.navigator.userAgent.indexOf("Windows NT 5.1") != -1) {
|
62
|
+
this.userAgent.os = "Windows XP";
|
63
|
+
}
|
64
|
+
else if (window.navigator.userAgent.indexOf("Windows NT 5.0") != -1) {
|
65
|
+
this.userAgent.os = "Windows 2000";
|
66
|
+
}
|
67
|
+
else if (window.navigator.userAgent.indexOf("Mac")!=-1) {
|
68
|
+
this.userAgent.os = "Mac/iOS";
|
69
|
+
}
|
70
|
+
else if (window.navigator.userAgent.indexOf("X11")!=-1) {
|
71
|
+
this.userAgent.os = "UNIX";
|
72
|
+
}
|
73
|
+
else if (window.navigator.userAgent.indexOf("Linux")!=-1) {
|
74
|
+
this.userAgent.os = "Linux";
|
75
|
+
}
|
76
|
+
if (this.debug) {
|
77
|
+
console.log('User agent:' + navigator.userAgent);
|
78
|
+
console.log('Vendor: ' + navigator.vendor);
|
79
|
+
console.log('Browser: ' + this.userAgent.browser.name);
|
80
|
+
console.log('Version: ' + this.userAgent.browser.version);
|
81
|
+
console.log('OS: ' + this.userAgent.os);
|
82
|
+
}
|
83
|
+
};
|
84
|
+
|
85
|
+
AblePlayer.prototype.isUserAgent = function(which) {
|
86
|
+
|
87
|
+
var userAgent = navigator.userAgent.toLowerCase();
|
88
|
+
if (this.debug) {
|
89
|
+
console.log('User agent: ' + userAgent);
|
90
|
+
}
|
91
|
+
if (userAgent.indexOf(which.toLowerCase()) !== -1) {
|
92
|
+
return true;
|
93
|
+
}
|
94
|
+
else {
|
95
|
+
return false;
|
96
|
+
}
|
97
|
+
};
|
98
|
+
|
99
|
+
AblePlayer.prototype.isIOS = function(version) {
|
100
|
+
|
101
|
+
// return true if this is IOS
|
102
|
+
// if version is provided check for a particular version
|
103
|
+
|
104
|
+
var userAgent, iOS;
|
105
|
+
|
106
|
+
userAgent = navigator.userAgent.toLowerCase();
|
107
|
+
iOS = /ipad|iphone|ipod/.exec(userAgent);
|
108
|
+
if (iOS) {
|
109
|
+
if (typeof version !== 'undefined') {
|
110
|
+
if (userAgent.indexOf('os ' + version) !== -1) {
|
111
|
+
// this is the target version of iOS
|
112
|
+
return true;
|
113
|
+
}
|
114
|
+
else {
|
115
|
+
return false;
|
116
|
+
}
|
117
|
+
}
|
118
|
+
else {
|
119
|
+
// no version was specified
|
120
|
+
return true;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
else {
|
124
|
+
// this is not IOS
|
125
|
+
return false;
|
126
|
+
}
|
127
|
+
};
|
128
|
+
|
129
|
+
AblePlayer.prototype.browserSupportsVolume = function() {
|
130
|
+
|
131
|
+
// ideally we could test for volume support
|
132
|
+
// However, that doesn't seem to be reliable
|
133
|
+
// http://stackoverflow.com/questions/12301435/html5-video-tag-volume-support
|
134
|
+
|
135
|
+
var userAgent, noVolume;
|
136
|
+
|
137
|
+
userAgent = navigator.userAgent.toLowerCase();
|
138
|
+
noVolume = /ipad|iphone|ipod|android|blackberry|windows ce|windows phone|webos|playbook/.exec(userAgent);
|
139
|
+
if (noVolume) {
|
140
|
+
if (noVolume[0] === 'android' && /firefox/.test(userAgent)) {
|
141
|
+
// Firefox on android DOES support changing the volume:
|
142
|
+
return true;
|
143
|
+
}
|
144
|
+
else {
|
145
|
+
return false;
|
146
|
+
}
|
147
|
+
}
|
148
|
+
else {
|
149
|
+
// as far as we know, this userAgent supports volume control
|
150
|
+
return true;
|
151
|
+
}
|
152
|
+
};
|
153
|
+
|
154
|
+
AblePlayer.prototype.nativeFullscreenSupported = function () {
|
155
|
+
|
156
|
+
return document.fullscreenEnabled ||
|
157
|
+
document.webkitFullscreenEnabled ||
|
158
|
+
document.mozFullScreenEnabled ||
|
159
|
+
document.msFullscreenEnabled;
|
160
|
+
};
|
161
161
|
|
162
162
|
})(jQuery);
|
@@ -1,1689 +1,1757 @@
|
|
1
1
|
(function ($) {
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1137
|
-
|
1138
|
-
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1145
|
-
|
1146
|
-
|
1147
|
-
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
|
1192
|
-
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
1221
|
-
|
1222
|
-
|
1223
|
-
|
1224
|
-
|
1225
|
-
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
|
1269
|
-
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1296
|
-
|
1297
|
-
|
1298
|
-
|
1299
|
-
|
1300
|
-
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
1340
|
-
|
1341
|
-
|
1342
|
-
|
1343
|
-
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
1354
|
-
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
|
1364
|
-
|
1365
|
-
|
1366
|
-
|
1367
|
-
|
1368
|
-
|
1369
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
1435
|
-
|
1436
|
-
|
1437
|
-
|
1438
|
-
|
1439
|
-
|
1440
|
-
|
1441
|
-
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
xhr.onerror = onErrorTimeout(xhr);
|
1454
|
-
xhr.ontimeout = onErrorTimeout(xhr);
|
1455
|
-
xhr.open('GET', base);
|
1456
|
-
xhr.send();
|
1457
|
-
inProgressCount += 1;
|
1458
|
-
}
|
1459
|
-
}
|
1460
|
-
}
|
1461
|
-
else {
|
1462
|
-
if (!isHidden) {
|
1463
|
-
if (cache[base] === undefined) {
|
1464
|
-
// remember this URL if the use element was not empty and no request was sent
|
1465
|
-
cache[base] = true;
|
1466
|
-
}
|
1467
|
-
else if (cache[base].onload) {
|
1468
|
-
// if it turns out that prepending the SVG is not necessary,
|
1469
|
-
// abort the in-progress xhr.
|
1470
|
-
cache[base].abort();
|
1471
|
-
cache[base].onload = undefined;
|
1472
|
-
cache[base] = true;
|
1473
|
-
}
|
1474
|
-
}
|
1475
|
-
}
|
1476
|
-
}
|
1477
|
-
uses = '';
|
1478
|
-
inProgressCount += 1;
|
1479
|
-
observeIfDone();
|
1480
|
-
};
|
3
|
+
AblePlayer.prototype.injectPlayerCode = function() {
|
4
|
+
|
5
|
+
// create and inject surrounding HTML structure
|
6
|
+
// If IOS:
|
7
|
+
// If video:
|
8
|
+
// IOS does not support any of the player's functionality
|
9
|
+
// - everything plays in its own player
|
10
|
+
// Therefore, AblePlayer is not loaded & all functionality is disabled
|
11
|
+
// (this all determined. If this is IOS && video, this function is never called)
|
12
|
+
// If audio:
|
13
|
+
// HTML cannot be injected as a *parent* of the <audio> element
|
14
|
+
// It is therefore injected *after* the <audio> element
|
15
|
+
// This is only a problem in IOS 6 and earlier,
|
16
|
+
// & is a known bug, fixed in IOS 7
|
17
|
+
|
18
|
+
var thisObj, vidcapContainer, prefsGroups, i;
|
19
|
+
thisObj = this;
|
20
|
+
|
21
|
+
// create three wrappers and wrap them around the media element. From inner to outer:
|
22
|
+
// $mediaContainer - contains the original media element
|
23
|
+
// $ableDiv - contains the media player and all its objects (e.g., captions, controls, descriptions)
|
24
|
+
// $ableWrapper - contains additional widgets (e.g., transcript window, sign window)
|
25
|
+
this.$mediaContainer = this.$media.wrap('<div class="able-media-container"></div>').parent();
|
26
|
+
this.$ableDiv = this.$mediaContainer.wrap('<div class="able"></div>').parent();
|
27
|
+
this.$ableWrapper = this.$ableDiv.wrap('<div class="able-wrapper"></div>').parent();
|
28
|
+
|
29
|
+
// NOTE: Excluding the following from youtube was resulting in a player
|
30
|
+
// that exceeds the width of the YouTube video
|
31
|
+
// Unclear why it was originally excluded; commented out in 3.1.20
|
32
|
+
// if (this.player !== 'youtube') {
|
33
|
+
this.$ableWrapper.css({
|
34
|
+
'max-width': this.playerMaxWidth + 'px'
|
35
|
+
});
|
36
|
+
|
37
|
+
this.injectOffscreenHeading();
|
38
|
+
|
39
|
+
if (this.mediaType === 'video') {
|
40
|
+
// youtube adds its own big play button
|
41
|
+
// don't show ours *unless* video has a poster attribute
|
42
|
+
// (which obstructs the YouTube poster & big play button)
|
43
|
+
if (this.iconType != 'image' && (this.player !== 'youtube' || this.hasPoster)) {
|
44
|
+
this.injectBigPlayButton();
|
45
|
+
}
|
46
|
+
|
47
|
+
// add container that captions or description will be appended to
|
48
|
+
// Note: new Jquery object must be assigned _after_ wrap, hence the temp vidcapContainer variable
|
49
|
+
vidcapContainer = $('<div>',{
|
50
|
+
'class' : 'able-vidcap-container'
|
51
|
+
});
|
52
|
+
this.$vidcapContainer = this.$mediaContainer.wrap(vidcapContainer).parent();
|
53
|
+
}
|
54
|
+
this.injectPlayerControlArea();
|
55
|
+
this.injectTextDescriptionArea();
|
56
|
+
this.injectAlert();
|
57
|
+
this.injectPlaylist();
|
58
|
+
};
|
59
|
+
|
60
|
+
AblePlayer.prototype.injectOffscreenHeading = function () {
|
61
|
+
// Inject an offscreen heading to the media container.
|
62
|
+
// If heading hasn't already been manually defined via data-heading-level,
|
63
|
+
// automatically assign a level that is one level deeper than the closest parent heading
|
64
|
+
// as determined by getNextHeadingLevel()
|
65
|
+
var headingType;
|
66
|
+
if (this.playerHeadingLevel == '0') {
|
67
|
+
// do NOT inject a heading (at author's request)
|
68
|
+
}
|
69
|
+
else {
|
70
|
+
if (typeof this.playerHeadingLevel === 'undefined') {
|
71
|
+
this.playerHeadingLevel = this.getNextHeadingLevel(this.$ableDiv); // returns in integer 1-6
|
72
|
+
}
|
73
|
+
headingType = 'h' + this.playerHeadingLevel.toString();
|
74
|
+
this.$headingDiv = $('<' + headingType + '>');
|
75
|
+
this.$ableDiv.prepend(this.$headingDiv);
|
76
|
+
this.$headingDiv.addClass('able-offscreen');
|
77
|
+
this.$headingDiv.text(this.tt.playerHeading);
|
78
|
+
}
|
79
|
+
};
|
80
|
+
|
81
|
+
AblePlayer.prototype.injectBigPlayButton = function () {
|
82
|
+
|
83
|
+
this.$bigPlayButton = $('<button>', {
|
84
|
+
'class': 'able-big-play-button icon-play',
|
85
|
+
'aria-hidden': true,
|
86
|
+
'tabindex': -1
|
87
|
+
});
|
88
|
+
|
89
|
+
var thisObj = this;
|
90
|
+
this.$bigPlayButton.click(function () {
|
91
|
+
thisObj.handlePlay();
|
92
|
+
});
|
93
|
+
|
94
|
+
this.$mediaContainer.append(this.$bigPlayButton);
|
95
|
+
};
|
96
|
+
|
97
|
+
AblePlayer.prototype.injectPlayerControlArea = function () {
|
98
|
+
|
99
|
+
this.$playerDiv = $('<div>', {
|
100
|
+
'class' : 'able-player',
|
101
|
+
'role' : 'region',
|
102
|
+
'aria-label' : this.mediaType + ' player'
|
103
|
+
});
|
104
|
+
this.$playerDiv.addClass('able-'+this.mediaType);
|
105
|
+
|
106
|
+
// The default skin depends a bit on a Now Playing div
|
107
|
+
// so go ahead and add one
|
108
|
+
// However, it's only populated if this.showNowPlaying = true
|
109
|
+
this.$nowPlayingDiv = $('<div>',{
|
110
|
+
'class' : 'able-now-playing',
|
111
|
+
'aria-live' : 'assertive',
|
112
|
+
'aria-atomic': 'true'
|
113
|
+
});
|
114
|
+
|
115
|
+
this.$controllerDiv = $('<div>',{
|
116
|
+
'class' : 'able-controller'
|
117
|
+
});
|
118
|
+
this.$controllerDiv.addClass('able-' + this.iconColor + '-controls');
|
119
|
+
|
120
|
+
this.$statusBarDiv = $('<div>',{
|
121
|
+
'class' : 'able-status-bar'
|
122
|
+
});
|
123
|
+
this.$timer = $('<span>',{
|
124
|
+
'class' : 'able-timer'
|
125
|
+
});
|
126
|
+
this.$elapsedTimeContainer = $('<span>',{
|
127
|
+
'class': 'able-elapsedTime',
|
128
|
+
text: '0:00'
|
129
|
+
});
|
130
|
+
this.$durationContainer = $('<span>',{
|
131
|
+
'class': 'able-duration'
|
132
|
+
});
|
133
|
+
this.$timer.append(this.$elapsedTimeContainer).append(this.$durationContainer);
|
134
|
+
|
135
|
+
this.$speed = $('<span>',{
|
136
|
+
'class' : 'able-speed',
|
137
|
+
'aria-live' : 'assertive'
|
138
|
+
}).text(this.tt.speed + ': 1x');
|
139
|
+
|
140
|
+
this.$status = $('<span>',{
|
141
|
+
'class' : 'able-status',
|
142
|
+
'aria-live' : 'polite'
|
143
|
+
});
|
144
|
+
|
145
|
+
// Put everything together.
|
146
|
+
this.$statusBarDiv.append(this.$timer, this.$speed, this.$status);
|
147
|
+
this.$playerDiv.append(this.$nowPlayingDiv, this.$controllerDiv, this.$statusBarDiv);
|
148
|
+
this.$ableDiv.append(this.$playerDiv);
|
149
|
+
};
|
150
|
+
|
151
|
+
AblePlayer.prototype.injectTextDescriptionArea = function () {
|
152
|
+
|
153
|
+
// create a div for exposing description
|
154
|
+
// description will be exposed via role="alert" & announced by screen readers
|
155
|
+
this.$descDiv = $('<div>',{
|
156
|
+
'class': 'able-descriptions',
|
157
|
+
'aria-live': 'assertive',
|
158
|
+
'aria-atomic': 'true'
|
159
|
+
});
|
160
|
+
// Start off with description hidden.
|
161
|
+
// It will be exposed conditionally within description.js > initDescription()
|
162
|
+
this.$descDiv.hide();
|
163
|
+
this.$ableDiv.append(this.$descDiv);
|
164
|
+
};
|
165
|
+
|
166
|
+
AblePlayer.prototype.getDefaultWidth = function(which) {
|
167
|
+
|
168
|
+
// return default width of resizable elements
|
169
|
+
// these values are somewhat arbitrary, but seem to result in good usability
|
170
|
+
// if users disagree, they can resize (and resposition) them
|
171
|
+
if (which === 'transcript') {
|
172
|
+
return 450;
|
173
|
+
}
|
174
|
+
else if (which === 'sign') {
|
175
|
+
return 400;
|
176
|
+
}
|
177
|
+
};
|
178
|
+
|
179
|
+
AblePlayer.prototype.positionDraggableWindow = function (which, width) {
|
180
|
+
|
181
|
+
// which is either 'transcript' or 'sign'
|
182
|
+
|
183
|
+
var cookie, cookiePos, $window, dragged, windowPos, currentWindowPos, firstTime, zIndex;
|
184
|
+
|
185
|
+
cookie = this.getCookie();
|
186
|
+
if (which === 'transcript') {
|
187
|
+
$window = this.$transcriptArea;
|
188
|
+
if (typeof cookie.transcript !== 'undefined') {
|
189
|
+
cookiePos = cookie.transcript;
|
190
|
+
}
|
191
|
+
}
|
192
|
+
else if (which === 'sign') {
|
193
|
+
$window = this.$signWindow;
|
194
|
+
if (typeof cookie.transcript !== 'undefined') {
|
195
|
+
cookiePos = cookie.sign;
|
196
|
+
}
|
197
|
+
}
|
198
|
+
if (typeof cookiePos !== 'undefined' && !($.isEmptyObject(cookiePos))) {
|
199
|
+
// position window using stored values from cookie
|
200
|
+
$window.css({
|
201
|
+
'position': cookiePos['position'],
|
202
|
+
'width': cookiePos['width'],
|
203
|
+
'z-index': cookiePos['zindex']
|
204
|
+
});
|
205
|
+
if (cookiePos['position'] === 'absolute') {
|
206
|
+
$window.css({
|
207
|
+
'top': cookiePos['top'],
|
208
|
+
'left': cookiePos['left']
|
209
|
+
});
|
210
|
+
}
|
211
|
+
// since cookie is not page-specific, z-index needs may vary across different pages
|
212
|
+
this.updateZIndex(which);
|
213
|
+
}
|
214
|
+
else {
|
215
|
+
// position window using default values
|
216
|
+
windowPos = this.getOptimumPosition(which, width);
|
217
|
+
if (typeof width === 'undefined') {
|
218
|
+
width = this.getDefaultWidth(which);
|
219
|
+
}
|
220
|
+
$window.css({
|
221
|
+
'position': windowPos[0],
|
222
|
+
'width': width,
|
223
|
+
'z-index': windowPos[3]
|
224
|
+
});
|
225
|
+
if (windowPos[0] === 'absolute') {
|
226
|
+
$window.css({
|
227
|
+
'top': windowPos[1] + 'px',
|
228
|
+
'left': windowPos[2] + 'px',
|
229
|
+
});
|
230
|
+
}
|
231
|
+
}
|
232
|
+
};
|
233
|
+
|
234
|
+
AblePlayer.prototype.getOptimumPosition = function (targetWindow, targetWidth) {
|
235
|
+
|
236
|
+
// returns optimum position for targetWindow, as an array with the following structure:
|
237
|
+
// 0 - CSS position ('absolute' or 'relative')
|
238
|
+
// 1 - top
|
239
|
+
// 2 - left
|
240
|
+
// 3 - zindex (if not default)
|
241
|
+
// targetWindow is either 'transcript' or 'sign'
|
242
|
+
// if there is room to the right of the player, position element there
|
243
|
+
// else if there is room the left of the player, position element there
|
244
|
+
// else position element beneath player
|
245
|
+
|
246
|
+
var gap, position, ableWidth, ableHeight, ableOffset, ableTop, ableLeft,
|
247
|
+
windowWidth, otherWindowWidth, zIndex;
|
248
|
+
|
249
|
+
if (typeof targetWidth === 'undefined') {
|
250
|
+
targetWidth = this.getDefaultWidth(targetWindow);
|
251
|
+
}
|
252
|
+
|
253
|
+
gap = 5; // number of pixels to preserve between Able Player objects
|
254
|
+
|
255
|
+
position = []; // position, top, left
|
256
|
+
|
257
|
+
ableWidth = this.$ableDiv.width();
|
258
|
+
ableHeight = this.$ableDiv.height();
|
259
|
+
ableOffset = this.$ableDiv.offset();
|
260
|
+
ableTop = ableOffset.top;
|
261
|
+
ableLeft = ableOffset.left;
|
262
|
+
windowWidth = $(window).width();
|
263
|
+
otherWindowWidth = 0; // width of other visiable draggable windows will be added to this
|
264
|
+
|
265
|
+
if (targetWindow === 'transcript') {
|
266
|
+
if (typeof this.$signWindow !== 'undefined') {
|
267
|
+
if (this.$signWindow.is(':visible')) {
|
268
|
+
otherWindowWidth = this.$signWindow.width() + gap;
|
269
|
+
}
|
270
|
+
}
|
271
|
+
}
|
272
|
+
else if (targetWindow === 'sign') {
|
273
|
+
if (typeof this.$transcriptArea !== 'undefined') {
|
274
|
+
if (this.$transcriptArea.is(':visible')) {
|
275
|
+
otherWindowWidth = this.$transcriptArea.width() + gap;
|
276
|
+
}
|
277
|
+
}
|
278
|
+
}
|
279
|
+
if (targetWidth < (windowWidth - (ableLeft + ableWidth + gap + otherWindowWidth))) {
|
280
|
+
// there's room to the left of $ableDiv
|
281
|
+
position[0] = 'absolute';
|
282
|
+
position[1] = 0;
|
283
|
+
position[2] = ableWidth + otherWindowWidth + gap;
|
284
|
+
}
|
285
|
+
else if (targetWidth + gap < ableLeft) {
|
286
|
+
// there's room to the right of $ableDiv
|
287
|
+
position[0] = 'absolute';
|
288
|
+
position[1] = 0;
|
289
|
+
position[2] = ableLeft - targetWidth - gap;
|
290
|
+
}
|
291
|
+
else {
|
292
|
+
// position element below $ableDiv
|
293
|
+
position[0] = 'relative';
|
294
|
+
// no need to define top, left, or z-index
|
295
|
+
}
|
296
|
+
return position;
|
297
|
+
};
|
298
|
+
|
299
|
+
AblePlayer.prototype.injectPoster = function ($element, context) {
|
300
|
+
|
301
|
+
// get poster attribute from media element and append that as an img to $element
|
302
|
+
// context is either 'youtube' or 'fallback'
|
303
|
+
var poster, width, height;
|
304
|
+
|
305
|
+
if (context === 'youtube') {
|
306
|
+
if (typeof this.ytWidth !== 'undefined') {
|
307
|
+
width = this.ytWidth;
|
308
|
+
height = this.ytHeight;
|
309
|
+
}
|
310
|
+
else if (typeof this.playerMaxWidth !== 'undefined') {
|
311
|
+
width = this.playerMaxWidth;
|
312
|
+
height = this.playerMaxHeight;
|
313
|
+
}
|
314
|
+
else if (typeof this.playerWidth !== 'undefined') {
|
315
|
+
width = this.playerWidth;
|
316
|
+
height = this.playerHeight;
|
317
|
+
}
|
318
|
+
}
|
319
|
+
else if (context === 'fallback') {
|
320
|
+
width = '100%';
|
321
|
+
height = 'auto';
|
322
|
+
}
|
323
|
+
|
324
|
+
if (this.hasPoster) {
|
325
|
+
poster = this.$media.attr('poster');
|
326
|
+
this.$posterImg = $('<img>',{
|
327
|
+
'class': 'able-poster',
|
328
|
+
'src' : poster,
|
329
|
+
'alt' : "",
|
330
|
+
'role': "presentation",
|
331
|
+
'width': width,
|
332
|
+
'height': height
|
333
|
+
});
|
334
|
+
$element.append(this.$posterImg);
|
335
|
+
}
|
336
|
+
};
|
337
|
+
|
338
|
+
AblePlayer.prototype.injectAlert = function () {
|
339
|
+
|
340
|
+
// inject two alerts, one visible for all users and one for screen reader users only
|
341
|
+
|
342
|
+
var top;
|
343
|
+
|
344
|
+
this.$alertBox = $('<div role="alert"></div>');
|
345
|
+
this.$alertBox.addClass('able-alert');
|
346
|
+
this.$alertBox.hide();
|
347
|
+
this.$alertBox.appendTo(this.$ableDiv);
|
348
|
+
if (this.mediaType == 'audio') {
|
349
|
+
top = '-10';
|
350
|
+
}
|
351
|
+
else {
|
352
|
+
// position just below the vertical center of the mediaContainer
|
353
|
+
// hopefully above captions, but not too far from the controller bar
|
354
|
+
top = Math.round(this.$mediaContainer.height() / 3) * 2;
|
355
|
+
}
|
356
|
+
this.$alertBox.css({
|
357
|
+
top: top + 'px'
|
358
|
+
});
|
359
|
+
|
360
|
+
this.$srAlertBox = $('<div role="alert"></div>');
|
361
|
+
this.$srAlertBox.addClass('able-screenreader-alert');
|
362
|
+
this.$srAlertBox.appendTo(this.$ableDiv);
|
363
|
+
};
|
364
|
+
|
365
|
+
AblePlayer.prototype.injectPlaylist = function () {
|
366
|
+
|
367
|
+
if (this.playlistEmbed === true) {
|
368
|
+
// move playlist into player, immediately before statusBarDiv
|
369
|
+
var playlistClone = this.$playlistDom.clone();
|
370
|
+
playlistClone.insertBefore(this.$statusBarDiv);
|
371
|
+
// Update to the new playlist copy.
|
372
|
+
this.$playlist = playlistClone.find('li');
|
373
|
+
}
|
374
|
+
};
|
375
|
+
|
376
|
+
AblePlayer.prototype.createPopup = function (which, tracks) {
|
377
|
+
|
378
|
+
// Create popup menu and append to player
|
379
|
+
// 'which' parameter is either 'captions', 'chapters', 'prefs', 'transcript-window' or 'sign-window'
|
380
|
+
// 'tracks', if provided, is a list of tracks to be used as menu items
|
381
|
+
|
382
|
+
var thisObj, $menu, prefCats, i, $menuItem, prefCat, whichPref,
|
383
|
+
hasDefault, track, windowOptions, whichPref, whichMenu,
|
384
|
+
$thisItem, $prevItem, $nextItem;
|
385
|
+
|
386
|
+
thisObj = this;
|
387
|
+
|
388
|
+
$menu = $('<ul>',{
|
389
|
+
'id': this.mediaId + '-' + which + '-menu',
|
390
|
+
'class': 'able-popup',
|
391
|
+
'role': 'menu'
|
392
|
+
}).hide();
|
393
|
+
|
394
|
+
if (which === 'captions') {
|
395
|
+
$menu.addClass('able-popup-captions');
|
396
|
+
}
|
397
|
+
|
398
|
+
// Populate menu with menu items
|
399
|
+
if (which === 'prefs') {
|
400
|
+
prefCats = this.getPreferencesGroups();
|
401
|
+
for (i = 0; i < prefCats.length; i++) {
|
402
|
+
$menuItem = $('<li></li>',{
|
403
|
+
'role': 'menuitem',
|
404
|
+
'tabindex': '-1'
|
405
|
+
});
|
406
|
+
prefCat = prefCats[i];
|
407
|
+
if (prefCat === 'captions') {
|
408
|
+
$menuItem.text(this.tt.prefMenuCaptions);
|
409
|
+
}
|
410
|
+
else if (prefCat === 'descriptions') {
|
411
|
+
$menuItem.text(this.tt.prefMenuDescriptions);
|
412
|
+
}
|
413
|
+
else if (prefCat === 'keyboard') {
|
414
|
+
$menuItem.text(this.tt.prefMenuKeyboard);
|
415
|
+
}
|
416
|
+
else if (prefCat === 'transcript') {
|
417
|
+
$menuItem.text(this.tt.prefMenuTranscript);
|
418
|
+
}
|
419
|
+
$menuItem.on('click',function() {
|
420
|
+
whichPref = $(this).text();
|
421
|
+
thisObj.setFullscreen(false);
|
422
|
+
if (whichPref === thisObj.tt.prefMenuCaptions) {
|
423
|
+
thisObj.captionPrefsDialog.show();
|
424
|
+
}
|
425
|
+
else if (whichPref === thisObj.tt.prefMenuDescriptions) {
|
426
|
+
thisObj.descPrefsDialog.show();
|
427
|
+
}
|
428
|
+
else if (whichPref === thisObj.tt.prefMenuKeyboard) {
|
429
|
+
thisObj.keyboardPrefsDialog.show();
|
430
|
+
}
|
431
|
+
else if (whichPref === thisObj.tt.prefMenuTranscript) {
|
432
|
+
thisObj.transcriptPrefsDialog.show();
|
433
|
+
}
|
434
|
+
thisObj.closePopups();
|
435
|
+
});
|
436
|
+
$menu.append($menuItem);
|
437
|
+
}
|
438
|
+
}
|
439
|
+
else if (which === 'captions' || which === 'chapters') {
|
440
|
+
hasDefault = false;
|
441
|
+
for (i = 0; i < tracks.length; i++) {
|
442
|
+
track = tracks[i];
|
443
|
+
$menuItem = $('<li></li>',{
|
444
|
+
'role': 'menuitemradio',
|
445
|
+
'tabindex': '-1',
|
446
|
+
'lang': track.language
|
447
|
+
});
|
448
|
+
if (track.def) {
|
449
|
+
$menuItem.attr('aria-checked','true');
|
450
|
+
hasDefault = true;
|
451
|
+
}
|
452
|
+
else {
|
453
|
+
$menuItem.attr('aria-checked','false');
|
454
|
+
}
|
455
|
+
// Get a label using track data
|
456
|
+
if (which == 'captions') {
|
457
|
+
$menuItem.text(track.label);
|
458
|
+
$menuItem.on('click',this.getCaptionClickFunction(track));
|
459
|
+
}
|
460
|
+
else if (which == 'chapters') {
|
461
|
+
$menuItem.text(this.flattenCueForCaption(track) + ' - ' + this.formatSecondsAsColonTime(track.start));
|
462
|
+
$menuItem.on('click',this.getChapterClickFunction(track.start));
|
463
|
+
}
|
464
|
+
$menu.append($menuItem);
|
465
|
+
}
|
466
|
+
if (which === 'captions') {
|
467
|
+
// add a 'captions off' menu item
|
468
|
+
$menuItem = $('<li></li>',{
|
469
|
+
'role': 'menuitemradio',
|
470
|
+
'tabindex': '-1',
|
471
|
+
}).text(this.tt.captionsOff);
|
472
|
+
if (this.prefCaptions === 0) {
|
473
|
+
$menuItem.attr('aria-checked','true');
|
474
|
+
hasDefault = true;
|
475
|
+
}
|
476
|
+
$menuItem.on('click',this.getCaptionOffFunction());
|
477
|
+
$menu.append($menuItem);
|
478
|
+
}
|
479
|
+
}
|
480
|
+
else if (which === 'transcript-window' || which === 'sign-window') {
|
481
|
+
windowOptions = [];
|
482
|
+
windowOptions.push({
|
483
|
+
'name': 'move',
|
484
|
+
'label': this.tt.windowMove
|
485
|
+
});
|
486
|
+
windowOptions.push({
|
487
|
+
'name': 'resize',
|
488
|
+
'label': this.tt.windowResize
|
489
|
+
});
|
490
|
+
windowOptions.push({
|
491
|
+
'name': 'close',
|
492
|
+
'label': this.tt.windowClose
|
493
|
+
});
|
494
|
+
for (i = 0; i < windowOptions.length; i++) {
|
495
|
+
$menuItem = $('<li></li>',{
|
496
|
+
'role': 'menuitem',
|
497
|
+
'tabindex': '-1',
|
498
|
+
'data-choice': windowOptions[i].name
|
499
|
+
});
|
500
|
+
$menuItem.text(windowOptions[i].label);
|
501
|
+
$menuItem.on('click mousedown',function(e) {
|
502
|
+
e.stopPropagation();
|
503
|
+
if (e.button !== 0) { // not a left click
|
504
|
+
return false;
|
505
|
+
}
|
506
|
+
if (!thisObj.windowMenuClickRegistered && !thisObj.finishingDrag) {
|
507
|
+
thisObj.windowMenuClickRegistered = true;
|
508
|
+
thisObj.handleMenuChoice(which.substr(0, which.indexOf('-')), $(this).attr('data-choice'), e);
|
509
|
+
}
|
510
|
+
});
|
511
|
+
$menu.append($menuItem);
|
512
|
+
}
|
513
|
+
}
|
514
|
+
// assign default item, if there isn't one already
|
515
|
+
if (which === 'captions' && !hasDefault) {
|
516
|
+
// check the menu item associated with the default language
|
517
|
+
// as determined in control.js > syncTrackLanguages()
|
518
|
+
if ($menu.find('li[lang=' + this.captionLang + ']')) {
|
519
|
+
// a track exists for the default language. Check that item in the menu
|
520
|
+
$menu.find('li[lang=' + this.captionLang + ']').attr('aria-checked','true');
|
521
|
+
}
|
522
|
+
else {
|
523
|
+
// check the last item (captions off)
|
524
|
+
$menu.find('li').last().attr('aria-checked','true');
|
525
|
+
}
|
526
|
+
}
|
527
|
+
else if (which === 'chapters') {
|
528
|
+
if ($menu.find('li:contains("' + this.defaultChapter + '")')) {
|
529
|
+
$menu.find('li:contains("' + this.defaultChapter + '")').attr('aria-checked','true').addClass('able-focus');
|
530
|
+
}
|
531
|
+
else {
|
532
|
+
$menu.find('li').first().attr('aria-checked','true').addClass('able-focus');
|
533
|
+
}
|
534
|
+
}
|
535
|
+
// add keyboard handlers for navigating within popups
|
536
|
+
$menu.on('keydown',function (e) {
|
537
|
+
whichMenu = $(this).attr('id').split('-')[1];
|
538
|
+
$thisItem = $(this).find('li:focus');
|
539
|
+
if ($thisItem.is(':first-child')) {
|
540
|
+
// this is the first item in the menu
|
541
|
+
$prevItem = $(this).find('li').last(); // wrap to bottom
|
542
|
+
$nextItem = $thisItem.next();
|
543
|
+
}
|
544
|
+
else if ($thisItem.is(':last-child')) {
|
545
|
+
// this is the last Item
|
546
|
+
$prevItem = $thisItem.prev();
|
547
|
+
$nextItem = $(this).find('li').first(); // wrap to top
|
548
|
+
}
|
549
|
+
else {
|
550
|
+
$prevItem = $thisItem.prev();
|
551
|
+
$nextItem = $thisItem.next();
|
552
|
+
}
|
553
|
+
if (e.which === 9) { // Tab
|
554
|
+
if (e.shiftKey) {
|
555
|
+
$thisItem.removeClass('able-focus');
|
556
|
+
$prevItem.focus().addClass('able-focus');
|
557
|
+
}
|
558
|
+
else {
|
559
|
+
$thisItem.removeClass('able-focus');
|
560
|
+
$nextItem.focus().addClass('able-focus');
|
561
|
+
}
|
562
|
+
}
|
563
|
+
else if (e.which === 40 || e.which === 39) { // down or right arrow
|
564
|
+
$thisItem.removeClass('able-focus');
|
565
|
+
$nextItem.focus().addClass('able-focus');
|
566
|
+
}
|
567
|
+
else if (e.which == 38 || e.which === 37) { // up or left arrow
|
568
|
+
$thisItem.removeClass('able-focus');
|
569
|
+
$prevItem.focus().addClass('able-focus');
|
570
|
+
}
|
571
|
+
else if (e.which === 32 || e.which === 13) { // space or enter
|
572
|
+
$thisItem.click();
|
573
|
+
}
|
574
|
+
else if (e.which === 27) { // Escape
|
575
|
+
$thisItem.removeClass('able-focus');
|
576
|
+
thisObj.closePopups();
|
577
|
+
}
|
578
|
+
e.preventDefault();
|
579
|
+
});
|
580
|
+
this.$controllerDiv.append($menu);
|
581
|
+
return $menu;
|
582
|
+
};
|
583
|
+
|
584
|
+
AblePlayer.prototype.closePopups = function () {
|
585
|
+
|
586
|
+
if (this.chaptersPopup && this.chaptersPopup.is(':visible')) {
|
587
|
+
this.chaptersPopup.hide();
|
588
|
+
this.$chaptersButton.attr('aria-expanded','false').focus();
|
589
|
+
}
|
590
|
+
if (this.captionsPopup && this.captionsPopup.is(':visible')) {
|
591
|
+
this.captionsPopup.hide();
|
592
|
+
this.$ccButton.attr('aria-expanded','false').focus();
|
593
|
+
}
|
594
|
+
if (this.prefsPopup && this.prefsPopup.is(':visible')) {
|
595
|
+
this.prefsPopup.hide();
|
596
|
+
// restore menu items to their original state
|
597
|
+
this.prefsPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
|
598
|
+
this.$prefsButton.attr('aria-expanded','false').focus();
|
599
|
+
}
|
600
|
+
if (this.$volumeSlider && this.$volumeSlider.is(':visible')) {
|
601
|
+
this.$volumeSlider.hide().attr('aria-hidden','true');
|
602
|
+
this.$volumeAlert.text(this.tt.volumeSliderClosed);
|
603
|
+
this.$volumeButton.attr('aria-expanded','false').focus();
|
604
|
+
}
|
605
|
+
if (this.$transcriptPopup && this.$transcriptPopup.is(':visible')) {
|
606
|
+
this.$transcriptPopup.hide();
|
607
|
+
// restore menu items to their original state
|
608
|
+
this.$transcriptPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
|
609
|
+
this.$transcriptPopupButton.attr('aria-expanded','false').focus();
|
610
|
+
}
|
611
|
+
if (this.$signPopup && this.$signPopup.is(':visible')) {
|
612
|
+
this.$signPopup.hide();
|
613
|
+
// restore menu items to their original state
|
614
|
+
this.$signPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
|
615
|
+
this.$signPopupButton.attr('aria-expanded','false').focus();
|
616
|
+
}
|
617
|
+
};
|
618
|
+
|
619
|
+
AblePlayer.prototype.setupPopups = function (which) {
|
620
|
+
|
621
|
+
// Create and fill in the popup menu forms for various controls.
|
622
|
+
// parameter 'which' is passed if refreshing content of an existing popup ('captions' or 'chapters')
|
623
|
+
// If which is undefined, automatically setup 'captions', 'chapters', and 'prefs' popups
|
624
|
+
// However, only setup 'transcript-window' and 'sign-window' popups if passed as value of which
|
625
|
+
var popups, thisObj, hasDefault, i, j,
|
626
|
+
tracks, track, $trackButton, $trackLabel,
|
627
|
+
radioName, radioId, $menu, $menuItem,
|
628
|
+
prefCats, prefCat, prefLabel;
|
629
|
+
|
630
|
+
popups = [];
|
631
|
+
if (typeof which === 'undefined') {
|
632
|
+
popups.push('prefs');
|
633
|
+
}
|
634
|
+
|
635
|
+
if (which === 'captions' || (typeof which === 'undefined')) {
|
636
|
+
if (this.captions.length > 0) {
|
637
|
+
popups.push('captions');
|
638
|
+
}
|
639
|
+
}
|
640
|
+
if (which === 'chapters' || (typeof which === 'undefined')) {
|
641
|
+
if (this.chapters.length > 0 && this.useChaptersButton) {
|
642
|
+
popups.push('chapters');
|
643
|
+
}
|
644
|
+
}
|
645
|
+
if (which === 'transcript-window' && this.transcriptType === 'popup') {
|
646
|
+
popups.push('transcript-window');
|
647
|
+
}
|
648
|
+
if (which === 'sign-window' && this.hasSignLanguage) {
|
649
|
+
popups.push('sign-window');
|
650
|
+
}
|
651
|
+
if (popups.length > 0) {
|
652
|
+
thisObj = this;
|
653
|
+
for (var i=0; i<popups.length; i++) {
|
654
|
+
var popup = popups[i];
|
655
|
+
hasDefault = false;
|
656
|
+
if (popup == 'prefs') {
|
657
|
+
this.prefsPopup = this.createPopup('prefs');
|
658
|
+
}
|
659
|
+
else if (popup == 'captions') {
|
660
|
+
if (typeof this.captionsPopup === 'undefined' || !this.captionsPopup) {
|
661
|
+
this.captionsPopup = this.createPopup('captions',this.captions);
|
662
|
+
}
|
663
|
+
}
|
664
|
+
else if (popup == 'chapters') {
|
665
|
+
if (this.selectedChapters) {
|
666
|
+
tracks = this.selectedChapters.cues;
|
667
|
+
}
|
668
|
+
else if (this.chapters.length >= 1) {
|
669
|
+
tracks = this.chapters[0].cues;
|
670
|
+
}
|
671
|
+
else {
|
672
|
+
tracks = [];
|
673
|
+
}
|
674
|
+
if (typeof this.chaptersPopup === 'undefined' || !this.chaptersPopup) {
|
675
|
+
this.chaptersPopup = this.createPopup('chapters',tracks);
|
676
|
+
}
|
677
|
+
}
|
678
|
+
else if (popup == 'transcript-window') {
|
679
|
+
return this.createPopup('transcript-window');
|
680
|
+
}
|
681
|
+
else if (popup == 'sign-window') {
|
682
|
+
return this.createPopup('sign-window');
|
683
|
+
}
|
684
|
+
}
|
685
|
+
}
|
686
|
+
};
|
687
|
+
|
688
|
+
AblePlayer.prototype.provideFallback = function() {
|
689
|
+
|
690
|
+
// provide ultimate fallback for users who are unable to play the media
|
691
|
+
// If there is HTML content nested within the media element, display that
|
692
|
+
// Otherwise, display standard localized error text
|
693
|
+
|
694
|
+
var $fallbackDiv, width, mediaClone, fallback, fallbackText,
|
695
|
+
showBrowserList, browsers, i, b, browserList;
|
696
|
+
|
697
|
+
// Could show list of supporting browsers if 99.9% confident the error is truly an outdated browser
|
698
|
+
// Too many sites say "You need to update your browser" when in fact I'm using a current version
|
699
|
+
showBrowserList = false;
|
700
|
+
|
701
|
+
$fallbackDiv = $('<div>',{
|
702
|
+
'class' : 'able-fallback',
|
703
|
+
'role' : 'alert',
|
704
|
+
});
|
705
|
+
// override default width of .able-fallback with player width, if known
|
706
|
+
if (typeof this.playerMaxWidth !== 'undefined') {
|
707
|
+
width = this.playerMaxWidth + 'px';
|
708
|
+
}
|
709
|
+
else if (this.$media.attr('width')) {
|
710
|
+
width = parseInt(this.$media.attr('width'), 10) + 'px';
|
711
|
+
}
|
712
|
+
else {
|
713
|
+
width = '100%';
|
714
|
+
}
|
715
|
+
$fallbackDiv.css('max-width',width);
|
716
|
+
|
717
|
+
// use fallback content that's nested inside the HTML5 media element, if there is any
|
718
|
+
mediaClone = this.$media.clone();
|
719
|
+
$('source, track', mediaClone).remove();
|
720
|
+
fallback = mediaClone.html().trim();
|
721
|
+
if (fallback.length) {
|
722
|
+
$fallbackDiv.html(fallback);
|
723
|
+
}
|
724
|
+
else {
|
725
|
+
// use standard localized error message
|
726
|
+
fallbackText = this.tt.fallbackError1 + ' ' + this.tt[this.mediaType] + '. ';
|
727
|
+
fallbackText += this.tt.fallbackError2 + ':';
|
728
|
+
fallback = $('<p>').text(fallbackText);
|
729
|
+
$fallbackDiv.html(fallback);
|
730
|
+
showBrowserList = true;
|
731
|
+
}
|
732
|
+
|
733
|
+
if (showBrowserList) {
|
734
|
+
browserList = $('<ul>');
|
735
|
+
browsers = this.getSupportingBrowsers();
|
736
|
+
for (i=0; i<browsers.length; i++) {
|
737
|
+
b = $('<li>');
|
738
|
+
b.text(browsers[i].name + ' ' + browsers[i].minVersion + ' ' + this.tt.orHigher);
|
739
|
+
browserList.append(b);
|
740
|
+
}
|
741
|
+
$fallbackDiv.append(browserList);
|
742
|
+
}
|
743
|
+
|
744
|
+
// if there's a poster, show that as well
|
745
|
+
this.injectPoster($fallbackDiv, 'fallback');
|
746
|
+
|
747
|
+
// inject $fallbackDiv into the DOM and remove broken content
|
748
|
+
if (typeof this.$ableWrapper !== 'undefined') {
|
749
|
+
this.$ableWrapper.before($fallbackDiv);
|
750
|
+
this.$ableWrapper.remove();
|
751
|
+
}
|
752
|
+
else if (typeof this.$media !== 'undefined') {
|
753
|
+
this.$media.before($fallbackDiv);
|
754
|
+
this.$media.remove();
|
755
|
+
}
|
756
|
+
else {
|
757
|
+
$('body').prepend($fallbackDiv);
|
758
|
+
}
|
759
|
+
};
|
760
|
+
|
761
|
+
AblePlayer.prototype.getSupportingBrowsers = function() {
|
762
|
+
|
763
|
+
var browsers = [];
|
764
|
+
browsers[0] = {
|
765
|
+
name:'Chrome',
|
766
|
+
minVersion: '31'
|
767
|
+
};
|
768
|
+
browsers[1] = {
|
769
|
+
name:'Firefox',
|
770
|
+
minVersion: '34'
|
771
|
+
};
|
772
|
+
browsers[2] = {
|
773
|
+
name:'Internet Explorer',
|
774
|
+
minVersion: '10'
|
775
|
+
};
|
776
|
+
browsers[3] = {
|
777
|
+
name:'Opera',
|
778
|
+
minVersion: '26'
|
779
|
+
};
|
780
|
+
browsers[4] = {
|
781
|
+
name:'Safari for Mac OS X',
|
782
|
+
minVersion: '7.1'
|
783
|
+
};
|
784
|
+
browsers[5] = {
|
785
|
+
name:'Safari for iOS',
|
786
|
+
minVersion: '7.1'
|
787
|
+
};
|
788
|
+
browsers[6] = {
|
789
|
+
name:'Android Browser',
|
790
|
+
minVersion: '4.1'
|
791
|
+
};
|
792
|
+
browsers[7] = {
|
793
|
+
name:'Chrome for Android',
|
794
|
+
minVersion: '40'
|
795
|
+
};
|
796
|
+
return browsers;
|
797
|
+
}
|
798
|
+
|
799
|
+
AblePlayer.prototype.calculateControlLayout = function () {
|
800
|
+
|
801
|
+
// Calculates the layout for controls based on media and options.
|
802
|
+
// Returns an object with keys 'ul', 'ur', 'bl', 'br' for upper-left, etc.
|
803
|
+
// Each associated value is array of control names to put at that location.
|
804
|
+
|
805
|
+
var controlLayout = {
|
806
|
+
'ul': ['play','restart','rewind','forward'],
|
807
|
+
'ur': ['seek'],
|
808
|
+
'bl': [],
|
809
|
+
'br': []
|
810
|
+
}
|
811
|
+
|
812
|
+
// test for browser support for volume before displaying volume button
|
813
|
+
if (this.browserSupportsVolume()) {
|
814
|
+
// volume buttons are: 'mute','volume-soft','volume-medium','volume-loud'
|
815
|
+
// previously supported button were: 'volume-up','volume-down'
|
816
|
+
this.volumeButton = 'volume-' + this.getVolumeName(this.volume);
|
817
|
+
controlLayout['ur'].push('volume');
|
818
|
+
}
|
819
|
+
else {
|
820
|
+
this.volume = false;
|
821
|
+
}
|
822
|
+
|
823
|
+
// Calculate the two sides of the bottom-left grouping to see if we need separator pipe.
|
824
|
+
var bll = [];
|
825
|
+
var blr = [];
|
826
|
+
|
827
|
+
if (this.isPlaybackRateSupported()) {
|
828
|
+
bll.push('slower');
|
829
|
+
bll.push('faster');
|
830
|
+
}
|
831
|
+
|
832
|
+
if (this.mediaType === 'video') {
|
833
|
+
if (this.hasCaptions) {
|
834
|
+
bll.push('captions'); //closed captions
|
835
|
+
}
|
836
|
+
if (this.hasSignLanguage) {
|
837
|
+
bll.push('sign'); // sign language
|
838
|
+
}
|
839
|
+
if ((this.hasOpenDesc || this.hasClosedDesc) && (this.useDescriptionsButton)) {
|
840
|
+
bll.push('descriptions'); //audio description
|
841
|
+
}
|
842
|
+
}
|
843
|
+
if (this.transcriptType === 'popup') {
|
844
|
+
bll.push('transcript');
|
845
|
+
}
|
846
|
+
|
847
|
+
if (this.mediaType === 'video' && this.hasChapters && this.useChaptersButton) {
|
848
|
+
bll.push('chapters');
|
849
|
+
}
|
850
|
+
|
851
|
+
controlLayout['br'].push('preferences');
|
852
|
+
|
853
|
+
if (this.mediaType === 'video' && this.allowFullScreen) {
|
854
|
+
controlLayout['br'].push('fullscreen');
|
855
|
+
}
|
856
|
+
|
857
|
+
// Include the pipe only if we need to.
|
858
|
+
if (bll.length > 0 && blr.length > 0) {
|
859
|
+
controlLayout['bl'] = bll;
|
860
|
+
controlLayout['bl'].push('pipe');
|
861
|
+
controlLayout['bl'] = controlLayout['bl'].concat(blr);
|
862
|
+
}
|
863
|
+
else {
|
864
|
+
controlLayout['bl'] = bll.concat(blr);
|
865
|
+
}
|
866
|
+
|
867
|
+
return controlLayout;
|
868
|
+
};
|
869
|
+
|
870
|
+
AblePlayer.prototype.addControls = function() {
|
871
|
+
|
872
|
+
// determine which controls to show based on several factors:
|
873
|
+
// mediaType (audio vs video)
|
874
|
+
// availability of tracks (e.g., for closed captions & audio description)
|
875
|
+
// browser support (e.g., for sliders and speedButtons)
|
876
|
+
// user preferences (???)
|
877
|
+
// some controls are aligned on the left, and others on the right
|
878
|
+
var thisObj, baseSliderWidth, controlLayout, sectionByOrder, useSpeedButtons, useFullScreen,
|
879
|
+
i, j, k, controls, $controllerSpan, $sliderDiv, sliderLabel, mediaTimes, duration, $pipe, $pipeImg,
|
880
|
+
tooltipId, tooltipX, tooltipY, control,
|
881
|
+
buttonImg, buttonImgSrc, buttonTitle, $newButton, iconClass, buttonIcon, buttonUse, svgPath,
|
882
|
+
leftWidth, rightWidth, totalWidth, leftWidthStyle, rightWidthStyle,
|
883
|
+
controllerStyles, vidcapStyles, captionLabel, popupMenuId;
|
884
|
+
|
885
|
+
thisObj = this;
|
886
|
+
|
887
|
+
baseSliderWidth = 100; // arbitrary value, will be recalculated in refreshControls()
|
888
|
+
|
889
|
+
// Initialize the layout into the this.controlLayout variable.
|
890
|
+
controlLayout = this.calculateControlLayout();
|
891
|
+
|
892
|
+
sectionByOrder = {0: 'ul', 1:'ur', 2:'bl', 3:'br'};
|
893
|
+
|
894
|
+
// add an empty div to serve as a tooltip
|
895
|
+
tooltipId = this.mediaId + '-tooltip';
|
896
|
+
this.$tooltipDiv = $('<div>',{
|
897
|
+
'id': tooltipId,
|
898
|
+
'class': 'able-tooltip'
|
899
|
+
}).hide();
|
900
|
+
this.$controllerDiv.append(this.$tooltipDiv);
|
901
|
+
|
902
|
+
// step separately through left and right controls
|
903
|
+
for (i = 0; i <= 3; i++) {
|
904
|
+
controls = controlLayout[sectionByOrder[i]];
|
905
|
+
if ((i % 2) === 0) {
|
906
|
+
$controllerSpan = $('<div>',{
|
907
|
+
'class': 'able-left-controls'
|
908
|
+
});
|
909
|
+
}
|
910
|
+
else {
|
911
|
+
$controllerSpan = $('<div>',{
|
912
|
+
'class': 'able-right-controls'
|
913
|
+
});
|
914
|
+
}
|
915
|
+
this.$controllerDiv.append($controllerSpan);
|
916
|
+
for (j=0; j<controls.length; j++) {
|
917
|
+
control = controls[j];
|
918
|
+
if (control === 'seek') {
|
919
|
+
$sliderDiv = $('<div class="able-seekbar"></div>');
|
920
|
+
sliderLabel = this.mediaType + ' ' + this.tt.seekbarLabel;
|
921
|
+
$controllerSpan.append($sliderDiv);
|
922
|
+
if (typeof this.duration === 'undefined' || this.duration === 0) {
|
923
|
+
// set arbitrary starting duration, and change it when duration is known
|
924
|
+
this.duration = 100;
|
925
|
+
// also set elapsed to 0
|
926
|
+
this.elapsed = 0;
|
927
|
+
}
|
928
|
+
this.seekBar = new AccessibleSlider(this.mediaType, $sliderDiv, 'horizontal', baseSliderWidth, 0, this.duration, this.seekInterval, sliderLabel, 'seekbar', true, 'visible');
|
929
|
+
}
|
930
|
+
else if (control === 'pipe') {
|
931
|
+
// TODO: Unify this with buttons somehow to avoid code duplication
|
932
|
+
$pipe = $('<span>', {
|
933
|
+
'tabindex': '-1',
|
934
|
+
'aria-hidden': 'true'
|
935
|
+
});
|
936
|
+
if (this.iconType === 'font') {
|
937
|
+
$pipe.addClass('icon-pipe');
|
938
|
+
}
|
939
|
+
else {
|
940
|
+
$pipeImg = $('<img>', {
|
941
|
+
src: this.rootPath + 'button-icons/' + this.iconColor + '/pipe.png',
|
942
|
+
alt: '',
|
943
|
+
role: 'presentation'
|
944
|
+
});
|
945
|
+
$pipe.append($pipeImg);
|
946
|
+
}
|
947
|
+
$controllerSpan.append($pipe);
|
948
|
+
}
|
949
|
+
else {
|
950
|
+
// this control is a button
|
951
|
+
if (control === 'volume') {
|
952
|
+
buttonImgSrc = this.rootPath + 'button-icons/' + this.iconColor + '/' + this.volumeButton + '.png';
|
953
|
+
}
|
954
|
+
else if (control === 'fullscreen') {
|
955
|
+
buttonImgSrc = this.rootPath + 'button-icons/' + this.iconColor + '/fullscreen-expand.png';
|
956
|
+
}
|
957
|
+
else if (control === 'slower') {
|
958
|
+
if (this.speedIcons === 'animals') {
|
959
|
+
buttonImgSrc = this.rootPath + 'button-icons/' + this.iconColor + '/turtle.png';
|
960
|
+
}
|
961
|
+
else {
|
962
|
+
buttonImgSrc = this.rootPath + 'button-icons/' + this.iconColor + '/slower.png';
|
963
|
+
}
|
964
|
+
}
|
965
|
+
else if (control === 'faster') {
|
966
|
+
if (this.speedIcons === 'animals') {
|
967
|
+
buttonImgSrc = this.rootPath + 'button-icons/' + this.iconColor + '/rabbit.png';
|
968
|
+
}
|
969
|
+
else {
|
970
|
+
buttonImgSrc = this.rootPath + 'button-icons/' + this.iconColor + '/faster.png';
|
971
|
+
}
|
972
|
+
}
|
973
|
+
else {
|
974
|
+
buttonImgSrc = this.rootPath + 'button-icons/' + this.iconColor + '/' + control + '.png';
|
975
|
+
}
|
976
|
+
buttonTitle = this.getButtonTitle(control);
|
977
|
+
|
978
|
+
// icomoon documentation recommends the following markup for screen readers:
|
979
|
+
// 1. link element (or in our case, button). Nested inside this element:
|
980
|
+
// 2. span that contains the icon font (in our case, buttonIcon)
|
981
|
+
// 3. span that contains a visually hidden label for screen readers (buttonLabel)
|
982
|
+
// In addition, we are adding aria-label to the button (but not title)
|
983
|
+
// And if iconType === 'image', we are replacing #2 with an image (with alt="" and role="presentation")
|
984
|
+
// This has been thoroughly tested and works well in all screen reader/browser combinations
|
985
|
+
// See https://github.com/ableplayer/ableplayer/issues/81
|
986
|
+
$newButton = $('<button>',{
|
987
|
+
'type': 'button',
|
988
|
+
'tabindex': '0',
|
989
|
+
'aria-label': buttonTitle,
|
990
|
+
'class': 'able-button-handler-' + control
|
991
|
+
});
|
992
|
+
if (control === 'volume' || control === 'preferences') {
|
993
|
+
if (control == 'preferences') {
|
994
|
+
popupMenuId = this.mediaId + '-prefs-menu';
|
995
|
+
}
|
996
|
+
else if (control === 'volume') {
|
997
|
+
popupMenuId = this.mediaId + '-volume-slider';
|
998
|
+
}
|
999
|
+
$newButton.attr({
|
1000
|
+
'aria-controls': popupMenuId,
|
1001
|
+
'aria-expanded': 'false'
|
1002
|
+
});
|
1003
|
+
}
|
1004
|
+
if (this.iconType === 'font') {
|
1005
|
+
if (control === 'volume') {
|
1006
|
+
iconClass = 'icon-' + this.volumeButton;
|
1007
|
+
}
|
1008
|
+
else if (control === 'slower') {
|
1009
|
+
if (this.speedIcons === 'animals') {
|
1010
|
+
iconClass = 'icon-turtle';
|
1011
|
+
}
|
1012
|
+
else {
|
1013
|
+
iconClass = 'icon-slower';
|
1014
|
+
}
|
1015
|
+
}
|
1016
|
+
else if (control === 'faster') {
|
1017
|
+
if (this.speedIcons === 'animals') {
|
1018
|
+
iconClass = 'icon-rabbit';
|
1019
|
+
}
|
1020
|
+
else {
|
1021
|
+
iconClass = 'icon-faster';
|
1022
|
+
}
|
1023
|
+
}
|
1024
|
+
else {
|
1025
|
+
iconClass = 'icon-' + control;
|
1026
|
+
}
|
1027
|
+
buttonIcon = $('<span>',{
|
1028
|
+
'class': iconClass,
|
1029
|
+
'aria-hidden': 'true'
|
1030
|
+
});
|
1031
|
+
$newButton.append(buttonIcon);
|
1032
|
+
}
|
1033
|
+
else if (this.iconType === 'svg') {
|
1034
|
+
|
1035
|
+
/*
|
1036
|
+
// Unused option for adding SVG:
|
1037
|
+
// Use <use> element to link to button-icons/able-icons.svg
|
1038
|
+
// Advantage: SVG file can be cached
|
1039
|
+
// Disadvantage: Not supported by Safari 6, IE 6-11, or Edge 12
|
1040
|
+
// Instead, adding <svg> element within each <button>
|
1041
|
+
if (control === 'volume') {
|
1042
|
+
iconClass = 'svg-' + this.volumeButton;
|
1043
|
+
}
|
1044
|
+
else if (control === 'fullscreen') {
|
1045
|
+
iconClass = 'svg-fullscreen-expand';
|
1046
|
+
}
|
1047
|
+
else if (control === 'slower') {
|
1048
|
+
if (this.speedIcons === 'animals') {
|
1049
|
+
iconClass = 'svg-turtle';
|
1050
|
+
}
|
1051
|
+
else {
|
1052
|
+
iconClass = 'svg-slower';
|
1053
|
+
}
|
1054
|
+
}
|
1055
|
+
else if (control === 'faster') {
|
1056
|
+
if (this.speedIcons === 'animals') {
|
1057
|
+
iconClass = 'svg-rabbit';
|
1058
|
+
}
|
1059
|
+
else {
|
1060
|
+
iconClass = 'svg-faster';
|
1061
|
+
}
|
1062
|
+
}
|
1063
|
+
else {
|
1064
|
+
iconClass = 'svg-' + control;
|
1065
|
+
}
|
1066
|
+
buttonIcon = $('<svg>',{
|
1067
|
+
'class': iconClass
|
1068
|
+
});
|
1069
|
+
buttonUse = $('<use>',{
|
1070
|
+
'xlink:href': this.rootPath + 'button-icons/able-icons.svg#' + iconClass
|
1071
|
+
});
|
1072
|
+
buttonIcon.append(buttonUse);
|
1073
|
+
*/
|
1074
|
+
var svgData;
|
1075
|
+
if (control === 'volume') {
|
1076
|
+
svgData = this.getSvgData(this.volumeButton);
|
1077
|
+
}
|
1078
|
+
else if (control === 'fullscreen') {
|
1079
|
+
svgData = this.getSvgData('fullscreen-expand');
|
1080
|
+
}
|
1081
|
+
else if (control === 'slower') {
|
1082
|
+
if (this.speedIcons === 'animals') {
|
1083
|
+
svgData = this.getSvgData('turtle');
|
1084
|
+
}
|
1085
|
+
else {
|
1086
|
+
svgData = this.getSvgData('slower');
|
1087
|
+
}
|
1088
|
+
}
|
1089
|
+
else if (control === 'faster') {
|
1090
|
+
if (this.speedIcons === 'animals') {
|
1091
|
+
svgData = this.getSvgData('rabbit');
|
1092
|
+
}
|
1093
|
+
else {
|
1094
|
+
svgData = this.getSvgData('faster');
|
1095
|
+
}
|
1096
|
+
}
|
1097
|
+
else {
|
1098
|
+
svgData = this.getSvgData(control);
|
1099
|
+
}
|
1100
|
+
buttonIcon = $('<svg>',{
|
1101
|
+
'focusable': 'false',
|
1102
|
+
'aria-hidden': 'true',
|
1103
|
+
'viewBox': svgData[0]
|
1104
|
+
});
|
1105
|
+
svgPath = $('<path>',{
|
1106
|
+
'd': svgData[1]
|
1107
|
+
});
|
1108
|
+
buttonIcon.append(svgPath);
|
1109
|
+
$newButton.html(buttonIcon);
|
1110
|
+
|
1111
|
+
// Final step: Need to refresh the DOM in order for browser to process & display the SVG
|
1112
|
+
$newButton.html($newButton.html());
|
1113
|
+
}
|
1114
|
+
else {
|
1115
|
+
// use images
|
1116
|
+
buttonImg = $('<img>',{
|
1117
|
+
'src': buttonImgSrc,
|
1118
|
+
'alt': '',
|
1119
|
+
'role': 'presentation'
|
1120
|
+
});
|
1121
|
+
$newButton.append(buttonImg);
|
1122
|
+
}
|
1123
|
+
// add the visibly-hidden label for screen readers that don't support aria-label on the button
|
1124
|
+
var buttonLabel = $('<span>',{
|
1125
|
+
'class': 'able-clipped'
|
1126
|
+
}).text(buttonTitle);
|
1127
|
+
$newButton.append(buttonLabel);
|
1128
|
+
// add an event listener that displays a tooltip on mouseenter or focus
|
1129
|
+
$newButton.on('mouseenter focus',function(e) {
|
1130
|
+
var label = $(this).attr('aria-label');
|
1131
|
+
// get position of this button
|
1132
|
+
var position = $(this).position();
|
1133
|
+
var buttonHeight = $(this).height();
|
1134
|
+
var buttonWidth = $(this).width();
|
1135
|
+
var tooltipY = position.top - buttonHeight - 15;
|
1136
|
+
var centerTooltip = true;
|
1137
|
+
if ($(this).closest('div').hasClass('able-right-controls')) {
|
1138
|
+
// this control is on the right side
|
1139
|
+
if ($(this).closest('div').find('button:last').get(0) == $(this).get(0)) {
|
1140
|
+
// this is the last control on the right
|
1141
|
+
// position tooltip using the "right" property
|
1142
|
+
centerTooltip = false;
|
1143
|
+
var tooltipX = 0;
|
1144
|
+
var tooltipStyle = {
|
1145
|
+
left: '',
|
1146
|
+
right: tooltipX + 'px',
|
1147
|
+
top: tooltipY + 'px'
|
1148
|
+
};
|
1149
|
+
}
|
1150
|
+
}
|
1151
|
+
else {
|
1152
|
+
// this control is on the left side
|
1153
|
+
if ($(this).is(':first-child')) {
|
1154
|
+
// this is the first control on the left
|
1155
|
+
centerTooltip = false;
|
1156
|
+
var tooltipX = position.left;
|
1157
|
+
var tooltipStyle = {
|
1158
|
+
left: tooltipX + 'px',
|
1159
|
+
right: '',
|
1160
|
+
top: tooltipY + 'px'
|
1161
|
+
};
|
1162
|
+
}
|
1163
|
+
}
|
1164
|
+
if (centerTooltip) {
|
1165
|
+
// populate tooltip, then calculate its width before showing it
|
1166
|
+
var tooltipWidth = AblePlayer.localGetElementById($newButton[0], tooltipId).text(label).width();
|
1167
|
+
// center the tooltip horizontally over the button
|
1168
|
+
var tooltipX = position.left - tooltipWidth/2;
|
1169
|
+
var tooltipStyle = {
|
1170
|
+
left: tooltipX + 'px',
|
1171
|
+
right: '',
|
1172
|
+
top: tooltipY + 'px'
|
1173
|
+
};
|
1174
|
+
}
|
1175
|
+
var tooltip = AblePlayer.localGetElementById($newButton[0], tooltipId).text(label).css(tooltipStyle);
|
1176
|
+
thisObj.showTooltip(tooltip);
|
1177
|
+
$(this).on('mouseleave blur',function() {
|
1178
|
+
AblePlayer.localGetElementById($newButton[0], tooltipId).text('').hide();
|
1179
|
+
})
|
1180
|
+
});
|
1181
|
+
|
1182
|
+
if (control === 'captions') {
|
1183
|
+
if (!this.prefCaptions || this.prefCaptions !== 1) {
|
1184
|
+
// captions are available, but user has them turned off
|
1185
|
+
if (this.captions.length > 1) {
|
1186
|
+
captionLabel = this.tt.captions;
|
1187
|
+
}
|
1188
|
+
else {
|
1189
|
+
captionLabel = this.tt.showCaptions;
|
1190
|
+
}
|
1191
|
+
$newButton.addClass('buttonOff').attr('title',captionLabel);
|
1192
|
+
}
|
1193
|
+
}
|
1194
|
+
else if (control === 'descriptions') {
|
1195
|
+
if (!this.prefDesc || this.prefDesc !== 1) {
|
1196
|
+
// user prefer non-audio described version
|
1197
|
+
// Therefore, load media without description
|
1198
|
+
// Description can be toggled on later with this button
|
1199
|
+
$newButton.addClass('buttonOff').attr('title',this.tt.turnOnDescriptions);
|
1200
|
+
}
|
1201
|
+
}
|
1202
|
+
|
1203
|
+
$controllerSpan.append($newButton);
|
1204
|
+
|
1205
|
+
// create variables of buttons that are referenced throughout the AblePlayer object
|
1206
|
+
if (control === 'play') {
|
1207
|
+
this.$playpauseButton = $newButton;
|
1208
|
+
}
|
1209
|
+
else if (control === 'captions') {
|
1210
|
+
this.$ccButton = $newButton;
|
1211
|
+
}
|
1212
|
+
else if (control === 'sign') {
|
1213
|
+
this.$signButton = $newButton;
|
1214
|
+
// gray out sign button if sign language window is not active
|
1215
|
+
if (!(this.$signWindow.is(':visible'))) {
|
1216
|
+
this.$signButton.addClass('buttonOff');
|
1217
|
+
}
|
1218
|
+
}
|
1219
|
+
else if (control === 'descriptions') {
|
1220
|
+
this.$descButton = $newButton;
|
1221
|
+
// button will be enabled or disabled in description.js > initDescription()
|
1222
|
+
}
|
1223
|
+
else if (control === 'mute') {
|
1224
|
+
this.$muteButton = $newButton;
|
1225
|
+
}
|
1226
|
+
else if (control === 'transcript') {
|
1227
|
+
this.$transcriptButton = $newButton;
|
1228
|
+
// gray out transcript button if transcript is not active
|
1229
|
+
if (!(this.$transcriptDiv.is(':visible'))) {
|
1230
|
+
this.$transcriptButton.addClass('buttonOff').attr('title',this.tt.showTranscript);
|
1231
|
+
}
|
1232
|
+
}
|
1233
|
+
else if (control === 'fullscreen') {
|
1234
|
+
this.$fullscreenButton = $newButton;
|
1235
|
+
}
|
1236
|
+
else if (control === 'chapters') {
|
1237
|
+
this.$chaptersButton = $newButton;
|
1238
|
+
}
|
1239
|
+
else if (control === 'preferences') {
|
1240
|
+
this.$prefsButton = $newButton;
|
1241
|
+
}
|
1242
|
+
else if (control === 'volume') {
|
1243
|
+
this.$volumeButton = $newButton;
|
1244
|
+
}
|
1245
|
+
}
|
1246
|
+
if (control === 'volume') {
|
1247
|
+
// in addition to the volume button, add a hidden slider
|
1248
|
+
this.addVolumeSlider($controllerSpan);
|
1249
|
+
}
|
1250
|
+
}
|
1251
|
+
if ((i % 2) == 1) {
|
1252
|
+
this.$controllerDiv.append('<div style="clear:both;"></div>');
|
1253
|
+
}
|
1254
|
+
}
|
1255
|
+
|
1256
|
+
if (this.mediaType === 'video') {
|
1257
|
+
|
1258
|
+
if (typeof this.$captionsDiv !== 'undefined') {
|
1259
|
+
// stylize captions based on user prefs
|
1260
|
+
this.stylizeCaptions(this.$captionsDiv);
|
1261
|
+
}
|
1262
|
+
if (typeof this.$descDiv !== 'undefined') {
|
1263
|
+
// stylize descriptions based on user's caption prefs
|
1264
|
+
this.stylizeCaptions(this.$descDiv);
|
1265
|
+
}
|
1266
|
+
}
|
1267
|
+
|
1268
|
+
// combine left and right controls arrays for future reference
|
1269
|
+
this.controls = [];
|
1270
|
+
for (var sec in controlLayout) if (controlLayout.hasOwnProperty(sec)) {
|
1271
|
+
this.controls = this.controls.concat(controlLayout[sec]);
|
1272
|
+
}
|
1273
|
+
|
1274
|
+
// Update state-based display of controls.
|
1275
|
+
this.refreshControls('init');
|
1276
|
+
};
|
1277
|
+
|
1278
|
+
AblePlayer.prototype.useSvg = function () {
|
1279
|
+
|
1280
|
+
// Modified from IcoMoon.io svgxuse
|
1281
|
+
// @copyright Copyright (c) 2016 IcoMoon.io
|
1282
|
+
// @license Licensed under MIT license
|
1283
|
+
// See https://github.com/Keyamoon/svgxuse
|
1284
|
+
// @version 1.1.16
|
1285
|
+
|
1286
|
+
var cache = Object.create(null); // holds xhr objects to prevent multiple requests
|
1287
|
+
var checkUseElems,
|
1288
|
+
tid; // timeout id
|
1289
|
+
var debouncedCheck = function () {
|
1290
|
+
clearTimeout(tid);
|
1291
|
+
tid = setTimeout(checkUseElems, 100);
|
1292
|
+
};
|
1293
|
+
var unobserveChanges = function () {
|
1294
|
+
return;
|
1295
|
+
};
|
1296
|
+
var observeChanges = function () {
|
1297
|
+
var observer;
|
1298
|
+
window.addEventListener('resize', debouncedCheck, false);
|
1299
|
+
window.addEventListener('orientationchange', debouncedCheck, false);
|
1300
|
+
if (window.MutationObserver) {
|
1301
|
+
observer = new MutationObserver(debouncedCheck);
|
1302
|
+
observer.observe(document.documentElement, {
|
1303
|
+
childList: true,
|
1304
|
+
subtree: true,
|
1305
|
+
attributes: true
|
1306
|
+
});
|
1307
|
+
unobserveChanges = function () {
|
1308
|
+
try {
|
1309
|
+
observer.disconnect();
|
1310
|
+
window.removeEventListener('resize', debouncedCheck, false);
|
1311
|
+
window.removeEventListener('orientationchange', debouncedCheck, false);
|
1312
|
+
} catch (ignore) {}
|
1313
|
+
};
|
1314
|
+
}
|
1315
|
+
else {
|
1316
|
+
document.documentElement.addEventListener('DOMSubtreeModified', debouncedCheck, false);
|
1317
|
+
unobserveChanges = function () {
|
1318
|
+
document.documentElement.removeEventListener('DOMSubtreeModified', debouncedCheck, false);
|
1319
|
+
window.removeEventListener('resize', debouncedCheck, false);
|
1320
|
+
window.removeEventListener('orientationchange', debouncedCheck, false);
|
1321
|
+
};
|
1322
|
+
}
|
1323
|
+
};
|
1324
|
+
var xlinkNS = 'http://www.w3.org/1999/xlink';
|
1325
|
+
checkUseElems = function () {
|
1326
|
+
var base,
|
1327
|
+
bcr,
|
1328
|
+
fallback = '', // optional fallback URL in case no base path to SVG file was given and no symbol definition was found.
|
1329
|
+
hash,
|
1330
|
+
i,
|
1331
|
+
Request,
|
1332
|
+
inProgressCount = 0,
|
1333
|
+
isHidden,
|
1334
|
+
url,
|
1335
|
+
uses,
|
1336
|
+
xhr;
|
1337
|
+
if (window.XMLHttpRequest) {
|
1338
|
+
Request = new XMLHttpRequest();
|
1339
|
+
if (Request.withCredentials !== undefined) {
|
1340
|
+
Request = XMLHttpRequest;
|
1341
|
+
}
|
1342
|
+
else {
|
1343
|
+
Request = XDomainRequest || undefined;
|
1344
|
+
}
|
1345
|
+
}
|
1346
|
+
if (Request === undefined) {
|
1347
|
+
return;
|
1348
|
+
}
|
1349
|
+
function observeIfDone() {
|
1350
|
+
// If done with making changes, start watching for chagnes in DOM again
|
1351
|
+
inProgressCount -= 1;
|
1352
|
+
if (inProgressCount === 0) { // if all xhrs were resolved
|
1353
|
+
observeChanges(); // watch for changes to DOM
|
1354
|
+
}
|
1355
|
+
}
|
1356
|
+
function attrUpdateFunc(spec) {
|
1357
|
+
return function () {
|
1358
|
+
if (cache[spec.base] !== true) {
|
1359
|
+
spec.useEl.setAttributeNS(xlinkNS, 'xlink:href', '#' + spec.hash);
|
1360
|
+
}
|
1361
|
+
};
|
1362
|
+
}
|
1363
|
+
function onloadFunc(xhr) {
|
1364
|
+
return function () {
|
1365
|
+
var body = document.body;
|
1366
|
+
var x = document.createElement('x');
|
1367
|
+
var svg;
|
1368
|
+
xhr.onload = null;
|
1369
|
+
x.innerHTML = xhr.responseText;
|
1370
|
+
svg = x.getElementsByTagName('svg')[0];
|
1371
|
+
if (svg) {
|
1372
|
+
svg.setAttribute('aria-hidden', 'true');
|
1373
|
+
svg.style.position = 'absolute';
|
1374
|
+
svg.style.width = 0;
|
1375
|
+
svg.style.height = 0;
|
1376
|
+
svg.style.overflow = 'hidden';
|
1377
|
+
body.insertBefore(svg, body.firstChild);
|
1378
|
+
}
|
1379
|
+
observeIfDone();
|
1380
|
+
};
|
1381
|
+
}
|
1382
|
+
function onErrorTimeout(xhr) {
|
1383
|
+
return function () {
|
1384
|
+
xhr.onerror = null;
|
1385
|
+
xhr.ontimeout = null;
|
1386
|
+
observeIfDone();
|
1387
|
+
};
|
1388
|
+
}
|
1389
|
+
unobserveChanges(); // stop watching for changes to DOM
|
1390
|
+
// find all use elements
|
1391
|
+
uses = document.getElementsByTagName('use');
|
1392
|
+
for (i = 0; i < uses.length; i += 1) {
|
1393
|
+
try {
|
1394
|
+
bcr = uses[i].getBoundingClientRect();
|
1395
|
+
} catch (ignore) {
|
1396
|
+
// failed to get bounding rectangle of the use element
|
1397
|
+
bcr = false;
|
1398
|
+
}
|
1399
|
+
url = uses[i].getAttributeNS(xlinkNS, 'href').split('#');
|
1400
|
+
base = url[0];
|
1401
|
+
hash = url[1];
|
1402
|
+
isHidden = bcr && bcr.left === 0 && bcr.right === 0 && bcr.top === 0 && bcr.bottom === 0;
|
1403
|
+
if (bcr && bcr.width === 0 && bcr.height === 0 && !isHidden) {
|
1404
|
+
// the use element is empty
|
1405
|
+
// if there is a reference to an external SVG, try to fetch it
|
1406
|
+
// use the optional fallback URL if there is no reference to an external SVG
|
1407
|
+
if (fallback && !base.length && hash && !document.getElementById(hash)) {
|
1408
|
+
base = fallback;
|
1409
|
+
}
|
1410
|
+
if (base.length) {
|
1411
|
+
// schedule updating xlink:href
|
1412
|
+
xhr = cache[base];
|
1413
|
+
if (xhr !== true) {
|
1414
|
+
// true signifies that prepending the SVG was not required
|
1415
|
+
setTimeout(attrUpdateFunc({
|
1416
|
+
useEl: uses[i],
|
1417
|
+
base: base,
|
1418
|
+
hash: hash
|
1419
|
+
}), 0);
|
1420
|
+
}
|
1421
|
+
if (xhr === undefined) {
|
1422
|
+
xhr = new Request();
|
1423
|
+
cache[base] = xhr;
|
1424
|
+
xhr.onload = onloadFunc(xhr);
|
1425
|
+
xhr.onerror = onErrorTimeout(xhr);
|
1426
|
+
xhr.ontimeout = onErrorTimeout(xhr);
|
1427
|
+
xhr.open('GET', base);
|
1428
|
+
xhr.send();
|
1429
|
+
inProgressCount += 1;
|
1430
|
+
}
|
1431
|
+
}
|
1432
|
+
}
|
1433
|
+
else {
|
1434
|
+
if (!isHidden) {
|
1435
|
+
if (cache[base] === undefined) {
|
1436
|
+
// remember this URL if the use element was not empty and no request was sent
|
1437
|
+
cache[base] = true;
|
1438
|
+
}
|
1439
|
+
else if (cache[base].onload) {
|
1440
|
+
// if it turns out that prepending the SVG is not necessary,
|
1441
|
+
// abort the in-progress xhr.
|
1442
|
+
cache[base].abort();
|
1443
|
+
cache[base].onload = undefined;
|
1444
|
+
cache[base] = true;
|
1445
|
+
}
|
1446
|
+
}
|
1447
|
+
}
|
1448
|
+
}
|
1449
|
+
uses = '';
|
1450
|
+
inProgressCount += 1;
|
1451
|
+
observeIfDone();
|
1452
|
+
};
|
1481
1453
|
/*
|
1482
|
-
|
1483
|
-
|
1484
|
-
|
1485
|
-
|
1486
|
-
|
1454
|
+
// The load event fires when all resources have finished loading, which allows detecting whether SVG use elements are empty.
|
1455
|
+
window.addEventListener('load', function winLoad() {
|
1456
|
+
window.removeEventListener('load', winLoad, false); // to prevent memory leaks
|
1457
|
+
tid = setTimeout(checkUseElems, 0);
|
1458
|
+
}, false);
|
1487
1459
|
*/
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1496
|
-
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
|
1569
|
-
|
1570
|
-
|
1571
|
-
|
1572
|
-
|
1573
|
-
|
1574
|
-
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
|
1580
|
-
|
1581
|
-
|
1582
|
-
|
1583
|
-
|
1584
|
-
|
1585
|
-
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
|
1590
|
-
|
1591
|
-
|
1592
|
-
|
1593
|
-
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1600
|
-
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1604
|
-
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1620
|
-
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1624
|
-
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1645
|
-
|
1646
|
-
|
1647
|
-
|
1648
|
-
|
1649
|
-
|
1650
|
-
|
1651
|
-
|
1652
|
-
|
1653
|
-
|
1654
|
-
|
1655
|
-
|
1656
|
-
|
1657
|
-
|
1658
|
-
|
1659
|
-
|
1660
|
-
|
1661
|
-
|
1662
|
-
|
1663
|
-
|
1664
|
-
|
1665
|
-
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
|
1670
|
-
|
1671
|
-
|
1672
|
-
|
1673
|
-
|
1674
|
-
|
1675
|
-
|
1676
|
-
|
1677
|
-
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1684
|
-
|
1685
|
-
|
1686
|
-
|
1460
|
+
};
|
1461
|
+
|
1462
|
+
AblePlayer.prototype.cuePlaylistItem = function(sourceIndex) {
|
1463
|
+
|
1464
|
+
// Move to a new item in a playlist.
|
1465
|
+
// NOTE: Swapping source for audio description is handled elsewhere;
|
1466
|
+
// see description.js > swapDescription()
|
1467
|
+
|
1468
|
+
/*
|
1469
|
+
// Decided against preventing a reload of the current item in the playlist.
|
1470
|
+
// If it's clickable, users should be able to click on it and expect something to happen.
|
1471
|
+
// Leaving here though in case it's determined to be desirable.
|
1472
|
+
if (sourceIndex === this.playlistItemIndex) {
|
1473
|
+
// user has requested the item that's currently playing
|
1474
|
+
// just ignore the request
|
1475
|
+
return;
|
1476
|
+
}
|
1477
|
+
this.playlistItemIndex = sourceIndex;
|
1478
|
+
*/
|
1479
|
+
|
1480
|
+
var $newItem, prevPlayer, newPlayer, itemTitle, itemLang, sources, s, i, $newSource, nowPlayingSpan;
|
1481
|
+
|
1482
|
+
var thisObj = this;
|
1483
|
+
|
1484
|
+
prevPlayer = this.player;
|
1485
|
+
|
1486
|
+
if (this.initializing) { // this is the first track - user hasn't pressed play yet
|
1487
|
+
// do nothing.
|
1488
|
+
}
|
1489
|
+
else {
|
1490
|
+
if (this.playerCreated) {
|
1491
|
+
// remove the old
|
1492
|
+
this.deletePlayer();
|
1493
|
+
}
|
1494
|
+
}
|
1495
|
+
|
1496
|
+
// Determine appropriate player to play this media
|
1497
|
+
$newItem = this.$playlist.eq(sourceIndex);
|
1498
|
+
if (this.hasAttr($newItem,'data-youtube-id')) {
|
1499
|
+
this.youTubeId = $newItem.attr('data-youtube-id');
|
1500
|
+
newPlayer = 'youtube';
|
1501
|
+
}
|
1502
|
+
else {
|
1503
|
+
newPlayer = 'html5';
|
1504
|
+
}
|
1505
|
+
|
1506
|
+
if (newPlayer === 'youtube') {
|
1507
|
+
if (prevPlayer === 'html5') {
|
1508
|
+
// pause and hide the previous media
|
1509
|
+
if (this.playing) {
|
1510
|
+
this.pauseMedia();
|
1511
|
+
}
|
1512
|
+
this.$media.hide();
|
1513
|
+
}
|
1514
|
+
}
|
1515
|
+
else {
|
1516
|
+
// the new player is not youtube
|
1517
|
+
this.youTubeId = false;
|
1518
|
+
if (prevPlayer === 'youtube') {
|
1519
|
+
// unhide the media element
|
1520
|
+
this.$media.show();
|
1521
|
+
}
|
1522
|
+
}
|
1523
|
+
this.player = newPlayer;
|
1524
|
+
|
1525
|
+
// set swappingSrc; needs to be true within recreatePlayer(), called below
|
1526
|
+
this.swappingSrc = true;
|
1527
|
+
|
1528
|
+
// transfer media attributes from playlist to media element
|
1529
|
+
if (this.hasAttr($newItem,'data-poster')) {
|
1530
|
+
this.$media.attr('poster',$newItem.attr('data-poster'));
|
1531
|
+
}
|
1532
|
+
if (this.hasAttr($newItem,'data-width')) {
|
1533
|
+
this.$media.attr('width',$newItem.attr('data-width'));
|
1534
|
+
}
|
1535
|
+
if (this.hasAttr($newItem,'data-height')) {
|
1536
|
+
this.$media.attr('height',$newItem.attr('data-height'));
|
1537
|
+
}
|
1538
|
+
if (this.hasAttr($newItem,'data-youtube-desc-id')) {
|
1539
|
+
this.$media.attr('data-youtube-desc-id',$newItem.attr('data-youtube-desc-id'));
|
1540
|
+
}
|
1541
|
+
if (this.youTubeId) {
|
1542
|
+
this.$media.attr('data-youtube-id',$newItem.attr('data-youtube-id'));
|
1543
|
+
}
|
1544
|
+
|
1545
|
+
// add new <source> elements from playlist data
|
1546
|
+
var $sourceSpans = $newItem.children('span.able-source');
|
1547
|
+
if ($sourceSpans.length) {
|
1548
|
+
$sourceSpans.each(function() {
|
1549
|
+
if (thisObj.hasAttr($(this),'data-src')) {
|
1550
|
+
// this is the only required attribute
|
1551
|
+
var $newSource = $('<source>',{
|
1552
|
+
'src': $(this).attr('data-src')
|
1553
|
+
});
|
1554
|
+
if (thisObj.hasAttr($(this),'data-type')) {
|
1555
|
+
$newSource.attr('type',$(this).attr('data-type'));
|
1556
|
+
}
|
1557
|
+
if (thisObj.hasAttr($(this),'data-desc-src')) {
|
1558
|
+
$newSource.attr('data-desc-src',$(this).attr('data-desc-src'));
|
1559
|
+
}
|
1560
|
+
if (thisObj.hasAttr($(this),'data-sign-src')) {
|
1561
|
+
$newSource.attr('data-sign-src',$(this).attr('data-sign-src'));
|
1562
|
+
}
|
1563
|
+
thisObj.$media.append($newSource);
|
1564
|
+
}
|
1565
|
+
});
|
1566
|
+
}
|
1567
|
+
|
1568
|
+
// add new <track> elements from playlist data
|
1569
|
+
var $trackSpans = $newItem.children('span.able-track');
|
1570
|
+
if ($trackSpans.length) {
|
1571
|
+
// for each element in $trackSpans, create a new <track> element
|
1572
|
+
$trackSpans.each(function() {
|
1573
|
+
if (thisObj.hasAttr($(this),'data-src') &&
|
1574
|
+
thisObj.hasAttr($(this),'data-kind') &&
|
1575
|
+
thisObj.hasAttr($(this),'data-srclang')) {
|
1576
|
+
// all required attributes are present
|
1577
|
+
var $newTrack = $('<track>',{
|
1578
|
+
'src': $(this).attr('data-src'),
|
1579
|
+
'kind': $(this).attr('data-kind'),
|
1580
|
+
'srclang': $(this).attr('data-srclang')
|
1581
|
+
});
|
1582
|
+
if (thisObj.hasAttr($(this),'data-label')) {
|
1583
|
+
$newTrack.attr('label',$(this).attr('data-label'));
|
1584
|
+
}
|
1585
|
+
thisObj.$media.append($newTrack);
|
1586
|
+
}
|
1587
|
+
});
|
1588
|
+
}
|
1589
|
+
|
1590
|
+
itemTitle = $newItem.text();
|
1591
|
+
if (this.hasAttr($newItem,'lang')) {
|
1592
|
+
itemLang = $newItem.attr('lang');
|
1593
|
+
}
|
1594
|
+
// Update relevant arrays
|
1595
|
+
this.$sources = this.$media.find('source');
|
1596
|
+
|
1597
|
+
// recreate player, informed by new attributes and track elements
|
1598
|
+
this.recreatePlayer();
|
1599
|
+
|
1600
|
+
// update playlist to indicate which item is playing
|
1601
|
+
//$('.able-playlist li').removeClass('able-current');
|
1602
|
+
this.$playlist.removeClass('able-current');
|
1603
|
+
this.$playlist.eq(sourceIndex).addClass('able-current');
|
1604
|
+
|
1605
|
+
// update Now Playing div
|
1606
|
+
if (this.showNowPlaying === true) {
|
1607
|
+
if (typeof this.$nowPlayingDiv !== 'undefined') {
|
1608
|
+
nowPlayingSpan = $('<span>');
|
1609
|
+
if (typeof itemLang !== 'undefined') {
|
1610
|
+
nowPlayingSpan.attr('lang',itemLang);
|
1611
|
+
}
|
1612
|
+
nowPlayingSpan.html('<span>' + this.tt.selectedTrack + ':</span>' + itemTitle);
|
1613
|
+
this.$nowPlayingDiv.html(nowPlayingSpan);
|
1614
|
+
}
|
1615
|
+
}
|
1616
|
+
|
1617
|
+
// finished swapping src, now reload the new source file.
|
1618
|
+
this.swappingSrc = false;
|
1619
|
+
|
1620
|
+
if (this.player === 'html5') {
|
1621
|
+
this.media.load();
|
1622
|
+
}
|
1623
|
+
else if (this.player === 'youtube') {
|
1624
|
+
// TODO: Load new youTubeId
|
1625
|
+
}
|
1626
|
+
|
1627
|
+
// if this.swappingSrc is true, media will autoplay when ready
|
1628
|
+
if (this.initializing) { // this is the first track - user hasn't pressed play yet
|
1629
|
+
this.swappingSrc = false;
|
1630
|
+
}
|
1631
|
+
else {
|
1632
|
+
this.swappingSrc = true;
|
1633
|
+
if (this.player === 'html5') {
|
1634
|
+
this.media.load();
|
1635
|
+
}
|
1636
|
+
else if (this.player === 'youtube') {
|
1637
|
+
this.okToPlay = true;
|
1638
|
+
}
|
1639
|
+
}
|
1640
|
+
};
|
1641
|
+
|
1642
|
+
AblePlayer.prototype.deletePlayer = function() {
|
1643
|
+
|
1644
|
+
// remove previous video's attributes and child elements from media element
|
1645
|
+
if (this.player == 'youtube') {
|
1646
|
+
var $youTubeIframe = this.$mediaContainer.find('iframe');
|
1647
|
+
$youTubeIframe.remove();
|
1648
|
+
}
|
1649
|
+
this.$media.removeAttr('poster width height');
|
1650
|
+
this.$media.empty();
|
1651
|
+
|
1652
|
+
// Empty elements that will be rebuilt
|
1653
|
+
this.$controllerDiv.empty();
|
1654
|
+
// this.$statusBarDiv.empty();
|
1655
|
+
// this.$timer.empty();
|
1656
|
+
this.$elapsedTimeContainer.empty().text('0:00'); // span.able-elapsedTime
|
1657
|
+
this.$durationContainer.empty(); // span.able-duration
|
1658
|
+
|
1659
|
+
// Remove popup windows and modal dialogs; these too will be rebuilt
|
1660
|
+
if (this.$signWindow) {
|
1661
|
+
this.$signWindow.remove();
|
1662
|
+
}
|
1663
|
+
if (this.$transcriptArea) {
|
1664
|
+
this.$transcriptArea.remove();
|
1665
|
+
}
|
1666
|
+
$('.able-modal-dialog').remove();
|
1667
|
+
|
1668
|
+
// reset key variables
|
1669
|
+
this.hasCaptions = false;
|
1670
|
+
this.hasChapters = false;
|
1671
|
+
this.captionsPopup = null;
|
1672
|
+
this.chaptersPopup = null;
|
1673
|
+
};
|
1674
|
+
|
1675
|
+
AblePlayer.prototype.getButtonTitle = function(control) {
|
1676
|
+
|
1677
|
+
if (control === 'playpause') {
|
1678
|
+
return this.tt.play;
|
1679
|
+
}
|
1680
|
+
else if (control === 'play') {
|
1681
|
+
return this.tt.play;
|
1682
|
+
}
|
1683
|
+
else if (control === 'pause') {
|
1684
|
+
return this.tt.pause;
|
1685
|
+
}
|
1686
|
+
else if (control === 'restart') {
|
1687
|
+
return this.tt.restart;
|
1688
|
+
}
|
1689
|
+
else if (control === 'rewind') {
|
1690
|
+
return this.tt.rewind;
|
1691
|
+
}
|
1692
|
+
else if (control === 'forward') {
|
1693
|
+
return this.tt.forward;
|
1694
|
+
}
|
1695
|
+
else if (control === 'captions') {
|
1696
|
+
if (this.captions.length > 1) {
|
1697
|
+
return this.tt.captions;
|
1698
|
+
}
|
1699
|
+
else {
|
1700
|
+
if (this.captionsOn) {
|
1701
|
+
return this.tt.hideCaptions;
|
1702
|
+
}
|
1703
|
+
else {
|
1704
|
+
return this.tt.showCaptions;
|
1705
|
+
}
|
1706
|
+
}
|
1707
|
+
}
|
1708
|
+
else if (control === 'descriptions') {
|
1709
|
+
if (this.descOn) {
|
1710
|
+
return this.tt.turnOffDescriptions;
|
1711
|
+
}
|
1712
|
+
else {
|
1713
|
+
return this.tt.turnOnDescriptions;
|
1714
|
+
}
|
1715
|
+
}
|
1716
|
+
else if (control === 'transcript') {
|
1717
|
+
if (this.$transcriptDiv.is(':visible')) {
|
1718
|
+
return this.tt.hideTranscript;
|
1719
|
+
}
|
1720
|
+
else {
|
1721
|
+
return this.tt.showTranscript;
|
1722
|
+
}
|
1723
|
+
}
|
1724
|
+
else if (control === 'chapters') {
|
1725
|
+
return this.tt.chapters;
|
1726
|
+
}
|
1727
|
+
else if (control === 'sign') {
|
1728
|
+
return this.tt.sign;
|
1729
|
+
}
|
1730
|
+
else if (control === 'volume') {
|
1731
|
+
return this.tt.volume;
|
1732
|
+
}
|
1733
|
+
else if (control === 'faster') {
|
1734
|
+
return this.tt.faster;
|
1735
|
+
}
|
1736
|
+
else if (control === 'slower') {
|
1737
|
+
return this.tt.slower;
|
1738
|
+
}
|
1739
|
+
else if (control === 'preferences') {
|
1740
|
+
return this.tt.preferences;
|
1741
|
+
}
|
1742
|
+
else if (control === 'help') {
|
1743
|
+
// return this.tt.help;
|
1744
|
+
}
|
1745
|
+
else {
|
1746
|
+
// there should be no other controls, but just in case:
|
1747
|
+
// return the name of the control with first letter in upper case
|
1748
|
+
// ultimately will need to get a translated label from this.tt
|
1749
|
+
if (this.debug) {
|
1750
|
+
console.log('Found an untranslated label: ' + control);
|
1751
|
+
}
|
1752
|
+
return control.charAt(0).toUpperCase() + control.slice(1);
|
1753
|
+
}
|
1754
|
+
};
|
1687
1755
|
|
1688
1756
|
|
1689
1757
|
})(jQuery);
|