aboutsummaryrefslogtreecommitdiffstats
path: root/src/js
diff options
context:
space:
mode:
authorSam Potts <me@sampotts.me>2017-11-23 17:35:35 +1100
committerSam Potts <me@sampotts.me>2017-11-23 17:35:35 +1100
commit921cefd212f65290349aa1d9d533c95cb1f6fcce (patch)
tree69c58f43d154440d9f327fb5028372a488522fe2 /src/js
parentde6f0f1b778180f7b26f85f45053ffb97eb526af (diff)
downloadplyr-921cefd212f65290349aa1d9d533c95cb1f6fcce.tar.lz
plyr-921cefd212f65290349aa1d9d533c95cb1f6fcce.tar.xz
plyr-921cefd212f65290349aa1d9d533c95cb1f6fcce.zip
Moved to provider + type to make it cleaner in future, fix for multiple players
Diffstat (limited to 'src/js')
-rw-r--r--src/js/captions.js6
-rw-r--r--src/js/controls.js2
-rw-r--r--src/js/defaults.js3
-rw-r--r--src/js/fullscreen.js2
-rw-r--r--src/js/listeners.js4
-rw-r--r--src/js/media.js9
-rw-r--r--src/js/plugins/vimeo.js5
-rw-r--r--src/js/plugins/youtube.js5
-rw-r--r--src/js/plyr.js101
-rw-r--r--src/js/source.js31
-rw-r--r--src/js/support.js18
-rw-r--r--src/js/types.js16
-rw-r--r--src/js/ui.js2
-rw-r--r--src/js/utils.js27
14 files changed, 123 insertions, 108 deletions
diff --git a/src/js/captions.js b/src/js/captions.js
index ed1dab8b..61ed80bf 100644
--- a/src/js/captions.js
+++ b/src/js/captions.js
@@ -32,7 +32,7 @@ const captions = {
}
// Only Vimeo and HTML5 video supported at this point
- if (!['video', 'vimeo'].includes(this.type) || (this.type === 'video' && !support.textTracks)) {
+ if (!this.isVideo || this.isYouTube || (this.isVideo && !support.textTracks)) {
// Clear menu and hide
if (this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
controls.setCaptionsMenu.call(this);
@@ -71,7 +71,7 @@ const captions = {
// Set the captions language
setLanguage() {
// Setup HTML5 track rendering
- if (this.type === 'video') {
+ if (this.isVideo) {
captions.getTracks.call(this).forEach(track => {
// Remove previous bindings
utils.on(track, 'cuechange', event => captions.setCue.call(this, event));
@@ -91,7 +91,7 @@ const captions = {
captions.setCue.call(this, currentTrack);
}
}
- } else if (this.type === 'vimeo' && this.captions.active) {
+ } else if (this.isVimeo && this.captions.active) {
this.embed.enableTextTrack(this.language);
}
},
diff --git a/src/js/controls.js b/src/js/controls.js
index e7248f66..2adea50f 100644
--- a/src/js/controls.js
+++ b/src/js/controls.js
@@ -445,7 +445,7 @@ const controls = {
}
// Toggle the pane and tab
- const toggle = !utils.is.empty(this.options.quality) && this.type === 'youtube';
+ const toggle = !utils.is.empty(this.options.quality) && this.isYouTube;
controls.toggleTab.call(this, type, toggle);
// If we're hiding, nothing more to do
diff --git a/src/js/defaults.js b/src/js/defaults.js
index 77bc2457..1a0faeed 100644
--- a/src/js/defaults.js
+++ b/src/js/defaults.js
@@ -13,7 +13,7 @@ const defaults = {
autoplay: false,
// Only allow one media playing at once (vimeo only)
- autopause: false,
+ autopause: true,
// Default time to skip when rewind/fast forward
seekTime: 10,
@@ -267,6 +267,7 @@ const defaults = {
embed: 'plyr__video-embed',
control: 'plyr__control',
type: 'plyr--{0}',
+ provider: 'plyr--{0}',
stopped: 'plyr--stopped',
playing: 'plyr--playing',
loading: 'plyr--loading',
diff --git a/src/js/fullscreen.js b/src/js/fullscreen.js
index 01703659..ac65c2bf 100644
--- a/src/js/fullscreen.js
+++ b/src/js/fullscreen.js
@@ -92,7 +92,7 @@ const fullscreen = {
// Setup fullscreen
setup() {
- if (!this.supported.ui || this.type === 'audio' || !this.config.fullscreen.enabled) {
+ if (!this.supported.ui || this.isAudio || !this.config.fullscreen.enabled) {
return;
}
diff --git a/src/js/listeners.js b/src/js/listeners.js
index 8f95b1a7..7f64375c 100644
--- a/src/js/listeners.js
+++ b/src/js/listeners.js
@@ -221,7 +221,7 @@ const listeners = {
// Handle the media finishing
utils.on(this.media, 'ended', () => {
// Show poster on end
- if (this.type === 'video' && this.config.showPosterOnEnd) {
+ if (this.isHTML5 && this.isVideo && this.config.showPosterOnEnd) {
// Restart
this.restart();
@@ -243,7 +243,7 @@ const listeners = {
utils.on(this.media, 'stalled waiting canplay seeked playing', event => ui.checkLoading.call(this, event));
// Click video
- if (this.supported.ui && this.config.clickToPlay && this.type !== 'audio') {
+ if (this.supported.ui && this.config.clickToPlay && !this.isAudio) {
// Re-fetch the wrapper
const wrapper = utils.getElement.call(this, `.${this.config.classNames.video}`);
diff --git a/src/js/media.js b/src/js/media.js
index 8c0559ac..9eea9a5d 100644
--- a/src/js/media.js
+++ b/src/js/media.js
@@ -23,6 +23,9 @@ const media = {
// Add type class
utils.toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', this.type), true);
+ // Add provider class
+ utils.toggleClass(this.elements.container, this.config.classNames.provider.replace('{0}', this.provider), true);
+
// Add video class for embeds
// This will require changes if audio embeds are added
if (this.isEmbed) {
@@ -31,7 +34,7 @@ const media = {
if (this.supported.ui) {
// Check for picture-in-picture support
- utils.toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.type === 'video');
+ utils.toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo);
// Check for airplay support
utils.toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5);
@@ -47,7 +50,7 @@ const media = {
}
// Inject the player wrapper
- if (['video', 'youtube', 'vimeo'].includes(this.type)) {
+ if (this.isVideo || this.isYouTube || this.isVimeo) {
// Create the wrapper div
this.elements.wrapper = utils.createElement('div', {
class: this.config.classNames.video,
@@ -58,7 +61,7 @@ const media = {
}
if (this.isEmbed) {
- switch (this.type) {
+ switch (this.provider) {
case 'youtube':
youtube.setup.call(this);
break;
diff --git a/src/js/plugins/vimeo.js b/src/js/plugins/vimeo.js
index 6c32302a..21d34beb 100644
--- a/src/js/plugins/vimeo.js
+++ b/src/js/plugins/vimeo.js
@@ -4,13 +4,12 @@
import utils from './../utils';
import captions from './../captions';
-import controls from './../controls';
import ui from './../ui';
const vimeo = {
setup() {
// Remove old containers
- const containers = utils.getElements.call(this, `[id^="${this.type}-"]`);
+ const containers = utils.getElements.call(this, `[id^="${this.provider}-"]`);
Array.from(containers).forEach(utils.removeElement);
// Add embed class for responsive
@@ -20,7 +19,7 @@ const vimeo = {
vimeo.setAspectRatio.call(this);
// Set ID
- this.media.setAttribute('id', utils.generateId(this.type));
+ this.media.setAttribute('id', utils.generateId(this.provider));
// Load the API if not already
if (!utils.is.object(window.Vimeo)) {
diff --git a/src/js/plugins/youtube.js b/src/js/plugins/youtube.js
index ce5b46e1..cf529fba 100644
--- a/src/js/plugins/youtube.js
+++ b/src/js/plugins/youtube.js
@@ -11,7 +11,7 @@ const youtube = {
const videoId = utils.parseYouTubeId(this.embedId);
// Remove old containers
- const containers = utils.getElements.call(this, `[id^="${this.type}-"]`);
+ const containers = utils.getElements.call(this, `[id^="${this.provider}-"]`);
Array.from(containers).forEach(utils.removeElement);
// Add embed class for responsive
@@ -21,7 +21,7 @@ const youtube = {
youtube.setAspectRatio.call(this);
// Set ID
- this.media.setAttribute('id', utils.generateId(this.type));
+ this.media.setAttribute('id', utils.generateId(this.provider));
// Setup API
if (utils.is.object(window.YT)) {
@@ -31,6 +31,7 @@ const youtube = {
utils.loadScript(this.config.urls.youtube.api);
// Setup callback for the API
+ // YouTube has it's own system of course...
window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || [];
// Add to queue
diff --git a/src/js/plyr.js b/src/js/plyr.js
index ea6d3dec..54734fde 100644
--- a/src/js/plyr.js
+++ b/src/js/plyr.js
@@ -5,8 +5,8 @@
// License: The MIT License (MIT)
// ==========================================================================
+import { providers, types } from './types';
import defaults from './defaults';
-import types from './types';
import support from './support';
import utils from './utils';
@@ -40,11 +40,7 @@ class Plyr {
}
// jQuery, NodeList or Array passed, use first element
- if (
- (window.jQuery && this.media instanceof jQuery) ||
- utils.is.nodeList(this.media) ||
- utils.is.array(this.media)
- ) {
+ if ((window.jQuery && this.media instanceof jQuery) || utils.is.nodeList(this.media) || utils.is.array(this.media)) {
// eslint-disable-next-line
this.media = this.media[0];
}
@@ -149,7 +145,7 @@ class Plyr {
// Embed attributes
const attributes = {
provider: 'data-plyr-provider',
- id: 'data-plyr-provider-id',
+ id: 'data-plyr-embed-id',
};
// Different setup based on type
@@ -157,16 +153,18 @@ class Plyr {
// TODO: Handle passing an iframe for true progressive enhancement
// case 'iframe':
case 'div':
- this.type = this.media.getAttribute(attributes.provider);
+ this.type = types.video; // Audio will come later for external providers
+ this.provider = this.media.getAttribute(attributes.provider);
this.embedId = this.media.getAttribute(attributes.id);
- if (utils.is.empty(this.type)) {
- this.console.error('Setup failed: embed type missing');
+ if (utils.is.empty(this.provider) || !Object.keys(providers).includes(this.provider)) {
+ this.console.error('Setup failed: Invalid provider');
return;
}
+ // Try and get the embed id
if (utils.is.empty(this.embedId)) {
- this.console.error('Setup failed: video id missing');
+ this.console.error('Setup failed: Embed ID or URL missing');
return;
}
@@ -179,19 +177,24 @@ class Plyr {
case 'video':
case 'audio':
this.type = type;
+ this.provider = providers.html5;
if (this.media.hasAttribute('crossorigin')) {
this.config.crossorigin = true;
}
+
if (this.media.hasAttribute('autoplay')) {
this.config.autoplay = true;
}
+
if (this.media.hasAttribute('playsinline')) {
this.config.inline = true;
}
+
if (this.media.hasAttribute('muted')) {
this.config.muted = true;
}
+
if (this.media.hasAttribute('loop')) {
this.config.loop.active = true;
}
@@ -207,7 +210,7 @@ class Plyr {
storage.setup.call(this);
// Check for support again but with type
- this.supported = support.check(this.type, this.config.inline);
+ this.supported = support.check(this.type, this.provider, this.config.inline);
// If no support for even API, bail
if (!this.supported.api) {
@@ -253,17 +256,25 @@ class Plyr {
// ---------------------------------------
/**
- * If the player is HTML5
+ * Types and provider helpers
*/
get isHTML5() {
- return types.html5.includes(this.type);
+ return this.provider === providers.html5;
}
-
- /**
- * If the player is an embed - e.g. YouTube or Vimeo
- */
get isEmbed() {
- return types.embed.includes(this.type);
+ return this.isYouTube || this.isVimeo;
+ }
+ get isYouTube() {
+ return this.provider === providers.youtube;
+ }
+ get isVimeo() {
+ return this.provider === providers.vimeo;
+ }
+ get isVideo() {
+ return this.type === types.video;
+ }
+ get isAudio() {
+ return this.type === types.audio;
}
/**
@@ -518,11 +529,7 @@ class Plyr {
}
// Get audio tracks
- return (
- this.media.mozHasAudio ||
- Boolean(this.media.webkitAudioDecodedByteCount) ||
- Boolean(this.media.audioTracks && this.media.audioTracks.length)
- );
+ return this.media.mozHasAudio || Boolean(this.media.webkitAudioDecodedByteCount) || Boolean(this.media.audioTracks && this.media.audioTracks.length);
}
/**
@@ -683,7 +690,7 @@ class Plyr {
* @param {input} - the URL for the new poster image
*/
set poster(input) {
- if (!this.isHTML5 || this.type !== 'video') {
+ if (!this.isHTML5 || !this.isVideo) {
this.console.warn('Poster can only be set on HTML5 video');
return;
}
@@ -697,7 +704,7 @@ class Plyr {
* Get the current poster image
*/
get poster() {
- if (!this.isHTML5 || this.type !== 'video') {
+ if (!this.isHTML5 || !this.isVideo) {
return null;
}
@@ -731,9 +738,7 @@ class Plyr {
}
// If the method is called without parameter, toggle based on current value
- const show = utils.is.boolean(input)
- ? input
- : this.elements.container.className.indexOf(this.config.classNames.captions.active) === -1;
+ const show = utils.is.boolean(input) ? input : this.elements.container.className.indexOf(this.config.classNames.captions.active) === -1;
// Nothing to change...
if (this.captions.enabled === show) {
@@ -828,11 +833,7 @@ class Plyr {
this.fullscreen.active = !this.fullscreen.active;
// Add class hook
- utils.toggleClass(
- this.elements.container,
- this.config.classNames.fullscreen.fallback,
- this.fullscreen.active
- );
+ utils.toggleClass(this.elements.container, this.config.classNames.fullscreen.fallback, this.fullscreen.active);
// Make sure we don't lose scroll position
if (this.fullscreen.active) {
@@ -920,7 +921,7 @@ class Plyr {
}
// Don't hide if no UI support or it's audio
- if (!this.supported.ui || this.type === 'audio') {
+ if (!this.supported.ui || this.isAudio) {
return this;
}
@@ -980,13 +981,13 @@ class Plyr {
// then set the timer to hide the controls
if (!show || this.playing) {
this.timers.controls = window.setTimeout(() => {
- console.warn({
+ /* this.console.warn({
pressed: this.elements.controls.pressed,
hover: this.elements.controls.pressed,
playing: this.playing,
paused: this.paused,
loading: this.loading,
- });
+ }); */
// If the mouse is over the controls (and not entering fullscreen), bail
if ((this.elements.controls.pressed || this.elements.controls.hover) && !isEnterFullscreen) {
@@ -1105,8 +1106,18 @@ class Plyr {
};
// Type specific stuff
- switch (this.type) {
- case 'youtube':
+ switch (`${this.provider}:${this.type}`) {
+ case 'html5:video':
+ case 'html5:audio':
+ // Restore native video controls
+ ui.toggleNativeControls.call(this, true);
+
+ // Clean up
+ done();
+
+ break;
+
+ case 'youtube:video':
// Clear timers
window.clearInterval(this.timers.buffering);
window.clearInterval(this.timers.playing);
@@ -1119,7 +1130,7 @@ class Plyr {
break;
- case 'vimeo':
+ case 'vimeo:video':
// Destroy Vimeo API
// then clean up (wait, to prevent postmessage errors)
this.embed.unload().then(done);
@@ -1129,16 +1140,6 @@ class Plyr {
break;
- case 'video':
- case 'audio':
- // Restore native video controls
- ui.toggleNativeControls.call(this, true);
-
- // Clean up
- done();
-
- break;
-
default:
break;
}
diff --git a/src/js/source.js b/src/js/source.js
index c670ab09..cbea5433 100644
--- a/src/js/source.js
+++ b/src/js/source.js
@@ -2,7 +2,7 @@
// Plyr source update
// ==========================================================================
-import types from './types';
+import { providers } from './types';
import utils from './utils';
import media from './media';
import ui from './ui';
@@ -48,35 +48,25 @@ const source = {
this.elements.container.removeAttribute('class');
}
- // Set the type
- if ('type' in input) {
- this.type = input.type;
-
- // Get child type for video (it might be an embed)
- if (this.type === 'video') {
- const firstSource = input.sources[0];
-
- if ('type' in firstSource && types.embed.includes(firstSource.type)) {
- this.type = firstSource.type;
- }
- }
- }
+ // Set the type and provider
+ this.type = input.type;
+ this.provider = !utils.is.empty(input.sources[0].provider) ? input.sources[0].provider : providers.html5;
// Check for support
- this.supported = support.check(this.type, this.config.inline);
+ this.supported = support.check(this.type, this.provider, this.config.inline);
// Create new markup
- switch (this.type) {
- case 'video':
+ switch (`${this.provider}:${this.type}`) {
+ case 'html5:video':
this.media = utils.createElement('video');
break;
- case 'audio':
+ case 'html5:audio':
this.media = utils.createElement('audio');
break;
- case 'youtube':
- case 'vimeo':
+ case 'youtube:video':
+ case 'vimeo:video':
this.media = utils.createElement('div');
this.embedId = input.sources[0].src;
break;
@@ -117,7 +107,6 @@ const source = {
// Restore class hooks
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.supported.ui && this.captions.enabled);
-
ui.addStyleHook.call(this);
// Set new sources for html5
diff --git a/src/js/support.js b/src/js/support.js
index a8e0cc3e..fc61bbf6 100644
--- a/src/js/support.js
+++ b/src/js/support.js
@@ -12,29 +12,29 @@ const support = {
// Check for support
// Basic functionality vs full UI
- check(type, inline) {
+ check(type, provider, inline) {
let api = false;
let ui = false;
const browser = utils.getBrowser();
const playsInline = browser.isIPhone && inline && support.inline;
- switch (type) {
- case 'video':
+ switch (`${provider}:${type}`) {
+ case 'html5:video':
api = support.video;
ui = api && support.rangeInput && (!browser.isIPhone || playsInline);
break;
- case 'audio':
+ case 'html5:audio':
api = support.audio;
ui = api && support.rangeInput;
break;
- case 'youtube':
+ case 'youtube:video':
api = true;
ui = support.rangeInput && (!browser.isIPhone || playsInline);
break;
- case 'vimeo':
+ case 'vimeo:video':
api = true;
ui = support.rangeInput && !browser.isIPhone;
break;
@@ -92,12 +92,12 @@ const support = {
try {
// Bail if no checking function
- if (!utils.is.function(media.canPlayType)) {
+ if (!this.isHTML5 || !utils.is.function(media.canPlayType)) {
return false;
}
// Type specific checks
- if (this.type === 'video') {
+ if (this.isVideo) {
switch (type) {
case 'video/webm':
return media.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/, '');
@@ -111,7 +111,7 @@ const support = {
default:
return false;
}
- } else if (this.type === 'audio') {
+ } else if (this.isAudio) {
switch (type) {
case 'audio/mpeg':
return media.canPlayType('audio/mpeg;').replace(/no/, '');
diff --git a/src/js/types.js b/src/js/types.js
index 1f402a9b..35716c3c 100644
--- a/src/js/types.js
+++ b/src/js/types.js
@@ -1,10 +1,16 @@
// ==========================================================================
-// Plyr supported types
+// Plyr supported types and providers
// ==========================================================================
-const types = {
- embed: ['youtube', 'vimeo'],
- html5: ['video', 'audio'],
+export const providers = {
+ html5: 'html5',
+ youtube: 'youtube',
+ vimeo: 'vimeo',
};
-export default types;
+export const types = {
+ audio: 'audio',
+ video: 'video',
+};
+
+export default { providers, types };
diff --git a/src/js/ui.js b/src/js/ui.js
index 3ab3a1eb..5fb723d6 100644
--- a/src/js/ui.js
+++ b/src/js/ui.js
@@ -31,7 +31,7 @@ const ui = {
// Don't setup interface if no support
if (!this.supported.ui) {
- this.console.warn(`Basic support only for ${this.type}`);
+ this.console.warn(`Basic support only for ${this.provider} ${this.type}`);
// Remove controls
utils.removeElement.call(this, 'controls');
diff --git a/src/js/utils.js b/src/js/utils.js
index 25617ac8..b3ce509b 100644
--- a/src/js/utils.js
+++ b/src/js/utils.js
@@ -73,24 +73,39 @@ const utils = {
// Load an external script
loadScript(url, callback) {
- // Check script is not already referenced
- if (document.querySelectorAll(`script[src="${url}"]`).length) {
+ const current = document.querySelector(`script[src="${url}"]`);
+
+ // Check script is not already referenced, if so wait for load
+ if (current !== null) {
+ current.callbacks = current.callbacks || [];
+ current.callbacks.push(callback);
return;
}
// Build the element
const element = document.createElement('script');
- element.src = url;
- // Find first script
- const first = document.getElementsByTagName('script')[0];
+ // Callback queue
+ element.callbacks = element.callbacks || [];
+ element.callbacks.push(callback);
// Bind callback
if (utils.is.function(callback)) {
- element.addEventListener('load', event => callback.call(null, event), false);
+ element.addEventListener(
+ 'load',
+ event => {
+ element.callbacks.forEach(cb => cb.call(null, event));
+ element.callbacks = null;
+ },
+ false
+ );
}
+ // Set the URL after binding callback
+ element.src = url;
+
// Inject
+ const first = document.getElementsByTagName('script')[0];
first.parentNode.insertBefore(element, first);
},