wai-website-theme 1.3.1 → 1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/_includes/different.html +2 -1
  3. data/_includes/external.html +2 -1
  4. data/_includes/header.html +2 -1
  5. data/_includes/menuitem.html +6 -2
  6. data/_includes/peoplelist.html +21 -0
  7. data/_includes/prevnext-navigation.html +56 -0
  8. data/_includes/{prevnext.html → prevnext-order.html} +9 -0
  9. data/_includes/translation-note-msg.html +5 -3
  10. data/_includes/video-player.html +2 -2
  11. data/_layouts/default.html +8 -1
  12. data/_layouts/news.html +7 -1
  13. data/_layouts/policy.html +7 -1
  14. data/_layouts/sidenav.html +8 -1
  15. data/_layouts/sidenavsidebar.html +8 -1
  16. data/assets/ableplayer/Gruntfile.js +2 -1
  17. data/assets/ableplayer/README.md +158 -85
  18. data/assets/ableplayer/build/ableplayer.dist.js +15445 -13823
  19. data/assets/ableplayer/build/ableplayer.js +15445 -13823
  20. data/assets/ableplayer/build/ableplayer.min.css +1 -2
  21. data/assets/ableplayer/build/ableplayer.min.js +3 -10
  22. data/assets/ableplayer/package-lock.json +944 -346
  23. data/assets/ableplayer/package.json +8 -8
  24. data/assets/ableplayer/scripts/ableplayer-base.js +515 -524
  25. data/assets/ableplayer/scripts/browser.js +158 -158
  26. data/assets/ableplayer/scripts/buildplayer.js +1750 -1682
  27. data/assets/ableplayer/scripts/caption.js +424 -401
  28. data/assets/ableplayer/scripts/chapters.js +259 -259
  29. data/assets/ableplayer/scripts/control.js +1831 -1594
  30. data/assets/ableplayer/scripts/description.js +333 -256
  31. data/assets/ableplayer/scripts/dialog.js +145 -145
  32. data/assets/ableplayer/scripts/dragdrop.js +746 -749
  33. data/assets/ableplayer/scripts/event.js +875 -696
  34. data/assets/ableplayer/scripts/initialize.js +819 -912
  35. data/assets/ableplayer/scripts/langs.js +979 -743
  36. data/assets/ableplayer/scripts/metadata.js +124 -124
  37. data/assets/ableplayer/scripts/misc.js +170 -137
  38. data/assets/ableplayer/scripts/preference.js +904 -904
  39. data/assets/ableplayer/scripts/search.js +172 -172
  40. data/assets/ableplayer/scripts/sign.js +82 -78
  41. data/assets/ableplayer/scripts/slider.js +449 -448
  42. data/assets/ableplayer/scripts/track.js +409 -309
  43. data/assets/ableplayer/scripts/transcript.js +684 -595
  44. data/assets/ableplayer/scripts/translation.js +63 -67
  45. data/assets/ableplayer/scripts/ttml2webvtt.js +85 -85
  46. data/assets/ableplayer/scripts/vimeo.js +448 -0
  47. data/assets/ableplayer/scripts/volume.js +395 -380
  48. data/assets/ableplayer/scripts/vts.js +1077 -1077
  49. data/assets/ableplayer/scripts/webvtt.js +766 -763
  50. data/assets/ableplayer/scripts/youtube.js +695 -478
  51. data/assets/ableplayer/styles/ableplayer.css +54 -46
  52. data/assets/ableplayer/translations/nl.js +54 -54
  53. data/assets/ableplayer/translations/pt-br.js +311 -0
  54. data/assets/ableplayer/translations/tr.js +311 -0
  55. data/assets/ableplayer/translations/zh-tw.js +1 -1
  56. data/assets/css/style.css +1 -1
  57. data/assets/css/style.css.map +1 -1
  58. data/assets/images/icons.svg +5 -5
  59. data/assets/scripts/main.js +7 -0
  60. data/assets/search/tipuesearch.js +3 -3
  61. metadata +8 -3
@@ -1,75 +1,71 @@
1
1
  (function ($) {
2
- AblePlayer.prototype.getSupportedLangs = function() {
3
- // returns an array of languages for which AblePlayer has translation tables
4
- // Removing 'nl' as of 2.3.54, pending updates
5
- var langs = ['ca','de','en','es','fr','he','it','ja','nb','zh-tw'];
6
- return langs;
7
- };
2
+ AblePlayer.prototype.getSupportedLangs = function() {
3
+ // returns an array of languages for which AblePlayer has translation tables
4
+ var langs = ['ca','de','en','es','fr','he','it','ja','nb','nl','pt-br','tr','zh-tw'];
5
+ return langs;
6
+ };
8
7
 
9
- AblePlayer.prototype.getTranslationText = function() {
10
- // determine language, then get labels and prompts from corresponding translation var
11
- var deferred, thisObj, lang, thisObj, msg, translationFile;
12
- deferred = $.Deferred();
8
+ AblePlayer.prototype.getTranslationText = function() {
9
+ // determine language, then get labels and prompts from corresponding translation var
10
+ var deferred, thisObj, lang, thisObj, msg, translationFile, collapsedLang;
11
+ deferred = $.Deferred();
13
12
 
14
- thisObj = this;
13
+ thisObj = this;
14
+ // get language of the web page, if specified
15
+ if ($('body').attr('lang')) {
16
+ lang = $('body').attr('lang');
17
+ }
18
+ else if ($('html').attr('lang')) {
19
+ lang = $('html').attr('lang');
20
+ }
21
+ else {
22
+ lang = null;
23
+ }
15
24
 
16
- // get language of the web page, if specified
17
- if ($('body').attr('lang')) {
18
- lang = $('body').attr('lang');
19
- }
20
- else if ($('html').attr('lang')) {
21
- lang = $('html').attr('lang');
22
- }
23
- else {
24
- lang = null;
25
- }
25
+ // override this.lang to language of the web page, if known and supported
26
+ // otherwise this.lang will continue using default
27
+ if (!this.forceLang) {
28
+ if (lang) {
29
+ if (lang !== this.lang) {
30
+ if ($.inArray(lang,this.getSupportedLangs()) !== -1) {
31
+ // this is a supported lang
32
+ this.lang = lang;
33
+ }
34
+ else {
35
+ msg = lang + ' is not currently supported. Using default language (' + this.lang + ')';
36
+ if (this.debug) {
37
+ console.log(msg);
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ if (!this.searchLang) {
44
+ this.searchLang = this.lang;
45
+ }
46
+ translationFile = this.rootPath + 'translations/' + this.lang + '.js';
47
+ this.importTranslationFile(translationFile).then(function(result) {
48
+ collapsedLang = thisObj.lang.replace('-','');
49
+ thisObj.tt = eval(collapsedLang);
50
+ deferred.resolve();
51
+ });
52
+ return deferred.promise();
53
+ };
26
54
 
27
- // override this.lang to language of the web page, if known and supported
28
- // otherwise this.lang will continue using default
29
- if (!this.forceLang) {
30
- if (lang) {
31
- if (lang !== this.lang) {
32
- msg = 'Language of web page (' + lang +') ';
33
- if ($.inArray(lang,this.getSupportedLangs()) !== -1) {
34
- // this is a supported lang
35
- msg += ' has a translation table available.';
36
- this.lang = lang;
37
- }
38
- else {
39
- msg += ' is not currently supported. Using default language (' + this.lang + ')';
40
- }
41
- if (this.debug) {
42
- console.log(msg);
43
- }
44
- }
45
- }
46
- }
47
- if (!this.searchLang) {
48
- this.searchLang = this.lang;
49
- }
50
- translationFile = this.rootPath + 'translations/' + this.lang + '.js';
51
- this.importTranslationFile(translationFile).then(function(result) {
52
- thisObj.tt = eval(thisObj.lang);
53
- deferred.resolve();
54
- });
55
- return deferred.promise();
56
- };
55
+ AblePlayer.prototype.importTranslationFile = function(translationFile) {
57
56
 
58
- AblePlayer.prototype.importTranslationFile = function(translationFile) {
59
-
60
- var deferred = $.Deferred();
61
-
62
- $.getScript(translationFile)
63
- .done(function(translationVar,textStatus) {
64
- // translation file successfully retrieved
65
- deferred.resolve(translationVar);
66
- })
67
- .fail(function(jqxhr, settings, exception) {
68
- deferred.fail();
69
- // error retrieving file
70
- // TODO: handle this
71
- });
72
- return deferred.promise();
73
- };
57
+ var deferred = $.Deferred();
58
+ $.getScript(translationFile)
59
+ .done(function(translationVar,textStatus) {
60
+ // translation file successfully retrieved
61
+ deferred.resolve(translationVar);
62
+ })
63
+ .fail(function(jqxhr, settings, exception) {
64
+ deferred.fail();
65
+ // error retrieving file
66
+ // TODO: handle this
67
+ });
68
+ return deferred.promise();
69
+ };
74
70
 
75
71
  })(jQuery);
@@ -1,87 +1,87 @@
1
1
  (function($) {
2
- AblePlayer.prototype.computeEndTime = function(startTime, durationTime) {
3
- var SECONDS = 0;
4
- var MINUTES = 1;
5
- var HOURS = 2;
6
-
7
- var startParts = startTime
8
- .split(':')
9
- .reverse()
10
- .map(function(value) {
11
- return parseFloat(value);
12
- });
13
-
14
- var durationParts = durationTime
15
- .split(':')
16
- .reverse()
17
- .map(function(value) {
18
- return parseFloat(value);
19
- });
20
-
21
- var endTime = startParts
22
- .reduce(function(acc, val, index) {
23
- var sum = val + durationParts[index];
24
-
25
- if (index === SECONDS) {
26
- if (sum > 60) {
27
- durationParts[index + 1] += 1;
28
- sum -= 60;
29
- }
30
-
31
- sum = sum.toFixed(3);
32
- }
33
-
34
- if (index === MINUTES) {
35
- if (sum > 60) {
36
- durationParts[index + 1] += 1;
37
- sum -= 60;
38
- }
39
- }
40
-
41
- if (sum < 10) {
42
- sum = '0' + sum;
43
- }
44
-
45
- acc.push(sum);
46
-
47
- return acc;
48
- }, [])
49
- .reverse()
50
- .join(':');
51
-
52
- return endTime;
53
- };
54
-
55
- AblePlayer.prototype.ttml2webvtt = function(contents) {
56
- var thisObj = this;
57
-
58
- var xml = thisObj.convert.xml2json(contents, {
59
- ignoreComment: true,
60
- alwaysChildren: true,
61
- compact: true,
62
- spaces: 2
63
- });
64
-
65
- var vttHeader = 'WEBVTT\n\n\n';
66
- var captions = JSON.parse(xml).tt.body.div.p;
67
-
68
- var vttCaptions = captions.reduce(function(acc, value, index) {
69
- var text = value._text;
70
- var isArray = Array.isArray(text);
71
- var attributes = value._attributes;
72
- var endTime = thisObj.computeEndTime(attributes.begin, attributes.dur);
73
-
74
- var caption =
75
- thisObj.computeEndTime(attributes.begin, '00:00:0') +
76
- ' --> ' +
77
- thisObj.computeEndTime(attributes.begin, attributes.dur) +
78
- '\n' +
79
- (isArray ? text.join('\n') : text) +
80
- '\n\n';
81
-
82
- return acc + caption;
83
- }, vttHeader);
84
-
85
- return vttCaptions;
86
- };
2
+ AblePlayer.prototype.computeEndTime = function(startTime, durationTime) {
3
+ var SECONDS = 0;
4
+ var MINUTES = 1;
5
+ var HOURS = 2;
6
+
7
+ var startParts = startTime
8
+ .split(':')
9
+ .reverse()
10
+ .map(function(value) {
11
+ return parseFloat(value);
12
+ });
13
+
14
+ var durationParts = durationTime
15
+ .split(':')
16
+ .reverse()
17
+ .map(function(value) {
18
+ return parseFloat(value);
19
+ });
20
+
21
+ var endTime = startParts
22
+ .reduce(function(acc, val, index) {
23
+ var sum = val + durationParts[index];
24
+
25
+ if (index === SECONDS) {
26
+ if (sum > 60) {
27
+ durationParts[index + 1] += 1;
28
+ sum -= 60;
29
+ }
30
+
31
+ sum = sum.toFixed(3);
32
+ }
33
+
34
+ if (index === MINUTES) {
35
+ if (sum > 60) {
36
+ durationParts[index + 1] += 1;
37
+ sum -= 60;
38
+ }
39
+ }
40
+
41
+ if (sum < 10) {
42
+ sum = '0' + sum;
43
+ }
44
+
45
+ acc.push(sum);
46
+
47
+ return acc;
48
+ }, [])
49
+ .reverse()
50
+ .join(':');
51
+
52
+ return endTime;
53
+ };
54
+
55
+ AblePlayer.prototype.ttml2webvtt = function(contents) {
56
+ var thisObj = this;
57
+
58
+ var xml = thisObj.convert.xml2json(contents, {
59
+ ignoreComment: true,
60
+ alwaysChildren: true,
61
+ compact: true,
62
+ spaces: 2
63
+ });
64
+
65
+ var vttHeader = 'WEBVTT\n\n\n';
66
+ var captions = JSON.parse(xml).tt.body.div.p;
67
+
68
+ var vttCaptions = captions.reduce(function(acc, value, index) {
69
+ var text = value._text;
70
+ var isArray = Array.isArray(text);
71
+ var attributes = value._attributes;
72
+ var endTime = thisObj.computeEndTime(attributes.begin, attributes.dur);
73
+
74
+ var caption =
75
+ thisObj.computeEndTime(attributes.begin, '00:00:0') +
76
+ ' --> ' +
77
+ thisObj.computeEndTime(attributes.begin, attributes.dur) +
78
+ '\n' +
79
+ (isArray ? text.join('\n') : text) +
80
+ '\n\n';
81
+
82
+ return acc + caption;
83
+ }, vttHeader);
84
+
85
+ return vttCaptions;
86
+ };
87
87
  })(jQuery);
@@ -0,0 +1,448 @@
1
+
2
+ (function ($) {
3
+ AblePlayer.prototype.initVimeoPlayer = function () {
4
+
5
+ var thisObj, deferred, promise, containerId, vimeoId, autoplay, videoDimensions, options;
6
+ thisObj = this;
7
+
8
+ deferred = new $.Deferred();
9
+ promise = deferred.promise();
10
+
11
+ deferred.resolve();
12
+
13
+ containerId = this.mediaId + '_vimeo';
14
+
15
+ // add container to which Vimeo player iframe will be appended
16
+ this.$mediaContainer.prepend($('<div>').attr('id', containerId));
17
+
18
+ // if a described version is available && user prefers desription
19
+ // init player using the described version
20
+ if (this.vimeoDescId && this.prefDesc) {
21
+ vimeoId = this.vimeoDescId;
22
+ }
23
+ else {
24
+ vimeoId = this.vimeoId;
25
+ }
26
+ this.activeVimeoId = vimeoId;
27
+
28
+ // Notes re. Vimeo Embed Options:
29
+ // If a video is owned by a user with a paid Plus, PRO, or Business account,
30
+ // setting the "background" option to "true" will hide the default controls.
31
+ // It has no effect on videos owned by a free basic account owner (their controls cannot be hidden).
32
+ // Also, setting "background" to "true" has a couple of side effects:
33
+ // In addition to hiding the controls, it also autoplays and loops the video.
34
+ // If the player is initialized with options to set both "autoplay" and "loop" to "false",
35
+ // this does not override the "background" setting.
36
+ // Passing this.autoplay and this.loop anyway, just in case it works someday
37
+ // Meanwhile, workaround is to setup an event listener to immediately pause after video autoplays
38
+
39
+ if (this.autoplay && this.okToPlay) {
40
+ autoplay = 'true';
41
+ }
42
+ else {
43
+ autoplay = 'false';
44
+ }
45
+
46
+ videoDimensions = this.getVimeoDimensions(this.activeVimeoId, containerId);
47
+ if (videoDimensions) {
48
+ this.vimeoWidth = videoDimensions[0];
49
+ this.vimeoHeight = videoDimensions[1];
50
+ this.aspectRatio = thisObj.ytWidth / thisObj.ytHeight;
51
+ }
52
+ else {
53
+ // dimensions are initially unknown
54
+ // sending null values to Vimeo results in a video that uses the default Vimeo dimensions
55
+ // these can then be scraped from the iframe and applied to this.$ableWrapper
56
+ this.vimeoWidth = null;
57
+ this.vimeoHeight = null;
58
+ }
59
+ options = {
60
+ id: vimeoId,
61
+ width: this.vimeoWidth,
62
+ background: true,
63
+ autoplay: this.autoplay,
64
+ loop: this.loop
65
+ };
66
+
67
+ this.vimeoPlayer = new Vimeo.Player(containerId, options);
68
+
69
+ this.vimeoPlayer.ready().then(function() {
70
+
71
+ if (!thisObj.hasPlaylist) {
72
+ // remove the media element, since Vimeo replaces that with its own element in an iframe
73
+ // this is handled differently for playlists. See buildplayer.js > cuePlaylistItem()
74
+ thisObj.$media.remove();
75
+
76
+ // define variables that will impact player setup
77
+
78
+ // vimeoSupportsPlaybackRateChange
79
+ // changing playbackRate is only supported if the video is hosted on a Pro or Business account
80
+ // unfortunately there is no direct way to query for that information.
81
+ // this.vimeoPlayer.getPlaybackRate() returns a value, regardless of account type
82
+ // This is a hack:
83
+ // Attempt to change the playbackRate. If it results in an error, assume changing playbackRate is not supported.
84
+ // Supported playbackRate values are 0.5 to 2.
85
+ thisObj.vimeoPlaybackRate = 1;
86
+ thisObj.vimeoPlayer.setPlaybackRate(thisObj.vimeoPlaybackRate).then(function(playbackRate) {
87
+ // playback rate was set
88
+ thisObj.vimeoSupportsPlaybackRateChange = true;
89
+ }).catch(function(error) {
90
+ thisObj.vimeoSupportsPlaybackRateChange = false;
91
+ });
92
+ deferred.resolve();
93
+ }
94
+ });
95
+ return promise;
96
+ };
97
+
98
+ AblePlayer.prototype.getVimeoPaused = function () {
99
+
100
+ var deferred, promise;
101
+ deferred = new $.Deferred();
102
+ promise = deferred.promise();
103
+
104
+ this.vimeoPlayer.getPaused().then(function (paused) {
105
+ // paused is Boolean
106
+ deferred.resolve(paused);
107
+ });
108
+
109
+ return promise;
110
+ }
111
+
112
+ AblePlayer.prototype.getVimeoEnded = function () {
113
+
114
+ var deferred, promise;
115
+ deferred = new $.Deferred();
116
+ promise = deferred.promise();
117
+
118
+ this.vimeoPlayer.getEnded().then(function (ended) {
119
+ // ended is Boolean
120
+ deferred.resolve(ended);
121
+ });
122
+
123
+ return promise;
124
+ }
125
+
126
+ AblePlayer.prototype.getVimeoState = function () {
127
+
128
+ var thisObj, deferred, promise, promises, gettingPausedPromise, gettingEndedPromise;
129
+
130
+ thisObj = this;
131
+
132
+ deferred = new $.Deferred();
133
+ promise = deferred.promise();
134
+ promises = [];
135
+
136
+ gettingPausedPromise = this.vimeoPlayer.getPaused();
137
+ gettingEndedPromise = this.vimeoPlayer.getEnded();
138
+
139
+ promises.push(gettingPausedPromise);
140
+ promises.push(gettingEndedPromise);
141
+
142
+ gettingPausedPromise.then(function (paused) {
143
+ deferred.resolve(paused);
144
+ });
145
+ gettingEndedPromise.then(function (ended) {
146
+ deferred.resolve(ended);
147
+ });
148
+ $.when.apply($, promises).then(function () {
149
+ deferred.resolve();
150
+ });
151
+ return promise;
152
+ }
153
+
154
+ AblePlayer.prototype.getVimeoDimensions = function (vimeoContainerId) {
155
+
156
+ // get dimensions of YouTube video, return array with width & height
157
+ // Sources, in order of priority:
158
+ // 1. The width and height attributes on <video>
159
+ // 2. YouTube (not yet supported; can't seem to get this data via YouTube Data API without OAuth!)
160
+
161
+ var d, url, $iframe, width, height;
162
+
163
+ d = [];
164
+
165
+ if (typeof this.playerMaxWidth !== 'undefined') {
166
+ d[0] = this.playerMaxWidth;
167
+ // optional: set height as well; not required though since YouTube will adjust height to match width
168
+ if (typeof this.playerMaxHeight !== 'undefined') {
169
+ d[1] = this.playerMaxHeight;
170
+ }
171
+ return d;
172
+ }
173
+ else {
174
+ if (typeof $('#' + vimeoContainerId) !== 'undefined') {
175
+ $iframe = $('#' + vimeoContainerId);
176
+ width = $iframe.width();
177
+ height = $iframe.height();
178
+ if (width > 0 && height > 0) {
179
+ d[0] = width;
180
+ d[1] = height;
181
+ return d;
182
+ }
183
+ }
184
+ }
185
+ return false;
186
+ };
187
+
188
+ AblePlayer.prototype.resizeVimeoPlayer = function(youTubeId, youTubeContainerId) {
189
+
190
+ // called after player is ready, if youTube dimensions were previously unknown
191
+ // Now need to get them from the iframe element that YouTube injected
192
+ // and resize Able Player to match
193
+ var d, width, height;
194
+ if (typeof this.aspectRatio !== 'undefined') {
195
+ // video dimensions have already been collected
196
+ if (this.restoringAfterFullScreen) {
197
+ // restore using saved values
198
+ if (this.youTubePlayer) {
199
+ this.youTubePlayer.setSize(this.ytWidth, this.ytHeight);
200
+ }
201
+ this.restoringAfterFullScreen = false;
202
+ }
203
+ else {
204
+ // recalculate with new wrapper size
205
+ width = this.$ableWrapper.parent().width();
206
+ height = Math.round(width / this.aspectRatio);
207
+ this.$ableWrapper.css({
208
+ 'max-width': width + 'px',
209
+ 'width': ''
210
+ });
211
+ this.youTubePlayer.setSize(width, height);
212
+ if (this.fullscreen) {
213
+ this.youTubePlayer.setSize(width, height);
214
+ }
215
+ else {
216
+ // resizing due to a change in window size, not full screen
217
+ this.youTubePlayer.setSize(this.ytWidth, this.ytHeight);
218
+ }
219
+ }
220
+ }
221
+ else {
222
+ d = this.getYouTubeDimensions(youTubeId, youTubeContainerId);
223
+ if (d) {
224
+ width = d[0];
225
+ height = d[1];
226
+ if (width > 0 && height > 0) {
227
+ this.aspectRatio = width / height;
228
+ this.ytWidth = width;
229
+ this.ytHeight = height;
230
+ if (width !== this.$ableWrapper.width()) {
231
+ // now that we've retrieved YouTube's default width,
232
+ // need to adjust to fit the current player wrapper
233
+ width = this.$ableWrapper.width();
234
+ height = Math.round(width / this.aspectRatio);
235
+ if (this.youTubePlayer) {
236
+ this.youTubePlayer.setSize(width, height);
237
+ }
238
+ }
239
+ }
240
+ }
241
+ }
242
+ };
243
+
244
+ AblePlayer.prototype.setupVimeoCaptions = function () {
245
+
246
+ // called from setupAltCaptions if player is YouTube and there are no <track> captions
247
+
248
+ // use YouTube Data API to get caption data from YouTube
249
+ // function is called only if these conditions are met:
250
+ // 1. this.player === 'youtube'
251
+ // 2. there are no <track> elements with kind="captions"
252
+ // 3. youTubeDataApiKey is defined
253
+
254
+ var deferred = new $.Deferred();
255
+ var promise = deferred.promise();
256
+
257
+ var thisObj, googleApiPromise, youTubeId, i;
258
+
259
+ thisObj = this;
260
+
261
+ // if a described version is available && user prefers desription
262
+ // Use the described version, and get its captions
263
+ if (this.youTubeDescId && this.prefDesc) {
264
+ youTubeId = this.youTubeDescId;
265
+ }
266
+ else {
267
+ youTubeId = this.youTubeId;
268
+ }
269
+ if (typeof youTubeDataAPIKey !== 'undefined') {
270
+ // Wait until Google Client API is loaded
271
+ // When loaded, it sets global var googleApiReady to true
272
+
273
+ // Thanks to Paul Tavares for $.doWhen()
274
+ // https://gist.github.com/purtuga/8257269
275
+ $.doWhen({
276
+ when: function(){
277
+ return googleApiReady;
278
+ },
279
+ interval: 100, // ms
280
+ attempts: 1000
281
+ })
282
+ .done(function(){
283
+ deferred.resolve();
284
+ })
285
+ .fail(function(){
286
+ console.log('Unable to initialize Google API. YouTube captions are currently unavailable.');
287
+ });
288
+ }
289
+ else {
290
+ deferred.resolve();
291
+ }
292
+ return promise;
293
+ };
294
+
295
+ AblePlayer.prototype.getVimeoCaptionTracks = function () {
296
+
297
+ // get data via Vimeo Player API, and push data to this.captions
298
+ // Note: Vimeo doesn't expose the caption cues themselves
299
+ // so this.captions will only include metadata about caption tracks; not cues
300
+ var deferred = new $.Deferred();
301
+ var promise = deferred.promise();
302
+
303
+ var thisObj, i, trackId, isDefaultTrack;
304
+
305
+ thisObj = this;
306
+
307
+ this.vimeoPlayer.getTextTracks().then(function(tracks) {
308
+
309
+ // each Vimeo track includes the following:
310
+ // label (local name of the language)
311
+ // language (2-character code)
312
+ // kind (captions or subtitles, as declared by video owner)
313
+ // mode ('disabled' or 'showing')
314
+
315
+ if (tracks.length) {
316
+
317
+ // create a new button for each caption track
318
+ for (i=0; i<tracks.length; i++) {
319
+
320
+ thisObj.hasCaptions = true;
321
+ thisObj.usingVimeoCaptions = true;
322
+ if (thisObj.prefCaptions === 1) {
323
+ thisObj.captionsOn = true;
324
+ }
325
+ else {
326
+ thisObj.captionsOn = false;
327
+ }
328
+ // assign the default track based on language of the player
329
+ if (tracks[i]['language'] === thisObj.lang) {
330
+ isDefaultTrack = true;
331
+ }
332
+ else {
333
+ isDefaultTrack = false;
334
+ }
335
+ thisObj.tracks.push({
336
+ 'kind': tracks[i]['kind'],
337
+ 'language': tracks[i]['language'],
338
+ 'label': tracks[i]['label'],
339
+ 'def': isDefaultTrack
340
+ });
341
+ }
342
+
343
+ // setupPopups again with new captions array, replacing original
344
+ thisObj.setupPopups('captions');
345
+ deferred.resolve();
346
+ }
347
+ else {
348
+ thisObj.hasCaptions = false;
349
+ thisObj.usingVimeoCaptions = false;
350
+ deferred.resolve();
351
+ }
352
+ });
353
+
354
+ return promise;
355
+ };
356
+
357
+ AblePlayer.prototype.initVimeoCaptionModule = function () {
358
+ // This function is called when YouTube onApiChange event fires
359
+ // to indicate that the player has loaded (or unloaded) a module with exposed API methods
360
+ // it isn't fired until the video starts playing
361
+ // and only fires if captions are available for this video (automated captions don't count)
362
+ // If no captions are available, onApichange event never fires & this function is never called
363
+
364
+ // YouTube iFrame API documentation is incomplete related to captions
365
+ // Found undocumented features on user forums and by playing around
366
+ // Details are here: http://terrillthompson.com/blog/648
367
+ // Summary:
368
+ // User might get either the AS3 (Flash) or HTML5 YouTube player
369
+ // The API uses a different caption module for each player (AS3 = 'cc'; HTML5 = 'captions')
370
+ // There are differences in the data and methods available through these modules
371
+ // This function therefore is used to determine which captions module is being used
372
+ // If it's a known module, this.ytCaptionModule will be used elsewhere to control captions
373
+ var options, fontSize, displaySettings;
374
+
375
+ options = this.youTubePlayer.getOptions();
376
+ if (options.length) {
377
+ for (var i=0; i<options.length; i++) {
378
+ if (options[i] == 'cc') { // this is the AS3 (Flash) player
379
+ this.ytCaptionModule = 'cc';
380
+ if (!this.hasCaptions) {
381
+ // there are captions available via other sources (e.g., <track>)
382
+ // so use these
383
+ this.hasCaptions = true;
384
+ this.usingYouTubeCaptions = true;
385
+ }
386
+ break;
387
+ }
388
+ else if (options[i] == 'captions') { // this is the HTML5 player
389
+ this.ytCaptionModule = 'captions';
390
+ if (!this.hasCaptions) {
391
+ // there are captions available via other sources (e.g., <track>)
392
+ // so use these
393
+ this.hasCaptions = true;
394
+ this.usingYouTubeCaptions = true;
395
+ }
396
+ break;
397
+ }
398
+ }
399
+ if (typeof this.ytCaptionModule !== 'undefined') {
400
+ if (this.usingYouTubeCaptions) {
401
+ // set default languaage
402
+ this.youTubePlayer.setOption(this.ytCaptionModule, 'track', {'languageCode': this.captionLang});
403
+ // set font size using Able Player prefs (values are -1, 0, 1, 2, and 3, where 0 is default)
404
+ this.youTubePlayer.setOption(this.ytCaptionModule,'fontSize',this.translatePrefs('size',this.prefCaptionsSize,'youtube'));
405
+ // ideally could set other display options too, but no others seem to be supported by setOption()
406
+ }
407
+ else {
408
+ // now that we know which cc module was loaded, unload it!
409
+ // we don't want it if we're using local <track> elements for captions
410
+ this.youTubePlayer.unloadModule(this.ytCaptionModule)
411
+ }
412
+ }
413
+ }
414
+ else {
415
+ // no modules were loaded onApiChange
416
+ // unfortunately, gonna have to disable captions if we can't control them
417
+ this.hasCaptions = false;
418
+ this.usingYouTubeCaptions = false;
419
+ }
420
+ this.refreshControls('captions');
421
+ };
422
+
423
+ AblePlayer.prototype.getVimeoPosterUrl = function (youTubeId, width) {
424
+
425
+ // return a URL for retrieving a YouTube poster image
426
+ // supported values of width: 120, 320, 480, 640
427
+
428
+ var url = 'https://img.youtube.com/vi/' + youTubeId;
429
+ if (width == '120') {
430
+ // default (small) thumbnail, 120 x 90
431
+ return url + '/default.jpg';
432
+ }
433
+ else if (width == '320') {
434
+ // medium quality thumbnail, 320 x 180
435
+ return url + '/hqdefault.jpg';
436
+ }
437
+ else if (width == '480') {
438
+ // high quality thumbnail, 480 x 360
439
+ return url + '/hqdefault.jpg';
440
+ }
441
+ else if (width == '640') {
442
+ // standard definition poster image, 640 x 480
443
+ return url + '/sddefault.jpg';
444
+ }
445
+ return false;
446
+ };
447
+
448
+ })(jQuery);