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