aboutsummaryrefslogtreecommitdiffstats
path: root/src/js/plyr.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/js/plyr.js')
-rw-r--r--src/js/plyr.js800
1 files changed, 543 insertions, 257 deletions
diff --git a/src/js/plyr.js b/src/js/plyr.js
index d6b16b7b..f01866bd 100644
--- a/src/js/plyr.js
+++ b/src/js/plyr.js
@@ -1,6 +1,6 @@
// ==========================================================================
// Plyr
-// plyr.js v1.6.11
+// plyr.js v1.8.12
// https://github.com/selz/plyr
// License: The MIT License (MIT)
// ==========================================================================
@@ -23,13 +23,13 @@
}
}(typeof window !== 'undefined' ? window : this, function(window, document) {
'use strict';
- /*global YT,$f*/
// Globals
- var fullscreen, api = {};
+ var fullscreen,
+ scroll = { x: 0, y: 0 },
// Default config
- var defaults = {
+ defaults = {
enabled: true,
debug: false,
autoplay: false,
@@ -41,16 +41,20 @@
volumeStep: 1,
duration: null,
displayDuration: true,
+ loadSprite: true,
iconPrefix: 'plyr',
- iconUrl: '',
+ iconUrl: 'https://cdn.plyr.io/1.8.12/plyr.svg',
clickToPlay: true,
hideControls: true,
showPosterOnEnd: false,
+ disableContextMenu: true,
tooltips: {
controls: false,
seek: true
},
selectors: {
+ html5: 'video, audio',
+ embed: '[data-type]',
container: '.plyr',
controls: {
container: null,
@@ -141,7 +145,7 @@
// URLs
urls: {
vimeo: {
- api: 'https://cdn.plyr.io/froogaloop/1.0.1/plyr.froogaloop.js',
+ api: 'https://player.vimeo.com/api/player.js',
},
youtube: {
api: 'https://www.youtube.com/iframe_api'
@@ -170,51 +174,61 @@
// Credits: http://paypal.github.io/accessible-html5-video-player/
// Unfortunately, due to mixed support, UA sniffing is required
function _browserSniff() {
- var nAgt = navigator.userAgent,
+ var ua = navigator.userAgent,
name = navigator.appName,
fullVersion = '' + parseFloat(navigator.appVersion),
majorVersion = parseInt(navigator.appVersion, 10),
nameOffset,
verOffset,
- ix;
+ ix,
+ isIE = false,
+ isFirefox = false,
+ isChrome = false,
+ isSafari = false;
// MSIE 11
if ((navigator.appVersion.indexOf('Windows NT') !== -1) && (navigator.appVersion.indexOf('rv:11') !== -1)) {
+ isIE = true;
name = 'IE';
- fullVersion = '11;';
+ fullVersion = '11';
}
// MSIE
- else if ((verOffset=nAgt.indexOf('MSIE')) !== -1) {
+ else if ((verOffset = ua.indexOf('MSIE')) !== -1) {
+ isIE = true;
name = 'IE';
- fullVersion = nAgt.substring(verOffset + 5);
+ fullVersion = ua.substring(verOffset + 5);
}
// Chrome
- else if ((verOffset=nAgt.indexOf('Chrome')) !== -1) {
+ else if ((verOffset = ua.indexOf('Chrome')) !== -1) {
+ isChrome = true;
name = 'Chrome';
- fullVersion = nAgt.substring(verOffset + 7);
+ fullVersion = ua.substring(verOffset + 7);
}
// Safari
- else if ((verOffset=nAgt.indexOf('Safari')) !== -1) {
+ else if ((verOffset = ua.indexOf('Safari')) !== -1) {
+ isSafari = true;
name = 'Safari';
- fullVersion = nAgt.substring(verOffset + 7);
- if ((verOffset = nAgt.indexOf('Version')) !== -1) {
- fullVersion = nAgt.substring(verOffset + 8);
+ fullVersion = ua.substring(verOffset + 7);
+ if ((verOffset = ua.indexOf('Version')) !== -1) {
+ fullVersion = ua.substring(verOffset + 8);
}
}
// Firefox
- else if ((verOffset=nAgt.indexOf('Firefox')) !== -1) {
+ else if ((verOffset = ua.indexOf('Firefox')) !== -1) {
+ isFirefox = true;
name = 'Firefox';
- fullVersion = nAgt.substring(verOffset + 8);
+ fullVersion = ua.substring(verOffset + 8);
}
// In most other browsers, 'name/version' is at the end of userAgent
- else if ((nameOffset=nAgt.lastIndexOf(' ') + 1) < (verOffset=nAgt.lastIndexOf('/'))) {
- name = nAgt.substring(nameOffset,verOffset);
- fullVersion = nAgt.substring(verOffset + 1);
+ else if ((nameOffset = ua.lastIndexOf(' ') + 1) < (verOffset = ua.lastIndexOf('/'))) {
+ name = ua.substring(nameOffset,verOffset);
+ fullVersion = ua.substring(verOffset + 1);
if (name.toLowerCase() == name.toUpperCase()) {
name = navigator.appName;
}
}
+
// Trim the fullVersion string at semicolon/space if present
if ((ix = fullVersion.indexOf(';')) !== -1) {
fullVersion = fullVersion.substring(0, ix);
@@ -222,6 +236,7 @@
if ((ix = fullVersion.indexOf(' ')) !== -1) {
fullVersion = fullVersion.substring(0, ix);
}
+
// Get major version
majorVersion = parseInt('' + fullVersion, 10);
if (isNaN(majorVersion)) {
@@ -233,8 +248,12 @@
return {
name: name,
version: majorVersion,
- ios: /(iPad|iPhone|iPod)/g.test(navigator.platform),
- touch: 'ontouchstart' in document.documentElement
+ isIE: isIE,
+ isFirefox: isFirefox,
+ isChrome: isChrome,
+ isSafari: isSafari,
+ isIos: /(iPad|iPhone|iPod)/g.test(navigator.platform),
+ isTouch: 'ontouchstart' in document.documentElement
};
}
@@ -320,6 +339,8 @@
else {
parent.appendChild(child);
}
+
+ return child;
}
}
@@ -354,7 +375,7 @@
// Set attributes
function _setAttributes(element, attributes) {
for (var key in attributes) {
- element.setAttribute(key, (typeof attributes[key] === 'boolean' && attributes[key]) ? '' : attributes[key]);
+ element.setAttribute(key, (_is.boolean(attributes[key]) && attributes[key]) ? '' : attributes[key]);
}
}
@@ -401,6 +422,17 @@
return false;
}
+ // Element matches selector
+ function _matches(element, selector) {
+ var p = Element.prototype;
+
+ var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) {
+ return [].indexOf.call(document.querySelectorAll(s), this) !== -1;
+ };
+
+ return f.call(element, selector);
+ }
+
// Bind event
function _on(element, events, callback, useCapture) {
if (element) {
@@ -431,7 +463,7 @@
// Whether the listener is a capturing listener or not
// Default to false
- if (typeof useCapture !== 'boolean') {
+ if (!_is.boolean(useCapture)) {
useCapture = false;
}
@@ -459,7 +491,7 @@
}
// Default bubbles to false
- if (typeof bubbles !== 'boolean') {
+ if (!_is.boolean(bubbles)) {
bubbles = false;
}
@@ -482,7 +514,7 @@
}
// Get state
- state = (typeof state === 'boolean' ? state : !target.getAttribute('aria-pressed'));
+ state = (_is.boolean(state) ? state : !target.getAttribute('aria-pressed'));
// Set the attribute on target
target.setAttribute('aria-pressed', state);
@@ -537,6 +569,34 @@
return destination;
}
+ // Check variable types
+ var _is = {
+ object: function(input) {
+ return input !== null && typeof(input) === 'object';
+ },
+ array: function(input) {
+ return input !== null && typeof(input) === 'object' && input.constructor === Array;
+ },
+ number: function(input) {
+ return typeof(input) === 'number' && !isNaN(input - 0) || (typeof input == 'object' && input.constructor === Number);
+ },
+ string: function(input) {
+ return typeof input === 'string' || (typeof input == 'object' && input.constructor === String);
+ },
+ boolean: function(input) {
+ return typeof input === 'boolean';
+ },
+ nodeList: function(input) {
+ return input instanceof NodeList;
+ },
+ htmlElement: function(input) {
+ return input instanceof HTMLElement;
+ },
+ undefined: function(input) {
+ return typeof input === 'undefined';
+ }
+ };
+
// Fullscreen API
function _fullscreen() {
var fullscreen = {
@@ -551,7 +611,7 @@
browserPrefixes = 'webkit moz o ms khtml'.split(' ');
// Check for native support
- if (typeof document.cancelFullScreen !== 'undefined') {
+ if (!_is.undefined(document.cancelFullScreen)) {
fullscreen.supportsFullScreen = true;
}
else {
@@ -559,12 +619,12 @@
for (var i = 0, il = browserPrefixes.length; i < il; i++ ) {
fullscreen.prefix = browserPrefixes[i];
- if (typeof document[fullscreen.prefix + 'CancelFullScreen'] !== 'undefined') {
+ if (!_is.undefined(document[fullscreen.prefix + 'CancelFullScreen'])) {
fullscreen.supportsFullScreen = true;
break;
}
// Special case for MS (when isn't it?)
- else if (typeof document.msExitFullscreen !== 'undefined' && document.msFullscreenEnabled) {
+ else if (!_is.undefined(document.msExitFullscreen) && document.msFullscreenEnabled) {
fullscreen.prefix = 'ms';
fullscreen.supportsFullScreen = true;
break;
@@ -579,7 +639,7 @@
fullscreen.fullScreenEventName = (fullscreen.prefix == 'ms' ? 'MSFullscreenChange' : fullscreen.prefix + 'fullscreenchange');
fullscreen.isFullScreen = function(element) {
- if (typeof element === 'undefined') {
+ if (_is.undefined(element)) {
element = document.body;
}
switch (this.prefix) {
@@ -592,7 +652,7 @@
}
};
fullscreen.requestFullScreen = function(element) {
- if (typeof element === 'undefined') {
+ if (_is.undefined(element)) {
element = document.body;
}
return (this.prefix === '') ? element.requestFullScreen() : element[this.prefix + (this.prefix == 'ms' ? 'RequestFullscreen' : 'RequestFullScreen')]();
@@ -651,17 +711,31 @@
_log(config);
// Debugging
- function _log(text, warn) {
+ function _log() {
+ if (config.debug && window.console) {
+ console.log.apply(console, arguments);
+ }
+ }
+ function _warn() {
if (config.debug && window.console) {
- console[(warn ? 'warn' : 'log')](text);
+ console.warn.apply(console, arguments);
}
}
+ // Get icon URL
+ function _getIconUrl() {
+ return {
+ url: config.iconUrl,
+ absolute: (config.iconUrl.indexOf("http") === 0) || plyr.browser.isIE
+ };
+ }
+
// Build the default HTML
function _buildControls() {
// Create html array
- var html = [],
- iconPath = config.iconUrl + '#' + config.iconPrefix;
+ var html = [],
+ iconUrl = _getIconUrl(),
+ iconPath = (!iconUrl.absolute ? iconUrl.url : '') + '#' + config.iconPrefix;
// Larger overlaid play button
if (_inArray(config.controls, 'play-large')) {
@@ -831,7 +905,9 @@
}
// Toggle state
- _toggleState(plyr.buttons.fullscreen, false);
+ if (plyr.buttons && plyr.buttons.fullscreen) {
+ _toggleState(plyr.buttons.fullscreen, false);
+ }
// Setup focus trap
_focusTrap();
@@ -840,6 +916,7 @@
// Setup captions
function _setupCaptions() {
+ // Bail if not HTML5 video
if (plyr.type !== 'video') {
return;
}
@@ -897,8 +974,8 @@
// Disable unsupported browsers than report false positive
// Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1033144
- if ((plyr.browser.name === 'IE' && plyr.browser.version >= 10) ||
- (plyr.browser.name === 'Firefox' && plyr.browser.version >= 31)) {
+ if ((plyr.browser.isIE && plyr.browser.version >= 10) ||
+ (plyr.browser.isFirefox && plyr.browser.version >= 31)) {
// Debugging
_log('Detected browser with known TextTrack issues - using manual fallback');
@@ -971,7 +1048,7 @@
_log('Successfully loaded the caption file via AJAX');
}
else {
- _log('There was a problem loading the caption file via AJAX', true);
+ _warn('There was a problem loading the caption file via AJAX');
}
}
};
@@ -994,12 +1071,12 @@
container.innerHTML = '';
// Default to empty
- if (typeof caption === 'undefined') {
+ if (_is.undefined(caption)) {
caption = '';
}
// Set the span content
- if (typeof caption === 'string') {
+ if (_is.undefined(caption)) {
content.innerHTML = caption.trim();
}
else {
@@ -1059,7 +1136,7 @@
// Check time is a number, if not use currentTime
// IE has a bug where currentTime doesn't go to 0
// https://twitter.com/Sam_Potts/status/573715746506731521
- time = typeof time === 'number' ? time : plyr.media.currentTime;
+ time = _is.number(time) ? time : plyr.media.currentTime;
// If there's no subs available, bail
if (!plyr.captions[plyr.subcount]) {
@@ -1096,7 +1173,15 @@
_toggleClass(plyr.container, config.classes.captions.enabled, true);
- if (config.captions.defaultActive) {
+ // Try to load the value from storage
+ var active = plyr.storage.captionsEnabled;
+
+ // Otherwise fall back to the default config
+ if (!_is.boolean(active)) {
+ active = config.captions.defaultActive;
+ }
+
+ if (active) {
_toggleClass(plyr.container, config.classes.captions.active, true);
_toggleState(plyr.buttons.captions, true);
}
@@ -1150,7 +1235,7 @@
// Add elements to HTML5 media (source, tracks, etc)
function _insertChildElements(type, attributes) {
- if (typeof attributes === 'string') {
+ if (_is.string(attributes)) {
_insertElement(type, plyr.media, { src: attributes });
}
else if (attributes.constructor === Array) {
@@ -1162,6 +1247,20 @@
// Insert controls
function _injectControls() {
+ // Sprite
+ if (config.loadSprite) {
+ var iconUrl = _getIconUrl();
+
+ // Only load external sprite using AJAX
+ if (iconUrl.absolute) {
+ _log('AJAX loading absolute SVG sprite' + (plyr.browser.isIE ? ' (due to IE)' : ''));
+ loadSprite(iconUrl.url, "sprite-plyr");
+ }
+ else {
+ _log('Sprite will be used as external resource directly');
+ }
+ }
+
// Make a copy of the html
var html = config.html;
@@ -1186,13 +1285,13 @@
if (config.selectors.controls.container !== null) {
container = config.selectors.controls.container;
- if (typeof selector === 'string') {
+ if (_is.string(container)) {
container = document.querySelector(container);
}
}
// Inject into the container by default
- if (!(container instanceof HTMLElement)) {
+ if (!_is.htmlElement(container)) {
container = plyr.container
}
@@ -1259,7 +1358,7 @@
return true;
}
catch(e) {
- _log('It looks like there is a problem with your controls html', true);
+ _warn('It looks like there is a problem with your controls HTML');
// Restore native video controls
_toggleNativeControls(true);
@@ -1275,7 +1374,7 @@
// Toggle native controls
function _toggleNativeControls(toggle) {
- if (toggle) {
+ if (toggle && _inArray(config.types.html5, plyr.type)) {
plyr.media.setAttribute('controls', '');
}
else {
@@ -1289,7 +1388,7 @@
var label = config.i18n.play;
// If there's a media title set, use that for the label
- if (typeof(config.title) !== 'undefined' && config.title.length) {
+ if (!_is.undefined(config.title) && config.title.length) {
label += ', ' + config.title;
}
@@ -1302,17 +1401,64 @@
// Set iframe title
// https://github.com/Selz/plyr/issues/124
- if (iframe instanceof HTMLElement) {
+ if (_is.htmlElement(iframe)) {
iframe.setAttribute('title', config.i18n.frameTitle.replace('{title}', config.title));
}
}
+ // Setup localStorage
+ function _setupStorage() {
+ var value = null;
+ plyr.storage = {};
+
+ // Bail if we don't have localStorage support or it's disabled
+ if (!_storage().supported || !config.storage.enabled) {
+ return;
+ }
+
+ // Clean up old volume
+ // https://github.com/Selz/plyr/issues/171
+ window.localStorage.removeItem('plyr-volume');
+
+ // load value from the current key
+ value = window.localStorage.getItem(config.storage.key);
+
+ if (!value) {
+ // Key wasn't set (or had been cleared), move along
+ return;
+ }
+ else if (/^\d+(\.\d+)?$/.test(value)) {
+ // If value is a number, it's probably volume from an older
+ // version of plyr. See: https://github.com/Selz/plyr/pull/313
+ // Update the key to be JSON
+ _updateStorage({volume: parseFloat(value)});
+ }
+ else {
+ // Assume it's JSON from this or a later version of plyr
+ plyr.storage = JSON.parse(value);
+ }
+ }
+
+ // Save a value back to local storage
+ function _updateStorage(value) {
+ // Bail if we don't have localStorage support or it's disabled
+ if (!_storage().supported || !config.storage.enabled) {
+ return;
+ }
+
+ // Update the working copy of the values
+ _extend(plyr.storage, value);
+
+ window.localStorage.setItem(
+ config.storage.key, JSON.stringify(plyr.storage));
+ }
+
// Setup media
function _setupMedia() {
// If there's no media, bail
if (!plyr.media) {
- _log('No audio or video element found', true);
- return false;
+ _warn('No media element found!');
+ return;
}
if (plyr.supported.full) {
@@ -1329,10 +1475,10 @@
_toggleClass(plyr.container, config.classes.stopped, config.autoplay);
// Add iOS class
- _toggleClass(plyr.container, config.classes.isIos, plyr.browser.ios);
+ _toggleClass(plyr.container, config.classes.isIos, plyr.browser.isIos);
// Add touch class
- _toggleClass(plyr.container, config.classes.isTouch, plyr.browser.touch);
+ _toggleClass(plyr.container, config.classes.isTouch, plyr.browser.isTouch);
// Inject the player wrapper
if (plyr.type === 'video') {
@@ -1382,7 +1528,7 @@
container.setAttribute('id', id);
// Setup API
- if (typeof YT === 'object' && YT.loaded !== 0) {
+ if (_is.object(window.YT)) {
_youTubeReady(mediaId, container);
}
else {
@@ -1403,45 +1549,35 @@
}
// Vimeo
else if (plyr.type === 'vimeo') {
- // Inject the iframe
- var vimeo = document.createElement('iframe');
-
- // Watch for iframe load
- vimeo.loaded = false;
- _on(vimeo, 'load', function() { vimeo.loaded = true; });
-
- _setAttributes(vimeo, {
- 'src': 'https://player.vimeo.com/video/' + mediaId + '?player_id=' + id + '&api=1&badge=0&byline=0&portrait=0&title=0',
- 'id': id,
- 'webkitallowfullscreen': '',
- 'mozallowfullscreen': '',
- 'allowfullscreen': '',
- 'frameborder': 0
- });
-
- // If full support, we can use custom controls (hiding Vimeos), if not, use Vimeo
+ // Vimeo needs an extra div to hide controls on desktop (which has full support)
if (plyr.supported.full) {
- container.appendChild(vimeo);
plyr.media.appendChild(container);
}
else {
- plyr.media.appendChild(vimeo);
+ container = plyr.media;
}
+ // Set ID
+ container.setAttribute('id', id);
+
// Load the API if not already
- if (!('$f' in window)) {
+ if (!_is.object(window.Vimeo)) {
_injectScript(config.urls.vimeo.api);
- }
- // Wait for fragaloop load
- var vimeoTimer = window.setInterval(function() {
- if ('$f' in window && vimeo.loaded) {
- window.clearInterval(vimeoTimer);
- _vimeoReady.call(vimeo);
- }
- }, 50);
+ // Wait for fragaloop load
+ var vimeoTimer = window.setInterval(function() {
+ if (_is.object(window.Vimeo)) {
+ window.clearInterval(vimeoTimer);
+ _vimeoReady(mediaId, container);
+ }
+ }, 50);
+ }
+ else {
+ _vimeoReady(mediaId, container);
+ }
}
// Soundcloud
+ // TODO: Currently unsupported and undocumented
else if (plyr.type === 'soundcloud') {
// Inject the iframe
var soundCloud = document.createElement('iframe');
@@ -1478,8 +1614,10 @@
// Store reference to API
plyr.container.plyr.embed = plyr.embed;
- // Setup the UI
- _setupInterface();
+ // Setup the UI if full support
+ if (plyr.supported.full) {
+ _setupInterface();
+ }
// Set title
_setTitle(_getElement('iframe'));
@@ -1495,7 +1633,7 @@
// Setup instance
// https://developers.google.com/youtube/iframe_api_reference
- plyr.embed = new YT.Player(container.id, {
+ plyr.embed = new window.YT.Player(container.id, {
videoId: videoId,
playerVars: {
autoplay: (config.autoplay ? 1 : 0),
@@ -1623,86 +1761,95 @@
}
// Vimeo ready
- function _vimeoReady() {
- /* jshint validthis: true */
- plyr.embed = $f(this);
-
- // Setup on ready
- plyr.embed.addEvent('ready', function() {
+ function _vimeoReady(mediaId, container) {
+ // Setup instance
+ // https://github.com/vimeo/player.js
+ plyr.embed = new window.Vimeo.Player(container.id, {
+ id: mediaId,
+ loop: config.loop,
+ autoplay: config.autoplay,
+ byline: false,
+ portrait: false,
+ title: false
+ });
- // Create a faux HTML5 API using the Vimeo API
- plyr.media.play = function() {
- plyr.embed.api('play');
- plyr.media.paused = false;
- };
- plyr.media.pause = function() {
- plyr.embed.api('pause');
- plyr.media.paused = true;
- };
- plyr.media.stop = function() {
- plyr.embed.api('stop');
- plyr.media.paused = true;
- };
+ // Create a faux HTML5 API using the Vimeo API
+ plyr.media.play = function() {
+ plyr.embed.play();
+ plyr.media.paused = false;
+ };
+ plyr.media.pause = function() {
+ plyr.embed.pause();
plyr.media.paused = true;
- plyr.media.currentTime = 0;
-
- // Update UI
- _embedReady();
+ };
+ plyr.media.stop = function() {
+ plyr.embed.stop();
+ plyr.media.paused = true;
+ };
+ plyr.media.paused = true;
+ plyr.media.currentTime = 0;
- plyr.embed.api('getCurrentTime', function (value) {
- plyr.media.currentTime = value;
+ // Update UI
+ _embedReady();
- // Trigger timeupdate
- _triggerEvent(plyr.media, 'timeupdate');
- });
+ plyr.embed.getCurrentTime().then(function (value) {
+ plyr.media.currentTime = value;
- plyr.embed.api('getDuration', function(value) {
- plyr.media.duration = value;
+ // Trigger timeupdate
+ _triggerEvent(plyr.media, 'timeupdate');
+ });
- // Display duration if available
- _displayDuration();
- });
+ plyr.embed.getDuration().then(function(value) {
+ plyr.media.duration = value;
- plyr.embed.addEvent('play', function() {
- plyr.media.paused = false;
- _triggerEvent(plyr.media, 'play');
- _triggerEvent(plyr.media, 'playing');
- });
+ // Display duration if available
+ _displayDuration();
+ });
- plyr.embed.addEvent('pause', function() {
- plyr.media.paused = true;
- _triggerEvent(plyr.media, 'pause');
- });
+ // TODO: Captions
+ /*if (config.captions.defaultActive) {
+ plyr.embed.enableTextTrack('en');
+ }*/
- plyr.embed.addEvent('playProgress', function(data) {
- plyr.media.seeking = false;
- plyr.media.currentTime = data.seconds;
- _triggerEvent(plyr.media, 'timeupdate');
- });
+ // Fix keyboard focus issues
+ // https://github.com/Selz/plyr/issues/317
+ plyr.embed.on('loaded', function() {
+ if(_is.htmlElement(plyr.embed.element) && plyr.supported.full) {
+ plyr.embed.element.setAttribute('tabindex', '-1');
+ }
+ });
- plyr.embed.addEvent('loadProgress', function(data) {
- plyr.media.buffered = data.percent;
- _triggerEvent(plyr.media, 'progress');
+ plyr.embed.on('play', function() {
+ plyr.media.paused = false;
+ _triggerEvent(plyr.media, 'play');
+ _triggerEvent(plyr.media, 'playing');
+ });
- if (parseInt(data.percent) === 1) {
- // Trigger event
- _triggerEvent(plyr.media, 'canplaythrough');
- }
- });
+ plyr.embed.on('pause', function() {
+ plyr.media.paused = true;
+ _triggerEvent(plyr.media, 'pause');
+ });
- plyr.embed.addEvent('finish', function() {
- plyr.media.paused = true;
- _triggerEvent(plyr.media, 'ended');
- });
+ plyr.embed.on('timeupdate', function(data) {
+ plyr.media.seeking = false;
+ plyr.media.currentTime = data.seconds;
+ _triggerEvent(plyr.media, 'timeupdate');
+ });
- // Always seek to 0
- // plyr.embed.api('seekTo', 0);
+ plyr.embed.on('progress', function(data) {
+ plyr.media.buffered = data.percent;
+ _triggerEvent(plyr.media, 'progress');
- // Autoplay
- if (config.autoplay) {
- plyr.embed.api('play');
+ if (parseInt(data.percent) === 1) {
+ // Trigger event
+ _triggerEvent(plyr.media, 'canplaythrough');
}
});
+
+ plyr.embed.on('ended', function() {
+ plyr.media.paused = true;
+ _triggerEvent(plyr.media, 'ended');
+ });
}
// Soundcloud ready
@@ -1817,7 +1964,7 @@
// Rewind
function _rewind(seekTime) {
// Use default if needed
- if (typeof seekTime !== 'number') {
+ if (!_is.number(seekTime)) {
seekTime = config.seekTime;
}
_seek(plyr.media.currentTime - seekTime);
@@ -1826,7 +1973,7 @@
// Fast forward
function _forward(seekTime) {
// Use default if needed
- if (typeof seekTime !== 'number') {
+ if (!_is.number(seekTime)) {
seekTime = config.seekTime;
}
_seek(plyr.media.currentTime + seekTime);
@@ -1840,11 +1987,11 @@
duration = _getDuration();
// Explicit position
- if (typeof input === 'number') {
+ if (_is.number(input)) {
targetTime = input;
}
// Event
- else if (typeof input === 'object' && (input.type === 'input' || input.type === 'change')) {
+ else if (_is.object(input) && _inArray(['input', 'change'], input.type)) {
// It's the seek slider
// Seek to the selected time
targetTime = ((input.target.value / input.target.max) * duration);
@@ -1864,7 +2011,7 @@
// Set the current time
// Try/catch incase the media isn't set and we're calling seek() from source() and IE moans
try {
- plyr.media.currentTime = targetTime.toFixed(1);
+ plyr.media.currentTime = targetTime.toFixed(4);
}
catch(e) {}
@@ -1878,7 +2025,7 @@
case 'vimeo':
// Round to nearest second for vimeo
- plyr.embed.api('seekTo', targetTime.toFixed(0));
+ plyr.embed.setCurrentTime(targetTime.toFixed(0));
break;
case 'soundcloud':
@@ -1929,6 +2076,19 @@
_toggleControls(plyr.media.paused);
}
+ // Save scroll position
+ function _saveScrollPosition() {
+ scroll = {
+ x: window.pageXOffset || 0,
+ y: window.pageYOffset || 0
+ };
+ }
+
+ // Restore scroll position
+ function _restoreScrollPosition() {
+ window.scrollTo(scroll.x, scroll.y);
+ }
+
// Toggle fullscreen
function _toggleFullscreen(event) {
// Check for native support
@@ -1942,6 +2102,10 @@
else if (nativeSupport) {
// Request fullscreen
if (!fullscreen.isFullScreen(plyr.container)) {
+ // Save scroll position
+ _saveScrollPosition();
+
+ // Request full screen
fullscreen.requestFullScreen(plyr.container);
}
// Bail from fullscreen
@@ -1982,10 +2146,17 @@
_focusTrap(plyr.isFullscreen);
// Set button state
- _toggleState(plyr.buttons.fullscreen, plyr.isFullscreen);
+ if (plyr.buttons && plyr.buttons.fullscreen) {
+ _toggleState(plyr.buttons.fullscreen, plyr.isFullscreen);
+ }
// Trigger an event
- _triggerEvent(plyr.container, plyr.isFullscreen ? 'enterfullscreen' : 'exitfullscreen');
+ _triggerEvent(plyr.container, plyr.isFullscreen ? 'enterfullscreen' : 'exitfullscreen', true);
+
+ // Restore scroll position
+ if (!plyr.isFullscreen && nativeSupport) {
+ _restoreScrollPosition();
+ }
}
// Bail from faux-fullscreen
@@ -1999,7 +2170,7 @@
// Mute
function _toggleMute(muted) {
// If the method is called without parameter, toggle based on current value
- if (typeof muted !== 'boolean') {
+ if (!_is.boolean(muted)) {
muted = !plyr.media.muted;
}
@@ -2023,9 +2194,6 @@
break;
case 'vimeo':
- plyr.embed.api('setVolume', plyr.media.muted ? 0 : parseFloat(config.volume / config.volumeMax));
- break;
-
case 'soundcloud':
plyr.embed.setVolume(plyr.media.muted ? 0 : parseFloat(config.volume / config.volumeMax));
break;
@@ -2041,17 +2209,9 @@
var max = config.volumeMax,
min = config.volumeMin;
- // Use default if no value specified
- if (typeof volume === 'undefined') {
- volume = config.volume;
-
- if (config.storage.enabled && _storage().supported) {
- volume = window.localStorage.getItem(config.storage.key);
-
- // Clean up old volume
- // https://github.com/Selz/plyr/issues/171
- window.localStorage.removeItem('plyr-volume');
- }
+ // Load volume from storage if no value specified
+ if (_is.undefined(volume)) {
+ volume = plyr.storage.volume;
}
// Use config if all else fails
@@ -2078,16 +2238,12 @@
// Embeds
if (_inArray(config.types.embed, plyr.type)) {
- // YouTube
switch(plyr.type) {
case 'youtube':
plyr.embed.setVolume(plyr.media.volume * 100);
break;
case 'vimeo':
- plyr.embed.api('setVolume', plyr.media.volume);
- break;
-
case 'soundcloud':
plyr.embed.setVolume(plyr.media.volume);
break;
@@ -2107,14 +2263,14 @@
function _increaseVolume() {
var volume = plyr.media.muted ? 0 : (plyr.media.volume * config.volumeMax);
- _setVolume(volume + config.volumeStep);
+ _setVolume(volume + (config.volumeStep / 5));
}
// Decrease volume
function _decreaseVolume() {
var volume = plyr.media.muted ? 0 : (plyr.media.volume * config.volumeMax);
- _setVolume(volume - config.volumeStep);
+ _setVolume(volume - (config.volumeStep / 5));
}
// Update volume UI and storage
@@ -2132,10 +2288,8 @@
}
}
- // Store the volume in storage
- if (config.storage.enabled && _storage().supported && !isNaN(volume)) {
- window.localStorage.setItem(config.storage.key, volume);
- }
+ // Update the volume in storage
+ _updateStorage({volume: volume});
// Toggle class if muted
_toggleClass(plyr.container, config.classes.muted, (volume === 0));
@@ -2154,7 +2308,7 @@
}
// If the method is called without parameter, toggle based on current value
- if (typeof show !== 'boolean') {
+ if (!_is.boolean(show)) {
show = (plyr.container.className.indexOf(config.classes.captions.active) === -1);
}
@@ -2168,7 +2322,10 @@
_toggleClass(plyr.container, config.classes.captions.active, plyr.captionsEnabled);
// Trigger an event
- _triggerEvent(plyr.container, plyr.captionsEnabled ? 'captionsenabled' : 'captionsdisabled');
+ _triggerEvent(plyr.container, plyr.captionsEnabled ? 'captionsenabled' : 'captionsdisabled', true);
+
+ // Save captions state to localStorage
+ _updateStorage({captionsEnabled: plyr.captionsEnabled});
}
// Check if media is loading
@@ -2199,6 +2356,10 @@
// Video playing
case 'timeupdate':
case 'seeking':
+ if (plyr.controls.pressed) {
+ return;
+ }
+
value = _getPercentage(plyr.media.currentTime, duration);
// Set seek range value only if it's a 'natural' time event
@@ -2220,7 +2381,7 @@
return _getPercentage(buffered.end(0), duration);
}
// YouTube returns between 0 and 1
- else if (typeof buffered === 'number') {
+ else if (_is.number(buffered)) {
return (buffered * 100);
}
@@ -2242,11 +2403,11 @@
}
// Default to 0
- if (typeof value === 'undefined') {
+ if (_is.undefined(value)) {
value = 0;
}
// Default to buffer or bail
- if (typeof progress === 'undefined') {
+ if (_is.undefined(progress)) {
if (plyr.progress && plyr.progress.buffer) {
progress = plyr.progress.buffer;
}
@@ -2256,7 +2417,7 @@
}
// One progress element passed
- if (progress instanceof HTMLElement) {
+ if (_is.htmlElement(progress)) {
progress.value = value;
}
// Object of progress + text element
@@ -2337,7 +2498,7 @@
// Update seek range and progress
function _updateSeekDisplay(time) {
// Default to 0
- if (typeof time !== 'number') {
+ if (!_is.number(time)) {
time = 0;
}
@@ -2408,21 +2569,22 @@
if (!config.hideControls || plyr.type === 'audio') {
return;
}
+
var delay = 0,
isEnterFullscreen = false,
show = toggle;
// Default to false if no boolean
- if (typeof toggle !== "boolean") {
+ if (!_is.boolean(toggle)) {
if (toggle && toggle.type) {
// Is the enter fullscreen event
isEnterFullscreen = (toggle.type === 'enterfullscreen');
// Whether to show controls
- show = _inArray(['mousemove', 'mouseenter', 'focus'], toggle.type);
+ show = _inArray(['mousemove', 'touchstart', 'mouseenter', 'focus'], toggle.type);
- // Delay hiding on mousemove events
- if (toggle.type === 'mousemove') {
+ // Delay hiding on move events
+ if (_inArray(['mousemove', 'touchmove'], toggle.type)) {
delay = 2000;
}
@@ -2432,7 +2594,7 @@
}
}
else {
- show = false;
+ show = _hasClass(plyr.container, config.classes.hideControls);
}
}
@@ -2443,21 +2605,26 @@
if (show || plyr.media.paused) {
_toggleClass(plyr.container, config.classes.hideControls, false);
- // Always show controls when paused
+ // Always show controls when paused or if touch
if (plyr.media.paused) {
return;
}
+
+ // Delay for hiding on touch
+ if (plyr.browser.isTouch) {
+ delay = 3000;
+ }
}
- // If toggle is false or if we're playing (regardless of toggle), then
- // set the timer to hide the controls
+ // If toggle is false or if we're playing (regardless of toggle),
+ // then set the timer to hide the controls
if (!show || !plyr.media.paused) {
plyr.timers.hover = window.setTimeout(function() {
// If the mouse is over the controls (and not entering fullscreen), bail
- if (plyr.controls.active && !isEnterFullscreen) {
+ if ((plyr.controls.pressed || plyr.controls.hover) && !isEnterFullscreen) {
return;
}
-
+
_toggleClass(plyr.container, config.classes.hideControls, true);
}, delay);
}
@@ -2466,7 +2633,7 @@
// Add common function to retrieve media source
function _source(source) {
// If not null or undefined, parse it
- if (typeof source !== 'undefined') {
+ if (!_is.undefined(source)) {
_updateSource(source);
return;
}
@@ -2479,7 +2646,7 @@
break;
case 'vimeo':
- plyr.embed.api('getVideoUrl', function (value) {
+ plyr.embed.getVideoUrl.then(function (value) {
url = value;
});
break;
@@ -2501,8 +2668,8 @@
// Update source
// Sources are not checked for support so be careful
function _updateSource(source) {
- if (typeof source === 'undefined' || !('sources' in source) || !source.sources.length) {
- _log('Invalid source format', true);
+ if (!_is.object(source) || !('sources' in source) || !source.sources.length) {
+ _warn('Invalid source format');
return;
}
@@ -2554,7 +2721,7 @@
}
// Check for support
- plyr.supported = api.supported(plyr.type);
+ plyr.supported = supported(plyr.type);
// Create new markup
switch(plyr.type) {
@@ -2578,7 +2745,7 @@
_prependChild(plyr.container, plyr.media);
// Autoplay the new source?
- if (typeof source.autoplay !== 'undefined') {
+ if (_is.boolean(source.autoplay)) {
config.autoplay = source.autoplay;
}
@@ -2630,6 +2797,10 @@
// Display duration if available
_displayDuration();
}
+ // If embed but not fully supported, setupInterface now
+ else if (_inArray(config.types.embed, plyr.type) && !plyr.supported.full) {
+ _setupInterface();
+ }
// Set aria title and iframe title
config.title = source.title;
@@ -2649,7 +2820,7 @@
// Listen for control events
function _controlListeners() {
// IE doesn't support input event, so we fallback to change
- var inputEvent = (plyr.browser.name == 'IE' ? 'change' : 'input');
+ var inputEvent = (plyr.browser.isIE ? 'change' : 'input');
// Click play/pause helper
function _togglePlay() {
@@ -2703,7 +2874,7 @@
for (var button in plyr.buttons) {
var element = plyr.buttons[button];
- if (element instanceof NodeList) {
+ if (_is.nodeList(element)) {
for (var i = 0; i < element.length; i++) {
_toggleClass(element[i], config.classes.tabFocus, (element[i] === focused));
}
@@ -2775,26 +2946,48 @@
// Toggle controls visibility based on mouse movement
if (config.hideControls) {
// Toggle controls on mouse events and entering fullscreen
- _on(plyr.container, 'mouseenter mouseleave mousemove enterfullscreen', _toggleControls);
+ _on(plyr.container, 'mouseenter mouseleave mousemove touchstart touchend touchcancel touchmove enterfullscreen', _toggleControls);
// Watch for cursor over controls so they don't hide when trying to interact
_on(plyr.controls, 'mouseenter mouseleave', function(event) {
- plyr.controls.active = (event.type === 'mouseenter');
+ plyr.controls.hover = event.type === 'mouseenter';
+ });
+
+ // Watch for cursor over controls so they don't hide when trying to interact
+ _on(plyr.controls, 'mousedown mouseup touchstart touchend touchcancel', function(event) {
+ plyr.controls.pressed = _inArray(['mousedown', 'touchstart'], event.type);
});
// Focus in/out on controls
_on(plyr.controls, 'focus blur', _toggleControls, true);
}
+ // Adjust volume on scroll
_on(plyr.volume.input, 'wheel', function(event) {
event.preventDefault();
+ // Detect "natural" scroll - suppored on OS X Safari only
+ // Other browsers on OS X will be inverted until support improves
+ var inverted = event.webkitDirectionInvertedFromDevice;
+
+ // Scroll down (or up on natural) to decrease
if (event.deltaY < 0 || event.deltaX > 0) {
- _increaseVolume();
+ if (inverted) {
+ _decreaseVolume();
+ }
+ else {
+ _increaseVolume();
+ }
}
+ // Scroll up (or down on natural) to increase
if (event.deltaY > 0 || event.deltaX < 0) {
- _decreaseVolume();
+ if (inverted) {
+ _increaseVolume();
+ }
+ else {
+ _decreaseVolume();
+ }
}
});
}
@@ -2860,6 +3053,11 @@
// On click play, pause ore restart
_on(wrapper, 'click', function() {
+ // Touch devices will just show controls (if we're hiding controls)
+ if (config.hideControls && plyr.browser.isTouch && !plyr.media.paused) {
+ return;
+ }
+
if (plyr.media.paused) {
_play();
}
@@ -2873,6 +3071,11 @@
});
}
+ // Disable right click
+ if (config.disableContextMenu) {
+ _on(plyr.media, 'contextmenu', function(event) { event.preventDefault(); });
+ }
+
// Proxy events to container
_on(plyr.media, config.events.join(' '), function(event) {
_triggerEvent(plyr.container, event.type, true);
@@ -2894,9 +3097,8 @@
// Set blank video src attribute
// This is to prevent a MEDIA_ERR_SRC_NOT_SUPPORTED error
- // Small mp4: https://github.com/mathiasbynens/small/blob/master/mp4.mp4
// Info: http://stackoverflow.com/questions/32231579/how-to-properly-dispose-of-an-html5-video-and-close-socket-or-connection
- plyr.media.setAttribute('src', 'data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAAGm1kYXQAAAGzABAHAAABthBgUYI9t+8AAAMNbW9vdgAAAGxtdmhkAAAAAMXMvvrFzL76AAAD6AAAACoAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAABhpb2RzAAAAABCAgIAHAE/////+/wAAAiF0cmFrAAAAXHRraGQAAAAPxcy++sXMvvoAAAABAAAAAAAAACoAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAgAAAAIAAAAAAG9bWRpYQAAACBtZGhkAAAAAMXMvvrFzL76AAAAGAAAAAEVxwAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAABaG1pbmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAAAShzdGJsAAAAxHN0c2QAAAAAAAAAAQAAALRtcDR2AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAgACABIAAAASAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGP//AAAAXmVzZHMAAAAAA4CAgE0AAQAEgICAPyARAAAAAAMNQAAAAAAFgICALQAAAbABAAABtYkTAAABAAAAASAAxI2IAMUARAEUQwAAAbJMYXZjNTMuMzUuMAaAgIABAgAAABhzdHRzAAAAAAAAAAEAAAABAAAAAQAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAASAAAAAQAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYHVkdGEAAABYbWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAraWxzdAAAACOpdG9vAAAAG2RhdGEAAAABAAAAAExhdmY1My4yMS4x');
+ plyr.media.setAttribute('src', 'https://cdn.selz.com/plyr/blank.mp4');
// Load the new empty source
// This will cancel existing requests
@@ -2967,7 +3169,7 @@
// Get the div placeholder for YouTube and Vimeo
if (!plyr.media) {
- plyr.media = plyr.container.querySelectorAll('div')[0];
+ plyr.media = plyr.container.querySelectorAll('[data-type]')[0];
}
// Bail if nothing to setup
@@ -2975,6 +3177,9 @@
return;
}
+ // Load saved settings from localStorage
+ _setupStorage();
+
// Get original classname
plyr.originalClassName = plyr.container.className;
@@ -2997,7 +3202,7 @@
}
// Check for support
- plyr.supported = api.supported(plyr.type);
+ plyr.supported = supported(plyr.type);
// Add style hook
_toggleStyleHook();
@@ -3035,6 +3240,10 @@
_play();
}
}
+ // If embed but not fully supported, setupInterface now (to avoid flash of controls)
+ else if (_inArray(config.types.embed, plyr.type) && !plyr.supported.full) {
+ _setupInterface();
+ }
// Successful setup
plyr.init = true;
@@ -3043,7 +3252,7 @@
function _setupInterface() {
// Don't setup interface if no support
if (!plyr.supported.full) {
- _log('No full support for this media type (' + plyr.type + ')', true);
+ _warn('No full support for this media type (' + plyr.type + ')');
// Remove controls
_remove(_getElement(config.selectors.controls.wrapper));
@@ -3101,7 +3310,7 @@
_displayDuration();
// Ready event
- _triggerEvent(plyr.container, 'ready');
+ _triggerEvent(plyr.container, 'ready', true);
}
// Initialize instance
@@ -3127,95 +3336,166 @@
toggleMute: _toggleMute,
toggleCaptions: _toggleCaptions,
toggleFullscreen: _toggleFullscreen,
+ toggleControls: _toggleControls,
isFullscreen: function() { return plyr.isFullscreen || false; },
support: function(mimeType) { return _supportMime(plyr, mimeType); },
destroy: _destroy,
- restore: _init
+ restore: _init,
+ getCurrentTime: function() { return plyr.media.currentTime; }
};
}
+ // Load a sprite
+ function loadSprite(url, id) {
+ var x = new XMLHttpRequest();
+
+ // If the id is set and sprite exists, bail
+ if (_is.string(id) && document.querySelector('#' + id) !== null) {
+ return;
+ }
+
+ // Check for CORS support
+ if ('withCredentials' in x) {
+ x.open('GET', url, true);
+ }
+ else {
+ return;
+ }
+
+ // Inject hidden div with sprite on load
+ x.onload = function() {
+ var c = document.createElement('div');
+ c.setAttribute('hidden', '');
+ if (_is.string(id)) {
+ c.setAttribute('id', id);
+ }
+ c.innerHTML = x.responseText;
+ document.body.insertBefore(c, document.body.childNodes[0]);
+ }
+
+ x.send();
+ }
+
// Check for support
- api.supported = function(type) {
- var browser = _browserSniff(),
- oldIE = (browser.name === 'IE' && browser.version <= 9),
- iPhone = /iPhone|iPod/i.test(navigator.userAgent),
- audio = !!document.createElement('audio').canPlayType,
- video = !!document.createElement('video').canPlayType,
+ function supported(type) {
+ var browser = _browserSniff(),
+ isOldIE = (browser.isIE && browser.version <= 9),
+ isIos = browser.isIos,
+ isIphone = /iPhone|iPod/i.test(navigator.userAgent),
+ audio = !!document.createElement('audio').canPlayType,
+ video = !!document.createElement('video').canPlayType,
basic, full;
switch (type) {
case 'video':
basic = video;
- full = (basic && (!oldIE && !iPhone));
+ full = (basic && (!isOldIE && !isIphone));
break;
case 'audio':
basic = audio;
- full = (basic && !oldIE);
+ full = (basic && !isOldIE);
break;
case 'vimeo':
case 'youtube':
case 'soundcloud':
basic = true;
- full = (!oldIE && !iPhone);
+ full = (!isOldIE && !isIos);
break;
default:
basic = (audio && video);
- full = (basic && !oldIE);
+ full = (basic && !isOldIE);
}
return {
basic: basic,
full: full
};
- };
+ }
- // Expose setup function
- api.setup = function(elements, options) {
+ // Setup function
+ function setup(targets, options) {
// Get the players
- var instances = [];
+ var elements = [],
+ containers = [],
+ selector = [defaults.selectors.html5, defaults.selectors.embed].join(',');
// Select the elements
// Assume elements is a NodeList by default
- if (typeof elements === 'string') {
- elements = document.querySelectorAll(elements);
+ if (_is.string(targets)) {
+ targets = document.querySelectorAll(targets);
}
// Single HTMLElement passed
- else if (elements instanceof HTMLElement) {
- elements = [elements];
+ else if (_is.htmlElement(targets)) {
+ targets = [targets];
}
// No selector passed, possibly options as first argument
- else if (!(elements instanceof NodeList) && typeof elements !== 'string') {
+ else if (!_is.nodeList(targets) && !_is.array(targets) && !_is.string(targets)) {
// If options are the first argument
- if (typeof options === 'undefined' && typeof elements === 'object') {
- options = elements;
+ if (_is.undefined(options) && _is.object(targets)) {
+ options = targets;
}
// Use default selector
- elements = document.querySelectorAll(defaults.selectors.container);
+ targets = document.querySelectorAll(selector);
+ }
+
+ // Convert NodeList to array
+ if (_is.nodeList(targets)) {
+ targets = Array.prototype.slice.call(targets);
}
// Bail if disabled or no basic support
// You may want to disable certain UAs etc
- if (!api.supported().basic || !elements.length) {
+ if (!supported().basic || !targets.length) {
return false;
}
+ // Check if the targets have multiple media elements
+ for (var i = 0; i < targets.length; i++) {
+ var target = targets[i];
+
+ // Get children
+ var children = target.querySelectorAll(selector);
+
+ // If there's more than one media element, wrap them
+ if (children.length > 1) {
+ for (var x = 0; x < children.length; x++) {
+ containers.push({
+ element: _wrap(children[x], document.createElement('div')),
+ original: target
+ });
+ }
+ }
+ else {
+ containers.push({
+ element: target
+ });
+ }
+ }
+
// Create a player instance for each element
- for (var i = 0; i < elements.length; i++) {
- // Get the current element
- var element = elements[i];
+ for (var key in containers) {
+ var element = containers[key].element,
+ original = containers[key].original || element;
+
+ // Wrap each media element if is target is media element
+ // as opposed to a wrapper
+ if (_matches(element, selector)) {
+ // Wrap in a <div>
+ element = _wrap(element, document.createElement('div'));
+ }
// Setup a player instance and add to the element
- if (typeof element.plyr === 'undefined') {
+ if (!('plyr' in element)) {
// Create instance-specific config
- var config = _extend(defaults, options, JSON.parse(element.getAttribute("data-plyr")));
+ var config = _extend({}, defaults, options, JSON.parse(original.getAttribute('data-plyr')));
// Bail if not enabled
if (!config.enabled) {
- return;
+ return null;
}
// Create new instance
@@ -3225,27 +3505,33 @@
element.plyr = (Object.keys(instance).length ? instance : false);
// Callback
- _triggerEvent(element, 'setup', { plyr: element.plyr });
+ _triggerEvent(original, 'setup', true, {
+ plyr: element.plyr
+ });
}
// Add to return array even if it's already setup
- instances.push(element.plyr);
+ elements.push(element);
}
- return instances;
- };
+ return elements;
+ }
- return api;
+ return {
+ setup: setup,
+ supported: supported,
+ loadSprite: loadSprite
+ };
}));
// Custom event polyfill
// https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
(function () {
if (typeof window.CustomEvent === 'function') {
- return false;
+ return;
}
- function CustomEvent (event, params) {
+ function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);