120 line width, package upgrade

This commit is contained in:
Sam Potts
2018-06-17 01:04:55 +10:00
parent 828ce66942
commit d4abb4b143
29 changed files with 25698 additions and 25563 deletions

View File

@ -6,12 +6,20 @@
import controls from './controls';
import i18n from './i18n';
import support from './support';
import { dedupe } from './utils/arrays';
import browser from './utils/browser';
import { createElement, emptyElement, getAttributesFromSelector, insertAfter, removeElement, toggleClass, toggleState } from './utils/elements';
import {
createElement,
emptyElement,
getAttributesFromSelector,
insertAfter,
removeElement,
toggleClass,
toggleState,
} from './utils/elements';
import { on, triggerEvent } from './utils/events';
import fetch from './utils/fetch';
import is from './utils/is';
import { dedupe } from './utils/arrays';
import { getHTML } from './utils/strings';
import { parseUrl } from './utils/urls';
@ -26,7 +34,11 @@ const captions = {
// Only Vimeo and HTML5 video supported at this point
if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) {
// Clear menu and hide
if (is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
if (
is.array(this.config.controls) &&
this.config.controls.includes('settings') &&
this.config.settings.includes('captions')
) {
controls.setCaptionsMenu.call(this);
}
@ -49,7 +61,11 @@ const captions = {
const src = track.getAttribute('src');
const url = parseUrl(src);
if (url !== null && url.hostname !== window.location.href.hostname && ['http:', 'https:'].includes(url.protocol)) {
if (
url !== null &&
url.hostname !== window.location.href.hostname &&
['http:', 'https:'].includes(url.protocol)
) {
fetch(src, 'blob')
.then(blob => {
track.setAttribute('src', window.URL.createObjectURL(blob));
@ -68,8 +84,9 @@ const captions = {
// * active: The state preferred by user settings or config
// * toggled: The real captions state
const languages = dedupe(Array.from(navigator.languages || navigator.userLanguage)
.map(language => language.split('-')[0]));
const languages = dedupe(
Array.from(navigator.languages || navigator.userLanguage).map(language => language.split('-')[0]),
);
let language = (this.storage.get('language') || this.config.captions.language || 'auto').toLowerCase();
@ -165,10 +182,7 @@ const captions = {
// Force language if the call isn't passive and there is no matching language to toggle to
if (!this.language && active && !passive) {
const tracks = captions.getTracks.call(this);
const track = captions.findTrack.call(this, [
this.captions.language,
...this.captions.languages,
], true);
const track = captions.findTrack.call(this, [this.captions.language, ...this.captions.languages], true);
// Override user preferences to avoid switching languages if a matching track is added
this.captions.language = track.language;

View File

@ -197,7 +197,8 @@ const defaults = {
},
youtube: {
sdk: 'https://www.youtube.com/iframe_api',
api: 'https://www.googleapis.com/youtube/v3/videos?id={0}&key={1}&fields=items(snippet(title))&part=snippet',
api:
'https://www.googleapis.com/youtube/v3/videos?id={0}&key={1}&fields=items(snippet(title))&part=snippet',
},
googleIMA: {
sdk: 'https://imasdk.googleapis.com/js/sdkloader/ima3.js',

38
src/js/controls.js vendored
View File

@ -9,8 +9,20 @@ import support from './support';
import { repaint, transitionEndEvent } from './utils/animation';
import { dedupe } from './utils/arrays';
import browser from './utils/browser';
import { createElement, emptyElement, getAttributesFromSelector, getElement, getElements, hasClass, removeElement, setAttributes, toggleClass, toggleHidden, toggleState } from './utils/elements';
import { on, off } from './utils/events';
import {
createElement,
emptyElement,
getAttributesFromSelector,
getElement,
getElements,
hasClass,
removeElement,
setAttributes,
toggleClass,
toggleHidden,
toggleState,
} from './utils/elements';
import { off, on } from './utils/events';
import is from './utils/is';
import loadSprite from './utils/loadSprite';
import { extend } from './utils/objects';
@ -68,7 +80,9 @@ const controls = {
// Seek tooltip
if (is.element(this.elements.progress)) {
this.elements.display.seekTooltip = this.elements.progress.querySelector(`.${this.config.classNames.tooltip}`);
this.elements.display.seekTooltip = this.elements.progress.querySelector(
`.${this.config.classNames.tooltip}`,
);
}
return true;
@ -331,10 +345,10 @@ const controls = {
if (type !== 'volume') {
progress.appendChild(createElement('span', null, '0'));
const suffixKey = ({
const suffixKey = {
played: 'played',
buffer: 'buffered',
})[type];
}[type];
const suffix = suffixKey ? i18n.get(suffixKey, this.config) : '';
@ -519,7 +533,12 @@ const controls = {
// Update hover tooltip for seeking
updateSeekTooltip(event) {
// Bail if setting not true
if (!this.config.tooltips.seek || !is.element(this.elements.inputs.seek) || !is.element(this.elements.display.seekTooltip) || this.duration === 0) {
if (
!this.config.tooltips.seek ||
!is.element(this.elements.inputs.seek) ||
!is.element(this.elements.display.seekTooltip) ||
this.duration === 0
) {
return;
}
@ -573,7 +592,12 @@ const controls = {
const invert = !is.element(this.elements.display.duration) && this.config.invertTime;
// Duration
controls.updateTimeDisplay.call(this, this.elements.display.currentTime, invert ? this.duration - this.currentTime : this.currentTime, invert);
controls.updateTimeDisplay.call(
this,
this.elements.display.currentTime,
invert ? this.duration - this.currentTime : this.currentTime,
invert,
);
// Ignore updates while seeking
if (event && event.type === 'timeupdate' && this.media.seeking) {

View File

@ -63,10 +63,15 @@ class Fullscreen {
// Register event listeners
// Handle event (incase user presses escape etc)
on.call(this.player, document, this.prefix === 'ms' ? 'MSFullscreenChange' : `${this.prefix}fullscreenchange`, () => {
// TODO: Filter for target??
onChange.call(this);
});
on.call(
this.player,
document,
this.prefix === 'ms' ? 'MSFullscreenChange' : `${this.prefix}fullscreenchange`,
() => {
// TODO: Filter for target??
onChange.call(this);
},
);
// Fullscreen toggle on double click
on.call(this.player, this.player.elements.container, 'dblclick', event => {
@ -84,7 +89,12 @@ class Fullscreen {
// Determine if native supported
static get native() {
return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled);
return !!(
document.fullscreenEnabled ||
document.webkitFullscreenEnabled ||
document.mozFullScreenEnabled ||
document.msFullscreenEnabled
);
}
// Get the prefix for handlers
@ -142,7 +152,9 @@ class Fullscreen {
// Get target element
get target() {
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;
return browser.isIos && this.player.config.fullscreen.iosNative
? this.player.media
: this.player.elements.container;
}
// Update UI

View File

@ -52,9 +52,10 @@ class Listeners {
// and if the focused element is not editable (e.g. text input)
// and any that accept key input http://webaim.org/techniques/keyboard/
const focused = getFocusElement();
if (is.element(focused) && (
focused !== this.player.elements.inputs.seek &&
matches(focused, this.player.config.selectors.editable))
if (
is.element(focused) &&
(focused !== this.player.elements.inputs.seek &&
matches(focused, this.player.config.selectors.editable))
) {
return;
}
@ -174,7 +175,6 @@ class Listeners {
// Add touch class
toggleClass(this.player.elements.container, this.player.config.classNames.isTouch, true);
}
// Global window & document listeners
@ -217,40 +217,49 @@ class Listeners {
});
// Toggle controls on mouse events and entering fullscreen
on.call(this.player, this.player.elements.container, 'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen', event => {
const { controls } = this.player.elements;
on.call(
this.player,
this.player.elements.container,
'mousemove mouseleave touchstart touchmove enterfullscreen exitfullscreen',
event => {
const { controls } = this.player.elements;
// Remove button states for fullscreen
if (event.type === 'enterfullscreen') {
controls.pressed = false;
controls.hover = false;
}
// Remove button states for fullscreen
if (event.type === 'enterfullscreen') {
controls.pressed = false;
controls.hover = false;
}
// Show, then hide after a timeout unless another control event occurs
const show = ['touchstart', 'touchmove', 'mousemove'].includes(event.type);
// Show, then hide after a timeout unless another control event occurs
const show = ['touchstart', 'touchmove', 'mousemove'].includes(event.type);
let delay = 0;
let delay = 0;
if (show) {
ui.toggleControls.call(this.player, true);
// Use longer timeout for touch devices
delay = this.player.touch ? 3000 : 2000;
}
if (show) {
ui.toggleControls.call(this.player, true);
// Use longer timeout for touch devices
delay = this.player.touch ? 3000 : 2000;
}
// Clear timer
clearTimeout(this.player.timers.controls);
// Timer to prevent flicker when seeking
this.player.timers.controls = setTimeout(() => ui.toggleControls.call(this.player, false), delay);
});
// Clear timer
clearTimeout(this.player.timers.controls);
// Timer to prevent flicker when seeking
this.player.timers.controls = setTimeout(() => ui.toggleControls.call(this.player, false), delay);
},
);
}
// Listen for media events
media() {
// Time change on media
on.call(this.player, this.player.media, 'timeupdate seeking seeked', event => controls.timeUpdate.call(this.player, event));
on.call(this.player, this.player.media, 'timeupdate seeking seeked', event =>
controls.timeUpdate.call(this.player, event),
);
// Display duration
on.call(this.player, this.player.media, 'durationchange loadeddata loadedmetadata', event => controls.durationUpdate.call(this.player, event));
on.call(this.player, this.player.media, 'durationchange loadeddata loadedmetadata', event =>
controls.durationUpdate.call(this.player, event),
);
// Check for audio tracks on load
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
@ -269,16 +278,24 @@ class Listeners {
});
// Check for buffer progress
on.call(this.player, this.player.media, 'progress playing seeking seeked', event => controls.updateProgress.call(this.player, event));
on.call(this.player, this.player.media, 'progress playing seeking seeked', event =>
controls.updateProgress.call(this.player, event),
);
// Handle volume changes
on.call(this.player, this.player.media, 'volumechange', event => controls.updateVolume.call(this.player, event));
on.call(this.player, this.player.media, 'volumechange', event =>
controls.updateVolume.call(this.player, event),
);
// Handle play/pause
on.call(this.player, this.player.media, 'playing play pause ended emptied timeupdate', event => ui.checkPlaying.call(this.player, event));
on.call(this.player, this.player.media, 'playing play pause ended emptied timeupdate', event =>
ui.checkPlaying.call(this.player, event),
);
// Loading state
on.call(this.player, this.player.media, 'waiting canplay seeked playing', event => ui.checkLoading.call(this.player, event));
on.call(this.player, this.player.media, 'waiting canplay seeked playing', event =>
ui.checkLoading.call(this.player, event),
);
// If autoplay, then load advertisement if required
// TODO: Show some sort of loading state while the ad manager loads else there's a delay before ad shows
@ -324,7 +341,8 @@ class Listeners {
// Disable right click
if (this.player.supported.ui && this.player.config.disableContextMenu) {
on.call(this.player,
on.call(
this.player,
this.player.elements.wrapper,
'contextmenu',
event => {
@ -365,7 +383,7 @@ class Listeners {
// Bubble up key events for Edge
const proxyEvents = this.player.config.events.concat(['keyup', 'keydown']).join(' ');
on.call(this.player, this.player.media, proxyEvents, event => {
let {detail = {}} = event;
let { detail = {} } = event;
// Get error details from media
if (event.type === 'error') {
@ -403,7 +421,13 @@ class Listeners {
const customHandler = this.player.config.listeners[customHandlerKey];
const hasCustomHandler = is.function(customHandler);
on.call(this.player, element, type, event => proxy(event, defaultHandler, customHandlerKey), passive && !hasCustomHandler);
on.call(
this.player,
element,
type,
event => proxy(event, defaultHandler, customHandlerKey),
passive && !hasCustomHandler,
);
};
// Play/pause toggle
@ -592,7 +616,9 @@ class Listeners {
}
// Seek tooltip
bind(this.player.elements.progress, 'mouseenter mouseleave mousemove', event => controls.updateSeekTooltip.call(this.player, event));
bind(this.player.elements.progress, 'mouseenter mouseleave mousemove', event =>
controls.updateSeekTooltip.call(this.player, event),
);
// Update controls.hover state (used for ui.toggleControls to avoid hiding when interacting)
bind(this.player.elements.controls, 'mouseenter mouseleave', event => {
@ -665,7 +691,10 @@ class Listeners {
}
// Don't break page scrolling at max and min
if ((direction === 1 && this.player.media.volume < 1) || (direction === -1 && this.player.media.volume > 0)) {
if (
(direction === 1 && this.player.media.volume < 1) ||
(direction === -1 && this.player.media.volume > 0)
) {
event.preventDefault();
}
},

View File

@ -150,7 +150,11 @@ class Ads {
this.loader = new google.ima.AdsLoader(this.elements.displayContainer);
// Listen and respond to ads loaded and error events
this.loader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, event => this.onAdsManagerLoaded(event), false);
this.loader.addEventListener(
google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
event => this.onAdsManagerLoaded(event),
false,
);
this.loader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, error => this.onAdError(error), false);
// Request video ads

View File

@ -210,13 +210,14 @@ const youtube = {
if (!player.media.error) {
const code = event.data;
// Messages copied from https://developers.google.com/youtube/iframe_api_reference#onError
const message = ({
2: 'The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.',
5: 'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.',
100: 'The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.',
101: 'The owner of the requested video does not allow it to be played in embedded players.',
150: 'The owner of the requested video does not allow it to be played in embedded players.',
}[code]) || 'An unknown error occured';
const message =
{
2: 'The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.',
5: 'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.',
100: 'The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.',
101: 'The owner of the requested video does not allow it to be played in embedded players.',
150: 'The owner of the requested video does not allow it to be played in embedded players.',
}[code] || 'An unknown error occured';
player.media.error = { code, message };
@ -453,7 +454,10 @@ const youtube = {
}
// Get quality
controls.setQualityMenu.call(player, mapQualityUnits(instance.getAvailableQualityLevels()));
controls.setQualityMenu.call(
player,
mapQualityUnits(instance.getAvailableQualityLevels()),
);
}
break;

View File

@ -679,7 +679,12 @@ class Plyr {
return;
}
let quality = [!is.empty(input) && Number(input), this.storage.get('quality'), config.selected, config.default].find(is.number);
let quality = [
!is.empty(input) && Number(input),
this.storage.get('quality'),
config.selected,
config.default,
].find(is.number);
if (!options.includes(quality)) {
const value = closest(options, quality);

View File

@ -8,8 +8,8 @@ import media from './media';
import support from './support';
import ui from './ui';
import { createElement, insertElement, removeElement } from './utils/elements';
import { getDeep } from './utils/objects';
import is from './utils/is';
import { getDeep } from './utils/objects';
const source = {
// Add elements to HTML5 media (source, tracks, etc)

View File

@ -86,7 +86,11 @@ const ui = {
ui.checkPlaying.call(this);
// Check for picture-in-picture support
toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo);
toggleClass(
this.elements.container,
this.config.classNames.pip.supported,
support.pip && this.isHTML5 && this.isVideo,
);
// Check for airplay support
toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5);
@ -174,32 +178,35 @@ const ui = {
this.media.setAttribute('poster', poster);
// Wait until ui is ready
return ready.call(this)
// Load image
.then(() => loadImage(poster))
.catch(err => {
// Hide poster on error unless it's been set by another call
if (poster === this.poster) {
ui.togglePoster.call(this, false);
}
// Rethrow
throw err;
})
.then(() => {
// Prevent race conditions
if (poster !== this.poster) {
throw new Error('setPoster cancelled by later call to setPoster');
}
})
.then(() => {
Object.assign(this.elements.poster.style, {
backgroundImage: `url('${poster}')`,
// Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube)
backgroundSize: '',
});
ui.togglePoster.call(this, true);
return poster;
});
return (
ready
.call(this)
// Load image
.then(() => loadImage(poster))
.catch(err => {
// Hide poster on error unless it's been set by another call
if (poster === this.poster) {
ui.togglePoster.call(this, false);
}
// Rethrow
throw err;
})
.then(() => {
// Prevent race conditions
if (poster !== this.poster) {
throw new Error('setPoster cancelled by later call to setPoster');
}
})
.then(() => {
Object.assign(this.elements.poster.style, {
backgroundImage: `url('${poster}')`,
// Reset backgroundSize as well (since it can be set to "cover" for padded thumbnails for youtube)
backgroundSize: '',
});
ui.togglePoster.call(this, true);
return poster;
})
);
},
// Check playing state

View File

@ -218,7 +218,12 @@ export function matches(element, selector) {
return Array.from(document.querySelectorAll(selector)).includes(this);
}
const matches = prototype.matches || prototype.webkitMatchesSelector || prototype.mozMatchesSelector || prototype.msMatchesSelector || match;
const matches =
prototype.matches ||
prototype.webkitMatchesSelector ||
prototype.mozMatchesSelector ||
prototype.msMatchesSelector ||
match;
return matches.call(element, selector);
}

View File

@ -113,7 +113,8 @@ export function unbindListeners() {
}
// Run method when / if player is ready
export function ready () {
return new Promise(resolve => this.ready ? setTimeout(resolve, 0) : on.call(this, this.elements.container, 'ready', resolve))
.then(() => {});
export function ready() {
return new Promise(
resolve => (this.ready ? setTimeout(resolve, 0) : on.call(this, this.elements.container, 'ready', resolve)),
).then(() => {});
}

View File

@ -47,7 +47,10 @@ const is = {
return instanceOf(input, TextTrack) || (!is.nullOrUndefined(input) && is.string(input.kind));
},
url(input) {
return !is.nullOrUndefined(input) && /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/.test(input);
return (
!is.nullOrUndefined(input) &&
/(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/.test(input)
);
},
nullOrUndefined(input) {
return input === null || typeof input === 'undefined';

View File

@ -29,7 +29,10 @@ export function getPercentage(current, max) {
// Replace all occurances of a string in a string
export function replaceAll(input = '', find = '', replace = '') {
return input.replace(new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'), replace.toString());
return input.replace(
new RegExp(find.toString().replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'), 'g'),
replace.toString(),
);
}
// Convert to title case