wai-website-theme 0.1.0
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +52 -0
- data/_data/lang.json +730 -0
- data/_data/techniques.yml +180 -0
- data/_data/wcag.yml +125 -0
- data/_includes/.DS_Store +0 -0
- data/_includes/body-class.html +1 -0
- data/_includes/box.html +10 -0
- data/_includes/excol.html +13 -0
- data/_includes/footer.html +40 -0
- data/_includes/head.html +23 -0
- data/_includes/header.html +59 -0
- data/_includes/icon.html +6 -0
- data/_includes/img.html +17 -0
- data/_includes/multilang-list-policy-links.html +29 -0
- data/_includes/multilang-list.html +35 -0
- data/_includes/multilang-policy-title.html +5 -0
- data/_includes/multilang-title-full.html +1 -0
- data/_includes/multilang-title.html +1 -0
- data/_includes/navlist.html +22 -0
- data/_includes/notes.html +2 -0
- data/_includes/prevnext.html +34 -0
- data/_includes/resources.html +19 -0
- data/_includes/sidenav.html +65 -0
- data/_includes/sidenote.html +14 -0
- data/_includes/toc.html +10 -0
- data/_includes/video-player.html +99 -0
- data/_layouts/default.html +26 -0
- data/_layouts/home.html +14 -0
- data/_layouts/news.html +21 -0
- data/_layouts/none.html +1 -0
- data/_layouts/policy.html +72 -0
- data/_layouts/sidenav.html +27 -0
- data/_layouts/sidenavsidebar.html +22 -0
- data/assets/ableplayer/.gitattributes +14 -0
- data/assets/ableplayer/.gitignore +7 -0
- data/assets/ableplayer/Gruntfile.js +105 -0
- data/assets/ableplayer/LICENSE +26 -0
- data/assets/ableplayer/README.md +656 -0
- data/assets/ableplayer/build/ableplayer.dist.js +12157 -0
- data/assets/ableplayer/build/ableplayer.js +12157 -0
- data/assets/ableplayer/build/ableplayer.min.css +2 -0
- data/assets/ableplayer/build/ableplayer.min.js +8 -0
- data/assets/ableplayer/button-icons/able-icons.svg +116 -0
- data/assets/ableplayer/button-icons/black/captions.png +0 -0
- data/assets/ableplayer/button-icons/black/chapters.png +0 -0
- data/assets/ableplayer/button-icons/black/close.png +0 -0
- data/assets/ableplayer/button-icons/black/descriptions.png +0 -0
- data/assets/ableplayer/button-icons/black/ellipsis.png +0 -0
- data/assets/ableplayer/button-icons/black/faster.png +0 -0
- data/assets/ableplayer/button-icons/black/forward.png +0 -0
- data/assets/ableplayer/button-icons/black/fullscreen-collapse.png +0 -0
- data/assets/ableplayer/button-icons/black/fullscreen-expand.png +0 -0
- data/assets/ableplayer/button-icons/black/help.png +0 -0
- data/assets/ableplayer/button-icons/black/next.png +0 -0
- data/assets/ableplayer/button-icons/black/pause.png +0 -0
- data/assets/ableplayer/button-icons/black/pipe.png +0 -0
- data/assets/ableplayer/button-icons/black/play.png +0 -0
- data/assets/ableplayer/button-icons/black/preferences.png +0 -0
- data/assets/ableplayer/button-icons/black/previous.png +0 -0
- data/assets/ableplayer/button-icons/black/rabbit.png +0 -0
- data/assets/ableplayer/button-icons/black/restart.png +0 -0
- data/assets/ableplayer/button-icons/black/rewind.png +0 -0
- data/assets/ableplayer/button-icons/black/sign.png +0 -0
- data/assets/ableplayer/button-icons/black/slower.png +0 -0
- data/assets/ableplayer/button-icons/black/stop.png +0 -0
- data/assets/ableplayer/button-icons/black/transcript.png +0 -0
- data/assets/ableplayer/button-icons/black/turtle.png +0 -0
- data/assets/ableplayer/button-icons/black/volume-loud.png +0 -0
- data/assets/ableplayer/button-icons/black/volume-medium.png +0 -0
- data/assets/ableplayer/button-icons/black/volume-mute.png +0 -0
- data/assets/ableplayer/button-icons/black/volume-soft.png +0 -0
- data/assets/ableplayer/button-icons/fonts/able.eot +0 -0
- data/assets/ableplayer/button-icons/fonts/able.svg +40 -0
- data/assets/ableplayer/button-icons/fonts/able.ttf +0 -0
- data/assets/ableplayer/button-icons/fonts/able.woff +0 -0
- data/assets/ableplayer/button-icons/white/captions.png +0 -0
- data/assets/ableplayer/button-icons/white/chapters.png +0 -0
- data/assets/ableplayer/button-icons/white/close.png +0 -0
- data/assets/ableplayer/button-icons/white/descriptions.png +0 -0
- data/assets/ableplayer/button-icons/white/ellipsis.png +0 -0
- data/assets/ableplayer/button-icons/white/faster.png +0 -0
- data/assets/ableplayer/button-icons/white/forward.png +0 -0
- data/assets/ableplayer/button-icons/white/fullscreen-collapse.png +0 -0
- data/assets/ableplayer/button-icons/white/fullscreen-expand.png +0 -0
- data/assets/ableplayer/button-icons/white/help.png +0 -0
- data/assets/ableplayer/button-icons/white/next.png +0 -0
- data/assets/ableplayer/button-icons/white/pause.png +0 -0
- data/assets/ableplayer/button-icons/white/pipe.png +0 -0
- data/assets/ableplayer/button-icons/white/play.png +0 -0
- data/assets/ableplayer/button-icons/white/preferences.png +0 -0
- data/assets/ableplayer/button-icons/white/previous.png +0 -0
- data/assets/ableplayer/button-icons/white/rabbit.png +0 -0
- data/assets/ableplayer/button-icons/white/restart.png +0 -0
- data/assets/ableplayer/button-icons/white/rewind.png +0 -0
- data/assets/ableplayer/button-icons/white/sign.png +0 -0
- data/assets/ableplayer/button-icons/white/slower.png +0 -0
- data/assets/ableplayer/button-icons/white/stop.png +0 -0
- data/assets/ableplayer/button-icons/white/transcript.png +0 -0
- data/assets/ableplayer/button-icons/white/turtle.png +0 -0
- data/assets/ableplayer/button-icons/white/volume-loud.png +0 -0
- data/assets/ableplayer/button-icons/white/volume-medium.png +0 -0
- data/assets/ableplayer/button-icons/white/volume-mute.png +0 -0
- data/assets/ableplayer/button-icons/white/volume-soft.png +0 -0
- data/assets/ableplayer/images/wingrip.png +0 -0
- data/assets/ableplayer/package.json +22 -0
- data/assets/ableplayer/scripts/JQuery.doWhen.js +113 -0
- data/assets/ableplayer/scripts/ableplayer-base.js +440 -0
- data/assets/ableplayer/scripts/browser.js +162 -0
- data/assets/ableplayer/scripts/buildplayer.js +1609 -0
- data/assets/ableplayer/scripts/caption.js +385 -0
- data/assets/ableplayer/scripts/chapters.js +242 -0
- data/assets/ableplayer/scripts/control.js +1514 -0
- data/assets/ableplayer/scripts/description.js +283 -0
- data/assets/ableplayer/scripts/dialog.js +147 -0
- data/assets/ableplayer/scripts/dragdrop.js +766 -0
- data/assets/ableplayer/scripts/event.js +595 -0
- data/assets/ableplayer/scripts/initialize.js +725 -0
- data/assets/ableplayer/scripts/langs.js +750 -0
- data/assets/ableplayer/scripts/metadata.js +134 -0
- data/assets/ableplayer/scripts/misc.js +72 -0
- data/assets/ableplayer/scripts/preference.js +909 -0
- data/assets/ableplayer/scripts/search.js +171 -0
- data/assets/ableplayer/scripts/sign.js +92 -0
- data/assets/ableplayer/scripts/slider.js +454 -0
- data/assets/ableplayer/scripts/track.js +296 -0
- data/assets/ableplayer/scripts/transcript.js +590 -0
- data/assets/ableplayer/scripts/translation.js +66 -0
- data/assets/ableplayer/scripts/volume.js +383 -0
- data/assets/ableplayer/scripts/webvtt.js +765 -0
- data/assets/ableplayer/scripts/youtube.js +471 -0
- data/assets/ableplayer/styles/ableplayer.css +1241 -0
- data/assets/ableplayer/thirdparty/js.cookie.js +145 -0
- data/assets/ableplayer/thirdparty/modernizr.custom.js +4 -0
- data/assets/ableplayer/translations/ca.js +1 -0
- data/assets/ableplayer/translations/de.js +1 -0
- data/assets/ableplayer/translations/en.js +305 -0
- data/assets/ableplayer/translations/es.js +305 -0
- data/assets/ableplayer/translations/fr.js +305 -0
- data/assets/ableplayer/translations/it.js +303 -0
- data/assets/ableplayer/translations/ja.js +305 -0
- data/assets/ableplayer/translations/nl.js +305 -0
- data/assets/css/style.css +4360 -0
- data/assets/css/style.css.map +1 -0
- data/assets/fonts/anonymouspro-bold.woff +0 -0
- data/assets/fonts/anonymouspro-bold.woff2 +0 -0
- data/assets/fonts/anonymouspro-bolditalic.woff +0 -0
- data/assets/fonts/anonymouspro-bolditalic.woff2 +0 -0
- data/assets/fonts/anonymouspro-italic.woff +0 -0
- data/assets/fonts/anonymouspro-italic.woff2 +0 -0
- data/assets/fonts/anonymouspro-regular.woff +0 -0
- data/assets/fonts/anonymouspro-regular.woff2 +0 -0
- data/assets/fonts/notosans-bold.woff +0 -0
- data/assets/fonts/notosans-bold.woff2 +0 -0
- data/assets/fonts/notosans-bolditalic.woff +0 -0
- data/assets/fonts/notosans-bolditalic.woff2 +0 -0
- data/assets/fonts/notosans-italic.woff +0 -0
- data/assets/fonts/notosans-italic.woff2 +0 -0
- data/assets/fonts/notosans-regular.woff +0 -0
- data/assets/fonts/notosans-regular.woff2 +0 -0
- data/assets/images/.DS_Store +0 -0
- data/assets/images/Shape.svg +10 -0
- data/assets/images/icon-related-content.svg +14 -0
- data/assets/images/icons.svg +126 -0
- data/assets/images/teaser-image@1x.jpg +0 -0
- data/assets/images/teaser-image@2x.jpg +0 -0
- data/assets/images/w3c.sketch +0 -0
- data/assets/images/w3c.svg +10 -0
- data/assets/scripts/jquery.min.js +4 -0
- data/assets/scripts/main.js +208 -0
- data/assets/scripts/svg4everybody.js +1 -0
- metadata +257 -0
|
@@ -0,0 +1,1514 @@
|
|
|
1
|
+
(function ($) {
|
|
2
|
+
AblePlayer.prototype.seekTo = function (newTime) {
|
|
3
|
+
|
|
4
|
+
this.seeking = true;
|
|
5
|
+
this.liveUpdatePending = true;
|
|
6
|
+
|
|
7
|
+
if (this.player === 'html5') {
|
|
8
|
+
var seekable;
|
|
9
|
+
|
|
10
|
+
this.startTime = newTime;
|
|
11
|
+
// Check HTML5 media "seekable" property to be sure media is seekable to startTime
|
|
12
|
+
seekable = this.media.seekable;
|
|
13
|
+
if (seekable.length > 0 && this.startTime >= seekable.start(0) && this.startTime <= seekable.end(0)) {
|
|
14
|
+
// ok to seek to startTime
|
|
15
|
+
// canplaythrough will be triggered when seeking is complete
|
|
16
|
+
// this.seeking will be set to false at that point
|
|
17
|
+
this.media.currentTime = this.startTime;
|
|
18
|
+
if (this.hasSignLanguage && this.signVideo) {
|
|
19
|
+
// keep sign languge video in sync
|
|
20
|
+
this.signVideo.currentTime = this.startTime;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (this.player === 'jw' && this.jwPlayer) {
|
|
25
|
+
// pause JW Player temporarily.
|
|
26
|
+
// When seek has successfully reached newTime,
|
|
27
|
+
// onSeek event will be called, and playback will be resumed
|
|
28
|
+
this.jwSeekPause = true;
|
|
29
|
+
this.jwPlayer.seek(newTime);
|
|
30
|
+
}
|
|
31
|
+
else if (this.player === 'youtube') {
|
|
32
|
+
this.youTubePlayer.seekTo(newTime,true);
|
|
33
|
+
if (newTime > 0) {
|
|
34
|
+
if (typeof this.$posterImg !== 'undefined') {
|
|
35
|
+
this.$posterImg.hide();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
this.refreshControls();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
AblePlayer.prototype.getDuration = function () {
|
|
43
|
+
|
|
44
|
+
var duration;
|
|
45
|
+
if (this.player === 'html5') {
|
|
46
|
+
duration = this.media.duration;
|
|
47
|
+
}
|
|
48
|
+
else if (this.player === 'jw' && this.jwPlayer) {
|
|
49
|
+
duration = this.jwPlayer.getDuration();
|
|
50
|
+
}
|
|
51
|
+
else if (this.player === 'youtube' && this.youTubePlayer) {
|
|
52
|
+
duration = this.youTubePlayer.getDuration();
|
|
53
|
+
}
|
|
54
|
+
if (duration === undefined || isNaN(duration) || duration === -1) {
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
return duration;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
AblePlayer.prototype.getElapsed = function () {
|
|
61
|
+
var position;
|
|
62
|
+
if (this.player === 'html5') {
|
|
63
|
+
position = this.media.currentTime;
|
|
64
|
+
}
|
|
65
|
+
else if (this.player === 'jw' && this.jwPlayer) {
|
|
66
|
+
if (this.jwPlayer.getState() === 'IDLE') {
|
|
67
|
+
return 0;
|
|
68
|
+
}
|
|
69
|
+
position = this.jwPlayer.getPosition();
|
|
70
|
+
}
|
|
71
|
+
else if (this.player === 'youtube') {
|
|
72
|
+
if (this.youTubePlayer) {
|
|
73
|
+
position = this.youTubePlayer.getCurrentTime();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (position === undefined || isNaN(position) || position === -1) {
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
return position;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Returns one of the following states:
|
|
84
|
+
// 'stopped' - Not yet played for the first time, or otherwise reset to unplayed.
|
|
85
|
+
// 'ended' - Finished playing.
|
|
86
|
+
// 'paused' - Not playing, but not stopped or ended.
|
|
87
|
+
// 'buffering' - Momentarily paused to load, but will resume once data is loaded.
|
|
88
|
+
// 'playing' - Currently playing.
|
|
89
|
+
AblePlayer.prototype.getPlayerState = function () {
|
|
90
|
+
if (this.swappingSrc) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (this.player === 'html5') {
|
|
94
|
+
if (this.media.paused) {
|
|
95
|
+
if (this.getElapsed() === 0) {
|
|
96
|
+
return 'stopped';
|
|
97
|
+
}
|
|
98
|
+
else if (this.media.ended) {
|
|
99
|
+
return 'ended';
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
return 'paused';
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (this.media.readyState !== 4) {
|
|
106
|
+
return 'buffering';
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
return 'playing';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else if (this.player === 'jw' && this.jwPlayer) {
|
|
113
|
+
if (this.jwPlayer.getState() === 'PAUSED' || this.jwPlayer.getState() === 'IDLE' || this.jwPlayer.getState() === undefined) {
|
|
114
|
+
|
|
115
|
+
if (this.getElapsed() === 0) {
|
|
116
|
+
return 'stopped';
|
|
117
|
+
}
|
|
118
|
+
else if (this.getElapsed() === this.getDuration()) {
|
|
119
|
+
return 'ended';
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
return 'paused';
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
else if (this.jwPlayer.getState() === 'BUFFERING') {
|
|
126
|
+
return 'buffering';
|
|
127
|
+
}
|
|
128
|
+
else if (this.jwPlayer.getState() === 'PLAYING') {
|
|
129
|
+
return 'playing';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else if (this.player === 'youtube' && this.youTubePlayer) {
|
|
133
|
+
var state = this.youTubePlayer.getPlayerState();
|
|
134
|
+
if (state === -1 || state === 5) {
|
|
135
|
+
return 'stopped';
|
|
136
|
+
}
|
|
137
|
+
else if (state === 0) {
|
|
138
|
+
return 'ended';
|
|
139
|
+
}
|
|
140
|
+
else if (state === 1) {
|
|
141
|
+
return 'playing';
|
|
142
|
+
}
|
|
143
|
+
else if (state === 2) {
|
|
144
|
+
return 'paused';
|
|
145
|
+
}
|
|
146
|
+
else if (state === 3) {
|
|
147
|
+
return 'buffering';
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
AblePlayer.prototype.isPlaybackRateSupported = function () {
|
|
153
|
+
if (this.player === 'html5') {
|
|
154
|
+
return this.media.playbackRate ? true : false;
|
|
155
|
+
}
|
|
156
|
+
else if (this.player === 'jw' && this.jwPlayer) {
|
|
157
|
+
// Not directly supported by JW player; can hack for HTML5 version by finding the dynamically generated video tag, but decided not to do that.
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
else if (this.player === 'youtube') {
|
|
161
|
+
// Youtube always supports a finite list of playback rates. Only expose controls if more than one is available.
|
|
162
|
+
return (this.youTubePlayer.getAvailablePlaybackRates().length > 1);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
AblePlayer.prototype.setPlaybackRate = function (rate) {
|
|
167
|
+
rate = Math.max(0.5, rate);
|
|
168
|
+
if (this.player === 'html5') {
|
|
169
|
+
this.media.playbackRate = rate;
|
|
170
|
+
}
|
|
171
|
+
else if (this.player === 'youtube') {
|
|
172
|
+
this.youTubePlayer.setPlaybackRate(rate);
|
|
173
|
+
}
|
|
174
|
+
if (this.hasSignLanguage && this.signVideo) {
|
|
175
|
+
this.signVideo.playbackRate = rate;
|
|
176
|
+
}
|
|
177
|
+
this.$speed.text(this.tt.speed + ': ' + rate.toFixed(2).toString() + 'x');
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
AblePlayer.prototype.getPlaybackRate = function () {
|
|
181
|
+
if (this.player === 'html5') {
|
|
182
|
+
return this.media.playbackRate;
|
|
183
|
+
}
|
|
184
|
+
else if (this.player === 'jw' && this.jwPlayer) {
|
|
185
|
+
// Unsupported, always the normal rate.
|
|
186
|
+
return 1;
|
|
187
|
+
}
|
|
188
|
+
else if (this.player === 'youtube') {
|
|
189
|
+
return this.youTubePlayer.getPlaybackRate();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// Note there are three player states that count as paused in this sense,
|
|
194
|
+
// and one of them is named 'paused'.
|
|
195
|
+
// A better name would be 'isCurrentlyNotPlayingOrBuffering'
|
|
196
|
+
AblePlayer.prototype.isPaused = function () {
|
|
197
|
+
var state = this.getPlayerState();
|
|
198
|
+
return state === 'paused' || state === 'stopped' || state === 'ended';
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
AblePlayer.prototype.pauseMedia = function () {
|
|
202
|
+
if (this.player === 'html5') {
|
|
203
|
+
this.media.pause(true);
|
|
204
|
+
if (this.hasSignLanguage && this.signVideo) {
|
|
205
|
+
this.signVideo.pause(true);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else if (this.player === 'jw' && this.jwPlayer) {
|
|
209
|
+
this.jwPlayer.pause(true);
|
|
210
|
+
}
|
|
211
|
+
else if (this.player === 'youtube') {
|
|
212
|
+
this.youTubePlayer.pauseVideo();
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
AblePlayer.prototype.playMedia = function () {
|
|
217
|
+
if (this.player === 'html5') {
|
|
218
|
+
this.media.play(true);
|
|
219
|
+
if (this.hasSignLanguage && this.signVideo) {
|
|
220
|
+
this.signVideo.play(true);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
else if (this.player === 'jw' && this.jwPlayer) {
|
|
224
|
+
this.jwPlayer.play(true);
|
|
225
|
+
}
|
|
226
|
+
else if (this.player === 'youtube') {
|
|
227
|
+
this.youTubePlayer.playVideo();
|
|
228
|
+
if (typeof this.$posterImg !== 'undefined') {
|
|
229
|
+
this.$posterImg.hide();
|
|
230
|
+
}
|
|
231
|
+
this.stoppingYouTube = false;
|
|
232
|
+
}
|
|
233
|
+
this.startedPlaying = true;
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
AblePlayer.prototype.refreshControls = function() {
|
|
237
|
+
|
|
238
|
+
var thisObj, duration, elapsed, lastChapterIndex, displayElapsed,
|
|
239
|
+
updateLive, textByState, timestamp, widthUsed,
|
|
240
|
+
leftControls, rightControls, seekbarWidth, seekbarSpacer, captionsCount,
|
|
241
|
+
buffered, newTop, svgLink, newSvgLink,
|
|
242
|
+
statusBarHeight, speedHeight, statusBarWidthBreakpoint;
|
|
243
|
+
|
|
244
|
+
thisObj = this;
|
|
245
|
+
if (this.swappingSrc) {
|
|
246
|
+
// wait until new source has loaded before refreshing controls
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
duration = this.getDuration();
|
|
251
|
+
elapsed = this.getElapsed();
|
|
252
|
+
|
|
253
|
+
if (this.useChapterTimes) {
|
|
254
|
+
this.chapterDuration = this.getChapterDuration();
|
|
255
|
+
this.chapterElapsed = this.getChapterElapsed();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (this.useFixedSeekInterval === false && this.seekIntervalCalculated === false && duration > 0) {
|
|
259
|
+
// couldn't calculate seekInterval previously; try again.
|
|
260
|
+
this.setSeekInterval();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (this.seekBar) {
|
|
264
|
+
|
|
265
|
+
if (this.useChapterTimes) {
|
|
266
|
+
lastChapterIndex = this.selectedChapters.cues.length-1;
|
|
267
|
+
if (this.selectedChapters.cues[lastChapterIndex] == this.currentChapter) {
|
|
268
|
+
// this is the last chapter
|
|
269
|
+
if (this.currentChapter.end !== duration) {
|
|
270
|
+
// chapter ends before or after video ends
|
|
271
|
+
// need to adjust seekbar duration to match video end
|
|
272
|
+
this.seekBar.setDuration(duration - this.currentChapter.start);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
this.seekBar.setDuration(this.chapterDuration);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
// this is not the last chapter
|
|
280
|
+
this.seekBar.setDuration(this.chapterDuration);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
this.seekBar.setDuration(duration);
|
|
285
|
+
}
|
|
286
|
+
if (!(this.seekBar.tracking)) {
|
|
287
|
+
// Only update the aria live region if we have an update pending (from a
|
|
288
|
+
// seek button control) or if the seekBar has focus.
|
|
289
|
+
// We use document.activeElement instead of $(':focus') due to a strange bug:
|
|
290
|
+
// When the seekHead element is focused, .is(':focus') is failing and $(':focus') is returning an undefined element.
|
|
291
|
+
updateLive = this.liveUpdatePending || this.seekBar.seekHead.is($(document.activeElement));
|
|
292
|
+
this.liveUpdatePending = false;
|
|
293
|
+
if (this.useChapterTimes) {
|
|
294
|
+
this.seekBar.setPosition(this.chapterElapsed, updateLive);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
this.seekBar.setPosition(elapsed, updateLive);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// When seeking, display the seek bar time instead of the actual elapsed time.
|
|
302
|
+
if (this.seekBar.tracking) {
|
|
303
|
+
displayElapsed = this.seekBar.lastTrackPosition;
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
if (this.useChapterTimes) {
|
|
307
|
+
displayElapsed = this.chapterElapsed;
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
displayElapsed = elapsed;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (this.useChapterTimes) {
|
|
315
|
+
this.$durationContainer.text(' / ' + this.formatSecondsAsColonTime(this.chapterDuration));
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
this.$durationContainer.text(' / ' + this.formatSecondsAsColonTime(duration));
|
|
319
|
+
}
|
|
320
|
+
this.$elapsedTimeContainer.text(this.formatSecondsAsColonTime(displayElapsed));
|
|
321
|
+
|
|
322
|
+
textByState = {
|
|
323
|
+
'stopped': this.tt.statusStopped,
|
|
324
|
+
'paused': this.tt.statusPaused,
|
|
325
|
+
'playing': this.tt.statusPlaying,
|
|
326
|
+
'buffering': this.tt.statusBuffering,
|
|
327
|
+
'ended': this.tt.statusEnd
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
if (this.stoppingYouTube) {
|
|
331
|
+
// stoppingYouTube is true temporarily while video is paused and seeking to 0
|
|
332
|
+
// See notes in handleRestart()
|
|
333
|
+
// this.stoppingYouTube will be reset when seek to 0 is finished (in event.js > onMediaUpdateTime())
|
|
334
|
+
if (this.$status.text() !== this.tt.statusStopped) {
|
|
335
|
+
this.$status.text(this.tt.statusStopped);
|
|
336
|
+
}
|
|
337
|
+
if (this.$playpauseButton.find('span').first().hasClass('icon-pause')) {
|
|
338
|
+
if (this.iconType === 'font') {
|
|
339
|
+
this.$playpauseButton.find('span').first().removeClass('icon-pause').addClass('icon-play');
|
|
340
|
+
this.$playpauseButton.find('span.able-clipped').text(this.tt.play);
|
|
341
|
+
}
|
|
342
|
+
else if (this.iconType === 'svg') {
|
|
343
|
+
// TODO: Add play/pause toggle for SVG
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
this.$playpauseButton.find('img').attr('src',this.playButtonImg);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
if (typeof this.$status !== 'undefined' && typeof this.seekBar !== 'undefined') {
|
|
352
|
+
// Update the text only if it's changed since it has role="alert";
|
|
353
|
+
// also don't update while tracking, since this may Pause/Play the player but we don't want to send a Pause/Play update.
|
|
354
|
+
if (this.$status.text() !== textByState[this.getPlayerState()] && !this.seekBar.tracking) {
|
|
355
|
+
// Debounce updates; only update after status has stayed steadily different for 250ms.
|
|
356
|
+
timestamp = (new Date()).getTime();
|
|
357
|
+
if (!this.statusDebounceStart) {
|
|
358
|
+
this.statusDebounceStart = timestamp;
|
|
359
|
+
// Make sure refreshControls gets called again at the appropriate time to check.
|
|
360
|
+
this.statusTimeout = setTimeout(function () {
|
|
361
|
+
thisObj.refreshControls();
|
|
362
|
+
}, 300);
|
|
363
|
+
}
|
|
364
|
+
else if ((timestamp - this.statusDebounceStart) > 250) {
|
|
365
|
+
this.$status.text(textByState[this.getPlayerState()]);
|
|
366
|
+
this.statusDebounceStart = null;
|
|
367
|
+
clearTimeout(this.statusTimeout);
|
|
368
|
+
this.statusTimeout = null;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
this.statusDebounceStart = null;
|
|
373
|
+
clearTimeout(this.statusTimeout);
|
|
374
|
+
this.statusTimeout = null;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Don't change play/pause button display while using the seek bar (or if YouTube stopped)
|
|
378
|
+
if (!this.seekBar.tracking && !this.stoppingYouTube) {
|
|
379
|
+
if (this.isPaused()) {
|
|
380
|
+
this.$playpauseButton.attr('aria-label',this.tt.play);
|
|
381
|
+
|
|
382
|
+
if (this.iconType === 'font') {
|
|
383
|
+
this.$playpauseButton.find('span').first().removeClass('icon-pause').addClass('icon-play');
|
|
384
|
+
this.$playpauseButton.find('span.able-clipped').text(this.tt.play);
|
|
385
|
+
}
|
|
386
|
+
else if (this.iconType === 'svg') {
|
|
387
|
+
// Not currently working. SVG is a work in progress
|
|
388
|
+
this.$playpauseButton.find('svg').removeClass('svg-pause').addClass('svg-play');
|
|
389
|
+
svgLink = this.$playpauseButton.find('use').attr('xlink:href');
|
|
390
|
+
newSvgLink = svgLink.replace('svg-pause','svg-play');
|
|
391
|
+
this.$playpauseButton.find('use').attr(newSvgLink);
|
|
392
|
+
this.$playpauseButton.find('span.able-clipped').text(this.tt.play);
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
this.$playpauseButton.find('img').attr('src',this.playButtonImg);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
this.$playpauseButton.attr('aria-label',this.tt.pause);
|
|
400
|
+
|
|
401
|
+
if (this.iconType === 'font') {
|
|
402
|
+
this.$playpauseButton.find('span').first().removeClass('icon-play').addClass('icon-pause');
|
|
403
|
+
this.$playpauseButton.find('span.able-clipped').text(this.tt.pause);
|
|
404
|
+
}
|
|
405
|
+
else if (this.iconType === 'svg') {
|
|
406
|
+
// Not currently working. SVG is a work in progress
|
|
407
|
+
this.$playpauseButton.find('svg').removeClass('svg-play').addClass('svg-pause');
|
|
408
|
+
svgLink = this.$playpauseButton.find('use').attr('xlink:href');
|
|
409
|
+
newSvgLink = svgLink.replace('svg-play','svg-pause');
|
|
410
|
+
this.$playpauseButton.find('use').attr(newSvgLink);
|
|
411
|
+
this.$playpauseButton.find('span.able-clipped').text(this.tt.pause);
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
this.$playpauseButton.find('img').attr('src',this.pauseButtonImg);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Update seekbar width.
|
|
422
|
+
// To do this, we need to calculate the width of all buttons surrounding it.
|
|
423
|
+
if (this.seekBar) {
|
|
424
|
+
widthUsed = 0;
|
|
425
|
+
seekbarSpacer = 40; // adjust for discrepancies in browsers' calculated button widths
|
|
426
|
+
|
|
427
|
+
leftControls = this.seekBar.wrapperDiv.parent().prev('div.able-left-controls');
|
|
428
|
+
rightControls = leftControls.next('div.able-right-controls');
|
|
429
|
+
leftControls.children().each(function () {
|
|
430
|
+
if ($(this).prop('tagName')=='BUTTON') {
|
|
431
|
+
widthUsed += $(this).width();
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
rightControls.children().each(function () {
|
|
435
|
+
if ($(this).prop('tagName')=='BUTTON') {
|
|
436
|
+
widthUsed += $(this).width();
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
if (this.isFullscreen()) {
|
|
440
|
+
seekbarWidth = $(window).width() - widthUsed - seekbarSpacer;
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
seekbarWidth = this.$ableWrapper.width() - widthUsed - seekbarSpacer;
|
|
444
|
+
}
|
|
445
|
+
// Sometimes some minor fluctuations based on browser weirdness, so set a threshold.
|
|
446
|
+
if (Math.abs(seekbarWidth - this.seekBar.getWidth()) > 5) {
|
|
447
|
+
this.seekBar.setWidth(seekbarWidth);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Show/hide status bar content conditionally
|
|
452
|
+
if (!this.isFullscreen()) {
|
|
453
|
+
statusBarWidthBreakpoint = 300;
|
|
454
|
+
statusBarHeight = this.$statusBarDiv.height();
|
|
455
|
+
speedHeight = this.$statusBarDiv.find('span.able-speed').height();
|
|
456
|
+
if (speedHeight > (statusBarHeight + 5)) {
|
|
457
|
+
// speed bar is wrapping (happens often in German player)
|
|
458
|
+
this.$statusBarDiv.find('span.able-speed').hide();
|
|
459
|
+
this.hidingSpeed = true;
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
if (this.hidingSpeed) {
|
|
463
|
+
this.$statusBarDiv.find('span.able-speed').show();
|
|
464
|
+
this.hidingSpeed = false;
|
|
465
|
+
}
|
|
466
|
+
if (this.$statusBarDiv.width() < statusBarWidthBreakpoint) {
|
|
467
|
+
// Player is too small for a speed span
|
|
468
|
+
this.$statusBarDiv.find('span.able-speed').hide();
|
|
469
|
+
this.hidingSpeed = true;
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
if (this.hidingSpeed) {
|
|
473
|
+
this.$statusBarDiv.find('span.able-speed').show();
|
|
474
|
+
this.hidingSpeed = false;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (this.$descButton) {
|
|
481
|
+
if (this.descOn) {
|
|
482
|
+
this.$descButton.removeClass('buttonOff').attr('aria-label',this.tt.turnOffDescriptions);
|
|
483
|
+
this.$descButton.find('span.able-clipped').text(this.tt.turnOffDescriptions);
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
this.$descButton.addClass('buttonOff').attr('aria-label',this.tt.turnOnDescriptions);
|
|
487
|
+
this.$descButton.find('span.able-clipped').text(this.tt.turnOnDescriptions);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (this.$ccButton) {
|
|
492
|
+
if (this.usingYouTubeCaptions) {
|
|
493
|
+
captionsCount = this.ytCaptions.length;
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
captionsCount = this.captions.length;
|
|
497
|
+
}
|
|
498
|
+
// Button has a different title depending on the number of captions.
|
|
499
|
+
// If only one caption track, this is "Show captions" and "Hide captions"
|
|
500
|
+
// Otherwise, it is just always "Captions"
|
|
501
|
+
if (!this.captionsOn) {
|
|
502
|
+
this.$ccButton.addClass('buttonOff');
|
|
503
|
+
if (captionsCount === 1) {
|
|
504
|
+
this.$ccButton.attr('aria-label',this.tt.showCaptions);
|
|
505
|
+
this.$ccButton.find('span.able-clipped').text(this.tt.showCaptions);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
this.$ccButton.removeClass('buttonOff');
|
|
510
|
+
if (captionsCount === 1) {
|
|
511
|
+
this.$ccButton.attr('aria-label',this.tt.hideCaptions);
|
|
512
|
+
this.$ccButton.find('span.able-clipped').text(this.tt.hideCaptions);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (captionsCount > 1) {
|
|
517
|
+
this.$ccButton.attr({
|
|
518
|
+
'aria-label': this.tt.captions,
|
|
519
|
+
'aria-haspopup': 'true',
|
|
520
|
+
'aria-controls': this.mediaId + '-captions-menu'
|
|
521
|
+
});
|
|
522
|
+
this.$ccButton.find('span.able-clipped').text(this.tt.captions);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (this.$chaptersButton) {
|
|
527
|
+
this.$chaptersButton.attr({
|
|
528
|
+
'aria-label': this.tt.chapters,
|
|
529
|
+
'aria-haspopup': 'true',
|
|
530
|
+
'aria-controls': this.mediaId + '-chapters-menu'
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
if (this.$fullscreenButton) {
|
|
534
|
+
if (!this.isFullscreen()) {
|
|
535
|
+
this.$fullscreenButton.attr('aria-label', this.tt.enterFullScreen);
|
|
536
|
+
if (this.iconType === 'font') {
|
|
537
|
+
this.$fullscreenButton.find('span').first().removeClass('icon-fullscreen-collapse').addClass('icon-fullscreen-expand');
|
|
538
|
+
this.$fullscreenButton.find('span.able-clipped').text(this.tt.enterFullScreen);
|
|
539
|
+
}
|
|
540
|
+
else if (this.iconType === 'svg') {
|
|
541
|
+
// Not currently working. SVG is a work in progress.
|
|
542
|
+
this.$fullscreenButton.find('svg').removeClass('icon-fullscreen-collapse').addClass('icon-fullscreen-expand');
|
|
543
|
+
this.$fullscreenButton.find('span.able-clipped').text(this.tt.enterFullScreen);
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
this.$fullscreenButton.find('img').attr('src',this.fullscreenExpandButtonImg);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
this.$fullscreenButton.attr('aria-label',this.tt.exitFullScreen);
|
|
551
|
+
if (this.iconType === 'font') {
|
|
552
|
+
this.$fullscreenButton.find('span').first().removeClass('icon-fullscreen-expand').addClass('icon-fullscreen-collapse');
|
|
553
|
+
this.$fullscreenButton.find('span.able-clipped').text(this.tt.exitFullScreen);
|
|
554
|
+
}
|
|
555
|
+
else if (this.iconType === 'svg') {
|
|
556
|
+
// Not currently working. SVG is a work in progress.
|
|
557
|
+
this.$fullscreenButton.find('svg').removeClass('icon-fullscreen-expand').addClass('icon-fullscreen-collapse');
|
|
558
|
+
this.$fullscreenButton.find('span.able-clipped').text(this.tt.exitFullScreen);
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
561
|
+
this.$fullscreenButton.find('img').attr('src',this.fullscreenCollapseButtonImg);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (typeof this.$bigPlayButton !== 'undefined') {
|
|
567
|
+
// Choose show/hide for big play button and adjust position.
|
|
568
|
+
if (this.isPaused() && !this.seekBar.tracking) {
|
|
569
|
+
if (!this.hideBigPlayButton) {
|
|
570
|
+
this.$bigPlayButton.show();
|
|
571
|
+
}
|
|
572
|
+
if (this.isFullscreen()) {
|
|
573
|
+
this.$bigPlayButton.width($(window).width());
|
|
574
|
+
this.$bigPlayButton.height($(window).height());
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
this.$bigPlayButton.width(this.$mediaContainer.width());
|
|
578
|
+
this.$bigPlayButton.height(this.$mediaContainer.height());
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
this.$bigPlayButton.hide();
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
if (this.transcriptType) {
|
|
587
|
+
// Sync checkbox and autoScrollTranscript with user preference
|
|
588
|
+
if (this.prefAutoScrollTranscript === 1) {
|
|
589
|
+
this.autoScrollTranscript = true;
|
|
590
|
+
this.$autoScrollTranscriptCheckbox.prop('checked',true);
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
this.autoScrollTranscript = false;
|
|
594
|
+
this.$autoScrollTranscriptCheckbox.prop('checked',false);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// If transcript locked, scroll transcript to current highlight location.
|
|
598
|
+
if (this.autoScrollTranscript && this.currentHighlight) {
|
|
599
|
+
newTop = Math.floor($('.able-transcript').scrollTop() +
|
|
600
|
+
$(this.currentHighlight).position().top -
|
|
601
|
+
($('.able-transcript').height() / 2) +
|
|
602
|
+
($(this.currentHighlight).height() / 2));
|
|
603
|
+
if (newTop !== Math.floor($('.able-transcript').scrollTop())) {
|
|
604
|
+
// Set a flag to ignore the coming scroll event.
|
|
605
|
+
// there's no other way I know of to differentiate programmatic and user-initiated scroll events.
|
|
606
|
+
this.scrollingTranscript = true;
|
|
607
|
+
$('.able-transcript').scrollTop(newTop);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Update buffering progress.
|
|
613
|
+
// TODO: Currently only using the first HTML5 buffered interval, but this fails sometimes when buffering is split into two or more intervals.
|
|
614
|
+
if (this.player === 'html5') {
|
|
615
|
+
if (this.media.buffered.length > 0) {
|
|
616
|
+
buffered = this.media.buffered.end(0)
|
|
617
|
+
if (this.useChapterTimes) {
|
|
618
|
+
if (buffered > this.chapterDuration) {
|
|
619
|
+
buffered = this.chapterDuration;
|
|
620
|
+
}
|
|
621
|
+
if (this.seekBar) {
|
|
622
|
+
this.seekBar.setBuffered(buffered / this.chapterDuration);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
if (this.seekBar) {
|
|
627
|
+
this.seekBar.setBuffered(buffered / duration);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
else if (this.player === 'jw' && this.jwPlayer) {
|
|
633
|
+
if (this.seekBar) {
|
|
634
|
+
this.seekBar.setBuffered(this.jwPlayer.getBuffer() / 100);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
else if (this.player === 'youtube') {
|
|
638
|
+
if (this.seekBar) {
|
|
639
|
+
this.seekBar.setBuffered(this.youTubePlayer.getVideoLoadedFraction());
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
AblePlayer.prototype.getHiddenWidth = function($el) {
|
|
645
|
+
|
|
646
|
+
// jQuery returns for width() if element is hidden
|
|
647
|
+
// this function is a workaround
|
|
648
|
+
|
|
649
|
+
// save a reference to a cloned element that can be measured
|
|
650
|
+
var $hiddenElement = $el.clone().appendTo('body');
|
|
651
|
+
|
|
652
|
+
// calculate the width of the clone
|
|
653
|
+
var width = $hiddenElement.outerWidth();
|
|
654
|
+
|
|
655
|
+
// remove the clone from the DOM
|
|
656
|
+
$hiddenElement.remove();
|
|
657
|
+
|
|
658
|
+
return width;
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
AblePlayer.prototype.handlePlay = function(e) {
|
|
662
|
+
if (this.isPaused()) {
|
|
663
|
+
this.playMedia();
|
|
664
|
+
}
|
|
665
|
+
else {
|
|
666
|
+
this.pauseMedia();
|
|
667
|
+
}
|
|
668
|
+
this.refreshControls();
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
AblePlayer.prototype.handleRestart = function() {
|
|
672
|
+
|
|
673
|
+
this.seekTo(0);
|
|
674
|
+
|
|
675
|
+
/*
|
|
676
|
+
// Prior to 2.3.68, this function was handleStop()
|
|
677
|
+
// which was a bit more challenging to implement
|
|
678
|
+
// Preserved here in case Stop is ever cool again...
|
|
679
|
+
|
|
680
|
+
var thisObj = this;
|
|
681
|
+
if (this.player == 'html5') {
|
|
682
|
+
this.pauseMedia();
|
|
683
|
+
this.seekTo(0);
|
|
684
|
+
}
|
|
685
|
+
else if (this.player === 'jw' && this.jwPlayer) {
|
|
686
|
+
this.jwPlayer.stop();
|
|
687
|
+
}
|
|
688
|
+
else if (this.player === 'youtube') {
|
|
689
|
+
// YouTube API function stopVideo() does not reset video to 0
|
|
690
|
+
// Also, the stopped video is not seekable so seekTo(0) after stopping doesn't work
|
|
691
|
+
// Workaround is to use pauseVideo(), followed by seekTo(0) to emulate stopping
|
|
692
|
+
// However, the tradeoff is that YouTube doesn't restore the poster image when video is paused
|
|
693
|
+
// Added 12/29/15: After seekTo(0) is finished, stopVideo() to reset video and restore poster image
|
|
694
|
+
// This final step is handled in event.js > onMediaUpdate()
|
|
695
|
+
this.youTubePlayer.pauseVideo();
|
|
696
|
+
this.seekTo(0);
|
|
697
|
+
this.stoppingYouTube = true;
|
|
698
|
+
}
|
|
699
|
+
*/
|
|
700
|
+
this.refreshControls();
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
AblePlayer.prototype.handleRewind = function() {
|
|
704
|
+
|
|
705
|
+
var elapsed, targetTime;
|
|
706
|
+
|
|
707
|
+
elapsed = this.getElapsed();
|
|
708
|
+
targetTime = elapsed - this.seekInterval;
|
|
709
|
+
if (this.useChapterTimes) {
|
|
710
|
+
if (targetTime < this.currentChapter.start) {
|
|
711
|
+
targetTime = this.currentChapter.start;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
else {
|
|
715
|
+
if (targetTime < 0) {
|
|
716
|
+
targetTime = 0;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
this.seekTo(targetTime);
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
AblePlayer.prototype.handleFastForward = function() {
|
|
723
|
+
|
|
724
|
+
var elapsed, duration, targetTime, lastChapterIndex;
|
|
725
|
+
|
|
726
|
+
elapsed = this.getElapsed();
|
|
727
|
+
duration = this.getDuration();
|
|
728
|
+
lastChapterIndex = this.chapters.length-1;
|
|
729
|
+
targetTime = elapsed + this.seekInterval;
|
|
730
|
+
|
|
731
|
+
if (this.useChapterTimes) {
|
|
732
|
+
if (this.chapters[lastChapterIndex] == this.currentChapter) {
|
|
733
|
+
// this is the last chapter
|
|
734
|
+
if (targetTime > duration || targetTime > this.currentChapter.end) {
|
|
735
|
+
// targetTime would exceed the end of the video (or chapter)
|
|
736
|
+
// scrub to end of whichever is earliest
|
|
737
|
+
targetTime = Math.min(duration, this.currentChapter.end);
|
|
738
|
+
}
|
|
739
|
+
else if (duration % targetTime < this.seekInterval) {
|
|
740
|
+
// nothing left but pocket change after seeking to targetTime
|
|
741
|
+
// go ahead and seek to end of video (or chapter), whichever is earliest
|
|
742
|
+
targetTime = Math.min(duration, this.currentChapter.end);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
// this is not the last chapter
|
|
747
|
+
if (targetTime > this.currentChapter.end) {
|
|
748
|
+
// targetTime would exceed the end of the chapter
|
|
749
|
+
// scrub exactly to end of chapter
|
|
750
|
+
targetTime = this.currentChapter.end;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
else {
|
|
755
|
+
// not using chapter times
|
|
756
|
+
if (targetTime > duration) {
|
|
757
|
+
targetTime = duration;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
this.seekTo(targetTime);
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
AblePlayer.prototype.handleRateIncrease = function() {
|
|
764
|
+
this.changeRate(1);
|
|
765
|
+
};
|
|
766
|
+
|
|
767
|
+
AblePlayer.prototype.handleRateDecrease = function() {
|
|
768
|
+
this.changeRate(-1);
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
// Increases or decreases playback rate, where dir is 1 or -1 indication direction.
|
|
772
|
+
AblePlayer.prototype.changeRate = function (dir) {
|
|
773
|
+
if (this.player === 'html5') {
|
|
774
|
+
this.setPlaybackRate(this.getPlaybackRate() + (0.25 * dir));
|
|
775
|
+
}
|
|
776
|
+
else if (this.player === 'youtube') {
|
|
777
|
+
var rates = this.youTubePlayer.getAvailablePlaybackRates();
|
|
778
|
+
var currentRate = this.getPlaybackRate();
|
|
779
|
+
var index = rates.indexOf(currentRate);
|
|
780
|
+
if (index === -1) {
|
|
781
|
+
console.log('ERROR: Youtube returning unknown playback rate ' + currentRate.toString());
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
index += dir;
|
|
785
|
+
// Can only increase or decrease rate if there's another rate available.
|
|
786
|
+
if (index < rates.length && index >= 0) {
|
|
787
|
+
this.setPlaybackRate(rates[index]);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
AblePlayer.prototype.handleCaptionToggle = function() {
|
|
794
|
+
|
|
795
|
+
var captions;
|
|
796
|
+
|
|
797
|
+
if (this.hidingPopup) {
|
|
798
|
+
// stopgap to prevent spacebar in Firefox from reopening popup
|
|
799
|
+
// immediately after closing it
|
|
800
|
+
this.hidingPopup = false;
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
if (this.captions.length) {
|
|
804
|
+
captions = this.captions;
|
|
805
|
+
}
|
|
806
|
+
else if (this.ytCaptions.length) {
|
|
807
|
+
captions = this.ytCaptions;
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
captions = [];
|
|
811
|
+
}
|
|
812
|
+
if (captions.length === 1) {
|
|
813
|
+
|
|
814
|
+
// When there's only one set of captions, just do an on/off toggle.
|
|
815
|
+
if (this.captionsOn === true) {
|
|
816
|
+
// turn them off
|
|
817
|
+
this.captionsOn = false;
|
|
818
|
+
this.prefCaptions = 0;
|
|
819
|
+
this.updateCookie('prefCaptions');
|
|
820
|
+
if (this.usingYouTubeCaptions) {
|
|
821
|
+
this.youTubePlayer.unloadModule(this.ytCaptionModule);
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
this.$captionsWrapper.hide();
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
else {
|
|
828
|
+
// captions are off. Turn them on.
|
|
829
|
+
this.captionsOn = true;
|
|
830
|
+
this.prefCaptions = 1;
|
|
831
|
+
this.updateCookie('prefCaptions');
|
|
832
|
+
if (this.usingYouTubeCaptions) {
|
|
833
|
+
if (typeof this.ytCaptionModule !== 'undefined') {
|
|
834
|
+
this.youTubePlayer.loadModule(this.ytCaptionModule);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
this.$captionsWrapper.show();
|
|
839
|
+
}
|
|
840
|
+
for (var i=0; i<captions.length; i++) {
|
|
841
|
+
if (captions[i].def === true) { // this is the default language
|
|
842
|
+
this.selectedCaptions = captions[i];
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
this.selectedCaptions = this.captions[0];
|
|
846
|
+
if (this.descriptions.length >= 0) {
|
|
847
|
+
this.selectedDescriptions = this.descriptions[0];
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
this.refreshControls();
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
|
|
854
|
+
// there is more than one caption track.
|
|
855
|
+
// clicking on a track is handled via caption.js > getCaptionClickFunction()
|
|
856
|
+
if (this.captionsPopup.is(':visible')) {
|
|
857
|
+
this.captionsPopup.hide();
|
|
858
|
+
this.hidingPopup = false;
|
|
859
|
+
this.$ccButton.focus();
|
|
860
|
+
}
|
|
861
|
+
else {
|
|
862
|
+
this.closePopups();
|
|
863
|
+
this.captionsPopup.show();
|
|
864
|
+
this.captionsPopup.css('top', this.$ccButton.position().top - this.captionsPopup.outerHeight());
|
|
865
|
+
this.captionsPopup.css('left', this.$ccButton.position().left)
|
|
866
|
+
// Focus on the checked button, if any buttons are checked
|
|
867
|
+
// Otherwise, focus on the first button
|
|
868
|
+
this.captionsPopup.find('li').removeClass('able-focus');
|
|
869
|
+
if (this.captionsPopup.find('input:checked')) {
|
|
870
|
+
this.captionsPopup.find('input:checked').focus().parent().addClass('able-focus');
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
this.captionsPopup.find('input').first().focus().parent().addClass('able-focus');
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
AblePlayer.prototype.handleChapters = function () {
|
|
880
|
+
if (this.hidingPopup) {
|
|
881
|
+
// stopgap to prevent spacebar in Firefox from reopening popup
|
|
882
|
+
// immediately after closing it
|
|
883
|
+
this.hidingPopup = false;
|
|
884
|
+
return false;
|
|
885
|
+
}
|
|
886
|
+
if (this.chaptersPopup.is(':visible')) {
|
|
887
|
+
this.chaptersPopup.hide();
|
|
888
|
+
this.hidingPopup = false;
|
|
889
|
+
this.$chaptersButton.focus();
|
|
890
|
+
}
|
|
891
|
+
else {
|
|
892
|
+
this.closePopups();
|
|
893
|
+
this.chaptersPopup.show();
|
|
894
|
+
this.chaptersPopup.css('top', this.$chaptersButton.position().top - this.chaptersPopup.outerHeight());
|
|
895
|
+
this.chaptersPopup.css('left', this.$chaptersButton.position().left)
|
|
896
|
+
// Focus on the checked button, if any buttons are checked
|
|
897
|
+
// Otherwise, focus on the first button
|
|
898
|
+
this.chaptersPopup.find('li').removeClass('able-focus');
|
|
899
|
+
if (this.chaptersPopup.find('input:checked')) {
|
|
900
|
+
this.chaptersPopup.find('input:checked').focus().parent().addClass('able-focus');
|
|
901
|
+
}
|
|
902
|
+
else {
|
|
903
|
+
this.chaptersPopup.find('input').first().focus().parent().addClass('able-focus');
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
AblePlayer.prototype.handleDescriptionToggle = function() {
|
|
909
|
+
this.descOn = !this.descOn;
|
|
910
|
+
this.prefDesc = + this.descOn; // convert boolean to integer
|
|
911
|
+
this.updateCookie('prefDesc');
|
|
912
|
+
this.refreshingDesc = true;
|
|
913
|
+
this.initDescription();
|
|
914
|
+
this.refreshControls();
|
|
915
|
+
};
|
|
916
|
+
|
|
917
|
+
AblePlayer.prototype.handlePrefsClick = function(pref) {
|
|
918
|
+
|
|
919
|
+
// NOTE: the prefs menu is positioned near the right edge of the player
|
|
920
|
+
// This assumes the Prefs button is also positioned in that vicinity
|
|
921
|
+
// (last or second-last button the right)
|
|
922
|
+
|
|
923
|
+
var prefsButtonPosition, prefsMenuRight, prefsMenuLeft;
|
|
924
|
+
|
|
925
|
+
if (this.hidingPopup) {
|
|
926
|
+
// stopgap to prevent spacebar in Firefox from reopening popup
|
|
927
|
+
// immediately after closing it
|
|
928
|
+
this.hidingPopup = false;
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
if (this.prefsPopup.is(':visible')) {
|
|
932
|
+
this.prefsPopup.hide();
|
|
933
|
+
this.hidingPopup = false;
|
|
934
|
+
this.$prefsButton.focus();
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
this.closePopups();
|
|
938
|
+
this.prefsPopup.show();
|
|
939
|
+
prefsButtonPosition = this.$prefsButton.position();
|
|
940
|
+
prefsMenuRight = this.$ableDiv.width() - 5;
|
|
941
|
+
prefsMenuLeft = prefsMenuRight - this.prefsPopup.width();
|
|
942
|
+
this.prefsPopup.css('top', prefsButtonPosition.top - this.prefsPopup.outerHeight());
|
|
943
|
+
this.prefsPopup.css('left', prefsMenuLeft);
|
|
944
|
+
// remove prior focus and set focus on first item
|
|
945
|
+
this.prefsPopup.find('li').removeClass('able-focus');
|
|
946
|
+
this.prefsPopup.find('input').first().focus().parent().addClass('able-focus');
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
AblePlayer.prototype.handleHelpClick = function() {
|
|
951
|
+
this.setFullscreen(false);
|
|
952
|
+
this.helpDialog.show();
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
AblePlayer.prototype.handleTranscriptToggle = function () {
|
|
956
|
+
if (this.$transcriptDiv.is(':visible')) {
|
|
957
|
+
this.$transcriptArea.hide();
|
|
958
|
+
this.$transcriptButton.addClass('buttonOff').attr('aria-label',this.tt.showTranscript);
|
|
959
|
+
this.$transcriptButton.find('span.able-clipped').text(this.tt.showTranscript);
|
|
960
|
+
this.prefTranscript = 0;
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
this.positionDraggableWindow('transcript');
|
|
964
|
+
this.$transcriptArea.show();
|
|
965
|
+
this.$transcriptButton.removeClass('buttonOff').attr('aria-label',this.tt.hideTranscript);
|
|
966
|
+
this.$transcriptButton.find('span.able-clipped').text(this.tt.hideTranscript);
|
|
967
|
+
this.prefTranscript = 1;
|
|
968
|
+
}
|
|
969
|
+
this.updateCookie('prefTranscript');
|
|
970
|
+
};
|
|
971
|
+
|
|
972
|
+
AblePlayer.prototype.handleSignToggle = function () {
|
|
973
|
+
if (this.$signWindow.is(':visible')) {
|
|
974
|
+
this.$signWindow.hide();
|
|
975
|
+
this.$signButton.addClass('buttonOff').attr('aria-label',this.tt.showSign);
|
|
976
|
+
this.$signButton.find('span.able-clipped').text(this.tt.showSign);
|
|
977
|
+
this.prefSign = 0;
|
|
978
|
+
}
|
|
979
|
+
else {
|
|
980
|
+
this.positionDraggableWindow('sign');
|
|
981
|
+
this.$signWindow.show();
|
|
982
|
+
this.$signButton.removeClass('buttonOff').attr('aria-label',this.tt.hideSign);
|
|
983
|
+
this.$signButton.find('span.able-clipped').text(this.tt.hideSign);
|
|
984
|
+
this.prefSign = 1;
|
|
985
|
+
}
|
|
986
|
+
this.updateCookie('prefSign');
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
AblePlayer.prototype.isFullscreen = function () {
|
|
990
|
+
if (this.nativeFullscreenSupported()) {
|
|
991
|
+
return (document.fullscreenElement ||
|
|
992
|
+
document.webkitFullscreenElement ||
|
|
993
|
+
document.webkitCurrentFullScreenElement ||
|
|
994
|
+
document.mozFullScreenElement ||
|
|
995
|
+
document.msFullscreenElement) ? true : false;
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
998
|
+
return this.modalFullscreenActive ? true : false;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
AblePlayer.prototype.setFullscreen = function (fullscreen) {
|
|
1003
|
+
if (this.isFullscreen() == fullscreen) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
var thisObj = this;
|
|
1007
|
+
var $el = this.$ableWrapper;
|
|
1008
|
+
var el = $el[0];
|
|
1009
|
+
|
|
1010
|
+
if (this.nativeFullscreenSupported()) {
|
|
1011
|
+
// Note: many varying names for options for browser compatibility.
|
|
1012
|
+
if (fullscreen) {
|
|
1013
|
+
// Initialize fullscreen
|
|
1014
|
+
|
|
1015
|
+
// But first, capture current settings so they can be restored later
|
|
1016
|
+
this.preFullScreenWidth = this.$ableWrapper.width();
|
|
1017
|
+
this.preFullScreenHeight = this.$ableWrapper.height();
|
|
1018
|
+
|
|
1019
|
+
if (el.requestFullscreen) {
|
|
1020
|
+
el.requestFullscreen();
|
|
1021
|
+
}
|
|
1022
|
+
else if (el.webkitRequestFullscreen) {
|
|
1023
|
+
el.webkitRequestFullscreen();
|
|
1024
|
+
}
|
|
1025
|
+
else if (el.mozRequestFullScreen) {
|
|
1026
|
+
el.mozRequestFullScreen();
|
|
1027
|
+
}
|
|
1028
|
+
else if (el.msRequestFullscreen) {
|
|
1029
|
+
el.msRequestFullscreen();
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
else {
|
|
1033
|
+
// Exit fullscreen
|
|
1034
|
+
if (document.exitFullscreen) {
|
|
1035
|
+
document.exitFullscreen();
|
|
1036
|
+
}
|
|
1037
|
+
else if (document.webkitExitFullscreen) {
|
|
1038
|
+
document.webkitExitFullscreen();
|
|
1039
|
+
}
|
|
1040
|
+
else if (document.webkitCancelFullScreen) {
|
|
1041
|
+
document.webkitCancelFullScreen();
|
|
1042
|
+
}
|
|
1043
|
+
else if (document.mozCancelFullScreen) {
|
|
1044
|
+
document.mozCancelFullScreen();
|
|
1045
|
+
}
|
|
1046
|
+
else if (document.msExitFullscreen) {
|
|
1047
|
+
document.msExitFullscreen();
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
// add event handlers for changes in full screen mode
|
|
1051
|
+
// currently most changes are made in response to windowResize event
|
|
1052
|
+
// However, that alone is not resulting in a properly restored player size in Opera Mac
|
|
1053
|
+
// More on the Opera Mac bug: https://github.com/ableplayer/ableplayer/issues/162
|
|
1054
|
+
// this fullscreen event handler added specifically for Opera Mac,
|
|
1055
|
+
// but includes event listeners for all browsers in case its functionality could be expanded
|
|
1056
|
+
// Added functionality in 2.3.45 for handling YouTube return from fullscreen as well
|
|
1057
|
+
$(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange', function() {
|
|
1058
|
+
if (!thisObj.isFullscreen()) {
|
|
1059
|
+
// user has just exited full screen
|
|
1060
|
+
thisObj.restoringAfterFullScreen = true;
|
|
1061
|
+
thisObj.resizePlayer(thisObj.preFullScreenWidth,thisObj.preFullScreenHeight);
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
else {
|
|
1066
|
+
// Non-native fullscreen support through modal dialog.
|
|
1067
|
+
// Create dialog on first run through.
|
|
1068
|
+
if (!this.fullscreenDialog) {
|
|
1069
|
+
var $dialogDiv = $('<div>');
|
|
1070
|
+
// create a hidden alert, communicated to screen readers via aria-describedby
|
|
1071
|
+
var $fsDialogAlert = $('<p>',{
|
|
1072
|
+
'class': 'able-screenreader-alert'
|
|
1073
|
+
}).text(this.tt.fullscreen); // In English: "Full screen"; TODO: Add alert text that is more descriptive
|
|
1074
|
+
$dialogDiv.append($fsDialogAlert);
|
|
1075
|
+
// now render this as a dialog
|
|
1076
|
+
this.fullscreenDialog = new AccessibleDialog($dialogDiv, this.$fullscreenButton, 'dialog', 'Fullscreen video player', $fsDialogAlert, this.tt.exitFullScreen, '100%', true, function () { thisObj.handleFullscreenToggle() });
|
|
1077
|
+
$('body').append($dialogDiv);
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// Track whether paused/playing before moving element; moving the element can stop playback.
|
|
1081
|
+
var wasPaused = this.isPaused();
|
|
1082
|
+
|
|
1083
|
+
if (fullscreen) {
|
|
1084
|
+
this.modalFullscreenActive = true;
|
|
1085
|
+
this.fullscreenDialog.show();
|
|
1086
|
+
|
|
1087
|
+
// Move player element into fullscreen dialog, then show.
|
|
1088
|
+
// Put a placeholder element where player was.
|
|
1089
|
+
this.$modalFullscreenPlaceholder = $('<div class="placeholder">');
|
|
1090
|
+
this.$modalFullscreenPlaceholder.insertAfter($el);
|
|
1091
|
+
$el.appendTo(this.fullscreenDialog.modal);
|
|
1092
|
+
|
|
1093
|
+
// Column left css is 50% by default; set to 100% for full screen.
|
|
1094
|
+
if ($el === this.$ableColumnLeft) {
|
|
1095
|
+
$el.width('100%');
|
|
1096
|
+
}
|
|
1097
|
+
var newHeight = $(window).height() - this.$playerDiv.height();
|
|
1098
|
+
if (!this.$descDiv.is(':hidden')) {
|
|
1099
|
+
newHeight -= this.$descDiv.height();
|
|
1100
|
+
}
|
|
1101
|
+
this.resizePlayer($(window).width(), newHeight);
|
|
1102
|
+
}
|
|
1103
|
+
else {
|
|
1104
|
+
this.modalFullscreenActive = false;
|
|
1105
|
+
if ($el === this.$ableColumnLeft) {
|
|
1106
|
+
$el.width('50%');
|
|
1107
|
+
}
|
|
1108
|
+
$el.insertAfter(this.$modalFullscreenPlaceholder);
|
|
1109
|
+
this.$modalFullscreenPlaceholder.remove();
|
|
1110
|
+
this.fullscreenDialog.hide();
|
|
1111
|
+
this.resizePlayer(this.$ableWrapper.width(), this.$ableWrapper.height());
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
// TODO: JW Player freezes after being moved on iPads (instead of being reset as in most browsers)
|
|
1115
|
+
// Need to call setup again after moving?
|
|
1116
|
+
|
|
1117
|
+
// Resume playback if moving stopped it.
|
|
1118
|
+
if (!wasPaused && this.isPaused()) {
|
|
1119
|
+
this.playMedia();
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
this.refreshControls();
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
AblePlayer.prototype.handleFullscreenToggle = function () {
|
|
1126
|
+
var stillPaused = this.isPaused(); //add boolean variable reading return from isPaused function
|
|
1127
|
+
this.setFullscreen(!this.isFullscreen());
|
|
1128
|
+
if (stillPaused) {
|
|
1129
|
+
this.pauseMedia(); // when toggling fullscreen and media is just paused, keep media paused.
|
|
1130
|
+
}
|
|
1131
|
+
else if (!stillPaused) {
|
|
1132
|
+
this.playMedia(); // when toggling fullscreen and media is playing, continue playing.
|
|
1133
|
+
}
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
AblePlayer.prototype.handleTranscriptLockToggle = function (val) {
|
|
1137
|
+
|
|
1138
|
+
this.autoScrollTranscript = val; // val is boolean
|
|
1139
|
+
this.prefAutoScrollTranscript = +val; // convert boolean to numeric 1 or 0 for cookie
|
|
1140
|
+
this.updateCookie('prefAutoScrollTranscript');
|
|
1141
|
+
this.refreshControls();
|
|
1142
|
+
};
|
|
1143
|
+
|
|
1144
|
+
|
|
1145
|
+
AblePlayer.prototype.showTooltip = function($tooltip) {
|
|
1146
|
+
|
|
1147
|
+
if (($tooltip).is(':animated')) {
|
|
1148
|
+
$tooltip.stop(true,true).show().delay(4000).fadeOut(1000);
|
|
1149
|
+
}
|
|
1150
|
+
else {
|
|
1151
|
+
$tooltip.stop().show().delay(4000).fadeOut(1000);
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
|
|
1155
|
+
AblePlayer.prototype.showAlert = function( msg, location ) {
|
|
1156
|
+
|
|
1157
|
+
// location is either of the following:
|
|
1158
|
+
// 'main' (default)
|
|
1159
|
+
// 'screenreader
|
|
1160
|
+
// 'sign' (sign language window)
|
|
1161
|
+
// 'transcript' (trasncript window)
|
|
1162
|
+
var thisObj, $alertBox, $parentWindow, alertLeft, alertTop;
|
|
1163
|
+
|
|
1164
|
+
thisObj = this;
|
|
1165
|
+
|
|
1166
|
+
if (location === 'transcript') {
|
|
1167
|
+
$alertBox = this.$transcriptAlert;
|
|
1168
|
+
$parentWindow = this.$transcriptArea;
|
|
1169
|
+
}
|
|
1170
|
+
else if (location === 'sign') {
|
|
1171
|
+
$alertBox = this.$signAlert;
|
|
1172
|
+
$parentWindow = this.$signWindow;
|
|
1173
|
+
}
|
|
1174
|
+
else if (location === 'screenreader') {
|
|
1175
|
+
$alertBox = this.$srAlertBox;
|
|
1176
|
+
}
|
|
1177
|
+
else {
|
|
1178
|
+
$alertBox = this.$alertBox;
|
|
1179
|
+
}
|
|
1180
|
+
$alertBox.show();
|
|
1181
|
+
$alertBox.text(msg);
|
|
1182
|
+
if (location == 'transcript' || location === 'sign') {
|
|
1183
|
+
if ($parentWindow.width() > $alertBox.width()) {
|
|
1184
|
+
alertLeft = $parentWindow.width() / 2 - $alertBox.width() / 2;
|
|
1185
|
+
}
|
|
1186
|
+
else {
|
|
1187
|
+
// alert box is wider than its container. Position it far left and let it wrap
|
|
1188
|
+
alertLeft = 10;
|
|
1189
|
+
}
|
|
1190
|
+
if (location === 'sign') {
|
|
1191
|
+
// position alert in the lower third of the sign window (to avoid covering the signer)
|
|
1192
|
+
alertTop = ($parentWindow.height() / 3) * 2;
|
|
1193
|
+
}
|
|
1194
|
+
else if (location === 'transcript') {
|
|
1195
|
+
// position alert just beneath the toolbar to avoid getting lost among transcript text
|
|
1196
|
+
alertTop = this.$transcriptToolbar.height() + 30;
|
|
1197
|
+
}
|
|
1198
|
+
$alertBox.css({
|
|
1199
|
+
top: alertTop + 'px',
|
|
1200
|
+
left: alertLeft + 'px'
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
else if (location !== 'screenreader') {
|
|
1204
|
+
// The original formula incorporated offset() into the calculation
|
|
1205
|
+
// but at some point this began resulting in an alert that's off-centered
|
|
1206
|
+
// Changed in v2.2.17, but here's the original for reference in case needed:
|
|
1207
|
+
// left: this.$playerDiv.offset().left + (this.$playerDiv.width() / 2) - ($alertBox.width() / 2)
|
|
1208
|
+
$alertBox.css({
|
|
1209
|
+
left: (this.$playerDiv.width() / 2) - ($alertBox.width() / 2)
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
if (location !== 'screenreader') {
|
|
1213
|
+
setTimeout(function () {
|
|
1214
|
+
$alertBox.fadeOut(300);
|
|
1215
|
+
}, 3000);
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
|
|
1219
|
+
AblePlayer.prototype.showedAlert = function (which) {
|
|
1220
|
+
|
|
1221
|
+
// returns true if the target alert has already been shown
|
|
1222
|
+
// useful for throttling alerts that only need to be shown once
|
|
1223
|
+
// e.g., move alerts with instructions for dragging a window
|
|
1224
|
+
|
|
1225
|
+
if (which === 'transcript') {
|
|
1226
|
+
if (this.showedTranscriptAlert) {
|
|
1227
|
+
return true;
|
|
1228
|
+
}
|
|
1229
|
+
else {
|
|
1230
|
+
return false;
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
else if (which === 'sign') {
|
|
1234
|
+
if (this.showedSignAlert) {
|
|
1235
|
+
return true;
|
|
1236
|
+
}
|
|
1237
|
+
else {
|
|
1238
|
+
return false;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
return false;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// Resizes all relevant player attributes.
|
|
1245
|
+
AblePlayer.prototype.resizePlayer = function (width, height) {
|
|
1246
|
+
|
|
1247
|
+
var jwHeight, captionSizeOkMin, captionSizeOkMax, captionSize, newCaptionSize, newLineHeight;
|
|
1248
|
+
|
|
1249
|
+
if (this.isFullscreen()) {
|
|
1250
|
+
if (typeof this.$vidcapContainer !== 'undefined') {
|
|
1251
|
+
this.$ableWrapper.css({
|
|
1252
|
+
'width': width + 'px',
|
|
1253
|
+
'max-width': ''
|
|
1254
|
+
})
|
|
1255
|
+
this.$vidcapContainer.css({
|
|
1256
|
+
'height': height + 'px',
|
|
1257
|
+
'width': width
|
|
1258
|
+
});
|
|
1259
|
+
this.$media.css({
|
|
1260
|
+
'height': height + 'px',
|
|
1261
|
+
'width': width
|
|
1262
|
+
})
|
|
1263
|
+
}
|
|
1264
|
+
if (typeof this.$transcriptArea !== 'undefined') {
|
|
1265
|
+
this.retrieveOffscreenWindow('transcript',width,height);
|
|
1266
|
+
}
|
|
1267
|
+
if (typeof this.$signWindow !== 'undefined') {
|
|
1268
|
+
this.retrieveOffscreenWindow('sign',width,height);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
else {
|
|
1272
|
+
// player resized
|
|
1273
|
+
if (this.restoringAfterFullScreen) {
|
|
1274
|
+
// User has just exited fullscreen mode. Restore to previous settings
|
|
1275
|
+
width = this.preFullScreenWidth;
|
|
1276
|
+
height = this.preFullScreenHeight;
|
|
1277
|
+
this.restoringAfterFullScreen = false;
|
|
1278
|
+
this.$ableWrapper.css({
|
|
1279
|
+
'max-width': width + 'px',
|
|
1280
|
+
'width': ''
|
|
1281
|
+
});
|
|
1282
|
+
if (typeof this.$vidcapContainer !== 'undefined') {
|
|
1283
|
+
this.$vidcapContainer.css({
|
|
1284
|
+
'height': '',
|
|
1285
|
+
'width': ''
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
this.$media.css({
|
|
1289
|
+
'width': '100%',
|
|
1290
|
+
'height': 'auto'
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// resize YouTube or JW Player
|
|
1296
|
+
if (this.player === 'youtube' && this.youTubePlayer) {
|
|
1297
|
+
this.youTubePlayer.setSize(width, height);
|
|
1298
|
+
}
|
|
1299
|
+
else if (this.player === 'jw' && this.jwPlayer) {
|
|
1300
|
+
if (this.mediaType === 'audio') {
|
|
1301
|
+
// keep height set to 0 to prevent JW PLayer from showing its own player
|
|
1302
|
+
this.jwPlayer.resize(width,0);
|
|
1303
|
+
}
|
|
1304
|
+
else {
|
|
1305
|
+
this.jwPlayer.resize(width, jwHeight);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// Resize captions
|
|
1310
|
+
if (typeof this.$captionsDiv !== 'undefined') {
|
|
1311
|
+
|
|
1312
|
+
// Font-size is too small in full screen view & too large in small-width view
|
|
1313
|
+
// The following vars define a somewhat arbitary zone outside of which
|
|
1314
|
+
// caption size requires adjustment
|
|
1315
|
+
captionSizeOkMin = 400;
|
|
1316
|
+
captionSizeOkMax = 1000;
|
|
1317
|
+
captionSize = parseInt(this.prefCaptionsSize,10);
|
|
1318
|
+
|
|
1319
|
+
// TODO: Need a better formula so that it scales proportionally to viewport
|
|
1320
|
+
if (width > captionSizeOkMax) {
|
|
1321
|
+
newCaptionSize = captionSize * 1.5;
|
|
1322
|
+
}
|
|
1323
|
+
else if (width < captionSizeOkMin) {
|
|
1324
|
+
newCaptionSize = captionSize / 1.5;
|
|
1325
|
+
}
|
|
1326
|
+
else {
|
|
1327
|
+
newCaptionSize = captionSize;
|
|
1328
|
+
}
|
|
1329
|
+
newLineHeight = newCaptionSize + 25;
|
|
1330
|
+
this.$captionsDiv.css('font-size',newCaptionSize + '%');
|
|
1331
|
+
this.$captionsWrapper.css('line-height',newLineHeight + '%');
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
this.refreshControls();
|
|
1335
|
+
};
|
|
1336
|
+
|
|
1337
|
+
AblePlayer.prototype.retrieveOffscreenWindow = function( which, width, height ) {
|
|
1338
|
+
|
|
1339
|
+
// check to be sure popup windows ('transcript' or 'sign') are positioned on-screen
|
|
1340
|
+
// (they sometimes disappear off-screen when entering fullscreen mode)
|
|
1341
|
+
// if off-screen, recalculate so they are back on screen
|
|
1342
|
+
|
|
1343
|
+
var window, windowPos, windowTop, windowLeft, windowRight, windowWidth, windowBottom, windowHeight;
|
|
1344
|
+
|
|
1345
|
+
if (which == 'transcript') {
|
|
1346
|
+
window = this.$transcriptArea;
|
|
1347
|
+
}
|
|
1348
|
+
else if (which == 'sign') {
|
|
1349
|
+
window = this.$signWindow;
|
|
1350
|
+
}
|
|
1351
|
+
windowWidth = window.width();
|
|
1352
|
+
windowHeight = window.height();
|
|
1353
|
+
windowPos = window.position();
|
|
1354
|
+
windowTop = windowPos.top;
|
|
1355
|
+
windowLeft = windowPos.left;
|
|
1356
|
+
windowRight = windowLeft + windowWidth;
|
|
1357
|
+
windowBottom = windowTop + windowHeight;
|
|
1358
|
+
|
|
1359
|
+
if (windowTop < 0) { // off-screen to the top
|
|
1360
|
+
windowTop = 10;
|
|
1361
|
+
window.css('top',windowTop);
|
|
1362
|
+
}
|
|
1363
|
+
if (windowLeft < 0) { // off-screen to the left
|
|
1364
|
+
windowLeft = 10;
|
|
1365
|
+
window.css('left',windowLeft);
|
|
1366
|
+
}
|
|
1367
|
+
if (windowRight > width) { // off-screen to the right
|
|
1368
|
+
windowLeft = (width - 20) - windowWidth;
|
|
1369
|
+
window.css('left',windowLeft);
|
|
1370
|
+
}
|
|
1371
|
+
if (windowBottom > height) { // off-screen to the bottom
|
|
1372
|
+
windowTop = (height - 10) - windowHeight;
|
|
1373
|
+
window.css('top',windowTop);
|
|
1374
|
+
}
|
|
1375
|
+
};
|
|
1376
|
+
|
|
1377
|
+
AblePlayer.prototype.getHighestZIndex = function() {
|
|
1378
|
+
|
|
1379
|
+
// returns the highest z-index on page
|
|
1380
|
+
// used to ensure dialogs (or potentially other windows) are on top
|
|
1381
|
+
|
|
1382
|
+
var max, $elements, z;
|
|
1383
|
+
max = 0;
|
|
1384
|
+
|
|
1385
|
+
// exclude the Able Player dialogs
|
|
1386
|
+
$elements = $('body *').not('.able-modal-dialog,.able-modal-dialog *,.able-modal-overlay,.able-modal-overlay *');
|
|
1387
|
+
|
|
1388
|
+
$elements.each(function(){
|
|
1389
|
+
z = $(this).css('z-index');
|
|
1390
|
+
if (Number.isInteger(+z)) { // work only with integer values, not 'auto'
|
|
1391
|
+
if (parseInt(z) > max) {
|
|
1392
|
+
max = parseInt(z);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1396
|
+
return max;
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1399
|
+
AblePlayer.prototype.updateZIndex = function(which) {
|
|
1400
|
+
|
|
1401
|
+
// update z-index of 'transcript' or 'sign', relative to each other
|
|
1402
|
+
// direction is always 'up' (i.e., move window to top)
|
|
1403
|
+
// windows come to the top when the user clicks on them
|
|
1404
|
+
|
|
1405
|
+
var transcriptZ, signZ, newHighZ, newLowZ;
|
|
1406
|
+
|
|
1407
|
+
if (typeof this.$transcriptArea === 'undefined' || typeof this.$signWindow === 'undefined' ) {
|
|
1408
|
+
// at least one of the windows doesn't exist, so there's no conflict
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// get current values
|
|
1413
|
+
transcriptZ = parseInt(this.$transcriptArea.css('z-index'));
|
|
1414
|
+
signZ = parseInt(this.$signWindow.css('z-index'));
|
|
1415
|
+
|
|
1416
|
+
if (transcriptZ === signZ) {
|
|
1417
|
+
// the two windows are equal; move the target window the top
|
|
1418
|
+
newHighZ = transcriptZ + 1000;
|
|
1419
|
+
newLowZ = transcriptZ;
|
|
1420
|
+
}
|
|
1421
|
+
else if (transcriptZ > signZ) {
|
|
1422
|
+
if (which === 'transcript') {
|
|
1423
|
+
// transcript is already on top; nothing to do
|
|
1424
|
+
return false;
|
|
1425
|
+
}
|
|
1426
|
+
else {
|
|
1427
|
+
// swap z's
|
|
1428
|
+
newHighZ = transcriptZ;
|
|
1429
|
+
newLowZ = signZ;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
else { // signZ is greater
|
|
1433
|
+
if (which === 'sign') {
|
|
1434
|
+
return false;
|
|
1435
|
+
}
|
|
1436
|
+
else {
|
|
1437
|
+
newHighZ = signZ;
|
|
1438
|
+
newLowZ = transcriptZ;
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// now assign the new values
|
|
1443
|
+
if (which === 'transcript') {
|
|
1444
|
+
this.$transcriptArea.css('z-index',newHighZ);
|
|
1445
|
+
this.$signWindow.css('z-index',newLowZ);
|
|
1446
|
+
}
|
|
1447
|
+
else if (which === 'sign') {
|
|
1448
|
+
this.$signWindow.css('z-index',newHighZ);
|
|
1449
|
+
this.$transcriptArea.css('z-index',newLowZ);
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
|
|
1453
|
+
AblePlayer.prototype.syncTrackLanguages = function (source, language) {
|
|
1454
|
+
|
|
1455
|
+
// this function is called when the player is built (source == 'init')
|
|
1456
|
+
// and again when user changes the language of either 'captions' or 'transcript'
|
|
1457
|
+
// It syncs the languages of chapters, descriptions, and metadata tracks
|
|
1458
|
+
// NOTE: Caption and transcript languages are somewhat independent from one another
|
|
1459
|
+
// If a user changes the caption language, the transcript follows
|
|
1460
|
+
// However, if a user changes the transcript language, this only affects the transcript
|
|
1461
|
+
// This was a group decision based on the belief that users may want a transcript
|
|
1462
|
+
// that is in a different language than the captions
|
|
1463
|
+
|
|
1464
|
+
var i, captions, descriptions, chapters, meta;
|
|
1465
|
+
|
|
1466
|
+
// Captions
|
|
1467
|
+
for (i = 0; i < this.captions.length; i++) {
|
|
1468
|
+
if (this.captions[i].language === language) {
|
|
1469
|
+
captions = this.captions[i];
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
// Chapters
|
|
1473
|
+
for (i = 0; i < this.chapters.length; i++) {
|
|
1474
|
+
if (this.chapters[i].language === language) {
|
|
1475
|
+
chapters = this.chapters[i];
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
// Descriptions
|
|
1479
|
+
for (i = 0; i < this.descriptions.length; i++) {
|
|
1480
|
+
if (this.descriptions[i].language === language) {
|
|
1481
|
+
descriptions = this.descriptions[i];
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
// Metadata
|
|
1485
|
+
for (i = 0; i < this.meta.length; i++) {
|
|
1486
|
+
if (this.meta[i].language === language) {
|
|
1487
|
+
meta = this.meta[i];
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
// regardless of source...
|
|
1492
|
+
this.transcriptLang = language;
|
|
1493
|
+
|
|
1494
|
+
if (source === 'init' || source === 'captions') {
|
|
1495
|
+
this.captionLang = language;
|
|
1496
|
+
this.selectedCaptions = captions;
|
|
1497
|
+
this.selectedChapters = chapters;
|
|
1498
|
+
this.selectedDescriptions = descriptions;
|
|
1499
|
+
this.selectedMeta = meta;
|
|
1500
|
+
this.transcriptCaptions = captions;
|
|
1501
|
+
this.transcriptChapters = chapters;
|
|
1502
|
+
this.transcriptDescriptions = descriptions;
|
|
1503
|
+
this.updateChaptersList();
|
|
1504
|
+
this.setupPopups('chapters');
|
|
1505
|
+
}
|
|
1506
|
+
else if (source === 'transcript') {
|
|
1507
|
+
this.transcriptCaptions = captions;
|
|
1508
|
+
this.transcriptChapters = chapters;
|
|
1509
|
+
this.transcriptDescriptions = descriptions;
|
|
1510
|
+
}
|
|
1511
|
+
this.updateTranscript();
|
|
1512
|
+
};
|
|
1513
|
+
|
|
1514
|
+
})(jQuery);
|