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,915 +1,822 @@
1
1
  (function ($) {
2
- // Set default variable values.
3
- AblePlayer.prototype.setDefaults = function () {
4
-
5
- this.playing = false; // will change to true after 'playing' event is triggered
6
- this.clickedPlay = false; // will change to true temporarily if user clicks 'play' (or pause)
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
- AblePlayer.prototype.getSvgData = function(button) {
123
-
124
- // returns array of values for creating <svg> tag for specified button
125
- // 0 = <svg> viewBox attribute
126
- // 1 = <path> d (description) attribute
127
- var svg = Array();
128
-
129
- switch (button) {
130
-
131
- case 'play':
132
- svg[0] = '0 0 16 20';
133
- svg[1] = 'M0 18.393v-16.429q0-0.29 0.184-0.402t0.441 0.033l14.821 8.237q0.257 0.145 0.257 0.346t-0.257 0.346l-14.821 8.237q-0.257 0.145-0.441 0.033t-0.184-0.402z';
134
- break;
135
-
136
- case 'pause':
137
- svg[0] = '0 0 20 20';
138
- svg[1] = 'M0 18.036v-15.714q0-0.29 0.212-0.502t0.502-0.212h5.714q0.29 0 0.502 0.212t0.212 0.502v15.714q0 0.29-0.212 0.502t-0.502 0.212h-5.714q-0.29 0-0.502-0.212t-0.212-0.502zM10 18.036v-15.714q0-0.29 0.212-0.502t0.502-0.212h5.714q0.29 0 0.502 0.212t0.212 0.502v15.714q0 0.29-0.212 0.502t-0.502 0.212h-5.714q-0.29 0-0.502-0.212t-0.212-0.502z';
139
- break;
140
-
141
- case 'stop':
142
- svg[0] = '0 0 20 20';
143
- svg[1] = 'M0 18.036v-15.714q0-0.29 0.212-0.502t0.502-0.212h15.714q0.29 0 0.502 0.212t0.212 0.502v15.714q0 0.29-0.212 0.502t-0.502 0.212h-15.714q-0.29 0-0.502-0.212t-0.212-0.502z';
144
- break;
145
-
146
- case 'restart':
147
- svg[0] = '0 0 20 20';
148
- svg[1] = 'M18 8h-6l2.243-2.243c-1.133-1.133-2.64-1.757-4.243-1.757s-3.109 0.624-4.243 1.757c-1.133 1.133-1.757 2.64-1.757 4.243s0.624 3.109 1.757 4.243c1.133 1.133 2.64 1.757 4.243 1.757s3.109-0.624 4.243-1.757c0.095-0.095 0.185-0.192 0.273-0.292l1.505 1.317c-1.466 1.674-3.62 2.732-6.020 2.732-4.418 0-8-3.582-8-8s3.582-8 8-8c2.209 0 4.209 0.896 5.656 2.344l2.344-2.344v6z';
149
- break;
150
-
151
- case 'rewind':
152
- svg[0] = '0 0 20 20';
153
- svg[1] = 'M11.25 3.125v6.25l6.25-6.25v13.75l-6.25-6.25v6.25l-6.875-6.875z';
154
- break;
155
-
156
- case 'forward':
157
- svg[0] = '0 0 20 20';
158
- svg[1] = 'M10 16.875v-6.25l-6.25 6.25v-13.75l6.25 6.25v-6.25l6.875 6.875z';
159
- break;
160
-
161
- case 'previous':
162
- svg[0] = '0 0 20 20';
163
- svg[1] = 'M5 17.5v-15h2.5v6.875l6.25-6.25v13.75l-6.25-6.25v6.875z';
164
- break;
165
-
166
- case 'next':
167
- svg[0] = '0 0 20 20';
168
- svg[1] = 'M15 2.5v15h-2.5v-6.875l-6.25 6.25v-13.75l6.25 6.25v-6.875z';
169
- break;
170
-
171
- case 'slower':
172
- svg[0] = '0 0 20 20';
173
- svg[1] = 'M0 7.321q0-0.29 0.212-0.502t0.502-0.212h10q0.29 0 0.502 0.212t0.212 0.502-0.212 0.502l-5 5q-0.212 0.212-0.502 0.212t-0.502-0.212l-5-5q-0.212-0.212-0.212-0.502z';
174
- break;
175
-
176
- case 'faster':
177
- svg[0] = '0 0 11 20';
178
- svg[1] = 'M0 12.411q0-0.29 0.212-0.502l5-5q0.212-0.212 0.502-0.212t0.502 0.212l5 5q0.212 0.212 0.212 0.502t-0.212 0.502-0.502 0.212h-10q-0.29 0-0.502-0.212t-0.212-0.502z';
179
- break;
180
-
181
- case 'turtle':
182
- svg[0] = '0 0 20 20';
183
- svg[1] = 'M17.212 3.846c-0.281-0.014-0.549 0.025-0.817 0.144-1.218 0.542-1.662 2.708-2.163 3.942-1.207 2.972-7.090 4.619-11.755 5.216-0.887 0.114-1.749 0.74-2.428 1.466 0.82-0.284 2.126-0.297 2.74 0.144 0.007 0.488-0.376 1.062-0.625 1.37-0.404 0.5-0.398 0.793 0.12 0.793 0.473 0 0.752 0.007 1.635 0 0.393-0.003 0.618-0.16 1.49-1.49 3.592 0.718 5.986-0.264 5.986-0.264s0.407 1.755 1.418 1.755h1.49c0.633 0 0.667-0.331 0.625-0.433-0.448-1.082-0.68-1.873-0.769-2.5-0.263-1.857 0.657-3.836 2.524-5.457 0.585 0.986 2.253 0.845 2.909-0.096s0.446-2.268-0.192-3.221c-0.49-0.732-1.345-1.327-2.188-1.37zM8.221 4.663c-0.722-0.016-1.536 0.111-2.5 0.409-4.211 1.302-4.177 4.951-3.51 5.745 0 0-0.955 0.479-0.409 1.274 0.448 0.652 3.139 0.191 5.409-0.529s4.226-1.793 5.312-2.692c0.948-0.785 0.551-2.106-0.505-1.947-0.494-0.98-1.632-2.212-3.798-2.26zM18.846 5.962c0.325 0 0.577 0.252 0.577 0.577s-0.252 0.577-0.577 0.577c-0.325 0-0.577-0.252-0.577-0.577s0.252-0.577 0.577-0.577z';
184
- break;
185
-
186
- case 'rabbit':
187
- svg[0] = '0 0 20 20';
188
- svg[1] = 'M10.817 0c-2.248 0-1.586 0.525-1.154 0.505 1.551-0.072 5.199 0.044 6.851 2.428 0 0-1.022-2.933-5.697-2.933zM10.529 0.769c-2.572 0-2.837 0.51-2.837 1.106 0 0.545 1.526 0.836 2.524 0.697 2.778-0.386 4.231-0.12 5.264 0.865-1.010 0.779-0.75 1.401-1.274 1.851-1.093 0.941-2.643-0.673-4.976-0.673-2.496 0-4.712 1.92-4.712 4.76-0.157-0.537-0.769-0.913-1.442-0.913-0.974 0-1.514 0.637-1.514 1.49 0 0.769 1.13 1.791 2.861 0.938 0.499 1.208 2.265 1.364 2.452 1.418 0.538 0.154 1.875 0.098 1.875 0.865 0 0.794-1.034 1.094-1.034 1.707 0 1.070 1.758 0.873 2.284 1.034 1.683 0.517 2.103 1.214 2.788 2.212 0.771 1.122 2.572 1.408 2.572 0.625 0-3.185-4.413-4.126-4.399-4.135 0.608-0.382 2.139-1.397 2.139-3.534 0-1.295-0.703-2.256-1.755-2.861 1.256 0.094 2.572 1.205 2.572 2.74 0 1.877-0.653 2.823-0.769 2.957 1.975-1.158 3.193-3.91 3.029-6.37 0.61 0.401 1.27 0.577 1.971 0.625 0.751 0.052 1.475-0.225 1.635-0.529 0.38-0.723 0.162-2.321-0.12-2.837-0.763-1.392-2.236-1.73-3.606-1.683-1.202-1.671-3.812-2.356-5.529-2.356zM1.37 3.077l-0.553 1.538h3.726c0.521-0.576 1.541-1.207 2.284-1.538h-5.457zM18.846 5.192c0.325 0 0.577 0.252 0.577 0.577s-0.252 0.577-0.577 0.577c-0.325 0-0.577-0.252-0.577-0.577s0.252-0.577 0.577-0.577zM0.553 5.385l-0.553 1.538h3.197c0.26-0.824 0.586-1.328 0.769-1.538h-3.413z';
189
- break;
190
-
191
- case 'ellipsis':
192
- svg[0] = '0 0 20 20';
193
- svg[1] = 'M10.001 7.8c-1.215 0-2.201 0.985-2.201 2.2s0.986 2.2 2.201 2.2c1.215 0 2.199-0.985 2.199-2.2s-0.984-2.2-2.199-2.2zM3.001 7.8c-1.215 0-2.201 0.985-2.201 2.2s0.986 2.2 2.201 2.2c1.215 0 2.199-0.986 2.199-2.2s-0.984-2.2-2.199-2.2zM17.001 7.8c-1.215 0-2.201 0.985-2.201 2.2s0.986 2.2 2.201 2.2c1.215 0 2.199-0.985 2.199-2.2s-0.984-2.2-2.199-2.2z';
194
- break;
195
-
196
- case 'pipe':
197
- svg[0] = '0 0 20 20';
198
- svg[1] = 'M10.15 0.179h0.623c0.069 0 0.127 0.114 0.127 0.253v19.494c0 0.139-0.057 0.253-0.127 0.253h-1.247c-0.069 0-0.126-0.114-0.126-0.253v-19.494c0-0.139 0.057-0.253 0.126-0.253h0.623z';
199
- break;
200
-
201
- case 'captions':
202
- svg[0] = '0 0 20 20';
203
- svg[1] = 'M0.033 3.624h19.933v12.956h-19.933v-12.956zM18.098 10.045c-0.025-2.264-0.124-3.251-0.743-3.948-0.112-0.151-0.322-0.236-0.496-0.344-0.606-0.386-3.465-0.526-6.782-0.526s-6.313 0.14-6.907 0.526c-0.185 0.108-0.396 0.193-0.519 0.344-0.607 0.697-0.693 1.684-0.731 3.948 0.037 2.265 0.124 3.252 0.731 3.949 0.124 0.161 0.335 0.236 0.519 0.344 0.594 0.396 3.59 0.526 6.907 0.547 3.317-0.022 6.176-0.151 6.782-0.547 0.174-0.108 0.384-0.183 0.496-0.344 0.619-0.697 0.717-1.684 0.743-3.949v0 0zM9.689 9.281c-0.168-1.77-1.253-2.813-3.196-2.813-1.773 0-3.168 1.387-3.168 3.617 0 2.239 1.271 3.636 3.372 3.636 1.676 0 2.851-1.071 3.035-2.852h-2.003c-0.079 0.661-0.397 1.168-1.068 1.168-1.059 0-1.253-0.91-1.253-1.876 0-1.33 0.442-2.010 1.174-2.010 0.653 0 1.068 0.412 1.13 1.129h1.977zM16.607 9.281c-0.167-1.77-1.252-2.813-3.194-2.813-1.773 0-3.168 1.387-3.168 3.617 0 2.239 1.271 3.636 3.372 3.636 1.676 0 2.851-1.071 3.035-2.852h-2.003c-0.079 0.661-0.397 1.168-1.068 1.168-1.059 0-1.253-0.91-1.253-1.876 0-1.33 0.441-2.010 1.174-2.010 0.653 0 1.068 0.412 1.13 1.129h1.976z';
204
- break;
205
-
206
- case 'descriptions':
207
- svg[0] = '0 0 20 20';
208
- svg[1] = 'M17.623 3.57h-1.555c1.754 1.736 2.763 4.106 2.763 6.572 0 2.191-0.788 4.286-2.189 5.943h1.484c1.247-1.704 1.945-3.792 1.945-5.943-0-2.418-0.886-4.754-2.447-6.572v0zM14.449 3.57h-1.55c1.749 1.736 2.757 4.106 2.757 6.572 0 2.191-0.788 4.286-2.187 5.943h1.476c1.258-1.704 1.951-3.792 1.951-5.943-0-2.418-0.884-4.754-2.447-6.572v0zM11.269 3.57h-1.542c1.752 1.736 2.752 4.106 2.752 6.572 0 2.191-0.791 4.286-2.181 5.943h1.473c1.258-1.704 1.945-3.792 1.945-5.943 0-2.418-0.876-4.754-2.447-6.572v0zM10.24 9.857c0 3.459-2.826 6.265-6.303 6.265v0.011h-3.867v-12.555h3.896c3.477 0 6.274 2.806 6.274 6.279v0zM6.944 9.857c0-1.842-1.492-3.338-3.349-3.338h-0.876v6.686h0.876c1.858 0 3.349-1.498 3.349-3.348v0z';
209
- break;
210
-
211
- case 'sign':
212
- svg[0] = '0 0 20 20';
213
- svg[1] = 'M10.954 10.307c0.378 0.302 0.569 1.202 0.564 1.193 0.697 0.221 1.136 0.682 1.136 0.682 1.070-0.596 1.094-0.326 1.558-0.682 0.383-0.263 0.366-0.344 0.567-1.048 0.187-0.572-0.476-0.518-1.021-1.558-0.95 0.358-1.463 0.196-1.784 0.167-0.145-0.020-0.12 0.562-1.021 1.247zM14.409 17.196c-0.133 0.182-0.196 0.218-0.363 0.454-0.28 0.361 0.076 0.906 0.253 0.82 0.206-0.076 0.341-0.488 0.567-0.623 0.115-0.061 0.422-0.513 0.709-0.82 0.211-0.238 0.363-0.344 0.564-0.594 0.341-0.422 0.412-0.744 0.709-1.193 0.184-0.236 0.312-0.307 0.481-0.594 0.886-1.679 0.628-2.432 1.475-3.629 0.26-0.353 0.552-0.442 0.964-0.653 0.383-2.793-0.888-4.356-0.879-4.361-1.067 0.623-1.644 0.879-2.751 0.82-0.417-0.005-0.636-0.182-1.048-0.145-0.385 0.015-0.582 0.159-0.964 0.29-0.589 0.182-0.91 0.344-1.529 0.535-0.393 0.11-0.643 0.115-1.050 0.255-0.348 0.147-0.182 0.029-0.427 0.312-0.317 0.348-0.238 0.623-0.535 1.222-0.371 0.785-0.326 0.891-0.115 0.987-0.14 0.402-0.174 0.672-0.14 1.107 0.039 0.331-0.101 0.562 0.255 0.825 0.483 0.361 1.499 1.205 1.757 1.217 0.39-0.012 1.521 0.029 2.096-0.368 0.13-0.081 0.167-0.162 0.056 0.145-0.022 0.037-1.433 1.136-1.585 1.131-1.794 0.056-1.193 0.157-1.303 0.115-0.091 0-0.955-1.055-1.477-0.682-0.196 0.12-0.287 0.236-0.363 0.452 0.066 0.137 0.383 0.358 0.675 0.54 0.422 0.27 0.461 0.552 0.881 0.653 0.513 0.115 1.060 0.039 1.387 0.081 0.125 0.034 1.256-0.297 1.961-0.675 0.65-0.336-0.898 0.648-1.276 1.131-1.141 0.358-0.82 0.373-1.362 0.483-0.503 0.115-0.479 0.086-0.822 0.196-0.356 0.086-0.648 0.572-0.312 0.825 0.201 0.167 0.827-0.066 1.445-0.086 0.275-0.005 1.391-0.518 1.644-0.653 0.633-0.339 1.099-0.81 1.472-1.077 0.518-0.361-0.584 0.991-1.050 1.558zM8.855 9.799c-0.378-0.312-0.569-1.212-0.564-1.217-0.697-0.206-1.136-0.667-1.136-0.653-1.070 0.582-1.099 0.312-1.558 0.653-0.388 0.277-0.366 0.363-0.567 1.045-0.187 0.594 0.471 0.535 1.021 1.561 0.95-0.344 1.463-0.182 1.784-0.142 0.145 0.010 0.12-0.572 1.021-1.247zM5.4 2.911c0.133-0.191 0.196-0.228 0.368-0.454 0.27-0.371-0.081-0.915-0.253-0.849-0.211 0.096-0.346 0.508-0.599 0.653-0.093 0.052-0.4 0.503-0.682 0.82-0.211 0.228-0.363 0.334-0.564 0.599-0.346 0.407-0.412 0.729-0.709 1.161-0.184 0.258-0.317 0.324-0.481 0.621-0.886 1.669-0.631 2.422-1.475 3.6-0.26 0.38-0.552 0.461-0.964 0.682-0.383 2.788 0.883 4.346 0.879 4.336 1.068-0.609 1.639-0.861 2.751-0.825 0.417 0.025 0.636 0.201 1.048 0.174 0.385-0.025 0.582-0.169 0.964-0.285 0.589-0.196 0.91-0.358 1.499-0.54 0.422-0.12 0.672-0.125 1.080-0.285 0.348-0.128 0.182-0.010 0.427-0.282 0.312-0.358 0.238-0.633 0.508-1.217 0.398-0.8 0.353-0.906 0.142-0.991 0.135-0.412 0.174-0.677 0.14-1.107-0.044-0.336 0.101-0.572-0.255-0.82-0.483-0.375-1.499-1.22-1.752-1.222-0.395 0.002-1.526-0.039-2.101 0.339-0.13 0.101-0.167 0.182-0.056-0.11 0.022-0.052 1.433-1.148 1.585-1.163 1.794-0.039 1.193-0.14 1.303-0.088 0.091-0.007 0.955 1.045 1.477 0.682 0.191-0.13 0.287-0.245 0.368-0.452-0.071-0.147-0.388-0.368-0.68-0.537-0.422-0.282-0.464-0.564-0.881-0.655-0.513-0.125-1.065-0.049-1.387-0.11-0.125-0.015-1.256 0.317-1.956 0.68-0.66 0.351 0.893-0.631 1.276-1.136 1.136-0.339 0.81-0.353 1.36-0.479 0.501-0.101 0.476-0.071 0.82-0.172 0.351-0.096 0.648-0.577 0.312-0.849-0.206-0.152-0.827 0.081-1.44 0.086-0.28 0.020-1.396 0.533-1.649 0.677-0.633 0.329-1.099 0.8-1.472 1.048-0.523 0.38 0.584-0.967 1.050-1.529z';
214
- break;
215
-
216
- case 'mute':
217
- svg[0] = '0 0 20 20';
218
- svg[1] = 'M7.839 1.536c0.501-0.501 0.911-0.331 0.911 0.378v16.172c0 0.709-0.41 0.879-0.911 0.378l-4.714-4.713h-3.125v-7.5h3.125l4.714-4.714zM18.75 12.093v1.657h-1.657l-2.093-2.093-2.093 2.093h-1.657v-1.657l2.093-2.093-2.093-2.093v-1.657h1.657l2.093 2.093 2.093-2.093h1.657v1.657l-2.093 2.093z';
219
- break;
220
-
221
- case 'volume-mute':
222
- svg[0] = '0 0 20 20';
223
- svg[1] = 'M10.723 14.473c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 1.584-1.584 1.584-4.161 0-5.745-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c2.315 2.315 2.315 6.082 0 8.397-0.183 0.183-0.423 0.275-0.663 0.275zM7.839 1.536c0.501-0.501 0.911-0.331 0.911 0.378v16.172c0 0.709-0.41 0.879-0.911 0.378l-4.714-4.713h-3.125v-7.5h3.125l4.714-4.714z';
224
- break;
225
-
226
- case 'volume-medium':
227
- svg[0] = '0 0 20 20';
228
- svg[1] = 'M14.053 16.241c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 2.559-2.559 2.559-6.722 0-9.281-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c1.594 1.594 2.471 3.712 2.471 5.966s-0.878 4.373-2.471 5.966c-0.183 0.183-0.423 0.275-0.663 0.275zM10.723 14.473c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 1.584-1.584 1.584-4.161 0-5.745-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c2.315 2.315 2.315 6.082 0 8.397-0.183 0.183-0.423 0.275-0.663 0.275zM7.839 1.536c0.501-0.501 0.911-0.331 0.911 0.378v16.172c0 0.709-0.41 0.879-0.911 0.378l-4.714-4.713h-3.125v-7.5h3.125l4.714-4.714z';
229
- break;
230
-
231
- case 'volume-loud':
232
- svg[0] = '0 0 21 20';
233
- svg[1] = 'M17.384 18.009c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 1.712-1.712 2.654-3.988 2.654-6.408s-0.943-4.696-2.654-6.408c-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c2.066 2.066 3.204 4.813 3.204 7.734s-1.138 5.668-3.204 7.734c-0.183 0.183-0.423 0.275-0.663 0.275zM14.053 16.241c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 2.559-2.559 2.559-6.722 0-9.281-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c1.594 1.594 2.471 3.712 2.471 5.966s-0.878 4.373-2.471 5.966c-0.183 0.183-0.423 0.275-0.663 0.275zM10.723 14.473c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 1.584-1.584 1.584-4.161 0-5.745-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c2.315 2.315 2.315 6.082 0 8.397-0.183 0.183-0.423 0.275-0.663 0.275zM7.839 1.536c0.501-0.501 0.911-0.331 0.911 0.378v16.172c0 0.709-0.41 0.879-0.911 0.378l-4.714-4.713h-3.125v-7.5h3.125l4.714-4.714z';
234
- break;
235
-
236
- case 'chapters':
237
- svg[0] = '0 0 20 20';
238
- svg[1] = 'M5 2.5v17.5l6.25-6.25 6.25 6.25v-17.5zM15 0h-12.5v17.5l1.25-1.25v-15h11.25z';
239
- break;
240
-
241
- case 'transcript':
242
- svg[0] = '0 0 20 20';
243
- svg[1] = 'M0 19.107v-17.857q0-0.446 0.313-0.759t0.759-0.313h8.929v6.071q0 0.446 0.313 0.759t0.759 0.313h6.071v11.786q0 0.446-0.313 0.759t-0.759 0.312h-15q-0.446 0-0.759-0.313t-0.313-0.759zM4.286 15.536q0 0.156 0.1 0.257t0.257 0.1h7.857q0.156 0 0.257-0.1t0.1-0.257v-0.714q0-0.156-0.1-0.257t-0.257-0.1h-7.857q-0.156 0-0.257 0.1t-0.1 0.257v0.714zM4.286 12.679q0 0.156 0.1 0.257t0.257 0.1h7.857q0.156 0 0.257-0.1t0.1-0.257v-0.714q0-0.156-0.1-0.257t-0.257-0.1h-7.857q-0.156 0-0.257 0.1t-0.1 0.257v0.714zM4.286 9.821q0 0.156 0.1 0.257t0.257 0.1h7.857q0.156 0 0.257-0.1t0.1-0.257v-0.714q0-0.156-0.1-0.257t-0.257-0.1h-7.857q-0.156 0-0.257 0.1t-0.1 0.257v0.714zM11.429 5.893v-5.268q0.246 0.156 0.402 0.313l4.554 4.554q0.156 0.156 0.313 0.402h-5.268z';
244
- break;
245
-
246
- case 'preferences':
247
- svg[0] = '0 0 20 20';
248
- svg[1] = 'M18.238 11.919c-1.049-1.817-0.418-4.147 1.409-5.205l-1.965-3.404c-0.562 0.329-1.214 0.518-1.911 0.518-2.1 0-3.803-1.714-3.803-3.828h-3.931c0.005 0.653-0.158 1.314-0.507 1.919-1.049 1.818-3.382 2.436-5.212 1.382l-1.965 3.404c0.566 0.322 1.056 0.793 1.404 1.396 1.048 1.815 0.42 4.139-1.401 5.2l1.965 3.404c0.56-0.326 1.209-0.513 1.902-0.513 2.094 0 3.792 1.703 3.803 3.808h3.931c-0.002-0.646 0.162-1.3 0.507-1.899 1.048-1.815 3.375-2.433 5.203-1.387l1.965-3.404c-0.562-0.322-1.049-0.791-1.395-1.391zM10 14.049c-2.236 0-4.050-1.813-4.050-4.049s1.813-4.049 4.050-4.049 4.049 1.813 4.049 4.049c-0 2.237-1.813 4.049-4.049 4.049z';
249
- break;
250
-
251
- case 'close':
252
- svg[0] = '0 0 16 20';
253
- svg[1] = 'M1.228 14.933q0-0.446 0.312-0.759l3.281-3.281-3.281-3.281q-0.313-0.313-0.313-0.759t0.313-0.759l1.518-1.518q0.313-0.313 0.759-0.313t0.759 0.313l3.281 3.281 3.281-3.281q0.313-0.313 0.759-0.313t0.759 0.313l1.518 1.518q0.313 0.313 0.313 0.759t-0.313 0.759l-3.281 3.281 3.281 3.281q0.313 0.313 0.313 0.759t-0.313 0.759l-1.518 1.518q-0.313 0.313-0.759 0.313t-0.759-0.313l-3.281-3.281-3.281 3.281q-0.313 0.313-0.759 0.313t-0.759-0.313l-1.518-1.518q-0.313-0.313-0.313-0.759z';
254
- break;
255
-
256
- case 'fullscreen-expand':
257
- svg[0] = '0 0 20 20';
258
- svg[1] = 'M0 18.036v-5q0-0.29 0.212-0.502t0.502-0.212 0.502 0.212l1.607 1.607 3.705-3.705q0.112-0.112 0.257-0.112t0.257 0.112l1.272 1.272q0.112 0.112 0.112 0.257t-0.112 0.257l-3.705 3.705 1.607 1.607q0.212 0.212 0.212 0.502t-0.212 0.502-0.502 0.212h-5q-0.29 0-0.502-0.212t-0.212-0.502zM8.717 8.393q0-0.145 0.112-0.257l3.705-3.705-1.607-1.607q-0.212-0.212-0.212-0.502t0.212-0.502 0.502-0.212h5q0.29 0 0.502 0.212t0.212 0.502v5q0 0.29-0.212 0.502t-0.502 0.212-0.502-0.212l-1.607-1.607-3.705 3.705q-0.112 0.112-0.257 0.112t-0.257-0.112l-1.272-1.272q-0.112-0.112-0.112-0.257z';
259
- break;
260
-
261
- case 'fullscreen-collapse':
262
- svg[0] = '0 0 20 20';
263
- svg[1] = 'M0.145 16.964q0-0.145 0.112-0.257l3.705-3.705-1.607-1.607q-0.212-0.212-0.212-0.502t0.212-0.502 0.502-0.212h5q0.29 0 0.502 0.212t0.212 0.502v5q0 0.29-0.212 0.502t-0.502 0.212-0.502-0.212l-1.607-1.607-3.705 3.705q-0.112 0.112-0.257 0.112t-0.257-0.112l-1.272-1.272q-0.112-0.112-0.112-0.257zM8.571 9.464v-5q0-0.29 0.212-0.502t0.502-0.212 0.502 0.212l1.607 1.607 3.705-3.705q0.112-0.112 0.257-0.112t0.257 0.112l1.272 1.272q0.112 0.112 0.112 0.257t-0.112 0.257l-3.705 3.705 1.607 1.607q0.212 0.212 0.212 0.502t-0.212 0.502-0.502 0.212h-5q-0.29 0-0.502-0.212t-0.212-0.502z';
264
- break;
265
-
266
- case 'help':
267
- svg[0] = '0 0 11 20';
268
- svg[1] = 'M0.577 6.317q-0.028-0.167 0.061-0.313 1.786-2.969 5.179-2.969 0.893 0 1.797 0.346t1.629 0.926 1.183 1.423 0.458 1.769q0 0.603-0.173 1.127t-0.391 0.854-0.614 0.664-0.642 0.485-0.681 0.396q-0.458 0.257-0.765 0.725t-0.307 0.748q0 0.19-0.134 0.363t-0.313 0.173h-2.679q-0.167 0-0.285-0.206t-0.117-0.419v-0.502q0-0.926 0.725-1.747t1.596-1.211q0.658-0.301 0.938-0.625t0.279-0.848q0-0.469-0.519-0.826t-1.2-0.357q-0.725 0-1.205 0.324-0.391 0.279-1.194 1.283-0.145 0.179-0.346 0.179-0.134 0-0.279-0.089l-1.83-1.395q-0.145-0.112-0.173-0.279zM3.786 16.875v-2.679q0-0.179 0.134-0.313t0.313-0.134h2.679q0.179 0 0.313 0.134t0.134 0.313v2.679q0 0.179-0.134 0.313t-0.313 0.134h-2.679q-0.179 0-0.313-0.134t-0.134-0.313z';
269
- break;
270
- }
271
-
272
- return svg;
273
- };
274
-
275
- // Initialize player based on data on page.
276
- // This sets some variables, but does not modify anything. Safe to call multiple times.
277
- // Can call again after updating this.media so long as new media element has the same ID.
278
- AblePlayer.prototype.reinitialize = function () {
279
-
280
- var deferred, promise, thisObj, errorMsg, srcFile;
281
-
282
- deferred = new $.Deferred();
283
- promise = deferred.promise();
284
- thisObj = this;
285
-
286
- // if F12 Developer Tools aren't open in IE (through 9, no longer a problen in IE10)
287
- // console.log causes an error - can't use debug without a console to log messages to
288
- if (! window.console) {
289
- this.debug = false;
290
- }
291
-
292
- this.startedPlaying = false;
293
- // TODO: Move this setting to cookie.
294
- this.autoScrollTranscript = true;
295
- //this.autoScrollTranscript = this.getCookie(autoScrollTranscript); // (doesn't work)
296
-
297
- // Bootstrap from this.media possibly being an ID or other selector.
298
- this.$media = $(this.media).first();
299
- this.media = this.$media[0];
300
-
301
- // Set media type to 'audio' or 'video'; this determines some of the behavior of player creation.
302
- if (this.$media.is('audio')) {
303
- this.mediaType = 'audio';
304
- }
305
- else if (this.$media.is('video')) {
306
- this.mediaType = 'video';
307
- }
308
- else {
309
- // Able Player was initialized with some element other than <video> or <audio>
310
- this.provideFallback();
311
- deferred.fail();
312
- return promise;
313
- }
314
-
315
- this.$sources = this.$media.find('source');
316
-
317
- this.player = this.getPlayer();
318
- if (!this.player) {
319
- // an error was generated in getPlayer()
320
- this.provideFallback();
321
- }
322
- this.setIconType();
323
- this.setDimensions();
324
-
325
- deferred.resolve();
326
- return promise;
327
- };
328
-
329
- AblePlayer.prototype.setDimensions = function() {
330
- // if media element includes width and height attributes,
331
- // use these to set the max-width and max-height of the player
332
- if (this.$media.attr('width') && this.$media.attr('height')) {
333
- this.playerMaxWidth = parseInt(this.$media.attr('width'), 10);
334
- this.playerMaxHeight = parseInt(this.$media.attr('height'), 10);
335
- }
336
- else if (this.$media.attr('width')) {
337
- // media element includes a width attribute, but not height
338
- this.playerMaxWidth = parseInt(this.$media.attr('width'), 10);
339
- }
340
- else {
341
- // set width to width of #player
342
- // don't set height though; YouTube will automatically set that to match width
343
- this.playerMaxWidth = this.$media.parent().width();
344
- this.playerMaxHeight = this.getMatchingHeight(this.playerMaxWidth);
345
- }
346
- // override width and height attributes with in-line CSS to make video responsive
347
- this.$media.css({
348
- 'width': '100%',
349
- 'height': 'auto'
350
- });
351
- };
352
-
353
- AblePlayer.prototype.getMatchingHeight = function(width) {
354
-
355
- // returns likely height for a video, given width
356
- // These calculations assume 16:9 aspect ratio (the YouTube standard)
357
- // Videos recorded in other resolutions will be sized to fit, with black bars on each side
358
- // This function is only called if the <video> element does not have width and height attributes
359
-
360
- var widths, heights, closestWidth, closestIndex, closestHeight, height;
361
-
362
- widths = [ 3840, 2560, 1920, 1280, 854, 640, 426 ];
363
- heights = [ 2160, 1440, 1080, 720, 480, 360, 240 ];
364
- closestWidth = null;
365
- closestIndex = null;
366
-
367
- $.each(widths, function(index){
368
- if (closestWidth == null || Math.abs(this - width) < Math.abs(closestWidth - width)) {
369
- closestWidth = this;
370
- closestIndex = index;
371
- }
372
- });
373
- closestHeight = heights[closestIndex];
374
- this.aspectRatio = closestWidth / closestHeight;
375
- height = Math.round(width / this.aspectRatio);
376
- return height;
377
- };
378
-
379
- AblePlayer.prototype.setIconType = function() {
380
-
381
- // returns either "svg", "font" or "image" (in descending order of preference)
382
- // Test for support of each type. If not supported, test the next type.
383
- // last resort is image icons
384
-
385
- var $tempButton, $testButton, controllerFont;
386
-
387
- if (this.forceIconType) {
388
- // use value specified in data-icon-type
389
- return false;
390
- }
391
-
392
- // test for SVG support
393
- // Test this method widely; failed as expected on IE8 and below
394
- // https://stackoverflow.com/a/27568129/744281
395
- if (!!(document.createElementNS && document.createElementNS('http://www.w3.org/2000/svg','svg').createSVGRect)) {
396
- // browser supports SVG
397
- this.iconType = 'svg';
398
- }
399
- else {
400
- // browser does NOT support SVG
401
- // test whether browser can support icon fonts, and whether user has overriding the default style sheet
402
- // which could cause problems with proper display of the icon fonts
403
- if (window.getComputedStyle) {
404
-
405
- // webkit doesn't return calculated styles unless element has been added to the DOM
406
- // and is visible (note: visibly clipped is considered "visible")
407
- // use playpauseButton for font-family test if it exists; otherwise must create a new temp button
408
- if ($('span.icon-play').length) {
409
- $testButton = $('span.icon-play');
410
- }
411
- else {
412
- $tempButton = $('<span>',{
413
- 'class': 'icon-play able-clipped'
414
- });
415
- $('body').append($tempButton);
416
- $testButton = $tempButton;
417
- }
418
-
419
- // the following retrieves the computed value of font-family
420
- // tested in Firefox 45.x with "Allow pages to choose their own fonts" unchecked - works!
421
- // tested in Chrome 49.x with Font Changer plugin - works!
422
- // tested in IE with user-defined style sheet enables - works!
423
- // It does NOT account for users who have "ignore font styles on web pages" checked in IE
424
- // There is no known way to check for that ???
425
- controllerFont = window.getComputedStyle($testButton.get(0), null).getPropertyValue('font-family');
426
- if (typeof controllerFont !== 'undefined') {
427
- if (controllerFont.indexOf('able') !== -1) {
428
- this.iconType = 'font';
429
- }
430
- else {
431
- this.iconType = 'image';
432
- }
433
- }
434
- else {
435
- // couldn't get computed font-family; use images to be safe
436
- this.iconType = 'image';
437
- }
438
- }
439
- else { // window.getComputedStyle is not supported (IE 8 and earlier)
440
- // No known way to detect computed font
441
- // The following retrieves the value from the style sheet, not the computed font
442
- // controllerFont = $tempButton.get(0).currentStyle.fontFamily;
443
- // It will therefore return "able", even if the user is overriding that with a custom style sheet
444
- // To be safe, use images
445
- this.iconType = 'image';
446
- }
447
- if (this.debug) {
448
- console.log('Using ' + this.iconType + 's for player controls');
449
- }
450
- if (typeof $tempButton !== 'undefined') {
451
- $tempButton.remove();
452
- }
453
- }
454
- };
455
-
456
- // Perform one-time setup for this instance of player; called after player is first initialized.
457
- AblePlayer.prototype.setupInstance = function () {
458
- var deferred = new $.Deferred();
459
- var promise = deferred.promise();
460
-
461
- if (this.$media.attr('id')) {
462
- this.mediaId = this.$media.attr('id');
463
- }
464
- else {
465
- // Ensure the base media element always has an ID.
466
- this.mediaId = "ableMediaId_" + this.ableIndex;
467
- this.$media.attr('id', this.mediaId);
468
- }
469
- // get playlist for this media element
470
- this.setupInstancePlaylist();
471
-
472
- deferred.resolve();
473
- return promise;
474
- };
475
-
476
- AblePlayer.prototype.setupInstancePlaylist = function() {
477
- // find a matching playlist and set this.hasPlaylist
478
- // if there is one, also set this.$playlist, this.playlistIndex, & this.playlistEmbed
479
- var thisObj = this;
480
-
481
- this.hasPlaylist = false; // will change to true if a matching playlist is found
482
-
483
- $('.able-playlist').each(function() {
484
- if ($(this).data('player') === thisObj.mediaId) {
485
- // this is the playlist for the current player
486
- thisObj.hasPlaylist = true;
487
- // If using an embedded player, we'll replace $playlist with the clone later.
488
- thisObj.$playlist = $(this).find('li');
489
- // add tabindex to each list item
490
- $(this).find('li').attr('tabindex', '0');
491
- thisObj.playlistIndex = 0;
492
- var dataEmbedded = $(this).data('embedded');
493
- if (typeof dataEmbedded !== 'undefined' && dataEmbedded !== false) {
494
- // embed playlist within player
495
- thisObj.playlistEmbed = true;
496
- }
497
- else {
498
- thisObj.playlistEmbed = false;
499
- }
500
- }
501
- });
502
-
503
- if (this.hasPlaylist && this.loop) {
504
- // browser will loop the current track in the playlist, rather than the playlist
505
- // therefore, need to remove loop attribute from media element
506
- // but keep this.loop as true and handle the playlist looping ourselves
507
- this.media.removeAttribute('loop');
508
- }
509
-
510
- if (this.hasPlaylist && this.playlistEmbed) {
511
- // Copy the playlist out of the dom, so we can reinject when we build the player.
512
- var parent = this.$playlist.parent();
513
- this.$playlistDom = parent.clone();
514
- parent.remove();
515
- }
516
- };
517
-
518
- // Creates the appropriate player for the current source.
519
- AblePlayer.prototype.recreatePlayer = function () {
520
- var thisObj, prefsGroups, i;
521
- thisObj = this;
522
-
523
- // TODO: Ensure when recreating player that we carry over the mediaId
524
- if (!this.player) {
525
- console.log("Can't create player; no appropriate player type detected.");
526
- return;
527
- }
528
-
529
- this.loadCurrentPreferences();
530
-
531
- this.injectPlayerCode();
532
- this.initSignLanguage();
533
- this.setupTracks().then(function() {
534
-
535
- thisObj.setupAltCaptions().then(function() {
536
-
537
- if (thisObj.transcriptType === 'external' || thisObj.transcriptType === 'popup') {
538
- if (thisObj.captions.length <= 1) {
539
- // without captions/subtitles in multiple languages,
540
- // there is no need for a transcript language selector
541
- thisObj.$transcriptLanguageSelect.parent().remove();
542
- }
543
- }
544
-
545
- thisObj.initDescription();
546
- thisObj.initDefaultCaption();
547
-
548
- thisObj.initPlayer().then(function() { // initPlayer success
549
- thisObj.initializing = false;
550
-
551
- // setMediaAttributes() sets textTrack.mode to 'disabled' for all tracks
552
- // This tells browsers to ignore the text tracks so Able Player can handle them
553
- // However, timing is critical as browsers - especially Safari - tend to ignore this request
554
- // unless it's sent late in the intialization process.
555
- // If browsers ignore the request, the result is redundant captions
556
- thisObj.setMediaAttributes();
557
-
558
- // inject each of the hidden forms that will be accessed from the Preferences popup menu
559
- prefsGroups = thisObj.getPreferencesGroups();
560
- for (i = 0; i < prefsGroups.length; i++) {
561
- thisObj.injectPrefsForm(prefsGroups[i]);
562
- }
563
- thisObj.setupPopups();
564
- thisObj.updateCaption();
565
- thisObj.updateTranscript();
566
- thisObj.injectVTS();
567
- if (thisObj.chaptersDivLocation) {
568
- thisObj.populateChaptersDiv();
569
- }
570
- thisObj.showSearchResults();
571
- },
572
- function() { // initPlayer fail
573
- thisObj.provideFallback();
574
- }
575
- );
576
- });
577
- });
578
- };
579
-
580
- AblePlayer.prototype.initPlayer = function () {
581
-
582
- var thisObj = this;
583
- var playerPromise;
584
-
585
- // First run player specific initialization.
586
- if (this.player === 'html5') {
587
- playerPromise = this.initHtml5Player();
588
- }
589
- else if (this.player === 'jw') {
590
- playerPromise = this.initJwPlayer();
591
- }
592
- else if (this.player === 'youtube') {
593
- playerPromise = this.initYouTubePlayer();
594
- }
595
-
596
- // After player specific initialization is done, run remaining general initialization.
597
- var deferred = new $.Deferred();
598
- var promise = deferred.promise();
599
- playerPromise.done(
600
- function () { // done/resolved
601
- if (thisObj.useFixedSeekInterval === false) {
602
- thisObj.setSeekInterval();
603
- }
604
- thisObj.addControls();
605
- thisObj.addEventListeners();
606
- // Calling these set functions also initializes some icons.
607
- if (thisObj.Volume) {
608
- thisObj.setMute(false);
609
- }
610
- thisObj.setFullscreen(false);
611
- thisObj.setVolume(thisObj.defaultVolume);
612
- thisObj.refreshControls();
613
-
614
- // Go ahead and load media, without user requesting it
615
- // Normally, we wait until user clicks play, rather than unnecessarily consume their bandwidth
616
- // Exceptions are if the video is intended to autostart or if running on iOS (a workaround for iOS issues)
617
- // TODO: Confirm that this is still necessary with iOS (this would added early, & I don't remember what the issues were)
618
- if (thisObj.player === 'html5' && (thisObj.isIOS() || thisObj.startTime > 0 || thisObj.autoplay)) {
619
- thisObj.$media[0].load();
620
- }
621
- deferred.resolve();
622
- }
623
- ).fail(function () { // failed
624
- deferred.reject();
625
- }
626
- );
627
-
628
- return promise;
629
- };
630
-
631
- AblePlayer.prototype.setSeekInterval = function () {
632
- // this function is only called if this.useFixedSeekInterval is false
633
- // if this.useChapterTimes, this is called as each new chapter is loaded
634
- // otherwise, it's called once, as the player is initialized
635
- var duration;
636
- this.seekInterval = this.defaultSeekInterval;
637
- if (this.useChapterTimes) {
638
- duration = this.chapterDuration;
639
- }
640
- else {
641
- duration = this.getDuration();
642
- }
643
- if (typeof duration === 'undefined' || duration < 1) {
644
- // no duration; just use default for now but keep trying until duration is available
645
- this.seekIntervalCalculated = false;
646
- return;
647
- }
648
- else {
649
- if (duration <= 20) {
650
- this.seekInterval = 5; // 4 steps max
651
- }
652
- else if (duration <= 30) {
653
- this.seekInterval = 6; // 5 steps max
654
- }
655
- else if (duration <= 40) {
656
- this.seekInterval = 8; // 5 steps max
657
- }
658
- else if (duration <= 100) {
659
- this.seekInterval = 10; // 10 steps max
660
- }
661
- else {
662
- // never more than 10 steps from start to end
663
- this.seekInterval = (duration / 10);
664
- }
665
- this.seekIntervalCalculated = true;
666
- }
667
- };
668
-
669
- AblePlayer.prototype.initDefaultCaption = function () {
670
-
671
- var captions, i;
672
-
673
- if (this.usingYouTubeCaptions) {
674
- captions = this.ytCaptions;
675
- }
676
- else {
677
- captions = this.captions;
678
- }
679
-
680
- if (captions.length > 0) {
681
- for (i=0; i<captions.length; i++) {
682
- if (captions[i].def === true) {
683
- this.captionLang = captions[i].language;
684
- this.selectedCaptions = captions[i];
685
- }
686
- }
687
- if (typeof this.captionLang === 'undefined') {
688
- // No caption track was flagged as default
689
- // find and use a caption language that matches the player language
690
- for (i=0; i<captions.length; i++) {
691
- if (captions[i].language === this.lang) {
692
- this.captionLang = captions[i].language;
693
- this.selectedCaptions = captions[i];
694
- }
695
- }
696
- }
697
- if (typeof this.captionLang === 'undefined') {
698
- // Still no matching caption track
699
- // just use the first track
700
- this.captionLang = captions[0].language;
701
- this.selectedCaptions = captions[0];
702
- }
703
- if (typeof this.captionLang !== 'undefined') {
704
- // reset transcript selected <option> to this.captionLang
705
- if (this.$transcriptLanguageSelect) {
706
- this.$transcriptLanguageSelect.find('option[lang=' + this.captionLang + ']').prop('selected',true);
707
- }
708
- // sync all other tracks to this same languge
709
- this.syncTrackLanguages('init',this.captionLang);
710
- }
711
- }
712
- };
713
-
714
- AblePlayer.prototype.initHtml5Player = function () {
715
- // Nothing special to do!
716
- var deferred = new $.Deferred();
717
- var promise = deferred.promise();
718
- deferred.resolve();
719
- return promise;
720
- };
721
-
722
- AblePlayer.prototype.initJwPlayer = function () {
723
-
724
- var jwHeight;
725
- var thisObj = this;
726
- var deferred = new $.Deferred();
727
- var promise = deferred.promise();
728
-
729
- $.ajax({
730
- async: false,
731
- url: this.fallbackPath + 'jwplayer.js',
732
- dataType: 'script',
733
- success: function( data, textStatus, jqXHR) {
734
- // add jwplayer key for selfhosted when fallback is activated
735
- if (thisObj.fallbackJwKey) {
736
- $('head').append(
737
- '<script type="text/javascript">jwplayer.key="' +
738
- thisObj.fallbackJwKey +
739
- '";</script>'
740
- );
741
- }
742
-
743
- // Successfully loaded the JW Player
744
- // add an id to div.able-media-container (JW Player needs this)
745
- thisObj.jwId = thisObj.mediaId + '_fallback';
746
- thisObj.$mediaContainer.attr('id', thisObj.jwId);
747
- if (thisObj.mediaType === 'audio') {
748
- // JW Player always shows its own controls if height <= 40
749
- // Must set height to 0 to hide them
750
- jwHeight = 0;
751
- }
752
- else {
753
- jwHeight = thisObj.playerHeight;
754
- }
755
- var sources = [];
756
- $.each(thisObj.$sources, function (ii, source) {
757
- sources.push({file: $(source).attr('src')});
758
- });
759
-
760
- var flashplayer = thisObj.fallbackPath + 'jwplayer.flash.swf';
761
- var html5player = thisObj.fallbackPath + 'jwplayer.html5.js';
762
-
763
- // Initializing JW Player with width:100% results in player that is either the size of the video
764
- // or scaled down to fit the container if container is smaller
765
- // After onReady event fires, actual dimensions will be collected for future use
766
- // in preserving the video ratio
767
- if (thisObj.mediaType === 'video') {
768
- thisObj.jwPlayer = jwplayer(thisObj.jwId).setup({
769
- playlist: [{
770
- image: thisObj.$media.attr('poster'),
771
- sources: sources
772
- }],
773
- flashplayer: flashplayer,
774
- html5player: html5player,
775
- controls: false,
776
- volume: thisObj.defaultVolume * 100,
777
- width: '100%',
778
- fallback: false,
779
- primary: 'flash',
780
- wmode: 'transparent' // necessary to get HTML captions to appear as overlay
781
- });
782
- }
783
- else { // if this is an audio player
784
- thisObj.jwPlayer = jwplayer(thisObj.jwId).setup({
785
- playlist: [{
786
- sources: sources
787
- }],
788
- flashplayer: flashplayer,
789
- html5player: html5player,
790
- controls: false,
791
- volume: this.defaultVolume * 100,
792
- height: 0,
793
- width: '100%',
794
- fallback: false,
795
- primary: 'flash'
796
- });
797
- }
798
- // remove the media element - we're done with it
799
- // keeping it would cause too many potential problems with HTML5 & JW event listeners both firing
800
- thisObj.$media.remove();
801
-
802
- deferred.resolve();
803
- },
804
- error: function(jqXHR, textStatus, errorThrown) {
805
- // Loading the JW Player failed
806
- deferred.reject();
807
- }
808
- });
809
- // Done with JW Player initialization.
810
- return promise;
811
- };
812
-
813
- // Sets media/track/source attributes; is called whenever player is recreated since $media may have changed.
814
- AblePlayer.prototype.setMediaAttributes = function () {
815
- // Firefox puts videos in tab order; remove.
816
- this.$media.attr('tabindex', -1);
817
-
818
- // Keep native player from displaying captions/subtitles by setting textTrack.mode='disabled'
819
- // https://dev.w3.org/html5/spec-author-view/video.html#text-track-mode
820
- // This *should* work but historically hasn't been supported in all browsers
821
- // Workaround for non-supporting browsers is to remove default attribute
822
- // We're doing that too in track.js > setupCaptions()
823
- var textTracks = this.$media.get(0).textTracks;
824
- if (textTracks) {
825
- var i = 0;
826
- while (i < textTracks.length) {
827
- textTracks[i].mode = 'disabled';
828
- i += 1;
829
- }
830
- }
831
- };
832
-
833
- AblePlayer.prototype.getPlayer = function() {
834
-
835
- // Determine which player to use, if any
836
- // return 'html5', 'jw' or null
837
- var i, sourceType, $newItem;
838
- if (this.youTubeId) {
839
- if (this.mediaType !== 'video') {
840
- // attempting to play a YouTube video using an element other than <video>
841
- return null;
842
- }
843
- else {
844
- return 'youtube';
845
- }
846
- }
847
- else if (this.testFallback ||
848
- ((this.isUserAgent('msie 7') || this.isUserAgent('msie 8') || this.isUserAgent('msie 9')) && this.mediaType === 'video') ||
849
- (this.isIOS() && (this.isIOS(4) || this.isIOS(5) || this.isIOS(6)))
850
- ) {
851
- // the user wants to test the fallback player, or
852
- // the user is using an older version of IE or IOS,
853
- // both of which had buggy implementation of HTML5 video
854
- if (this.fallback === 'jw') {
855
- if (this.jwCanPlay()) {
856
- return 'jw';
857
- }
858
- else {
859
- // JW Player is available as fallback, but can't play this source file
860
- return null;
861
- }
862
- }
863
- else {
864
- // browser doesn't support HTML5 video and there is no fallback player
865
- return null;
866
- }
867
- }
868
- else if (this.media.canPlayType) {
869
- return 'html5';
870
- }
871
- else {
872
- // Browser does not support the available media file
873
- return null;
874
- }
875
- };
876
-
877
- AblePlayer.prototype.jwCanPlay = function() {
878
- // Determine whether there are media files that JW supports
879
- var i, sourceType, $firstItem;
880
-
881
- if (this.$sources.length > 0) { // this media has one or more <source> elements
882
- for (i = 0; i < this.$sources.length; i++) {
883
- sourceType = this.$sources[i].getAttribute('type');
884
- if ((this.mediaType === 'video' && sourceType === 'video/mp4') ||
885
- (this.mediaType === 'audio' && sourceType === 'audio/mpeg')) {
886
- // JW Player can play this
887
- return true;
888
- }
889
- }
890
- }
891
- // still here? That means there's no source that JW can play
892
- // check for an mp3 or mp4 in a able-playlist
893
- // TODO: Implement this more efficiently
894
- // Playlist is initialized later in setupInstancePlaylist()
895
- // but we can't wait for that...
896
- if ($('.able-playlist')) {
897
- // there's at least one playlist on this page
898
- // get the first item from the first playlist
899
- // if JW Player can play that one, assume it can play all items in all playlists
900
- $firstItem = $('.able-playlist').eq(0).find('li').eq(0);
901
- if (this.mediaType === 'audio') {
902
- if ($firstItem.attr('data-mp3')) {
903
- return true;
904
- }
905
- else if (this.mediaType === 'video') {
906
- if ($firstItem.attr('data-mp4')) {
907
- return true;
908
- }
909
- }
910
- }
911
- }
912
- return false;
913
- };
2
+ // Set default variable values.
3
+ AblePlayer.prototype.setDefaults = function () {
4
+
5
+ this.playerCreated = false; // will set to true after recreatePlayer() is complete the first time
6
+ this.playing = false; // will change to true after 'playing' event is triggered
7
+ this.paused = true; // will always be the opposite of this.playing (available for convenience)
8
+ this.clickedPlay = false; // will change to true temporarily if user clicks 'play' (or pause)
9
+ this.fullscreen = false; // will change to true if player is in full screen mode
10
+ this.swappingSrc = false; // will change to true temporarily while media source is being swapped
11
+ this.initializing = false; // will change to true temporarily while initPlayer() is processing
12
+ this.cueingPlaylistItems = false; // will change to true temporarily while cueing next playlist item
13
+ this.okToPlay = false; // will change to true if conditions are acceptible for automatic playback after media loads
14
+
15
+ this.getUserAgent();
16
+ this.setIconColor();
17
+ this.setButtonImages();
18
+ };
19
+
20
+ AblePlayer.prototype.getRootPath = function() {
21
+
22
+ // returns Able Player root path (assumes ableplayer.js is in /build, one directory removed from root)
23
+ var scripts, i, scriptSrc, scriptFile, fullPath, ablePath, parentFolderIndex, rootPath;
24
+ scripts= document.getElementsByTagName('script');
25
+ for (i=0; i < scripts.length; i++) {
26
+ scriptSrc = scripts[i].src;
27
+ scriptFile = scriptSrc.substr(scriptSrc.lastIndexOf('/'));
28
+ if (scriptFile.indexOf('ableplayer') !== -1) {
29
+ // this is the ableplayerscript
30
+ fullPath = scriptSrc.split('?')[0]; // remove any ? params
31
+ break;
32
+ }
33
+ }
34
+ ablePath= fullPath.split('/').slice(0, -1).join('/'); // remove last filename part of path
35
+ parentFolderIndex = ablePath.lastIndexOf('/');
36
+ rootPath = ablePath.substring(0, parentFolderIndex) + '/';
37
+ return rootPath;
38
+ }
39
+
40
+ AblePlayer.prototype.setIconColor = function() {
41
+
42
+ // determine the best color choice (white or black) for icons,
43
+ // given the background-color of their container elements
44
+ // Source for relative luminance formula:
45
+ // https://en.wikipedia.org/wiki/Relative_luminance
46
+
47
+ // We need to know the color *before* creating the element
48
+ // so the element doesn't exist yet when this function is called
49
+ // therefore, need to create a temporary element then remove it after color is determined
50
+ // Temp element must be added to the DOM or WebKit can't retrieve its CSS properties
51
+
52
+ var $elements, i, $el, bgColor, rgb, red, green, blue, luminance, iconColor;
53
+
54
+ $elements = ['controller', 'toolbar'];
55
+ for (i=0; i<$elements.length; i++) {
56
+ if ($elements[i] == 'controller') {
57
+ $el = $('<div>', {
58
+ 'class': 'able-controller'
59
+ }).hide();
60
+ }
61
+ else if ($elements[i] === 'toolbar') {
62
+ $el = $('<div>', {
63
+ 'class': 'able-window-toolbar'
64
+ }).hide();
65
+ }
66
+ $('body').append($el);
67
+ bgColor = $el.css('background-color');
68
+ // bgColor is a string in the form 'rgb(R, G, B)', perhaps with a 4th item for alpha;
69
+ // split the 3 or 4 channels into an array
70
+ rgb = bgColor.replace(/[^\d,]/g, '').split(',');
71
+ red = rgb[0];
72
+ green = rgb[1];
73
+ blue = rgb[2];
74
+ luminance = (0.2126 * red) + (0.7152 * green) + (0.0722 * blue);
75
+ // range is 1 - 255; therefore 125 is the tipping point
76
+ if (luminance < 125) { // background is dark
77
+ iconColor = 'white';
78
+ }
79
+ else { // background is light
80
+ iconColor = 'black';
81
+ }
82
+ if ($elements[i] === 'controller') {
83
+ this.iconColor = iconColor;
84
+ }
85
+ else if ($elements[i] === 'toolbar') {
86
+ this.toolbarIconColor = iconColor;
87
+ }
88
+ $el.remove();
89
+ }
90
+ };
91
+
92
+ AblePlayer.prototype.setButtonImages = function() {
93
+
94
+ // NOTE: volume button images are now set dynamically within volume.js
95
+ this.imgPath = this.rootPath + 'button-icons/' + this.iconColor + '/';
96
+ this.playButtonImg = this.imgPath + 'play.png';
97
+ this.pauseButtonImg = this.imgPath + 'pause.png';
98
+
99
+ this.restartButtonImg = this.imgPath + 'restart.png';
100
+
101
+ this.rewindButtonImg = this.imgPath + 'rewind.png';
102
+ this.forwardButtonImg = this.imgPath + 'forward.png';
103
+
104
+ this.previousButtonImg = this.imgPath + 'previous.png';
105
+ this.nextButtonImg = this.imgPath + 'next.png';
106
+
107
+ if (this.speedIcons === 'arrows') {
108
+ this.fasterButtonImg = this.imgPath + 'slower.png';
109
+ this.slowerButtonImg = this.imgPath + 'faster.png';
110
+ }
111
+ else if (this.speedIcons === 'animals') {
112
+ this.fasterButtonImg = this.imgPath + 'rabbit.png';
113
+ this.slowerButtonImg = this.imgPath + 'turtle.png';
114
+ }
115
+
116
+ this.captionsButtonImg = this.imgPath + 'captions.png';
117
+ this.chaptersButtonImg = this.imgPath + 'chapters.png';
118
+ this.signButtonImg = this.imgPath + 'sign.png';
119
+ this.transcriptButtonImg = this.imgPath + 'transcript.png';
120
+ this.descriptionsButtonImg = this.imgPath + 'descriptions.png';
121
+
122
+ this.fullscreenExpandButtonImg = this.imgPath + 'fullscreen-expand.png';
123
+ this.fullscreenCollapseButtonImg = this.imgPath + 'fullscreen-collapse.png';
124
+
125
+ this.prefsButtonImg = this.imgPath + 'preferences.png';
126
+ this.helpButtonImg = this.imgPath + 'help.png';
127
+ };
128
+
129
+ AblePlayer.prototype.getSvgData = function(button) {
130
+
131
+ // returns array of values for creating <svg> tag for specified button
132
+ // 0 = <svg> viewBox attribute
133
+ // 1 = <path> d (description) attribute
134
+ var svg = Array();
135
+
136
+ switch (button) {
137
+
138
+ case 'play':
139
+ svg[0] = '0 0 16 20';
140
+ svg[1] = 'M0 18.393v-16.429q0-0.29 0.184-0.402t0.441 0.033l14.821 8.237q0.257 0.145 0.257 0.346t-0.257 0.346l-14.821 8.237q-0.257 0.145-0.441 0.033t-0.184-0.402z';
141
+ break;
142
+
143
+ case 'pause':
144
+ svg[0] = '0 0 20 20';
145
+ svg[1] = 'M0 18.036v-15.714q0-0.29 0.212-0.502t0.502-0.212h5.714q0.29 0 0.502 0.212t0.212 0.502v15.714q0 0.29-0.212 0.502t-0.502 0.212h-5.714q-0.29 0-0.502-0.212t-0.212-0.502zM10 18.036v-15.714q0-0.29 0.212-0.502t0.502-0.212h5.714q0.29 0 0.502 0.212t0.212 0.502v15.714q0 0.29-0.212 0.502t-0.502 0.212h-5.714q-0.29 0-0.502-0.212t-0.212-0.502z';
146
+ break;
147
+
148
+ case 'stop':
149
+ svg[0] = '0 0 20 20';
150
+ svg[1] = 'M0 18.036v-15.714q0-0.29 0.212-0.502t0.502-0.212h15.714q0.29 0 0.502 0.212t0.212 0.502v15.714q0 0.29-0.212 0.502t-0.502 0.212h-15.714q-0.29 0-0.502-0.212t-0.212-0.502z';
151
+ break;
152
+
153
+ case 'restart':
154
+ svg[0] = '0 0 20 20';
155
+ svg[1] = 'M18 8h-6l2.243-2.243c-1.133-1.133-2.64-1.757-4.243-1.757s-3.109 0.624-4.243 1.757c-1.133 1.133-1.757 2.64-1.757 4.243s0.624 3.109 1.757 4.243c1.133 1.133 2.64 1.757 4.243 1.757s3.109-0.624 4.243-1.757c0.095-0.095 0.185-0.192 0.273-0.292l1.505 1.317c-1.466 1.674-3.62 2.732-6.020 2.732-4.418 0-8-3.582-8-8s3.582-8 8-8c2.209 0 4.209 0.896 5.656 2.344l2.344-2.344v6z';
156
+ break;
157
+
158
+ case 'rewind':
159
+ svg[0] = '0 0 20 20';
160
+ svg[1] = 'M11.25 3.125v6.25l6.25-6.25v13.75l-6.25-6.25v6.25l-6.875-6.875z';
161
+ break;
162
+
163
+ case 'forward':
164
+ svg[0] = '0 0 20 20';
165
+ svg[1] = 'M10 16.875v-6.25l-6.25 6.25v-13.75l6.25 6.25v-6.25l6.875 6.875z';
166
+ break;
167
+
168
+ case 'previous':
169
+ svg[0] = '0 0 20 20';
170
+ svg[1] = 'M5 17.5v-15h2.5v6.875l6.25-6.25v13.75l-6.25-6.25v6.875z';
171
+ break;
172
+
173
+ case 'next':
174
+ svg[0] = '0 0 20 20';
175
+ svg[1] = 'M15 2.5v15h-2.5v-6.875l-6.25 6.25v-13.75l6.25 6.25v-6.875z';
176
+ break;
177
+
178
+ case 'slower':
179
+ svg[0] = '0 0 20 20';
180
+ svg[1] = 'M0 7.321q0-0.29 0.212-0.502t0.502-0.212h10q0.29 0 0.502 0.212t0.212 0.502-0.212 0.502l-5 5q-0.212 0.212-0.502 0.212t-0.502-0.212l-5-5q-0.212-0.212-0.212-0.502z';
181
+ break;
182
+
183
+ case 'faster':
184
+ svg[0] = '0 0 11 20';
185
+ svg[1] = 'M0 12.411q0-0.29 0.212-0.502l5-5q0.212-0.212 0.502-0.212t0.502 0.212l5 5q0.212 0.212 0.212 0.502t-0.212 0.502-0.502 0.212h-10q-0.29 0-0.502-0.212t-0.212-0.502z';
186
+ break;
187
+
188
+ case 'turtle':
189
+ svg[0] = '0 0 20 20';
190
+ svg[1] = 'M17.212 3.846c-0.281-0.014-0.549 0.025-0.817 0.144-1.218 0.542-1.662 2.708-2.163 3.942-1.207 2.972-7.090 4.619-11.755 5.216-0.887 0.114-1.749 0.74-2.428 1.466 0.82-0.284 2.126-0.297 2.74 0.144 0.007 0.488-0.376 1.062-0.625 1.37-0.404 0.5-0.398 0.793 0.12 0.793 0.473 0 0.752 0.007 1.635 0 0.393-0.003 0.618-0.16 1.49-1.49 3.592 0.718 5.986-0.264 5.986-0.264s0.407 1.755 1.418 1.755h1.49c0.633 0 0.667-0.331 0.625-0.433-0.448-1.082-0.68-1.873-0.769-2.5-0.263-1.857 0.657-3.836 2.524-5.457 0.585 0.986 2.253 0.845 2.909-0.096s0.446-2.268-0.192-3.221c-0.49-0.732-1.345-1.327-2.188-1.37zM8.221 4.663c-0.722-0.016-1.536 0.111-2.5 0.409-4.211 1.302-4.177 4.951-3.51 5.745 0 0-0.955 0.479-0.409 1.274 0.448 0.652 3.139 0.191 5.409-0.529s4.226-1.793 5.312-2.692c0.948-0.785 0.551-2.106-0.505-1.947-0.494-0.98-1.632-2.212-3.798-2.26zM18.846 5.962c0.325 0 0.577 0.252 0.577 0.577s-0.252 0.577-0.577 0.577c-0.325 0-0.577-0.252-0.577-0.577s0.252-0.577 0.577-0.577z';
191
+ break;
192
+
193
+ case 'rabbit':
194
+ svg[0] = '0 0 20 20';
195
+ svg[1] = 'M10.817 0c-2.248 0-1.586 0.525-1.154 0.505 1.551-0.072 5.199 0.044 6.851 2.428 0 0-1.022-2.933-5.697-2.933zM10.529 0.769c-2.572 0-2.837 0.51-2.837 1.106 0 0.545 1.526 0.836 2.524 0.697 2.778-0.386 4.231-0.12 5.264 0.865-1.010 0.779-0.75 1.401-1.274 1.851-1.093 0.941-2.643-0.673-4.976-0.673-2.496 0-4.712 1.92-4.712 4.76-0.157-0.537-0.769-0.913-1.442-0.913-0.974 0-1.514 0.637-1.514 1.49 0 0.769 1.13 1.791 2.861 0.938 0.499 1.208 2.265 1.364 2.452 1.418 0.538 0.154 1.875 0.098 1.875 0.865 0 0.794-1.034 1.094-1.034 1.707 0 1.070 1.758 0.873 2.284 1.034 1.683 0.517 2.103 1.214 2.788 2.212 0.771 1.122 2.572 1.408 2.572 0.625 0-3.185-4.413-4.126-4.399-4.135 0.608-0.382 2.139-1.397 2.139-3.534 0-1.295-0.703-2.256-1.755-2.861 1.256 0.094 2.572 1.205 2.572 2.74 0 1.877-0.653 2.823-0.769 2.957 1.975-1.158 3.193-3.91 3.029-6.37 0.61 0.401 1.27 0.577 1.971 0.625 0.751 0.052 1.475-0.225 1.635-0.529 0.38-0.723 0.162-2.321-0.12-2.837-0.763-1.392-2.236-1.73-3.606-1.683-1.202-1.671-3.812-2.356-5.529-2.356zM1.37 3.077l-0.553 1.538h3.726c0.521-0.576 1.541-1.207 2.284-1.538h-5.457zM18.846 5.192c0.325 0 0.577 0.252 0.577 0.577s-0.252 0.577-0.577 0.577c-0.325 0-0.577-0.252-0.577-0.577s0.252-0.577 0.577-0.577zM0.553 5.385l-0.553 1.538h3.197c0.26-0.824 0.586-1.328 0.769-1.538h-3.413z';
196
+ break;
197
+
198
+ case 'ellipsis':
199
+ svg[0] = '0 0 20 20';
200
+ svg[1] = 'M10.001 7.8c-1.215 0-2.201 0.985-2.201 2.2s0.986 2.2 2.201 2.2c1.215 0 2.199-0.985 2.199-2.2s-0.984-2.2-2.199-2.2zM3.001 7.8c-1.215 0-2.201 0.985-2.201 2.2s0.986 2.2 2.201 2.2c1.215 0 2.199-0.986 2.199-2.2s-0.984-2.2-2.199-2.2zM17.001 7.8c-1.215 0-2.201 0.985-2.201 2.2s0.986 2.2 2.201 2.2c1.215 0 2.199-0.985 2.199-2.2s-0.984-2.2-2.199-2.2z';
201
+ break;
202
+
203
+ case 'pipe':
204
+ svg[0] = '0 0 20 20';
205
+ svg[1] = 'M10.15 0.179h0.623c0.069 0 0.127 0.114 0.127 0.253v19.494c0 0.139-0.057 0.253-0.127 0.253h-1.247c-0.069 0-0.126-0.114-0.126-0.253v-19.494c0-0.139 0.057-0.253 0.126-0.253h0.623z';
206
+ break;
207
+
208
+ case 'captions':
209
+ svg[0] = '0 0 20 20';
210
+ svg[1] = 'M0.033 3.624h19.933v12.956h-19.933v-12.956zM18.098 10.045c-0.025-2.264-0.124-3.251-0.743-3.948-0.112-0.151-0.322-0.236-0.496-0.344-0.606-0.386-3.465-0.526-6.782-0.526s-6.313 0.14-6.907 0.526c-0.185 0.108-0.396 0.193-0.519 0.344-0.607 0.697-0.693 1.684-0.731 3.948 0.037 2.265 0.124 3.252 0.731 3.949 0.124 0.161 0.335 0.236 0.519 0.344 0.594 0.396 3.59 0.526 6.907 0.547 3.317-0.022 6.176-0.151 6.782-0.547 0.174-0.108 0.384-0.183 0.496-0.344 0.619-0.697 0.717-1.684 0.743-3.949v0 0zM9.689 9.281c-0.168-1.77-1.253-2.813-3.196-2.813-1.773 0-3.168 1.387-3.168 3.617 0 2.239 1.271 3.636 3.372 3.636 1.676 0 2.851-1.071 3.035-2.852h-2.003c-0.079 0.661-0.397 1.168-1.068 1.168-1.059 0-1.253-0.91-1.253-1.876 0-1.33 0.442-2.010 1.174-2.010 0.653 0 1.068 0.412 1.13 1.129h1.977zM16.607 9.281c-0.167-1.77-1.252-2.813-3.194-2.813-1.773 0-3.168 1.387-3.168 3.617 0 2.239 1.271 3.636 3.372 3.636 1.676 0 2.851-1.071 3.035-2.852h-2.003c-0.079 0.661-0.397 1.168-1.068 1.168-1.059 0-1.253-0.91-1.253-1.876 0-1.33 0.441-2.010 1.174-2.010 0.653 0 1.068 0.412 1.13 1.129h1.976z';
211
+ break;
212
+
213
+ case 'descriptions':
214
+ svg[0] = '0 0 20 20';
215
+ svg[1] = 'M17.623 3.57h-1.555c1.754 1.736 2.763 4.106 2.763 6.572 0 2.191-0.788 4.286-2.189 5.943h1.484c1.247-1.704 1.945-3.792 1.945-5.943-0-2.418-0.886-4.754-2.447-6.572v0zM14.449 3.57h-1.55c1.749 1.736 2.757 4.106 2.757 6.572 0 2.191-0.788 4.286-2.187 5.943h1.476c1.258-1.704 1.951-3.792 1.951-5.943-0-2.418-0.884-4.754-2.447-6.572v0zM11.269 3.57h-1.542c1.752 1.736 2.752 4.106 2.752 6.572 0 2.191-0.791 4.286-2.181 5.943h1.473c1.258-1.704 1.945-3.792 1.945-5.943 0-2.418-0.876-4.754-2.447-6.572v0zM10.24 9.857c0 3.459-2.826 6.265-6.303 6.265v0.011h-3.867v-12.555h3.896c3.477 0 6.274 2.806 6.274 6.279v0zM6.944 9.857c0-1.842-1.492-3.338-3.349-3.338h-0.876v6.686h0.876c1.858 0 3.349-1.498 3.349-3.348v0z';
216
+ break;
217
+
218
+ case 'sign':
219
+ svg[0] = '0 0 20 20';
220
+ svg[1] = 'M10.954 10.307c0.378 0.302 0.569 1.202 0.564 1.193 0.697 0.221 1.136 0.682 1.136 0.682 1.070-0.596 1.094-0.326 1.558-0.682 0.383-0.263 0.366-0.344 0.567-1.048 0.187-0.572-0.476-0.518-1.021-1.558-0.95 0.358-1.463 0.196-1.784 0.167-0.145-0.020-0.12 0.562-1.021 1.247zM14.409 17.196c-0.133 0.182-0.196 0.218-0.363 0.454-0.28 0.361 0.076 0.906 0.253 0.82 0.206-0.076 0.341-0.488 0.567-0.623 0.115-0.061 0.422-0.513 0.709-0.82 0.211-0.238 0.363-0.344 0.564-0.594 0.341-0.422 0.412-0.744 0.709-1.193 0.184-0.236 0.312-0.307 0.481-0.594 0.886-1.679 0.628-2.432 1.475-3.629 0.26-0.353 0.552-0.442 0.964-0.653 0.383-2.793-0.888-4.356-0.879-4.361-1.067 0.623-1.644 0.879-2.751 0.82-0.417-0.005-0.636-0.182-1.048-0.145-0.385 0.015-0.582 0.159-0.964 0.29-0.589 0.182-0.91 0.344-1.529 0.535-0.393 0.11-0.643 0.115-1.050 0.255-0.348 0.147-0.182 0.029-0.427 0.312-0.317 0.348-0.238 0.623-0.535 1.222-0.371 0.785-0.326 0.891-0.115 0.987-0.14 0.402-0.174 0.672-0.14 1.107 0.039 0.331-0.101 0.562 0.255 0.825 0.483 0.361 1.499 1.205 1.757 1.217 0.39-0.012 1.521 0.029 2.096-0.368 0.13-0.081 0.167-0.162 0.056 0.145-0.022 0.037-1.433 1.136-1.585 1.131-1.794 0.056-1.193 0.157-1.303 0.115-0.091 0-0.955-1.055-1.477-0.682-0.196 0.12-0.287 0.236-0.363 0.452 0.066 0.137 0.383 0.358 0.675 0.54 0.422 0.27 0.461 0.552 0.881 0.653 0.513 0.115 1.060 0.039 1.387 0.081 0.125 0.034 1.256-0.297 1.961-0.675 0.65-0.336-0.898 0.648-1.276 1.131-1.141 0.358-0.82 0.373-1.362 0.483-0.503 0.115-0.479 0.086-0.822 0.196-0.356 0.086-0.648 0.572-0.312 0.825 0.201 0.167 0.827-0.066 1.445-0.086 0.275-0.005 1.391-0.518 1.644-0.653 0.633-0.339 1.099-0.81 1.472-1.077 0.518-0.361-0.584 0.991-1.050 1.558zM8.855 9.799c-0.378-0.312-0.569-1.212-0.564-1.217-0.697-0.206-1.136-0.667-1.136-0.653-1.070 0.582-1.099 0.312-1.558 0.653-0.388 0.277-0.366 0.363-0.567 1.045-0.187 0.594 0.471 0.535 1.021 1.561 0.95-0.344 1.463-0.182 1.784-0.142 0.145 0.010 0.12-0.572 1.021-1.247zM5.4 2.911c0.133-0.191 0.196-0.228 0.368-0.454 0.27-0.371-0.081-0.915-0.253-0.849-0.211 0.096-0.346 0.508-0.599 0.653-0.093 0.052-0.4 0.503-0.682 0.82-0.211 0.228-0.363 0.334-0.564 0.599-0.346 0.407-0.412 0.729-0.709 1.161-0.184 0.258-0.317 0.324-0.481 0.621-0.886 1.669-0.631 2.422-1.475 3.6-0.26 0.38-0.552 0.461-0.964 0.682-0.383 2.788 0.883 4.346 0.879 4.336 1.068-0.609 1.639-0.861 2.751-0.825 0.417 0.025 0.636 0.201 1.048 0.174 0.385-0.025 0.582-0.169 0.964-0.285 0.589-0.196 0.91-0.358 1.499-0.54 0.422-0.12 0.672-0.125 1.080-0.285 0.348-0.128 0.182-0.010 0.427-0.282 0.312-0.358 0.238-0.633 0.508-1.217 0.398-0.8 0.353-0.906 0.142-0.991 0.135-0.412 0.174-0.677 0.14-1.107-0.044-0.336 0.101-0.572-0.255-0.82-0.483-0.375-1.499-1.22-1.752-1.222-0.395 0.002-1.526-0.039-2.101 0.339-0.13 0.101-0.167 0.182-0.056-0.11 0.022-0.052 1.433-1.148 1.585-1.163 1.794-0.039 1.193-0.14 1.303-0.088 0.091-0.007 0.955 1.045 1.477 0.682 0.191-0.13 0.287-0.245 0.368-0.452-0.071-0.147-0.388-0.368-0.68-0.537-0.422-0.282-0.464-0.564-0.881-0.655-0.513-0.125-1.065-0.049-1.387-0.11-0.125-0.015-1.256 0.317-1.956 0.68-0.66 0.351 0.893-0.631 1.276-1.136 1.136-0.339 0.81-0.353 1.36-0.479 0.501-0.101 0.476-0.071 0.82-0.172 0.351-0.096 0.648-0.577 0.312-0.849-0.206-0.152-0.827 0.081-1.44 0.086-0.28 0.020-1.396 0.533-1.649 0.677-0.633 0.329-1.099 0.8-1.472 1.048-0.523 0.38 0.584-0.967 1.050-1.529z';
221
+ break;
222
+
223
+ case 'mute':
224
+ svg[0] = '0 0 20 20';
225
+ svg[1] = 'M7.839 1.536c0.501-0.501 0.911-0.331 0.911 0.378v16.172c0 0.709-0.41 0.879-0.911 0.378l-4.714-4.713h-3.125v-7.5h3.125l4.714-4.714zM18.75 12.093v1.657h-1.657l-2.093-2.093-2.093 2.093h-1.657v-1.657l2.093-2.093-2.093-2.093v-1.657h1.657l2.093 2.093 2.093-2.093h1.657v1.657l-2.093 2.093z';
226
+ break;
227
+
228
+ case 'volume-mute':
229
+ svg[0] = '0 0 20 20';
230
+ svg[1] = 'M10.723 14.473c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 1.584-1.584 1.584-4.161 0-5.745-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c2.315 2.315 2.315 6.082 0 8.397-0.183 0.183-0.423 0.275-0.663 0.275zM7.839 1.536c0.501-0.501 0.911-0.331 0.911 0.378v16.172c0 0.709-0.41 0.879-0.911 0.378l-4.714-4.713h-3.125v-7.5h3.125l4.714-4.714z';
231
+ break;
232
+
233
+ case 'volume-medium':
234
+ svg[0] = '0 0 20 20';
235
+ svg[1] = 'M14.053 16.241c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 2.559-2.559 2.559-6.722 0-9.281-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c1.594 1.594 2.471 3.712 2.471 5.966s-0.878 4.373-2.471 5.966c-0.183 0.183-0.423 0.275-0.663 0.275zM10.723 14.473c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 1.584-1.584 1.584-4.161 0-5.745-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c2.315 2.315 2.315 6.082 0 8.397-0.183 0.183-0.423 0.275-0.663 0.275zM7.839 1.536c0.501-0.501 0.911-0.331 0.911 0.378v16.172c0 0.709-0.41 0.879-0.911 0.378l-4.714-4.713h-3.125v-7.5h3.125l4.714-4.714z';
236
+ break;
237
+
238
+ case 'volume-loud':
239
+ svg[0] = '0 0 21 20';
240
+ svg[1] = 'M17.384 18.009c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 1.712-1.712 2.654-3.988 2.654-6.408s-0.943-4.696-2.654-6.408c-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c2.066 2.066 3.204 4.813 3.204 7.734s-1.138 5.668-3.204 7.734c-0.183 0.183-0.423 0.275-0.663 0.275zM14.053 16.241c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 2.559-2.559 2.559-6.722 0-9.281-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c1.594 1.594 2.471 3.712 2.471 5.966s-0.878 4.373-2.471 5.966c-0.183 0.183-0.423 0.275-0.663 0.275zM10.723 14.473c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 1.584-1.584 1.584-4.161 0-5.745-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c2.315 2.315 2.315 6.082 0 8.397-0.183 0.183-0.423 0.275-0.663 0.275zM7.839 1.536c0.501-0.501 0.911-0.331 0.911 0.378v16.172c0 0.709-0.41 0.879-0.911 0.378l-4.714-4.713h-3.125v-7.5h3.125l4.714-4.714z';
241
+ break;
242
+
243
+ case 'chapters':
244
+ svg[0] = '0 0 20 20';
245
+ svg[1] = 'M5 2.5v17.5l6.25-6.25 6.25 6.25v-17.5zM15 0h-12.5v17.5l1.25-1.25v-15h11.25z';
246
+ break;
247
+
248
+ case 'transcript':
249
+ svg[0] = '0 0 20 20';
250
+ svg[1] = 'M0 19.107v-17.857q0-0.446 0.313-0.759t0.759-0.313h8.929v6.071q0 0.446 0.313 0.759t0.759 0.313h6.071v11.786q0 0.446-0.313 0.759t-0.759 0.312h-15q-0.446 0-0.759-0.313t-0.313-0.759zM4.286 15.536q0 0.156 0.1 0.257t0.257 0.1h7.857q0.156 0 0.257-0.1t0.1-0.257v-0.714q0-0.156-0.1-0.257t-0.257-0.1h-7.857q-0.156 0-0.257 0.1t-0.1 0.257v0.714zM4.286 12.679q0 0.156 0.1 0.257t0.257 0.1h7.857q0.156 0 0.257-0.1t0.1-0.257v-0.714q0-0.156-0.1-0.257t-0.257-0.1h-7.857q-0.156 0-0.257 0.1t-0.1 0.257v0.714zM4.286 9.821q0 0.156 0.1 0.257t0.257 0.1h7.857q0.156 0 0.257-0.1t0.1-0.257v-0.714q0-0.156-0.1-0.257t-0.257-0.1h-7.857q-0.156 0-0.257 0.1t-0.1 0.257v0.714zM11.429 5.893v-5.268q0.246 0.156 0.402 0.313l4.554 4.554q0.156 0.156 0.313 0.402h-5.268z';
251
+ break;
252
+
253
+ case 'preferences':
254
+ svg[0] = '0 0 20 20';
255
+ svg[1] = 'M18.238 11.919c-1.049-1.817-0.418-4.147 1.409-5.205l-1.965-3.404c-0.562 0.329-1.214 0.518-1.911 0.518-2.1 0-3.803-1.714-3.803-3.828h-3.931c0.005 0.653-0.158 1.314-0.507 1.919-1.049 1.818-3.382 2.436-5.212 1.382l-1.965 3.404c0.566 0.322 1.056 0.793 1.404 1.396 1.048 1.815 0.42 4.139-1.401 5.2l1.965 3.404c0.56-0.326 1.209-0.513 1.902-0.513 2.094 0 3.792 1.703 3.803 3.808h3.931c-0.002-0.646 0.162-1.3 0.507-1.899 1.048-1.815 3.375-2.433 5.203-1.387l1.965-3.404c-0.562-0.322-1.049-0.791-1.395-1.391zM10 14.049c-2.236 0-4.050-1.813-4.050-4.049s1.813-4.049 4.050-4.049 4.049 1.813 4.049 4.049c-0 2.237-1.813 4.049-4.049 4.049z';
256
+ break;
257
+
258
+ case 'close':
259
+ svg[0] = '0 0 16 20';
260
+ svg[1] = 'M1.228 14.933q0-0.446 0.312-0.759l3.281-3.281-3.281-3.281q-0.313-0.313-0.313-0.759t0.313-0.759l1.518-1.518q0.313-0.313 0.759-0.313t0.759 0.313l3.281 3.281 3.281-3.281q0.313-0.313 0.759-0.313t0.759 0.313l1.518 1.518q0.313 0.313 0.313 0.759t-0.313 0.759l-3.281 3.281 3.281 3.281q0.313 0.313 0.313 0.759t-0.313 0.759l-1.518 1.518q-0.313 0.313-0.759 0.313t-0.759-0.313l-3.281-3.281-3.281 3.281q-0.313 0.313-0.759 0.313t-0.759-0.313l-1.518-1.518q-0.313-0.313-0.313-0.759z';
261
+ break;
262
+
263
+ case 'fullscreen-expand':
264
+ svg[0] = '0 0 20 20';
265
+ svg[1] = 'M0 18.036v-5q0-0.29 0.212-0.502t0.502-0.212 0.502 0.212l1.607 1.607 3.705-3.705q0.112-0.112 0.257-0.112t0.257 0.112l1.272 1.272q0.112 0.112 0.112 0.257t-0.112 0.257l-3.705 3.705 1.607 1.607q0.212 0.212 0.212 0.502t-0.212 0.502-0.502 0.212h-5q-0.29 0-0.502-0.212t-0.212-0.502zM8.717 8.393q0-0.145 0.112-0.257l3.705-3.705-1.607-1.607q-0.212-0.212-0.212-0.502t0.212-0.502 0.502-0.212h5q0.29 0 0.502 0.212t0.212 0.502v5q0 0.29-0.212 0.502t-0.502 0.212-0.502-0.212l-1.607-1.607-3.705 3.705q-0.112 0.112-0.257 0.112t-0.257-0.112l-1.272-1.272q-0.112-0.112-0.112-0.257z';
266
+ break;
267
+
268
+ case 'fullscreen-collapse':
269
+ svg[0] = '0 0 20 20';
270
+ svg[1] = 'M0.145 16.964q0-0.145 0.112-0.257l3.705-3.705-1.607-1.607q-0.212-0.212-0.212-0.502t0.212-0.502 0.502-0.212h5q0.29 0 0.502 0.212t0.212 0.502v5q0 0.29-0.212 0.502t-0.502 0.212-0.502-0.212l-1.607-1.607-3.705 3.705q-0.112 0.112-0.257 0.112t-0.257-0.112l-1.272-1.272q-0.112-0.112-0.112-0.257zM8.571 9.464v-5q0-0.29 0.212-0.502t0.502-0.212 0.502 0.212l1.607 1.607 3.705-3.705q0.112-0.112 0.257-0.112t0.257 0.112l1.272 1.272q0.112 0.112 0.112 0.257t-0.112 0.257l-3.705 3.705 1.607 1.607q0.212 0.212 0.212 0.502t-0.212 0.502-0.502 0.212h-5q-0.29 0-0.502-0.212t-0.212-0.502z';
271
+ break;
272
+
273
+ case 'help':
274
+ svg[0] = '0 0 11 20';
275
+ svg[1] = 'M0.577 6.317q-0.028-0.167 0.061-0.313 1.786-2.969 5.179-2.969 0.893 0 1.797 0.346t1.629 0.926 1.183 1.423 0.458 1.769q0 0.603-0.173 1.127t-0.391 0.854-0.614 0.664-0.642 0.485-0.681 0.396q-0.458 0.257-0.765 0.725t-0.307 0.748q0 0.19-0.134 0.363t-0.313 0.173h-2.679q-0.167 0-0.285-0.206t-0.117-0.419v-0.502q0-0.926 0.725-1.747t1.596-1.211q0.658-0.301 0.938-0.625t0.279-0.848q0-0.469-0.519-0.826t-1.2-0.357q-0.725 0-1.205 0.324-0.391 0.279-1.194 1.283-0.145 0.179-0.346 0.179-0.134 0-0.279-0.089l-1.83-1.395q-0.145-0.112-0.173-0.279zM3.786 16.875v-2.679q0-0.179 0.134-0.313t0.313-0.134h2.679q0.179 0 0.313 0.134t0.134 0.313v2.679q0 0.179-0.134 0.313t-0.313 0.134h-2.679q-0.179 0-0.313-0.134t-0.134-0.313z';
276
+ break;
277
+ }
278
+
279
+ return svg;
280
+ };
281
+
282
+ // Initialize player based on data on page.
283
+ // This sets some variables, but does not modify anything. Safe to call multiple times.
284
+ // Can call again after updating this.media so long as new media element has the same ID.
285
+ AblePlayer.prototype.reinitialize = function () {
286
+
287
+ var deferred, promise, thisObj, errorMsg, srcFile;
288
+
289
+ deferred = new $.Deferred();
290
+ promise = deferred.promise();
291
+ thisObj = this;
292
+
293
+ // if F12 Developer Tools aren't open in IE (through 9, no longer a problen in IE10)
294
+ // console.log causes an error - can't use debug without a console to log messages to
295
+ if (! window.console) {
296
+ this.debug = false;
297
+ }
298
+
299
+ this.startedPlaying = false;
300
+ // TODO: Move this setting to cookie.
301
+ this.autoScrollTranscript = true;
302
+ //this.autoScrollTranscript = this.getCookie(autoScrollTranscript); // (doesn't work)
303
+
304
+ // Bootstrap from this.media possibly being an ID or other selector.
305
+ this.$media = $(this.media).first();
306
+ this.media = this.$media[0];
307
+
308
+ // Set media type to 'audio' or 'video'; this determines some of the behavior of player creation.
309
+ if (this.$media.is('audio')) {
310
+ this.mediaType = 'audio';
311
+ }
312
+ else if (this.$media.is('video')) {
313
+ this.mediaType = 'video';
314
+ }
315
+ else {
316
+ // Able Player was initialized with some element other than <video> or <audio>
317
+ this.provideFallback();
318
+ deferred.fail();
319
+ return promise;
320
+ }
321
+
322
+ this.$sources = this.$media.find('source');
323
+
324
+ this.player = this.getPlayer();
325
+ if (!this.player) {
326
+ // an error was generated in getPlayer()
327
+ this.provideFallback();
328
+ }
329
+ this.setIconType();
330
+ this.setDimensions();
331
+
332
+ deferred.resolve();
333
+ return promise;
334
+ };
335
+
336
+ AblePlayer.prototype.setDimensions = function() {
337
+ // if media element includes width and height attributes,
338
+ // use these to set the max-width and max-height of the player
339
+ if (this.$media.attr('width') && this.$media.attr('height')) {
340
+ this.playerMaxWidth = parseInt(this.$media.attr('width'), 10);
341
+ this.playerMaxHeight = parseInt(this.$media.attr('height'), 10);
342
+ }
343
+ else if (this.$media.attr('width')) {
344
+ // media element includes a width attribute, but not height
345
+ this.playerMaxWidth = parseInt(this.$media.attr('width'), 10);
346
+ }
347
+ else {
348
+ // set width to width of #player
349
+ // don't set height though; YouTube will automatically set that to match width
350
+ this.playerMaxWidth = this.$media.parent().width();
351
+ this.playerMaxHeight = this.getMatchingHeight(this.playerMaxWidth);
352
+ }
353
+ // override width and height attributes with in-line CSS to make video responsive
354
+ this.$media.css({
355
+ 'width': '100%',
356
+ 'height': 'auto'
357
+ });
358
+ };
359
+
360
+ AblePlayer.prototype.getMatchingHeight = function(width) {
361
+
362
+ // returns likely height for a video, given width
363
+ // These calculations assume 16:9 aspect ratio (the YouTube standard)
364
+ // Videos recorded in other resolutions will be sized to fit, with black bars on each side
365
+ // This function is only called if the <video> element does not have width and height attributes
366
+
367
+ var widths, heights, closestWidth, closestIndex, closestHeight, height;
368
+
369
+ widths = [ 3840, 2560, 1920, 1280, 854, 640, 426 ];
370
+ heights = [ 2160, 1440, 1080, 720, 480, 360, 240 ];
371
+ closestWidth = null;
372
+ closestIndex = null;
373
+
374
+ $.each(widths, function(index){
375
+ if (closestWidth == null || Math.abs(this - width) < Math.abs(closestWidth - width)) {
376
+ closestWidth = this;
377
+ closestIndex = index;
378
+ }
379
+ });
380
+ closestHeight = heights[closestIndex];
381
+ this.aspectRatio = closestWidth / closestHeight;
382
+ height = Math.round(width / this.aspectRatio);
383
+ return height;
384
+ };
385
+
386
+ AblePlayer.prototype.setIconType = function() {
387
+
388
+ // returns either "svg", "font" or "image" (in descending order of preference)
389
+ // Test for support of each type. If not supported, test the next type.
390
+ // last resort is image icons
391
+
392
+ var $tempButton, $testButton, controllerFont;
393
+
394
+ if (this.forceIconType) {
395
+ // use value specified in data-icon-type
396
+ return false;
397
+ }
398
+
399
+ // test for SVG support
400
+ // Test this method widely; failed as expected on IE8 and below
401
+ // https://stackoverflow.com/a/27568129/744281
402
+ if (!!(document.createElementNS && document.createElementNS('http://www.w3.org/2000/svg','svg').createSVGRect)) {
403
+ // browser supports SVG
404
+ this.iconType = 'svg';
405
+ }
406
+ else {
407
+ // browser does NOT support SVG
408
+ // test whether browser can support icon fonts, and whether user has overriding the default style sheet
409
+ // which could cause problems with proper display of the icon fonts
410
+ if (window.getComputedStyle) {
411
+
412
+ // webkit doesn't return calculated styles unless element has been added to the DOM
413
+ // and is visible (note: visibly clipped is considered "visible")
414
+ // use playpauseButton for font-family test if it exists; otherwise must create a new temp button
415
+ if ($('span.icon-play').length) {
416
+ $testButton = $('span.icon-play');
417
+ }
418
+ else {
419
+ $tempButton = $('<span>',{
420
+ 'class': 'icon-play able-clipped'
421
+ });
422
+ $('body').append($tempButton);
423
+ $testButton = $tempButton;
424
+ }
425
+
426
+ // the following retrieves the computed value of font-family
427
+ // tested in Firefox 45.x with "Allow pages to choose their own fonts" unchecked - works!
428
+ // tested in Chrome 49.x with Font Changer plugin - works!
429
+ // tested in IE with user-defined style sheet enables - works!
430
+ // It does NOT account for users who have "ignore font styles on web pages" checked in IE
431
+ // There is no known way to check for that ???
432
+ controllerFont = window.getComputedStyle($testButton.get(0), null).getPropertyValue('font-family');
433
+ if (typeof controllerFont !== 'undefined') {
434
+ if (controllerFont.indexOf('able') !== -1) {
435
+ this.iconType = 'font';
436
+ }
437
+ else {
438
+ this.iconType = 'image';
439
+ }
440
+ }
441
+ else {
442
+ // couldn't get computed font-family; use images to be safe
443
+ this.iconType = 'image';
444
+ }
445
+ }
446
+ else { // window.getComputedStyle is not supported (IE 8 and earlier)
447
+ // No known way to detect computed font
448
+ // The following retrieves the value from the style sheet, not the computed font
449
+ // controllerFont = $tempButton.get(0).currentStyle.fontFamily;
450
+ // It will therefore return "able", even if the user is overriding that with a custom style sheet
451
+ // To be safe, use images
452
+ this.iconType = 'image';
453
+ }
454
+ if (this.debug) {
455
+ console.log('Using ' + this.iconType + 's for player controls');
456
+ }
457
+ if (typeof $tempButton !== 'undefined') {
458
+ $tempButton.remove();
459
+ }
460
+ }
461
+ };
462
+
463
+ // Perform one-time setup for this instance of player; called after player is first initialized.
464
+ AblePlayer.prototype.setupInstance = function () {
465
+
466
+ var deferred = new $.Deferred();
467
+ var promise = deferred.promise();
468
+
469
+ if (this.$media.attr('id')) {
470
+ this.mediaId = this.$media.attr('id');
471
+ }
472
+ else {
473
+ // Ensure the base media element always has an ID.
474
+ this.mediaId = "ableMediaId_" + this.ableIndex;
475
+ this.$media.attr('id', this.mediaId);
476
+ }
477
+ deferred.resolve();
478
+ return promise;
479
+ };
480
+
481
+ AblePlayer.prototype.setupInstancePlaylist = function() {
482
+
483
+ // find a matching playlist and set this.hasPlaylist
484
+ // if there is one, also set this.$playlist, this.playlistIndex, & this.playlistEmbed
485
+ var thisObj = this;
486
+
487
+ this.hasPlaylist = false; // will change to true if a matching playlist is found
488
+
489
+ $('.able-playlist').each(function() {
490
+ if ($(this).data('player') === thisObj.mediaId) {
491
+ // this is the playlist for the current player
492
+ thisObj.hasPlaylist = true;
493
+ // If using an embedded player, we'll replace $playlist with the clone later.
494
+ thisObj.$playlist = $(this).find('li');
495
+
496
+ // check to see if list item has YouTube as its source
497
+ // if it does, inject a thumbnail from YouTube
498
+ var $youTubeVideos = $(this).find('li[data-youtube-id]');
499
+ $youTubeVideos.each(function() {
500
+ var youTubeId = $(this).attr('data-youtube-id');
501
+ var youTubePoster = thisObj.getYouTubePosterUrl(youTubeId,'120');
502
+ var $youTubeImg = $('<img>',{
503
+ 'src': youTubePoster,
504
+ 'alt': ''
505
+ });
506
+ $(this).find('button').prepend($youTubeImg);
507
+ });
508
+
509
+ // add accessibility to the list markup
510
+ $(this).find('li span').attr('aria-hidden','true');
511
+ thisObj.playlistIndex = 0;
512
+ var dataEmbedded = $(this).data('embedded');
513
+ if (typeof dataEmbedded !== 'undefined' && dataEmbedded !== false) {
514
+ // embed playlist within player
515
+ thisObj.playlistEmbed = true;
516
+ }
517
+ else {
518
+ thisObj.playlistEmbed = false;
519
+ }
520
+ }
521
+ });
522
+
523
+ if (this.hasPlaylist && this.loop) {
524
+ // browser will loop the current track in the playlist, rather than the playlist
525
+ // therefore, need to remove loop attribute from media element
526
+ // but keep this.loop as true and handle the playlist looping ourselves
527
+ this.media.removeAttribute('loop');
528
+ }
529
+ if (this.hasPlaylist && this.playlistEmbed) {
530
+ // Copy the playlist out of the dom, so we can reinject when we build the player.
531
+ var parent = this.$playlist.parent();
532
+ this.$playlistDom = parent.clone();
533
+ parent.remove();
534
+ }
535
+ if (this.hasPlaylist && this.$sources.length === 0) {
536
+ // no source elements were provided. Construct them from the first playlist item
537
+ this.cuePlaylistItem(0);
538
+ // redefine this.$sources now that media contains one or more <source> elements
539
+ this.$sources = this.$media.find('source');
540
+ }
541
+
542
+ };
543
+
544
+ AblePlayer.prototype.recreatePlayer = function () {
545
+
546
+ // Creates the appropriate player for the current source.
547
+ var thisObj, prefsGroups, i;
548
+ thisObj = this;
549
+
550
+ // TODO: Ensure when recreating player that we carry over the mediaId
551
+ if (!this.player) {
552
+ console.log("Can't create player; no appropriate player type detected.");
553
+ return;
554
+ }
555
+ if (!this.playerCreated) {
556
+ // only call these functions once
557
+ this.loadCurrentPreferences();
558
+ this.injectPlayerCode();
559
+ }
560
+
561
+ // call all remaining functions each time a new media instance is loaded
562
+
563
+ this.initSignLanguage();
564
+
565
+ // thisObj.initializing = true;
566
+ this.initPlayer().then(function() { // initPlayer success
567
+ // thisObj.initializing = false;
568
+
569
+ thisObj.setupTracks().then(function() {
570
+
571
+ thisObj.setupAltCaptions().then(function() {
572
+
573
+ thisObj.setupTranscript().then(function() {
574
+
575
+ if (thisObj.Volume) {
576
+ thisObj.setMute(false);
577
+ }
578
+ thisObj.setFullscreen(false);
579
+ thisObj.setVolume(thisObj.defaultVolume);
580
+
581
+ if (thisObj.transcriptType) {
582
+ thisObj.addTranscriptAreaEvents();
583
+ thisObj.updateTranscript();
584
+ }
585
+ if (thisObj.mediaType === 'video') {
586
+ thisObj.initDescription();
587
+ }
588
+ if (thisObj.captions.length) {
589
+ thisObj.initDefaultCaption();
590
+ }
591
+
592
+ // setMediaAttributes() sets textTrack.mode to 'disabled' for all tracks
593
+ // This tells browsers to ignore the text tracks so Able Player can handle them
594
+ // However, timing is critical as browsers - especially Safari - tend to ignore this request
595
+ // unless it's sent late in the intialization process.
596
+ // If browsers ignore the request, the result is redundant captions
597
+ thisObj.setMediaAttributes();
598
+ thisObj.addControls();
599
+ thisObj.addEventListeners();
600
+
601
+ // inject each of the hidden forms that will be accessed from the Preferences popup menu
602
+ prefsGroups = thisObj.getPreferencesGroups();
603
+ for (i = 0; i < prefsGroups.length; i++) {
604
+ thisObj.injectPrefsForm(prefsGroups[i]);
605
+ }
606
+ thisObj.setupPopups();
607
+ thisObj.updateCaption();
608
+ thisObj.injectVTS();
609
+ if (thisObj.chaptersDivLocation) {
610
+ thisObj.populateChaptersDiv();
611
+ }
612
+ thisObj.showSearchResults();
613
+
614
+ // Go ahead and load media, without user requesting it
615
+ // Normally, we wait until user clicks play, rather than unnecessarily consume their bandwidth
616
+ // Exceptions are if the video is intended to autostart or if running on iOS (a workaround for iOS issues)
617
+ // TODO: Confirm that this is still necessary with iOS (this would added early, & I don't remember what the issues were)
618
+ if (thisObj.player === 'html5' &&
619
+ (thisObj.isIOS() || thisObj.startTime > 0 || thisObj.autoplay || thisObj.okToPlay)) {
620
+ thisObj.$media[0].load();
621
+ }
622
+ // refreshControls is called twice building/initializing the player
623
+ // this is the second. Best to pause a bit before executing, to be sure all prior steps are complete
624
+ setTimeout(function() {
625
+ thisObj.refreshControls('init');
626
+ },100);
627
+ },
628
+ function() { // initPlayer fail
629
+ thisObj.provideFallback();
630
+ });
631
+ });
632
+ });
633
+ });
634
+ };
635
+
636
+ AblePlayer.prototype.initPlayer = function () {
637
+
638
+ var thisObj = this;
639
+ var playerPromise;
640
+
641
+ // First run player specific initialization.
642
+ if (this.player === 'html5') {
643
+ playerPromise = this.initHtml5Player();
644
+ }
645
+ else if (this.player === 'youtube') {
646
+ playerPromise = this.initYouTubePlayer();
647
+ }
648
+ else if (this.player === 'vimeo') {
649
+ playerPromise = this.initVimeoPlayer();
650
+ }
651
+
652
+ // After player specific initialization is done, run remaining general initialization.
653
+ var deferred = new $.Deferred();
654
+ var promise = deferred.promise();
655
+ playerPromise.done(
656
+ function () { // done/resolved
657
+ if (thisObj.useFixedSeekInterval === false) {
658
+ thisObj.setSeekInterval();
659
+ }
660
+ deferred.resolve();
661
+ }
662
+ ).fail(function () { // failed
663
+ deferred.reject();
664
+ }
665
+ );
666
+
667
+ return promise;
668
+ };
669
+
670
+ AblePlayer.prototype.setSeekInterval = function () {
671
+
672
+ // this function is only called if this.useFixedSeekInterval is false
673
+ // if this.useChapterTimes, this is called as each new chapter is loaded
674
+ // otherwise, it's called once, as the player is initialized
675
+ var thisObj, duration;
676
+ thisObj = this;
677
+ this.seekInterval = this.defaultSeekInterval;
678
+
679
+ if (this.useChapterTimes) {
680
+ duration = this.chapterDuration;
681
+ }
682
+ else {
683
+ duration = this.duration;
684
+ }
685
+ if (typeof duration === 'undefined' || duration < 1) {
686
+ // no duration; just use default for now but keep trying until duration is available
687
+ this.seekIntervalCalculated = false;
688
+ return;
689
+ }
690
+ else {
691
+ if (duration <= 20) {
692
+ this.seekInterval = 5; // 4 steps max
693
+ }
694
+ else if (duration <= 30) {
695
+ this.seekInterval = 6; // 5 steps max
696
+ }
697
+ else if (duration <= 40) {
698
+ this.seekInterval = 8; // 5 steps max
699
+ }
700
+ else if (duration <= 100) {
701
+ this.seekInterval = 10; // 10 steps max
702
+ }
703
+ else {
704
+ // never more than 10 steps from start to end
705
+ this.seekInterval = (duration / 10);
706
+ }
707
+ this.seekIntervalCalculated = true;
708
+ }
709
+ };
710
+
711
+ AblePlayer.prototype.initDefaultCaption = function () {
712
+
713
+ var captions, i;
714
+
715
+ captions = this.captions;
716
+
717
+ if (captions.length > 0) {
718
+ for (i=0; i<captions.length; i++) {
719
+ if (captions[i].def === true) {
720
+ this.captionLang = captions[i].language;
721
+ this.selectedCaptions = captions[i];
722
+ }
723
+ }
724
+ if (typeof this.captionLang === 'undefined') {
725
+ // No caption track was flagged as default
726
+ // find and use a caption language that matches the player language
727
+ for (i=0; i<captions.length; i++) {
728
+ if (captions[i].language === this.lang) {
729
+ this.captionLang = captions[i].language;
730
+ this.selectedCaptions = captions[i];
731
+ }
732
+ }
733
+ }
734
+ if (typeof this.captionLang === 'undefined') {
735
+ // Still no matching caption track
736
+ // just use the first track
737
+ this.captionLang = captions[0].language;
738
+ this.selectedCaptions = captions[0];
739
+ }
740
+ if (typeof this.captionLang !== 'undefined') {
741
+ // reset transcript selected <option> to this.captionLang
742
+ if (this.$transcriptLanguageSelect) {
743
+ this.$transcriptLanguageSelect.find('option[lang=' + this.captionLang + ']').prop('selected',true);
744
+ }
745
+ // sync all other tracks to this same languge
746
+ this.syncTrackLanguages('init',this.captionLang);
747
+ }
748
+ }
749
+ };
750
+
751
+ AblePlayer.prototype.initHtml5Player = function () {
752
+ // Nothing special to do!
753
+ var deferred = new $.Deferred();
754
+ var promise = deferred.promise();
755
+ deferred.resolve();
756
+ return promise;
757
+ };
758
+
759
+ // Sets media/track/source attributes; is called whenever player is recreated since $media may have changed.
760
+ AblePlayer.prototype.setMediaAttributes = function () {
761
+ // Firefox puts videos in tab order; remove.
762
+ this.$media.attr('tabindex', -1);
763
+
764
+ // Keep native player from displaying captions/subtitles by setting textTrack.mode='disabled'
765
+ // https://dev.w3.org/html5/spec-author-view/video.html#text-track-mode
766
+ // This *should* work but historically hasn't been supported in all browsers
767
+ // Workaround for non-supporting browsers is to remove default attribute
768
+ // We're doing that too in track.js > setupCaptions()
769
+ var textTracks = this.$media.get(0).textTracks;
770
+ if (textTracks) {
771
+ var i = 0;
772
+ while (i < textTracks.length) {
773
+ textTracks[i].mode = 'disabled';
774
+ i += 1;
775
+ }
776
+ }
777
+ };
778
+
779
+ AblePlayer.prototype.getPlayer = function() {
780
+
781
+ // Determine which player to use, if any
782
+ // return 'html5', 'youtube', 'vimeo', or null
783
+
784
+ var i, sourceType, $newItem;
785
+ if (this.youTubeId) {
786
+ if (this.mediaType !== 'video') {
787
+ // attempting to play a YouTube video using an element other than <video>
788
+ return null;
789
+ }
790
+ else {
791
+ return 'youtube';
792
+ }
793
+ }
794
+ else if (this.vimeoId) {
795
+ if (this.mediaType !== 'video') {
796
+ // attempting to play a Vimeo video using an element other than <video>
797
+ return null;
798
+ }
799
+ else {
800
+ return 'vimeo';
801
+ }
802
+
803
+ }
804
+ else if (this.testFallback ||
805
+ ((this.isUserAgent('msie 7') || this.isUserAgent('msie 8') || this.isUserAgent('msie 9')) && this.mediaType === 'video') ||
806
+ (this.isIOS() && (this.isIOS(4) || this.isIOS(5) || this.isIOS(6)))
807
+ ) {
808
+ // the user wants to test the fallback player, or
809
+ // the user is using an older version of IE or IOS,
810
+ // both of which had buggy implementation of HTML5 video
811
+ return null;
812
+ }
813
+ else if (this.media.canPlayType) {
814
+ return 'html5';
815
+ }
816
+ else {
817
+ // Browser does not support the available media file
818
+ return null;
819
+ }
820
+ };
914
821
 
915
822
  })(jQuery);