plyr-rails 2.0.11 → 2.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/plyr/rails.rb +1 -4
- data/lib/plyr/rails/version.rb +1 -1
- data/vendor/assets/javascripts/plyr.js +2 -3775
- data/vendor/assets/stylesheets/plyr.css +1 -0
- metadata +2 -4
- data/vendor/assets/stylesheets/mixins.scss +0 -29
- data/vendor/assets/stylesheets/plyr.scss +0 -758
- data/vendor/assets/stylesheets/variables.scss +0 -73
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b68c85d16f138eb61ed634f6d49e0559963627c
|
4
|
+
data.tar.gz: c091f2d0729a0c7acf9c01cb063e411902746482
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1893b4c943ce6b0f37bfa8cadc2c0bb3a0e8135135b1b53a07aca3010cff0c18d9b74f3efec897f171c48ceb75c36b170818cecea0fbce18befc0823d82a5d80
|
7
|
+
data.tar.gz: d91e46ac89e5b08bd5cff73c014383fa5afff009a1a149b5c724554588ea4f4ed15342b3071e159435c5a354e8736d53054cd50a639fda8a8cf99369ab5790b2
|
data/lib/plyr/rails.rb
CHANGED
@@ -4,10 +4,7 @@ module Plyr
|
|
4
4
|
module Rails
|
5
5
|
class Engine < ::Rails::Engine
|
6
6
|
initializer :append_dependent_assets_path, :group => :all do |app|
|
7
|
-
app.config.assets.paths += %w( sprite )
|
8
|
-
|
9
|
-
app.config.assets.precompile += %w( plyr.scss )
|
10
|
-
app.config.assets.precompile += %w( plyr.js )
|
7
|
+
app.config.assets.paths += %w( sprite )
|
11
8
|
|
12
9
|
app.config.assets.precompile += %w( plyr-captions-off.svg )
|
13
10
|
app.config.assets.precompile += %w( plyr-captions-on.svg )
|
data/lib/plyr/rails/version.rb
CHANGED
@@ -1,3775 +1,2 @@
|
|
1
|
-
// ==========================================================================
|
2
|
-
// Plyr
|
3
|
-
// plyr.js v2.0.11
|
4
|
-
// https://github.com/selz/plyr
|
5
|
-
// License: The MIT License (MIT)
|
6
|
-
// ==========================================================================
|
7
|
-
// Credits: http://paypal.github.io/accessible-html5-video-player/
|
8
|
-
// ==========================================================================
|
9
|
-
|
10
|
-
;(function(root, factory) {
|
11
|
-
'use strict';
|
12
|
-
/*global define,module*/
|
13
|
-
|
14
|
-
if (typeof module === 'object' && typeof module.exports === 'object') {
|
15
|
-
// Node, CommonJS-like
|
16
|
-
module.exports = factory(root, document);
|
17
|
-
} else if (typeof define === 'function' && define.amd) {
|
18
|
-
// AMD
|
19
|
-
define([], function () { return factory(root, document); });
|
20
|
-
} else {
|
21
|
-
// Browser globals (root is window)
|
22
|
-
root.plyr = factory(root, document);
|
23
|
-
}
|
24
|
-
}(typeof window !== 'undefined' ? window : this, function(window, document) {
|
25
|
-
'use strict';
|
26
|
-
|
27
|
-
// Globals
|
28
|
-
var fullscreen,
|
29
|
-
scroll = { x: 0, y: 0 },
|
30
|
-
|
31
|
-
// Default config
|
32
|
-
defaults = {
|
33
|
-
enabled: true,
|
34
|
-
debug: false,
|
35
|
-
autoplay: false,
|
36
|
-
loop: false,
|
37
|
-
seekTime: 10,
|
38
|
-
volume: 10,
|
39
|
-
volumeMin: 0,
|
40
|
-
volumeMax: 10,
|
41
|
-
volumeStep: 1,
|
42
|
-
duration: null,
|
43
|
-
displayDuration: true,
|
44
|
-
loadSprite: true,
|
45
|
-
iconPrefix: 'plyr',
|
46
|
-
iconUrl: 'https://cdn.plyr.io/2.0.11/plyr.svg',
|
47
|
-
clickToPlay: true,
|
48
|
-
hideControls: true,
|
49
|
-
showPosterOnEnd: false,
|
50
|
-
disableContextMenu: true,
|
51
|
-
keyboardShorcuts: {
|
52
|
-
focused: true,
|
53
|
-
global: false
|
54
|
-
},
|
55
|
-
tooltips: {
|
56
|
-
controls: false,
|
57
|
-
seek: true
|
58
|
-
},
|
59
|
-
selectors: {
|
60
|
-
html5: 'video, audio',
|
61
|
-
embed: '[data-type]',
|
62
|
-
editable: 'input, textarea, select, [contenteditable]',
|
63
|
-
container: '.plyr',
|
64
|
-
controls: {
|
65
|
-
container: null,
|
66
|
-
wrapper: '.plyr__controls'
|
67
|
-
},
|
68
|
-
labels: '[data-plyr]',
|
69
|
-
buttons: {
|
70
|
-
seek: '[data-plyr="seek"]',
|
71
|
-
play: '[data-plyr="play"]',
|
72
|
-
pause: '[data-plyr="pause"]',
|
73
|
-
restart: '[data-plyr="restart"]',
|
74
|
-
rewind: '[data-plyr="rewind"]',
|
75
|
-
forward: '[data-plyr="fast-forward"]',
|
76
|
-
mute: '[data-plyr="mute"]',
|
77
|
-
captions: '[data-plyr="captions"]',
|
78
|
-
fullscreen: '[data-plyr="fullscreen"]'
|
79
|
-
},
|
80
|
-
volume: {
|
81
|
-
input: '[data-plyr="volume"]',
|
82
|
-
display: '.plyr__volume--display'
|
83
|
-
},
|
84
|
-
progress: {
|
85
|
-
container: '.plyr__progress',
|
86
|
-
buffer: '.plyr__progress--buffer',
|
87
|
-
played: '.plyr__progress--played'
|
88
|
-
},
|
89
|
-
captions: '.plyr__captions',
|
90
|
-
currentTime: '.plyr__time--current',
|
91
|
-
duration: '.plyr__time--duration'
|
92
|
-
},
|
93
|
-
classes: {
|
94
|
-
setup: 'plyr--setup',
|
95
|
-
ready: 'plyr--ready',
|
96
|
-
videoWrapper: 'plyr__video-wrapper',
|
97
|
-
embedWrapper: 'plyr__video-embed',
|
98
|
-
type: 'plyr--{0}',
|
99
|
-
stopped: 'plyr--stopped',
|
100
|
-
playing: 'plyr--playing',
|
101
|
-
muted: 'plyr--muted',
|
102
|
-
loading: 'plyr--loading',
|
103
|
-
hover: 'plyr--hover',
|
104
|
-
tooltip: 'plyr__tooltip',
|
105
|
-
hidden: 'plyr__sr-only',
|
106
|
-
hideControls: 'plyr--hide-controls',
|
107
|
-
isIos: 'plyr--is-ios',
|
108
|
-
isTouch: 'plyr--is-touch',
|
109
|
-
captions: {
|
110
|
-
enabled: 'plyr--captions-enabled',
|
111
|
-
active: 'plyr--captions-active'
|
112
|
-
},
|
113
|
-
fullscreen: {
|
114
|
-
enabled: 'plyr--fullscreen-enabled',
|
115
|
-
active: 'plyr--fullscreen-active'
|
116
|
-
},
|
117
|
-
tabFocus: 'tab-focus'
|
118
|
-
},
|
119
|
-
captions: {
|
120
|
-
defaultActive: false
|
121
|
-
},
|
122
|
-
fullscreen: {
|
123
|
-
enabled: true,
|
124
|
-
fallback: true,
|
125
|
-
allowAudio: false
|
126
|
-
},
|
127
|
-
storage: {
|
128
|
-
enabled: true,
|
129
|
-
key: 'plyr'
|
130
|
-
},
|
131
|
-
controls: ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'fullscreen'],
|
132
|
-
i18n: {
|
133
|
-
restart: 'Restart',
|
134
|
-
rewind: 'Rewind {seektime} secs',
|
135
|
-
play: 'Play',
|
136
|
-
pause: 'Pause',
|
137
|
-
forward: 'Forward {seektime} secs',
|
138
|
-
played: 'played',
|
139
|
-
buffered: 'buffered',
|
140
|
-
currentTime: 'Current time',
|
141
|
-
duration: 'Duration',
|
142
|
-
volume: 'Volume',
|
143
|
-
toggleMute: 'Toggle Mute',
|
144
|
-
toggleCaptions: 'Toggle Captions',
|
145
|
-
toggleFullscreen: 'Toggle Fullscreen',
|
146
|
-
frameTitle: 'Player for {title}'
|
147
|
-
},
|
148
|
-
types: {
|
149
|
-
embed: ['youtube', 'vimeo', 'soundcloud'],
|
150
|
-
html5: ['video', 'audio']
|
151
|
-
},
|
152
|
-
// URLs
|
153
|
-
urls: {
|
154
|
-
vimeo: {
|
155
|
-
api: 'https://player.vimeo.com/api/player.js',
|
156
|
-
},
|
157
|
-
youtube: {
|
158
|
-
api: 'https://www.youtube.com/iframe_api'
|
159
|
-
},
|
160
|
-
soundcloud: {
|
161
|
-
api: 'https://w.soundcloud.com/player/api.js'
|
162
|
-
}
|
163
|
-
},
|
164
|
-
// Custom control listeners
|
165
|
-
listeners: {
|
166
|
-
seek: null,
|
167
|
-
play: null,
|
168
|
-
pause: null,
|
169
|
-
restart: null,
|
170
|
-
rewind: null,
|
171
|
-
forward: null,
|
172
|
-
mute: null,
|
173
|
-
volume: null,
|
174
|
-
captions: null,
|
175
|
-
fullscreen: null
|
176
|
-
},
|
177
|
-
// Events to watch on HTML5 media elements
|
178
|
-
events: ['ready', 'ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'seeked', 'emptied'],
|
179
|
-
// Logging
|
180
|
-
logPrefix: '[Plyr]'
|
181
|
-
};
|
182
|
-
|
183
|
-
// Credits: http://paypal.github.io/accessible-html5-video-player/
|
184
|
-
// Unfortunately, due to mixed support, UA sniffing is required
|
185
|
-
function _browserSniff() {
|
186
|
-
var ua = navigator.userAgent,
|
187
|
-
name = navigator.appName,
|
188
|
-
fullVersion = '' + parseFloat(navigator.appVersion),
|
189
|
-
majorVersion = parseInt(navigator.appVersion, 10),
|
190
|
-
nameOffset,
|
191
|
-
verOffset,
|
192
|
-
ix,
|
193
|
-
isIE = false,
|
194
|
-
isFirefox = false,
|
195
|
-
isChrome = false,
|
196
|
-
isSafari = false;
|
197
|
-
|
198
|
-
if ((navigator.appVersion.indexOf('Windows NT') !== -1) && (navigator.appVersion.indexOf('rv:11') !== -1)) {
|
199
|
-
// MSIE 11
|
200
|
-
isIE = true;
|
201
|
-
name = 'IE';
|
202
|
-
fullVersion = '11';
|
203
|
-
} else if ((verOffset = ua.indexOf('MSIE')) !== -1) {
|
204
|
-
// MSIE
|
205
|
-
isIE = true;
|
206
|
-
name = 'IE';
|
207
|
-
fullVersion = ua.substring(verOffset + 5);
|
208
|
-
} else if ((verOffset = ua.indexOf('Chrome')) !== -1) {
|
209
|
-
// Chrome
|
210
|
-
isChrome = true;
|
211
|
-
name = 'Chrome';
|
212
|
-
fullVersion = ua.substring(verOffset + 7);
|
213
|
-
} else if ((verOffset = ua.indexOf('Safari')) !== -1) {
|
214
|
-
// Safari
|
215
|
-
isSafari = true;
|
216
|
-
name = 'Safari';
|
217
|
-
fullVersion = ua.substring(verOffset + 7);
|
218
|
-
if ((verOffset = ua.indexOf('Version')) !== -1) {
|
219
|
-
fullVersion = ua.substring(verOffset + 8);
|
220
|
-
}
|
221
|
-
} else if ((verOffset = ua.indexOf('Firefox')) !== -1) {
|
222
|
-
// Firefox
|
223
|
-
isFirefox = true;
|
224
|
-
name = 'Firefox';
|
225
|
-
fullVersion = ua.substring(verOffset + 8);
|
226
|
-
} else if ((nameOffset = ua.lastIndexOf(' ') + 1) < (verOffset = ua.lastIndexOf('/'))) {
|
227
|
-
// In most other browsers, 'name/version' is at the end of userAgent
|
228
|
-
name = ua.substring(nameOffset,verOffset);
|
229
|
-
fullVersion = ua.substring(verOffset + 1);
|
230
|
-
|
231
|
-
if (name.toLowerCase() === name.toUpperCase()) {
|
232
|
-
name = navigator.appName;
|
233
|
-
}
|
234
|
-
}
|
235
|
-
|
236
|
-
// Trim the fullVersion string at semicolon/space if present
|
237
|
-
if ((ix = fullVersion.indexOf(';')) !== -1) {
|
238
|
-
fullVersion = fullVersion.substring(0, ix);
|
239
|
-
}
|
240
|
-
if ((ix = fullVersion.indexOf(' ')) !== -1) {
|
241
|
-
fullVersion = fullVersion.substring(0, ix);
|
242
|
-
}
|
243
|
-
|
244
|
-
// Get major version
|
245
|
-
majorVersion = parseInt('' + fullVersion, 10);
|
246
|
-
if (isNaN(majorVersion)) {
|
247
|
-
fullVersion = '' + parseFloat(navigator.appVersion);
|
248
|
-
majorVersion = parseInt(navigator.appVersion, 10);
|
249
|
-
}
|
250
|
-
|
251
|
-
// Return data
|
252
|
-
return {
|
253
|
-
name: name,
|
254
|
-
version: majorVersion,
|
255
|
-
isIE: isIE,
|
256
|
-
isFirefox: isFirefox,
|
257
|
-
isChrome: isChrome,
|
258
|
-
isSafari: isSafari,
|
259
|
-
isIos: /(iPad|iPhone|iPod)/g.test(navigator.platform),
|
260
|
-
isIphone: /(iPhone|iPod)/g.test(navigator.userAgent),
|
261
|
-
isTouch: 'ontouchstart' in document.documentElement
|
262
|
-
};
|
263
|
-
}
|
264
|
-
|
265
|
-
// Check for mime type support against a player instance
|
266
|
-
// Credits: http://diveintohtml5.info/everything.html
|
267
|
-
// Related: http://www.leanbackplyr.com/test/h5mt.html
|
268
|
-
function _supportMime(plyr, mimeType) {
|
269
|
-
var media = plyr.media;
|
270
|
-
|
271
|
-
if (plyr.type === 'video') {
|
272
|
-
// Check type
|
273
|
-
switch (mimeType) {
|
274
|
-
case 'video/webm': return !!(media.canPlayType && media.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/, ''));
|
275
|
-
case 'video/mp4': return !!(media.canPlayType && media.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/, ''));
|
276
|
-
case 'video/ogg': return !!(media.canPlayType && media.canPlayType('video/ogg; codecs="theora"').replace(/no/, ''));
|
277
|
-
}
|
278
|
-
} else if (plyr.type === 'audio') {
|
279
|
-
// Check type
|
280
|
-
switch (mimeType) {
|
281
|
-
case 'audio/mpeg': return !!(media.canPlayType && media.canPlayType('audio/mpeg;').replace(/no/, ''));
|
282
|
-
case 'audio/ogg': return !!(media.canPlayType && media.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/, ''));
|
283
|
-
case 'audio/wav': return !!(media.canPlayType && media.canPlayType('audio/wav; codecs="1"').replace(/no/, ''));
|
284
|
-
}
|
285
|
-
}
|
286
|
-
|
287
|
-
// If we got this far, we're stuffed
|
288
|
-
return false;
|
289
|
-
}
|
290
|
-
|
291
|
-
// Inject a script
|
292
|
-
function _injectScript(source) {
|
293
|
-
if (document.querySelectorAll('script[src="' + source + '"]').length) {
|
294
|
-
return;
|
295
|
-
}
|
296
|
-
|
297
|
-
var tag = document.createElement('script');
|
298
|
-
tag.src = source;
|
299
|
-
var firstScriptTag = document.getElementsByTagName('script')[0];
|
300
|
-
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
301
|
-
}
|
302
|
-
|
303
|
-
// Element exists in an array
|
304
|
-
function _inArray(haystack, needle) {
|
305
|
-
return Array.prototype.indexOf && (haystack.indexOf(needle) !== -1);
|
306
|
-
}
|
307
|
-
|
308
|
-
// Replace all
|
309
|
-
function _replaceAll(string, find, replace) {
|
310
|
-
return string.replace(new RegExp(find.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g, '\\$1'), 'g'), replace);
|
311
|
-
}
|
312
|
-
|
313
|
-
// Wrap an element
|
314
|
-
function _wrap(elements, wrapper) {
|
315
|
-
// Convert `elements` to an array, if necessary.
|
316
|
-
if (!elements.length) {
|
317
|
-
elements = [elements];
|
318
|
-
}
|
319
|
-
|
320
|
-
// Loops backwards to prevent having to clone the wrapper on the
|
321
|
-
// first element (see `child` below).
|
322
|
-
for (var i = elements.length - 1; i >= 0; i--) {
|
323
|
-
var child = (i > 0) ? wrapper.cloneNode(true) : wrapper;
|
324
|
-
var element = elements[i];
|
325
|
-
|
326
|
-
// Cache the current parent and sibling.
|
327
|
-
var parent = element.parentNode;
|
328
|
-
var sibling = element.nextSibling;
|
329
|
-
|
330
|
-
// Wrap the element (is automatically removed from its current
|
331
|
-
// parent).
|
332
|
-
child.appendChild(element);
|
333
|
-
|
334
|
-
// If the element had a sibling, insert the wrapper before
|
335
|
-
// the sibling to maintain the HTML structure; otherwise, just
|
336
|
-
// append it to the parent.
|
337
|
-
if (sibling) {
|
338
|
-
parent.insertBefore(child, sibling);
|
339
|
-
} else {
|
340
|
-
parent.appendChild(child);
|
341
|
-
}
|
342
|
-
|
343
|
-
return child;
|
344
|
-
}
|
345
|
-
}
|
346
|
-
|
347
|
-
// Unwrap an element
|
348
|
-
// http://plainjs.com/javascript/manipulation/unwrap-a-dom-element-35/
|
349
|
-
/*function _unwrap(wrapper) {
|
350
|
-
// Get the element's parent node
|
351
|
-
var parent = wrapper.parentNode;
|
352
|
-
|
353
|
-
// Move all children out of the element
|
354
|
-
while (wrapper.firstChild) {
|
355
|
-
parent.insertBefore(wrapper.firstChild, wrapper);
|
356
|
-
}
|
357
|
-
|
358
|
-
// Remove the empty element
|
359
|
-
parent.removeChild(wrapper);
|
360
|
-
}*/
|
361
|
-
|
362
|
-
// Remove an element
|
363
|
-
function _remove(element) {
|
364
|
-
if (!element) {
|
365
|
-
return;
|
366
|
-
}
|
367
|
-
element.parentNode.removeChild(element);
|
368
|
-
}
|
369
|
-
|
370
|
-
// Prepend child
|
371
|
-
function _prependChild(parent, element) {
|
372
|
-
parent.insertBefore(element, parent.firstChild);
|
373
|
-
}
|
374
|
-
|
375
|
-
// Set attributes
|
376
|
-
function _setAttributes(element, attributes) {
|
377
|
-
for (var key in attributes) {
|
378
|
-
element.setAttribute(key, (_is.boolean(attributes[key]) && attributes[key]) ? '' : attributes[key]);
|
379
|
-
}
|
380
|
-
}
|
381
|
-
|
382
|
-
// Insert a HTML element
|
383
|
-
function _insertElement(type, parent, attributes) {
|
384
|
-
// Create a new <element>
|
385
|
-
var element = document.createElement(type);
|
386
|
-
|
387
|
-
// Set all passed attributes
|
388
|
-
_setAttributes(element, attributes);
|
389
|
-
|
390
|
-
// Inject the new element
|
391
|
-
_prependChild(parent, element);
|
392
|
-
}
|
393
|
-
|
394
|
-
// Get a classname from selector
|
395
|
-
function _getClassname(selector) {
|
396
|
-
return selector.replace('.', '');
|
397
|
-
}
|
398
|
-
|
399
|
-
// Toggle class on an element
|
400
|
-
function _toggleClass(element, className, state) {
|
401
|
-
if (element) {
|
402
|
-
if (element.classList) {
|
403
|
-
element.classList[state ? 'add' : 'remove'](className);
|
404
|
-
} else {
|
405
|
-
var name = (' ' + element.className + ' ').replace(/\s+/g, ' ').replace(' ' + className + ' ', '');
|
406
|
-
element.className = name + (state ? ' ' + className : '');
|
407
|
-
}
|
408
|
-
}
|
409
|
-
}
|
410
|
-
|
411
|
-
// Has class name
|
412
|
-
function _hasClass(element, className) {
|
413
|
-
if (element) {
|
414
|
-
if (element.classList) {
|
415
|
-
return element.classList.contains(className);
|
416
|
-
} else {
|
417
|
-
return new RegExp('(\\s|^)' + className + '(\\s|$)').test(element.className);
|
418
|
-
}
|
419
|
-
}
|
420
|
-
return false;
|
421
|
-
}
|
422
|
-
|
423
|
-
// Element matches selector
|
424
|
-
function _matches(element, selector) {
|
425
|
-
var p = Element.prototype;
|
426
|
-
|
427
|
-
var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) {
|
428
|
-
return [].indexOf.call(document.querySelectorAll(s), this) !== -1;
|
429
|
-
};
|
430
|
-
|
431
|
-
return f.call(element, selector);
|
432
|
-
}
|
433
|
-
|
434
|
-
// Bind along with custom handler
|
435
|
-
function _proxyListener(element, eventName, userListener, defaultListener, useCapture) {
|
436
|
-
_on(element, eventName, function(event) {
|
437
|
-
if (userListener) {
|
438
|
-
userListener.apply(element, [event]);
|
439
|
-
}
|
440
|
-
defaultListener.apply(element, [event]);
|
441
|
-
}, useCapture);
|
442
|
-
}
|
443
|
-
|
444
|
-
// Toggle event listener
|
445
|
-
function _toggleListener(element, events, callback, toggle, useCapture) {
|
446
|
-
var eventList = events.split(' ');
|
447
|
-
|
448
|
-
// Whether the listener is a capturing listener or not
|
449
|
-
// Default to false
|
450
|
-
if (!_is.boolean(useCapture)) {
|
451
|
-
useCapture = false;
|
452
|
-
}
|
453
|
-
|
454
|
-
// If a nodelist is passed, call itself on each node
|
455
|
-
if (element instanceof NodeList) {
|
456
|
-
for (var x = 0; x < element.length; x++) {
|
457
|
-
if (element[x] instanceof Node) {
|
458
|
-
_toggleListener(element[x], arguments[1], arguments[2], arguments[3]);
|
459
|
-
}
|
460
|
-
}
|
461
|
-
return;
|
462
|
-
}
|
463
|
-
|
464
|
-
// If a single node is passed, bind the event listener
|
465
|
-
for (var i = 0; i < eventList.length; i++) {
|
466
|
-
element[toggle ? 'addEventListener' : 'removeEventListener'](eventList[i], callback, useCapture);
|
467
|
-
}
|
468
|
-
}
|
469
|
-
|
470
|
-
// Bind event
|
471
|
-
function _on(element, events, callback, useCapture) {
|
472
|
-
if (element) {
|
473
|
-
_toggleListener(element, events, callback, true, useCapture);
|
474
|
-
}
|
475
|
-
}
|
476
|
-
|
477
|
-
// Unbind event
|
478
|
-
/*function _off(element, events, callback, useCapture) {
|
479
|
-
if (element) {
|
480
|
-
_toggleListener(element, events, callback, false, useCapture);
|
481
|
-
}
|
482
|
-
}*/
|
483
|
-
|
484
|
-
// Trigger event
|
485
|
-
function _event(element, type, bubbles, properties) {
|
486
|
-
// Bail if no element
|
487
|
-
if (!element || !type) {
|
488
|
-
return;
|
489
|
-
}
|
490
|
-
|
491
|
-
// Default bubbles to false
|
492
|
-
if (!_is.boolean(bubbles)) {
|
493
|
-
bubbles = false;
|
494
|
-
}
|
495
|
-
|
496
|
-
// Create and dispatch the event
|
497
|
-
var event = new CustomEvent(type, {
|
498
|
-
bubbles: bubbles,
|
499
|
-
detail: properties
|
500
|
-
});
|
501
|
-
|
502
|
-
// Dispatch the event
|
503
|
-
element.dispatchEvent(event);
|
504
|
-
}
|
505
|
-
|
506
|
-
// Toggle aria-pressed state on a toggle button
|
507
|
-
// http://www.ssbbartgroup.com/blog/how-not-to-misuse-aria-states-properties-and-roles
|
508
|
-
function _toggleState(target, state) {
|
509
|
-
// Bail if no target
|
510
|
-
if (!target) {
|
511
|
-
return;
|
512
|
-
}
|
513
|
-
|
514
|
-
// Get state
|
515
|
-
state = (_is.boolean(state) ? state : !target.getAttribute('aria-pressed'));
|
516
|
-
|
517
|
-
// Set the attribute on target
|
518
|
-
target.setAttribute('aria-pressed', state);
|
519
|
-
|
520
|
-
return state;
|
521
|
-
}
|
522
|
-
|
523
|
-
// Get percentage
|
524
|
-
function _getPercentage(current, max) {
|
525
|
-
if (current === 0 || max === 0 || isNaN(current) || isNaN(max)) {
|
526
|
-
return 0;
|
527
|
-
}
|
528
|
-
return ((current / max) * 100).toFixed(2);
|
529
|
-
}
|
530
|
-
|
531
|
-
// Deep extend/merge destination object with N more objects
|
532
|
-
// http://andrewdupont.net/2009/08/28/deep-extending-objects-in-javascript/
|
533
|
-
// Removed call to arguments.callee (used explicit function name instead)
|
534
|
-
function _extend() {
|
535
|
-
// Get arguments
|
536
|
-
var objects = arguments;
|
537
|
-
|
538
|
-
// Bail if nothing to merge
|
539
|
-
if (!objects.length) {
|
540
|
-
return;
|
541
|
-
}
|
542
|
-
|
543
|
-
// Return first if specified but nothing to merge
|
544
|
-
if (objects.length === 1) {
|
545
|
-
return objects[0];
|
546
|
-
}
|
547
|
-
|
548
|
-
// First object is the destination
|
549
|
-
var destination = Array.prototype.shift.call(objects),
|
550
|
-
length = objects.length;
|
551
|
-
|
552
|
-
// Loop through all objects to merge
|
553
|
-
for (var i = 0; i < length; i++) {
|
554
|
-
var source = objects[i];
|
555
|
-
|
556
|
-
for (var property in source) {
|
557
|
-
if (source[property] && source[property].constructor && source[property].constructor === Object) {
|
558
|
-
destination[property] = destination[property] || {};
|
559
|
-
_extend(destination[property], source[property]);
|
560
|
-
} else {
|
561
|
-
destination[property] = source[property];
|
562
|
-
}
|
563
|
-
}
|
564
|
-
}
|
565
|
-
|
566
|
-
return destination;
|
567
|
-
}
|
568
|
-
|
569
|
-
// Check variable types
|
570
|
-
var _is = {
|
571
|
-
object: function(input) {
|
572
|
-
return input !== null && typeof(input) === 'object';
|
573
|
-
},
|
574
|
-
array: function(input) {
|
575
|
-
return input !== null && (typeof(input) === 'object' && input.constructor === Array);
|
576
|
-
},
|
577
|
-
number: function(input) {
|
578
|
-
return input !== null && (typeof(input) === 'number' && !isNaN(input - 0) || (typeof input === 'object' && input.constructor === Number));
|
579
|
-
},
|
580
|
-
string: function(input) {
|
581
|
-
return input !== null && (typeof input === 'string' || (typeof input === 'object' && input.constructor === String));
|
582
|
-
},
|
583
|
-
boolean: function(input) {
|
584
|
-
return input !== null && typeof input === 'boolean';
|
585
|
-
},
|
586
|
-
nodeList: function(input) {
|
587
|
-
return input !== null && input instanceof NodeList;
|
588
|
-
},
|
589
|
-
htmlElement: function(input) {
|
590
|
-
return input !== null && input instanceof HTMLElement;
|
591
|
-
},
|
592
|
-
function: function(input) {
|
593
|
-
return input !== null && typeof input === 'function';
|
594
|
-
},
|
595
|
-
undefined: function(input) {
|
596
|
-
return input !== null && typeof input === 'undefined';
|
597
|
-
}
|
598
|
-
};
|
599
|
-
|
600
|
-
// Parse YouTube ID from url
|
601
|
-
function _parseYouTubeId(url) {
|
602
|
-
var regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;
|
603
|
-
return (url.match(regex)) ? RegExp.$2 : url;
|
604
|
-
}
|
605
|
-
|
606
|
-
// Parse Vimeo ID from url
|
607
|
-
function _parseVimeoId(url) {
|
608
|
-
var regex = /^.*(vimeo.com\/|video\/)(\d+).*/;
|
609
|
-
return (url.match(regex)) ? RegExp.$2 : url;
|
610
|
-
}
|
611
|
-
|
612
|
-
// Fullscreen API
|
613
|
-
function _fullscreen() {
|
614
|
-
var fullscreen = {
|
615
|
-
supportsFullScreen: false,
|
616
|
-
isFullScreen: function() { return false; },
|
617
|
-
requestFullScreen: function() {},
|
618
|
-
cancelFullScreen: function() {},
|
619
|
-
fullScreenEventName: '',
|
620
|
-
element: null,
|
621
|
-
prefix: ''
|
622
|
-
},
|
623
|
-
browserPrefixes = 'webkit o moz ms khtml'.split(' ');
|
624
|
-
|
625
|
-
// Check for native support
|
626
|
-
if (!_is.undefined(document.cancelFullScreen)) {
|
627
|
-
fullscreen.supportsFullScreen = true;
|
628
|
-
} else {
|
629
|
-
// Check for fullscreen support by vendor prefix
|
630
|
-
for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
|
631
|
-
fullscreen.prefix = browserPrefixes[i];
|
632
|
-
|
633
|
-
if (!_is.undefined(document[fullscreen.prefix + 'CancelFullScreen'])) {
|
634
|
-
fullscreen.supportsFullScreen = true;
|
635
|
-
break;
|
636
|
-
} else if (!_is.undefined(document.msExitFullscreen) && document.msFullscreenEnabled) {
|
637
|
-
// Special case for MS (when isn't it?)
|
638
|
-
fullscreen.prefix = 'ms';
|
639
|
-
fullscreen.supportsFullScreen = true;
|
640
|
-
break;
|
641
|
-
}
|
642
|
-
}
|
643
|
-
}
|
644
|
-
|
645
|
-
// Update methods to do something useful
|
646
|
-
if (fullscreen.supportsFullScreen) {
|
647
|
-
// Yet again Microsoft awesomeness,
|
648
|
-
// Sometimes the prefix is 'ms', sometimes 'MS' to keep you on your toes
|
649
|
-
fullscreen.fullScreenEventName = (fullscreen.prefix === 'ms' ? 'MSFullscreenChange' : fullscreen.prefix + 'fullscreenchange');
|
650
|
-
|
651
|
-
fullscreen.isFullScreen = function(element) {
|
652
|
-
if (_is.undefined(element)) {
|
653
|
-
element = document.body;
|
654
|
-
}
|
655
|
-
switch (this.prefix) {
|
656
|
-
case '':
|
657
|
-
return document.fullscreenElement === element;
|
658
|
-
case 'moz':
|
659
|
-
return document.mozFullScreenElement === element;
|
660
|
-
default:
|
661
|
-
return document[this.prefix + 'FullscreenElement'] === element;
|
662
|
-
}
|
663
|
-
};
|
664
|
-
fullscreen.requestFullScreen = function(element) {
|
665
|
-
if (_is.undefined(element)) {
|
666
|
-
element = document.body;
|
667
|
-
}
|
668
|
-
return (this.prefix === '') ? element.requestFullScreen() : element[this.prefix + (this.prefix === 'ms' ? 'RequestFullscreen' : 'RequestFullScreen')]();
|
669
|
-
};
|
670
|
-
fullscreen.cancelFullScreen = function() {
|
671
|
-
return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + (this.prefix === 'ms' ? 'ExitFullscreen' : 'CancelFullScreen')]();
|
672
|
-
};
|
673
|
-
fullscreen.element = function() {
|
674
|
-
return (this.prefix === '') ? document.fullscreenElement : document[this.prefix + 'FullscreenElement'];
|
675
|
-
};
|
676
|
-
}
|
677
|
-
|
678
|
-
return fullscreen;
|
679
|
-
}
|
680
|
-
|
681
|
-
// Local storage
|
682
|
-
var _storage = {
|
683
|
-
supported: (function() {
|
684
|
-
if (!('localStorage' in window)) {
|
685
|
-
return false;
|
686
|
-
}
|
687
|
-
|
688
|
-
// Try to use it (it might be disabled, e.g. user is in private/porn mode)
|
689
|
-
// see: https://github.com/Selz/plyr/issues/131
|
690
|
-
try {
|
691
|
-
// Add test item
|
692
|
-
window.localStorage.setItem('___test', 'OK');
|
693
|
-
|
694
|
-
// Get the test item
|
695
|
-
var result = window.localStorage.getItem('___test');
|
696
|
-
|
697
|
-
// Clean up
|
698
|
-
window.localStorage.removeItem('___test');
|
699
|
-
|
700
|
-
// Check if value matches
|
701
|
-
return (result === 'OK');
|
702
|
-
}
|
703
|
-
catch (e) {
|
704
|
-
return false;
|
705
|
-
}
|
706
|
-
|
707
|
-
return false;
|
708
|
-
})()
|
709
|
-
};
|
710
|
-
|
711
|
-
// Player instance
|
712
|
-
function Plyr(media, config) {
|
713
|
-
var plyr = this,
|
714
|
-
timers = {},
|
715
|
-
api;
|
716
|
-
|
717
|
-
// Set media
|
718
|
-
plyr.media = media;
|
719
|
-
var original = media.cloneNode(true);
|
720
|
-
|
721
|
-
// Trigger events, with plyr instance passed
|
722
|
-
function _triggerEvent(element, type, bubbles, properties) {
|
723
|
-
_event(element, type, bubbles, _extend({}, properties, {
|
724
|
-
plyr: api
|
725
|
-
}));
|
726
|
-
}
|
727
|
-
|
728
|
-
// Debugging
|
729
|
-
function _console(type, args) {
|
730
|
-
if (config.debug && window.console) {
|
731
|
-
args = Array.prototype.slice.call(args);
|
732
|
-
|
733
|
-
if (_is.string(config.logPrefix) && config.logPrefix.length) {
|
734
|
-
args.unshift(config.logPrefix);
|
735
|
-
}
|
736
|
-
|
737
|
-
console[type].apply(console, args);
|
738
|
-
}
|
739
|
-
}
|
740
|
-
var _log = function() { _console('log', arguments) },
|
741
|
-
_warn = function() { _console('warn', arguments) };
|
742
|
-
|
743
|
-
// Log config options
|
744
|
-
_log('Config', config);
|
745
|
-
|
746
|
-
// Get icon URL
|
747
|
-
function _getIconUrl() {
|
748
|
-
return {
|
749
|
-
url: config.iconUrl,
|
750
|
-
absolute: (config.iconUrl.indexOf("http") === 0) || plyr.browser.isIE
|
751
|
-
};
|
752
|
-
}
|
753
|
-
|
754
|
-
// Build the default HTML
|
755
|
-
function _buildControls() {
|
756
|
-
// Create html array
|
757
|
-
var html = [],
|
758
|
-
iconUrl = _getIconUrl(),
|
759
|
-
iconPath = (!iconUrl.absolute ? iconUrl.url : '') + '#' + config.iconPrefix;
|
760
|
-
|
761
|
-
// Larger overlaid play button
|
762
|
-
if (_inArray(config.controls, 'play-large')) {
|
763
|
-
html.push(
|
764
|
-
'<button type="button" data-plyr="play" class="plyr__play-large">',
|
765
|
-
'<svg><use xlink:href="' + iconPath + '-play" /></svg>',
|
766
|
-
'<span class="plyr__sr-only">' + config.i18n.play + '</span>',
|
767
|
-
'</button>'
|
768
|
-
);
|
769
|
-
}
|
770
|
-
|
771
|
-
html.push('<div class="plyr__controls">');
|
772
|
-
|
773
|
-
// Restart button
|
774
|
-
if (_inArray(config.controls, 'restart')) {
|
775
|
-
html.push(
|
776
|
-
'<button type="button" data-plyr="restart">',
|
777
|
-
'<svg><use xlink:href="' + iconPath + '-restart" /></svg>',
|
778
|
-
'<span class="plyr__sr-only">' + config.i18n.restart + '</span>',
|
779
|
-
'</button>'
|
780
|
-
);
|
781
|
-
}
|
782
|
-
|
783
|
-
// Rewind button
|
784
|
-
if (_inArray(config.controls, 'rewind')) {
|
785
|
-
html.push(
|
786
|
-
'<button type="button" data-plyr="rewind">',
|
787
|
-
'<svg><use xlink:href="' + iconPath + '-rewind" /></svg>',
|
788
|
-
'<span class="plyr__sr-only">' + config.i18n.rewind + '</span>',
|
789
|
-
'</button>'
|
790
|
-
);
|
791
|
-
}
|
792
|
-
|
793
|
-
// Play Pause button
|
794
|
-
// TODO: This should be a toggle button really?
|
795
|
-
if (_inArray(config.controls, 'play')) {
|
796
|
-
html.push(
|
797
|
-
'<button type="button" data-plyr="play">',
|
798
|
-
'<svg><use xlink:href="' + iconPath + '-play" /></svg>',
|
799
|
-
'<span class="plyr__sr-only">' + config.i18n.play + '</span>',
|
800
|
-
'</button>',
|
801
|
-
'<button type="button" data-plyr="pause">',
|
802
|
-
'<svg><use xlink:href="' + iconPath + '-pause" /></svg>',
|
803
|
-
'<span class="plyr__sr-only">' + config.i18n.pause + '</span>',
|
804
|
-
'</button>'
|
805
|
-
);
|
806
|
-
}
|
807
|
-
|
808
|
-
// Fast forward button
|
809
|
-
if (_inArray(config.controls, 'fast-forward')) {
|
810
|
-
html.push(
|
811
|
-
'<button type="button" data-plyr="fast-forward">',
|
812
|
-
'<svg><use xlink:href="' + iconPath + '-fast-forward" /></svg>',
|
813
|
-
'<span class="plyr__sr-only">' + config.i18n.forward + '</span>',
|
814
|
-
'</button>'
|
815
|
-
);
|
816
|
-
}
|
817
|
-
|
818
|
-
// Progress
|
819
|
-
if (_inArray(config.controls, 'progress')) {
|
820
|
-
// Create progress
|
821
|
-
html.push('<span class="plyr__progress">',
|
822
|
-
'<label for="seek{id}" class="plyr__sr-only">Seek</label>',
|
823
|
-
'<input id="seek{id}" class="plyr__progress--seek" type="range" min="0" max="100" step="0.1" value="0" data-plyr="seek">',
|
824
|
-
'<progress class="plyr__progress--played" max="100" value="0" role="presentation"></progress>',
|
825
|
-
'<progress class="plyr__progress--buffer" max="100" value="0">',
|
826
|
-
'<span>0</span>% ' + config.i18n.buffered,
|
827
|
-
'</progress>');
|
828
|
-
|
829
|
-
// Seek tooltip
|
830
|
-
if (config.tooltips.seek) {
|
831
|
-
html.push('<span class="plyr__tooltip">00:00</span>');
|
832
|
-
}
|
833
|
-
|
834
|
-
// Close
|
835
|
-
html.push('</span>');
|
836
|
-
}
|
837
|
-
|
838
|
-
// Media current time display
|
839
|
-
if (_inArray(config.controls, 'current-time')) {
|
840
|
-
html.push(
|
841
|
-
'<span class="plyr__time">',
|
842
|
-
'<span class="plyr__sr-only">' + config.i18n.currentTime + '</span>',
|
843
|
-
'<span class="plyr__time--current">00:00</span>',
|
844
|
-
'</span>'
|
845
|
-
);
|
846
|
-
}
|
847
|
-
|
848
|
-
// Media duration display
|
849
|
-
if (_inArray(config.controls, 'duration')) {
|
850
|
-
html.push(
|
851
|
-
'<span class="plyr__time">',
|
852
|
-
'<span class="plyr__sr-only">' + config.i18n.duration + '</span>',
|
853
|
-
'<span class="plyr__time--duration">00:00</span>',
|
854
|
-
'</span>'
|
855
|
-
);
|
856
|
-
}
|
857
|
-
|
858
|
-
// Toggle mute button
|
859
|
-
if (_inArray(config.controls, 'mute')) {
|
860
|
-
html.push(
|
861
|
-
'<button type="button" data-plyr="mute">',
|
862
|
-
'<svg class="icon--muted"><use xlink:href="' + iconPath + '-muted" /></svg>',
|
863
|
-
'<svg><use xlink:href="' + iconPath + '-volume" /></svg>',
|
864
|
-
'<span class="plyr__sr-only">' + config.i18n.toggleMute + '</span>',
|
865
|
-
'</button>'
|
866
|
-
);
|
867
|
-
}
|
868
|
-
|
869
|
-
// Volume range control
|
870
|
-
if (_inArray(config.controls, 'volume')) {
|
871
|
-
html.push(
|
872
|
-
'<span class="plyr__volume">',
|
873
|
-
'<label for="volume{id}" class="plyr__sr-only">' + config.i18n.volume + '</label>',
|
874
|
-
'<input id="volume{id}" class="plyr__volume--input" type="range" min="' + config.volumeMin + '" max="' + config.volumeMax + '" value="' + config.volume + '" data-plyr="volume">',
|
875
|
-
'<progress class="plyr__volume--display" max="' + config.volumeMax + '" value="' + config.volumeMin + '" role="presentation"></progress>',
|
876
|
-
'</span>'
|
877
|
-
);
|
878
|
-
}
|
879
|
-
|
880
|
-
// Toggle captions button
|
881
|
-
if (_inArray(config.controls, 'captions')) {
|
882
|
-
html.push(
|
883
|
-
'<button type="button" data-plyr="captions">',
|
884
|
-
'<svg class="icon--captions-on"><use xlink:href="' + iconPath + '-captions-on" /></svg>',
|
885
|
-
'<svg><use xlink:href="' + iconPath+ '-captions-off" /></svg>',
|
886
|
-
'<span class="plyr__sr-only">' + config.i18n.toggleCaptions + '</span>',
|
887
|
-
'</button>'
|
888
|
-
);
|
889
|
-
}
|
890
|
-
|
891
|
-
// Toggle fullscreen button
|
892
|
-
if (_inArray(config.controls, 'fullscreen')) {
|
893
|
-
html.push(
|
894
|
-
'<button type="button" data-plyr="fullscreen">',
|
895
|
-
'<svg class="icon--exit-fullscreen"><use xlink:href="' + iconPath + '-exit-fullscreen" /></svg>',
|
896
|
-
'<svg><use xlink:href="' + iconPath + '-enter-fullscreen" /></svg>',
|
897
|
-
'<span class="plyr__sr-only">' + config.i18n.toggleFullscreen + '</span>',
|
898
|
-
'</button>'
|
899
|
-
);
|
900
|
-
}
|
901
|
-
|
902
|
-
// Close everything
|
903
|
-
html.push('</div>');
|
904
|
-
|
905
|
-
return html.join('');
|
906
|
-
}
|
907
|
-
|
908
|
-
// Setup fullscreen
|
909
|
-
function _setupFullscreen() {
|
910
|
-
if (!plyr.supported.full) {
|
911
|
-
return;
|
912
|
-
}
|
913
|
-
|
914
|
-
if ((plyr.type !== 'audio' || config.fullscreen.allowAudio) && config.fullscreen.enabled) {
|
915
|
-
// Check for native support
|
916
|
-
var nativeSupport = fullscreen.supportsFullScreen;
|
917
|
-
|
918
|
-
if (nativeSupport || (config.fullscreen.fallback && !_inFrame())) {
|
919
|
-
_log((nativeSupport ? 'Native' : 'Fallback') + ' fullscreen enabled');
|
920
|
-
|
921
|
-
// Add styling hook
|
922
|
-
_toggleClass(plyr.container, config.classes.fullscreen.enabled, true);
|
923
|
-
} else {
|
924
|
-
_log('Fullscreen not supported and fallback disabled');
|
925
|
-
}
|
926
|
-
|
927
|
-
// Toggle state
|
928
|
-
if (plyr.buttons && plyr.buttons.fullscreen) {
|
929
|
-
_toggleState(plyr.buttons.fullscreen, false);
|
930
|
-
}
|
931
|
-
|
932
|
-
// Setup focus trap
|
933
|
-
_focusTrap();
|
934
|
-
}
|
935
|
-
}
|
936
|
-
|
937
|
-
// Setup captions
|
938
|
-
function _setupCaptions() {
|
939
|
-
// Bail if not HTML5 video
|
940
|
-
if (plyr.type !== 'video') {
|
941
|
-
return;
|
942
|
-
}
|
943
|
-
|
944
|
-
// Inject the container
|
945
|
-
if (!_getElement(config.selectors.captions)) {
|
946
|
-
plyr.videoContainer.insertAdjacentHTML('afterbegin', '<div class="' + _getClassname(config.selectors.captions) + '"></div>');
|
947
|
-
}
|
948
|
-
|
949
|
-
// Determine if HTML5 textTracks is supported
|
950
|
-
plyr.usingTextTracks = false;
|
951
|
-
if (plyr.media.textTracks) {
|
952
|
-
plyr.usingTextTracks = true;
|
953
|
-
}
|
954
|
-
|
955
|
-
// Get URL of caption file if exists
|
956
|
-
var captionSrc = '',
|
957
|
-
kind,
|
958
|
-
children = plyr.media.childNodes;
|
959
|
-
|
960
|
-
for (var i = 0; i < children.length; i++) {
|
961
|
-
if (children[i].nodeName.toLowerCase() === 'track') {
|
962
|
-
kind = children[i].kind;
|
963
|
-
if (kind === 'captions' || kind === 'subtitles') {
|
964
|
-
captionSrc = children[i].getAttribute('src');
|
965
|
-
}
|
966
|
-
}
|
967
|
-
}
|
968
|
-
|
969
|
-
// Record if caption file exists or not
|
970
|
-
plyr.captionExists = true;
|
971
|
-
if (captionSrc === '') {
|
972
|
-
plyr.captionExists = false;
|
973
|
-
_log('No caption track found');
|
974
|
-
} else {
|
975
|
-
_log('Caption track found; URI: ' + captionSrc);
|
976
|
-
}
|
977
|
-
|
978
|
-
// If no caption file exists, hide container for caption text
|
979
|
-
if (!plyr.captionExists) {
|
980
|
-
_toggleClass(plyr.container, config.classes.captions.enabled);
|
981
|
-
} else {
|
982
|
-
// Turn off native caption rendering to avoid double captions
|
983
|
-
// This doesn't seem to work in Safari 7+, so the <track> elements are removed from the dom below
|
984
|
-
var tracks = plyr.media.textTracks;
|
985
|
-
for (var x = 0; x < tracks.length; x++) {
|
986
|
-
tracks[x].mode = 'hidden';
|
987
|
-
}
|
988
|
-
|
989
|
-
// Enable UI
|
990
|
-
_showCaptions(plyr);
|
991
|
-
|
992
|
-
// Disable unsupported browsers than report false positive
|
993
|
-
// Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1033144
|
994
|
-
if ((plyr.browser.isIE && plyr.browser.version >= 10) ||
|
995
|
-
(plyr.browser.isFirefox && plyr.browser.version >= 31)) {
|
996
|
-
|
997
|
-
// Debugging
|
998
|
-
_log('Detected browser with known TextTrack issues - using manual fallback');
|
999
|
-
|
1000
|
-
// Set to false so skips to 'manual' captioning
|
1001
|
-
plyr.usingTextTracks = false;
|
1002
|
-
}
|
1003
|
-
|
1004
|
-
// Rendering caption tracks
|
1005
|
-
// Native support required - http://caniuse.com/webvtt
|
1006
|
-
if (plyr.usingTextTracks) {
|
1007
|
-
_log('TextTracks supported');
|
1008
|
-
|
1009
|
-
for (var y = 0; y < tracks.length; y++) {
|
1010
|
-
var track = tracks[y];
|
1011
|
-
|
1012
|
-
if (track.kind === 'captions' || track.kind === 'subtitles') {
|
1013
|
-
_on(track, 'cuechange', function() {
|
1014
|
-
// Display a cue, if there is one
|
1015
|
-
if (this.activeCues[0] && 'text' in this.activeCues[0]) {
|
1016
|
-
_setCaption(this.activeCues[0].getCueAsHTML());
|
1017
|
-
} else {
|
1018
|
-
_setCaption();
|
1019
|
-
}
|
1020
|
-
});
|
1021
|
-
}
|
1022
|
-
}
|
1023
|
-
} else {
|
1024
|
-
// Caption tracks not natively supported
|
1025
|
-
_log('TextTracks not supported so rendering captions manually');
|
1026
|
-
|
1027
|
-
// Render captions from array at appropriate time
|
1028
|
-
plyr.currentCaption = '';
|
1029
|
-
plyr.captions = [];
|
1030
|
-
|
1031
|
-
if (captionSrc !== '') {
|
1032
|
-
// Create XMLHttpRequest Object
|
1033
|
-
var xhr = new XMLHttpRequest();
|
1034
|
-
|
1035
|
-
xhr.onreadystatechange = function() {
|
1036
|
-
if (xhr.readyState === 4) {
|
1037
|
-
if (xhr.status === 200) {
|
1038
|
-
var captions = [],
|
1039
|
-
caption,
|
1040
|
-
req = xhr.responseText;
|
1041
|
-
|
1042
|
-
//According to webvtt spec, line terminator consists of one of the following
|
1043
|
-
// CRLF (U+000D U+000A), LF (U+000A) or CR (U+000D)
|
1044
|
-
var lineSeparator = '\r\n';
|
1045
|
-
if(req.indexOf(lineSeparator+lineSeparator) === -1) {
|
1046
|
-
if(req.indexOf('\r\r') !== -1){
|
1047
|
-
lineSeparator = '\r';
|
1048
|
-
} else {
|
1049
|
-
lineSeparator = '\n';
|
1050
|
-
}
|
1051
|
-
}
|
1052
|
-
|
1053
|
-
captions = req.split(lineSeparator+lineSeparator);
|
1054
|
-
|
1055
|
-
for (var r = 0; r < captions.length; r++) {
|
1056
|
-
caption = captions[r];
|
1057
|
-
plyr.captions[r] = [];
|
1058
|
-
|
1059
|
-
// Get the parts of the captions
|
1060
|
-
var parts = caption.split(lineSeparator),
|
1061
|
-
index = 0;
|
1062
|
-
|
1063
|
-
// Incase caption numbers are added
|
1064
|
-
if (parts[index].indexOf(":") === -1) {
|
1065
|
-
index = 1;
|
1066
|
-
}
|
1067
|
-
|
1068
|
-
plyr.captions[r] = [parts[index], parts[index + 1]];
|
1069
|
-
}
|
1070
|
-
|
1071
|
-
// Remove first element ('VTT')
|
1072
|
-
plyr.captions.shift();
|
1073
|
-
|
1074
|
-
_log('Successfully loaded the caption file via AJAX');
|
1075
|
-
} else {
|
1076
|
-
_warn(config.logPrefix + 'There was a problem loading the caption file via AJAX');
|
1077
|
-
}
|
1078
|
-
}
|
1079
|
-
};
|
1080
|
-
|
1081
|
-
xhr.open('get', captionSrc, true);
|
1082
|
-
|
1083
|
-
xhr.send();
|
1084
|
-
}
|
1085
|
-
}
|
1086
|
-
}
|
1087
|
-
}
|
1088
|
-
|
1089
|
-
// Set the current caption
|
1090
|
-
function _setCaption(caption) {
|
1091
|
-
/* jshint unused:false */
|
1092
|
-
var container = _getElement(config.selectors.captions),
|
1093
|
-
content = document.createElement('span');
|
1094
|
-
|
1095
|
-
// Empty the container
|
1096
|
-
container.innerHTML = '';
|
1097
|
-
|
1098
|
-
// Default to empty
|
1099
|
-
if (_is.undefined(caption)) {
|
1100
|
-
caption = '';
|
1101
|
-
}
|
1102
|
-
|
1103
|
-
// Set the span content
|
1104
|
-
if (_is.string(caption)) {
|
1105
|
-
content.innerHTML = caption.trim();
|
1106
|
-
} else {
|
1107
|
-
content.appendChild(caption);
|
1108
|
-
}
|
1109
|
-
|
1110
|
-
// Set new caption text
|
1111
|
-
container.appendChild(content);
|
1112
|
-
|
1113
|
-
// Force redraw (for Safari)
|
1114
|
-
var redraw = container.offsetHeight;
|
1115
|
-
}
|
1116
|
-
|
1117
|
-
// Captions functions
|
1118
|
-
// Seek the manual caption time and update UI
|
1119
|
-
function _seekManualCaptions(time) {
|
1120
|
-
// Utilities for caption time codes
|
1121
|
-
function _timecodeCommon(tc, pos) {
|
1122
|
-
var tcpair = [];
|
1123
|
-
tcpair = tc.split(' --> ');
|
1124
|
-
for(var i = 0; i < tcpair.length; i++) {
|
1125
|
-
// WebVTT allows for extra meta data after the timestamp line
|
1126
|
-
// So get rid of this if it exists
|
1127
|
-
tcpair[i] = tcpair[i].replace(/(\d+:\d+:\d+\.\d+).*/, "$1");
|
1128
|
-
}
|
1129
|
-
return _subTcSecs(tcpair[pos]);
|
1130
|
-
}
|
1131
|
-
function _timecodeMin(tc) {
|
1132
|
-
return _timecodeCommon(tc, 0);
|
1133
|
-
}
|
1134
|
-
function _timecodeMax(tc) {
|
1135
|
-
return _timecodeCommon(tc, 1);
|
1136
|
-
}
|
1137
|
-
function _subTcSecs(tc) {
|
1138
|
-
if (tc === null || tc === undefined) {
|
1139
|
-
return 0;
|
1140
|
-
} else {
|
1141
|
-
var tc1 = [],
|
1142
|
-
tc2 = [],
|
1143
|
-
seconds;
|
1144
|
-
tc1 = tc.split(',');
|
1145
|
-
tc2 = tc1[0].split(':');
|
1146
|
-
seconds = Math.floor(tc2[0]*60*60) + Math.floor(tc2[1]*60) + Math.floor(tc2[2]);
|
1147
|
-
return seconds;
|
1148
|
-
}
|
1149
|
-
}
|
1150
|
-
|
1151
|
-
// If it's not video, or we're using textTracks, bail.
|
1152
|
-
if (plyr.usingTextTracks || plyr.type !== 'video' || !plyr.supported.full) {
|
1153
|
-
return;
|
1154
|
-
}
|
1155
|
-
|
1156
|
-
// Reset subcount
|
1157
|
-
plyr.subcount = 0;
|
1158
|
-
|
1159
|
-
// Check time is a number, if not use currentTime
|
1160
|
-
// IE has a bug where currentTime doesn't go to 0
|
1161
|
-
// https://twitter.com/Sam_Potts/status/573715746506731521
|
1162
|
-
time = _is.number(time) ? time : plyr.media.currentTime;
|
1163
|
-
|
1164
|
-
// If there's no subs available, bail
|
1165
|
-
if (!plyr.captions[plyr.subcount]) {
|
1166
|
-
return;
|
1167
|
-
}
|
1168
|
-
|
1169
|
-
while (_timecodeMax(plyr.captions[plyr.subcount][0]) < time.toFixed(1)) {
|
1170
|
-
plyr.subcount++;
|
1171
|
-
if (plyr.subcount > plyr.captions.length - 1) {
|
1172
|
-
plyr.subcount = plyr.captions.length - 1;
|
1173
|
-
break;
|
1174
|
-
}
|
1175
|
-
}
|
1176
|
-
|
1177
|
-
// Check if the next caption is in the current time range
|
1178
|
-
if (plyr.media.currentTime.toFixed(1) >= _timecodeMin(plyr.captions[plyr.subcount][0]) &&
|
1179
|
-
plyr.media.currentTime.toFixed(1) <= _timecodeMax(plyr.captions[plyr.subcount][0])) {
|
1180
|
-
plyr.currentCaption = plyr.captions[plyr.subcount][1];
|
1181
|
-
|
1182
|
-
// Render the caption
|
1183
|
-
_setCaption(plyr.currentCaption);
|
1184
|
-
} else {
|
1185
|
-
_setCaption();
|
1186
|
-
}
|
1187
|
-
}
|
1188
|
-
|
1189
|
-
// Display captions container and button (for initialization)
|
1190
|
-
function _showCaptions() {
|
1191
|
-
// If there's no caption toggle, bail
|
1192
|
-
if (!plyr.buttons.captions) {
|
1193
|
-
return;
|
1194
|
-
}
|
1195
|
-
|
1196
|
-
_toggleClass(plyr.container, config.classes.captions.enabled, true);
|
1197
|
-
|
1198
|
-
// Try to load the value from storage
|
1199
|
-
var active = plyr.storage.captionsEnabled;
|
1200
|
-
|
1201
|
-
// Otherwise fall back to the default config
|
1202
|
-
if (!_is.boolean(active)) {
|
1203
|
-
active = config.captions.defaultActive;
|
1204
|
-
}
|
1205
|
-
|
1206
|
-
if (active) {
|
1207
|
-
_toggleClass(plyr.container, config.classes.captions.active, true);
|
1208
|
-
_toggleState(plyr.buttons.captions, true);
|
1209
|
-
}
|
1210
|
-
}
|
1211
|
-
|
1212
|
-
// Find all elements
|
1213
|
-
function _getElements(selector) {
|
1214
|
-
return plyr.container.querySelectorAll(selector);
|
1215
|
-
}
|
1216
|
-
|
1217
|
-
// Find a single element
|
1218
|
-
function _getElement(selector) {
|
1219
|
-
return _getElements(selector)[0];
|
1220
|
-
}
|
1221
|
-
|
1222
|
-
// Determine if we're in an iframe
|
1223
|
-
function _inFrame() {
|
1224
|
-
try {
|
1225
|
-
return window.self !== window.top;
|
1226
|
-
}
|
1227
|
-
catch (e) {
|
1228
|
-
return true;
|
1229
|
-
}
|
1230
|
-
}
|
1231
|
-
|
1232
|
-
// Trap focus inside container
|
1233
|
-
function _focusTrap() {
|
1234
|
-
var tabbables = _getElements('input:not([disabled]), button:not([disabled])'),
|
1235
|
-
first = tabbables[0],
|
1236
|
-
last = tabbables[tabbables.length - 1];
|
1237
|
-
|
1238
|
-
function _checkFocus(event) {
|
1239
|
-
// If it is TAB
|
1240
|
-
if (event.which === 9 && plyr.isFullscreen) {
|
1241
|
-
if (event.target === last && !event.shiftKey) {
|
1242
|
-
// Move focus to first element that can be tabbed if Shift isn't used
|
1243
|
-
event.preventDefault();
|
1244
|
-
first.focus();
|
1245
|
-
} else if (event.target === first && event.shiftKey) {
|
1246
|
-
// Move focus to last element that can be tabbed if Shift is used
|
1247
|
-
event.preventDefault();
|
1248
|
-
last.focus();
|
1249
|
-
}
|
1250
|
-
}
|
1251
|
-
}
|
1252
|
-
|
1253
|
-
// Bind the handler
|
1254
|
-
_on(plyr.container, 'keydown', _checkFocus);
|
1255
|
-
}
|
1256
|
-
|
1257
|
-
// Add elements to HTML5 media (source, tracks, etc)
|
1258
|
-
function _insertChildElements(type, attributes) {
|
1259
|
-
if (_is.string(attributes)) {
|
1260
|
-
_insertElement(type, plyr.media, { src: attributes });
|
1261
|
-
} else if (attributes.constructor === Array) {
|
1262
|
-
for (var i = attributes.length - 1; i >= 0; i--) {
|
1263
|
-
_insertElement(type, plyr.media, attributes[i]);
|
1264
|
-
}
|
1265
|
-
}
|
1266
|
-
}
|
1267
|
-
|
1268
|
-
// Insert controls
|
1269
|
-
function _injectControls() {
|
1270
|
-
// Sprite
|
1271
|
-
if (config.loadSprite) {
|
1272
|
-
var iconUrl = _getIconUrl();
|
1273
|
-
|
1274
|
-
// Only load external sprite using AJAX
|
1275
|
-
if (iconUrl.absolute) {
|
1276
|
-
_log('AJAX loading absolute SVG sprite' + (plyr.browser.isIE ? ' (due to IE)' : ''));
|
1277
|
-
loadSprite(iconUrl.url, "sprite-plyr");
|
1278
|
-
} else {
|
1279
|
-
_log('Sprite will be used as external resource directly');
|
1280
|
-
}
|
1281
|
-
}
|
1282
|
-
|
1283
|
-
// Make a copy of the html
|
1284
|
-
var html = config.html;
|
1285
|
-
|
1286
|
-
// Insert custom video controls
|
1287
|
-
_log('Injecting custom controls');
|
1288
|
-
|
1289
|
-
// If no controls are specified, create default
|
1290
|
-
if (!html) {
|
1291
|
-
html = _buildControls();
|
1292
|
-
}
|
1293
|
-
|
1294
|
-
// Replace seek time instances
|
1295
|
-
html = _replaceAll(html, '{seektime}', config.seekTime);
|
1296
|
-
|
1297
|
-
// Replace all id references with random numbers
|
1298
|
-
html = _replaceAll(html, '{id}', Math.floor(Math.random() * (10000)));
|
1299
|
-
|
1300
|
-
// Controls container
|
1301
|
-
var target;
|
1302
|
-
|
1303
|
-
// Inject to custom location
|
1304
|
-
if (_is.string(config.selectors.controls.container)) {
|
1305
|
-
target = document.querySelector(config.selectors.controls.container);
|
1306
|
-
}
|
1307
|
-
|
1308
|
-
// Inject into the container by default
|
1309
|
-
if (!_is.htmlElement(target)) {
|
1310
|
-
target = plyr.container
|
1311
|
-
}
|
1312
|
-
|
1313
|
-
// Inject controls HTML
|
1314
|
-
target.insertAdjacentHTML('beforeend', html);
|
1315
|
-
|
1316
|
-
// Setup tooltips
|
1317
|
-
if (config.tooltips.controls) {
|
1318
|
-
var labels = _getElements([config.selectors.controls.wrapper, ' ', config.selectors.labels, ' .', config.classes.hidden].join(''));
|
1319
|
-
|
1320
|
-
for (var i = labels.length - 1; i >= 0; i--) {
|
1321
|
-
var label = labels[i];
|
1322
|
-
|
1323
|
-
_toggleClass(label, config.classes.hidden, false);
|
1324
|
-
_toggleClass(label, config.classes.tooltip, true);
|
1325
|
-
}
|
1326
|
-
}
|
1327
|
-
}
|
1328
|
-
|
1329
|
-
// Find the UI controls and store references
|
1330
|
-
function _findElements() {
|
1331
|
-
try {
|
1332
|
-
plyr.controls = _getElement(config.selectors.controls.wrapper);
|
1333
|
-
|
1334
|
-
// Buttons
|
1335
|
-
plyr.buttons = {};
|
1336
|
-
plyr.buttons.seek = _getElement(config.selectors.buttons.seek);
|
1337
|
-
plyr.buttons.play = _getElements(config.selectors.buttons.play);
|
1338
|
-
plyr.buttons.pause = _getElement(config.selectors.buttons.pause);
|
1339
|
-
plyr.buttons.restart = _getElement(config.selectors.buttons.restart);
|
1340
|
-
plyr.buttons.rewind = _getElement(config.selectors.buttons.rewind);
|
1341
|
-
plyr.buttons.forward = _getElement(config.selectors.buttons.forward);
|
1342
|
-
plyr.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen);
|
1343
|
-
|
1344
|
-
// Inputs
|
1345
|
-
plyr.buttons.mute = _getElement(config.selectors.buttons.mute);
|
1346
|
-
plyr.buttons.captions = _getElement(config.selectors.buttons.captions);
|
1347
|
-
|
1348
|
-
// Progress
|
1349
|
-
plyr.progress = {};
|
1350
|
-
plyr.progress.container = _getElement(config.selectors.progress.container);
|
1351
|
-
|
1352
|
-
// Progress - Buffering
|
1353
|
-
plyr.progress.buffer = {};
|
1354
|
-
plyr.progress.buffer.bar = _getElement(config.selectors.progress.buffer);
|
1355
|
-
plyr.progress.buffer.text = plyr.progress.buffer.bar && plyr.progress.buffer.bar.getElementsByTagName('span')[0];
|
1356
|
-
|
1357
|
-
// Progress - Played
|
1358
|
-
plyr.progress.played = _getElement(config.selectors.progress.played);
|
1359
|
-
|
1360
|
-
// Seek tooltip
|
1361
|
-
plyr.progress.tooltip = plyr.progress.container && plyr.progress.container.querySelector('.' + config.classes.tooltip);
|
1362
|
-
|
1363
|
-
// Volume
|
1364
|
-
plyr.volume = {};
|
1365
|
-
plyr.volume.input = _getElement(config.selectors.volume.input);
|
1366
|
-
plyr.volume.display = _getElement(config.selectors.volume.display);
|
1367
|
-
|
1368
|
-
// Timing
|
1369
|
-
plyr.duration = _getElement(config.selectors.duration);
|
1370
|
-
plyr.currentTime = _getElement(config.selectors.currentTime);
|
1371
|
-
plyr.seekTime = _getElements(config.selectors.seekTime);
|
1372
|
-
|
1373
|
-
return true;
|
1374
|
-
}
|
1375
|
-
catch(e) {
|
1376
|
-
_warn('It looks like there is a problem with your controls HTML');
|
1377
|
-
|
1378
|
-
// Restore native video controls
|
1379
|
-
_toggleNativeControls(true);
|
1380
|
-
|
1381
|
-
return false;
|
1382
|
-
}
|
1383
|
-
}
|
1384
|
-
|
1385
|
-
// Toggle style hook
|
1386
|
-
function _toggleStyleHook() {
|
1387
|
-
_toggleClass(plyr.container, config.selectors.container.replace('.', ''), plyr.supported.full);
|
1388
|
-
}
|
1389
|
-
|
1390
|
-
// Toggle native controls
|
1391
|
-
function _toggleNativeControls(toggle) {
|
1392
|
-
if (toggle && _inArray(config.types.html5, plyr.type)) {
|
1393
|
-
plyr.media.setAttribute('controls', '');
|
1394
|
-
} else {
|
1395
|
-
plyr.media.removeAttribute('controls');
|
1396
|
-
}
|
1397
|
-
}
|
1398
|
-
|
1399
|
-
// Setup aria attribute for play and iframe title
|
1400
|
-
function _setTitle(iframe) {
|
1401
|
-
// Find the current text
|
1402
|
-
var label = config.i18n.play;
|
1403
|
-
|
1404
|
-
// If there's a media title set, use that for the label
|
1405
|
-
if (_is.string(config.title) && config.title.length) {
|
1406
|
-
label += ', ' + config.title;
|
1407
|
-
|
1408
|
-
// Set container label
|
1409
|
-
plyr.container.setAttribute('aria-label', config.title);
|
1410
|
-
}
|
1411
|
-
|
1412
|
-
// If there's a play button, set label
|
1413
|
-
if (plyr.supported.full && plyr.buttons.play) {
|
1414
|
-
for (var i = plyr.buttons.play.length - 1; i >= 0; i--) {
|
1415
|
-
plyr.buttons.play[i].setAttribute('aria-label', label);
|
1416
|
-
}
|
1417
|
-
}
|
1418
|
-
|
1419
|
-
// Set iframe title
|
1420
|
-
// https://github.com/Selz/plyr/issues/124
|
1421
|
-
if (_is.htmlElement(iframe)) {
|
1422
|
-
iframe.setAttribute('title', config.i18n.frameTitle.replace('{title}', config.title));
|
1423
|
-
}
|
1424
|
-
}
|
1425
|
-
|
1426
|
-
// Setup localStorage
|
1427
|
-
function _setupStorage() {
|
1428
|
-
var value = null;
|
1429
|
-
plyr.storage = {};
|
1430
|
-
|
1431
|
-
// Bail if we don't have localStorage support or it's disabled
|
1432
|
-
if (!_storage.supported || !config.storage.enabled) {
|
1433
|
-
return;
|
1434
|
-
}
|
1435
|
-
|
1436
|
-
// Clean up old volume
|
1437
|
-
// https://github.com/Selz/plyr/issues/171
|
1438
|
-
window.localStorage.removeItem('plyr-volume');
|
1439
|
-
|
1440
|
-
// load value from the current key
|
1441
|
-
value = window.localStorage.getItem(config.storage.key);
|
1442
|
-
|
1443
|
-
if (!value) {
|
1444
|
-
// Key wasn't set (or had been cleared), move along
|
1445
|
-
return;
|
1446
|
-
} else if (/^\d+(\.\d+)?$/.test(value)) {
|
1447
|
-
// If value is a number, it's probably volume from an older
|
1448
|
-
// version of plyr. See: https://github.com/Selz/plyr/pull/313
|
1449
|
-
// Update the key to be JSON
|
1450
|
-
_updateStorage({volume: parseFloat(value)});
|
1451
|
-
} else {
|
1452
|
-
// Assume it's JSON from this or a later version of plyr
|
1453
|
-
plyr.storage = JSON.parse(value);
|
1454
|
-
}
|
1455
|
-
}
|
1456
|
-
|
1457
|
-
// Save a value back to local storage
|
1458
|
-
function _updateStorage(value) {
|
1459
|
-
// Bail if we don't have localStorage support or it's disabled
|
1460
|
-
if (!_storage.supported || !config.storage.enabled) {
|
1461
|
-
return;
|
1462
|
-
}
|
1463
|
-
|
1464
|
-
// Update the working copy of the values
|
1465
|
-
_extend(plyr.storage, value);
|
1466
|
-
|
1467
|
-
// Update storage
|
1468
|
-
window.localStorage.setItem(config.storage.key, JSON.stringify(plyr.storage));
|
1469
|
-
}
|
1470
|
-
|
1471
|
-
// Setup media
|
1472
|
-
function _setupMedia() {
|
1473
|
-
// If there's no media, bail
|
1474
|
-
if (!plyr.media) {
|
1475
|
-
_warn('No media element found!');
|
1476
|
-
return;
|
1477
|
-
}
|
1478
|
-
|
1479
|
-
if (plyr.supported.full) {
|
1480
|
-
// Add type class
|
1481
|
-
_toggleClass(plyr.container, config.classes.type.replace('{0}', plyr.type), true);
|
1482
|
-
|
1483
|
-
// Add video class for embeds
|
1484
|
-
// This will require changes if audio embeds are added
|
1485
|
-
if (_inArray(config.types.embed, plyr.type)) {
|
1486
|
-
_toggleClass(plyr.container, config.classes.type.replace('{0}', 'video'), true);
|
1487
|
-
}
|
1488
|
-
|
1489
|
-
// If there's no autoplay attribute, assume the video is stopped and add state class
|
1490
|
-
_toggleClass(plyr.container, config.classes.stopped, config.autoplay);
|
1491
|
-
|
1492
|
-
// Add iOS class
|
1493
|
-
_toggleClass(plyr.ontainer, config.classes.isIos, plyr.browser.isIos);
|
1494
|
-
|
1495
|
-
// Add touch class
|
1496
|
-
_toggleClass(plyr.container, config.classes.isTouch, plyr.browser.isTouch);
|
1497
|
-
|
1498
|
-
// Inject the player wrapper
|
1499
|
-
if (plyr.type === 'video') {
|
1500
|
-
// Create the wrapper div
|
1501
|
-
var wrapper = document.createElement('div');
|
1502
|
-
wrapper.setAttribute('class', config.classes.videoWrapper);
|
1503
|
-
|
1504
|
-
// Wrap the video in a container
|
1505
|
-
_wrap(plyr.media, wrapper);
|
1506
|
-
|
1507
|
-
// Cache the container
|
1508
|
-
plyr.videoContainer = wrapper;
|
1509
|
-
}
|
1510
|
-
}
|
1511
|
-
|
1512
|
-
// Embeds
|
1513
|
-
if (_inArray(config.types.embed, plyr.type)) {
|
1514
|
-
_setupEmbed();
|
1515
|
-
}
|
1516
|
-
}
|
1517
|
-
|
1518
|
-
// Setup YouTube/Vimeo
|
1519
|
-
function _setupEmbed() {
|
1520
|
-
var container = document.createElement('div'),
|
1521
|
-
mediaId,
|
1522
|
-
id = plyr.type + '-' + Math.floor(Math.random() * (10000));
|
1523
|
-
|
1524
|
-
// Parse IDs from URLs if supplied
|
1525
|
-
switch (plyr.type) {
|
1526
|
-
case 'youtube':
|
1527
|
-
mediaId = _parseYouTubeId(plyr.embedId);
|
1528
|
-
break;
|
1529
|
-
|
1530
|
-
case 'vimeo':
|
1531
|
-
mediaId = _parseVimeoId(plyr.embedId);
|
1532
|
-
break;
|
1533
|
-
|
1534
|
-
default:
|
1535
|
-
mediaId = plyr.embedId;
|
1536
|
-
}
|
1537
|
-
|
1538
|
-
// Remove old containers
|
1539
|
-
var containers = _getElements('[id^="' + plyr.type + '-"]');
|
1540
|
-
for (var i = containers.length - 1; i >= 0; i--) {
|
1541
|
-
_remove(containers[i]);
|
1542
|
-
}
|
1543
|
-
|
1544
|
-
// Add embed class for responsive
|
1545
|
-
_toggleClass(plyr.media, config.classes.videoWrapper, true);
|
1546
|
-
_toggleClass(plyr.media, config.classes.embedWrapper, true);
|
1547
|
-
|
1548
|
-
if (plyr.type === 'youtube') {
|
1549
|
-
// Create the YouTube container
|
1550
|
-
plyr.media.appendChild(container);
|
1551
|
-
|
1552
|
-
// Set ID
|
1553
|
-
container.setAttribute('id', id);
|
1554
|
-
|
1555
|
-
// Setup API
|
1556
|
-
if (_is.object(window.YT)) {
|
1557
|
-
_youTubeReady(mediaId, container);
|
1558
|
-
} else {
|
1559
|
-
// Load the API
|
1560
|
-
_injectScript(config.urls.youtube.api);
|
1561
|
-
|
1562
|
-
// Setup callback for the API
|
1563
|
-
window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || [];
|
1564
|
-
|
1565
|
-
// Add to queue
|
1566
|
-
window.onYouTubeReadyCallbacks.push(function() { _youTubeReady(mediaId, container); });
|
1567
|
-
|
1568
|
-
// Set callback to process queue
|
1569
|
-
window.onYouTubeIframeAPIReady = function () {
|
1570
|
-
window.onYouTubeReadyCallbacks.forEach(function(callback) { callback(); });
|
1571
|
-
};
|
1572
|
-
}
|
1573
|
-
} else if (plyr.type === 'vimeo') {
|
1574
|
-
// Vimeo needs an extra div to hide controls on desktop (which has full support)
|
1575
|
-
if (plyr.supported.full) {
|
1576
|
-
plyr.media.appendChild(container);
|
1577
|
-
} else {
|
1578
|
-
container = plyr.media;
|
1579
|
-
}
|
1580
|
-
|
1581
|
-
// Set ID
|
1582
|
-
container.setAttribute('id', id);
|
1583
|
-
|
1584
|
-
// Load the API if not already
|
1585
|
-
if (!_is.object(window.Vimeo)) {
|
1586
|
-
_injectScript(config.urls.vimeo.api);
|
1587
|
-
|
1588
|
-
// Wait for fragaloop load
|
1589
|
-
var vimeoTimer = window.setInterval(function() {
|
1590
|
-
if (_is.object(window.Vimeo)) {
|
1591
|
-
window.clearInterval(vimeoTimer);
|
1592
|
-
_vimeoReady(mediaId, container);
|
1593
|
-
}
|
1594
|
-
}, 50);
|
1595
|
-
} else {
|
1596
|
-
_vimeoReady(mediaId, container);
|
1597
|
-
}
|
1598
|
-
} else if (plyr.type === 'soundcloud') {
|
1599
|
-
// TODO: Currently unsupported and undocumented
|
1600
|
-
// Inject the iframe
|
1601
|
-
var soundCloud = document.createElement('iframe');
|
1602
|
-
|
1603
|
-
// Watch for iframe load
|
1604
|
-
soundCloud.loaded = false;
|
1605
|
-
_on(soundCloud, 'load', function() { soundCloud.loaded = true; });
|
1606
|
-
|
1607
|
-
_setAttributes(soundCloud, {
|
1608
|
-
'src': 'https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/' + mediaId,
|
1609
|
-
'id': id
|
1610
|
-
});
|
1611
|
-
|
1612
|
-
container.appendChild(soundCloud);
|
1613
|
-
plyr.media.appendChild(container);
|
1614
|
-
|
1615
|
-
// Load the API if not already
|
1616
|
-
if (!window.SC) {
|
1617
|
-
_injectScript(config.urls.soundcloud.api);
|
1618
|
-
}
|
1619
|
-
|
1620
|
-
// Wait for SC load
|
1621
|
-
var soundCloudTimer = window.setInterval(function() {
|
1622
|
-
if (window.SC && soundCloud.loaded) {
|
1623
|
-
window.clearInterval(soundCloudTimer);
|
1624
|
-
_soundcloudReady.call(soundCloud);
|
1625
|
-
}
|
1626
|
-
}, 50);
|
1627
|
-
}
|
1628
|
-
}
|
1629
|
-
|
1630
|
-
// When embeds are ready
|
1631
|
-
function _embedReady() {
|
1632
|
-
// Setup the UI and call ready if full support
|
1633
|
-
if (plyr.supported.full) {
|
1634
|
-
_setupInterface();
|
1635
|
-
_ready();
|
1636
|
-
}
|
1637
|
-
|
1638
|
-
// Set title
|
1639
|
-
_setTitle(_getElement('iframe'));
|
1640
|
-
}
|
1641
|
-
|
1642
|
-
// Handle YouTube API ready
|
1643
|
-
function _youTubeReady(videoId, container) {
|
1644
|
-
// Setup instance
|
1645
|
-
// https://developers.google.com/youtube/iframe_api_reference
|
1646
|
-
plyr.embed = new window.YT.Player(container.id, {
|
1647
|
-
videoId: videoId,
|
1648
|
-
playerVars: {
|
1649
|
-
autoplay: (config.autoplay ? 1 : 0),
|
1650
|
-
controls: (plyr.supported.full ? 0 : 1),
|
1651
|
-
rel: 0,
|
1652
|
-
showinfo: 0,
|
1653
|
-
iv_load_policy: 3,
|
1654
|
-
cc_load_policy: (config.captions.defaultActive ? 1 : 0),
|
1655
|
-
cc_lang_pref: 'en',
|
1656
|
-
wmode: 'transparent',
|
1657
|
-
modestbranding: 1,
|
1658
|
-
disablekb: 1,
|
1659
|
-
origin: '*' // https://code.google.com/p/gdata-issues/issues/detail?id=5788#c45
|
1660
|
-
},
|
1661
|
-
events: {
|
1662
|
-
'onError': function(event) {
|
1663
|
-
_triggerEvent(plyr.container, 'error', true, {
|
1664
|
-
code: event.data,
|
1665
|
-
embed: event.target
|
1666
|
-
});
|
1667
|
-
},
|
1668
|
-
'onReady': function(event) {
|
1669
|
-
// Get the instance
|
1670
|
-
var instance = event.target;
|
1671
|
-
|
1672
|
-
// Create a faux HTML5 API using the YouTube API
|
1673
|
-
plyr.media.play = function() {
|
1674
|
-
instance.playVideo();
|
1675
|
-
plyr.media.paused = false;
|
1676
|
-
};
|
1677
|
-
plyr.media.pause = function() {
|
1678
|
-
instance.pauseVideo();
|
1679
|
-
plyr.media.paused = true;
|
1680
|
-
};
|
1681
|
-
plyr.media.stop = function() {
|
1682
|
-
instance.stopVideo();
|
1683
|
-
plyr.media.paused = true;
|
1684
|
-
};
|
1685
|
-
plyr.media.duration = instance.getDuration();
|
1686
|
-
plyr.media.paused = true;
|
1687
|
-
plyr.media.currentTime = 0;
|
1688
|
-
plyr.media.muted = instance.isMuted();
|
1689
|
-
|
1690
|
-
// Set title
|
1691
|
-
config.title = instance.getVideoData().title;
|
1692
|
-
|
1693
|
-
// Set the tabindex
|
1694
|
-
if (plyr.supported.full) {
|
1695
|
-
plyr.media.querySelector('iframe').setAttribute('tabindex', '-1');
|
1696
|
-
}
|
1697
|
-
|
1698
|
-
// Update UI
|
1699
|
-
_embedReady();
|
1700
|
-
|
1701
|
-
// Trigger timeupdate
|
1702
|
-
_triggerEvent(plyr.media, 'timeupdate');
|
1703
|
-
|
1704
|
-
// Trigger timeupdate
|
1705
|
-
_triggerEvent(plyr.media, 'durationchange');
|
1706
|
-
|
1707
|
-
// Reset timer
|
1708
|
-
window.clearInterval(timers.buffering);
|
1709
|
-
|
1710
|
-
// Setup buffering
|
1711
|
-
timers.buffering = window.setInterval(function() {
|
1712
|
-
// Get loaded % from YouTube
|
1713
|
-
plyr.media.buffered = instance.getVideoLoadedFraction();
|
1714
|
-
|
1715
|
-
// Trigger progress only when we actually buffer something
|
1716
|
-
if (plyr.media.lastBuffered === null || plyr.media.lastBuffered < plyr.media.buffered) {
|
1717
|
-
_triggerEvent(plyr.media, 'progress');
|
1718
|
-
}
|
1719
|
-
|
1720
|
-
// Set last buffer point
|
1721
|
-
plyr.media.lastBuffered = plyr.media.buffered;
|
1722
|
-
|
1723
|
-
// Bail if we're at 100%
|
1724
|
-
if (plyr.media.buffered === 1) {
|
1725
|
-
window.clearInterval(timers.buffering);
|
1726
|
-
|
1727
|
-
// Trigger event
|
1728
|
-
_triggerEvent(plyr.media, 'canplaythrough');
|
1729
|
-
}
|
1730
|
-
}, 200);
|
1731
|
-
},
|
1732
|
-
'onStateChange': function(event) {
|
1733
|
-
// Get the instance
|
1734
|
-
var instance = event.target;
|
1735
|
-
|
1736
|
-
// Reset timer
|
1737
|
-
window.clearInterval(timers.playing);
|
1738
|
-
|
1739
|
-
// Handle events
|
1740
|
-
// -1 Unstarted
|
1741
|
-
// 0 Ended
|
1742
|
-
// 1 Playing
|
1743
|
-
// 2 Paused
|
1744
|
-
// 3 Buffering
|
1745
|
-
// 5 Video cued
|
1746
|
-
switch (event.data) {
|
1747
|
-
case 0:
|
1748
|
-
plyr.media.paused = true;
|
1749
|
-
_triggerEvent(plyr.media, 'ended');
|
1750
|
-
break;
|
1751
|
-
|
1752
|
-
case 1:
|
1753
|
-
plyr.media.paused = false;
|
1754
|
-
|
1755
|
-
// If we were seeking, fire seeked event
|
1756
|
-
if (plyr.media.seeking) {
|
1757
|
-
_triggerEvent(plyr.media, 'seeked');
|
1758
|
-
}
|
1759
|
-
|
1760
|
-
plyr.media.seeking = false;
|
1761
|
-
_triggerEvent(plyr.media, 'play');
|
1762
|
-
_triggerEvent(plyr.media, 'playing');
|
1763
|
-
|
1764
|
-
// Poll to get playback progress
|
1765
|
-
timers.playing = window.setInterval(function() {
|
1766
|
-
// Set the current time
|
1767
|
-
plyr.media.currentTime = instance.getCurrentTime();
|
1768
|
-
|
1769
|
-
// Trigger timeupdate
|
1770
|
-
_triggerEvent(plyr.media, 'timeupdate');
|
1771
|
-
}, 100);
|
1772
|
-
|
1773
|
-
// Check duration again due to YouTube bug
|
1774
|
-
// https://github.com/Selz/plyr/issues/374
|
1775
|
-
// https://code.google.com/p/gdata-issues/issues/detail?id=8690
|
1776
|
-
if (plyr.media.duration !== instance.getDuration()) {
|
1777
|
-
plyr.media.duration = instance.getDuration();
|
1778
|
-
_triggerEvent(plyr.media, 'durationchange');
|
1779
|
-
}
|
1780
|
-
|
1781
|
-
break;
|
1782
|
-
|
1783
|
-
case 2:
|
1784
|
-
plyr.media.paused = true;
|
1785
|
-
_triggerEvent(plyr.media, 'pause');
|
1786
|
-
break;
|
1787
|
-
}
|
1788
|
-
|
1789
|
-
_triggerEvent(plyr.container, 'statechange', false, {
|
1790
|
-
code: event.data
|
1791
|
-
});
|
1792
|
-
}
|
1793
|
-
}
|
1794
|
-
});
|
1795
|
-
}
|
1796
|
-
|
1797
|
-
// Vimeo ready
|
1798
|
-
function _vimeoReady(mediaId, container) {
|
1799
|
-
// Setup instance
|
1800
|
-
// https://github.com/vimeo/player.js
|
1801
|
-
plyr.embed = new window.Vimeo.Player(container, {
|
1802
|
-
id: parseInt(mediaId),
|
1803
|
-
loop: config.loop,
|
1804
|
-
autoplay: config.autoplay,
|
1805
|
-
byline: false,
|
1806
|
-
portrait: false,
|
1807
|
-
title: false
|
1808
|
-
});
|
1809
|
-
|
1810
|
-
// Create a faux HTML5 API using the Vimeo API
|
1811
|
-
plyr.media.play = function() {
|
1812
|
-
plyr.embed.play();
|
1813
|
-
plyr.media.paused = false;
|
1814
|
-
};
|
1815
|
-
plyr.media.pause = function() {
|
1816
|
-
plyr.embed.pause();
|
1817
|
-
plyr.media.paused = true;
|
1818
|
-
};
|
1819
|
-
plyr.media.stop = function() {
|
1820
|
-
plyr.embed.stop();
|
1821
|
-
plyr.media.paused = true;
|
1822
|
-
};
|
1823
|
-
|
1824
|
-
plyr.media.paused = true;
|
1825
|
-
plyr.media.currentTime = 0;
|
1826
|
-
|
1827
|
-
// Update UI
|
1828
|
-
_embedReady();
|
1829
|
-
|
1830
|
-
plyr.embed.getCurrentTime().then(function(value) {
|
1831
|
-
plyr.media.currentTime = value;
|
1832
|
-
|
1833
|
-
// Trigger timeupdate
|
1834
|
-
_triggerEvent(plyr.media, 'timeupdate');
|
1835
|
-
});
|
1836
|
-
|
1837
|
-
plyr.embed.getDuration().then(function(value) {
|
1838
|
-
plyr.media.duration = value;
|
1839
|
-
|
1840
|
-
// Trigger timeupdate
|
1841
|
-
_triggerEvent(plyr.media, 'durationchange');
|
1842
|
-
});
|
1843
|
-
|
1844
|
-
// TODO: Captions
|
1845
|
-
/*if (config.captions.defaultActive) {
|
1846
|
-
plyr.embed.enableTextTrack('en');
|
1847
|
-
}*/
|
1848
|
-
|
1849
|
-
plyr.embed.on('loaded', function() {
|
1850
|
-
// Fix keyboard focus issues
|
1851
|
-
// https://github.com/Selz/plyr/issues/317
|
1852
|
-
if (_is.htmlElement(plyr.embed.element) && plyr.supported.full) {
|
1853
|
-
plyr.embed.element.setAttribute('tabindex', '-1');
|
1854
|
-
}
|
1855
|
-
});
|
1856
|
-
|
1857
|
-
plyr.embed.on('play', function() {
|
1858
|
-
plyr.media.paused = false;
|
1859
|
-
_triggerEvent(plyr.media, 'play');
|
1860
|
-
_triggerEvent(plyr.media, 'playing');
|
1861
|
-
});
|
1862
|
-
|
1863
|
-
plyr.embed.on('pause', function() {
|
1864
|
-
plyr.media.paused = true;
|
1865
|
-
_triggerEvent(plyr.media, 'pause');
|
1866
|
-
});
|
1867
|
-
|
1868
|
-
plyr.embed.on('timeupdate', function(data) {
|
1869
|
-
plyr.media.seeking = false;
|
1870
|
-
plyr.media.currentTime = data.seconds;
|
1871
|
-
_triggerEvent(plyr.media, 'timeupdate');
|
1872
|
-
});
|
1873
|
-
|
1874
|
-
plyr.embed.on('progress', function(data) {
|
1875
|
-
plyr.media.buffered = data.percent;
|
1876
|
-
_triggerEvent(plyr.media, 'progress');
|
1877
|
-
|
1878
|
-
if (parseInt(data.percent) === 1) {
|
1879
|
-
// Trigger event
|
1880
|
-
_triggerEvent(plyr.media, 'canplaythrough');
|
1881
|
-
}
|
1882
|
-
});
|
1883
|
-
|
1884
|
-
plyr.embed.on('seeked', function() {
|
1885
|
-
plyr.media.seeking = false;
|
1886
|
-
_triggerEvent(plyr.media, 'seeked');
|
1887
|
-
_triggerEvent(plyr.media, 'play');
|
1888
|
-
});
|
1889
|
-
|
1890
|
-
plyr.embed.on('ended', function() {
|
1891
|
-
plyr.media.paused = true;
|
1892
|
-
_triggerEvent(plyr.media, 'ended');
|
1893
|
-
});
|
1894
|
-
}
|
1895
|
-
|
1896
|
-
// Soundcloud ready
|
1897
|
-
function _soundcloudReady() {
|
1898
|
-
/* jshint validthis: true */
|
1899
|
-
plyr.embed = window.SC.Widget(this);
|
1900
|
-
|
1901
|
-
// Setup on ready
|
1902
|
-
plyr.embed.bind(window.SC.Widget.Events.READY, function() {
|
1903
|
-
// Create a faux HTML5 API using the Soundcloud API
|
1904
|
-
plyr.media.play = function() {
|
1905
|
-
plyr.embed.play();
|
1906
|
-
plyr.media.paused = false;
|
1907
|
-
};
|
1908
|
-
plyr.media.pause = function() {
|
1909
|
-
plyr.embed.pause();
|
1910
|
-
plyr.media.paused = true;
|
1911
|
-
};
|
1912
|
-
plyr.media.stop = function() {
|
1913
|
-
plyr.embed.seekTo(0);
|
1914
|
-
plyr.embed.pause();
|
1915
|
-
plyr.media.paused = true;
|
1916
|
-
};
|
1917
|
-
|
1918
|
-
plyr.media.paused = true;
|
1919
|
-
plyr.media.currentTime = 0;
|
1920
|
-
|
1921
|
-
plyr.embed.getDuration(function(value) {
|
1922
|
-
plyr.media.duration = value/1000;
|
1923
|
-
|
1924
|
-
// Update UI
|
1925
|
-
_embedReady();
|
1926
|
-
});
|
1927
|
-
|
1928
|
-
plyr.embed.getPosition(function(value) {
|
1929
|
-
plyr.media.currentTime = value;
|
1930
|
-
|
1931
|
-
// Trigger timeupdate
|
1932
|
-
_triggerEvent(plyr.media, 'timeupdate');
|
1933
|
-
});
|
1934
|
-
|
1935
|
-
plyr.embed.bind(window.SC.Widget.Events.PLAY, function() {
|
1936
|
-
plyr.media.paused = false;
|
1937
|
-
_triggerEvent(plyr.media, 'play');
|
1938
|
-
_triggerEvent(plyr.media, 'playing');
|
1939
|
-
});
|
1940
|
-
|
1941
|
-
plyr.embed.bind(window.SC.Widget.Events.PAUSE, function() {
|
1942
|
-
plyr.media.paused = true;
|
1943
|
-
_triggerEvent(plyr.media, 'pause');
|
1944
|
-
});
|
1945
|
-
|
1946
|
-
plyr.embed.bind(window.SC.Widget.Events.PLAY_PROGRESS, function(data) {
|
1947
|
-
plyr.media.seeking = false;
|
1948
|
-
plyr.media.currentTime = data.currentPosition/1000;
|
1949
|
-
_triggerEvent(plyr.media, 'timeupdate');
|
1950
|
-
});
|
1951
|
-
|
1952
|
-
plyr.embed.bind(window.SC.Widget.Events.LOAD_PROGRESS, function(data) {
|
1953
|
-
plyr.media.buffered = data.loadProgress;
|
1954
|
-
_triggerEvent(plyr.media, 'progress');
|
1955
|
-
|
1956
|
-
if (parseInt(data.loadProgress) === 1) {
|
1957
|
-
// Trigger event
|
1958
|
-
_triggerEvent(plyr.media, 'canplaythrough');
|
1959
|
-
}
|
1960
|
-
});
|
1961
|
-
|
1962
|
-
plyr.embed.bind(window.SC.Widget.Events.FINISH, function() {
|
1963
|
-
plyr.media.paused = true;
|
1964
|
-
_triggerEvent(plyr.media, 'ended');
|
1965
|
-
});
|
1966
|
-
});
|
1967
|
-
}
|
1968
|
-
|
1969
|
-
// Play media
|
1970
|
-
function _play() {
|
1971
|
-
if ('play' in plyr.media) {
|
1972
|
-
plyr.media.play();
|
1973
|
-
}
|
1974
|
-
}
|
1975
|
-
|
1976
|
-
// Pause media
|
1977
|
-
function _pause() {
|
1978
|
-
if ('pause' in plyr.media) {
|
1979
|
-
plyr.media.pause();
|
1980
|
-
}
|
1981
|
-
}
|
1982
|
-
|
1983
|
-
// Toggle playback
|
1984
|
-
function _togglePlay(toggle) {
|
1985
|
-
// True toggle
|
1986
|
-
if (!_is.boolean(toggle)) {
|
1987
|
-
toggle = plyr.media.paused;
|
1988
|
-
}
|
1989
|
-
|
1990
|
-
if (toggle) {
|
1991
|
-
_play();
|
1992
|
-
} else {
|
1993
|
-
_pause();
|
1994
|
-
}
|
1995
|
-
|
1996
|
-
return toggle;
|
1997
|
-
}
|
1998
|
-
|
1999
|
-
// Rewind
|
2000
|
-
function _rewind(seekTime) {
|
2001
|
-
// Use default if needed
|
2002
|
-
if (!_is.number(seekTime)) {
|
2003
|
-
seekTime = config.seekTime;
|
2004
|
-
}
|
2005
|
-
_seek(plyr.media.currentTime - seekTime);
|
2006
|
-
}
|
2007
|
-
|
2008
|
-
// Fast forward
|
2009
|
-
function _forward(seekTime) {
|
2010
|
-
// Use default if needed
|
2011
|
-
if (!_is.number(seekTime)) {
|
2012
|
-
seekTime = config.seekTime;
|
2013
|
-
}
|
2014
|
-
_seek(plyr.media.currentTime + seekTime);
|
2015
|
-
}
|
2016
|
-
|
2017
|
-
// Seek to time
|
2018
|
-
// The input parameter can be an event or a number
|
2019
|
-
function _seek(input) {
|
2020
|
-
var targetTime = 0,
|
2021
|
-
paused = plyr.media.paused,
|
2022
|
-
duration = _getDuration();
|
2023
|
-
|
2024
|
-
if (_is.number(input)) {
|
2025
|
-
targetTime = input;
|
2026
|
-
} else if (_is.object(input) && _inArray(['input', 'change'], input.type)) {
|
2027
|
-
// It's the seek slider
|
2028
|
-
// Seek to the selected time
|
2029
|
-
targetTime = ((input.target.value / input.target.max) * duration);
|
2030
|
-
}
|
2031
|
-
|
2032
|
-
// Normalise targetTime
|
2033
|
-
if (targetTime < 0) {
|
2034
|
-
targetTime = 0;
|
2035
|
-
} else if (targetTime > duration) {
|
2036
|
-
targetTime = duration;
|
2037
|
-
}
|
2038
|
-
|
2039
|
-
// Update seek range and progress
|
2040
|
-
_updateSeekDisplay(targetTime);
|
2041
|
-
|
2042
|
-
// Set the current time
|
2043
|
-
// Try/catch incase the media isn't set and we're calling seek() from source() and IE moans
|
2044
|
-
try {
|
2045
|
-
plyr.media.currentTime = targetTime.toFixed(4);
|
2046
|
-
}
|
2047
|
-
catch(e) {}
|
2048
|
-
|
2049
|
-
// Embeds
|
2050
|
-
if (_inArray(config.types.embed, plyr.type)) {
|
2051
|
-
switch(plyr.type) {
|
2052
|
-
case 'youtube':
|
2053
|
-
plyr.embed.seekTo(targetTime);
|
2054
|
-
break;
|
2055
|
-
|
2056
|
-
case 'vimeo':
|
2057
|
-
// Round to nearest second for vimeo
|
2058
|
-
plyr.embed.setCurrentTime(targetTime.toFixed(0));
|
2059
|
-
break;
|
2060
|
-
|
2061
|
-
case 'soundcloud':
|
2062
|
-
plyr.embed.seekTo(targetTime * 1000);
|
2063
|
-
break;
|
2064
|
-
}
|
2065
|
-
|
2066
|
-
if (paused) {
|
2067
|
-
_pause();
|
2068
|
-
}
|
2069
|
-
|
2070
|
-
// Trigger timeupdate
|
2071
|
-
_triggerEvent(plyr.media, 'timeupdate');
|
2072
|
-
|
2073
|
-
// Set seeking flag
|
2074
|
-
plyr.media.seeking = true;
|
2075
|
-
|
2076
|
-
// Trigger seeking
|
2077
|
-
_triggerEvent(plyr.media, 'seeking');
|
2078
|
-
}
|
2079
|
-
|
2080
|
-
// Logging
|
2081
|
-
_log('Seeking to ' + plyr.media.currentTime + ' seconds');
|
2082
|
-
|
2083
|
-
// Special handling for 'manual' captions
|
2084
|
-
_seekManualCaptions(targetTime);
|
2085
|
-
}
|
2086
|
-
|
2087
|
-
// Get the duration (or custom if set)
|
2088
|
-
function _getDuration() {
|
2089
|
-
// It should be a number, but parse it just incase
|
2090
|
-
var duration = parseInt(config.duration),
|
2091
|
-
|
2092
|
-
// True duration
|
2093
|
-
mediaDuration = 0;
|
2094
|
-
|
2095
|
-
// Only if duration available
|
2096
|
-
if (plyr.media.duration !== null && !isNaN(plyr.media.duration)) {
|
2097
|
-
mediaDuration = plyr.media.duration;
|
2098
|
-
}
|
2099
|
-
|
2100
|
-
// If custom duration is funky, use regular duration
|
2101
|
-
return (isNaN(duration) ? mediaDuration : duration);
|
2102
|
-
}
|
2103
|
-
|
2104
|
-
// Check playing state
|
2105
|
-
function _checkPlaying() {
|
2106
|
-
_toggleClass(plyr.container, config.classes.playing, !plyr.media.paused);
|
2107
|
-
|
2108
|
-
_toggleClass(plyr.container, config.classes.stopped, plyr.media.paused);
|
2109
|
-
|
2110
|
-
_toggleControls(plyr.media.paused);
|
2111
|
-
}
|
2112
|
-
|
2113
|
-
// Save scroll position
|
2114
|
-
function _saveScrollPosition() {
|
2115
|
-
scroll = {
|
2116
|
-
x: window.pageXOffset || 0,
|
2117
|
-
y: window.pageYOffset || 0
|
2118
|
-
};
|
2119
|
-
}
|
2120
|
-
|
2121
|
-
// Restore scroll position
|
2122
|
-
function _restoreScrollPosition() {
|
2123
|
-
window.scrollTo(scroll.x, scroll.y);
|
2124
|
-
}
|
2125
|
-
|
2126
|
-
// Toggle fullscreen
|
2127
|
-
function _toggleFullscreen(event) {
|
2128
|
-
// Check for native support
|
2129
|
-
var nativeSupport = fullscreen.supportsFullScreen;
|
2130
|
-
|
2131
|
-
if (nativeSupport) {
|
2132
|
-
// If it's a fullscreen change event, update the UI
|
2133
|
-
if (event && event.type === fullscreen.fullScreenEventName) {
|
2134
|
-
plyr.isFullscreen = fullscreen.isFullScreen(plyr.container);
|
2135
|
-
} else {
|
2136
|
-
// Else it's a user request to enter or exit
|
2137
|
-
if (!fullscreen.isFullScreen(plyr.container)) {
|
2138
|
-
// Save scroll position
|
2139
|
-
_saveScrollPosition();
|
2140
|
-
|
2141
|
-
// Request full screen
|
2142
|
-
fullscreen.requestFullScreen(plyr.container);
|
2143
|
-
} else {
|
2144
|
-
// Bail from fullscreen
|
2145
|
-
fullscreen.cancelFullScreen();
|
2146
|
-
}
|
2147
|
-
|
2148
|
-
// Check if we're actually full screen (it could fail)
|
2149
|
-
plyr.isFullscreen = fullscreen.isFullScreen(plyr.container);
|
2150
|
-
|
2151
|
-
return;
|
2152
|
-
}
|
2153
|
-
} else {
|
2154
|
-
// Otherwise, it's a simple toggle
|
2155
|
-
plyr.isFullscreen = !plyr.isFullscreen;
|
2156
|
-
|
2157
|
-
// Bind/unbind escape key
|
2158
|
-
document.body.style.overflow = plyr.isFullscreen ? 'hidden' : '';
|
2159
|
-
}
|
2160
|
-
|
2161
|
-
// Set class hook
|
2162
|
-
_toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen);
|
2163
|
-
|
2164
|
-
// Trap focus
|
2165
|
-
_focusTrap(plyr.isFullscreen);
|
2166
|
-
|
2167
|
-
// Set button state
|
2168
|
-
if (plyr.buttons && plyr.buttons.fullscreen) {
|
2169
|
-
_toggleState(plyr.buttons.fullscreen, plyr.isFullscreen);
|
2170
|
-
}
|
2171
|
-
|
2172
|
-
// Trigger an event
|
2173
|
-
_triggerEvent(plyr.container, plyr.isFullscreen ? 'enterfullscreen' : 'exitfullscreen', true);
|
2174
|
-
|
2175
|
-
// Restore scroll position
|
2176
|
-
if (!plyr.isFullscreen && nativeSupport) {
|
2177
|
-
_restoreScrollPosition();
|
2178
|
-
}
|
2179
|
-
}
|
2180
|
-
|
2181
|
-
// Mute
|
2182
|
-
function _toggleMute(muted) {
|
2183
|
-
// If the method is called without parameter, toggle based on current value
|
2184
|
-
if (!_is.boolean(muted)) {
|
2185
|
-
muted = !plyr.media.muted;
|
2186
|
-
}
|
2187
|
-
|
2188
|
-
// Set button state
|
2189
|
-
_toggleState(plyr.buttons.mute, muted);
|
2190
|
-
|
2191
|
-
// Set mute on the player
|
2192
|
-
plyr.media.muted = muted;
|
2193
|
-
|
2194
|
-
// If volume is 0 after unmuting, set to default
|
2195
|
-
if (plyr.media.volume === 0) {
|
2196
|
-
_setVolume(config.volume);
|
2197
|
-
}
|
2198
|
-
|
2199
|
-
// Embeds
|
2200
|
-
if (_inArray(config.types.embed, plyr.type)) {
|
2201
|
-
// YouTube
|
2202
|
-
switch(plyr.type) {
|
2203
|
-
case 'youtube':
|
2204
|
-
plyr.embed[plyr.media.muted ? 'mute' : 'unMute']();
|
2205
|
-
break;
|
2206
|
-
|
2207
|
-
case 'vimeo':
|
2208
|
-
case 'soundcloud':
|
2209
|
-
plyr.embed.setVolume(plyr.media.muted ? 0 : parseFloat(config.volume / config.volumeMax));
|
2210
|
-
break;
|
2211
|
-
}
|
2212
|
-
|
2213
|
-
// Trigger volumechange for embeds
|
2214
|
-
_triggerEvent(plyr.media, 'volumechange');
|
2215
|
-
}
|
2216
|
-
}
|
2217
|
-
|
2218
|
-
// Set volume
|
2219
|
-
function _setVolume(volume) {
|
2220
|
-
var max = config.volumeMax,
|
2221
|
-
min = config.volumeMin;
|
2222
|
-
|
2223
|
-
// Load volume from storage if no value specified
|
2224
|
-
if (_is.undefined(volume)) {
|
2225
|
-
volume = plyr.storage.volume;
|
2226
|
-
}
|
2227
|
-
|
2228
|
-
// Use config if all else fails
|
2229
|
-
if (volume === null || isNaN(volume)) {
|
2230
|
-
volume = config.volume;
|
2231
|
-
}
|
2232
|
-
|
2233
|
-
// Maximum is volumeMax
|
2234
|
-
if (volume > max) {
|
2235
|
-
volume = max;
|
2236
|
-
}
|
2237
|
-
// Minimum is volumeMin
|
2238
|
-
if (volume < min) {
|
2239
|
-
volume = min;
|
2240
|
-
}
|
2241
|
-
|
2242
|
-
// Set the player volume
|
2243
|
-
plyr.media.volume = parseFloat(volume / max);
|
2244
|
-
|
2245
|
-
// Set the display
|
2246
|
-
if (plyr.volume.display) {
|
2247
|
-
plyr.volume.display.value = volume;
|
2248
|
-
}
|
2249
|
-
|
2250
|
-
// Embeds
|
2251
|
-
if (_inArray(config.types.embed, plyr.type)) {
|
2252
|
-
switch(plyr.type) {
|
2253
|
-
case 'youtube':
|
2254
|
-
plyr.embed.setVolume(plyr.media.volume * 100);
|
2255
|
-
break;
|
2256
|
-
|
2257
|
-
case 'vimeo':
|
2258
|
-
case 'soundcloud':
|
2259
|
-
plyr.embed.setVolume(plyr.media.volume);
|
2260
|
-
break;
|
2261
|
-
}
|
2262
|
-
|
2263
|
-
// Trigger volumechange for embeds
|
2264
|
-
_triggerEvent(plyr.media, 'volumechange');
|
2265
|
-
}
|
2266
|
-
|
2267
|
-
// Toggle muted state
|
2268
|
-
if (volume === 0) {
|
2269
|
-
plyr.media.muted = true;
|
2270
|
-
} else if (plyr.media.muted && volume > 0) {
|
2271
|
-
_toggleMute();
|
2272
|
-
}
|
2273
|
-
}
|
2274
|
-
|
2275
|
-
// Increase volume
|
2276
|
-
function _increaseVolume(step) {
|
2277
|
-
var volume = plyr.media.muted ? 0 : (plyr.media.volume * config.volumeMax);
|
2278
|
-
|
2279
|
-
if (!_is.number(step)) {
|
2280
|
-
step = config.volumeStep;
|
2281
|
-
}
|
2282
|
-
|
2283
|
-
_setVolume(volume + step);
|
2284
|
-
}
|
2285
|
-
|
2286
|
-
// Decrease volume
|
2287
|
-
function _decreaseVolume(step) {
|
2288
|
-
var volume = plyr.media.muted ? 0 : (plyr.media.volume * config.volumeMax);
|
2289
|
-
|
2290
|
-
if (!_is.number(step)) {
|
2291
|
-
step = config.volumeStep;
|
2292
|
-
}
|
2293
|
-
|
2294
|
-
_setVolume(volume - step);
|
2295
|
-
}
|
2296
|
-
|
2297
|
-
// Update volume UI and storage
|
2298
|
-
function _updateVolume() {
|
2299
|
-
// Get the current volume
|
2300
|
-
var volume = plyr.media.muted ? 0 : (plyr.media.volume * config.volumeMax);
|
2301
|
-
|
2302
|
-
// Update the <input type="range"> if present
|
2303
|
-
if (plyr.supported.full) {
|
2304
|
-
if (plyr.volume.input) {
|
2305
|
-
plyr.volume.input.value = volume;
|
2306
|
-
}
|
2307
|
-
if (plyr.volume.display) {
|
2308
|
-
plyr.volume.display.value = volume;
|
2309
|
-
}
|
2310
|
-
}
|
2311
|
-
|
2312
|
-
// Update the volume in storage
|
2313
|
-
_updateStorage({volume: volume});
|
2314
|
-
|
2315
|
-
// Toggle class if muted
|
2316
|
-
_toggleClass(plyr.container, config.classes.muted, (volume === 0));
|
2317
|
-
|
2318
|
-
// Update checkbox for mute state
|
2319
|
-
if (plyr.supported.full && plyr.buttons.mute) {
|
2320
|
-
_toggleState(plyr.buttons.mute, (volume === 0));
|
2321
|
-
}
|
2322
|
-
}
|
2323
|
-
|
2324
|
-
// Toggle captions
|
2325
|
-
function _toggleCaptions(show) {
|
2326
|
-
// If there's no full support, or there's no caption toggle
|
2327
|
-
if (!plyr.supported.full || !plyr.buttons.captions) {
|
2328
|
-
return;
|
2329
|
-
}
|
2330
|
-
|
2331
|
-
// If the method is called without parameter, toggle based on current value
|
2332
|
-
if (!_is.boolean(show)) {
|
2333
|
-
show = (plyr.container.className.indexOf(config.classes.captions.active) === -1);
|
2334
|
-
}
|
2335
|
-
|
2336
|
-
// Set global
|
2337
|
-
plyr.captionsEnabled = show;
|
2338
|
-
|
2339
|
-
// Toggle state
|
2340
|
-
_toggleState(plyr.buttons.captions, plyr.captionsEnabled);
|
2341
|
-
|
2342
|
-
// Add class hook
|
2343
|
-
_toggleClass(plyr.container, config.classes.captions.active, plyr.captionsEnabled);
|
2344
|
-
|
2345
|
-
// Trigger an event
|
2346
|
-
_triggerEvent(plyr.container, plyr.captionsEnabled ? 'captionsenabled' : 'captionsdisabled', true);
|
2347
|
-
|
2348
|
-
// Save captions state to localStorage
|
2349
|
-
_updateStorage({captionsEnabled: plyr.captionsEnabled});
|
2350
|
-
}
|
2351
|
-
|
2352
|
-
// Check if media is loading
|
2353
|
-
function _checkLoading(event) {
|
2354
|
-
var loading = (event.type === 'waiting');
|
2355
|
-
|
2356
|
-
// Clear timer
|
2357
|
-
clearTimeout(timers.loading);
|
2358
|
-
|
2359
|
-
// Timer to prevent flicker when seeking
|
2360
|
-
timers.loading = setTimeout(function() {
|
2361
|
-
// Toggle container class hook
|
2362
|
-
_toggleClass(plyr.container, config.classes.loading, loading);
|
2363
|
-
|
2364
|
-
// Show controls if loading, hide if done
|
2365
|
-
_toggleControls(loading);
|
2366
|
-
}, (loading ? 250 : 0));
|
2367
|
-
}
|
2368
|
-
|
2369
|
-
// Update <progress> elements
|
2370
|
-
function _updateProgress(event) {
|
2371
|
-
if (!plyr.supported.full) {
|
2372
|
-
return;
|
2373
|
-
}
|
2374
|
-
|
2375
|
-
var progress = plyr.progress.played,
|
2376
|
-
value = 0,
|
2377
|
-
duration = _getDuration();
|
2378
|
-
|
2379
|
-
if (event) {
|
2380
|
-
switch (event.type) {
|
2381
|
-
// Video playing
|
2382
|
-
case 'timeupdate':
|
2383
|
-
case 'seeking':
|
2384
|
-
if (plyr.controls.pressed) {
|
2385
|
-
return;
|
2386
|
-
}
|
2387
|
-
|
2388
|
-
value = _getPercentage(plyr.media.currentTime, duration);
|
2389
|
-
|
2390
|
-
// Set seek range value only if it's a 'natural' time event
|
2391
|
-
if (event.type === 'timeupdate' && plyr.buttons.seek) {
|
2392
|
-
plyr.buttons.seek.value = value;
|
2393
|
-
}
|
2394
|
-
|
2395
|
-
break;
|
2396
|
-
|
2397
|
-
// Check buffer status
|
2398
|
-
case 'playing':
|
2399
|
-
case 'progress':
|
2400
|
-
progress = plyr.progress.buffer;
|
2401
|
-
value = (function() {
|
2402
|
-
var buffered = plyr.media.buffered;
|
2403
|
-
|
2404
|
-
if (buffered && buffered.length) {
|
2405
|
-
// HTML5
|
2406
|
-
return _getPercentage(buffered.end(0), duration);
|
2407
|
-
} else if (_is.number(buffered)) {
|
2408
|
-
// YouTube returns between 0 and 1
|
2409
|
-
return (buffered * 100);
|
2410
|
-
}
|
2411
|
-
|
2412
|
-
return 0;
|
2413
|
-
})();
|
2414
|
-
|
2415
|
-
break;
|
2416
|
-
}
|
2417
|
-
}
|
2418
|
-
|
2419
|
-
// Set values
|
2420
|
-
_setProgress(progress, value);
|
2421
|
-
}
|
2422
|
-
|
2423
|
-
// Set <progress> value
|
2424
|
-
function _setProgress(progress, value) {
|
2425
|
-
if (!plyr.supported.full) {
|
2426
|
-
return;
|
2427
|
-
}
|
2428
|
-
|
2429
|
-
// Default to 0
|
2430
|
-
if (_is.undefined(value)) {
|
2431
|
-
value = 0;
|
2432
|
-
}
|
2433
|
-
// Default to buffer or bail
|
2434
|
-
if (_is.undefined(progress)) {
|
2435
|
-
if (plyr.progress && plyr.progress.buffer) {
|
2436
|
-
progress = plyr.progress.buffer;
|
2437
|
-
} else {
|
2438
|
-
return;
|
2439
|
-
}
|
2440
|
-
}
|
2441
|
-
|
2442
|
-
// One progress element passed
|
2443
|
-
if (_is.htmlElement(progress)) {
|
2444
|
-
progress.value = value;
|
2445
|
-
} else if (progress) {
|
2446
|
-
// Object of progress + text element
|
2447
|
-
if (progress.bar) {
|
2448
|
-
progress.bar.value = value;
|
2449
|
-
}
|
2450
|
-
if (progress.text) {
|
2451
|
-
progress.text.innerHTML = value;
|
2452
|
-
}
|
2453
|
-
}
|
2454
|
-
}
|
2455
|
-
|
2456
|
-
// Update the displayed time
|
2457
|
-
function _updateTimeDisplay(time, element) {
|
2458
|
-
// Bail if there's no duration display
|
2459
|
-
if (!element) {
|
2460
|
-
return;
|
2461
|
-
}
|
2462
|
-
|
2463
|
-
// Fallback to 0
|
2464
|
-
if (isNaN(time)) {
|
2465
|
-
time = 0;
|
2466
|
-
}
|
2467
|
-
|
2468
|
-
plyr.secs = parseInt(time % 60);
|
2469
|
-
plyr.mins = parseInt((time / 60) % 60);
|
2470
|
-
plyr.hours = parseInt(((time / 60) / 60) % 60);
|
2471
|
-
|
2472
|
-
// Do we need to display hours?
|
2473
|
-
var displayHours = (parseInt(((_getDuration() / 60) / 60) % 60) > 0);
|
2474
|
-
|
2475
|
-
// Ensure it's two digits. For example, 03 rather than 3.
|
2476
|
-
plyr.secs = ('0' + plyr.secs).slice(-2);
|
2477
|
-
plyr.mins = ('0' + plyr.mins).slice(-2);
|
2478
|
-
|
2479
|
-
// Render
|
2480
|
-
element.innerHTML = (displayHours ? plyr.hours + ':' : '') + plyr.mins + ':' + plyr.secs;
|
2481
|
-
}
|
2482
|
-
|
2483
|
-
// Show the duration on metadataloaded
|
2484
|
-
function _displayDuration() {
|
2485
|
-
if (!plyr.supported.full) {
|
2486
|
-
return;
|
2487
|
-
}
|
2488
|
-
|
2489
|
-
// Determine duration
|
2490
|
-
var duration = _getDuration() || 0;
|
2491
|
-
|
2492
|
-
// If there's only one time display, display duration there
|
2493
|
-
if (!plyr.duration && config.displayDuration && plyr.media.paused) {
|
2494
|
-
_updateTimeDisplay(duration, plyr.currentTime);
|
2495
|
-
}
|
2496
|
-
|
2497
|
-
// If there's a duration element, update content
|
2498
|
-
if (plyr.duration) {
|
2499
|
-
_updateTimeDisplay(duration, plyr.duration);
|
2500
|
-
}
|
2501
|
-
|
2502
|
-
// Update the tooltip (if visible)
|
2503
|
-
_updateSeekTooltip();
|
2504
|
-
}
|
2505
|
-
|
2506
|
-
// Handle time change event
|
2507
|
-
function _timeUpdate(event) {
|
2508
|
-
// Duration
|
2509
|
-
_updateTimeDisplay(plyr.media.currentTime, plyr.currentTime);
|
2510
|
-
|
2511
|
-
// Ignore updates while seeking
|
2512
|
-
if (event && event.type === 'timeupdate' && plyr.media.seeking) {
|
2513
|
-
return;
|
2514
|
-
}
|
2515
|
-
|
2516
|
-
// Playing progress
|
2517
|
-
_updateProgress(event);
|
2518
|
-
}
|
2519
|
-
|
2520
|
-
// Update seek range and progress
|
2521
|
-
function _updateSeekDisplay(time) {
|
2522
|
-
// Default to 0
|
2523
|
-
if (!_is.number(time)) {
|
2524
|
-
time = 0;
|
2525
|
-
}
|
2526
|
-
|
2527
|
-
var duration = _getDuration(),
|
2528
|
-
value = _getPercentage(time, duration);
|
2529
|
-
|
2530
|
-
// Update progress
|
2531
|
-
if (plyr.progress && plyr.progress.played) {
|
2532
|
-
plyr.progress.played.value = value;
|
2533
|
-
}
|
2534
|
-
|
2535
|
-
// Update seek range input
|
2536
|
-
if (plyr.buttons && plyr.buttons.seek) {
|
2537
|
-
plyr.buttons.seek.value = value;
|
2538
|
-
}
|
2539
|
-
}
|
2540
|
-
|
2541
|
-
// Update hover tooltip for seeking
|
2542
|
-
function _updateSeekTooltip(event) {
|
2543
|
-
var duration = _getDuration();
|
2544
|
-
|
2545
|
-
// Bail if setting not true
|
2546
|
-
if (!config.tooltips.seek || !plyr.progress.container || duration === 0) {
|
2547
|
-
return;
|
2548
|
-
}
|
2549
|
-
|
2550
|
-
// Calculate percentage
|
2551
|
-
var clientRect = plyr.progress.container.getBoundingClientRect(),
|
2552
|
-
percent = 0,
|
2553
|
-
visible = config.classes.tooltip + '--visible';
|
2554
|
-
|
2555
|
-
// Determine percentage, if already visible
|
2556
|
-
if (!event) {
|
2557
|
-
if (_hasClass(plyr.progress.tooltip, visible)) {
|
2558
|
-
percent = plyr.progress.tooltip.style.left.replace('%', '');
|
2559
|
-
} else {
|
2560
|
-
return;
|
2561
|
-
}
|
2562
|
-
} else {
|
2563
|
-
percent = ((100 / clientRect.width) * (event.pageX - clientRect.left));
|
2564
|
-
}
|
2565
|
-
|
2566
|
-
// Set bounds
|
2567
|
-
if (percent < 0) {
|
2568
|
-
percent = 0;
|
2569
|
-
} else if (percent > 100) {
|
2570
|
-
percent = 100;
|
2571
|
-
}
|
2572
|
-
|
2573
|
-
// Display the time a click would seek to
|
2574
|
-
_updateTimeDisplay(((duration / 100) * percent), plyr.progress.tooltip);
|
2575
|
-
|
2576
|
-
// Set position
|
2577
|
-
plyr.progress.tooltip.style.left = percent + "%";
|
2578
|
-
|
2579
|
-
// Show/hide the tooltip
|
2580
|
-
// If the event is a moues in/out and percentage is inside bounds
|
2581
|
-
if (event && _inArray(['mouseenter', 'mouseleave'], event.type)) {
|
2582
|
-
_toggleClass(plyr.progress.tooltip, visible, (event.type === 'mouseenter'));
|
2583
|
-
}
|
2584
|
-
}
|
2585
|
-
|
2586
|
-
// Show the player controls in fullscreen mode
|
2587
|
-
function _toggleControls(toggle) {
|
2588
|
-
// Don't hide if config says not to, it's audio, or not ready or loading
|
2589
|
-
if (!config.hideControls || plyr.type === 'audio') {
|
2590
|
-
return;
|
2591
|
-
}
|
2592
|
-
|
2593
|
-
var delay = 0,
|
2594
|
-
isEnterFullscreen = false,
|
2595
|
-
show = toggle,
|
2596
|
-
loading = _hasClass(plyr.container, config.classes.loading);
|
2597
|
-
|
2598
|
-
// Default to false if no boolean
|
2599
|
-
if (!_is.boolean(toggle)) {
|
2600
|
-
if (toggle && toggle.type) {
|
2601
|
-
// Is the enter fullscreen event
|
2602
|
-
isEnterFullscreen = (toggle.type === 'enterfullscreen');
|
2603
|
-
|
2604
|
-
// Whether to show controls
|
2605
|
-
show = _inArray(['mousemove', 'touchstart', 'mouseenter', 'focus'], toggle.type);
|
2606
|
-
|
2607
|
-
// Delay hiding on move events
|
2608
|
-
if (_inArray(['mousemove', 'touchmove'], toggle.type)) {
|
2609
|
-
delay = 2000;
|
2610
|
-
}
|
2611
|
-
|
2612
|
-
// Delay a little more for keyboard users
|
2613
|
-
if (toggle.type === 'focus') {
|
2614
|
-
delay = 3000;
|
2615
|
-
}
|
2616
|
-
} else {
|
2617
|
-
show = _hasClass(plyr.container, config.classes.hideControls);
|
2618
|
-
}
|
2619
|
-
}
|
2620
|
-
|
2621
|
-
// Clear timer every movement
|
2622
|
-
window.clearTimeout(timers.hover);
|
2623
|
-
|
2624
|
-
// If the mouse is not over the controls, set a timeout to hide them
|
2625
|
-
if (show || plyr.media.paused || loading) {
|
2626
|
-
_toggleClass(plyr.container, config.classes.hideControls, false);
|
2627
|
-
|
2628
|
-
// Always show controls when paused or if touch
|
2629
|
-
if (plyr.media.paused || loading) {
|
2630
|
-
return;
|
2631
|
-
}
|
2632
|
-
|
2633
|
-
// Delay for hiding on touch
|
2634
|
-
if (plyr.browser.isTouch) {
|
2635
|
-
delay = 3000;
|
2636
|
-
}
|
2637
|
-
}
|
2638
|
-
|
2639
|
-
// If toggle is false or if we're playing (regardless of toggle),
|
2640
|
-
// then set the timer to hide the controls
|
2641
|
-
if (!show || !plyr.media.paused) {
|
2642
|
-
timers.hover = window.setTimeout(function() {
|
2643
|
-
// If the mouse is over the controls (and not entering fullscreen), bail
|
2644
|
-
if ((plyr.controls.pressed || plyr.controls.hover) && !isEnterFullscreen) {
|
2645
|
-
return;
|
2646
|
-
}
|
2647
|
-
|
2648
|
-
_toggleClass(plyr.container, config.classes.hideControls, true);
|
2649
|
-
}, delay);
|
2650
|
-
}
|
2651
|
-
}
|
2652
|
-
|
2653
|
-
// Add common function to retrieve media source
|
2654
|
-
function _source(source) {
|
2655
|
-
// If not null or undefined, parse it
|
2656
|
-
if (!_is.undefined(source)) {
|
2657
|
-
_updateSource(source);
|
2658
|
-
return;
|
2659
|
-
}
|
2660
|
-
|
2661
|
-
// Return the current source
|
2662
|
-
var url;
|
2663
|
-
switch(plyr.type) {
|
2664
|
-
case 'youtube':
|
2665
|
-
url = plyr.embed.getVideoUrl();
|
2666
|
-
break;
|
2667
|
-
|
2668
|
-
case 'vimeo':
|
2669
|
-
plyr.embed.getVideoUrl.then(function (value) {
|
2670
|
-
url = value;
|
2671
|
-
});
|
2672
|
-
break;
|
2673
|
-
|
2674
|
-
case 'soundcloud':
|
2675
|
-
plyr.embed.getCurrentSound(function(object) {
|
2676
|
-
url = object.permalink_url;
|
2677
|
-
});
|
2678
|
-
break;
|
2679
|
-
|
2680
|
-
default:
|
2681
|
-
url = plyr.media.currentSrc;
|
2682
|
-
break;
|
2683
|
-
}
|
2684
|
-
|
2685
|
-
return url || '';
|
2686
|
-
}
|
2687
|
-
|
2688
|
-
// Update source
|
2689
|
-
// Sources are not checked for support so be careful
|
2690
|
-
function _updateSource(source) {
|
2691
|
-
if (!_is.object(source) || !('sources' in source) || !source.sources.length) {
|
2692
|
-
_warn('Invalid source format');
|
2693
|
-
return;
|
2694
|
-
}
|
2695
|
-
|
2696
|
-
// Remove ready class hook
|
2697
|
-
_toggleClass(plyr.container, config.classes.ready, false);
|
2698
|
-
|
2699
|
-
// Pause playback
|
2700
|
-
_pause();
|
2701
|
-
|
2702
|
-
// Update seek range and progress
|
2703
|
-
_updateSeekDisplay();
|
2704
|
-
|
2705
|
-
// Reset buffer progress
|
2706
|
-
_setProgress();
|
2707
|
-
|
2708
|
-
// Cancel current network requests
|
2709
|
-
_cancelRequests();
|
2710
|
-
|
2711
|
-
// Setup new source
|
2712
|
-
function setup() {
|
2713
|
-
// Remove embed object
|
2714
|
-
plyr.embed = null;
|
2715
|
-
|
2716
|
-
// Remove the old media
|
2717
|
-
_remove(plyr.media);
|
2718
|
-
|
2719
|
-
// Remove video container
|
2720
|
-
if (plyr.type === 'video' && plyr.videoContainer) {
|
2721
|
-
_remove(plyr.videoContainer);
|
2722
|
-
}
|
2723
|
-
|
2724
|
-
// Reset class name
|
2725
|
-
if (plyr.container) {
|
2726
|
-
plyr.container.removeAttribute('class');
|
2727
|
-
}
|
2728
|
-
|
2729
|
-
// Set the type
|
2730
|
-
if ('type' in source) {
|
2731
|
-
plyr.type = source.type;
|
2732
|
-
|
2733
|
-
// Get child type for video (it might be an embed)
|
2734
|
-
if (plyr.type === 'video') {
|
2735
|
-
var firstSource = source.sources[0];
|
2736
|
-
|
2737
|
-
if ('type' in firstSource && _inArray(config.types.embed, firstSource.type)) {
|
2738
|
-
plyr.type = firstSource.type;
|
2739
|
-
}
|
2740
|
-
}
|
2741
|
-
}
|
2742
|
-
|
2743
|
-
// Check for support
|
2744
|
-
plyr.supported = supported(plyr.type);
|
2745
|
-
|
2746
|
-
// Create new markup
|
2747
|
-
switch(plyr.type) {
|
2748
|
-
case 'video':
|
2749
|
-
plyr.media = document.createElement('video');
|
2750
|
-
break;
|
2751
|
-
|
2752
|
-
case 'audio':
|
2753
|
-
plyr.media = document.createElement('audio');
|
2754
|
-
break;
|
2755
|
-
|
2756
|
-
case 'youtube':
|
2757
|
-
case 'vimeo':
|
2758
|
-
case 'soundcloud':
|
2759
|
-
plyr.media = document.createElement('div');
|
2760
|
-
plyr.embedId = source.sources[0].src;
|
2761
|
-
break;
|
2762
|
-
}
|
2763
|
-
|
2764
|
-
// Inject the new element
|
2765
|
-
_prependChild(plyr.container, plyr.media);
|
2766
|
-
|
2767
|
-
// Autoplay the new source?
|
2768
|
-
if (_is.boolean(source.autoplay)) {
|
2769
|
-
config.autoplay = source.autoplay;
|
2770
|
-
}
|
2771
|
-
|
2772
|
-
// Set attributes for audio and video
|
2773
|
-
if (_inArray(config.types.html5, plyr.type)) {
|
2774
|
-
if (config.crossorigin) {
|
2775
|
-
plyr.media.setAttribute('crossorigin', '');
|
2776
|
-
}
|
2777
|
-
if (config.autoplay) {
|
2778
|
-
plyr.media.setAttribute('autoplay', '');
|
2779
|
-
}
|
2780
|
-
if ('poster' in source) {
|
2781
|
-
plyr.media.setAttribute('poster', source.poster);
|
2782
|
-
}
|
2783
|
-
if (config.loop) {
|
2784
|
-
plyr.media.setAttribute('loop', '');
|
2785
|
-
}
|
2786
|
-
}
|
2787
|
-
|
2788
|
-
// Restore class hooks
|
2789
|
-
_toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen);
|
2790
|
-
_toggleClass(plyr.container, config.classes.captions.active, plyr.captionsEnabled);
|
2791
|
-
_toggleStyleHook();
|
2792
|
-
|
2793
|
-
// Set new sources for html5
|
2794
|
-
if (_inArray(config.types.html5, plyr.type)) {
|
2795
|
-
_insertChildElements('source', source.sources);
|
2796
|
-
}
|
2797
|
-
|
2798
|
-
// Set up from scratch
|
2799
|
-
_setupMedia();
|
2800
|
-
|
2801
|
-
// HTML5 stuff
|
2802
|
-
if (_inArray(config.types.html5, plyr.type)) {
|
2803
|
-
// Setup captions
|
2804
|
-
if ('tracks' in source) {
|
2805
|
-
_insertChildElements('track', source.tracks);
|
2806
|
-
}
|
2807
|
-
|
2808
|
-
// Load HTML5 sources
|
2809
|
-
plyr.media.load();
|
2810
|
-
}
|
2811
|
-
|
2812
|
-
// If HTML5 or embed but not fully supported, setupInterface and call ready now
|
2813
|
-
if (_inArray(config.types.html5, plyr.type) || (_inArray(config.types.embed, plyr.type) && !plyr.supported.full)) {
|
2814
|
-
// Setup interface
|
2815
|
-
_setupInterface();
|
2816
|
-
|
2817
|
-
// Call ready
|
2818
|
-
_ready();
|
2819
|
-
}
|
2820
|
-
|
2821
|
-
// Set aria title and iframe title
|
2822
|
-
config.title = source.title;
|
2823
|
-
_setTitle();
|
2824
|
-
}
|
2825
|
-
|
2826
|
-
// Destroy instance adn wait for callback
|
2827
|
-
// Vimeo throws a wobbly if you don't wait
|
2828
|
-
_destroy(setup, false);
|
2829
|
-
}
|
2830
|
-
|
2831
|
-
// Update poster
|
2832
|
-
function _updatePoster(source) {
|
2833
|
-
if (plyr.type === 'video') {
|
2834
|
-
plyr.media.setAttribute('poster', source);
|
2835
|
-
}
|
2836
|
-
}
|
2837
|
-
|
2838
|
-
// Listen for control events
|
2839
|
-
function _controlListeners() {
|
2840
|
-
// IE doesn't support input event, so we fallback to change
|
2841
|
-
var inputEvent = (plyr.browser.isIE ? 'change' : 'input');
|
2842
|
-
|
2843
|
-
// Click play/pause helper
|
2844
|
-
function togglePlay() {
|
2845
|
-
var play = _togglePlay();
|
2846
|
-
|
2847
|
-
// Determine which buttons
|
2848
|
-
var trigger = plyr.buttons[play ? 'play' : 'pause'],
|
2849
|
-
target = plyr.buttons[play ? 'pause' : 'play'];
|
2850
|
-
|
2851
|
-
// Get the last play button to account for the large play button
|
2852
|
-
if (target && target.length > 1) {
|
2853
|
-
target = target[target.length - 1];
|
2854
|
-
} else {
|
2855
|
-
target = target[0];
|
2856
|
-
}
|
2857
|
-
|
2858
|
-
// Setup focus and tab focus
|
2859
|
-
if (target) {
|
2860
|
-
var hadTabFocus = _hasClass(trigger, config.classes.tabFocus);
|
2861
|
-
|
2862
|
-
setTimeout(function() {
|
2863
|
-
target.focus();
|
2864
|
-
|
2865
|
-
if (hadTabFocus) {
|
2866
|
-
_toggleClass(trigger, config.classes.tabFocus, false);
|
2867
|
-
_toggleClass(target, config.classes.tabFocus, true);
|
2868
|
-
}
|
2869
|
-
}, 100);
|
2870
|
-
}
|
2871
|
-
}
|
2872
|
-
|
2873
|
-
// Get the focused element
|
2874
|
-
function getFocusElement() {
|
2875
|
-
var focused = document.activeElement;
|
2876
|
-
|
2877
|
-
if (!focused || focused === document.body) {
|
2878
|
-
focused = null;
|
2879
|
-
} else {
|
2880
|
-
focused = document.querySelector(':focus');
|
2881
|
-
}
|
2882
|
-
|
2883
|
-
return focused;
|
2884
|
-
}
|
2885
|
-
|
2886
|
-
// Get the key code for an event
|
2887
|
-
function getKeyCode(event) {
|
2888
|
-
return event.keyCode ? event.keyCode : event.which;
|
2889
|
-
}
|
2890
|
-
|
2891
|
-
// Detect tab focus
|
2892
|
-
function checkTabFocus(focused) {
|
2893
|
-
for (var button in plyr.buttons) {
|
2894
|
-
var element = plyr.buttons[button];
|
2895
|
-
|
2896
|
-
if (_is.nodeList(element)) {
|
2897
|
-
for (var i = 0; i < element.length; i++) {
|
2898
|
-
_toggleClass(element[i], config.classes.tabFocus, (element[i] === focused));
|
2899
|
-
}
|
2900
|
-
} else {
|
2901
|
-
_toggleClass(element, config.classes.tabFocus, (element === focused));
|
2902
|
-
}
|
2903
|
-
}
|
2904
|
-
}
|
2905
|
-
|
2906
|
-
// Keyboard shortcuts
|
2907
|
-
if (config.keyboardShorcuts.focused) {
|
2908
|
-
var last = null;
|
2909
|
-
|
2910
|
-
// Handle global presses
|
2911
|
-
if (config.keyboardShorcuts.global) {
|
2912
|
-
_on(window, 'keydown keyup', function(event) {
|
2913
|
-
var code = getKeyCode(event),
|
2914
|
-
focused = getFocusElement(),
|
2915
|
-
allowed = [48,49,50,51,52,53,54,56,57,75,77,70,67],
|
2916
|
-
count = get().length;
|
2917
|
-
|
2918
|
-
// Only handle global key press if there's only one player
|
2919
|
-
// and the key is in the allowed keys
|
2920
|
-
// and if the focused element is not editable (e.g. text input)
|
2921
|
-
// and any that accept key input http://webaim.org/techniques/keyboard/
|
2922
|
-
if (count === 1 && _inArray(allowed, code) && (!_is.htmlElement(focused) || !_matches(focused, config.selectors.editable))) {
|
2923
|
-
handleKey(event);
|
2924
|
-
}
|
2925
|
-
});
|
2926
|
-
}
|
2927
|
-
|
2928
|
-
// Handle presses on focused
|
2929
|
-
_on(plyr.container, 'keydown keyup', handleKey);
|
2930
|
-
}
|
2931
|
-
|
2932
|
-
function handleKey(event) {
|
2933
|
-
var code = getKeyCode(event),
|
2934
|
-
pressed = event.type === 'keydown',
|
2935
|
-
held = pressed && code === last;
|
2936
|
-
|
2937
|
-
// If the event is bubbled from the media element
|
2938
|
-
// Firefox doesn't get the keycode for whatever reason
|
2939
|
-
if (!_is.number(code)) {
|
2940
|
-
return;
|
2941
|
-
}
|
2942
|
-
|
2943
|
-
// Seek by the number keys
|
2944
|
-
function seekByKey() {
|
2945
|
-
// Get current duration
|
2946
|
-
var duration = plyr.media.duration;
|
2947
|
-
|
2948
|
-
// Bail if we have no duration set
|
2949
|
-
if (!_is.number(duration)) {
|
2950
|
-
return;
|
2951
|
-
}
|
2952
|
-
|
2953
|
-
// Divide the max duration into 10th's and times by the number value
|
2954
|
-
_seek((duration / 10) * (code - 48));
|
2955
|
-
}
|
2956
|
-
|
2957
|
-
// Handle the key on keydown
|
2958
|
-
// Reset on keyup
|
2959
|
-
if (pressed) {
|
2960
|
-
// Which keycodes should we prevent default
|
2961
|
-
var preventDefault = [48,49,50,51,52,53,54,56,57,32,75,38,40,77,39,37,70,67];
|
2962
|
-
|
2963
|
-
// If the code is found prevent default (e.g. prevent scrolling for arrows)
|
2964
|
-
if (_inArray(preventDefault, code)) {
|
2965
|
-
event.preventDefault();
|
2966
|
-
event.stopPropagation();
|
2967
|
-
}
|
2968
|
-
|
2969
|
-
switch(code) {
|
2970
|
-
// 0-9
|
2971
|
-
case 48:
|
2972
|
-
case 49:
|
2973
|
-
case 50:
|
2974
|
-
case 51:
|
2975
|
-
case 52:
|
2976
|
-
case 53:
|
2977
|
-
case 54:
|
2978
|
-
case 55:
|
2979
|
-
case 56:
|
2980
|
-
case 57: if (!held) { seekByKey(); } break;
|
2981
|
-
// Space and K key
|
2982
|
-
case 32:
|
2983
|
-
case 75: if (!held) { _togglePlay(); } break;
|
2984
|
-
// Arrow up
|
2985
|
-
case 38: _increaseVolume(); break;
|
2986
|
-
// Arrow down
|
2987
|
-
case 40: _decreaseVolume(); break;
|
2988
|
-
// M key
|
2989
|
-
case 77: if (!held) { _toggleMute() } break;
|
2990
|
-
// Arrow forward
|
2991
|
-
case 39: _forward(); break;
|
2992
|
-
// Arrow back
|
2993
|
-
case 37: _rewind(); break;
|
2994
|
-
// F key
|
2995
|
-
case 70: _toggleFullscreen(); break;
|
2996
|
-
// C key
|
2997
|
-
case 67: if (!held) { _toggleCaptions(); } break;
|
2998
|
-
}
|
2999
|
-
|
3000
|
-
// Escape is handle natively when in full screen
|
3001
|
-
// So we only need to worry about non native
|
3002
|
-
if (!fullscreen.supportsFullScreen && plyr.isFullscreen && code === 27) {
|
3003
|
-
_toggleFullscreen();
|
3004
|
-
}
|
3005
|
-
|
3006
|
-
// Store last code for next cycle
|
3007
|
-
last = code;
|
3008
|
-
} else {
|
3009
|
-
last = null;
|
3010
|
-
}
|
3011
|
-
}
|
3012
|
-
|
3013
|
-
// Focus/tab management
|
3014
|
-
_on(window, 'keyup', function(event) {
|
3015
|
-
var code = getKeyCode(event),
|
3016
|
-
focused = getFocusElement();
|
3017
|
-
|
3018
|
-
if (code === 9) {
|
3019
|
-
checkTabFocus(focused);
|
3020
|
-
}
|
3021
|
-
});
|
3022
|
-
_on(document.body, 'click', function() {
|
3023
|
-
_toggleClass(_getElement('.' + config.classes.tabFocus), config.classes.tabFocus, false);
|
3024
|
-
});
|
3025
|
-
for (var button in plyr.buttons) {
|
3026
|
-
var element = plyr.buttons[button];
|
3027
|
-
|
3028
|
-
_on(element, 'blur', function() {
|
3029
|
-
_toggleClass(element, 'tab-focus', false);
|
3030
|
-
});
|
3031
|
-
}
|
3032
|
-
|
3033
|
-
// Play
|
3034
|
-
_proxyListener(plyr.buttons.play, 'click', config.listeners.play, togglePlay);
|
3035
|
-
|
3036
|
-
// Pause
|
3037
|
-
_proxyListener(plyr.buttons.pause, 'click', config.listeners.pause, togglePlay);
|
3038
|
-
|
3039
|
-
// Restart
|
3040
|
-
_proxyListener(plyr.buttons.restart, 'click', config.listeners.restart, _seek);
|
3041
|
-
|
3042
|
-
// Rewind
|
3043
|
-
_proxyListener(plyr.buttons.rewind, 'click', config.listeners.rewind, _rewind);
|
3044
|
-
|
3045
|
-
// Fast forward
|
3046
|
-
_proxyListener(plyr.buttons.forward, 'click', config.listeners.forward, _forward);
|
3047
|
-
|
3048
|
-
// Seek
|
3049
|
-
_proxyListener(plyr.buttons.seek, inputEvent, config.listeners.seek, _seek);
|
3050
|
-
|
3051
|
-
// Set volume
|
3052
|
-
_proxyListener(plyr.volume.input, inputEvent, config.listeners.volume, function() {
|
3053
|
-
_setVolume(plyr.volume.input.value);
|
3054
|
-
});
|
3055
|
-
|
3056
|
-
// Mute
|
3057
|
-
_proxyListener(plyr.buttons.mute, 'click', config.listeners.mute, _toggleMute);
|
3058
|
-
|
3059
|
-
// Fullscreen
|
3060
|
-
_proxyListener(plyr.buttons.fullscreen, 'click', config.listeners.fullscreen, _toggleFullscreen);
|
3061
|
-
|
3062
|
-
// Handle user exiting fullscreen by escaping etc
|
3063
|
-
if (fullscreen.supportsFullScreen) {
|
3064
|
-
_on(document, fullscreen.fullScreenEventName, _toggleFullscreen);
|
3065
|
-
}
|
3066
|
-
|
3067
|
-
// Captions
|
3068
|
-
_on(plyr.buttons.captions, 'click', _toggleCaptions);
|
3069
|
-
|
3070
|
-
// Seek tooltip
|
3071
|
-
_on(plyr.progress.container, 'mouseenter mouseleave mousemove', _updateSeekTooltip);
|
3072
|
-
|
3073
|
-
// Toggle controls visibility based on mouse movement
|
3074
|
-
if (config.hideControls) {
|
3075
|
-
// Toggle controls on mouse events and entering fullscreen
|
3076
|
-
_on(plyr.container, 'mouseenter mouseleave mousemove touchstart touchend touchcancel touchmove enterfullscreen', _toggleControls);
|
3077
|
-
|
3078
|
-
// Watch for cursor over controls so they don't hide when trying to interact
|
3079
|
-
_on(plyr.controls, 'mouseenter mouseleave', function(event) {
|
3080
|
-
plyr.controls.hover = event.type === 'mouseenter';
|
3081
|
-
});
|
3082
|
-
|
3083
|
-
// Watch for cursor over controls so they don't hide when trying to interact
|
3084
|
-
_on(plyr.controls, 'mousedown mouseup touchstart touchend touchcancel', function(event) {
|
3085
|
-
plyr.controls.pressed = _inArray(['mousedown', 'touchstart'], event.type);
|
3086
|
-
});
|
3087
|
-
|
3088
|
-
// Focus in/out on controls
|
3089
|
-
_on(plyr.controls, 'focus blur', _toggleControls, true);
|
3090
|
-
}
|
3091
|
-
|
3092
|
-
// Adjust volume on scroll
|
3093
|
-
_on(plyr.volume.input, 'wheel', function(event) {
|
3094
|
-
event.preventDefault();
|
3095
|
-
|
3096
|
-
// Detect "natural" scroll - suppored on OS X Safari only
|
3097
|
-
// Other browsers on OS X will be inverted until support improves
|
3098
|
-
var inverted = event.webkitDirectionInvertedFromDevice,
|
3099
|
-
step = (config.volumeStep / 5);
|
3100
|
-
|
3101
|
-
// Scroll down (or up on natural) to decrease
|
3102
|
-
if (event.deltaY < 0 || event.deltaX > 0) {
|
3103
|
-
if (inverted) {
|
3104
|
-
_decreaseVolume(step);
|
3105
|
-
} else {
|
3106
|
-
_increaseVolume(step);
|
3107
|
-
}
|
3108
|
-
}
|
3109
|
-
|
3110
|
-
// Scroll up (or down on natural) to increase
|
3111
|
-
if (event.deltaY > 0 || event.deltaX < 0) {
|
3112
|
-
if (inverted) {
|
3113
|
-
_increaseVolume(step);
|
3114
|
-
} else {
|
3115
|
-
_decreaseVolume(step);
|
3116
|
-
}
|
3117
|
-
}
|
3118
|
-
});
|
3119
|
-
}
|
3120
|
-
|
3121
|
-
// Listen for media events
|
3122
|
-
function _mediaListeners() {
|
3123
|
-
// Time change on media
|
3124
|
-
_on(plyr.media, 'timeupdate seeking', _timeUpdate);
|
3125
|
-
|
3126
|
-
// Update manual captions
|
3127
|
-
_on(plyr.media, 'timeupdate', _seekManualCaptions);
|
3128
|
-
|
3129
|
-
// Display duration
|
3130
|
-
_on(plyr.media, 'durationchange loadedmetadata', _displayDuration);
|
3131
|
-
|
3132
|
-
// Handle the media finishing
|
3133
|
-
_on(plyr.media, 'ended', function() {
|
3134
|
-
// Show poster on end
|
3135
|
-
if (plyr.type === 'video' && config.showPosterOnEnd) {
|
3136
|
-
// Clear
|
3137
|
-
if (plyr.type === 'video') {
|
3138
|
-
_setCaption();
|
3139
|
-
}
|
3140
|
-
|
3141
|
-
// Restart
|
3142
|
-
_seek();
|
3143
|
-
|
3144
|
-
// Re-load media
|
3145
|
-
plyr.media.load();
|
3146
|
-
}
|
3147
|
-
});
|
3148
|
-
|
3149
|
-
// Check for buffer progress
|
3150
|
-
_on(plyr.media, 'progress playing', _updateProgress);
|
3151
|
-
|
3152
|
-
// Handle native mute
|
3153
|
-
_on(plyr.media, 'volumechange', _updateVolume);
|
3154
|
-
|
3155
|
-
// Handle native play/pause
|
3156
|
-
_on(plyr.media, 'play pause ended', _checkPlaying);
|
3157
|
-
|
3158
|
-
// Loading
|
3159
|
-
_on(plyr.media, 'waiting canplay seeked', _checkLoading);
|
3160
|
-
|
3161
|
-
// Click video
|
3162
|
-
if (config.clickToPlay && plyr.type !== 'audio') {
|
3163
|
-
// Re-fetch the wrapper
|
3164
|
-
var wrapper = _getElement('.' + config.classes.videoWrapper);
|
3165
|
-
|
3166
|
-
// Bail if there's no wrapper (this should never happen)
|
3167
|
-
if (!wrapper) {
|
3168
|
-
return;
|
3169
|
-
}
|
3170
|
-
|
3171
|
-
// Set cursor
|
3172
|
-
wrapper.style.cursor = "pointer";
|
3173
|
-
|
3174
|
-
// On click play, pause ore restart
|
3175
|
-
_on(wrapper, 'click', function() {
|
3176
|
-
// Touch devices will just show controls (if we're hiding controls)
|
3177
|
-
if (config.hideControls && plyr.browser.isTouch && !plyr.media.paused) {
|
3178
|
-
return;
|
3179
|
-
}
|
3180
|
-
|
3181
|
-
if (plyr.media.paused) {
|
3182
|
-
_play();
|
3183
|
-
} else if (plyr.media.ended) {
|
3184
|
-
_seek();
|
3185
|
-
_play();
|
3186
|
-
} else {
|
3187
|
-
_pause();
|
3188
|
-
}
|
3189
|
-
});
|
3190
|
-
}
|
3191
|
-
|
3192
|
-
// Disable right click
|
3193
|
-
if (config.disableContextMenu) {
|
3194
|
-
_on(plyr.media, 'contextmenu', function(event) { event.preventDefault(); });
|
3195
|
-
}
|
3196
|
-
|
3197
|
-
// Proxy events to container
|
3198
|
-
// Bubble up key events for Edge
|
3199
|
-
_on(plyr.media, config.events.concat(['keyup', 'keydown']).join(' '), function(event) {
|
3200
|
-
_triggerEvent(plyr.container, event.type, true);
|
3201
|
-
});
|
3202
|
-
}
|
3203
|
-
|
3204
|
-
// Cancel current network requests
|
3205
|
-
// See https://github.com/Selz/plyr/issues/174
|
3206
|
-
function _cancelRequests() {
|
3207
|
-
if (!_inArray(config.types.html5, plyr.type)) {
|
3208
|
-
return;
|
3209
|
-
}
|
3210
|
-
|
3211
|
-
// Remove child sources
|
3212
|
-
var sources = plyr.media.querySelectorAll('source');
|
3213
|
-
for (var i = 0; i < sources.length; i++) {
|
3214
|
-
_remove(sources[i]);
|
3215
|
-
}
|
3216
|
-
|
3217
|
-
// Set blank video src attribute
|
3218
|
-
// This is to prevent a MEDIA_ERR_SRC_NOT_SUPPORTED error
|
3219
|
-
// Info: http://stackoverflow.com/questions/32231579/how-to-properly-dispose-of-an-html5-video-and-close-socket-or-connection
|
3220
|
-
plyr.media.setAttribute('src', 'https://cdn.selz.com/plyr/blank.mp4');
|
3221
|
-
|
3222
|
-
// Load the new empty source
|
3223
|
-
// This will cancel existing requests
|
3224
|
-
// See https://github.com/Selz/plyr/issues/174
|
3225
|
-
plyr.media.load();
|
3226
|
-
|
3227
|
-
// Debugging
|
3228
|
-
_log('Cancelled network requests');
|
3229
|
-
}
|
3230
|
-
|
3231
|
-
// Destroy an instance
|
3232
|
-
// Event listeners are removed when elements are removed
|
3233
|
-
// http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory
|
3234
|
-
function _destroy(callback, restore) {
|
3235
|
-
// Bail if the element is not initialized
|
3236
|
-
if (!plyr.init) {
|
3237
|
-
return null;
|
3238
|
-
}
|
3239
|
-
|
3240
|
-
// Type specific stuff
|
3241
|
-
switch (plyr.type) {
|
3242
|
-
case 'youtube':
|
3243
|
-
// Clear timers
|
3244
|
-
window.clearInterval(timers.buffering);
|
3245
|
-
window.clearInterval(timers.playing);
|
3246
|
-
|
3247
|
-
// Destroy YouTube API
|
3248
|
-
plyr.embed.destroy();
|
3249
|
-
|
3250
|
-
// Clean up
|
3251
|
-
cleanUp();
|
3252
|
-
|
3253
|
-
break;
|
3254
|
-
|
3255
|
-
case 'vimeo':
|
3256
|
-
// Destroy Vimeo API
|
3257
|
-
// then clean up (wait, to prevent postmessage errors)
|
3258
|
-
plyr.embed.unload().then(cleanUp);
|
3259
|
-
|
3260
|
-
// Vimeo does not always return
|
3261
|
-
timers.cleanUp = window.setTimeout(cleanUp, 200);
|
3262
|
-
|
3263
|
-
break;
|
3264
|
-
|
3265
|
-
case 'video':
|
3266
|
-
case 'audio':
|
3267
|
-
// Restore native video controls
|
3268
|
-
_toggleNativeControls(true);
|
3269
|
-
|
3270
|
-
// Clean up
|
3271
|
-
cleanUp();
|
3272
|
-
|
3273
|
-
break;
|
3274
|
-
}
|
3275
|
-
|
3276
|
-
function cleanUp() {
|
3277
|
-
clearTimeout(timers.cleanUp);
|
3278
|
-
|
3279
|
-
// Default to restore original element
|
3280
|
-
if (!_is.boolean(restore)) {
|
3281
|
-
restore = true;
|
3282
|
-
}
|
3283
|
-
|
3284
|
-
// Callback
|
3285
|
-
if (_is.function(callback)) {
|
3286
|
-
callback.call(original);
|
3287
|
-
}
|
3288
|
-
|
3289
|
-
// Bail if we don't need to restore the original element
|
3290
|
-
if (!restore) {
|
3291
|
-
return;
|
3292
|
-
}
|
3293
|
-
|
3294
|
-
// Remove init flag
|
3295
|
-
plyr.init = false;
|
3296
|
-
|
3297
|
-
// Replace the container with the original element provided
|
3298
|
-
plyr.container.parentNode.replaceChild(original, plyr.container);
|
3299
|
-
|
3300
|
-
// Allow overflow (set on fullscreen)
|
3301
|
-
document.body.style.overflow = '';
|
3302
|
-
|
3303
|
-
// Event
|
3304
|
-
_triggerEvent(original, 'destroyed', true);
|
3305
|
-
}
|
3306
|
-
}
|
3307
|
-
|
3308
|
-
// Setup a player
|
3309
|
-
function _init() {
|
3310
|
-
// Bail if the element is initialized
|
3311
|
-
if (plyr.init) {
|
3312
|
-
return null;
|
3313
|
-
}
|
3314
|
-
|
3315
|
-
// Setup the fullscreen api
|
3316
|
-
fullscreen = _fullscreen();
|
3317
|
-
|
3318
|
-
// Sniff out the browser
|
3319
|
-
plyr.browser = _browserSniff();
|
3320
|
-
|
3321
|
-
// Bail if nothing to setup
|
3322
|
-
if (!_is.htmlElement(plyr.media)) {
|
3323
|
-
return;
|
3324
|
-
}
|
3325
|
-
|
3326
|
-
// Load saved settings from localStorage
|
3327
|
-
_setupStorage();
|
3328
|
-
|
3329
|
-
// Set media type based on tag or data attribute
|
3330
|
-
// Supported: video, audio, vimeo, youtube
|
3331
|
-
var tagName = media.tagName.toLowerCase();
|
3332
|
-
if (tagName === 'div') {
|
3333
|
-
plyr.type = media.getAttribute('data-type');
|
3334
|
-
plyr.embedId = media.getAttribute('data-video-id');
|
3335
|
-
|
3336
|
-
// Clean up
|
3337
|
-
media.removeAttribute('data-type');
|
3338
|
-
media.removeAttribute('data-video-id');
|
3339
|
-
} else {
|
3340
|
-
plyr.type = tagName;
|
3341
|
-
config.crossorigin = (media.getAttribute('crossorigin') !== null);
|
3342
|
-
config.autoplay = (config.autoplay || (media.getAttribute('autoplay') !== null));
|
3343
|
-
config.loop = (config.loop || (media.getAttribute('loop') !== null));
|
3344
|
-
}
|
3345
|
-
|
3346
|
-
// Check for support
|
3347
|
-
plyr.supported = supported(plyr.type);
|
3348
|
-
|
3349
|
-
// If no native support, bail
|
3350
|
-
if (!plyr.supported.basic) {
|
3351
|
-
return;
|
3352
|
-
}
|
3353
|
-
|
3354
|
-
// Wrap media
|
3355
|
-
plyr.container = _wrap(media, document.createElement('div'));
|
3356
|
-
|
3357
|
-
// Allow focus to be captured
|
3358
|
-
plyr.container.setAttribute('tabindex', 0);
|
3359
|
-
|
3360
|
-
// Add style hook
|
3361
|
-
_toggleStyleHook();
|
3362
|
-
|
3363
|
-
// Debug info
|
3364
|
-
_log('' + plyr.browser.name + ' ' + plyr.browser.version);
|
3365
|
-
|
3366
|
-
// Setup media
|
3367
|
-
_setupMedia();
|
3368
|
-
|
3369
|
-
// Setup interface
|
3370
|
-
// If embed but not fully supported, setupInterface (to avoid flash of controls) and call ready now
|
3371
|
-
if (_inArray(config.types.html5, plyr.type) || (_inArray(config.types.embed, plyr.type) && !plyr.supported.full)) {
|
3372
|
-
// Setup UI
|
3373
|
-
_setupInterface();
|
3374
|
-
|
3375
|
-
// Call ready
|
3376
|
-
_ready();
|
3377
|
-
|
3378
|
-
// Set title on button and frame
|
3379
|
-
_setTitle();
|
3380
|
-
}
|
3381
|
-
|
3382
|
-
// Successful setup
|
3383
|
-
plyr.init = true;
|
3384
|
-
}
|
3385
|
-
|
3386
|
-
// Setup the UI
|
3387
|
-
function _setupInterface() {
|
3388
|
-
// Don't setup interface if no support
|
3389
|
-
if (!plyr.supported.full) {
|
3390
|
-
_warn('Basic support only', plyr.type);
|
3391
|
-
|
3392
|
-
// Remove controls
|
3393
|
-
_remove(_getElement(config.selectors.controls.wrapper));
|
3394
|
-
|
3395
|
-
// Remove large play
|
3396
|
-
_remove(_getElement(config.selectors.buttons.play));
|
3397
|
-
|
3398
|
-
// Restore native controls
|
3399
|
-
_toggleNativeControls(true);
|
3400
|
-
|
3401
|
-
// Bail
|
3402
|
-
return;
|
3403
|
-
}
|
3404
|
-
|
3405
|
-
// Inject custom controls if not present
|
3406
|
-
var controlsMissing = !_getElements(config.selectors.controls.wrapper).length;
|
3407
|
-
if (controlsMissing) {
|
3408
|
-
// Inject custom controls
|
3409
|
-
_injectControls();
|
3410
|
-
}
|
3411
|
-
|
3412
|
-
// Find the elements
|
3413
|
-
if (!_findElements()) {
|
3414
|
-
return;
|
3415
|
-
}
|
3416
|
-
|
3417
|
-
// If the controls are injected, re-bind listeners for controls
|
3418
|
-
if (controlsMissing) {
|
3419
|
-
_controlListeners();
|
3420
|
-
}
|
3421
|
-
|
3422
|
-
// Media element listeners
|
3423
|
-
_mediaListeners();
|
3424
|
-
|
3425
|
-
// Remove native controls
|
3426
|
-
_toggleNativeControls();
|
3427
|
-
|
3428
|
-
// Setup fullscreen
|
3429
|
-
_setupFullscreen();
|
3430
|
-
|
3431
|
-
// Captions
|
3432
|
-
_setupCaptions();
|
3433
|
-
|
3434
|
-
// Set volume
|
3435
|
-
_setVolume();
|
3436
|
-
_updateVolume();
|
3437
|
-
|
3438
|
-
// Reset time display
|
3439
|
-
_timeUpdate();
|
3440
|
-
|
3441
|
-
// Update the UI
|
3442
|
-
_checkPlaying();
|
3443
|
-
}
|
3444
|
-
|
3445
|
-
api = {
|
3446
|
-
getOriginal: function() { return original; },
|
3447
|
-
getContainer: function() { return plyr.container },
|
3448
|
-
getEmbed: function() { return plyr.embed; },
|
3449
|
-
getMedia: function() { return plyr.media; },
|
3450
|
-
getType: function() { return plyr.type; },
|
3451
|
-
getDuration: _getDuration,
|
3452
|
-
getCurrentTime: function() { return plyr.media.currentTime; },
|
3453
|
-
getVolume: function() { return plyr.media.volume; },
|
3454
|
-
isMuted: function() { return plyr.media.muted; },
|
3455
|
-
isReady: function() { return _hasClass(plyr.container, config.classes.ready); },
|
3456
|
-
isLoading: function() { return _hasClass(plyr.container, config.classes.loading); },
|
3457
|
-
isPaused: function() { return plyr.media.paused; },
|
3458
|
-
on: function(event, callback) { _on(plyr.container, event, callback); return this; },
|
3459
|
-
play: _play,
|
3460
|
-
pause: _pause,
|
3461
|
-
stop: function() { _pause(); _seek(); },
|
3462
|
-
restart: _seek,
|
3463
|
-
rewind: _rewind,
|
3464
|
-
forward: _forward,
|
3465
|
-
seek: _seek,
|
3466
|
-
source: _source,
|
3467
|
-
poster: _updatePoster,
|
3468
|
-
setVolume: _setVolume,
|
3469
|
-
togglePlay: _togglePlay,
|
3470
|
-
toggleMute: _toggleMute,
|
3471
|
-
toggleCaptions: _toggleCaptions,
|
3472
|
-
toggleFullscreen: _toggleFullscreen,
|
3473
|
-
toggleControls: _toggleControls,
|
3474
|
-
isFullscreen: function() { return plyr.isFullscreen || false; },
|
3475
|
-
support: function(mimeType) { return _supportMime(plyr, mimeType); },
|
3476
|
-
destroy: _destroy
|
3477
|
-
};
|
3478
|
-
|
3479
|
-
// Everything done
|
3480
|
-
function _ready() {
|
3481
|
-
// Ready event at end of execution stack
|
3482
|
-
window.setTimeout(function() {
|
3483
|
-
_triggerEvent(plyr.media, 'ready');
|
3484
|
-
}, 0);
|
3485
|
-
|
3486
|
-
// Set class hook on media element
|
3487
|
-
_toggleClass(plyr.media, defaults.classes.setup, true);
|
3488
|
-
|
3489
|
-
// Set container class for ready
|
3490
|
-
_toggleClass(plyr.container, config.classes.ready, true);
|
3491
|
-
|
3492
|
-
// Store a refernce to instance
|
3493
|
-
plyr.media.plyr = api;
|
3494
|
-
|
3495
|
-
// Autoplay
|
3496
|
-
if (config.autoplay) {
|
3497
|
-
_play();
|
3498
|
-
}
|
3499
|
-
}
|
3500
|
-
|
3501
|
-
// Initialize instance
|
3502
|
-
_init();
|
3503
|
-
|
3504
|
-
// If init failed, return null
|
3505
|
-
if (!plyr.init) {
|
3506
|
-
return null;
|
3507
|
-
}
|
3508
|
-
|
3509
|
-
return api;
|
3510
|
-
}
|
3511
|
-
|
3512
|
-
// Load a sprite
|
3513
|
-
function loadSprite(url, id) {
|
3514
|
-
var x = new XMLHttpRequest();
|
3515
|
-
|
3516
|
-
// If the id is set and sprite exists, bail
|
3517
|
-
if (_is.string(id) && _is.htmlElement(document.querySelector('#' + id))) {
|
3518
|
-
return;
|
3519
|
-
}
|
3520
|
-
|
3521
|
-
// Create placeholder (to prevent loading twice)
|
3522
|
-
var container = document.createElement('div');
|
3523
|
-
container.setAttribute('hidden', '');
|
3524
|
-
if (_is.string(id)) {
|
3525
|
-
container.setAttribute('id', id);
|
3526
|
-
}
|
3527
|
-
document.body.insertBefore(container, document.body.childNodes[0]);
|
3528
|
-
|
3529
|
-
// Check for CORS support
|
3530
|
-
if ('withCredentials' in x) {
|
3531
|
-
x.open('GET', url, true);
|
3532
|
-
} else {
|
3533
|
-
return;
|
3534
|
-
}
|
3535
|
-
|
3536
|
-
// Inject hidden div with sprite on load
|
3537
|
-
x.onload = function() {
|
3538
|
-
container.innerHTML = x.responseText;
|
3539
|
-
}
|
3540
|
-
|
3541
|
-
x.send();
|
3542
|
-
}
|
3543
|
-
|
3544
|
-
// Check for support
|
3545
|
-
function supported(type) {
|
3546
|
-
var browser = _browserSniff(),
|
3547
|
-
isOldIE = (browser.isIE && browser.version <= 9),
|
3548
|
-
isIos = browser.isIos,
|
3549
|
-
isIphone = browser.isIphone,
|
3550
|
-
audioSupport = !!document.createElement('audio').canPlayType,
|
3551
|
-
videoSupport = !!document.createElement('video').canPlayType,
|
3552
|
-
basic = false,
|
3553
|
-
full = false;
|
3554
|
-
|
3555
|
-
switch (type) {
|
3556
|
-
case 'video':
|
3557
|
-
basic = videoSupport;
|
3558
|
-
full = (basic && (!isOldIE && !isIphone));
|
3559
|
-
break;
|
3560
|
-
|
3561
|
-
case 'audio':
|
3562
|
-
basic = audioSupport;
|
3563
|
-
full = (basic && !isOldIE);
|
3564
|
-
break;
|
3565
|
-
|
3566
|
-
// Vimeo does not seem to be supported on iOS via API
|
3567
|
-
// Issue raised https://github.com/vimeo/player.js/issues/87
|
3568
|
-
case 'vimeo':
|
3569
|
-
basic = true;
|
3570
|
-
full = (!isOldIE && !isIos);
|
3571
|
-
break;
|
3572
|
-
|
3573
|
-
case 'youtube':
|
3574
|
-
basic = true;
|
3575
|
-
full = (!isOldIE && !isIos);
|
3576
|
-
|
3577
|
-
// YouTube seems to work on iOS 10+ on iPad
|
3578
|
-
if (isIos && !isIphone && browser.version >= 10) {
|
3579
|
-
full = true;
|
3580
|
-
}
|
3581
|
-
|
3582
|
-
break;
|
3583
|
-
|
3584
|
-
case 'soundcloud':
|
3585
|
-
basic = true;
|
3586
|
-
full = (!isOldIE && !isIphone);
|
3587
|
-
break;
|
3588
|
-
|
3589
|
-
default:
|
3590
|
-
basic = (audioSupport && videoSupport);
|
3591
|
-
full = (basic && !isOldIE);
|
3592
|
-
}
|
3593
|
-
|
3594
|
-
return {
|
3595
|
-
basic: basic,
|
3596
|
-
full: full
|
3597
|
-
};
|
3598
|
-
}
|
3599
|
-
|
3600
|
-
// Setup function
|
3601
|
-
function setup(targets, options) {
|
3602
|
-
// Get the players
|
3603
|
-
var players = [],
|
3604
|
-
instances = [],
|
3605
|
-
selector = [defaults.selectors.html5, defaults.selectors.embed].join(',');
|
3606
|
-
|
3607
|
-
// Select the elements
|
3608
|
-
if (_is.string(targets)) {
|
3609
|
-
// String selector passed
|
3610
|
-
targets = document.querySelectorAll(targets);
|
3611
|
-
} else if (_is.htmlElement(targets)) {
|
3612
|
-
// Single HTMLElement passed
|
3613
|
-
targets = [targets];
|
3614
|
-
} else if (!_is.nodeList(targets) && !_is.array(targets) && !_is.string(targets)) {
|
3615
|
-
// No selector passed, possibly options as first argument
|
3616
|
-
// If options are the first argument
|
3617
|
-
if (_is.undefined(options) && _is.object(targets)) {
|
3618
|
-
options = targets;
|
3619
|
-
}
|
3620
|
-
|
3621
|
-
// Use default selector
|
3622
|
-
targets = document.querySelectorAll(selector);
|
3623
|
-
}
|
3624
|
-
|
3625
|
-
// Convert NodeList to array
|
3626
|
-
if (_is.nodeList(targets)) {
|
3627
|
-
targets = Array.prototype.slice.call(targets);
|
3628
|
-
}
|
3629
|
-
|
3630
|
-
// Bail if disabled or no basic support
|
3631
|
-
// You may want to disable certain UAs etc
|
3632
|
-
if (!supported().basic || !targets.length) {
|
3633
|
-
return false;
|
3634
|
-
}
|
3635
|
-
|
3636
|
-
// Add to container list
|
3637
|
-
function add(target, media) {
|
3638
|
-
if (!_hasClass(media, defaults.classes.hook)) {
|
3639
|
-
players.push({
|
3640
|
-
// Always wrap in a <div> for styling
|
3641
|
-
//container: _wrap(media, document.createElement('div')),
|
3642
|
-
// Could be a container or the media itself
|
3643
|
-
target: target,
|
3644
|
-
// This should be the <video>, <audio> or <div> (YouTube/Vimeo)
|
3645
|
-
media: media
|
3646
|
-
});
|
3647
|
-
}
|
3648
|
-
}
|
3649
|
-
|
3650
|
-
// Check if the targets have multiple media elements
|
3651
|
-
for (var i = 0; i < targets.length; i++) {
|
3652
|
-
var target = targets[i];
|
3653
|
-
|
3654
|
-
// Get children
|
3655
|
-
var children = target.querySelectorAll(selector);
|
3656
|
-
|
3657
|
-
// If there's more than one media element child, wrap them
|
3658
|
-
if (children.length) {
|
3659
|
-
for (var x = 0; x < children.length; x++) {
|
3660
|
-
add(target, children[x]);
|
3661
|
-
}
|
3662
|
-
} else if (_matches(target, selector)) {
|
3663
|
-
// Target is media element
|
3664
|
-
add(target, target);
|
3665
|
-
}
|
3666
|
-
}
|
3667
|
-
|
3668
|
-
// Create a player instance for each element
|
3669
|
-
players.forEach(function(player) {
|
3670
|
-
var element = player.target,
|
3671
|
-
media = player.media,
|
3672
|
-
match = false;
|
3673
|
-
|
3674
|
-
// The target element can also be the media element
|
3675
|
-
if (media === element) {
|
3676
|
-
match = true;
|
3677
|
-
}
|
3678
|
-
|
3679
|
-
// Setup a player instance and add to the element
|
3680
|
-
// Create instance-specific config
|
3681
|
-
var data = {};
|
3682
|
-
|
3683
|
-
// Try parsing data attribute config
|
3684
|
-
try { data = JSON.parse(element.getAttribute('data-plyr')); }
|
3685
|
-
catch(e) { }
|
3686
|
-
|
3687
|
-
var config = _extend({}, defaults, options, data);
|
3688
|
-
|
3689
|
-
// Bail if not enabled
|
3690
|
-
if (!config.enabled) {
|
3691
|
-
return null;
|
3692
|
-
}
|
3693
|
-
|
3694
|
-
// Create new instance
|
3695
|
-
var instance = new Plyr(media, config);
|
3696
|
-
|
3697
|
-
// Go to next if setup failed
|
3698
|
-
if (!_is.object(instance)) {
|
3699
|
-
return;
|
3700
|
-
}
|
3701
|
-
|
3702
|
-
// Listen for events if debugging
|
3703
|
-
if (config.debug) {
|
3704
|
-
var events = config.events.concat(['setup', 'statechange', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled']);
|
3705
|
-
|
3706
|
-
_on(instance.getContainer(), events.join(' '), function(event) {
|
3707
|
-
console.log([config.logPrefix, 'event:', event.type].join(' '), event.detail.plyr);
|
3708
|
-
});
|
3709
|
-
}
|
3710
|
-
|
3711
|
-
// Callback
|
3712
|
-
_event(instance.getContainer(), 'setup', true, {
|
3713
|
-
plyr: instance
|
3714
|
-
});
|
3715
|
-
|
3716
|
-
// Add to return array even if it's already setup
|
3717
|
-
instances.push(instance);
|
3718
|
-
});
|
3719
|
-
|
3720
|
-
return instances;
|
3721
|
-
}
|
3722
|
-
|
3723
|
-
// Get all instances within a provided container
|
3724
|
-
function get(container) {
|
3725
|
-
if (_is.string(container)) {
|
3726
|
-
// Get selector if string passed
|
3727
|
-
container = document.querySelector(container);
|
3728
|
-
} else if (_is.undefined(container)) {
|
3729
|
-
// Use body by default to get all on page
|
3730
|
-
container = document.body;
|
3731
|
-
}
|
3732
|
-
|
3733
|
-
// If we have a HTML element
|
3734
|
-
if (_is.htmlElement(container)) {
|
3735
|
-
var elements = container.querySelectorAll('.' + defaults.classes.setup),
|
3736
|
-
instances = [];
|
3737
|
-
|
3738
|
-
Array.prototype.slice.call(elements).forEach(function(element) {
|
3739
|
-
if (_is.object(element.plyr)) {
|
3740
|
-
instances.push(element.plyr);
|
3741
|
-
}
|
3742
|
-
});
|
3743
|
-
|
3744
|
-
return instances;
|
3745
|
-
}
|
3746
|
-
|
3747
|
-
return [];
|
3748
|
-
}
|
3749
|
-
|
3750
|
-
return {
|
3751
|
-
setup: setup,
|
3752
|
-
supported: supported,
|
3753
|
-
loadSprite: loadSprite,
|
3754
|
-
get: get
|
3755
|
-
};
|
3756
|
-
}));
|
3757
|
-
|
3758
|
-
// Custom event polyfill
|
3759
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
|
3760
|
-
(function () {
|
3761
|
-
if (typeof window.CustomEvent === 'function') {
|
3762
|
-
return;
|
3763
|
-
}
|
3764
|
-
|
3765
|
-
function CustomEvent(event, params) {
|
3766
|
-
params = params || { bubbles: false, cancelable: false, detail: undefined };
|
3767
|
-
var evt = document.createEvent('CustomEvent');
|
3768
|
-
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
|
3769
|
-
return evt;
|
3770
|
-
}
|
3771
|
-
|
3772
|
-
CustomEvent.prototype = window.Event.prototype;
|
3773
|
-
|
3774
|
-
window.CustomEvent = CustomEvent;
|
3775
|
-
})();
|
1
|
+
!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=t(e,document):"function"==typeof define&&define.amd?define([],function(){return t(e,document)}):e.plyr=t(e,document)}("undefined"!=typeof window?window:this,function(e,t){"use strict";function n(){var e,n,r,a=navigator.userAgent,s=navigator.appName,o=""+parseFloat(navigator.appVersion),i=parseInt(navigator.appVersion,10),l=!1,u=!1,c=!1,d=!1;return navigator.appVersion.indexOf("Windows NT")!==-1&&navigator.appVersion.indexOf("rv:11")!==-1?(l=!0,s="IE",o="11"):(n=a.indexOf("MSIE"))!==-1?(l=!0,s="IE",o=a.substring(n+5)):(n=a.indexOf("Chrome"))!==-1?(c=!0,s="Chrome",o=a.substring(n+7)):(n=a.indexOf("Safari"))!==-1?(d=!0,s="Safari",o=a.substring(n+7),(n=a.indexOf("Version"))!==-1&&(o=a.substring(n+8))):(n=a.indexOf("Firefox"))!==-1?(u=!0,s="Firefox",o=a.substring(n+8)):(e=a.lastIndexOf(" ")+1)<(n=a.lastIndexOf("/"))&&(s=a.substring(e,n),o=a.substring(n+1),s.toLowerCase()===s.toUpperCase()&&(s=navigator.appName)),(r=o.indexOf(";"))!==-1&&(o=o.substring(0,r)),(r=o.indexOf(" "))!==-1&&(o=o.substring(0,r)),i=parseInt(""+o,10),isNaN(i)&&(o=""+parseFloat(navigator.appVersion),i=parseInt(navigator.appVersion,10)),{name:s,version:i,isIE:l,isFirefox:u,isChrome:c,isSafari:d,isIos:/(iPad|iPhone|iPod)/g.test(navigator.platform),isIphone:/(iPhone|iPod)/g.test(navigator.userAgent),isTouch:"ontouchstart"in t.documentElement}}function r(e,t){var n=e.media;if("video"===e.type)switch(t){case"video/webm":return!(!n.canPlayType||!n.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/,""));case"video/mp4":return!(!n.canPlayType||!n.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/,""));case"video/ogg":return!(!n.canPlayType||!n.canPlayType('video/ogg; codecs="theora"').replace(/no/,""))}else if("audio"===e.type)switch(t){case"audio/mpeg":return!(!n.canPlayType||!n.canPlayType("audio/mpeg;").replace(/no/,""));case"audio/ogg":return!(!n.canPlayType||!n.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/,""));case"audio/wav":return!(!n.canPlayType||!n.canPlayType('audio/wav; codecs="1"').replace(/no/,""))}return!1}function a(e){if(!t.querySelectorAll('script[src="'+e+'"]').length){var n=t.createElement("script");n.src=e;var r=t.getElementsByTagName("script")[0];r.parentNode.insertBefore(n,r)}}function s(e,t){return Array.prototype.indexOf&&e.indexOf(t)!==-1}function o(e,t,n){return e.replace(new RegExp(t.replace(/([.*+?\^=!:${}()|\[\]\/\\])/g,"\\$1"),"g"),n)}function i(e,t){e.length||(e=[e]);for(var n=e.length-1;n>=0;n--){var r=n>0?t.cloneNode(!0):t,a=e[n],s=a.parentNode,o=a.nextSibling;return r.appendChild(a),o?s.insertBefore(r,o):s.appendChild(r),r}}function l(e){e&&e.parentNode.removeChild(e)}function u(e,t){e.insertBefore(t,e.firstChild)}function c(e,t){for(var n in t)e.setAttribute(n,O.boolean(t[n])&&t[n]?"":t[n])}function d(e,n,r){var a=t.createElement(e);c(a,r),u(n,a)}function p(e){return e.replace(".","")}function m(e,t,n){if(e)if(e.classList)e.classList[n?"add":"remove"](t);else{var r=(" "+e.className+" ").replace(/\s+/g," ").replace(" "+t+" ","");e.className=r+(n?" "+t:"")}}function f(e,t){return!!e&&(e.classList?e.classList.contains(t):new RegExp("(\\s|^)"+t+"(\\s|$)").test(e.className))}function y(e,n){var r=Element.prototype,a=r.matches||r.webkitMatchesSelector||r.mozMatchesSelector||r.msMatchesSelector||function(e){return[].indexOf.call(t.querySelectorAll(e),this)!==-1};return a.call(e,n)}function b(e,t,n,r,a){g(e,t,function(t){n&&n.apply(e,[t]),r.apply(e,[t])},a)}function v(e,t,n,r,a){var s=t.split(" ");if(O.boolean(a)||(a=!1),e instanceof NodeList)for(var o=0;o<e.length;o++)e[o]instanceof Node&&v(e[o],arguments[1],arguments[2],arguments[3]);else for(var i=0;i<s.length;i++)e[r?"addEventListener":"removeEventListener"](s[i],n,a)}function g(e,t,n,r){e&&v(e,t,n,!0,r)}function h(e,t,n,r){if(e&&t){O.boolean(n)||(n=!1);var a=new CustomEvent(t,{bubbles:n,detail:r});e.dispatchEvent(a)}}function k(e,t){if(e)return t=O.boolean(t)?t:!e.getAttribute("aria-pressed"),e.setAttribute("aria-pressed",t),t}function w(e,t){return 0===e||0===t||isNaN(e)||isNaN(t)?0:(e/t*100).toFixed(2)}function x(){var e=arguments;if(e.length){if(1===e.length)return e[0];for(var t=Array.prototype.shift.call(e),n=e.length,r=0;r<n;r++){var a=e[r];for(var s in a)a[s]&&a[s].constructor&&a[s].constructor===Object?(t[s]=t[s]||{},x(t[s],a[s])):t[s]=a[s]}return t}}function T(e){var t=/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/;return e.match(t)?RegExp.$2:e}function S(e){var t=/^.*(vimeo.com\/|video\/)(\d+).*/;return e.match(t)?RegExp.$2:e}function _(){var e={supportsFullScreen:!1,isFullScreen:function(){return!1},requestFullScreen:function(){},cancelFullScreen:function(){},fullScreenEventName:"",element:null,prefix:""},n="webkit o moz ms khtml".split(" ");if(O.undefined(t.cancelFullScreen))for(var r=0,a=n.length;r<a;r++){if(e.prefix=n[r],!O.undefined(t[e.prefix+"CancelFullScreen"])){e.supportsFullScreen=!0;break}if(!O.undefined(t.msExitFullscreen)&&t.msFullscreenEnabled){e.prefix="ms",e.supportsFullScreen=!0;break}}else e.supportsFullScreen=!0;return e.supportsFullScreen&&(e.fullScreenEventName="ms"===e.prefix?"MSFullscreenChange":e.prefix+"fullscreenchange",e.isFullScreen=function(e){switch(O.undefined(e)&&(e=t.body),this.prefix){case"":return t.fullscreenElement===e;case"moz":return t.mozFullScreenElement===e;default:return t[this.prefix+"FullscreenElement"]===e}},e.requestFullScreen=function(e){return O.undefined(e)&&(e=t.body),""===this.prefix?e.requestFullScreen():e[this.prefix+("ms"===this.prefix?"RequestFullscreen":"RequestFullScreen")]()},e.cancelFullScreen=function(){return""===this.prefix?t.cancelFullScreen():t[this.prefix+("ms"===this.prefix?"ExitFullscreen":"CancelFullScreen")]()},e.element=function(){return""===this.prefix?t.fullscreenElement:t[this.prefix+"FullscreenElement"]}),e}function E(v,E){function A(e,t,n,r){h(e,t,n,x({},r,{plyr:Be}))}function j(t,n){E.debug&&e.console&&(n=Array.prototype.slice.call(n),O.string(E.logPrefix)&&E.logPrefix.length&&n.unshift(E.logPrefix),console[t].apply(console,n))}function V(){return{url:E.iconUrl,absolute:0===E.iconUrl.indexOf("http")||Ue.browser.isIE}}function R(){var e=[],t=V(),n=(t.absolute?"":t.url)+"#"+E.iconPrefix;return s(E.controls,"play-large")&&e.push('<button type="button" data-plyr="play" class="plyr__play-large">','<svg><use xlink:href="'+n+'-play" /></svg>','<span class="plyr__sr-only">'+E.i18n.play+"</span>","</button>"),e.push('<div class="plyr__controls">'),s(E.controls,"restart")&&e.push('<button type="button" data-plyr="restart">','<svg><use xlink:href="'+n+'-restart" /></svg>','<span class="plyr__sr-only">'+E.i18n.restart+"</span>","</button>"),s(E.controls,"rewind")&&e.push('<button type="button" data-plyr="rewind">','<svg><use xlink:href="'+n+'-rewind" /></svg>','<span class="plyr__sr-only">'+E.i18n.rewind+"</span>","</button>"),s(E.controls,"play")&&e.push('<button type="button" data-plyr="play">','<svg><use xlink:href="'+n+'-play" /></svg>','<span class="plyr__sr-only">'+E.i18n.play+"</span>","</button>",'<button type="button" data-plyr="pause">','<svg><use xlink:href="'+n+'-pause" /></svg>','<span class="plyr__sr-only">'+E.i18n.pause+"</span>","</button>"),s(E.controls,"fast-forward")&&e.push('<button type="button" data-plyr="fast-forward">','<svg><use xlink:href="'+n+'-fast-forward" /></svg>','<span class="plyr__sr-only">'+E.i18n.forward+"</span>","</button>"),s(E.controls,"progress")&&(e.push('<span class="plyr__progress">','<label for="seek{id}" class="plyr__sr-only">Seek</label>','<input id="seek{id}" class="plyr__progress--seek" type="range" min="0" max="100" step="0.1" value="0" data-plyr="seek">','<progress class="plyr__progress--played" max="100" value="0" role="presentation"></progress>','<progress class="plyr__progress--buffer" max="100" value="0">',"<span>0</span>% "+E.i18n.buffered,"</progress>"),E.tooltips.seek&&e.push('<span class="plyr__tooltip">00:00</span>'),e.push("</span>")),s(E.controls,"current-time")&&e.push('<span class="plyr__time">','<span class="plyr__sr-only">'+E.i18n.currentTime+"</span>",'<span class="plyr__time--current">00:00</span>',"</span>"),s(E.controls,"duration")&&e.push('<span class="plyr__time">','<span class="plyr__sr-only">'+E.i18n.duration+"</span>",'<span class="plyr__time--duration">00:00</span>',"</span>"),s(E.controls,"mute")&&e.push('<button type="button" data-plyr="mute">','<svg class="icon--muted"><use xlink:href="'+n+'-muted" /></svg>','<svg><use xlink:href="'+n+'-volume" /></svg>','<span class="plyr__sr-only">'+E.i18n.toggleMute+"</span>","</button>"),s(E.controls,"volume")&&e.push('<span class="plyr__volume">','<label for="volume{id}" class="plyr__sr-only">'+E.i18n.volume+"</label>",'<input id="volume{id}" class="plyr__volume--input" type="range" min="'+E.volumeMin+'" max="'+E.volumeMax+'" value="'+E.volume+'" data-plyr="volume">','<progress class="plyr__volume--display" max="'+E.volumeMax+'" value="'+E.volumeMin+'" role="presentation"></progress>',"</span>"),s(E.controls,"captions")&&e.push('<button type="button" data-plyr="captions">','<svg class="icon--captions-on"><use xlink:href="'+n+'-captions-on" /></svg>','<svg><use xlink:href="'+n+'-captions-off" /></svg>','<span class="plyr__sr-only">'+E.i18n.toggleCaptions+"</span>","</button>"),s(E.controls,"fullscreen")&&e.push('<button type="button" data-plyr="fullscreen">','<svg class="icon--exit-fullscreen"><use xlink:href="'+n+'-exit-fullscreen" /></svg>','<svg><use xlink:href="'+n+'-enter-fullscreen" /></svg>','<span class="plyr__sr-only">'+E.i18n.toggleFullscreen+"</span>","</button>"),e.push("</div>"),e.join("")}function q(){if(Ue.supported.full&&("audio"!==Ue.type||E.fullscreen.allowAudio)&&E.fullscreen.enabled){var e=N.supportsFullScreen;e||E.fullscreen.fallback&&!X()?(Je((e?"Native":"Fallback")+" fullscreen enabled"),m(Ue.container,E.classes.fullscreen.enabled,!0)):Je("Fullscreen not supported and fallback disabled"),Ue.buttons&&Ue.buttons.fullscreen&&k(Ue.buttons.fullscreen,!1),$()}}function D(){if("video"===Ue.type){U(E.selectors.captions)||Ue.videoContainer.insertAdjacentHTML("afterbegin",'<div class="'+p(E.selectors.captions)+'"></div>'),Ue.usingTextTracks=!1,Ue.media.textTracks&&(Ue.usingTextTracks=!0);for(var e,t="",n=Ue.media.childNodes,r=0;r<n.length;r++)"track"===n[r].nodeName.toLowerCase()&&(e=n[r].kind,"captions"!==e&&"subtitles"!==e||(t=n[r].getAttribute("src")));if(Ue.captionExists=!0,""===t?(Ue.captionExists=!1,Je("No caption track found")):Je("Caption track found; URI: "+t),Ue.captionExists){for(var a=Ue.media.textTracks,s=0;s<a.length;s++)a[s].mode="hidden";if(Y(Ue),(Ue.browser.isIE&&Ue.browser.version>=10||Ue.browser.isFirefox&&Ue.browser.version>=31)&&(Je("Detected browser with known TextTrack issues - using manual fallback"),Ue.usingTextTracks=!1),Ue.usingTextTracks){Je("TextTracks supported");for(var o=0;o<a.length;o++){var i=a[o];"captions"!==i.kind&&"subtitles"!==i.kind||g(i,"cuechange",function(){this.activeCues[0]&&"text"in this.activeCues[0]?H(this.activeCues[0].getCueAsHTML()):H()})}}else if(Je("TextTracks not supported so rendering captions manually"),Ue.currentCaption="",Ue.captions=[],""!==t){var l=new XMLHttpRequest;l.onreadystatechange=function(){if(4===l.readyState)if(200===l.status){var e,t=[],n=l.responseText,r="\r\n";n.indexOf(r+r)===-1&&(r=n.indexOf("\r\r")!==-1?"\r":"\n"),t=n.split(r+r);for(var a=0;a<t.length;a++){e=t[a],Ue.captions[a]=[];var s=e.split(r),o=0;s[o].indexOf(":")===-1&&(o=1),Ue.captions[a]=[s[o],s[o+1]]}Ue.captions.shift(),Je("Successfully loaded the caption file via AJAX")}else ze(E.logPrefix+"There was a problem loading the caption file via AJAX")},l.open("get",t,!0),l.send()}}else m(Ue.container,E.classes.captions.enabled)}}function H(e){var n=U(E.selectors.captions),r=t.createElement("span");n.innerHTML="",O.undefined(e)&&(e=""),O.string(e)?r.innerHTML=e.trim():r.appendChild(e),n.appendChild(r);n.offsetHeight}function W(e){function t(e,t){var n=[];n=e.split(" --> ");for(var r=0;r<n.length;r++)n[r]=n[r].replace(/(\d+:\d+:\d+\.\d+).*/,"$1");return a(n[t])}function n(e){return t(e,0)}function r(e){return t(e,1)}function a(e){if(null===e||void 0===e)return 0;var t,n=[],r=[];return n=e.split(","),r=n[0].split(":"),t=Math.floor(60*r[0]*60)+Math.floor(60*r[1])+Math.floor(r[2])}if(!Ue.usingTextTracks&&"video"===Ue.type&&Ue.supported.full&&(Ue.subcount=0,e=O.number(e)?e:Ue.media.currentTime,Ue.captions[Ue.subcount])){for(;r(Ue.captions[Ue.subcount][0])<e.toFixed(1);)if(Ue.subcount++,Ue.subcount>Ue.captions.length-1){Ue.subcount=Ue.captions.length-1;break}Ue.media.currentTime.toFixed(1)>=n(Ue.captions[Ue.subcount][0])&&Ue.media.currentTime.toFixed(1)<=r(Ue.captions[Ue.subcount][0])?(Ue.currentCaption=Ue.captions[Ue.subcount][1],H(Ue.currentCaption)):H()}}function Y(){if(Ue.buttons.captions){m(Ue.container,E.classes.captions.enabled,!0);var e=Ue.storage.captionsEnabled;O.boolean(e)||(e=E.captions.defaultActive),e&&(m(Ue.container,E.classes.captions.active,!0),k(Ue.buttons.captions,!0))}}function B(e){return Ue.container.querySelectorAll(e)}function U(e){return B(e)[0]}function X(){try{return e.self!==e.top}catch(e){return!0}}function $(){function e(e){9===e.which&&Ue.isFullscreen&&(e.target!==r||e.shiftKey?e.target===n&&e.shiftKey&&(e.preventDefault(),r.focus()):(e.preventDefault(),n.focus()))}var t=B("input:not([disabled]), button:not([disabled])"),n=t[0],r=t[t.length-1];g(Ue.container,"keydown",e)}function J(e,t){if(O.string(t))d(e,Ue.media,{src:t});else if(t.constructor===Array)for(var n=t.length-1;n>=0;n--)d(e,Ue.media,t[n])}function z(){if(E.loadSprite){var e=V();e.absolute?(Je("AJAX loading absolute SVG sprite"+(Ue.browser.isIE?" (due to IE)":"")),C(e.url,"sprite-plyr")):Je("Sprite will be used as external resource directly")}var n=E.html;Je("Injecting custom controls"),n||(n=R()),n=o(n,"{seektime}",E.seekTime),n=o(n,"{id}",Math.floor(1e4*Math.random()));var r;if(O.string(E.selectors.controls.container)&&(r=t.querySelector(E.selectors.controls.container)),O.htmlElement(r)||(r=Ue.container),r.insertAdjacentHTML("beforeend",n),E.tooltips.controls)for(var a=B([E.selectors.controls.wrapper," ",E.selectors.labels," .",E.classes.hidden].join("")),s=a.length-1;s>=0;s--){var i=a[s];m(i,E.classes.hidden,!1),m(i,E.classes.tooltip,!0)}}function G(){try{return Ue.controls=U(E.selectors.controls.wrapper),Ue.buttons={},Ue.buttons.seek=U(E.selectors.buttons.seek),Ue.buttons.play=B(E.selectors.buttons.play),Ue.buttons.pause=U(E.selectors.buttons.pause),Ue.buttons.restart=U(E.selectors.buttons.restart),Ue.buttons.rewind=U(E.selectors.buttons.rewind),Ue.buttons.forward=U(E.selectors.buttons.forward),Ue.buttons.fullscreen=U(E.selectors.buttons.fullscreen),Ue.buttons.mute=U(E.selectors.buttons.mute),Ue.buttons.captions=U(E.selectors.buttons.captions),Ue.progress={},Ue.progress.container=U(E.selectors.progress.container),Ue.progress.buffer={},Ue.progress.buffer.bar=U(E.selectors.progress.buffer),Ue.progress.buffer.text=Ue.progress.buffer.bar&&Ue.progress.buffer.bar.getElementsByTagName("span")[0],Ue.progress.played=U(E.selectors.progress.played),Ue.progress.tooltip=Ue.progress.container&&Ue.progress.container.querySelector("."+E.classes.tooltip),Ue.volume={},Ue.volume.input=U(E.selectors.volume.input),Ue.volume.display=U(E.selectors.volume.display),Ue.duration=U(E.selectors.duration),Ue.currentTime=U(E.selectors.currentTime),Ue.seekTime=B(E.selectors.seekTime),!0}catch(e){return ze("It looks like there is a problem with your controls HTML"),Q(!0),!1}}function K(){m(Ue.container,E.selectors.container.replace(".",""),Ue.supported.full)}function Q(e){e&&s(E.types.html5,Ue.type)?Ue.media.setAttribute("controls",""):Ue.media.removeAttribute("controls")}function Z(e){var t=E.i18n.play;if(O.string(E.title)&&E.title.length&&(t+=", "+E.title,Ue.container.setAttribute("aria-label",E.title)),Ue.supported.full&&Ue.buttons.play)for(var n=Ue.buttons.play.length-1;n>=0;n--)Ue.buttons.play[n].setAttribute("aria-label",t);O.htmlElement(e)&&e.setAttribute("title",E.i18n.frameTitle.replace("{title}",E.title))}function ee(){var t=null;Ue.storage={},L.supported&&E.storage.enabled&&(e.localStorage.removeItem("plyr-volume"),t=e.localStorage.getItem(E.storage.key),t&&(/^\d+(\.\d+)?$/.test(t)?te({volume:parseFloat(t)}):Ue.storage=JSON.parse(t)))}function te(t){L.supported&&E.storage.enabled&&(x(Ue.storage,t),e.localStorage.setItem(E.storage.key,JSON.stringify(Ue.storage)))}function ne(){if(!Ue.media)return void ze("No media element found!");if(Ue.supported.full&&(m(Ue.container,E.classes.type.replace("{0}",Ue.type),!0),s(E.types.embed,Ue.type)&&m(Ue.container,E.classes.type.replace("{0}","video"),!0),m(Ue.container,E.classes.stopped,E.autoplay),m(Ue.ontainer,E.classes.isIos,Ue.browser.isIos),m(Ue.container,E.classes.isTouch,Ue.browser.isTouch),"video"===Ue.type)){var e=t.createElement("div");e.setAttribute("class",E.classes.videoWrapper),i(Ue.media,e),Ue.videoContainer=e}s(E.types.embed,Ue.type)&&re()}function re(){var n,r=t.createElement("div"),s=Ue.type+"-"+Math.floor(1e4*Math.random());switch(Ue.type){case"youtube":n=T(Ue.embedId);break;case"vimeo":n=S(Ue.embedId);break;default:n=Ue.embedId}for(var o=B('[id^="'+Ue.type+'-"]'),i=o.length-1;i>=0;i--)l(o[i]);if(m(Ue.media,E.classes.videoWrapper,!0),m(Ue.media,E.classes.embedWrapper,!0),"youtube"===Ue.type)Ue.media.appendChild(r),r.setAttribute("id",s),O.object(e.YT)?se(n,r):(a(E.urls.youtube.api),e.onYouTubeReadyCallbacks=e.onYouTubeReadyCallbacks||[],e.onYouTubeReadyCallbacks.push(function(){se(n,r)}),e.onYouTubeIframeAPIReady=function(){e.onYouTubeReadyCallbacks.forEach(function(e){e()})});else if("vimeo"===Ue.type)if(Ue.supported.full?Ue.media.appendChild(r):r=Ue.media,r.setAttribute("id",s),O.object(e.Vimeo))oe(n,r);else{a(E.urls.vimeo.api);var u=e.setInterval(function(){O.object(e.Vimeo)&&(e.clearInterval(u),oe(n,r))},50)}else if("soundcloud"===Ue.type){var d=t.createElement("iframe");d.loaded=!1,g(d,"load",function(){d.loaded=!0}),c(d,{src:"https://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/"+n,id:s}),r.appendChild(d),Ue.media.appendChild(r),e.SC||a(E.urls.soundcloud.api);var p=e.setInterval(function(){e.SC&&d.loaded&&(e.clearInterval(p),ie.call(d))},50)}}function ae(){Ue.supported.full&&(We(),Ye()),Z(U("iframe"))}function se(t,n){Ue.embed=new e.YT.Player(n.id,{videoId:t,playerVars:{autoplay:E.autoplay?1:0,controls:Ue.supported.full?0:1,rel:0,showinfo:0,iv_load_policy:3,cc_load_policy:E.captions.defaultActive?1:0,cc_lang_pref:"en",wmode:"transparent",modestbranding:1,disablekb:1,origin:"*"},events:{onError:function(e){A(Ue.container,"error",!0,{code:e.data,embed:e.target})},onReady:function(t){var n=t.target;Ue.media.play=function(){n.playVideo(),Ue.media.paused=!1},Ue.media.pause=function(){n.pauseVideo(),Ue.media.paused=!0},Ue.media.stop=function(){n.stopVideo(),Ue.media.paused=!0},Ue.media.duration=n.getDuration(),Ue.media.paused=!0,Ue.media.currentTime=0,Ue.media.muted=n.isMuted(),E.title=n.getVideoData().title,Ue.supported.full&&Ue.media.querySelector("iframe").setAttribute("tabindex","-1"),ae(),A(Ue.media,"timeupdate"),A(Ue.media,"durationchange"),e.clearInterval(Xe.buffering),Xe.buffering=e.setInterval(function(){Ue.media.buffered=n.getVideoLoadedFraction(),(null===Ue.media.lastBuffered||Ue.media.lastBuffered<Ue.media.buffered)&&A(Ue.media,"progress"),Ue.media.lastBuffered=Ue.media.buffered,1===Ue.media.buffered&&(e.clearInterval(Xe.buffering),A(Ue.media,"canplaythrough"))},200)},onStateChange:function(t){var n=t.target;switch(e.clearInterval(Xe.playing),t.data){case 0:Ue.media.paused=!0,A(Ue.media,"ended");break;case 1:Ue.media.paused=!1,Ue.media.seeking&&A(Ue.media,"seeked"),Ue.media.seeking=!1,A(Ue.media,"play"),A(Ue.media,"playing"),Xe.playing=e.setInterval(function(){Ue.media.currentTime=n.getCurrentTime(),A(Ue.media,"timeupdate")},100),Ue.media.duration!==n.getDuration()&&(Ue.media.duration=n.getDuration(),A(Ue.media,"durationchange"));break;case 2:Ue.media.paused=!0,A(Ue.media,"pause")}A(Ue.container,"statechange",!1,{code:t.data})}}})}function oe(t,n){Ue.embed=new e.Vimeo.Player(n,{id:parseInt(t),loop:E.loop,autoplay:E.autoplay,byline:!1,portrait:!1,title:!1}),Ue.media.play=function(){Ue.embed.play(),Ue.media.paused=!1},Ue.media.pause=function(){Ue.embed.pause(),Ue.media.paused=!0},Ue.media.stop=function(){Ue.embed.stop(),Ue.media.paused=!0},Ue.media.paused=!0,Ue.media.currentTime=0,ae(),Ue.embed.getCurrentTime().then(function(e){Ue.media.currentTime=e,A(Ue.media,"timeupdate")}),Ue.embed.getDuration().then(function(e){Ue.media.duration=e,A(Ue.media,"durationchange")}),Ue.embed.on("loaded",function(){O.htmlElement(Ue.embed.element)&&Ue.supported.full&&Ue.embed.element.setAttribute("tabindex","-1")}),Ue.embed.on("play",function(){Ue.media.paused=!1,A(Ue.media,"play"),A(Ue.media,"playing")}),Ue.embed.on("pause",function(){Ue.media.paused=!0,A(Ue.media,"pause")}),Ue.embed.on("timeupdate",function(e){Ue.media.seeking=!1,Ue.media.currentTime=e.seconds,A(Ue.media,"timeupdate")}),Ue.embed.on("progress",function(e){Ue.media.buffered=e.percent,A(Ue.media,"progress"),1===parseInt(e.percent)&&A(Ue.media,"canplaythrough")}),Ue.embed.on("seeked",function(){Ue.media.seeking=!1,A(Ue.media,"seeked"),A(Ue.media,"play")}),Ue.embed.on("ended",function(){Ue.media.paused=!0,A(Ue.media,"ended")})}function ie(){Ue.embed=e.SC.Widget(this),Ue.embed.bind(e.SC.Widget.Events.READY,function(){Ue.media.play=function(){Ue.embed.play(),Ue.media.paused=!1},Ue.media.pause=function(){Ue.embed.pause(),Ue.media.paused=!0},Ue.media.stop=function(){Ue.embed.seekTo(0),Ue.embed.pause(),Ue.media.paused=!0},Ue.media.paused=!0,Ue.media.currentTime=0,Ue.embed.getDuration(function(e){Ue.media.duration=e/1e3,ae()}),Ue.embed.getPosition(function(e){Ue.media.currentTime=e,A(Ue.media,"timeupdate")}),Ue.embed.bind(e.SC.Widget.Events.PLAY,function(){Ue.media.paused=!1,A(Ue.media,"play"),A(Ue.media,"playing")}),Ue.embed.bind(e.SC.Widget.Events.PAUSE,function(){Ue.media.paused=!0,A(Ue.media,"pause")}),Ue.embed.bind(e.SC.Widget.Events.PLAY_PROGRESS,function(e){Ue.media.seeking=!1,Ue.media.currentTime=e.currentPosition/1e3,A(Ue.media,"timeupdate")}),Ue.embed.bind(e.SC.Widget.Events.LOAD_PROGRESS,function(e){Ue.media.buffered=e.loadProgress,A(Ue.media,"progress"),1===parseInt(e.loadProgress)&&A(Ue.media,"canplaythrough")}),Ue.embed.bind(e.SC.Widget.Events.FINISH,function(){Ue.media.paused=!0,A(Ue.media,"ended")})})}function le(){"play"in Ue.media&&Ue.media.play()}function ue(){"pause"in Ue.media&&Ue.media.pause()}function ce(e){return O.boolean(e)||(e=Ue.media.paused),e?le():ue(),e}function de(e){O.number(e)||(e=E.seekTime),me(Ue.media.currentTime-e)}function pe(e){O.number(e)||(e=E.seekTime),me(Ue.media.currentTime+e)}function me(e){var t=0,n=Ue.media.paused,r=fe();O.number(e)?t=e:O.object(e)&&s(["input","change"],e.type)&&(t=e.target.value/e.target.max*r),t<0?t=0:t>r&&(t=r),Ne(t);try{Ue.media.currentTime=t.toFixed(4)}catch(e){}if(s(E.types.embed,Ue.type)){switch(Ue.type){case"youtube":Ue.embed.seekTo(t);break;case"vimeo":Ue.embed.setCurrentTime(t.toFixed(0));break;case"soundcloud":Ue.embed.seekTo(1e3*t)}n&&ue(),A(Ue.media,"timeupdate"),Ue.media.seeking=!0,A(Ue.media,"seeking")}Je("Seeking to "+Ue.media.currentTime+" seconds"),W(t)}function fe(){var e=parseInt(E.duration),t=0;return null===Ue.media.duration||isNaN(Ue.media.duration)||(t=Ue.media.duration),isNaN(e)?t:e}function ye(){m(Ue.container,E.classes.playing,!Ue.media.paused),m(Ue.container,E.classes.stopped,Ue.media.paused),Me(Ue.media.paused)}function be(){P={x:e.pageXOffset||0,y:e.pageYOffset||0}}function ve(){e.scrollTo(P.x,P.y)}function ge(e){var n=N.supportsFullScreen;if(n){if(!e||e.type!==N.fullScreenEventName)return N.isFullScreen(Ue.container)?N.cancelFullScreen():(be(),N.requestFullScreen(Ue.container)),void(Ue.isFullscreen=N.isFullScreen(Ue.container));Ue.isFullscreen=N.isFullScreen(Ue.container)}else Ue.isFullscreen=!Ue.isFullscreen,t.body.style.overflow=Ue.isFullscreen?"hidden":"";m(Ue.container,E.classes.fullscreen.active,Ue.isFullscreen),$(Ue.isFullscreen),Ue.buttons&&Ue.buttons.fullscreen&&k(Ue.buttons.fullscreen,Ue.isFullscreen),A(Ue.container,Ue.isFullscreen?"enterfullscreen":"exitfullscreen",!0),!Ue.isFullscreen&&n&&ve()}function he(e){if(O.boolean(e)||(e=!Ue.media.muted),k(Ue.buttons.mute,e),Ue.media.muted=e,0===Ue.media.volume&&ke(E.volume),s(E.types.embed,Ue.type)){switch(Ue.type){case"youtube":Ue.embed[Ue.media.muted?"mute":"unMute"]();break;case"vimeo":case"soundcloud":Ue.embed.setVolume(Ue.media.muted?0:parseFloat(E.volume/E.volumeMax))}A(Ue.media,"volumechange")}}function ke(e){var t=E.volumeMax,n=E.volumeMin;if(O.undefined(e)&&(e=Ue.storage.volume),(null===e||isNaN(e))&&(e=E.volume),e>t&&(e=t),e<n&&(e=n),Ue.media.volume=parseFloat(e/t),Ue.volume.display&&(Ue.volume.display.value=e),s(E.types.embed,Ue.type)){switch(Ue.type){case"youtube":Ue.embed.setVolume(100*Ue.media.volume);break;case"vimeo":case"soundcloud":Ue.embed.setVolume(Ue.media.volume)}A(Ue.media,"volumechange")}0===e?Ue.media.muted=!0:Ue.media.muted&&e>0&&he()}function we(e){var t=Ue.media.muted?0:Ue.media.volume*E.volumeMax;O.number(e)||(e=E.volumeStep),ke(t+e)}function xe(e){var t=Ue.media.muted?0:Ue.media.volume*E.volumeMax;O.number(e)||(e=E.volumeStep),ke(t-e)}function Te(){var e=Ue.media.muted?0:Ue.media.volume*E.volumeMax;Ue.supported.full&&(Ue.volume.input&&(Ue.volume.input.value=e),Ue.volume.display&&(Ue.volume.display.value=e)),te({volume:e}),m(Ue.container,E.classes.muted,0===e),Ue.supported.full&&Ue.buttons.mute&&k(Ue.buttons.mute,0===e)}function Se(e){Ue.supported.full&&Ue.buttons.captions&&(O.boolean(e)||(e=Ue.container.className.indexOf(E.classes.captions.active)===-1),Ue.captionsEnabled=e,k(Ue.buttons.captions,Ue.captionsEnabled),m(Ue.container,E.classes.captions.active,Ue.captionsEnabled),A(Ue.container,Ue.captionsEnabled?"captionsenabled":"captionsdisabled",!0),te({captionsEnabled:Ue.captionsEnabled}))}function _e(e){var t="waiting"===e.type;clearTimeout(Xe.loading),Xe.loading=setTimeout(function(){m(Ue.container,E.classes.loading,t),Me(t)},t?250:0)}function Ee(e){if(Ue.supported.full){var t=Ue.progress.played,n=0,r=fe();if(e)switch(e.type){case"timeupdate":case"seeking":if(Ue.controls.pressed)return;n=w(Ue.media.currentTime,r),"timeupdate"===e.type&&Ue.buttons.seek&&(Ue.buttons.seek.value=n);break;case"playing":case"progress":t=Ue.progress.buffer,n=function(){var e=Ue.media.buffered;return e&&e.length?w(e.end(0),r):O.number(e)?100*e:0}()}Ce(t,n)}}function Ce(e,t){if(Ue.supported.full){if(O.undefined(t)&&(t=0),O.undefined(e)){if(!Ue.progress||!Ue.progress.buffer)return;e=Ue.progress.buffer}O.htmlElement(e)?e.value=t:e&&(e.bar&&(e.bar.value=t),e.text&&(e.text.innerHTML=t))}}function Fe(e,t){if(t){isNaN(e)&&(e=0),Ue.secs=parseInt(e%60),Ue.mins=parseInt(e/60%60),Ue.hours=parseInt(e/60/60%60);var n=parseInt(fe()/60/60%60)>0;Ue.secs=("0"+Ue.secs).slice(-2),Ue.mins=("0"+Ue.mins).slice(-2),t.innerHTML=(n?Ue.hours+":":"")+Ue.mins+":"+Ue.secs}}function Ae(){if(Ue.supported.full){var e=fe()||0;!Ue.duration&&E.displayDuration&&Ue.media.paused&&Fe(e,Ue.currentTime),Ue.duration&&Fe(e,Ue.duration),Pe()}}function Ie(e){Fe(Ue.media.currentTime,Ue.currentTime),e&&"timeupdate"===e.type&&Ue.media.seeking||Ee(e)}function Ne(e){O.number(e)||(e=0);var t=fe(),n=w(e,t);Ue.progress&&Ue.progress.played&&(Ue.progress.played.value=n),Ue.buttons&&Ue.buttons.seek&&(Ue.buttons.seek.value=n)}function Pe(e){var t=fe();if(E.tooltips.seek&&Ue.progress.container&&0!==t){var n=Ue.progress.container.getBoundingClientRect(),r=0,a=E.classes.tooltip+"--visible";if(e)r=100/n.width*(e.pageX-n.left);else{if(!f(Ue.progress.tooltip,a))return;r=Ue.progress.tooltip.style.left.replace("%","")}r<0?r=0:r>100&&(r=100),Fe(t/100*r,Ue.progress.tooltip),Ue.progress.tooltip.style.left=r+"%",e&&s(["mouseenter","mouseleave"],e.type)&&m(Ue.progress.tooltip,a,"mouseenter"===e.type)}}function Me(t){if(E.hideControls&&"audio"!==Ue.type){var n=0,r=!1,a=t,o=f(Ue.container,E.classes.loading);if(O.boolean(t)||(t&&t.type?(r="enterfullscreen"===t.type,a=s(["mousemove","touchstart","mouseenter","focus"],t.type),s(["mousemove","touchmove"],t.type)&&(n=2e3),"focus"===t.type&&(n=3e3)):a=f(Ue.container,E.classes.hideControls)),e.clearTimeout(Xe.hover),a||Ue.media.paused||o){if(m(Ue.container,E.classes.hideControls,!1),Ue.media.paused||o)return;Ue.browser.isTouch&&(n=3e3)}a&&Ue.media.paused||(Xe.hover=e.setTimeout(function(){(!Ue.controls.pressed&&!Ue.controls.hover||r)&&m(Ue.container,E.classes.hideControls,!0)},n))}}function Oe(e){if(!O.undefined(e))return void Le(e);var t;switch(Ue.type){case"youtube":t=Ue.embed.getVideoUrl();break;case"vimeo":Ue.embed.getVideoUrl.then(function(e){t=e});break;case"soundcloud":Ue.embed.getCurrentSound(function(e){t=e.permalink_url});break;default:t=Ue.media.currentSrc}return t||""}function Le(e){function n(){if(Ue.embed=null,l(Ue.media),"video"===Ue.type&&Ue.videoContainer&&l(Ue.videoContainer),Ue.container&&Ue.container.removeAttribute("class"),"type"in e&&(Ue.type=e.type,"video"===Ue.type)){var n=e.sources[0];"type"in n&&s(E.types.embed,n.type)&&(Ue.type=n.type)}switch(Ue.supported=F(Ue.type),Ue.type){case"video":Ue.media=t.createElement("video");break;case"audio":Ue.media=t.createElement("audio");break;case"youtube":case"vimeo":case"soundcloud":Ue.media=t.createElement("div"),Ue.embedId=e.sources[0].src}u(Ue.container,Ue.media),O.boolean(e.autoplay)&&(E.autoplay=e.autoplay),s(E.types.html5,Ue.type)&&(E.crossorigin&&Ue.media.setAttribute("crossorigin",""),E.autoplay&&Ue.media.setAttribute("autoplay",""),"poster"in e&&Ue.media.setAttribute("poster",e.poster),E.loop&&Ue.media.setAttribute("loop","")),m(Ue.container,E.classes.fullscreen.active,Ue.isFullscreen),m(Ue.container,E.classes.captions.active,Ue.captionsEnabled),K(),s(E.types.html5,Ue.type)&&J("source",e.sources),ne(),s(E.types.html5,Ue.type)&&("tracks"in e&&J("track",e.tracks),Ue.media.load()),(s(E.types.html5,Ue.type)||s(E.types.embed,Ue.type)&&!Ue.supported.full)&&(We(),Ye()),E.title=e.title,Z()}return O.object(e)&&"sources"in e&&e.sources.length?(m(Ue.container,E.classes.ready,!1),ue(),Ne(),Ce(),qe(),void De(n,!1)):void ze("Invalid source format")}function je(e){"video"===Ue.type&&Ue.media.setAttribute("poster",e)}function Ve(){function n(){var e=ce(),t=Ue.buttons[e?"play":"pause"],n=Ue.buttons[e?"pause":"play"];if(n=n&&n.length>1?n[n.length-1]:n[0]){var r=f(t,E.classes.tabFocus);setTimeout(function(){n.focus(),r&&(m(t,E.classes.tabFocus,!1),m(n,E.classes.tabFocus,!0))},100)}}function r(){var e=t.activeElement;return e=e&&e!==t.body?t.querySelector(":focus"):null}function a(e){return e.keyCode?e.keyCode:e.which}function o(e){for(var t in Ue.buttons){var n=Ue.buttons[t];if(O.nodeList(n))for(var r=0;r<n.length;r++)m(n[r],E.classes.tabFocus,n[r]===e);else m(n,E.classes.tabFocus,n===e)}}function i(e){function t(){var e=Ue.media.duration;O.number(e)&&me(e/10*(n-48))}var n=a(e),r="keydown"===e.type,o=r&&n===u;if(O.number(n))if(r){var i=[48,49,50,51,52,53,54,56,57,32,75,38,40,77,39,37,70,67];switch(s(i,n)&&(e.preventDefault(),e.stopPropagation()),n){case 48:case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:o||t();break;case 32:case 75:o||ce();break;case 38:we();break;case 40:xe();break;case 77:o||he();break;case 39:pe();break;case 37:de();break;case 70:ge();break;case 67:o||Se()}!N.supportsFullScreen&&Ue.isFullscreen&&27===n&&ge(),u=n}else u=null}var l=Ue.browser.isIE?"change":"input";if(E.keyboardShorcuts.focused){var u=null;E.keyboardShorcuts.global&&g(e,"keydown keyup",function(e){var t=a(e),n=r(),o=[48,49,50,51,52,53,54,56,57,75,77,70,67],l=I().length;1!==l||!s(o,t)||O.htmlElement(n)&&y(n,E.selectors.editable)||i(e)}),g(Ue.container,"keydown keyup",i)}g(e,"keyup",function(e){var t=a(e),n=r();9===t&&o(n)}),g(t.body,"click",function(){m(U("."+E.classes.tabFocus),E.classes.tabFocus,!1)});for(var c in Ue.buttons){var d=Ue.buttons[c];g(d,"blur",function(){m(d,"tab-focus",!1);
|
2
|
+
})}b(Ue.buttons.play,"click",E.listeners.play,n),b(Ue.buttons.pause,"click",E.listeners.pause,n),b(Ue.buttons.restart,"click",E.listeners.restart,me),b(Ue.buttons.rewind,"click",E.listeners.rewind,de),b(Ue.buttons.forward,"click",E.listeners.forward,pe),b(Ue.buttons.seek,l,E.listeners.seek,me),b(Ue.volume.input,l,E.listeners.volume,function(){ke(Ue.volume.input.value)}),b(Ue.buttons.mute,"click",E.listeners.mute,he),b(Ue.buttons.fullscreen,"click",E.listeners.fullscreen,ge),N.supportsFullScreen&&g(t,N.fullScreenEventName,ge),g(Ue.buttons.captions,"click",Se),g(Ue.progress.container,"mouseenter mouseleave mousemove",Pe),E.hideControls&&(g(Ue.container,"mouseenter mouseleave mousemove touchstart touchend touchcancel touchmove enterfullscreen",Me),g(Ue.controls,"mouseenter mouseleave",function(e){Ue.controls.hover="mouseenter"===e.type}),g(Ue.controls,"mousedown mouseup touchstart touchend touchcancel",function(e){Ue.controls.pressed=s(["mousedown","touchstart"],e.type)}),g(Ue.controls,"focus blur",Me,!0)),g(Ue.volume.input,"wheel",function(e){e.preventDefault();var t=e.webkitDirectionInvertedFromDevice,n=E.volumeStep/5;(e.deltaY<0||e.deltaX>0)&&(t?xe(n):we(n)),(e.deltaY>0||e.deltaX<0)&&(t?we(n):xe(n))})}function Re(){if(g(Ue.media,"timeupdate seeking",Ie),g(Ue.media,"timeupdate",W),g(Ue.media,"durationchange loadedmetadata",Ae),g(Ue.media,"ended",function(){"video"===Ue.type&&E.showPosterOnEnd&&("video"===Ue.type&&H(),me(),Ue.media.load())}),g(Ue.media,"progress playing",Ee),g(Ue.media,"volumechange",Te),g(Ue.media,"play pause ended",ye),g(Ue.media,"waiting canplay seeked",_e),E.clickToPlay&&"audio"!==Ue.type){var e=U("."+E.classes.videoWrapper);if(!e)return;e.style.cursor="pointer",g(e,"click",function(){E.hideControls&&Ue.browser.isTouch&&!Ue.media.paused||(Ue.media.paused?le():Ue.media.ended?(me(),le()):ue())})}E.disableContextMenu&&g(Ue.media,"contextmenu",function(e){e.preventDefault()}),g(Ue.media,E.events.concat(["keyup","keydown"]).join(" "),function(e){A(Ue.container,e.type,!0)})}function qe(){if(s(E.types.html5,Ue.type)){for(var e=Ue.media.querySelectorAll("source"),t=0;t<e.length;t++)l(e[t]);Ue.media.setAttribute("src","https://cdn.selz.com/plyr/blank.mp4"),Ue.media.load(),Je("Cancelled network requests")}}function De(n,r){function a(){clearTimeout(Xe.cleanUp),O.boolean(r)||(r=!0),O.function(n)&&n.call($e),r&&(Ue.init=!1,Ue.container.parentNode.replaceChild($e,Ue.container),t.body.style.overflow="",A($e,"destroyed",!0))}if(!Ue.init)return null;switch(Ue.type){case"youtube":e.clearInterval(Xe.buffering),e.clearInterval(Xe.playing),Ue.embed.destroy(),a();break;case"vimeo":Ue.embed.unload().then(a),Xe.cleanUp=e.setTimeout(a,200);break;case"video":case"audio":Q(!0),a()}}function He(){if(Ue.init)return null;if(N=_(),Ue.browser=n(),O.htmlElement(Ue.media)){ee();var e=v.tagName.toLowerCase();"div"===e?(Ue.type=v.getAttribute("data-type"),Ue.embedId=v.getAttribute("data-video-id"),v.removeAttribute("data-type"),v.removeAttribute("data-video-id")):(Ue.type=e,E.crossorigin=null!==v.getAttribute("crossorigin"),E.autoplay=E.autoplay||null!==v.getAttribute("autoplay"),E.loop=E.loop||null!==v.getAttribute("loop")),Ue.supported=F(Ue.type),Ue.supported.basic&&(Ue.container=i(v,t.createElement("div")),Ue.container.setAttribute("tabindex",0),K(),Je(""+Ue.browser.name+" "+Ue.browser.version),ne(),(s(E.types.html5,Ue.type)||s(E.types.embed,Ue.type)&&!Ue.supported.full)&&(We(),Ye(),Z()),Ue.init=!0)}}function We(){if(!Ue.supported.full)return ze("Basic support only",Ue.type),l(U(E.selectors.controls.wrapper)),l(U(E.selectors.buttons.play)),void Q(!0);var e=!B(E.selectors.controls.wrapper).length;e&&z(),G()&&(e&&Ve(),Re(),Q(),q(),D(),ke(),Te(),Ie(),ye())}function Ye(){e.setTimeout(function(){A(Ue.media,"ready")},0),m(Ue.media,M.classes.setup,!0),m(Ue.container,E.classes.ready,!0),Ue.media.plyr=Be,E.autoplay&&le()}var Be,Ue=this,Xe={};Ue.media=v;var $e=v.cloneNode(!0),Je=function(){j("log",arguments)},ze=function(){j("warn",arguments)};return Je("Config",E),Be={getOriginal:function(){return $e},getContainer:function(){return Ue.container},getEmbed:function(){return Ue.embed},getMedia:function(){return Ue.media},getType:function(){return Ue.type},getDuration:fe,getCurrentTime:function(){return Ue.media.currentTime},getVolume:function(){return Ue.media.volume},isMuted:function(){return Ue.media.muted},isReady:function(){return f(Ue.container,E.classes.ready)},isLoading:function(){return f(Ue.container,E.classes.loading)},isPaused:function(){return Ue.media.paused},on:function(e,t){return g(Ue.container,e,t),this},play:le,pause:ue,stop:function(){ue(),me()},restart:me,rewind:de,forward:pe,seek:me,source:Oe,poster:je,setVolume:ke,togglePlay:ce,toggleMute:he,toggleCaptions:Se,toggleFullscreen:ge,toggleControls:Me,isFullscreen:function(){return Ue.isFullscreen||!1},support:function(e){return r(Ue,e)},destroy:De},He(),Ue.init?Be:null}function C(e,n){var r=new XMLHttpRequest;if(!O.string(n)||!O.htmlElement(t.querySelector("#"+n))){var a=t.createElement("div");a.setAttribute("hidden",""),O.string(n)&&a.setAttribute("id",n),t.body.insertBefore(a,t.body.childNodes[0]),"withCredentials"in r&&(r.open("GET",e,!0),r.onload=function(){a.innerHTML=r.responseText},r.send())}}function F(e){var r=n(),a=r.isIE&&r.version<=9,s=r.isIos,o=r.isIphone,i=!!t.createElement("audio").canPlayType,l=!!t.createElement("video").canPlayType,u=!1,c=!1;switch(e){case"video":u=l,c=u&&!a&&!o;break;case"audio":u=i,c=u&&!a;break;case"vimeo":u=!0,c=!a&&!s;break;case"youtube":u=!0,c=!a&&!s,s&&!o&&r.version>=10&&(c=!0);break;case"soundcloud":u=!0,c=!a&&!o;break;default:u=i&&l,c=u&&!a}return{basic:u,full:c}}function A(e,n){function r(e,t){f(t,M.classes.hook)||a.push({target:e,media:t})}var a=[],s=[],o=[M.selectors.html5,M.selectors.embed].join(",");if(O.string(e)?e=t.querySelectorAll(e):O.htmlElement(e)?e=[e]:O.nodeList(e)||O.array(e)||O.string(e)||(O.undefined(n)&&O.object(e)&&(n=e),e=t.querySelectorAll(o)),O.nodeList(e)&&(e=Array.prototype.slice.call(e)),!F().basic||!e.length)return!1;for(var i=0;i<e.length;i++){var l=e[i],u=l.querySelectorAll(o);if(u.length)for(var c=0;c<u.length;c++)r(l,u[c]);else y(l,o)&&r(l,l)}return a.forEach(function(e){var t=e.target,r=e.media,a=!1;r===t&&(a=!0);var o={};try{o=JSON.parse(t.getAttribute("data-plyr"))}catch(e){}var i=x({},M,n,o);if(!i.enabled)return null;var l=new E(r,i);if(O.object(l)){if(i.debug){var u=i.events.concat(["setup","statechange","enterfullscreen","exitfullscreen","captionsenabled","captionsdisabled"]);g(l.getContainer(),u.join(" "),function(e){console.log([i.logPrefix,"event:",e.type].join(" "),e.detail.plyr)})}h(l.getContainer(),"setup",!0,{plyr:l}),s.push(l)}}),s}function I(e){if(O.string(e)?e=t.querySelector(e):O.undefined(e)&&(e=t.body),O.htmlElement(e)){var n=e.querySelectorAll("."+M.classes.setup),r=[];return Array.prototype.slice.call(n).forEach(function(e){O.object(e.plyr)&&r.push(e.plyr)}),r}return[]}var N,P={x:0,y:0},M={enabled:!0,debug:!1,autoplay:!1,loop:!1,seekTime:10,volume:10,volumeMin:0,volumeMax:10,volumeStep:1,duration:null,displayDuration:!0,loadSprite:!0,iconPrefix:"plyr",iconUrl:"https://cdn.plyr.io/2.0.10/plyr.svg",clickToPlay:!0,hideControls:!0,showPosterOnEnd:!1,disableContextMenu:!0,keyboardShorcuts:{focused:!0,global:!1},tooltips:{controls:!1,seek:!0},selectors:{html5:"video, audio",embed:"[data-type]",editable:"input, textarea, select, [contenteditable]",container:".plyr",controls:{container:null,wrapper:".plyr__controls"},labels:"[data-plyr]",buttons:{seek:'[data-plyr="seek"]',play:'[data-plyr="play"]',pause:'[data-plyr="pause"]',restart:'[data-plyr="restart"]',rewind:'[data-plyr="rewind"]',forward:'[data-plyr="fast-forward"]',mute:'[data-plyr="mute"]',captions:'[data-plyr="captions"]',fullscreen:'[data-plyr="fullscreen"]'},volume:{input:'[data-plyr="volume"]',display:".plyr__volume--display"},progress:{container:".plyr__progress",buffer:".plyr__progress--buffer",played:".plyr__progress--played"},captions:".plyr__captions",currentTime:".plyr__time--current",duration:".plyr__time--duration"},classes:{setup:"plyr--setup",ready:"plyr--ready",videoWrapper:"plyr__video-wrapper",embedWrapper:"plyr__video-embed",type:"plyr--{0}",stopped:"plyr--stopped",playing:"plyr--playing",muted:"plyr--muted",loading:"plyr--loading",hover:"plyr--hover",tooltip:"plyr__tooltip",hidden:"plyr__sr-only",hideControls:"plyr--hide-controls",isIos:"plyr--is-ios",isTouch:"plyr--is-touch",captions:{enabled:"plyr--captions-enabled",active:"plyr--captions-active"},fullscreen:{enabled:"plyr--fullscreen-enabled",active:"plyr--fullscreen-active"},tabFocus:"tab-focus"},captions:{defaultActive:!1},fullscreen:{enabled:!0,fallback:!0,allowAudio:!1},storage:{enabled:!0,key:"plyr"},controls:["play-large","play","progress","current-time","mute","volume","captions","fullscreen"],i18n:{restart:"Restart",rewind:"Rewind {seektime} secs",play:"Play",pause:"Pause",forward:"Forward {seektime} secs",played:"played",buffered:"buffered",currentTime:"Current time",duration:"Duration",volume:"Volume",toggleMute:"Toggle Mute",toggleCaptions:"Toggle Captions",toggleFullscreen:"Toggle Fullscreen",frameTitle:"Player for {title}"},types:{embed:["youtube","vimeo","soundcloud"],html5:["video","audio"]},urls:{vimeo:{api:"https://player.vimeo.com/api/player.js"},youtube:{api:"https://www.youtube.com/iframe_api"},soundcloud:{api:"https://w.soundcloud.com/player/api.js"}},listeners:{seek:null,play:null,pause:null,restart:null,rewind:null,forward:null,mute:null,volume:null,captions:null,fullscreen:null},events:["ready","ended","progress","stalled","playing","waiting","canplay","canplaythrough","loadstart","loadeddata","loadedmetadata","timeupdate","volumechange","play","pause","error","seeking","seeked","emptied"],logPrefix:"[Plyr]"},O={object:function(e){return null!==e&&"object"==typeof e},array:function(e){return null!==e&&"object"==typeof e&&e.constructor===Array},number:function(e){return null!==e&&("number"==typeof e&&!isNaN(e-0)||"object"==typeof e&&e.constructor===Number)},string:function(e){return null!==e&&("string"==typeof e||"object"==typeof e&&e.constructor===String)},boolean:function(e){return null!==e&&"boolean"==typeof e},nodeList:function(e){return null!==e&&e instanceof NodeList},htmlElement:function(e){return null!==e&&e instanceof HTMLElement},function:function(e){return null!==e&&"function"==typeof e},undefined:function(e){return null!==e&&"undefined"==typeof e}},L={supported:function(){if(!("localStorage"in e))return!1;try{e.localStorage.setItem("___test","OK");var t=e.localStorage.getItem("___test");return e.localStorage.removeItem("___test"),"OK"===t}catch(e){return!1}return!1}()};return{setup:A,supported:F,loadSprite:C,get:I}}),function(){function e(e,t){t=t||{bubbles:!1,cancelable:!1,detail:void 0};var n=document.createEvent("CustomEvent");return n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail),n}"function"!=typeof window.CustomEvent&&(e.prototype=window.Event.prototype,window.CustomEvent=e)}();
|