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.
Files changed (173) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +52 -0
  4. data/_data/lang.json +730 -0
  5. data/_data/techniques.yml +180 -0
  6. data/_data/wcag.yml +125 -0
  7. data/_includes/.DS_Store +0 -0
  8. data/_includes/body-class.html +1 -0
  9. data/_includes/box.html +10 -0
  10. data/_includes/excol.html +13 -0
  11. data/_includes/footer.html +40 -0
  12. data/_includes/head.html +23 -0
  13. data/_includes/header.html +59 -0
  14. data/_includes/icon.html +6 -0
  15. data/_includes/img.html +17 -0
  16. data/_includes/multilang-list-policy-links.html +29 -0
  17. data/_includes/multilang-list.html +35 -0
  18. data/_includes/multilang-policy-title.html +5 -0
  19. data/_includes/multilang-title-full.html +1 -0
  20. data/_includes/multilang-title.html +1 -0
  21. data/_includes/navlist.html +22 -0
  22. data/_includes/notes.html +2 -0
  23. data/_includes/prevnext.html +34 -0
  24. data/_includes/resources.html +19 -0
  25. data/_includes/sidenav.html +65 -0
  26. data/_includes/sidenote.html +14 -0
  27. data/_includes/toc.html +10 -0
  28. data/_includes/video-player.html +99 -0
  29. data/_layouts/default.html +26 -0
  30. data/_layouts/home.html +14 -0
  31. data/_layouts/news.html +21 -0
  32. data/_layouts/none.html +1 -0
  33. data/_layouts/policy.html +72 -0
  34. data/_layouts/sidenav.html +27 -0
  35. data/_layouts/sidenavsidebar.html +22 -0
  36. data/assets/ableplayer/.gitattributes +14 -0
  37. data/assets/ableplayer/.gitignore +7 -0
  38. data/assets/ableplayer/Gruntfile.js +105 -0
  39. data/assets/ableplayer/LICENSE +26 -0
  40. data/assets/ableplayer/README.md +656 -0
  41. data/assets/ableplayer/build/ableplayer.dist.js +12157 -0
  42. data/assets/ableplayer/build/ableplayer.js +12157 -0
  43. data/assets/ableplayer/build/ableplayer.min.css +2 -0
  44. data/assets/ableplayer/build/ableplayer.min.js +8 -0
  45. data/assets/ableplayer/button-icons/able-icons.svg +116 -0
  46. data/assets/ableplayer/button-icons/black/captions.png +0 -0
  47. data/assets/ableplayer/button-icons/black/chapters.png +0 -0
  48. data/assets/ableplayer/button-icons/black/close.png +0 -0
  49. data/assets/ableplayer/button-icons/black/descriptions.png +0 -0
  50. data/assets/ableplayer/button-icons/black/ellipsis.png +0 -0
  51. data/assets/ableplayer/button-icons/black/faster.png +0 -0
  52. data/assets/ableplayer/button-icons/black/forward.png +0 -0
  53. data/assets/ableplayer/button-icons/black/fullscreen-collapse.png +0 -0
  54. data/assets/ableplayer/button-icons/black/fullscreen-expand.png +0 -0
  55. data/assets/ableplayer/button-icons/black/help.png +0 -0
  56. data/assets/ableplayer/button-icons/black/next.png +0 -0
  57. data/assets/ableplayer/button-icons/black/pause.png +0 -0
  58. data/assets/ableplayer/button-icons/black/pipe.png +0 -0
  59. data/assets/ableplayer/button-icons/black/play.png +0 -0
  60. data/assets/ableplayer/button-icons/black/preferences.png +0 -0
  61. data/assets/ableplayer/button-icons/black/previous.png +0 -0
  62. data/assets/ableplayer/button-icons/black/rabbit.png +0 -0
  63. data/assets/ableplayer/button-icons/black/restart.png +0 -0
  64. data/assets/ableplayer/button-icons/black/rewind.png +0 -0
  65. data/assets/ableplayer/button-icons/black/sign.png +0 -0
  66. data/assets/ableplayer/button-icons/black/slower.png +0 -0
  67. data/assets/ableplayer/button-icons/black/stop.png +0 -0
  68. data/assets/ableplayer/button-icons/black/transcript.png +0 -0
  69. data/assets/ableplayer/button-icons/black/turtle.png +0 -0
  70. data/assets/ableplayer/button-icons/black/volume-loud.png +0 -0
  71. data/assets/ableplayer/button-icons/black/volume-medium.png +0 -0
  72. data/assets/ableplayer/button-icons/black/volume-mute.png +0 -0
  73. data/assets/ableplayer/button-icons/black/volume-soft.png +0 -0
  74. data/assets/ableplayer/button-icons/fonts/able.eot +0 -0
  75. data/assets/ableplayer/button-icons/fonts/able.svg +40 -0
  76. data/assets/ableplayer/button-icons/fonts/able.ttf +0 -0
  77. data/assets/ableplayer/button-icons/fonts/able.woff +0 -0
  78. data/assets/ableplayer/button-icons/white/captions.png +0 -0
  79. data/assets/ableplayer/button-icons/white/chapters.png +0 -0
  80. data/assets/ableplayer/button-icons/white/close.png +0 -0
  81. data/assets/ableplayer/button-icons/white/descriptions.png +0 -0
  82. data/assets/ableplayer/button-icons/white/ellipsis.png +0 -0
  83. data/assets/ableplayer/button-icons/white/faster.png +0 -0
  84. data/assets/ableplayer/button-icons/white/forward.png +0 -0
  85. data/assets/ableplayer/button-icons/white/fullscreen-collapse.png +0 -0
  86. data/assets/ableplayer/button-icons/white/fullscreen-expand.png +0 -0
  87. data/assets/ableplayer/button-icons/white/help.png +0 -0
  88. data/assets/ableplayer/button-icons/white/next.png +0 -0
  89. data/assets/ableplayer/button-icons/white/pause.png +0 -0
  90. data/assets/ableplayer/button-icons/white/pipe.png +0 -0
  91. data/assets/ableplayer/button-icons/white/play.png +0 -0
  92. data/assets/ableplayer/button-icons/white/preferences.png +0 -0
  93. data/assets/ableplayer/button-icons/white/previous.png +0 -0
  94. data/assets/ableplayer/button-icons/white/rabbit.png +0 -0
  95. data/assets/ableplayer/button-icons/white/restart.png +0 -0
  96. data/assets/ableplayer/button-icons/white/rewind.png +0 -0
  97. data/assets/ableplayer/button-icons/white/sign.png +0 -0
  98. data/assets/ableplayer/button-icons/white/slower.png +0 -0
  99. data/assets/ableplayer/button-icons/white/stop.png +0 -0
  100. data/assets/ableplayer/button-icons/white/transcript.png +0 -0
  101. data/assets/ableplayer/button-icons/white/turtle.png +0 -0
  102. data/assets/ableplayer/button-icons/white/volume-loud.png +0 -0
  103. data/assets/ableplayer/button-icons/white/volume-medium.png +0 -0
  104. data/assets/ableplayer/button-icons/white/volume-mute.png +0 -0
  105. data/assets/ableplayer/button-icons/white/volume-soft.png +0 -0
  106. data/assets/ableplayer/images/wingrip.png +0 -0
  107. data/assets/ableplayer/package.json +22 -0
  108. data/assets/ableplayer/scripts/JQuery.doWhen.js +113 -0
  109. data/assets/ableplayer/scripts/ableplayer-base.js +440 -0
  110. data/assets/ableplayer/scripts/browser.js +162 -0
  111. data/assets/ableplayer/scripts/buildplayer.js +1609 -0
  112. data/assets/ableplayer/scripts/caption.js +385 -0
  113. data/assets/ableplayer/scripts/chapters.js +242 -0
  114. data/assets/ableplayer/scripts/control.js +1514 -0
  115. data/assets/ableplayer/scripts/description.js +283 -0
  116. data/assets/ableplayer/scripts/dialog.js +147 -0
  117. data/assets/ableplayer/scripts/dragdrop.js +766 -0
  118. data/assets/ableplayer/scripts/event.js +595 -0
  119. data/assets/ableplayer/scripts/initialize.js +725 -0
  120. data/assets/ableplayer/scripts/langs.js +750 -0
  121. data/assets/ableplayer/scripts/metadata.js +134 -0
  122. data/assets/ableplayer/scripts/misc.js +72 -0
  123. data/assets/ableplayer/scripts/preference.js +909 -0
  124. data/assets/ableplayer/scripts/search.js +171 -0
  125. data/assets/ableplayer/scripts/sign.js +92 -0
  126. data/assets/ableplayer/scripts/slider.js +454 -0
  127. data/assets/ableplayer/scripts/track.js +296 -0
  128. data/assets/ableplayer/scripts/transcript.js +590 -0
  129. data/assets/ableplayer/scripts/translation.js +66 -0
  130. data/assets/ableplayer/scripts/volume.js +383 -0
  131. data/assets/ableplayer/scripts/webvtt.js +765 -0
  132. data/assets/ableplayer/scripts/youtube.js +471 -0
  133. data/assets/ableplayer/styles/ableplayer.css +1241 -0
  134. data/assets/ableplayer/thirdparty/js.cookie.js +145 -0
  135. data/assets/ableplayer/thirdparty/modernizr.custom.js +4 -0
  136. data/assets/ableplayer/translations/ca.js +1 -0
  137. data/assets/ableplayer/translations/de.js +1 -0
  138. data/assets/ableplayer/translations/en.js +305 -0
  139. data/assets/ableplayer/translations/es.js +305 -0
  140. data/assets/ableplayer/translations/fr.js +305 -0
  141. data/assets/ableplayer/translations/it.js +303 -0
  142. data/assets/ableplayer/translations/ja.js +305 -0
  143. data/assets/ableplayer/translations/nl.js +305 -0
  144. data/assets/css/style.css +4360 -0
  145. data/assets/css/style.css.map +1 -0
  146. data/assets/fonts/anonymouspro-bold.woff +0 -0
  147. data/assets/fonts/anonymouspro-bold.woff2 +0 -0
  148. data/assets/fonts/anonymouspro-bolditalic.woff +0 -0
  149. data/assets/fonts/anonymouspro-bolditalic.woff2 +0 -0
  150. data/assets/fonts/anonymouspro-italic.woff +0 -0
  151. data/assets/fonts/anonymouspro-italic.woff2 +0 -0
  152. data/assets/fonts/anonymouspro-regular.woff +0 -0
  153. data/assets/fonts/anonymouspro-regular.woff2 +0 -0
  154. data/assets/fonts/notosans-bold.woff +0 -0
  155. data/assets/fonts/notosans-bold.woff2 +0 -0
  156. data/assets/fonts/notosans-bolditalic.woff +0 -0
  157. data/assets/fonts/notosans-bolditalic.woff2 +0 -0
  158. data/assets/fonts/notosans-italic.woff +0 -0
  159. data/assets/fonts/notosans-italic.woff2 +0 -0
  160. data/assets/fonts/notosans-regular.woff +0 -0
  161. data/assets/fonts/notosans-regular.woff2 +0 -0
  162. data/assets/images/.DS_Store +0 -0
  163. data/assets/images/Shape.svg +10 -0
  164. data/assets/images/icon-related-content.svg +14 -0
  165. data/assets/images/icons.svg +126 -0
  166. data/assets/images/teaser-image@1x.jpg +0 -0
  167. data/assets/images/teaser-image@2x.jpg +0 -0
  168. data/assets/images/w3c.sketch +0 -0
  169. data/assets/images/w3c.svg +10 -0
  170. data/assets/scripts/jquery.min.js +4 -0
  171. data/assets/scripts/main.js +208 -0
  172. data/assets/scripts/svg4everybody.js +1 -0
  173. metadata +257 -0
@@ -0,0 +1,725 @@
1
+ (function ($) {
2
+ // Set default variable values.
3
+ AblePlayer.prototype.setDefaults = function () {
4
+
5
+ // this.playing will change to true after 'playing' event is triggered
6
+ this.playing = false;
7
+
8
+ this.getUserAgent();
9
+ this.setIconColor();
10
+ this.setButtonImages();
11
+ };
12
+
13
+ AblePlayer.prototype.getRootPath = function() {
14
+
15
+ // returns Able Player root path (assumes ableplayer.js is in /build, one directory removed from root)
16
+ var scripts, i, scriptSrc, scriptFile, fullPath, ablePath, parentFolderIndex, rootPath;
17
+ scripts= document.getElementsByTagName('script');
18
+ for (i=0; i < scripts.length; i++) {
19
+ scriptSrc = scripts[i].src;
20
+ scriptFile = scriptSrc.substr(scriptSrc.lastIndexOf('/'));
21
+ if (scriptFile.indexOf('ableplayer') !== -1) {
22
+ // this is the ableplayerscript
23
+ fullPath = scriptSrc.split('?')[0]; // remove any ? params
24
+ break;
25
+ }
26
+ }
27
+ ablePath= fullPath.split('/').slice(0, -1).join('/'); // remove last filename part of path
28
+ parentFolderIndex = ablePath.lastIndexOf('/');
29
+ rootPath = ablePath.substring(0, parentFolderIndex) + '/';
30
+ return rootPath;
31
+ }
32
+
33
+ AblePlayer.prototype.setIconColor = function() {
34
+
35
+ // determine the best color choice (white or black) for icons,
36
+ // given the background-color of their container elements
37
+ // Source for relative luminance formula:
38
+ // https://en.wikipedia.org/wiki/Relative_luminance
39
+
40
+ // We need to know the color *before* creating the element
41
+ // so the element doesn't exist yet when this function is called
42
+ // therefore, need to create a temporary element then remove it after color is determined
43
+ // Temp element must be added to the DOM or WebKit can't retrieve its CSS properties
44
+
45
+ var $elements, i, $el, bgColor, rgb, red, green, blue, luminance, iconColor;
46
+
47
+ $elements = ['controller', 'toolbar'];
48
+ for (i=0; i<$elements.length; i++) {
49
+ if ($elements[i] == 'controller') {
50
+ $el = $('<div>', {
51
+ 'class': 'able-controller'
52
+ }).hide();
53
+ }
54
+ else if ($elements[i] === 'toolbar') {
55
+ $el = $('<div>', {
56
+ 'class': 'able-window-toolbar'
57
+ }).hide();
58
+ }
59
+ $('body').append($el);
60
+ bgColor = $el.css('background-color');
61
+ // bgColor is a string in the form 'rgb(R, G, B)', perhaps with a 4th item for alpha;
62
+ // split the 3 or 4 channels into an array
63
+ rgb = bgColor.replace(/[^\d,]/g, '').split(',');
64
+ red = rgb[0];
65
+ green = rgb[1];
66
+ blue = rgb[2];
67
+ luminance = (0.2126 * red) + (0.7152 * green) + (0.0722 * blue);
68
+ // range is 1 - 255; therefore 125 is the tipping point
69
+ if (luminance < 125) { // background is dark
70
+ iconColor = 'white';
71
+ }
72
+ else { // background is light
73
+ iconColor = 'black';
74
+ }
75
+ if ($elements[i] === 'controller') {
76
+ this.iconColor = iconColor;
77
+ }
78
+ else if ($elements[i] === 'toolbar') {
79
+ this.toolbarIconColor = iconColor;
80
+ }
81
+ $el.remove();
82
+ }
83
+ };
84
+
85
+ AblePlayer.prototype.setButtonImages = function() {
86
+
87
+ // NOTE: volume button images are now set dynamically within volume.js
88
+ this.imgPath = this.rootPath + 'button-icons/' + this.iconColor + '/';
89
+ this.playButtonImg = this.imgPath + 'play.png';
90
+ this.pauseButtonImg = this.imgPath + 'pause.png';
91
+
92
+ this.restartButtonImg = this.imgPath + 'restart.png';
93
+
94
+ this.rewindButtonImg = this.imgPath + 'rewind.png';
95
+ this.forwardButtonImg = this.imgPath + 'forward.png';
96
+
97
+ this.previousButtonImg = this.imgPath + 'previous.png';
98
+ this.nextButtonImg = this.imgPath + 'next.png';
99
+
100
+ if (this.speedIcons === 'arrows') {
101
+ this.fasterButtonImg = this.imgPath + 'slower.png';
102
+ this.slowerButtonImg = this.imgPath + 'faster.png';
103
+ }
104
+ else if (this.speedIcons === 'animals') {
105
+ this.fasterButtonImg = this.imgPath + 'rabbit.png';
106
+ this.slowerButtonImg = this.imgPath + 'turtle.png';
107
+ }
108
+
109
+ this.captionsButtonImg = this.imgPath + 'captions.png';
110
+ this.chaptersButtonImg = this.imgPath + 'chapters.png';
111
+ this.signButtonImg = this.imgPath + 'sign.png';
112
+ this.transcriptButtonImg = this.imgPath + 'transcript.png';
113
+ this.descriptionsButtonImg = this.imgPath + 'descriptions.png';
114
+
115
+ this.fullscreenExpandButtonImg = this.imgPath + 'fullscreen-expand.png';
116
+ this.fullscreenCollapseButtonImg = this.imgPath + 'fullscreen-collapse.png';
117
+
118
+ this.prefsButtonImg = this.imgPath + 'preferences.png';
119
+ this.helpButtonImg = this.imgPath + 'help.png';
120
+ };
121
+
122
+ // Initialize player based on data on page.
123
+ // This sets some variables, but does not modify anything. Safe to call multiple times.
124
+ // Can call again after updating this.media so long as new media element has the same ID.
125
+ AblePlayer.prototype.reinitialize = function () {
126
+
127
+ var deferred, promise, thisObj, errorMsg, srcFile;
128
+
129
+ deferred = new $.Deferred();
130
+ promise = deferred.promise();
131
+ thisObj = this;
132
+
133
+ // if F12 Developer Tools aren't open in IE (through 9, no longer a problen in IE10)
134
+ // console.log causes an error - can't use debug without a console to log messages to
135
+ if (! window.console) {
136
+ this.debug = false;
137
+ }
138
+
139
+ this.startedPlaying = false;
140
+ // TODO: Move this setting to cookie.
141
+ this.autoScrollTranscript = true;
142
+ //this.autoScrollTranscript = this.getCookie(autoScrollTranscript); // (doesn't work)
143
+
144
+ // Bootstrap from this.media possibly being an ID or other selector.
145
+ this.$media = $(this.media).first();
146
+ this.media = this.$media[0];
147
+
148
+ // Set media type to 'audio' or 'video'; this determines some of the behavior of player creation.
149
+ if (this.$media.is('audio')) {
150
+ this.mediaType = 'audio';
151
+ }
152
+ else if (this.$media.is('video')) {
153
+ this.mediaType = 'video';
154
+ }
155
+ else {
156
+ this.mediaType = this.$media.get(0).tagName;
157
+ errorMsg = 'Media player initialized with ' + this.mediaType + '#' + this.mediaId + '. ';
158
+ errorMsg += 'Expecting an HTML5 audio or video element.';
159
+ this.provideFallback(errorMsg);
160
+ deferred.fail();
161
+ return promise;
162
+ }
163
+
164
+ this.$sources = this.$media.find('source');
165
+
166
+ this.player = this.getPlayer();
167
+ if (!this.player) {
168
+ // an error was generated in getPlayer()
169
+ this.provideFallback(this.error);
170
+ }
171
+ this.setIconType();
172
+ this.setDimensions();
173
+
174
+ deferred.resolve();
175
+ return promise;
176
+ };
177
+
178
+ AblePlayer.prototype.setDimensions = function() {
179
+ // if <video> element includes width and height attributes,
180
+ // use these to set the max-width and max-height of the player
181
+ if (this.$media.attr('width') && this.$media.attr('height')) {
182
+ this.playerMaxWidth = parseInt(this.$media.attr('width'), 10);
183
+ this.playerMaxHeight = parseInt(this.$media.attr('height'), 10);
184
+ }
185
+ else {
186
+ // set width to width of #player
187
+ // don't set height though; YouTube will automatically set that to match width
188
+ this.playerMaxWidth = this.$media.parent().width();
189
+ this.playerMaxHeight = this.getMatchingHeight(this.playerMaxWidth);
190
+ }
191
+ // override width and height attributes with in-line CSS to make video responsive
192
+ this.$media.css({
193
+ 'width': '100%',
194
+ 'height': 'auto'
195
+ });
196
+ };
197
+
198
+ AblePlayer.prototype.getMatchingHeight = function(width) {
199
+
200
+ // returns likely height for a video, given width
201
+ // These calculations assume 16:9 aspect ratio (the YouTube standard)
202
+ // Videos recorded in other resolutions will be sized to fit, with black bars on each side
203
+ // This function is only called if the <video> element does not have width and height attributes
204
+
205
+ var widths, heights, closestWidth, closestIndex, closestHeight, height;
206
+
207
+ widths = [ 3840, 2560, 1920, 1280, 854, 640, 426 ];
208
+ heights = [ 2160, 1440, 1080, 720, 480, 360, 240 ];
209
+ closestWidth = null;
210
+ closestIndex = null;
211
+
212
+ $.each(widths, function(index){
213
+ if (closestWidth == null || Math.abs(this - width) < Math.abs(closestWidth - width)) {
214
+ closestWidth = this;
215
+ closestIndex = index;
216
+ }
217
+ });
218
+ closestHeight = heights[closestIndex];
219
+ this.aspectRatio = closestWidth / closestHeight;
220
+ height = Math.round(width / this.aspectRatio);
221
+ return height;
222
+ };
223
+
224
+ AblePlayer.prototype.setIconType = function() {
225
+ // returns either "font" or "image"
226
+ // create a temporary play span and check to see if button has font-family == "able" (the default)
227
+ // if it doesn't, user has a custom style sheet and icon fonts will not display properly
228
+ // use images as fallback
229
+
230
+ var $tempButton, $testButton, controllerFont;
231
+
232
+ if (this.forceIconType) {
233
+ // use value specified in data-icon-type
234
+ return false;
235
+ }
236
+
237
+ if (window.getComputedStyle) {
238
+
239
+ // webkit doesn't return calculated styles unless element has been added to the DOM
240
+ // and is visible (note: visibly clipped is considered "visible")
241
+ // use playpauseButton for font-family test if it exists; otherwise must create a new temp button
242
+ if ($('span.icon-play').length) {
243
+ $testButton = $('span.icon-play');
244
+ }
245
+ else {
246
+ $tempButton = $('<span>',{
247
+ 'class': 'icon-play able-clipped'
248
+ });
249
+ $('body').append($tempButton);
250
+ $testButton = $tempButton;
251
+ }
252
+
253
+ // the following retrieves the computed value of font-family
254
+ // tested in Firefox 45.x with "Allow pages to choose their own fonts" unchecked - works!
255
+ // tested in Chrome 49.x with Font Changer plugin - works!
256
+ // tested in IE with user-defined style sheet enables - works!
257
+ // It does NOT account for users who have "ignore font styles on web pages" checked in IE
258
+ // There is no known way to check for that ???
259
+ controllerFont = window.getComputedStyle($testButton.get(0), null).getPropertyValue('font-family');
260
+ if (typeof controllerFont !== 'undefined') {
261
+ if (controllerFont.indexOf('able') !== -1) {
262
+ this.iconType = 'font';
263
+ }
264
+ else {
265
+ this.iconType = 'image';
266
+ }
267
+ }
268
+ else {
269
+ // couldn't get computed font-family; use images to be safe
270
+ this.iconType = 'image';
271
+ }
272
+ }
273
+ else { // window.getComputedStyle is not supported (IE 8 and earlier)
274
+ // No known way to detect computed font
275
+ // The following retrieves the value from the style sheet, not the computed font
276
+ // controllerFont = $tempButton.get(0).currentStyle.fontFamily;
277
+ // It will therefore return "able", even if the user is overriding that with a custom style sheet
278
+ // To be safe, use images
279
+ this.iconType = 'image';
280
+ }
281
+ if (this.debug) {
282
+ console.log('Using ' + this.iconType + 's for player controls');
283
+ }
284
+ if (typeof $tempButton !== 'undefined') {
285
+ $tempButton.remove();
286
+ }
287
+ };
288
+
289
+
290
+ // Perform one-time setup for this instance of player; called after player is first initialized.
291
+ AblePlayer.prototype.setupInstance = function () {
292
+ var deferred = new $.Deferred();
293
+ var promise = deferred.promise();
294
+
295
+ if (this.$media.attr('id')) {
296
+ this.mediaId = this.$media.attr('id');
297
+ }
298
+ else {
299
+ // Ensure the base media element always has an ID.
300
+ this.mediaId = "ableMediaId_" + this.ableIndex;
301
+ this.$media.attr('id', this.mediaId);
302
+ }
303
+ // get playlist for this media element
304
+ this.setupInstancePlaylist();
305
+
306
+ deferred.resolve();
307
+ return promise;
308
+ };
309
+
310
+ AblePlayer.prototype.setupInstancePlaylist = function() {
311
+ // find a matching playlist and set this.hasPlaylist
312
+ // if there is one, also set this.$playlist, this.playlistIndex, & this.playlistEmbed
313
+ var thisObj = this;
314
+
315
+ this.hasPlaylist = false; // will change to true if a matching playlist is found
316
+
317
+ $('.able-playlist').each(function() {
318
+ if ($(this).data('player') === thisObj.mediaId) {
319
+ // this is the playlist for the current player
320
+ thisObj.hasPlaylist = true;
321
+ // If using an embedded player, we'll replace $playlist with the clone later.
322
+ thisObj.$playlist = $(this).find('li');
323
+ // add tabindex to each list item
324
+ $(this).find('li').attr('tabindex', '0');
325
+ thisObj.playlistIndex = 0;
326
+ var dataEmbedded = $(this).data('embedded');
327
+ if (typeof dataEmbedded !== 'undefined' && dataEmbedded !== false) {
328
+ // embed playlist within player
329
+ thisObj.playlistEmbed = true;
330
+ }
331
+ else {
332
+ thisObj.playlistEmbed = false;
333
+ }
334
+ }
335
+ });
336
+
337
+ if (this.hasPlaylist && this.playlistEmbed) {
338
+ // Copy the playlist out of the dom, so we can reinject when we build the player.
339
+ var parent = this.$playlist.parent();
340
+ this.$playlistDom = parent.clone();
341
+ parent.remove();
342
+ }
343
+ };
344
+
345
+ // Creates the appropriate player for the current source.
346
+ AblePlayer.prototype.recreatePlayer = function () {
347
+
348
+ var thisObj, prefsGroups, i;
349
+ thisObj = this;
350
+
351
+ // TODO: Ensure when recreating player that we carry over the mediaId
352
+ if (!this.player) {
353
+ console.log("Can't create player; no appropriate player type detected.");
354
+ return;
355
+ }
356
+
357
+ // moved this until after setupTracks() is complete
358
+ // used to work fine in this location but was broken in Safari 10
359
+ // this.setMediaAttributes();
360
+
361
+ this.loadCurrentPreferences();
362
+
363
+ this.injectPlayerCode();
364
+ this.initSignLanguage();
365
+ this.setupTracks().then(function() {
366
+
367
+ // moved this here; in its original location was not working in Safari 10
368
+ thisObj.setMediaAttributes();
369
+
370
+ thisObj.setupAltCaptions().then(function() {
371
+
372
+ if (thisObj.transcriptType === 'external' || thisObj.transcriptType === 'popup') {
373
+ if (thisObj.captions.length <= 1) {
374
+ // without captions/subtitles in multiple languages,
375
+ // there is no need for a transcript language selector
376
+ thisObj.$transcriptLanguageSelect.parent().remove();
377
+ }
378
+ }
379
+
380
+ thisObj.initDescription();
381
+ thisObj.initDefaultCaption();
382
+
383
+ thisObj.initPlayer().then(function() { // initPlayer success
384
+ thisObj.initializing = false;
385
+
386
+ // inject each of the hidden forms that will be accessed from the Preferences popup menu
387
+ prefsGroups = thisObj.getPreferencesGroups();
388
+ for (i = 0; i < prefsGroups.length; i++) {
389
+ thisObj.injectPrefsForm(prefsGroups[i]);
390
+ }
391
+ thisObj.setupPopups();
392
+ thisObj.updateCaption();
393
+ thisObj.updateTranscript();
394
+ if (thisObj.chaptersDivLocation) {
395
+ thisObj.populateChaptersDiv();
396
+ }
397
+ thisObj.showSearchResults();
398
+ },
399
+ function() { // initPlayer fail
400
+ thisObj.provideFallback(this.error);
401
+ }
402
+ );
403
+ });
404
+ });
405
+ };
406
+
407
+ AblePlayer.prototype.initPlayer = function () {
408
+
409
+ var thisObj = this;
410
+ var playerPromise;
411
+
412
+ // First run player specific initialization.
413
+ if (this.player === 'html5') {
414
+ playerPromise = this.initHtml5Player();
415
+ }
416
+ else if (this.player === 'jw') {
417
+ playerPromise = this.initJwPlayer();
418
+ }
419
+ else if (this.player === 'youtube') {
420
+ playerPromise = this.initYouTubePlayer();
421
+ }
422
+
423
+ // After player specific initialization is done, run remaining general initialization.
424
+ var deferred = new $.Deferred();
425
+ var promise = deferred.promise();
426
+ playerPromise.done(
427
+ function () { // done/resolved
428
+ if (thisObj.useFixedSeekInterval === false) {
429
+ thisObj.setSeekInterval();
430
+ }
431
+ thisObj.addControls();
432
+ thisObj.addEventListeners();
433
+ // Calling these set functions also initializes some icons.
434
+ if (thisObj.Volume) {
435
+ thisObj.setMute(false);
436
+ }
437
+ thisObj.setFullscreen(false);
438
+ thisObj.setVolume(thisObj.defaultVolume);
439
+ thisObj.refreshControls();
440
+
441
+ // After done messing with the player, this is necessary to fix playback on iOS
442
+ if (thisObj.player === 'html5' && thisObj.isIOS()) {
443
+ thisObj.$media[0].load();
444
+ }
445
+ deferred.resolve();
446
+ }
447
+ ).fail(function () { // failed
448
+ deferred.reject();
449
+ }
450
+ );
451
+
452
+ return promise;
453
+ };
454
+
455
+ AblePlayer.prototype.setSeekInterval = function () {
456
+ // this function is only called if this.useFixedSeekInterval is false
457
+ // if this.useChapterTimes, this is called as each new chapter is loaded
458
+ // otherwise, it's called once, as the player is initialized
459
+ var duration;
460
+ this.seekInterval = this.defaultSeekInterval;
461
+ if (this.useChapterTimes) {
462
+ duration = this.chapterDuration;
463
+ }
464
+ else {
465
+ duration = this.getDuration();
466
+ }
467
+ if (typeof duration === 'undefined' || duration < 1) {
468
+ // no duration; just use default for now but keep trying until duration is available
469
+ this.seekIntervalCalculated = false;
470
+ return;
471
+ }
472
+ else {
473
+ if (duration <= 20) {
474
+ this.seekInterval = 5; // 4 steps max
475
+ }
476
+ else if (duration <= 30) {
477
+ this.seekInterval = 6; // 5 steps max
478
+ }
479
+ else if (duration <= 40) {
480
+ this.seekInterval = 8; // 5 steps max
481
+ }
482
+ else if (duration <= 100) {
483
+ this.seekInterval = 10; // 10 steps max
484
+ }
485
+ else {
486
+ // never more than 10 steps from start to end
487
+ this.seekInterval = (duration / 10);
488
+ }
489
+ this.seekIntervalCalculated = true;
490
+ }
491
+ };
492
+
493
+ AblePlayer.prototype.initDefaultCaption = function () {
494
+
495
+ var captions, i;
496
+
497
+ if (this.usingYouTubeCaptions) {
498
+ captions = this.ytCaptions;
499
+ }
500
+ else {
501
+ captions = this.captions;
502
+ }
503
+
504
+ if (captions.length > 0) {
505
+ for (i=0; i<captions.length; i++) {
506
+ if (captions[i].def === true) {
507
+ this.captionLang = captions[i].language;
508
+ this.selectedCaptions = captions[i];
509
+ }
510
+ }
511
+ if (typeof this.captionLang === 'undefined') {
512
+ // No caption track was flagged as default
513
+ // find and use a caption language that matches the player language
514
+ for (i=0; i<captions.length; i++) {
515
+ if (captions[i].language === this.lang) {
516
+ this.captionLang = captions[i].language;
517
+ this.selectedCaptions = captions[i];
518
+ }
519
+ }
520
+ }
521
+ if (typeof this.captionLang === 'undefined') {
522
+ // Still no matching caption track
523
+ // just use the first track
524
+ this.captionLang = captions[0].language;
525
+ this.selectedCaptions = captions[0];
526
+ }
527
+ if (typeof this.captionLang !== 'undefined') {
528
+ // reset transcript selected <option> to this.captionLang
529
+ if (this.$transcriptLanguageSelect) {
530
+ this.$transcriptLanguageSelect.find('option[lang=' + this.captionLang + ']').prop('selected',true);
531
+ }
532
+ // sync all other tracks to this same languge
533
+ this.syncTrackLanguages('init',this.captionLang);
534
+ }
535
+ }
536
+ };
537
+
538
+ AblePlayer.prototype.initHtml5Player = function () {
539
+ // Nothing special to do!
540
+ var deferred = new $.Deferred();
541
+ var promise = deferred.promise();
542
+ deferred.resolve();
543
+ return promise;
544
+ };
545
+
546
+ AblePlayer.prototype.initJwPlayer = function () {
547
+
548
+ var jwHeight;
549
+ var thisObj = this;
550
+ var deferred = new $.Deferred();
551
+ var promise = deferred.promise();
552
+
553
+ $.ajax({
554
+ async: false,
555
+ url: this.fallbackPath + 'jwplayer.js',
556
+ dataType: 'script',
557
+ success: function( data, textStatus, jqXHR) {
558
+ // Successfully loaded the JW Player
559
+ // add an id to div.able-media-container (JW Player needs this)
560
+ thisObj.jwId = thisObj.mediaId + '_fallback';
561
+ thisObj.$mediaContainer.attr('id', thisObj.jwId);
562
+ if (thisObj.mediaType === 'audio') {
563
+ // JW Player always shows its own controls if height <= 40
564
+ // Must set height to 0 to hide them
565
+ jwHeight = 0;
566
+ }
567
+ else {
568
+ jwHeight = thisObj.playerHeight;
569
+ }
570
+ var sources = [];
571
+ $.each(thisObj.$sources, function (ii, source) {
572
+ sources.push({file: $(source).attr('src')});
573
+ });
574
+
575
+ var flashplayer = thisObj.fallbackPath + 'jwplayer.flash.swf';
576
+ var html5player = thisObj.fallbackPath + 'jwplayer.html5.js';
577
+
578
+ // Initializing JW Player with width:100% results in player that is either the size of the video
579
+ // or scaled down to fit the container if container is smaller
580
+ // After onReady event fires, actual dimensions will be collected for future use
581
+ // in preserving the video ratio
582
+ if (thisObj.mediaType === 'video') {
583
+ thisObj.jwPlayer = jwplayer(thisObj.jwId).setup({
584
+ playlist: [{
585
+ image: thisObj.$media.attr('poster'),
586
+ sources: sources
587
+ }],
588
+ flashplayer: flashplayer,
589
+ html5player: html5player,
590
+ controls: false,
591
+ volume: thisObj.defaultVolume * 100,
592
+ width: '100%',
593
+ fallback: false,
594
+ primary: 'flash',
595
+ wmode: 'transparent' // necessary to get HTML captions to appear as overlay
596
+ });
597
+ }
598
+ else { // if this is an audio player
599
+ thisObj.jwPlayer = jwplayer(thisObj.jwId).setup({
600
+ playlist: [{
601
+ sources: sources
602
+ }],
603
+ flashplayer: flashplayer,
604
+ html5player: html5player,
605
+ controls: false,
606
+ volume: this.defaultVolume * 100,
607
+ height: 0,
608
+ width: '100%',
609
+ fallback: false,
610
+ primary: 'flash'
611
+ });
612
+ }
613
+ // remove the media element - we're done with it
614
+ // keeping it would cause too many potential problems with HTML5 & JW event listeners both firing
615
+ thisObj.$media.remove();
616
+
617
+ deferred.resolve();
618
+ },
619
+ error: function(jqXHR, textStatus, errorThrown) {
620
+ // Loading the JW Player failed
621
+ this.error = 'Failed to load JW Player.';
622
+ deferred.reject();
623
+ }
624
+ });
625
+ // Done with JW Player initialization.
626
+ return promise;
627
+ };
628
+
629
+ // Sets media/track/source attributes; is called whenever player is recreated since $media may have changed.
630
+ AblePlayer.prototype.setMediaAttributes = function () {
631
+ // Firefox puts videos in tab order; remove.
632
+ this.$media.attr('tabindex', -1);
633
+
634
+ // Keep native player from displaying captions/subtitles by setting textTrack.mode='disabled'
635
+ // https://dev.w3.org/html5/spec-author-view/video.html#text-track-mode
636
+ // This *should* work but historically hasn't been supported in all browsers
637
+ // Workaround for non-supporting browsers is to remove default attribute
638
+ // We're doing that too in track.js > setupCaptions()
639
+ var textTracks = this.$media.get(0).textTracks;
640
+ if (textTracks) {
641
+ var i = 0;
642
+ while (i < textTracks.length) {
643
+ textTracks[i].mode = 'disabled';
644
+ i += 1;
645
+ }
646
+ }
647
+ };
648
+
649
+ AblePlayer.prototype.getPlayer = function() {
650
+
651
+ // Determine which player to use, if any
652
+ // return 'html5', 'jw' or null
653
+ var i, sourceType, $newItem;
654
+ if (this.youTubeId) {
655
+ if (this.mediaType !== 'video') {
656
+ this.error = 'To play a YouTube video, use the &lt;video&gt; tag.';
657
+ return null;
658
+ }
659
+ else {
660
+ return 'youtube';
661
+ }
662
+ }
663
+ else if (this.testFallback ||
664
+ ((this.isUserAgent('msie 7') || this.isUserAgent('msie 8') || this.isUserAgent('msie 9')) && this.mediaType === 'video') ||
665
+ (this.isIOS() && (this.isIOS(4) || this.isIOS(5) || this.isIOS(6)))
666
+ ) {
667
+ // the user wants to test the fallback player, or
668
+ // the user is using an older version of IE or IOS,
669
+ // both of which had buggy implementation of HTML5 video
670
+ if (this.fallback === 'jw' && this.jwCanPlay()) {
671
+ return 'jw';
672
+ }
673
+ else {
674
+ this.error = 'The fallback player (JW Player) is unable to play the available media file.';
675
+ return null;
676
+ }
677
+ }
678
+ else if (this.media.canPlayType) {
679
+ return 'html5';
680
+ }
681
+ else {
682
+ this.error = 'This browser does not support the available media file.';
683
+ return null;
684
+ }
685
+ };
686
+
687
+ AblePlayer.prototype.jwCanPlay = function() {
688
+ // Determine whether there are media files that JW supports
689
+ var i, sourceType, $firstItem;
690
+
691
+ if (this.$sources.length > 0) { // this media has one or more <source> elements
692
+ for (i = 0; i < this.$sources.length; i++) {
693
+ sourceType = this.$sources[i].getAttribute('type');
694
+ if ((this.mediaType === 'video' && sourceType === 'video/mp4') ||
695
+ (this.mediaType === 'audio' && sourceType === 'audio/mpeg')) {
696
+ // JW Player can play this
697
+ return true;
698
+ }
699
+ }
700
+ }
701
+ // still here? That means there's no source that JW can play
702
+ // check for an mp3 or mp4 in a able-playlist
703
+ // TODO: Implement this more efficiently
704
+ // Playlist is initialized later in setupInstancePlaylist()
705
+ // but we can't wait for that...
706
+ if ($('.able-playlist')) {
707
+ // there's at least one playlist on this page
708
+ // get the first item from the first playlist
709
+ // if JW Player can play that one, assume it can play all items in all playlists
710
+ $firstItem = $('.able-playlist').eq(0).find('li').eq(0);
711
+ if (this.mediaType === 'audio') {
712
+ if ($firstItem.attr('data-mp3')) {
713
+ return true;
714
+ }
715
+ else if (this.mediaType === 'video') {
716
+ if ($firstItem.attr('data-mp4')) {
717
+ return true;
718
+ }
719
+ }
720
+ }
721
+ }
722
+ return false;
723
+ };
724
+
725
+ })(jQuery);