Started on documentation and aspect ratio option
This commit is contained in:
parent
5fe477340b
commit
0068710740
2
dist/plyr.css
vendored
2
dist/plyr.css
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.js
vendored
2
dist/plyr.js
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.js.map
vendored
2
dist/plyr.js.map
vendored
File diff suppressed because one or more lines are too long
@ -34,13 +34,6 @@ Oh and yes, it works with Bootstrap.
|
||||
## Changelog
|
||||
Check out the [changelog](changelog.md) to see what's new with Plyr.
|
||||
|
||||
## Features currently being developed
|
||||
- Playback speed selection
|
||||
- Quality selection
|
||||
- Caption language selection
|
||||
- AirPlay
|
||||
- Picture in Picture (MacOS Sierra + Safari)
|
||||
|
||||
[more info](https://github.com/sampotts/plyr/issues?q=is%3Aissue+is%3Aopen+label%3A%22In+Development%22)
|
||||
|
||||
## Planned features
|
||||
|
16
src/js/controls.js
vendored
16
src/js/controls.js
vendored
@ -6,11 +6,14 @@ import support from './support';
|
||||
import utils from './utils';
|
||||
import ui from './ui';
|
||||
|
||||
// Sniff out the browser
|
||||
const browser = utils.getBrowser();
|
||||
|
||||
const controls = {
|
||||
// Webkit polyfill for lower fill range
|
||||
updateRangeFill(target) {
|
||||
// WebKit only
|
||||
if (!this.browser.isWebkit) {
|
||||
if (!browser.isWebkit) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -49,7 +52,7 @@ const controls = {
|
||||
getIconUrl() {
|
||||
return {
|
||||
url: this.config.iconUrl,
|
||||
absolute: this.config.iconUrl.indexOf('http') === 0 || (this.browser.isIE && !window.svg4everybody),
|
||||
absolute: this.config.iconUrl.indexOf('http') === 0 || (browser.isIE && !window.svg4everybody),
|
||||
};
|
||||
},
|
||||
|
||||
@ -1139,14 +1142,11 @@ const controls = {
|
||||
inject() {
|
||||
// Sprite
|
||||
if (this.config.loadSprite) {
|
||||
const iconUrl = controls.getIconUrl.call(this);
|
||||
const icon = controls.getIconUrl.call(this);
|
||||
|
||||
// Only load external sprite using AJAX
|
||||
if (iconUrl.absolute) {
|
||||
this.log(`AJAX loading absolute SVG sprite ${this.browser.isIE ? '(due to IE)' : ''}`);
|
||||
utils.loadSprite(iconUrl.url, 'sprite-plyr');
|
||||
} else {
|
||||
this.log('Sprite will be used as external resource directly');
|
||||
if (icon.absolute) {
|
||||
utils.loadSprite(icon.url, 'sprite-plyr');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,9 @@ const defaults = {
|
||||
// Pass a custom duration
|
||||
duration: null,
|
||||
|
||||
// Aspect ratio (for embeds)
|
||||
ratio: '16:9',
|
||||
|
||||
// Quality default
|
||||
quality: {
|
||||
default: 'default',
|
||||
|
@ -9,6 +9,9 @@ import fullscreen from './fullscreen';
|
||||
import storage from './storage';
|
||||
import ui from './ui';
|
||||
|
||||
// Sniff out the browser
|
||||
const browser = utils.getBrowser();
|
||||
|
||||
const listeners = {
|
||||
// Listen for media events
|
||||
media() {
|
||||
@ -134,7 +137,7 @@ const listeners = {
|
||||
// Listen for control events
|
||||
controls() {
|
||||
// IE doesn't support input event, so we fallback to change
|
||||
const inputEvent = this.browser.isIE ? 'change' : 'input';
|
||||
const inputEvent = browser.isIE ? 'change' : 'input';
|
||||
let last = null;
|
||||
|
||||
// Trigger custom and default handlers
|
||||
@ -468,7 +471,7 @@ const listeners = {
|
||||
);
|
||||
|
||||
// Polyfill for lower fill in <input type="range"> for webkit
|
||||
if (this.browser.isWebkit) {
|
||||
if (browser.isWebkit) {
|
||||
utils.on(utils.getElements.call(this, 'input[type="range"]'), 'input', event => {
|
||||
controls.updateRangeFill.call(this, event.target);
|
||||
});
|
||||
|
@ -8,6 +8,9 @@ import youtube from './plugins/youtube';
|
||||
import vimeo from './plugins/vimeo';
|
||||
import ui from './ui';
|
||||
|
||||
// Sniff out the browser
|
||||
const browser = utils.getBrowser();
|
||||
|
||||
const media = {
|
||||
// Setup media
|
||||
setup() {
|
||||
@ -45,7 +48,7 @@ const media = {
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.config.autoplay);
|
||||
|
||||
// Add iOS class
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.isIos, this.browser.isIos);
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.isIos, browser.isIos);
|
||||
|
||||
// Add touch class
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.isTouch, support.touch);
|
||||
|
@ -15,6 +15,13 @@ const vimeo = {
|
||||
// Add embed class for responsive
|
||||
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
|
||||
|
||||
// Set aspect ratio
|
||||
const ratio = this.config.ratio.split(':');
|
||||
const padding = 100 / ratio[0] * ratio[1];
|
||||
const offset = (100 - padding) / 2;
|
||||
this.elements.wrapper.style.paddingBottom = `${padding}%`;
|
||||
this.media.style.transform = `translateY(-${offset}%)`;
|
||||
|
||||
// Set ID
|
||||
this.media.setAttribute('id', utils.generateId(this.type));
|
||||
|
||||
|
@ -17,6 +17,10 @@ const youtube = {
|
||||
// Add embed class for responsive
|
||||
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
|
||||
|
||||
// Set aspect ratio
|
||||
const ratio = this.config.ratio.split(':');
|
||||
this.elements.wrapper.style.paddingBottom = `${100 / ratio[0] * ratio[1]}%`;
|
||||
|
||||
// Set ID
|
||||
this.media.setAttribute('id', utils.generateId(this.type));
|
||||
|
||||
|
@ -192,10 +192,7 @@ class Plyr {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sniff out the browser
|
||||
this.browser = utils.getBrowser();
|
||||
|
||||
// Load saved settings from localStorage
|
||||
// Setup local storage for user settings
|
||||
storage.setup.call(this);
|
||||
|
||||
// Check for support again but with type
|
||||
@ -237,17 +234,27 @@ class Plyr {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// API
|
||||
// ---------------------------------------
|
||||
|
||||
/**
|
||||
* If the player is HTML5
|
||||
*/
|
||||
get isHTML5() {
|
||||
return types.html5.includes(this.type);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the player is an embed - e.g. YouTube or Vimeo
|
||||
*/
|
||||
get isEmbed() {
|
||||
return types.embed.includes(this.type);
|
||||
}
|
||||
|
||||
// Play
|
||||
/**
|
||||
* Play the media
|
||||
*/
|
||||
play() {
|
||||
if ('play' in this.media) {
|
||||
this.media.play();
|
||||
@ -257,7 +264,9 @@ class Plyr {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Pause
|
||||
/**
|
||||
* Pause the media
|
||||
*/
|
||||
pause() {
|
||||
if ('pause' in this.media) {
|
||||
this.media.pause();
|
||||
@ -267,7 +276,10 @@ class Plyr {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Toggle playback
|
||||
/**
|
||||
* Toggle playback based on current status
|
||||
* @param {boolean} toggle
|
||||
*/
|
||||
togglePlay(toggle) {
|
||||
// True toggle if nothing passed
|
||||
if ((!utils.is.boolean(toggle) && this.media.paused) || toggle) {
|
||||
@ -277,31 +289,43 @@ class Plyr {
|
||||
return this.pause();
|
||||
}
|
||||
|
||||
// Stop
|
||||
/**
|
||||
* Stop playback
|
||||
*/
|
||||
stop() {
|
||||
return this.restart().pause();
|
||||
}
|
||||
|
||||
// Restart
|
||||
/**
|
||||
* Restart playback
|
||||
*/
|
||||
restart() {
|
||||
this.currentTime = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Rewind
|
||||
/**
|
||||
* Rewind
|
||||
* @param {number} seekTime - how far to rewind in seconds. Defaults to the config.seekTime
|
||||
*/
|
||||
rewind(seekTime) {
|
||||
this.currentTime = this.currentTime - (utils.is.number(seekTime) ? seekTime : this.config.seekTime);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Fast forward
|
||||
/**
|
||||
* Fast forward
|
||||
* @param {number} seekTime - how far to fast forward in seconds. Defaults to the config.seekTime
|
||||
*/
|
||||
forward(seekTime) {
|
||||
this.currentTime = this.currentTime + (utils.is.number(seekTime) ? seekTime : this.config.seekTime);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Seek to time
|
||||
// The input parameter can be an event or a number
|
||||
/**
|
||||
* Seek to a time
|
||||
* @param {number} input - where to seek to in seconds. Defaults to 0 (the start)
|
||||
*/
|
||||
set currentTime(input) {
|
||||
let targetTime = 0;
|
||||
|
||||
@ -327,7 +351,9 @@ class Plyr {
|
||||
return Number(this.media.currentTime);
|
||||
}
|
||||
|
||||
// Duration
|
||||
/**
|
||||
* Get the duration of the current media
|
||||
*/
|
||||
get duration() {
|
||||
// Faux duration set via config
|
||||
const fauxDuration = parseInt(this.config.duration, 10);
|
||||
@ -339,7 +365,10 @@ class Plyr {
|
||||
return !Number.isNaN(fauxDuration) ? fauxDuration : realDuration;
|
||||
}
|
||||
|
||||
// Volume
|
||||
/**
|
||||
* Set the player volume
|
||||
* @param {number} value - must be between 0 and 1. Defaults to the value from local storage and config.volume if not set in storage
|
||||
*/
|
||||
set volume(value) {
|
||||
let volume = value;
|
||||
const max = 1;
|
||||
@ -377,6 +406,9 @@ class Plyr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current player volume
|
||||
*/
|
||||
get volume() {
|
||||
return this.media.volume;
|
||||
}
|
||||
|
@ -84,15 +84,17 @@ const ui = {
|
||||
// Update the UI
|
||||
ui.checkPlaying.call(this);
|
||||
|
||||
// Ready for API calls
|
||||
this.ready = true;
|
||||
|
||||
// Ready event at end of execution stack
|
||||
utils.dispatchEvent.call(this, this.media, 'ready');
|
||||
|
||||
// Autoplay
|
||||
if (this.config.autoplay) {
|
||||
// TODO: check we still need this?
|
||||
/* if (this.isEmbed && this.config.autoplay) {
|
||||
this.play();
|
||||
}
|
||||
} */
|
||||
},
|
||||
|
||||
// Show the duration on metadataloaded
|
||||
|
134
src/js/utils.js
134
src/js/utils.js
@ -89,6 +89,73 @@ const utils = {
|
||||
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
|
||||
},
|
||||
|
||||
// Load an external SVG sprite
|
||||
loadSprite(url, id) {
|
||||
if (typeof url !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
const prefix = 'cache-';
|
||||
const hasId = typeof id === 'string';
|
||||
let isCached = false;
|
||||
|
||||
function updateSprite(data) {
|
||||
// Inject content
|
||||
this.innerHTML = data;
|
||||
|
||||
// Inject the SVG to the body
|
||||
document.body.insertBefore(this, document.body.childNodes[0]);
|
||||
}
|
||||
|
||||
// Only load once
|
||||
if (!hasId || !document.querySelectorAll(`#${id}`).length) {
|
||||
// Create container
|
||||
const container = document.createElement('div');
|
||||
container.setAttribute('hidden', '');
|
||||
|
||||
if (hasId) {
|
||||
container.setAttribute('id', id);
|
||||
}
|
||||
|
||||
// Check in cache
|
||||
if (support.storage) {
|
||||
const cached = window.localStorage.getItem(prefix + id);
|
||||
isCached = cached !== null;
|
||||
|
||||
if (isCached) {
|
||||
const data = JSON.parse(cached);
|
||||
updateSprite.call(container, data.content);
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
// XHR for Chrome/Firefox/Opera/Safari
|
||||
if ('withCredentials' in xhr) {
|
||||
xhr.open('GET', url, true);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Once loaded, inject to container and body
|
||||
xhr.onload = () => {
|
||||
if (support.storage) {
|
||||
window.localStorage.setItem(
|
||||
prefix + id,
|
||||
JSON.stringify({
|
||||
content: xhr.responseText,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
updateSprite.call(container, xhr.responseText);
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
}
|
||||
},
|
||||
|
||||
// Generate a random ID
|
||||
generateId(prefix) {
|
||||
return `${prefix}-${Math.floor(Math.random() * 10000)}`;
|
||||
@ -564,73 +631,6 @@ const utils = {
|
||||
return fragment.firstChild.innerText;
|
||||
},
|
||||
|
||||
// Load an SVG sprite
|
||||
loadSprite(url, id) {
|
||||
if (typeof url !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
const prefix = 'cache-';
|
||||
const hasId = typeof id === 'string';
|
||||
let isCached = false;
|
||||
|
||||
function updateSprite(data) {
|
||||
// Inject content
|
||||
this.innerHTML = data;
|
||||
|
||||
// Inject the SVG to the body
|
||||
document.body.insertBefore(this, document.body.childNodes[0]);
|
||||
}
|
||||
|
||||
// Only load once
|
||||
if (!hasId || !document.querySelectorAll(`#${id}`).length) {
|
||||
// Create container
|
||||
const container = document.createElement('div');
|
||||
container.setAttribute('hidden', '');
|
||||
|
||||
if (hasId) {
|
||||
container.setAttribute('id', id);
|
||||
}
|
||||
|
||||
// Check in cache
|
||||
if (support.storage) {
|
||||
const cached = window.localStorage.getItem(prefix + id);
|
||||
isCached = cached !== null;
|
||||
|
||||
if (isCached) {
|
||||
const data = JSON.parse(cached);
|
||||
updateSprite.call(container, data.content);
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once InconsistentNaming
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
// XHR for Chrome/Firefox/Opera/Safari
|
||||
if ('withCredentials' in xhr) {
|
||||
xhr.open('GET', url, true);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Once loaded, inject to container and body
|
||||
xhr.onload = () => {
|
||||
if (support.storage) {
|
||||
window.localStorage.setItem(
|
||||
prefix + id,
|
||||
JSON.stringify({
|
||||
content: xhr.responseText,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
updateSprite.call(container, xhr.responseText);
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
}
|
||||
},
|
||||
|
||||
// Get the transition end event
|
||||
transitionEnd: (() => {
|
||||
const element = document.createElement('span');
|
||||
|
@ -4,7 +4,11 @@
|
||||
// --------------------------------------------------------------
|
||||
|
||||
.plyr__video-embed {
|
||||
padding-bottom: 56.25%; /* 16:9 */
|
||||
// Default to 16:9 ratio but this is set by JavaScript based on config
|
||||
@padding: ((100 / 16) * 9);
|
||||
@offset: unit((100 - @padding) / 2, ~'%');
|
||||
|
||||
padding-bottom: unit(@padding, ~'%');
|
||||
height: 0;
|
||||
|
||||
iframe {
|
||||
@ -20,8 +24,8 @@
|
||||
// Vimeo hack
|
||||
> div {
|
||||
position: relative;
|
||||
padding-bottom: 200%;
|
||||
transform: translateY(-35.95%);
|
||||
padding-bottom: 100%;
|
||||
transform: translateY(-@offset);
|
||||
}
|
||||
}
|
||||
// To allow mouse events to be captured if full support
|
||||
|
Loading…
x
Reference in New Issue
Block a user