aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/js/plyr.js635
1 files changed, 360 insertions, 275 deletions
diff --git a/src/js/plyr.js b/src/js/plyr.js
index f01866bd..96b4870e 100644
--- a/src/js/plyr.js
+++ b/src/js/plyr.js
@@ -35,7 +35,7 @@
autoplay: false,
loop: false,
seekTime: 10,
- volume: 5,
+ volume: 10,
volumeMin: 0,
volumeMax: 10,
volumeStep: 1,
@@ -86,6 +86,8 @@
duration: '.plyr__time--duration'
},
classes: {
+ setup: 'plyr--setup',
+ ready: 'plyr--ready',
videoWrapper: 'plyr__video-wrapper',
embedWrapper: 'plyr__video-embed',
type: 'plyr--{0}',
@@ -168,7 +170,9 @@
fullscreen: null
},
// Events to watch on HTML5 media elements
- events: ['ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'emptied']
+ events: ['ended', 'progress', 'stalled', 'playing', 'waiting', 'canplay', 'canplaythrough', 'loadstart', 'loadeddata', 'loadedmetadata', 'timeupdate', 'volumechange', 'play', 'pause', 'error', 'seeking', 'emptied'],
+ // Logging
+ logPrefix: '[Plyr]'
};
// Credits: http://paypal.github.io/accessible-html5-video-player/
@@ -346,7 +350,7 @@
// Unwrap an element
// http://plainjs.com/javascript/manipulation/unwrap-a-dom-element-35/
- function _unwrap(wrapper) {
+ /*function _unwrap(wrapper) {
// Get the element's parent node
var parent = wrapper.parentNode;
@@ -357,7 +361,7 @@
// Remove the empty element
parent.removeChild(wrapper);
- }
+ }*/
// Remove an element
function _remove(element) {
@@ -575,25 +579,28 @@
return input !== null && typeof(input) === 'object';
},
array: function(input) {
- return input !== null && typeof(input) === 'object' && input.constructor === Array;
+ 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);
+ return input !== null && (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);
+ return input !== null && (typeof input === 'string' || (typeof input == 'object' && input.constructor === String));
},
boolean: function(input) {
- return typeof input === 'boolean';
+ return input !== null && typeof input === 'boolean';
},
nodeList: function(input) {
- return input instanceof NodeList;
+ return input !== null && input instanceof NodeList;
},
htmlElement: function(input) {
- return input instanceof HTMLElement;
+ return input !== null && input instanceof HTMLElement;
+ },
+ function: function(input) {
+ return input !== null && typeof input === 'function';
},
undefined: function(input) {
- return typeof input === 'undefined';
+ return input !== null && typeof input === 'undefined';
}
};
@@ -702,25 +709,31 @@
}
// Player instance
- function Plyr(container, config) {
- var plyr = this;
- plyr.container = container;
- plyr.timers = {};
+ function Plyr(media, config) {
+ var plyr = this,
+ timers = {};
- // Log config options
- _log(config);
+ // Set media
+ plyr.media = media;
+ var original = media.cloneNode(true);
// Debugging
- function _log() {
- if (config.debug && window.console) {
- console.log.apply(console, arguments);
- }
- }
- function _warn() {
+ function _console(type, args) {
if (config.debug && window.console) {
- console.warn.apply(console, arguments);
+ args = Array.prototype.slice.call(args);
+
+ if (_is.string(config.logPrefix) && config.logPrefix.length) {
+ args.unshift(config.logPrefix);
+ }
+
+ console[type].apply(console, args);
}
}
+ var _log = function() { _console('log', arguments) },
+ _warn = function() { _console('warn', arguments) };
+
+ // Log config options
+ _log('Config', config);
// Get icon URL
function _getIconUrl() {
@@ -1048,7 +1061,7 @@
_log('Successfully loaded the caption file via AJAX');
}
else {
- _warn('There was a problem loading the caption file via AJAX');
+ _warn(config.logPrefix + 'There was a problem loading the caption file via AJAX');
}
}
};
@@ -1076,7 +1089,7 @@
}
// Set the span content
- if (_is.undefined(caption)) {
+ if (_is.string(caption)) {
content.innerHTML = caption.trim();
}
else {
@@ -1279,24 +1292,20 @@
html = _replaceAll(html, '{id}', Math.floor(Math.random() * (10000)));
// Controls container
- var container;
+ var target;
// Inject to custom location
- if (config.selectors.controls.container !== null) {
- container = config.selectors.controls.container;
-
- if (_is.string(container)) {
- container = document.querySelector(container);
- }
+ if (_is.string(config.selectors.controls.container)) {
+ target = document.querySelector(config.selectors.controls.container);
}
// Inject into the container by default
- if (!_is.htmlElement(container)) {
- container = plyr.container
+ if (!_is.htmlElement(target)) {
+ target = plyr.container
}
// Inject controls HTML
- container.insertAdjacentHTML('beforeend', html);
+ target.insertAdjacentHTML('beforeend', html);
// Setup tooltips
if (config.tooltips.controls) {
@@ -1475,7 +1484,7 @@
_toggleClass(plyr.container, config.classes.stopped, config.autoplay);
// Add iOS class
- _toggleClass(plyr.container, config.classes.isIos, plyr.browser.isIos);
+ _toggleClass(plyr.ontainer, config.classes.isIos, plyr.browser.isIos);
// Add touch class
_toggleClass(plyr.container, config.classes.isTouch, plyr.browser.isTouch);
@@ -1497,9 +1506,6 @@
// Embeds
if (_inArray(config.types.embed, plyr.type)) {
_setupEmbed();
-
- // Clean up
- plyr.embedId = null;
}
}
@@ -1611,9 +1617,6 @@
// When embeds are ready
function _embedReady() {
- // Store reference to API
- plyr.container.plyr.embed = plyr.embed;
-
// Setup the UI if full support
if (plyr.supported.full) {
_setupInterface();
@@ -1625,12 +1628,6 @@
// Handle YouTube API ready
function _youTubeReady(videoId, container) {
- // Setup timers object
- // We have to poll YouTube for updates
- if (!('timer' in plyr)) {
- plyr.timer = {};
- }
-
// Setup instance
// https://developers.google.com/youtube/iframe_api_reference
plyr.embed = new window.YT.Player(container.id, {
@@ -1650,7 +1647,7 @@
},
events: {
'onError': function(event) {
- _triggerEvent(plyr.container, 'error', true, {
+ _triggerEvent(container, 'error', true, {
code: event.data,
embed: event.target
});
@@ -1680,32 +1677,37 @@
// Set title
config.title = instance.getVideoData().title;
+ // Update UI
+ _embedReady();
+
// Trigger timeupdate
_triggerEvent(plyr.media, 'timeupdate');
// Reset timer
- window.clearInterval(plyr.timer.buffering);
+ window.clearInterval(timers.buffering);
// Setup buffering
- plyr.timer.buffering = window.setInterval(function() {
+ timers.buffering = window.setInterval(function() {
// Get loaded % from YouTube
plyr.media.buffered = instance.getVideoLoadedFraction();
- // Trigger progress
- _triggerEvent(plyr.media, 'progress');
+ // Trigger progress only when we actually buffer something
+ if (plyr.media.lastBuffered === null || plyr.media.lastBuffered < plyr.media.buffered) {
+ _triggerEvent(plyr.media, 'progress');
+ }
+
+ // Set last buffer point
+ plyr.media.lastBuffered = plyr.media.buffered;
// Bail if we're at 100%
if (plyr.media.buffered === 1) {
- window.clearInterval(plyr.timer.buffering);
+ window.clearInterval(timers.buffering);
// Trigger event
_triggerEvent(plyr.media, 'canplaythrough');
}
}, 200);
- // Update UI
- _embedReady();
-
// Display duration if available
_displayDuration();
},
@@ -1714,7 +1716,7 @@
var instance = event.target;
// Reset timer
- window.clearInterval(plyr.timer.playing);
+ window.clearInterval(timers.playing);
// Handle events
// -1 Unstarted
@@ -1736,7 +1738,7 @@
_triggerEvent(plyr.media, 'playing');
// Poll to get playback progress
- plyr.timer.playing = window.setInterval(function() {
+ timers.playing = window.setInterval(function() {
// Set the current time
plyr.media.currentTime = instance.getCurrentTime();
@@ -1752,7 +1754,7 @@
break;
}
- _triggerEvent(plyr.container, 'statechange', false, {
+ _triggerEvent(container, 'statechange', false, {
code: event.data
});
}
@@ -1786,13 +1788,14 @@
plyr.embed.stop();
plyr.media.paused = true;
};
+
plyr.media.paused = true;
plyr.media.currentTime = 0;
// Update UI
_embedReady();
- plyr.embed.getCurrentTime().then(function (value) {
+ plyr.embed.getCurrentTime().then(function(value) {
plyr.media.currentTime = value;
// Trigger timeupdate
@@ -1811,12 +1814,14 @@
plyr.embed.enableTextTrack('en');
}*/
- // Fix keyboard focus issues
- // https://github.com/Selz/plyr/issues/317
plyr.embed.on('loaded', function() {
+ // Fix keyboard focus issues
+ // https://github.com/Selz/plyr/issues/317
if(_is.htmlElement(plyr.embed.element) && plyr.supported.full) {
plyr.embed.element.setAttribute('tabindex', '-1');
}
+
+ //console.log(plyr.embed);
});
plyr.embed.on('play', function() {
@@ -1858,7 +1863,7 @@
plyr.embed = window.SC.Widget(this);
// Setup on ready
- plyr.embed.bind(window.SC.Widget.Events.READY, function() {
+ plyr.embed.bind(window.SC.Widget.Events.READY, function() {
// Create a faux HTML5 API using the Soundcloud API
plyr.media.play = function() {
plyr.embed.play();
@@ -1873,6 +1878,7 @@
plyr.embed.pause();
plyr.media.paused = true;
};
+
plyr.media.paused = true;
plyr.media.currentTime = 0;
@@ -2333,10 +2339,10 @@
var loading = (event.type === 'waiting');
// Clear timer
- clearTimeout(plyr.timers.loading);
+ clearTimeout(timers.loading);
// Timer to prevent flicker when seeking
- plyr.timers.loading = setTimeout(function() {
+ timers.loading = setTimeout(function() {
_toggleClass(plyr.container, config.classes.loading, loading);
}, (loading ? 250 : 0));
}
@@ -2566,7 +2572,8 @@
// Show the player controls in fullscreen mode
function _toggleControls(toggle) {
- if (!config.hideControls || plyr.type === 'audio') {
+ // Don't hide if config says not to, it's audio, or not loaded/ready
+ if (!config.hideControls || plyr.type === 'audio' || !_hasClass(plyr.container, config.classes.ready)) {
return;
}
@@ -2599,7 +2606,7 @@
}
// Clear timer every movement
- window.clearTimeout(plyr.timers.hover);
+ window.clearTimeout(timers.hover);
// If the mouse is not over the controls, set a timeout to hide them
if (show || plyr.media.paused) {
@@ -2619,7 +2626,7 @@
// 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() {
+ timers.hover = window.setTimeout(function() {
// If the mouse is over the controls (and not entering fullscreen), bail
if ((plyr.controls.pressed || plyr.controls.hover) && !isEnterFullscreen) {
return;
@@ -2685,129 +2692,125 @@
// Cancel current network requests
_cancelRequests();
- // Clean up YouTube stuff
- if (plyr.type === 'youtube') {
- // Destroy the embed instance
- plyr.embed.destroy();
+ // Destroy instance adn wait for callback
+ // Vimeo throws a wobbly if you don't wait
+ _destroy(setup, false);
- // Clear timer
- window.clearInterval(plyr.timer.buffering);
- window.clearInterval(plyr.timer.playing);
- }
- // HTML5 Video
- else if (plyr.type === 'video' && plyr.videoContainer) {
- // Remove video wrapper
- _remove(plyr.videoContainer);
- }
+ // Setup new source
+ function setup() {
+ // Remove embed object
+ plyr.embed = null;
+
+ // Remove video container
+ if (plyr.type === 'video' && plyr.videoContainer) {
+ _remove(plyr.videoContainer);
+ }
- // Remove embed object
- plyr.embed = null;
+ // Remove the old media
+ _remove(plyr.media);
- // Remove the old media
- _remove(plyr.media);
+ // Reset class name
+ if (plyr.container) {
+ plyr.container.removeAttribute('class');
+ }
- // Set the type
- if ('type' in source) {
- plyr.type = source.type;
+ // Set the type
+ if ('type' in source) {
+ plyr.type = source.type;
- // Get child type for video (it might be an embed)
- if (plyr.type === 'video') {
- var firstSource = source.sources[0];
+ // Get child type for video (it might be an embed)
+ if (plyr.type === 'video') {
+ var firstSource = source.sources[0];
- if ('type' in firstSource && _inArray(config.types.embed, firstSource.type)) {
- plyr.type = firstSource.type;
+ if ('type' in firstSource && _inArray(config.types.embed, firstSource.type)) {
+ plyr.type = firstSource.type;
+ }
}
}
- }
- // Check for support
- plyr.supported = supported(plyr.type);
-
- // Create new markup
- switch(plyr.type) {
- case 'video':
- plyr.media = document.createElement('video');
- break;
+ // Check for support
+ plyr.supported = supported(plyr.type);
- case 'audio':
- plyr.media = document.createElement('audio');
- break;
+ // Create new markup
+ switch(plyr.type) {
+ case 'video':
+ plyr.media = document.createElement('video');
+ break;
- case 'youtube':
- case 'vimeo':
- case 'soundcloud':
- plyr.media = document.createElement('div');
- plyr.embedId = source.sources[0].src;
- break;
- }
+ case 'audio':
+ plyr.media = document.createElement('audio');
+ break;
- // Inject the new element
- _prependChild(plyr.container, plyr.media);
+ case 'youtube':
+ case 'vimeo':
+ case 'soundcloud':
+ plyr.media = document.createElement('div');
+ plyr.embedId = source.sources[0].src;
+ break;
+ }
- // Autoplay the new source?
- if (_is.boolean(source.autoplay)) {
- config.autoplay = source.autoplay;
- }
+ // Inject the new element
+ _prependChild(plyr.container, plyr.media);
- // Set attributes for audio video
- if (_inArray(config.types.html5, plyr.type)) {
- if (config.crossorigin) {
- plyr.media.setAttribute('crossorigin', '');
+ // Autoplay the new source?
+ if (_is.boolean(source.autoplay)) {
+ config.autoplay = source.autoplay;
}
- if (config.autoplay) {
- plyr.media.setAttribute('autoplay', '');
- }
- if ('poster' in source) {
- plyr.media.setAttribute('poster', source.poster);
- }
- if (config.loop) {
- plyr.media.setAttribute('loop', '');
+
+ // Set attributes for audio and video
+ if (_inArray(config.types.html5, plyr.type)) {
+ if (config.crossorigin) {
+ plyr.media.setAttribute('crossorigin', '');
+ }
+ if (config.autoplay) {
+ plyr.media.setAttribute('autoplay', '');
+ }
+ if ('poster' in source) {
+ plyr.media.setAttribute('poster', source.poster);
+ }
+ if (config.loop) {
+ plyr.media.setAttribute('loop', '');
+ }
}
- }
- // Classname reset
- plyr.container.className = plyr.originalClassName;
+ // Restore class hooks
+ _toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen);
+ _toggleClass(plyr.container, config.classes.captions.active, plyr.captionsEnabled);
+ _toggleStyleHook();
- // Restore class hooks
- _toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen);
- _toggleClass(plyr.container, config.classes.captions.active, plyr.captionsEnabled);
- _toggleStyleHook();
+ // Set new sources for html5
+ if (_inArray(config.types.html5, plyr.type)) {
+ _insertChildElements('source', source.sources);
+ }
- // Set new sources for html5
- if (_inArray(config.types.html5, plyr.type)) {
- _insertChildElements('source', source.sources);
- }
+ // Set up from scratch
+ _setupMedia();
- // Set up from scratch
- _setupMedia();
+ // HTML5 stuff
+ if (_inArray(config.types.html5, plyr.type)) {
+ // Setup captions
+ if ('tracks' in source) {
+ _insertChildElements('track', source.tracks);
+ }
- // HTML5 stuff
- if (_inArray(config.types.html5, plyr.type)) {
- // Setup captions
- if ('tracks' in source) {
- _insertChildElements('track', source.tracks);
- }
+ // Load HTML5 sources
+ plyr.media.load();
- // Load HTML5 sources
- plyr.media.load();
+ // Setup interface
+ _setupInterface();
- // Setup interface
- _setupInterface();
+ // 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();
+ }
- // 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;
+ _setTitle();
}
-
- // Set aria title and iframe title
- config.title = source.title;
- _setTitle();
-
- // Reset media objects
- plyr.container.plyr.media = plyr.media;
}
// Update poster
@@ -3106,49 +3109,79 @@
plyr.media.load();
// Debugging
- _log("Cancelled network requests for old media");
+ _log('Cancelled network requests for old media');
}
// Destroy an instance
// Event listeners are removed when elements are removed
// http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory
- function _destroy() {
+ function _destroy(callback, restore) {
// Bail if the element is not initialized
if (!plyr.init) {
return null;
}
- // Reset container classname
- plyr.container.setAttribute('class', _getClassname(config.selectors.container));
+ // Type specific stuff
+ switch (plyr.type) {
+ case 'youtube':
+ // Clear timers
+ window.clearInterval(timers.buffering);
+ window.clearInterval(timers.playing);
- // Remove init flag
- plyr.init = false;
+ // Destroy YouTube API
+ plyr.embed.destroy();
- // Remove controls
- _remove(_getElement(config.selectors.controls.wrapper));
+ // Clean up
+ cleanUp();
+
+ break;
- // YouTube
- if (plyr.type === 'youtube') {
- plyr.embed.destroy();
- return;
- }
+ case 'vimeo':
+ // Destroy Vimeo API
+ // then clean up (wait, to prevent postmessage errors)
+ plyr.embed.unload().then(cleanUp);
- // If video, we need to remove some more
- if (plyr.type === 'video') {
- // Remove captions container
- _remove(_getElement(config.selectors.captions));
+ // Vimeo does not always return
+ window.setTimeout(cleanUp, 200);
- // Remove video wrapper
- _unwrap(plyr.videoContainer);
+ break;
+
+ case 'video':
+ case 'audio':
+ // Restore native video controls
+ _toggleNativeControls(true);
+
+ // Clean up
+ cleanUp();
+
+ break;
}
- // Restore native video controls
- _toggleNativeControls(true);
+ function cleanUp() {
+ // Default to restore original element
+ if (!_is.boolean(restore)) {
+ restore = true;
+ }
+
+ // Callback
+ if (_is.function(callback)) {
+ callback.call(original);
+ }
+
+ // Bail if we don't need to restore the original element
+ if (!restore) {
+ return;
+ }
+
+ // Remove init flag
+ plyr.init = false;
+
+ // Replace the container with the original element provided
+ plyr.container.parentNode.replaceChild(original, plyr.container);
- // Clone the media element to remove listeners
- // http://stackoverflow.com/questions/19469881/javascript-remove-all-event-listeners-of-specific-type
- var clone = plyr.media.cloneNode(true);
- plyr.media.parentNode.replaceChild(clone, plyr.media);
+ // Event
+ _triggerEvent(original, 'destroyed', true);
+ }
}
// Setup a player
@@ -3164,71 +3197,54 @@
// Sniff out the browser
plyr.browser = _browserSniff();
- // Get the media element
- plyr.media = plyr.container.querySelectorAll('audio, video')[0];
-
- // Get the div placeholder for YouTube and Vimeo
- if (!plyr.media) {
- plyr.media = plyr.container.querySelectorAll('[data-type]')[0];
- }
-
// Bail if nothing to setup
- if (!plyr.media) {
+ if (!_is.htmlElement(plyr.media)) {
return;
}
// Load saved settings from localStorage
_setupStorage();
- // Get original classname
- plyr.originalClassName = plyr.container.className;
-
// Set media type based on tag or data attribute
// Supported: video, audio, vimeo, youtube
- var tagName = plyr.media.tagName.toLowerCase();
+ var tagName = media.tagName.toLowerCase();
if (tagName === 'div') {
- plyr.type = plyr.media.getAttribute('data-type');
- plyr.embedId = plyr.media.getAttribute('data-video-id');
+ plyr.type = media.getAttribute('data-type');
+ plyr.embedId = media.getAttribute('data-video-id');
// Clean up
- plyr.media.removeAttribute('data-type');
- plyr.media.removeAttribute('data-video-id');
+ media.removeAttribute('data-type');
+ media.removeAttribute('data-video-id');
}
else {
plyr.type = tagName;
- config.crossorigin = (plyr.media.getAttribute('crossorigin') !== null);
- config.autoplay = (config.autoplay || (plyr.media.getAttribute('autoplay') !== null));
- config.loop = (config.loop || (plyr.media.getAttribute('loop') !== null));
+ config.crossorigin = (media.getAttribute('crossorigin') !== null);
+ config.autoplay = (config.autoplay || (media.getAttribute('autoplay') !== null));
+ config.loop = (config.loop || (media.getAttribute('loop') !== null));
}
// Check for support
plyr.supported = supported(plyr.type);
- // Add style hook
- _toggleStyleHook();
-
// If no native support, bail
if (!plyr.supported.basic) {
- return false;
+ return;
}
+ // Wrap media
+ plyr.container = _wrap(media, document.createElement('div'));
+
+ // Add style hook
+ _toggleStyleHook();
+
// Debug info
- _log(plyr.browser.name + ' ' + plyr.browser.version);
+ _log('' + plyr.browser.name + ' ' + plyr.browser.version);
// Setup media
_setupMedia();
// Setup interface
if (_inArray(config.types.html5, plyr.type)) {
- // Bail if no support
- if (!plyr.supported.full) {
- // Successful setup
- plyr.init = true;
-
- // Don't inject controls if no full support
- return;
- }
-
// Setup UI
_setupInterface();
@@ -3252,7 +3268,7 @@
function _setupInterface() {
// Don't setup interface if no support
if (!plyr.supported.full) {
- _warn('No full support for this media type (' + plyr.type + ')');
+ _warn('Basic support only', plyr.type);
// Remove controls
_remove(_getElement(config.selectors.controls.wrapper));
@@ -3311,6 +3327,9 @@
// Ready event
_triggerEvent(plyr.container, 'ready', true);
+
+ // Class
+ _toggleClass(plyr.container, config.classes.ready, true);
}
// Initialize instance
@@ -3322,9 +3341,15 @@
}
return {
- media: plyr.media,
+ getContainer: function() { return plyr.container },
+ getEmbed: function() { return plyr.embed; },
+ getMedia: function() { return plyr.media; },
+ getType: function() { return plyr.type; },
+ isReady: function() { return _hasClass(plyr.container, config.classes.ready); },
+ on: function(event, callback) { _on(plyr.container, event, callback); },
play: _play,
pause: _pause,
+ stop: function() { _pause(); _seek(); },
restart: _seek,
rewind: _rewind,
forward: _forward,
@@ -3340,8 +3365,7 @@
isFullscreen: function() { return plyr.isFullscreen || false; },
support: function(mimeType) { return _supportMime(plyr, mimeType); },
destroy: _destroy,
- restore: _init,
- getCurrentTime: function() { return plyr.media.currentTime; }
+ getCurrentTime: function() { return media.currentTime; }
};
}
@@ -3350,10 +3374,18 @@
var x = new XMLHttpRequest();
// If the id is set and sprite exists, bail
- if (_is.string(id) && document.querySelector('#' + id) !== null) {
+ if (_is.string(id) && _is.htmlElement(document.querySelector('#' + id))) {
return;
}
+ // Create placeholder (to prevent loading twice)
+ var c = document.createElement('div');
+ c.setAttribute('hidden', '');
+ if (_is.string(id)) {
+ c.setAttribute('id', id);
+ }
+ document.body.insertBefore(c, document.body.childNodes[0]);
+
// Check for CORS support
if ('withCredentials' in x) {
x.open('GET', url, true);
@@ -3364,13 +3396,7 @@
// 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();
@@ -3418,8 +3444,8 @@
// Setup function
function setup(targets, options) {
// Get the players
- var elements = [],
- containers = [],
+ var players = [],
+ instances = [],
selector = [defaults.selectors.html5, defaults.selectors.embed].join(',');
// Select the elements
@@ -3453,6 +3479,20 @@
return false;
}
+ // Add to container list
+ function add(target, media) {
+ if (!_hasClass(media, defaults.classes.hook)) {
+ players.push({
+ // Always wrap in a <div> for styling
+ //container: _wrap(media, document.createElement('div')),
+ // Could be a container or the media itself
+ target: target,
+ // This should be the <video>, <audio> or <div> (YouTube/Vimeo)
+ media: media
+ });
+ }
+ }
+
// Check if the targets have multiple media elements
for (var i = 0; i < targets.length; i++) {
var target = targets[i];
@@ -3460,67 +3500,112 @@
// Get children
var children = target.querySelectorAll(selector);
- // If there's more than one media element, wrap them
- if (children.length > 1) {
+ // If there's more than one media element child, wrap them
+ if (children.length) {
for (var x = 0; x < children.length; x++) {
- containers.push({
- element: _wrap(children[x], document.createElement('div')),
- original: target
- });
+ add(target, children[x]);
}
}
- else {
- containers.push({
- element: target
- });
+ // Wrap target if it's a media element
+ else if (_matches(target, selector)) {
+ add(target, target);
}
}
// Create a player instance for each element
- for (var key in containers) {
- var element = containers[key].element,
- original = containers[key].original || element;
+ players.forEach(function(player) {
+ var element = player.target,
+ media = player.media,
+ match = false;
- // 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'));
+ // The target element can also be the media element
+ if (media === element) {
+ match = true;
}
// Setup a player instance and add to the element
- if (!('plyr' in element)) {
- // Create instance-specific config
- var config = _extend({}, defaults, options, JSON.parse(original.getAttribute('data-plyr')));
+ // Create instance-specific config
+ var data = {};
- // Bail if not enabled
- if (!config.enabled) {
- return null;
- }
+ // Try parsing data attribute config
+ try { data = JSON.parse(element.getAttribute('data-plyr')); }
+ catch(e) { }
- // Create new instance
- var instance = new Plyr(element, config);
+ var config = _extend({}, defaults, options, data);
- // Set plyr to false if setup failed
- element.plyr = (Object.keys(instance).length ? instance : false);
+ // Bail if not enabled
+ if (!config.enabled) {
+ return null;
+ }
- // Callback
- _triggerEvent(original, 'setup', true, {
- plyr: element.plyr
+ // Create new instance
+ var instance = new Plyr(media, config);
+
+ // Go to next if setup failed
+ if (!_is.object(instance)) {
+ return;
+ }
+
+ // Set plyr to false if setup failed
+ // Maybe we remove this and add a .get() function to get the instance
+ // If passed a media element or container?
+ media.plyr = instance;
+
+ // Set class hook
+ _toggleClass(media, defaults.classes.setup, true);
+
+ // Listen for events if debugging
+ if (config.debug) {
+ var events = config.events.concat(['setup', 'ready', 'statechange', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled']);
+
+ _on(instance.getContainer(), events.join(' '), function() {
+ console.log([config.logPrefix, 'event:', event.type].join(' '));
});
}
+ // Callback
+ _triggerEvent(instance.getContainer(), 'setup', true, {
+ plyr: instance
+ });
+
// Add to return array even if it's already setup
- elements.push(element);
+ instances.push(instance);
+ });
+
+ return instances;
+ }
+
+ // Get all instances within a provided container
+ function get(container) {
+ // Get selector if string passed
+ if (_is.string(container)) {
+ container = document.querySelector(container);
+ }
+ // Use body by default to get all on page
+ else if (_is.undefined(container)) {
+ container = document.body;
+ }
+
+ // If we have a HTML element
+ if (_is.htmlElement(container)) {
+ var elements = container.querySelectorAll('.' + defaults.classes.setup),
+ instances = [];
+
+ Array.prototype.slice.call(elements).forEach(function(element) {
+ instances.push(element.plyr);
+ });
+
+ return instances;
}
- return elements;
+ return [];
}
return {
setup: setup,
supported: supported,
- loadSprite: loadSprite
+ loadSprite: loadSprite,
+ get: get
};
}));