Keyboard and focus improvements

This commit is contained in:
Sam Potts 2018-07-15 19:23:28 +10:00
parent ead6601394
commit e63ad7c74b
20 changed files with 1326 additions and 646 deletions

29
demo/dist/demo.js vendored
View File

@ -1874,7 +1874,7 @@ typeof navigator === "object" && (function () {
// webpack (using a build step causes webpack #1617). Grunt verifies that
// this value matches package.json during build.
// See: https://github.com/getsentry/raven-js/issues/465
VERSION: '3.26.2',
VERSION: '3.26.3',
debug: false,
@ -2351,7 +2351,9 @@ typeof navigator === "object" && (function () {
return;
}
if (this._globalOptions.stacktrace || (options && options.stacktrace)) {
// Always attempt to get stacktrace if message is empty.
// It's the only way to provide any helpful information to the user.
if (this._globalOptions.stacktrace || options.stacktrace || data.message === '') {
// fingerprint on msg, not stack trace (legacy behavior, could be revisited)
data.fingerprint = data.fingerprint == null ? msg : data.fingerprint;
@ -3508,6 +3510,11 @@ typeof navigator === "object" && (function () {
options
);
var ex = data.exception.values[0];
if (ex.type == null && ex.value === '') {
ex.value = 'Unrecoverable error caught';
}
// Move mechanism from options to exception interface
// We do this, as requiring user to pass `{exception:{mechanism:{ ... }}}` would be
// too much
@ -4090,6 +4097,9 @@ typeof navigator === "object" && (function () {
document.addEventListener('DOMContentLoaded', function () {
singleton.context(function () {
var selector = '#player';
var container = document.getElementById('container');
if (window.shr) {
window.shr.setup({
count: {
@ -4103,6 +4113,9 @@ typeof navigator === "object" && (function () {
// Remove class on blur
document.addEventListener('focusout', function (event) {
if (container.contains(event.target)) {
return;
}
event.target.classList.remove(tabClassName);
});
@ -4115,12 +4128,18 @@ typeof navigator === "object" && (function () {
// Delay the adding of classname until the focus has changed
// This event fires before the focusin event
setTimeout(function () {
document.activeElement.classList.add(tabClassName);
}, 0);
var focused = document.activeElement;
if (!focused || container.contains(focused)) {
return;
}
focused.classList.add(tabClassName);
}, 10);
});
// Setup the player
var player = new Plyr('#player', {
var player = new Plyr(selector, {
debug: true,
title: 'View From A Blue Moon',
iconUrl: '../dist/plyr.svg',

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -91,21 +91,22 @@
</header>
<main>
<video controls crossorigin playsinline poster="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg" id="player">
<!-- Video files -->
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4" type="video/mp4" size="576">
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-720p.mp4" type="video/mp4" size="720">
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1080p.mp4" type="video/mp4" size="1080">
<!-- <source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1440p.mp4" type="video/mp4" size="1440"> -->
<div id="container">
<video controls crossorigin playsinline poster="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg" id="player">
<!-- Video files -->
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4" type="video/mp4" size="576">
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-720p.mp4" type="video/mp4" size="720">
<source src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1080p.mp4" type="video/mp4" size="1080">
<!-- Caption files -->
<track kind="captions" label="English" srclang="en" src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt"
default>
<track kind="captions" label="Français" srclang="fr" src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt">
<!-- Caption files -->
<track kind="captions" label="English" srclang="en" src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt"
default>
<track kind="captions" label="Français" srclang="fr" src="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt">
<!-- Fallback for browsers that don't support the <video> element -->
<a href="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4" download>Download</a>
</video>
<!-- Fallback for browsers that don't support the <video> element -->
<a href="https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4" download>Download</a>
</video>
</div>
<ul>
<li class="plyr__cite plyr__cite--video" hidden>

View File

@ -12,11 +12,16 @@ import Raven from 'raven-js';
// Raven / Sentry
// For demo site (https://plyr.io) only
if (isLive) {
Raven.config('https://d4ad9866ad834437a4754e23937071e4@sentry.io/305555').install();
Raven.config(
'https://d4ad9866ad834437a4754e23937071e4@sentry.io/305555',
).install();
}
document.addEventListener('DOMContentLoaded', () => {
Raven.context(() => {
const selector = '#player';
const container = document.getElementById('container');
if (window.shr) {
window.shr.setup({
count: {
@ -30,6 +35,9 @@ import Raven from 'raven-js';
// Remove class on blur
document.addEventListener('focusout', event => {
if (container.contains(event.target)) {
return;
}
event.target.classList.remove(tabClassName);
});
@ -42,12 +50,18 @@ import Raven from 'raven-js';
// Delay the adding of classname until the focus has changed
// This event fires before the focusin event
setTimeout(() => {
document.activeElement.classList.add(tabClassName);
}, 0);
const focused = document.activeElement;
if (!focused || container.contains(focused)) {
return;
}
focused.classList.add(tabClassName);
}, 10);
});
// Setup the player
const player = new Plyr('#player', {
const player = new Plyr(selector, {
debug: true,
title: 'View From A Blue Moon',
iconUrl: '../dist/plyr.svg',
@ -159,40 +173,47 @@ import Raven from 'raven-js';
title: 'View From A Blue Moon',
sources: [
{
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4',
src:
'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-576p.mp4',
type: 'video/mp4',
size: 576,
},
{
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-720p.mp4',
src:
'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-720p.mp4',
type: 'video/mp4',
size: 720,
},
{
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1080p.mp4',
src:
'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1080p.mp4',
type: 'video/mp4',
size: 1080,
},
{
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1440p.mp4',
src:
'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-1440p.mp4',
type: 'video/mp4',
size: 1440,
},
],
poster: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg',
poster:
'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.jpg',
tracks: [
{
kind: 'captions',
label: 'English',
srclang: 'en',
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt',
src:
'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.en.vtt',
default: true,
},
{
kind: 'captions',
label: 'French',
srclang: 'fr',
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt',
src:
'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt',
},
],
};
@ -202,14 +223,17 @@ import Raven from 'raven-js';
case types.audio:
player.source = {
type: 'audio',
title: 'Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;',
title:
'Kishi Bashi &ndash; &ldquo;It All Began With A Burst&rdquo;',
sources: [
{
src: 'https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3',
src:
'https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3',
type: 'audio/mp3',
},
{
src: 'https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg',
src:
'https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg',
type: 'audio/ogg',
},
],
@ -222,7 +246,8 @@ import Raven from 'raven-js';
type: 'video',
sources: [
{
src: 'https://youtube.com/watch?v=bTqVqk7FSmY',
src:
'https://youtube.com/watch?v=bTqVqk7FSmY',
provider: 'youtube',
},
],
@ -251,16 +276,26 @@ import Raven from 'raven-js';
currentType = type;
// Remove active classes
Array.from(buttons).forEach(button => toggleClass(button.parentElement, 'active', false));
Array.from(buttons).forEach(button =>
toggleClass(button.parentElement, 'active', false),
);
// Set active on parent
toggleClass(document.querySelector(`[data-source="${type}"]`), 'active', true);
toggleClass(
document.querySelector(`[data-source="${type}"]`),
'active',
true,
);
// Show cite
Array.from(document.querySelectorAll('.plyr__cite')).forEach(cite => {
cite.setAttribute('hidden', '');
});
document.querySelector(`.plyr__cite--${type}`).removeAttribute('hidden');
Array.from(document.querySelectorAll('.plyr__cite')).forEach(
cite => {
cite.setAttribute('hidden', '');
},
);
document
.querySelector(`.plyr__cite--${type}`)
.removeAttribute('hidden');
}
// Bind to each button
@ -328,7 +363,13 @@ import Raven from 'raven-js';
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m);
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
})(
window,
document,
'script',
'https://www.google-analytics.com/analytics.js',
'ga',
);
window.ga('create', 'UA-40881672-11', 'auto');
window.ga('send', 'pageview');
}

474
dist/plyr.js vendored

File diff suppressed because it is too large Load Diff

2
dist/plyr.js.map vendored

File diff suppressed because one or more lines are too long

2
dist/plyr.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,19 @@
{
"name": "plyr",
"version": "3.3.16",
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"description":
"A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
"homepage": "https://plyr.io",
"author": "Sam Potts <sam@potts.es>",
"keywords": ["HTML5 Video", "HTML5 Audio", "Media Player", "DASH", "Shaka", "WordPress", "HLS"],
"keywords": [
"HTML5 Video",
"HTML5 Audio",
"Media Player",
"DASH",
"Shaka",
"WordPress",
"HLS"
],
"main": "./dist/plyr.js",
"browser": "./dist/plyr.min.js",
"sass": "./src/sass/plyr.scss",
@ -27,11 +36,11 @@
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-eslint": "^7.2.3",
"babel-eslint": "^8.2.5",
"babel-plugin-external-helpers": "^6.22.0",
"babel-preset-env": "^1.7.0",
"del": "^3.0.0",
"eslint": "^5.0.1",
"eslint": "^5.1.0",
"eslint-config-airbnb-base": "^13.0.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-import": "^2.13.0",
@ -58,7 +67,7 @@
"postcss-custom-properties": "^7.0.0",
"prettier-eslint": "^8.8.2",
"prettier-stylelint": "^0.4.2",
"rollup-plugin-babel": "^3.0.5",
"rollup-plugin-babel": "^3.0.7",
"rollup-plugin-commonjs": "^9.1.3",
"rollup-plugin-node-resolve": "^3.3.0",
"run-sequence": "^2.2.1",

128
src/js/controls.js vendored
View File

@ -1,5 +1,6 @@
// ==========================================================================
// Plyr controls
// TODO: This needs to be split into smaller files and cleaned up
// ==========================================================================
import captions from './captions';
@ -360,11 +361,53 @@ const controls = {
return container;
},
// Bind keyboard shortcuts for a menu item
bindMenuItemShortcuts(menuItem, type) {
// Handle space or -> to open menu
on(menuItem, 'keydown', event => {
// We only care about space and ⬆️ ⬇️️ ➡️
if (![32,38,39,40].includes(event.which)) {
return;
}
// Prevent play / seek
event.preventDefault();
event.stopPropagation();
const isRadioButton = matches(menuItem, '[role="menuitemradio"]');
// Show the respective menu
if (!isRadioButton && [32,39].includes(event.which)) {
controls.showMenuPanel.call(this, type);
} else {
let target;
if (event.which !== 32) {
if (event.which === 40 || isRadioButton && event.which === 39) {
target = menuItem.nextElementSibling;
if (!is.element(target)) {
target = menuItem.parentNode.firstElementChild;
}
} else {
target = menuItem.previousElementSibling;
if (!is.element(target)) {
target = menuItem.parentNode.lastElementChild;
}
}
setFocus.call(this, target, true);
}
}
}, false);
},
// Create a settings menu item
createMenuItem({ value, list, type, title, badge = null, checked = false }) {
const attributes = getAttributesFromSelector(this.config.selectors.inputs[type]);
const item = createElement(
const menuItem = createElement(
'button',
extend(attributes, {
type: 'button',
@ -384,30 +427,38 @@ const controls = {
flex.appendChild(badge);
}
item.appendChild(flex);
menuItem.appendChild(flex);
Object.defineProperty(item, 'checked', {
// Replicate radio button behaviour
Object.defineProperty(menuItem, 'checked', {
enumerable: true,
get() {
return item.getAttribute('aria-checked') === 'true';
return menuItem.getAttribute('aria-checked') === 'true';
},
set(checked) {
// Ensure exclusivity
if (checked) {
Array.from(item.parentNode.children)
Array.from(menuItem.parentNode.children)
.filter(node => matches(node, '[role="menuitemradio"]'))
.forEach(node => node.setAttribute('aria-checked', 'false'));
}
item.setAttribute('aria-checked', checked ? 'true' : 'false');
menuItem.setAttribute('aria-checked', checked ? 'true' : 'false');
},
});
this.listeners.bind(
item,
'click',
() => {
item.checked = true;
menuItem,
'click keydown',
event => {
if (event.type === 'keydown' && event.which !== 32) {
return;
}
event.preventDefault();
event.stopPropagation();
menuItem.checked = true;
switch (type) {
case 'language':
@ -429,9 +480,12 @@ const controls = {
controls.showMenuPanel.call(this, 'home');
},
type,
false,
);
list.appendChild(item);
controls.bindMenuItemShortcuts.call(this, menuItem, type);
list.appendChild(menuItem);
},
// Format a time for display
@ -993,7 +1047,7 @@ const controls = {
},
// Show/hide menu
toggleMenu(event) {
toggleMenu(input) {
const { popup } = this.elements.settings;
const button = this.elements.buttons.settings;
@ -1002,11 +1056,11 @@ const controls = {
return;
}
const show = is.boolean(event) ? event : is.element(popup) && popup.hasAttribute('hidden');
const show = is.boolean(input) ? input : is.element(popup) && popup.hasAttribute('hidden');
if (is.event(event)) {
const isMenuItem = is.element(popup) && popup.contains(event.target);
const isButton = event.target === this.elements.buttons.settings;
if (is.event(input)) {
const isMenuItem = is.element(popup) && popup.contains(input.target);
const isButton = input.target === this.elements.buttons.settings;
// If the click was inside the form or if the click
// wasn't the button or menu item and we're trying to
@ -1017,7 +1071,7 @@ const controls = {
// Prevent the toggle being caught by the doc listener
if (isButton) {
event.stopPropagation();
input.stopPropagation();
}
}
@ -1031,17 +1085,11 @@ const controls = {
toggleHidden(popup, !show);
toggleClass(this.elements.container, this.config.classNames.menu.open, show);
if (show) {
popup.removeAttribute('tabindex');
// Focus the first item if key interaction
if (event.type === 'keydown') {
const pane = Object.values(this.elements.settings.panels).find(pane => !pane.hidden);
const firstItem = pane.querySelector('[role^="menuitem"]');
setFocus.call(this, firstItem, true);
}
} else {
popup.setAttribute('tabindex', -1);
// Focus the first item if key interaction
if (show && is.event(input) && input.type === 'keydown') {
const pane = Object.values(this.elements.settings.panels).find(pane => !pane.hidden);
const firstItem = pane.querySelector('[role^="menuitem"]');
setFocus.call(this, firstItem, true);
}
}
},
@ -1275,9 +1323,11 @@ const controls = {
home.appendChild(menu);
inner.appendChild(home);
this.elements.settings.panels.home = home;
// Build the menu items
this.config.settings.forEach(type => {
// TODO: bundle this with the createMenuItem helper and bindings
const menuItem = createElement(
'button',
extend(getAttributesFromSelector(this.config.selectors.buttons.settings), {
@ -1289,20 +1339,8 @@ const controls = {
}),
);
// Handle space or -> to open menu
on(menuItem, 'keydown', event => {
// We only care about space and ->
if (![32,39].includes(event.which)) {
return;
}
// Prevent play / seek
event.preventDefault();
event.stopPropagation();
// Show the respective menu
controls.showMenuPanel.call(this, type);
}, false);
// Bind menu shortcuts for keyboard users
controls.bindMenuItemShortcuts.call(this, menuItem, type);
// Show menu on click
on(menuItem, 'click', () => {
@ -1356,8 +1394,8 @@ const controls = {
),
);
// Handle space or -> to open menu
on(backButton, 'keydown', event => {
// Go back via keyboard
on(pane, 'keydown', event => {
// We only care about <-
if (event.which !== 37) {
return;
@ -1371,7 +1409,7 @@ const controls = {
controls.showMenuPanel.call(this, 'home');
}, false);
// Go back
// Go back via button click
on(backButton, 'click', () => {
controls.showMenuPanel.call(this, 'home');
});

View File

@ -5,7 +5,13 @@
import controls from './controls';
import ui from './ui';
import browser from './utils/browser';
import { getElement, getElements, getFocusElement, matches, toggleClass, toggleHidden } from './utils/elements';
import {
getElement,
getElements,
matches,
toggleClass,
toggleHidden,
} from './utils/elements';
import { on, once, toggleListener, triggerEvent } from './utils/events';
import is from './utils/is';
@ -13,9 +19,12 @@ class Listeners {
constructor(player) {
this.player = player;
this.lastKey = null;
this.focusTimer = null;
this.lastKeyDown = null;
this.handleKey = this.handleKey.bind(this);
this.toggleMenu = this.toggleMenu.bind(this);
this.setTabFocus = this.setTabFocus.bind(this);
this.firstTouch = this.firstTouch.bind(this);
}
@ -45,21 +54,51 @@ class Listeners {
// Handle the key on keydown
// Reset on keyup
if (pressed) {
// Which keycodes should we prevent default
const preventDefault = [32, 37, 38, 39, 40, 48, 49, 50, 51, 52, 53, 54, 56, 57, 67, 70, 73, 75, 76, 77, 79];
// Check focused element
// 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))
) {
return;
const focused = document.activeElement;
if (is.element(focused)) {
const { editable } = this.player.config.selectors;
const { seek } = this.player.elements.inputs;
if (focused !== seek && matches(focused, editable)) {
return;
}
if (
event.which === 32 &&
matches(focused, 'button, [role^="menuitem"]')
) {
return;
}
}
// Which keycodes should we prevent default
const preventDefault = [
32,
37,
38,
39,
40,
48,
49,
50,
51,
52,
53,
54,
56,
57,
67,
70,
73,
75,
76,
77,
79,
];
// If the code is found prevent default (e.g. prevent scrolling for arrows)
if (preventDefault.includes(code)) {
event.preventDefault();
@ -153,7 +192,11 @@ class Listeners {
// Escape is handle natively when in full screen
// So we only need to worry about non native
if (!this.player.fullscreen.enabled && this.player.fullscreen.active && code === 27) {
if (
!this.player.fullscreen.enabled &&
this.player.fullscreen.active &&
code === 27
) {
this.player.fullscreen.toggle();
}
@ -174,48 +217,117 @@ class Listeners {
this.player.touch = true;
// Add touch class
toggleClass(this.player.elements.container, this.player.config.classNames.isTouch, true);
toggleClass(
this.player.elements.container,
this.player.config.classNames.isTouch,
true,
);
}
setTabFocus(event) {
clearTimeout(this.focusTimer);
// Ignore any key other than tab
if (event.type === 'keydown' && event.code !== 'Tab') {
return;
}
// Store reference to event timeStamp
if (event.type === 'keydown') {
this.lastKeyDown = event.timeStamp;
}
// Remove current classes
const removeCurrent = () => {
const className = this.player.config.classNames.tabFocus;
const current = getElements.call(this.player, `.${className}`);
toggleClass(current, className, false);
};
// Determine if a key was pressed to trigger this event
const wasKeyDown = event.timeStamp - this.lastKeyDown <= 20;
// Ignore focus events if a key was pressed prior
if (event.type === 'focus' && !wasKeyDown) {
return;
}
// Remove all current
removeCurrent();
// Delay the adding of classname until the focus has changed
// This event fires before the focusin event
this.focusTimer = setTimeout(() => {
const focused = document.activeElement;
// Ignore if current focus element isn't inside the player
if (!this.player.elements.container.contains(focused)) {
return;
}
toggleClass(
document.activeElement,
this.player.config.classNames.tabFocus,
true,
);
}, 10);
}
// Global window & document listeners
global(toggle = true) {
// Keyboard shortcuts
if (this.player.config.keyboard.global) {
toggleListener.call(this.player, window, 'keydown keyup', this.handleKey, toggle, false);
toggleListener.call(
this.player,
window,
'keydown keyup',
this.handleKey,
toggle,
false,
);
}
// Click anywhere closes menu
toggleListener.call(this.player, document.body, 'click', this.toggleMenu, toggle);
toggleListener.call(
this.player,
document.body,
'click',
this.toggleMenu,
toggle,
);
// Detect touch by events
once.call(this.player, document.body, 'touchstart', this.firstTouch);
// Tab focus detection
toggleListener.call(
this.player,
document.body,
'keydown focus blur',
this.setTabFocus,
toggle,
false,
true,
);
}
// Container listeners
container() {
// Keyboard shortcuts
if (!this.player.config.keyboard.global && this.player.config.keyboard.focused) {
on.call(this.player, this.player.elements.container, 'keydown keyup', this.handleKey, false);
if (
!this.player.config.keyboard.global &&
this.player.config.keyboard.focused
) {
on.call(
this.player,
this.player.elements.container,
'keydown keyup',
this.handleKey,
false,
);
}
// Detect tab focus
// Remove class on blur/focusout
on.call(this.player, this.player.elements.container, 'focusout', event => {
toggleClass(event.target, this.player.config.classNames.tabFocus, false);
});
// Add classname to tabbed elements
on.call(this.player, this.player.elements.container, 'keydown', event => {
if (event.keyCode !== 9) {
return;
}
// Delay the adding of classname until the focus has changed
// This event fires before the focusin event
setTimeout(() => {
toggleClass(getFocusElement(), this.player.config.classNames.tabFocus, true);
}, 0);
});
// Toggle controls on mouse events and entering fullscreen
on.call(
this.player,
@ -231,7 +343,9 @@ class Listeners {
}
// Show, then hide after a timeout unless another control event occurs
const show = ['touchstart', 'touchmove', 'mousemove'].includes(event.type);
const show = ['touchstart', 'touchmove', 'mousemove'].includes(
event.type,
);
let delay = 0;
@ -245,7 +359,10 @@ class Listeners {
clearTimeout(this.player.timers.controls);
// Set new timer to prevent flicker when seeking
this.player.timers.controls = setTimeout(() => ui.toggleControls.call(this.player, false), delay);
this.player.timers.controls = setTimeout(
() => ui.toggleControls.call(this.player, false),
delay,
);
},
);
}
@ -253,34 +370,50 @@ class Listeners {
// 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
on.call(this.player, this.player.media, 'canplay', () => {
toggleHidden(this.player.elements.volume, !this.player.hasAudio);
toggleHidden(this.player.elements.buttons.mute, !this.player.hasAudio);
toggleHidden(
this.player.elements.buttons.mute,
!this.player.hasAudio,
);
});
// Handle the media finishing
on.call(this.player, this.player.media, 'ended', () => {
// Show poster on end
if (this.player.isHTML5 && this.player.isVideo && this.player.config.resetOnEnd) {
if (
this.player.isHTML5 &&
this.player.isVideo &&
this.player.config.resetOnEnd
) {
// Restart
this.player.restart();
}
});
// 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
@ -289,13 +422,19 @@ class Listeners {
);
// 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
@ -308,14 +447,23 @@ class Listeners {
// If ads are enabled, wait for them first
if (this.player.ads.enabled && !this.player.ads.initialized) {
// Wait for manager response
this.player.ads.managerPromise.then(() => this.player.ads.play()).catch(() => this.player.play());
this.player.ads.managerPromise
.then(() => this.player.ads.play())
.catch(() => this.player.play());
}
});
// Click video
if (this.player.supported.ui && this.player.config.clickToPlay && !this.player.isAudio) {
if (
this.player.supported.ui &&
this.player.config.clickToPlay &&
!this.player.isAudio
) {
// Re-fetch the wrapper
const wrapper = getElement.call(this.player, `.${this.player.config.classNames.video}`);
const wrapper = getElement.call(
this.player,
`.${this.player.config.classNames.video}`,
);
// Bail if there's no wrapper (this should never happen)
if (!is.element(wrapper)) {
@ -325,7 +473,11 @@ class Listeners {
// On click play, pause ore restart
on.call(this.player, wrapper, 'click', () => {
// Touch devices will just show controls (if we're hiding controls)
if (this.player.config.hideControls && this.player.touch && !this.player.paused) {
if (
this.player.config.hideControls &&
this.player.touch &&
!this.player.paused
) {
return;
}
@ -356,7 +508,10 @@ class Listeners {
// Volume change
on.call(this.player, this.player.media, 'volumechange', () => {
// Save to storage
this.player.storage.set({ volume: this.player.volume, muted: this.player.muted });
this.player.storage.set({
volume: this.player.volume,
muted: this.player.muted,
});
});
// Speed change
@ -377,12 +532,20 @@ class Listeners {
// Quality change
on.call(this.player, this.player.media, 'qualitychange', event => {
// Update UI
controls.updateSetting.call(this.player, 'quality', null, event.detail.quality);
controls.updateSetting.call(
this.player,
'quality',
null,
event.detail.quality,
);
});
// Proxy events to container
// Bubble up key events for Edge
const proxyEvents = this.player.config.events.concat(['keyup', 'keydown']).join(' ');
const proxyEvents = this.player.config.events
.concat(['keyup', 'keydown'])
.join(' ');
on.call(this.player, this.player.media, proxyEvents, event => {
let { detail = {} } = event;
@ -391,7 +554,13 @@ class Listeners {
detail = this.player.media.error;
}
triggerEvent.call(this.player, this.player.elements.container, event.type, true, detail);
triggerEvent.call(
this.player,
this.player.elements.container,
event.type,
true,
detail,
);
});
}
@ -439,13 +608,28 @@ class Listeners {
}
// Pause
this.bind(this.player.elements.buttons.restart, 'click', this.player.restart, 'restart');
this.bind(
this.player.elements.buttons.restart,
'click',
this.player.restart,
'restart',
);
// Rewind
this.bind(this.player.elements.buttons.rewind, 'click', this.player.rewind, 'rewind');
this.bind(
this.player.elements.buttons.rewind,
'click',
this.player.rewind,
'rewind',
);
// Rewind
this.bind(this.player.elements.buttons.fastForward, 'click', this.player.forward, 'fastForward');
this.bind(
this.player.elements.buttons.fastForward,
'click',
this.player.forward,
'fastForward',
);
// Mute toggle
this.bind(
@ -458,7 +642,9 @@ class Listeners {
);
// Captions toggle
this.bind(this.player.elements.buttons.captions, 'click', () => this.player.toggleCaptions());
this.bind(this.player.elements.buttons.captions, 'click', () =>
this.player.toggleCaptions(),
);
// Fullscreen toggle
this.bind(
@ -481,7 +667,12 @@ class Listeners {
);
// Airplay
this.bind(this.player.elements.buttons.airplay, 'click', this.player.airplay, 'airplay');
this.bind(
this.player.elements.buttons.airplay,
'click',
this.player.airplay,
'airplay',
);
// Settings menu - click toggle
this.bind(this.player.elements.buttons.settings, 'click', event => {
@ -512,37 +703,51 @@ class Listeners {
);
// Set range input alternative "value", which matches the tooltip time (#954)
this.bind(this.player.elements.inputs.seek, 'mousedown mousemove', event => {
const clientRect = this.player.elements.progress.getBoundingClientRect();
const percent = 100 / clientRect.width * (event.pageX - clientRect.left);
event.currentTarget.setAttribute('seek-value', percent);
});
this.bind(
this.player.elements.inputs.seek,
'mousedown mousemove',
event => {
const clientRect = this.player.elements.progress.getBoundingClientRect();
const percent =
100 / clientRect.width * (event.pageX - clientRect.left);
event.currentTarget.setAttribute('seek-value', percent);
},
);
// Pause while seeking
this.bind(this.player.elements.inputs.seek, 'mousedown mouseup keydown keyup touchstart touchend', event => {
const seek = event.currentTarget;
this.bind(
this.player.elements.inputs.seek,
'mousedown mouseup keydown keyup touchstart touchend',
event => {
const seek = event.currentTarget;
const code = event.keyCode ? event.keyCode : event.which;
const eventType = event.type;
const code = event.keyCode ? event.keyCode : event.which;
const eventType = event.type;
if ((eventType === 'keydown' || eventType === 'keyup') && (code !== 39 && code !== 37)) {
return;
}
// Was playing before?
const play = seek.hasAttribute('play-on-seeked');
if (
(eventType === 'keydown' || eventType === 'keyup') &&
(code !== 39 && code !== 37)
) {
return;
}
// Was playing before?
const play = seek.hasAttribute('play-on-seeked');
// Done seeking
const done = ['mouseup', 'touchend', 'keyup'].includes(event.type);
// Done seeking
const done = ['mouseup', 'touchend', 'keyup'].includes(
event.type,
);
// If we're done seeking and it was playing, resume playback
if (play && done) {
seek.removeAttribute('play-on-seeked');
this.player.play();
} else if (!done && this.player.playing) {
seek.setAttribute('play-on-seeked', '');
this.player.pause();
}
});
// If we're done seeking and it was playing, resume playback
if (play && done) {
seek.removeAttribute('play-on-seeked');
this.player.play();
} else if (!done && this.player.playing) {
seek.setAttribute('play-on-seeked', '');
this.player.pause();
}
},
);
// Seek
this.bind(
@ -560,14 +765,18 @@ class Listeners {
seek.removeAttribute('seek-value');
this.player.currentTime = seekTo / seek.max * this.player.duration;
this.player.currentTime =
seekTo / seek.max * this.player.duration;
},
'seek',
);
// Current time invert
// Only if one time element is used for both currentTime and duration
if (this.player.config.toggleInvert && !is.element(this.player.elements.display.duration)) {
if (
this.player.config.toggleInvert &&
!is.element(this.player.elements.display.duration)
) {
this.bind(this.player.elements.display.currentTime, 'click', () => {
// Do nothing if we're at the start
if (this.player.currentTime === 0) {
@ -592,32 +801,54 @@ class Listeners {
// Polyfill for lower fill in <input type="range"> for webkit
if (browser.isWebkit) {
Array.from(getElements.call(this.player, 'input[type="range"]')).forEach(element => {
this.bind(element, 'input', event => controls.updateRangeFill.call(this.player, event.target));
Array.from(
getElements.call(this.player, 'input[type="range"]'),
).forEach(element => {
this.bind(element, 'input', event =>
controls.updateRangeFill.call(this.player, event.target),
);
});
}
// Seek tooltip
this.bind(this.player.elements.progress, 'mouseenter mouseleave mousemove', event =>
controls.updateSeekTooltip.call(this.player, event),
this.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)
this.bind(this.player.elements.controls, 'mouseenter mouseleave', event => {
this.player.elements.controls.hover = !this.player.touch && event.type === 'mouseenter';
});
this.bind(
this.player.elements.controls,
'mouseenter mouseleave',
event => {
this.player.elements.controls.hover =
!this.player.touch && event.type === 'mouseenter';
},
);
// Update controls.pressed state (used for ui.toggleControls to avoid hiding when interacting)
this.bind(this.player.elements.controls, 'mousedown mouseup touchstart touchend touchcancel', event => {
this.player.elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
});
this.bind(
this.player.elements.controls,
'mousedown mouseup touchstart touchend touchcancel',
event => {
this.player.elements.controls.pressed = [
'mousedown',
'touchstart',
].includes(event.type);
},
);
// Focus in/out on controls
this.bind(this.player.elements.controls, 'focusin focusout', event => {
const { config, elements, timers } = this.player;
// Skip transition to prevent focus from scrolling the parent element
toggleClass(elements.controls, config.classNames.noTransition, event.type === 'focusin');
toggleClass(
elements.controls,
config.classNames.noTransition,
event.type === 'focusin',
);
// Toggle
ui.toggleControls.call(this.player, event.type === 'focusin');
@ -626,7 +857,11 @@ class Listeners {
if (event.type === 'focusin') {
// Restore transition
setTimeout(() => {
toggleClass(elements.controls, config.classNames.noTransition, false);
toggleClass(
elements.controls,
config.classNames.noTransition,
false,
);
}, 0);
// Delay a little more for keyboard users
@ -634,8 +869,12 @@ class Listeners {
// Clear timer
clearTimeout(timers.controls);
// Hide
timers.controls = setTimeout(() => ui.toggleControls.call(this.player, false), delay);
timers.controls = setTimeout(
() => ui.toggleControls.call(this.player, false),
delay,
);
}
});
@ -649,7 +888,9 @@ class Listeners {
const inverted = event.webkitDirectionInvertedFromDevice;
// Get delta from event. Invert if `inverted` is true
const [x, y] = [event.deltaX, -event.deltaY].map(value => (inverted ? -value : value));
const [x, y] = [event.deltaX, -event.deltaY].map(
value => (inverted ? -value : value),
);
// Using the biggest delta, normalize to 1 or -1 (or 0 if no delta)
const direction = Math.sign(Math.abs(x) > Math.abs(y) ? x : y);
@ -659,7 +900,10 @@ class Listeners {
// Don't break page scrolling at max and min
const { volume } = this.player.media;
if ((direction === 1 && volume < 1) || (direction === -1 && volume > 0)) {
if (
(direction === 1 && volume < 1) ||
(direction === -1 && volume > 0)
) {
event.preventDefault();
}
},

View File

@ -116,7 +116,11 @@ export function emptyElement(element) {
// Replace element
export function replaceElement(newChild, oldChild) {
if (!is.element(oldChild) || !is.element(oldChild.parentNode) || !is.element(newChild)) {
if (
!is.element(oldChild) ||
!is.element(oldChild.parentNode) ||
!is.element(newChild)
) {
return null;
}
@ -203,6 +207,10 @@ export function toggleHidden(element, hidden) {
// Mirror Element.classList.toggle, with IE compatibility for "force" argument
export function toggleClass(element, className, force) {
if (is.nodeList(element)) {
return Array.from(element).map(e => toggleClass(e, className, force));
}
if (is.element(element)) {
let method = 'toggle';
if (typeof force !== 'undefined') {
@ -213,7 +221,7 @@ export function toggleClass(element, className, force) {
return element.classList.contains(className);
}
return null;
return false;
}
// Has class name
@ -249,26 +257,16 @@ export function getElement(selector) {
return this.elements.container.querySelector(selector);
}
// Get the focused element
export function getFocusElement() {
let focused = document.activeElement;
if (!focused || focused === document.body) {
focused = null;
} else {
focused = document.querySelector(':focus');
}
return focused;
}
// Trap focus inside container
export function trapFocus(element = null, toggle = false) {
if (!is.element(element)) {
return;
}
const focusable = getElements.call(this, 'button:not(:disabled), input:not(:disabled), [tabindex]');
const focusable = getElements.call(
this,
'button:not(:disabled), input:not(:disabled), [tabindex]',
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
@ -279,7 +277,7 @@ export function trapFocus(element = null, toggle = false) {
}
// Get the current focused element
const focused = getFocusElement();
const focused = document.activeElement;
if (focused === last && !event.shiftKey) {
// Move focus to first element that can be tabbed if Shift isn't used
@ -292,7 +290,14 @@ export function trapFocus(element = null, toggle = false) {
}
};
toggleListener.call(this, this.elements.container, 'keydown', trap, toggle, false);
toggleListener.call(
this,
this.elements.container,
'keydown',
trap,
toggle,
false,
);
}
// Set focus and tab focus class

View File

@ -27,9 +27,21 @@ const supportsPassiveListeners = (() => {
})();
// Toggle event listener
export function toggleListener(element, event, callback, toggle = false, passive = true, capture = false) {
export function toggleListener(
element,
event,
callback,
toggle = false,
passive = true,
capture = false,
) {
// Bail if no element, event, or callback
if (!element || !('addEventListener' in element) || is.empty(event) || !is.function(callback)) {
if (
!element ||
!('addEventListener' in element) ||
is.empty(event) ||
!is.function(callback)
) {
return;
}
@ -57,28 +69,74 @@ export function toggleListener(element, event, callback, toggle = false, passive
this.eventListeners.push({ element, type, callback, options });
}
element[toggle ? 'addEventListener' : 'removeEventListener'](type, callback, options);
element[toggle ? 'addEventListener' : 'removeEventListener'](
type,
callback,
options,
);
});
}
// Bind event handler
export function on(element, events = '', callback, passive = true, capture = false) {
toggleListener.call(this, element, events, callback, true, passive, capture);
export function on(
element,
events = '',
callback,
passive = true,
capture = false,
) {
toggleListener.call(
this,
element,
events,
callback,
true,
passive,
capture,
);
}
// Unbind event handler
export function off(element, events = '', callback, passive = true, capture = false) {
toggleListener.call(this, element, events, callback, false, passive, capture);
export function off(
element,
events = '',
callback,
passive = true,
capture = false,
) {
toggleListener.call(
this,
element,
events,
callback,
false,
passive,
capture,
);
}
// Bind once-only event handler
export function once(element, events = '', callback, passive = true, capture = false) {
export function once(
element,
events = '',
callback,
passive = true,
capture = false,
) {
function onceCallback(...args) {
off(element, events, onceCallback, passive, capture);
callback.apply(this, args);
}
toggleListener.call(this, element, events, onceCallback, true, passive, capture);
toggleListener.call(
this,
element,
events,
onceCallback,
true,
passive,
capture,
);
}
// Trigger event
@ -115,6 +173,9 @@ 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)),
resolve =>
this.ready
? setTimeout(resolve, 0)
: on.call(this, this.elements.container, 'ready', resolve),
).then(() => {});
}

145
yarn.lock
View File

@ -2,6 +2,82 @@
# yarn lockfile v1
"@babel/code-frame@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz#2a02643368de80916162be70865c97774f3adbd9"
dependencies:
"@babel/highlight" "7.0.0-beta.44"
"@babel/generator@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42"
dependencies:
"@babel/types" "7.0.0-beta.44"
jsesc "^2.5.1"
lodash "^4.2.0"
source-map "^0.5.0"
trim-right "^1.0.1"
"@babel/helper-function-name@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz#e18552aaae2231100a6e485e03854bc3532d44dd"
dependencies:
"@babel/helper-get-function-arity" "7.0.0-beta.44"
"@babel/template" "7.0.0-beta.44"
"@babel/types" "7.0.0-beta.44"
"@babel/helper-get-function-arity@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15"
dependencies:
"@babel/types" "7.0.0-beta.44"
"@babel/helper-split-export-declaration@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc"
dependencies:
"@babel/types" "7.0.0-beta.44"
"@babel/highlight@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5"
dependencies:
chalk "^2.0.0"
esutils "^2.0.2"
js-tokens "^3.0.0"
"@babel/template@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.44.tgz#f8832f4fdcee5d59bf515e595fc5106c529b394f"
dependencies:
"@babel/code-frame" "7.0.0-beta.44"
"@babel/types" "7.0.0-beta.44"
babylon "7.0.0-beta.44"
lodash "^4.2.0"
"@babel/traverse@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966"
dependencies:
"@babel/code-frame" "7.0.0-beta.44"
"@babel/generator" "7.0.0-beta.44"
"@babel/helper-function-name" "7.0.0-beta.44"
"@babel/helper-split-export-declaration" "7.0.0-beta.44"
"@babel/types" "7.0.0-beta.44"
babylon "7.0.0-beta.44"
debug "^3.1.0"
globals "^11.1.0"
invariant "^2.2.0"
lodash "^4.2.0"
"@babel/types@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757"
dependencies:
esutils "^2.0.2"
lodash "^4.2.0"
to-fast-properties "^2.0.0"
"@gulp-sourcemaps/identity-map@1.X":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/identity-map/-/identity-map-1.0.1.tgz#cfa23bc5840f9104ce32a65e74db7e7a974bbee1"
@ -388,14 +464,16 @@ babel-core@^6.26.3:
slash "^1.0.0"
source-map "^0.5.7"
babel-eslint@^7.2.3:
version "7.2.3"
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.2.3.tgz#b2fe2d80126470f5c19442dc757253a897710827"
babel-eslint@^8.2.5:
version "8.2.5"
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.5.tgz#dc2331c259d36782aa189da510c43dedd5adc7a3"
dependencies:
babel-code-frame "^6.22.0"
babel-traverse "^6.23.1"
babel-types "^6.23.0"
babylon "^6.17.0"
"@babel/code-frame" "7.0.0-beta.44"
"@babel/traverse" "7.0.0-beta.44"
"@babel/types" "7.0.0-beta.44"
babylon "7.0.0-beta.44"
eslint-scope "~3.7.1"
eslint-visitor-keys "^1.0.0"
babel-generator@^6.26.0:
version "6.26.1"
@ -810,7 +888,7 @@ babel-template@^6.24.1, babel-template@^6.26.0:
babylon "^6.18.0"
lodash "^4.17.4"
babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
babel-traverse@^6.24.1, babel-traverse@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
dependencies:
@ -824,7 +902,7 @@ babel-traverse@^6.23.1, babel-traverse@^6.24.1, babel-traverse@^6.26.0:
invariant "^2.2.2"
lodash "^4.17.4"
babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.26.0:
babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
dependencies:
@ -833,7 +911,11 @@ babel-types@^6.19.0, babel-types@^6.23.0, babel-types@^6.24.1, babel-types@^6.26
lodash "^4.17.4"
to-fast-properties "^1.0.3"
babylon@^6.17.0, babylon@^6.18.0:
babylon@7.0.0-beta.44:
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d"
babylon@^6.18.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
@ -1838,7 +1920,7 @@ eslint-restricted-globals@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7"
eslint-scope@^3.7.1:
eslint-scope@^3.7.1, eslint-scope@~3.7.1:
version "3.7.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
dependencies:
@ -1852,6 +1934,10 @@ eslint-scope@^4.0.0:
esrecurse "^4.1.0"
estraverse "^4.1.1"
eslint-utils@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512"
eslint-visitor-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
@ -1899,9 +1985,9 @@ eslint@^4.0.0:
table "4.0.2"
text-table "~0.2.0"
eslint@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.0.1.tgz#109b90ab7f7a736f54e0f341c8bb9d09777494c3"
eslint@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.1.0.tgz#2ed611f1ce163c0fb99e1e0cda5af8f662dff645"
dependencies:
ajv "^6.5.0"
babel-code-frame "^6.26.0"
@ -1910,6 +1996,7 @@ eslint@^5.0.1:
debug "^3.1.0"
doctrine "^2.1.0"
eslint-scope "^4.0.0"
eslint-utils "^1.3.1"
eslint-visitor-keys "^1.0.0"
espree "^4.0.0"
esquery "^1.0.1"
@ -1917,7 +2004,7 @@ eslint@^5.0.1:
file-entry-cache "^2.0.0"
functional-red-black-tree "^1.0.1"
glob "^7.1.2"
globals "^11.5.0"
globals "^11.7.0"
ignore "^3.3.3"
imurmurhash "^0.1.4"
inquirer "^5.2.0"
@ -2513,7 +2600,7 @@ globals@^11.0.1:
version "11.3.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.3.0.tgz#e04fdb7b9796d8adac9c8f64c14837b2313378b0"
globals@^11.5.0:
globals@^11.1.0, globals@^11.7.0:
version "11.7.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673"
@ -3122,7 +3209,7 @@ interpret@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
invariant@^2.2.2:
invariant@^2.2.0, invariant@^2.2.2:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
dependencies:
@ -3528,6 +3615,10 @@ jsesc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
jsesc@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe"
jsesc@~0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
@ -3947,7 +4038,7 @@ lodash@>=3.10.0, lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.3.0, l
version "4.17.5"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
lodash@^4.17.10, lodash@^4.17.5:
lodash@^4.17.10, lodash@^4.17.5, lodash@^4.2.0:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
@ -5092,9 +5183,9 @@ randomatic@^1.1.3:
is-number "^3.0.0"
kind-of "^4.0.0"
raven-js@^3.26.2:
version "3.26.2"
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.26.2.tgz#9153af2416e96ccf4e0b9cbc6c90c34dda0d7e88"
raven-js@^3.26.3:
version "3.26.3"
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.26.3.tgz#0efb49969b5b11ab965f7b0d6da4ca102b763cb0"
rc@^1.0.1, rc@^1.1.6:
version "1.2.6"
@ -5543,9 +5634,9 @@ rimraf@2, rimraf@^2.2.8:
dependencies:
glob "^7.0.5"
rollup-plugin-babel@^3.0.5:
version "3.0.5"
resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-3.0.5.tgz#9769a7977098da1dce5b5888fe38dfd8666bf08d"
rollup-plugin-babel@^3.0.7:
version "3.0.7"
resolved "https://registry.yarnpkg.com/rollup-plugin-babel/-/rollup-plugin-babel-3.0.7.tgz#5b13611f1ab8922497e9d15197ae5d8a23fe3b1e"
dependencies:
rollup-pluginutils "^1.5.0"
@ -5791,7 +5882,7 @@ source-map-url@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.3.0.tgz#7ecaf13b57bcd09da8a40c5d269db33799d4aaf9"
source-map@0.5.x, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7:
source-map@0.5.x, source-map@^0.5.0, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@ -6389,6 +6480,10 @@ to-fast-properties@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
to-object-path@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"