Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c8a337f26 | |||
| c24e52d97d | |||
| 574f40949c | |||
| cf3848fbd5 | |||
| a19ad69038 | |||
| d6f20e2756 | |||
| e2fc20ca76 | |||
| 37c3f7109d | |||
| 99d5211a33 | |||
| b97f143195 | |||
| e8da4326b6 | |||
| 67f908aa8d | |||
| 65eb5c1b8b | |||
| 7d484c6e09 | |||
| 8252e13eb9 | |||
| 1a9b860e68 | |||
| cede7d0f35 | |||
| fe26d383f1 | |||
| df4bc268dc | |||
| e49da6c13f | |||
| 67b7262764 | |||
| 88528ef979 | |||
| b6175b1ca9 | |||
| aa20ebaa9c | |||
| 779e45c11b | |||
| 5d5a6eabaa | |||
| 03c9b53232 | |||
| ebaded66b4 | |||
| c232eb2478 | |||
| 7559cc6897 | |||
| 69d0d6d7ee | |||
| 3e9336b15d | |||
| 5c78ecc15d | |||
| 06db3f702d | |||
| a2a82a96a6 | |||
| a86bbae851 | |||
| fac134dd95 | |||
| 515ae32160 | |||
| df8f040795 | |||
| 64a23992f0 | |||
| f5baff6e6b | |||
| 8bdd90a2a8 | |||
| 5536e97482 | |||
| de47071256 | |||
| 87072cb690 | |||
| d9565e9250 | |||
| f80b568e67 | |||
| 7fed689f9a | |||
| 3f48df8f10 | |||
| cc55092ca6 | |||
| 3331d9d01d | |||
| 62d80e6b76 | |||
| 7dc4d9cd22 | |||
| 8fb8ae1260 | |||
| 90304369f4 | |||
| 922456c46c | |||
| eaeccd66ae | |||
| 7a43649c13 | |||
| 525bbf313e | |||
| cfaebe9bf2 | |||
| b57b7b2153 | |||
| 48bf368316 | |||
| 8f94ce86a0 | |||
| 10a9cf08f1 | |||
| 286d0d1794 | |||
| 95f6fa2731 | |||
| 1aeef81288 | |||
| 211ad6c8f5 | |||
| 468b20d227 | |||
| f6bc42c2bc | |||
| 2c01b8ba76 | |||
| 4e1df8677f | |||
| 6953a12e2a | |||
| 1d0db89194 | |||
| 297f297d18 | |||
| 059205c378 | |||
| f94e53ffb1 | |||
| a4f1fdec5d | |||
| 75374eb154 | |||
| dbf768b1bd | |||
| a8f8486cf4 | |||
| a343e58e53 |
@@ -28,6 +28,7 @@ controls: [
|
|||||||
'settings', // Settings menu
|
'settings', // Settings menu
|
||||||
'pip', // Picture-in-picture (currently Safari only)
|
'pip', // Picture-in-picture (currently Safari only)
|
||||||
'airplay', // Airplay (currently Safari only)
|
'airplay', // Airplay (currently Safari only)
|
||||||
|
'download', // Show a download button with a link to either the current source or a custom URL you specify in your options
|
||||||
'fullscreen', // Toggle fullscreen
|
'fullscreen', // Toggle fullscreen
|
||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1833,7 +1833,6 @@ typeof navigator === "object" && (function () {
|
|||||||
};
|
};
|
||||||
this._fetchDefaults = {
|
this._fetchDefaults = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
keepalive: true,
|
|
||||||
// Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default
|
// Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default
|
||||||
// https://caniuse.com/#feat=referrer-policy
|
// https://caniuse.com/#feat=referrer-policy
|
||||||
// It doesn't. And it throw exception instead of ignoring this parameter...
|
// It doesn't. And it throw exception instead of ignoring this parameter...
|
||||||
@@ -1874,7 +1873,7 @@ typeof navigator === "object" && (function () {
|
|||||||
// webpack (using a build step causes webpack #1617). Grunt verifies that
|
// webpack (using a build step causes webpack #1617). Grunt verifies that
|
||||||
// this value matches package.json during build.
|
// this value matches package.json during build.
|
||||||
// See: https://github.com/getsentry/raven-js/issues/465
|
// See: https://github.com/getsentry/raven-js/issues/465
|
||||||
VERSION: '3.26.4',
|
VERSION: '3.27.0',
|
||||||
|
|
||||||
debug: false,
|
debug: false,
|
||||||
|
|
||||||
@@ -2612,7 +2611,7 @@ typeof navigator === "object" && (function () {
|
|||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
options = Object.assign(
|
options = objectMerge$1(
|
||||||
{
|
{
|
||||||
eventId: this.lastEventId(),
|
eventId: this.lastEventId(),
|
||||||
dsn: this._dsn,
|
dsn: this._dsn,
|
||||||
@@ -4093,275 +4092,263 @@ typeof navigator === "object" && (function () {
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
var host = window.location.host;
|
var host = window.location.host;
|
||||||
|
var env = {
|
||||||
|
prod: host === 'plyr.io',
|
||||||
|
dev: host === 'dev.plyr.io'
|
||||||
|
};
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
singleton.context(function () {
|
||||||
|
var selector = '#player';
|
||||||
|
var container = document.getElementById('container');
|
||||||
|
|
||||||
var env = {
|
if (window.shr) {
|
||||||
prod: host === 'plyr.io',
|
window.shr.setup({
|
||||||
dev: host === 'dev.plyr.io'
|
count: {
|
||||||
};
|
classname: 'button__count'
|
||||||
|
}
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
singleton.context(function () {
|
|
||||||
var selector = '#player';
|
|
||||||
var container = document.getElementById('container');
|
|
||||||
|
|
||||||
if (window.shr) {
|
|
||||||
window.shr.setup({
|
|
||||||
count: {
|
|
||||||
classname: 'button__count'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup tab focus
|
|
||||||
var tabClassName = 'tab-focus';
|
|
||||||
|
|
||||||
// Remove class on blur
|
|
||||||
document.addEventListener('focusout', function (event) {
|
|
||||||
if (container.contains(event.target)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.target.classList.remove(tabClassName);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add classname to tabbed elements
|
|
||||||
document.addEventListener('keydown', function (event) {
|
|
||||||
if (event.keyCode !== 9) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delay the adding of classname until the focus has changed
|
|
||||||
// This event fires before the focusin event
|
|
||||||
setTimeout(function () {
|
|
||||||
var focused = document.activeElement;
|
|
||||||
|
|
||||||
if (!focused || container.contains(focused)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
focused.classList.add(tabClassName);
|
|
||||||
}, 10);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Setup the player
|
|
||||||
var player = new Plyr(selector, {
|
|
||||||
debug: true,
|
|
||||||
title: 'View From A Blue Moon',
|
|
||||||
iconUrl: '../dist/plyr.svg',
|
|
||||||
keyboard: {
|
|
||||||
global: true
|
|
||||||
},
|
|
||||||
tooltips: {
|
|
||||||
controls: true
|
|
||||||
},
|
|
||||||
captions: {
|
|
||||||
active: true
|
|
||||||
},
|
|
||||||
keys: {
|
|
||||||
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c'
|
|
||||||
},
|
|
||||||
ads: {
|
|
||||||
enabled: env.prod || env.dev,
|
|
||||||
publisherId: '918848828995742'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Expose for tinkering in the console
|
|
||||||
window.player = player;
|
|
||||||
|
|
||||||
// Setup type toggle
|
|
||||||
var buttons = document.querySelectorAll('[data-source]');
|
|
||||||
var types = {
|
|
||||||
video: 'video',
|
|
||||||
audio: 'audio',
|
|
||||||
youtube: 'youtube',
|
|
||||||
vimeo: 'vimeo'
|
|
||||||
};
|
|
||||||
var currentType = window.location.hash.replace('#', '');
|
|
||||||
var historySupport = window.history && window.history.pushState;
|
|
||||||
|
|
||||||
// Toggle class on an element
|
|
||||||
function toggleClass(element, className, state) {
|
|
||||||
if (element) {
|
|
||||||
element.classList[state ? 'add' : 'remove'](className);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a new source
|
|
||||||
function newSource(type, init) {
|
|
||||||
// Bail if new type isn't known, it's the current type, or current type is empty (video is default) and new type is video
|
|
||||||
if (!(type in types) || !init && type === currentType || !currentType.length && type === types.video) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case types.video:
|
|
||||||
player.source = {
|
|
||||||
type: 'video',
|
|
||||||
title: 'View From A Blue Moon',
|
|
||||||
sources: [{
|
|
||||||
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',
|
|
||||||
type: 'video/mp4',
|
|
||||||
size: 720
|
|
||||||
}, {
|
|
||||||
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',
|
|
||||||
type: 'video/mp4',
|
|
||||||
size: 1440
|
|
||||||
}],
|
|
||||||
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',
|
|
||||||
default: true
|
|
||||||
}, {
|
|
||||||
kind: 'captions',
|
|
||||||
label: 'French',
|
|
||||||
srclang: 'fr',
|
|
||||||
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case types.audio:
|
|
||||||
player.source = {
|
|
||||||
type: 'audio',
|
|
||||||
title: 'Kishi Bashi – “It All Began With A Burst”',
|
|
||||||
sources: [{
|
|
||||||
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',
|
|
||||||
type: 'audio/ogg'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case types.youtube:
|
|
||||||
player.source = {
|
|
||||||
type: 'video',
|
|
||||||
sources: [{
|
|
||||||
src: 'https://youtube.com/watch?v=bTqVqk7FSmY',
|
|
||||||
provider: 'youtube'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case types.vimeo:
|
|
||||||
player.source = {
|
|
||||||
type: 'video',
|
|
||||||
sources: [{
|
|
||||||
src: 'https://vimeo.com/76979871',
|
|
||||||
provider: 'vimeo'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the current type for next time
|
|
||||||
currentType = type;
|
|
||||||
|
|
||||||
// Remove active classes
|
|
||||||
Array.from(buttons).forEach(function (button) {
|
|
||||||
return toggleClass(button.parentElement, 'active', false);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set active on parent
|
|
||||||
toggleClass(document.querySelector('[data-source="' + type + '"]'), 'active', true);
|
|
||||||
|
|
||||||
// Show cite
|
|
||||||
Array.from(document.querySelectorAll('.plyr__cite')).forEach(function (cite) {
|
|
||||||
cite.setAttribute('hidden', '');
|
|
||||||
});
|
|
||||||
document.querySelector('.plyr__cite--' + type).removeAttribute('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind to each button
|
|
||||||
Array.from(buttons).forEach(function (button) {
|
|
||||||
button.addEventListener('click', function () {
|
|
||||||
var type = button.getAttribute('data-source');
|
|
||||||
|
|
||||||
newSource(type);
|
|
||||||
|
|
||||||
if (historySupport) {
|
|
||||||
window.history.pushState({ type: type }, '', '#' + type);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// List for backwards/forwards
|
|
||||||
window.addEventListener('popstate', function (event) {
|
|
||||||
if (event.state && 'type' in event.state) {
|
|
||||||
newSource(event.state.type);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// On load
|
|
||||||
if (historySupport) {
|
|
||||||
var video = !currentType.length;
|
|
||||||
|
|
||||||
// If there's no current type set, assume video
|
|
||||||
if (video) {
|
|
||||||
currentType = types.video;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace current history state
|
|
||||||
if (currentType in types) {
|
|
||||||
window.history.replaceState({
|
|
||||||
type: currentType
|
|
||||||
}, '', video ? '' : '#' + currentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's not video, load the source
|
|
||||||
if (currentType !== types.video) {
|
|
||||||
newSource(currentType, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
} // Setup tab focus
|
||||||
|
|
||||||
// Raven / Sentry
|
|
||||||
// For demo site (https://plyr.io) only
|
|
||||||
if (env.prod) {
|
|
||||||
singleton.config('https://d4ad9866ad834437a4754e23937071e4@sentry.io/305555').install();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Google analytics
|
var tabClassName = 'tab-focus'; // Remove class on blur
|
||||||
// For demo site (https://plyr.io) only
|
|
||||||
/* eslint-disable */
|
document.addEventListener('focusout', function (event) {
|
||||||
if (env.prod) {
|
if (!event.target.classList || container.contains(event.target)) {
|
||||||
(function (i, s, o, g, r, a, m) {
|
return;
|
||||||
i.GoogleAnalyticsObject = r;
|
}
|
||||||
i[r] = i[r] || function () {
|
|
||||||
(i[r].q = i[r].q || []).push(arguments);
|
event.target.classList.remove(tabClassName);
|
||||||
|
}); // Add classname to tabbed elements
|
||||||
|
|
||||||
|
document.addEventListener('keydown', function (event) {
|
||||||
|
if (event.keyCode !== 9) {
|
||||||
|
return;
|
||||||
|
} // Delay the adding of classname until the focus has changed
|
||||||
|
// This event fires before the focusin event
|
||||||
|
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
var focused = document.activeElement;
|
||||||
|
|
||||||
|
if (!focused || !focused.classList || container.contains(focused)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
focused.classList.add(tabClassName);
|
||||||
|
}, 10);
|
||||||
|
}); // Setup the player
|
||||||
|
|
||||||
|
var player = new Plyr(selector, {
|
||||||
|
debug: true,
|
||||||
|
title: 'View From A Blue Moon',
|
||||||
|
iconUrl: '../dist/plyr.svg',
|
||||||
|
keyboard: {
|
||||||
|
global: true
|
||||||
|
},
|
||||||
|
tooltips: {
|
||||||
|
controls: true
|
||||||
|
},
|
||||||
|
captions: {
|
||||||
|
active: true
|
||||||
|
},
|
||||||
|
keys: {
|
||||||
|
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c'
|
||||||
|
},
|
||||||
|
ads: {
|
||||||
|
enabled: env.prod || env.dev,
|
||||||
|
publisherId: '918848828995742'
|
||||||
|
}
|
||||||
|
}); // Expose for tinkering in the console
|
||||||
|
|
||||||
|
window.player = player; // Setup type toggle
|
||||||
|
|
||||||
|
var buttons = document.querySelectorAll('[data-source]');
|
||||||
|
var types = {
|
||||||
|
video: 'video',
|
||||||
|
audio: 'audio',
|
||||||
|
youtube: 'youtube',
|
||||||
|
vimeo: 'vimeo'
|
||||||
|
};
|
||||||
|
var currentType = window.location.hash.replace('#', '');
|
||||||
|
var historySupport = window.history && window.history.pushState; // Toggle class on an element
|
||||||
|
|
||||||
|
function toggleClass(element, className, state) {
|
||||||
|
if (element) {
|
||||||
|
element.classList[state ? 'add' : 'remove'](className);
|
||||||
|
}
|
||||||
|
} // Set a new source
|
||||||
|
|
||||||
|
|
||||||
|
function newSource(type, init) {
|
||||||
|
// Bail if new type isn't known, it's the current type, or current type is empty (video is default) and new type is video
|
||||||
|
if (!(type in types) || !init && type === currentType || !currentType.length && type === types.video) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case types.video:
|
||||||
|
player.source = {
|
||||||
|
type: 'video',
|
||||||
|
title: 'View From A Blue Moon',
|
||||||
|
sources: [{
|
||||||
|
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',
|
||||||
|
type: 'video/mp4',
|
||||||
|
size: 720
|
||||||
|
}, {
|
||||||
|
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',
|
||||||
|
type: 'video/mp4',
|
||||||
|
size: 1440
|
||||||
|
}],
|
||||||
|
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',
|
||||||
|
default: true
|
||||||
|
}, {
|
||||||
|
kind: 'captions',
|
||||||
|
label: 'French',
|
||||||
|
srclang: 'fr',
|
||||||
|
src: 'https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt'
|
||||||
|
}]
|
||||||
};
|
};
|
||||||
i[r].l = 1 * new Date();
|
break;
|
||||||
a = s.createElement(o);
|
|
||||||
m = s.getElementsByTagName(o)[0];
|
case types.audio:
|
||||||
a.async = 1;
|
player.source = {
|
||||||
a.src = g;
|
type: 'audio',
|
||||||
m.parentNode.insertBefore(a, m);
|
title: 'Kishi Bashi – “It All Began With A Burst”',
|
||||||
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
|
sources: [{
|
||||||
window.ga('create', 'UA-40881672-11', 'auto');
|
src: 'https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3',
|
||||||
window.ga('send', 'pageview');
|
type: 'audio/mp3'
|
||||||
}
|
}, {
|
||||||
/* eslint-enable */
|
src: 'https://cdn.plyr.io/static/demo/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg',
|
||||||
|
type: 'audio/ogg'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case types.youtube:
|
||||||
|
player.source = {
|
||||||
|
type: 'video',
|
||||||
|
sources: [{
|
||||||
|
src: 'https://youtube.com/watch?v=bTqVqk7FSmY',
|
||||||
|
provider: 'youtube'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case types.vimeo:
|
||||||
|
player.source = {
|
||||||
|
type: 'video',
|
||||||
|
sources: [{
|
||||||
|
src: 'https://vimeo.com/76979871',
|
||||||
|
provider: 'vimeo'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
} // Set the current type for next time
|
||||||
|
|
||||||
|
|
||||||
|
currentType = type; // Remove active classes
|
||||||
|
|
||||||
|
Array.from(buttons).forEach(function (button) {
|
||||||
|
return toggleClass(button.parentElement, 'active', false);
|
||||||
|
}); // Set active on parent
|
||||||
|
|
||||||
|
toggleClass(document.querySelector("[data-source=\"".concat(type, "\"]")), 'active', true); // Show cite
|
||||||
|
|
||||||
|
Array.from(document.querySelectorAll('.plyr__cite')).forEach(function (cite) {
|
||||||
|
cite.setAttribute('hidden', '');
|
||||||
|
});
|
||||||
|
document.querySelector(".plyr__cite--".concat(type)).removeAttribute('hidden');
|
||||||
|
} // Bind to each button
|
||||||
|
|
||||||
|
|
||||||
|
Array.from(buttons).forEach(function (button) {
|
||||||
|
button.addEventListener('click', function () {
|
||||||
|
var type = button.getAttribute('data-source');
|
||||||
|
newSource(type);
|
||||||
|
|
||||||
|
if (historySupport) {
|
||||||
|
window.history.pushState({
|
||||||
|
type: type
|
||||||
|
}, '', "#".concat(type));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}); // List for backwards/forwards
|
||||||
|
|
||||||
|
window.addEventListener('popstate', function (event) {
|
||||||
|
if (event.state && 'type' in event.state) {
|
||||||
|
newSource(event.state.type);
|
||||||
|
}
|
||||||
|
}); // On load
|
||||||
|
|
||||||
|
if (historySupport) {
|
||||||
|
var video = !currentType.length; // If there's no current type set, assume video
|
||||||
|
|
||||||
|
if (video) {
|
||||||
|
currentType = types.video;
|
||||||
|
} // Replace current history state
|
||||||
|
|
||||||
|
|
||||||
|
if (currentType in types) {
|
||||||
|
window.history.replaceState({
|
||||||
|
type: currentType
|
||||||
|
}, '', video ? '' : "#".concat(currentType));
|
||||||
|
} // If it's not video, load the source
|
||||||
|
|
||||||
|
|
||||||
|
if (currentType !== types.video) {
|
||||||
|
newSource(currentType, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}); // Raven / Sentry
|
||||||
|
// For demo site (https://plyr.io) only
|
||||||
|
|
||||||
|
if (env.prod) {
|
||||||
|
singleton.config('https://d4ad9866ad834437a4754e23937071e4@sentry.io/305555').install();
|
||||||
|
} // Google analytics
|
||||||
|
// For demo site (https://plyr.io) only
|
||||||
|
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
|
||||||
|
if (env.prod) {
|
||||||
|
(function (i, s, o, g, r, a, m) {
|
||||||
|
i.GoogleAnalyticsObject = r;
|
||||||
|
|
||||||
|
i[r] = i[r] || function () {
|
||||||
|
(i[r].q = i[r].q || []).push(arguments);
|
||||||
|
};
|
||||||
|
|
||||||
|
i[r].l = 1 * new Date();
|
||||||
|
a = s.createElement(o);
|
||||||
|
m = s.getElementsByTagName(o)[0];
|
||||||
|
a.async = 1;
|
||||||
|
a.src = g;
|
||||||
|
m.parentNode.insertBefore(a, m);
|
||||||
|
})(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga');
|
||||||
|
|
||||||
|
window.ga('create', 'UA-40881672-11', 'auto');
|
||||||
|
window.ga('send', 'pageview');
|
||||||
|
}
|
||||||
|
/* eslint-enable */
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ import Raven from 'raven-js';
|
|||||||
|
|
||||||
// Remove class on blur
|
// Remove class on blur
|
||||||
document.addEventListener('focusout', event => {
|
document.addEventListener('focusout', event => {
|
||||||
if (container.contains(event.target)) {
|
if (!event.target.classList || container.contains(event.target)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
event.target.classList.remove(tabClassName);
|
event.target.classList.remove(tabClassName);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ import Raven from 'raven-js';
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const focused = document.activeElement;
|
const focused = document.activeElement;
|
||||||
|
|
||||||
if (!focused || container.contains(focused)) {
|
if (!focused || !focused.classList || container.contains(focused)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
font-family: 'Gordita';
|
font-family: 'Gordita';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: $font-weight-light;
|
font-weight: $font-weight-light;
|
||||||
src: url('https://cdn.plyr.io/static/fonts/gordita-light.woff2') format('woff2'), url('https://cdn.plyr.io/static/fonts/gordita-light.woff') format('woff');
|
src: url('https://cdn.plyr.io/static/fonts/gordita-light.woff2') format('woff2'),
|
||||||
|
url('https://cdn.plyr.io/static/fonts/gordita-light.woff') format('woff');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@@ -33,7 +34,8 @@
|
|||||||
font-family: 'Gordita';
|
font-family: 'Gordita';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: $font-weight-bold;
|
font-weight: $font-weight-bold;
|
||||||
src: url('https://cdn.plyr.io/static/fonts/gordita-bold.woff2') format('woff2'), url('https://cdn.plyr.io/static/fonts/gordita-bold.woff') format('woff');
|
src: url('https://cdn.plyr.io/static/fonts/gordita-bold.woff2') format('woff2'),
|
||||||
|
url('https://cdn.plyr.io/static/fonts/gordita-bold.woff') format('woff');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
@@ -41,5 +43,6 @@
|
|||||||
font-family: 'Gordita';
|
font-family: 'Gordita';
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: $font-weight-black;
|
font-weight: $font-weight-black;
|
||||||
src: url('https://cdn.plyr.io/static/fonts/gordita-black.woff2') format('woff2'), url('https://cdn.plyr.io/static/fonts/gordita-black.woff') format('woff');
|
src: url('https://cdn.plyr.io/static/fonts/gordita-black.woff2') format('woff2'),
|
||||||
|
url('https://cdn.plyr.io/static/fonts/gordita-black.woff') format('woff');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ $plyr-font-size-small: 12px;
|
|||||||
$plyr-font-size-time: 11px;
|
$plyr-font-size-time: 11px;
|
||||||
$plyr-font-size-badges: 9px;
|
$plyr-font-size-badges: 9px;
|
||||||
|
|
||||||
|
// Other
|
||||||
|
$plyr-font-smoothing: true;
|
||||||
|
|
||||||
// Captions
|
// Captions
|
||||||
$plyr-font-size-captions-base: $plyr-font-size-base;
|
$plyr-font-size-captions-base: $plyr-font-size-base;
|
||||||
$plyr-font-size-captions-small: $plyr-font-size-small;
|
$plyr-font-size-captions-small: $plyr-font-size-small;
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 5.3 KiB |
@@ -12,7 +12,6 @@ const concat = require('gulp-concat');
|
|||||||
const filter = require('gulp-filter');
|
const filter = require('gulp-filter');
|
||||||
const sass = require('gulp-sass');
|
const sass = require('gulp-sass');
|
||||||
const cleancss = require('gulp-clean-css');
|
const cleancss = require('gulp-clean-css');
|
||||||
const run = require('run-sequence');
|
|
||||||
const header = require('gulp-header');
|
const header = require('gulp-header');
|
||||||
const prefix = require('gulp-autoprefixer');
|
const prefix = require('gulp-autoprefixer');
|
||||||
const gitbranch = require('git-branch');
|
const gitbranch = require('git-branch');
|
||||||
@@ -44,7 +43,7 @@ const paths = {
|
|||||||
// Source paths
|
// Source paths
|
||||||
src: {
|
src: {
|
||||||
sass: path.join(root, 'src/sass/**/*.scss'),
|
sass: path.join(root, 'src/sass/**/*.scss'),
|
||||||
js: path.join(root, 'src/js/**/*'),
|
js: path.join(root, 'src/js/**/*.js'),
|
||||||
sprite: path.join(root, 'src/sprite/*.svg'),
|
sprite: path.join(root, 'src/sprite/*.svg'),
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ const paths = {
|
|||||||
// Source paths
|
// Source paths
|
||||||
src: {
|
src: {
|
||||||
sass: path.join(root, 'demo/src/sass/**/*.scss'),
|
sass: path.join(root, 'demo/src/sass/**/*.scss'),
|
||||||
js: path.join(root, 'demo/src/js/**/*'),
|
js: path.join(root, 'demo/src/js/**/*.js'),
|
||||||
},
|
},
|
||||||
|
|
||||||
// Output paths
|
// Output paths
|
||||||
@@ -88,34 +87,33 @@ const sizeOptions = { showFiles: true, gzip: true };
|
|||||||
const browsers = ['> 1%'];
|
const browsers = ['> 1%'];
|
||||||
|
|
||||||
// Babel config
|
// Babel config
|
||||||
const babelrc = {
|
const babelrc = (polyfill = false) => ({
|
||||||
presets: [
|
presets: [
|
||||||
[
|
[
|
||||||
'env',
|
'@babel/preset-env',
|
||||||
{
|
{
|
||||||
targets: {
|
targets: {
|
||||||
browsers,
|
browsers,
|
||||||
},
|
},
|
||||||
useBuiltIns: true,
|
useBuiltIns: polyfill ? 'usage' : false,
|
||||||
modules: false,
|
modules: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
plugins: ['external-helpers'],
|
|
||||||
babelrc: false,
|
babelrc: false,
|
||||||
exclude: 'node_modules/**',
|
exclude: 'node_modules/**',
|
||||||
};
|
});
|
||||||
|
|
||||||
// Clean out /dist
|
// Clean out /dist
|
||||||
gulp.task('clean', () => {
|
gulp.task('clean', done => {
|
||||||
const dirs = [paths.plyr.output, paths.demo.output].map(dir =>
|
const dirs = [paths.plyr.output, paths.demo.output].map(dir => path.join(dir, '**/*'));
|
||||||
path.join(dir, '**/*'),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Don't delete the mp4
|
// Don't delete the mp4
|
||||||
dirs.push(`!${path.join(paths.plyr.output, '**/*.mp4')}`);
|
dirs.push(`!${path.join(paths.plyr.output, '**/*.mp4')}`);
|
||||||
|
|
||||||
del(dirs);
|
del(dirs);
|
||||||
|
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
const build = {
|
const build = {
|
||||||
@@ -124,6 +122,7 @@ const build = {
|
|||||||
const name = `js:${key}`;
|
const name = `js:${key}`;
|
||||||
tasks.js.push(name);
|
tasks.js.push(name);
|
||||||
const { output } = paths[bundle];
|
const { output } = paths[bundle];
|
||||||
|
const polyfill = name.includes('polyfilled');
|
||||||
|
|
||||||
return gulp.task(name, () =>
|
return gulp.task(name, () =>
|
||||||
gulp
|
gulp
|
||||||
@@ -133,11 +132,7 @@ const build = {
|
|||||||
.pipe(
|
.pipe(
|
||||||
rollup(
|
rollup(
|
||||||
{
|
{
|
||||||
plugins: [
|
plugins: [resolve(), commonjs(), babel(babelrc(polyfill))],
|
||||||
resolve(),
|
|
||||||
commonjs(),
|
|
||||||
babel(babelrc),
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
options,
|
options,
|
||||||
),
|
),
|
||||||
@@ -207,31 +202,25 @@ build.sass(bundles.demo.sass, 'demo');
|
|||||||
build.js(bundles.demo.js, 'demo', { format: 'iife' });
|
build.js(bundles.demo.js, 'demo', { format: 'iife' });
|
||||||
|
|
||||||
// Build all JS
|
// Build all JS
|
||||||
gulp.task('js', () => {
|
gulp.task('js', () => gulp.parallel(tasks.js));
|
||||||
run(tasks.js);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Watch for file changes
|
// Watch for file changes
|
||||||
gulp.task('watch', () => {
|
gulp.task('watch', () => {
|
||||||
// Plyr core
|
// Plyr core
|
||||||
gulp.watch(paths.plyr.src.js, tasks.js);
|
gulp.watch(paths.plyr.src.js, gulp.parallel(tasks.js));
|
||||||
gulp.watch(paths.plyr.src.sass, tasks.sass);
|
gulp.watch(paths.plyr.src.sass, gulp.parallel(tasks.sass));
|
||||||
gulp.watch(paths.plyr.src.sprite, tasks.sprite);
|
gulp.watch(paths.plyr.src.sprite, gulp.parallel(tasks.sprite));
|
||||||
|
|
||||||
// Demo
|
// Demo
|
||||||
gulp.watch(paths.demo.src.js, tasks.js);
|
gulp.watch(paths.demo.src.js, gulp.parallel(tasks.js));
|
||||||
gulp.watch(paths.demo.src.sass, tasks.sass);
|
gulp.watch(paths.demo.src.sass, gulp.parallel(tasks.sass));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Build distribution
|
// Build distribution
|
||||||
gulp.task('build', () => {
|
gulp.task('build', gulp.series(tasks.clean, gulp.parallel(tasks.js, tasks.sass, tasks.sprite)));
|
||||||
run(tasks.clean, tasks.js, tasks.sass, tasks.sprite);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Default gulp task
|
// Default gulp task
|
||||||
gulp.task('default', () => {
|
gulp.task('default', gulp.series('build', 'watch'));
|
||||||
run('build', 'watch');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Publish a version to CDN and demo
|
// Publish a version to CDN and demo
|
||||||
// --------------------------------------------
|
// --------------------------------------------
|
||||||
@@ -244,10 +233,7 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If deployment is setup
|
// If deployment is setup
|
||||||
if (
|
if (Object.keys(credentials).includes('aws') && Object.keys(credentials).includes('fastly')) {
|
||||||
Object.keys(credentials).includes('aws') &&
|
|
||||||
Object.keys(credentials).includes('fastly')
|
|
||||||
) {
|
|
||||||
const { version } = pkg;
|
const { version } = pkg;
|
||||||
const { aws, fastly } = credentials;
|
const { aws, fastly } = credentials;
|
||||||
|
|
||||||
@@ -269,8 +255,7 @@ if (
|
|||||||
demo: {
|
demo: {
|
||||||
uploadPath: branch.current === branch.develop ? 'beta/' : null,
|
uploadPath: branch.current === branch.develop ? 'beta/' : null,
|
||||||
headers: {
|
headers: {
|
||||||
'Cache-Control':
|
'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
|
||||||
'no-cache, no-store, must-revalidate, max-age=0',
|
|
||||||
Vary: 'Accept-Encoding',
|
Vary: 'Accept-Encoding',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -279,8 +264,7 @@ if (
|
|||||||
headers: {
|
headers: {
|
||||||
// http://stackoverflow.com/questions/2272835/amazon-s3-object-redirect
|
// http://stackoverflow.com/questions/2272835/amazon-s3-object-redirect
|
||||||
'x-amz-website-redirect-location': `/${ver}/${filename}`,
|
'x-amz-website-redirect-location': `/${ver}/${filename}`,
|
||||||
'Cache-Control':
|
'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0',
|
||||||
'no-cache, no-store, must-revalidate, max-age=0',
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
@@ -303,11 +287,7 @@ if (
|
|||||||
const allowed = [branch.master, branch.develop];
|
const allowed = [branch.master, branch.develop];
|
||||||
|
|
||||||
if (!allowed.includes(branch.current)) {
|
if (!allowed.includes(branch.current)) {
|
||||||
console.error(
|
console.error(`Must be on ${allowed.join(', ')} to publish! (current: ${branch.current})`);
|
||||||
`Must be on ${allowed.join(', ')} to publish! (current: ${
|
|
||||||
branch.current
|
|
||||||
})`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -323,13 +303,13 @@ if (
|
|||||||
console.log(`Updating versions to '${version}'...`);
|
console.log(`Updating versions to '${version}'...`);
|
||||||
|
|
||||||
// Replace versioned URLs in source
|
// Replace versioned URLs in source
|
||||||
const files = ['plyr.js', 'plyr.polyfilled.js', 'defaults.js'];
|
const files = ['plyr.js', 'plyr.polyfilled.js', 'config/defaults.js'];
|
||||||
|
|
||||||
return gulp
|
return gulp
|
||||||
.src(files.map(file => path.join(root, `src/js/${file}`)))
|
.src(files.map(file => path.join(root, `src/js/${file}`)), { base: '.' })
|
||||||
.pipe(replace(semver, `v${version}`))
|
.pipe(replace(semver, `v${version}`))
|
||||||
.pipe(replace(cdnpath, `${aws.cdn.domain}/${version}/`))
|
.pipe(replace(cdnpath, `${aws.cdn.domain}/${version}/`))
|
||||||
.pipe(gulp.dest(path.join(root, 'src/js/')));
|
.pipe(gulp.dest('./'));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Publish version to CDN bucket
|
// Publish version to CDN bucket
|
||||||
@@ -349,8 +329,7 @@ if (
|
|||||||
.pipe(
|
.pipe(
|
||||||
replace(
|
replace(
|
||||||
/sourceMappingURL=([\w-?.]+)/,
|
/sourceMappingURL=([\w-?.]+)/,
|
||||||
(match, p1) =>
|
(match, p1) => `sourceMappingURL=${p1.replace(minSuffix, '')}`,
|
||||||
`sourceMappingURL=${p1.replace(minSuffix, '')}`,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.pipe(
|
.pipe(
|
||||||
@@ -368,27 +347,30 @@ if (
|
|||||||
gulp.task('purge', () => {
|
gulp.task('purge', () => {
|
||||||
const list = [];
|
const list = [];
|
||||||
|
|
||||||
return gulp.src(paths.upload).pipe(
|
return gulp
|
||||||
through.obj((file, enc, cb) => {
|
.src(paths.upload)
|
||||||
const filename = file.path.split('/').pop();
|
.pipe(
|
||||||
list.push(`${versionPath}/${filename}`);
|
through.obj((file, enc, cb) => {
|
||||||
cb(null);
|
const filename = file.path.split('/').pop();
|
||||||
}),
|
list.push(`${versionPath}/${filename}`);
|
||||||
).on('end', () => {
|
cb(null);
|
||||||
const purge = new FastlyPurge(fastly.token);
|
}),
|
||||||
|
)
|
||||||
|
.on('end', () => {
|
||||||
|
const purge = new FastlyPurge(fastly.token);
|
||||||
|
|
||||||
list.forEach(url => {
|
list.forEach(url => {
|
||||||
console.log(`Purging ${url}...`);
|
console.log(`Purging ${url}...`);
|
||||||
|
|
||||||
purge.url(url, (error, result) => {
|
purge.url(url, (error, result) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} else if (result) {
|
} else if (result) {
|
||||||
console.log(result);
|
console.log(result);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Publish to demo bucket
|
// Publish to demo bucket
|
||||||
@@ -400,8 +382,7 @@ if (
|
|||||||
console.log(`Uploading '${version}' demo to ${aws.demo.domain}...`);
|
console.log(`Uploading '${version}' demo to ${aws.demo.domain}...`);
|
||||||
|
|
||||||
// Replace versioned files in readme.md
|
// Replace versioned files in readme.md
|
||||||
gulp
|
gulp.src([`${root}/readme.md`])
|
||||||
.src([`${root}/readme.md`])
|
|
||||||
.pipe(replace(cdnpath, `${aws.cdn.domain}/${version}/`))
|
.pipe(replace(cdnpath, `${aws.cdn.domain}/${version}/`))
|
||||||
.pipe(gulp.dest(root));
|
.pipe(gulp.dest(root));
|
||||||
|
|
||||||
@@ -415,8 +396,7 @@ if (
|
|||||||
pages.push(error);
|
pages.push(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
gulp
|
gulp.src(pages)
|
||||||
.src(pages)
|
|
||||||
.pipe(replace(localPath, versionPath))
|
.pipe(replace(localPath, versionPath))
|
||||||
.pipe(s3(aws.demo, options.demo));
|
.pipe(s3(aws.demo, options.demo));
|
||||||
|
|
||||||
@@ -467,16 +447,15 @@ if (
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Do everything
|
// Do everything
|
||||||
gulp.task('deploy', () =>
|
gulp.task(
|
||||||
run(
|
'deploy',
|
||||||
|
gulp.series(
|
||||||
'version',
|
'version',
|
||||||
tasks.clean,
|
tasks.clean,
|
||||||
tasks.js,
|
gulp.parallel(tasks.js, tasks.sass, tasks.sprite),
|
||||||
tasks.sass,
|
|
||||||
tasks.sprite,
|
|
||||||
'cdn',
|
'cdn',
|
||||||
'purge',
|
|
||||||
'demo',
|
'demo',
|
||||||
|
'purge',
|
||||||
'open',
|
'open',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "plyr",
|
"name": "plyr",
|
||||||
"version": "3.4.0-beta.2",
|
"version": "3.4.7",
|
||||||
"description":
|
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
|
||||||
"A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
|
|
||||||
"homepage": "https://plyr.io",
|
"homepage": "https://plyr.io",
|
||||||
"author": "Sam Potts <sam@potts.es>",
|
"author": "Sam Potts <sam@potts.es>",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@@ -32,64 +31,61 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "gulp build",
|
"build": "gulp build",
|
||||||
"lint": "eslint src/js && npm run-script remark",
|
"lint": "eslint src/js && npm run-script remark",
|
||||||
"remark":
|
"remark": "remark -f --use 'validate-links=repository:\"sampotts/plyr\"' '{,!(node_modules),.?**/}*.md'",
|
||||||
"remark -f --use 'validate-links=repository:\"sampotts/plyr\"' '{,!(node_modules),.?**/}*.md'",
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-core": "^6.26.3",
|
"@babel/core": "^7.1.5",
|
||||||
"babel-eslint": "^8.2.6",
|
"babel-eslint": "^10.0.1",
|
||||||
"babel-plugin-external-helpers": "^6.22.0",
|
"@babel/preset-env": "^7.1.5",
|
||||||
"babel-preset-env": "^1.7.0",
|
|
||||||
"del": "^3.0.0",
|
"del": "^3.0.0",
|
||||||
"eslint": "^5.2.0",
|
"eslint": "^5.8.0",
|
||||||
"eslint-config-airbnb-base": "^13.0.0",
|
"eslint-config-airbnb-base": "^13.1.0",
|
||||||
"eslint-config-prettier": "^2.9.0",
|
"eslint-config-prettier": "^3.1.0",
|
||||||
"eslint-plugin-import": "^2.13.0",
|
"eslint-plugin-import": "^2.14.0",
|
||||||
"fastly-purge": "^1.0.1",
|
"fastly-purge": "^1.0.1",
|
||||||
"git-branch": "^2.0.1",
|
"git-branch": "^2.0.1",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^4.0.0",
|
||||||
"gulp-autoprefixer": "^5.0.0",
|
"gulp-autoprefixer": "^6.0.0",
|
||||||
"gulp-better-rollup": "^3.3.0",
|
"gulp-better-rollup": "^3.4.0",
|
||||||
"gulp-clean-css": "^3.9.4",
|
"gulp-clean-css": "^3.10.0",
|
||||||
"gulp-concat": "^2.6.1",
|
"gulp-concat": "^2.6.1",
|
||||||
"gulp-filter": "^5.1.0",
|
"gulp-filter": "^5.1.0",
|
||||||
"gulp-header": "^2.0.5",
|
"gulp-header": "^2.0.5",
|
||||||
"gulp-open": "^3.0.1",
|
"gulp-open": "^3.0.1",
|
||||||
"gulp-postcss": "^7.0.1",
|
"gulp-postcss": "^8.0.0",
|
||||||
"gulp-rename": "^1.4.0",
|
"gulp-rename": "^1.4.0",
|
||||||
"gulp-replace": "^1.0.0",
|
"gulp-replace": "^1.0.0",
|
||||||
"gulp-s3": "^0.11.0",
|
"gulp-s3": "^0.11.0",
|
||||||
"gulp-sass": "^4.0.1",
|
"gulp-sass": "^4.0.2",
|
||||||
"gulp-size": "^3.0.0",
|
"gulp-size": "^3.0.0",
|
||||||
"gulp-sourcemaps": "^2.6.4",
|
"gulp-sourcemaps": "^2.6.4",
|
||||||
"gulp-svgmin": "^1.2.4",
|
"gulp-svgmin": "^2.1.0",
|
||||||
"gulp-svgstore": "^6.1.1",
|
"gulp-svgstore": "^7.0.0",
|
||||||
"gulp-uglify-es": "^1.0.4",
|
"gulp-uglify-es": "^1.0.4",
|
||||||
"gulp-util": "^3.0.8",
|
"gulp-util": "^3.0.8",
|
||||||
"postcss-custom-properties": "^7.0.0",
|
"postcss-custom-properties": "^8.0.9",
|
||||||
"prettier-eslint": "^8.8.2",
|
"prettier-eslint": "^8.8.2",
|
||||||
"prettier-stylelint": "^0.4.2",
|
"prettier-stylelint": "^0.4.2",
|
||||||
"remark-cli": "^5.0.0",
|
"remark-cli": "^6.0.0",
|
||||||
"remark-validate-links": "^7.0.0",
|
"remark-validate-links": "^7.1.0",
|
||||||
"rollup-plugin-babel": "^3.0.7",
|
"rollup-plugin-babel": "^4.0.3",
|
||||||
"rollup-plugin-commonjs": "^9.1.4",
|
"rollup-plugin-commonjs": "^9.2.0",
|
||||||
"rollup-plugin-node-resolve": "^3.3.0",
|
"rollup-plugin-node-resolve": "^3.4.0",
|
||||||
"run-sequence": "^2.2.1",
|
"stylelint": "^9.7.1",
|
||||||
"stylelint": "^9.4.0",
|
"stylelint-config-prettier": "^4.0.0",
|
||||||
"stylelint-config-prettier": "^3.3.0",
|
|
||||||
"stylelint-config-recommended": "^2.1.0",
|
"stylelint-config-recommended": "^2.1.0",
|
||||||
"stylelint-config-sass-guidelines": "^5.0.0",
|
"stylelint-config-sass-guidelines": "^5.2.0",
|
||||||
"stylelint-order": "^0.8.1",
|
"stylelint-order": "^1.0.0",
|
||||||
"stylelint-scss": "^3.2.0",
|
"stylelint-scss": "^3.4.0",
|
||||||
"stylelint-selector-bem-pattern": "^2.0.0",
|
"stylelint-selector-bem-pattern": "^2.0.0",
|
||||||
"through2": "^2.0.3"
|
"through2": "^3.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"babel-polyfill": "^6.26.0",
|
"core-js": "^2.5.7",
|
||||||
"custom-event-polyfill": "^1.0.6",
|
"custom-event-polyfill": "^1.0.6",
|
||||||
"loadjs": "^3.5.4",
|
"loadjs": "^3.5.4",
|
||||||
"raven-js": "^3.26.4",
|
"raven-js": "^3.27.0",
|
||||||
"url-polyfill": "^1.0.13"
|
"url-polyfill": "^1.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,26 +8,26 @@ A simple, lightweight, accessible and customizable HTML5, YouTube and Vimeo medi
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* **Accessible** - full support for VTT captions and screen readers
|
- **Accessible** - full support for VTT captions and screen readers
|
||||||
* **[Customisable](#html)** - make the player look how you want with the markup you want
|
- **[Customisable](#html)** - make the player look how you want with the markup you want
|
||||||
* **Good HTML** - uses the _right_ elements. `<input type="range">` for volume and `<progress>` for progress and well, `<button>`s for buttons. There's no
|
- **Good HTML** - uses the _right_ elements. `<input type="range">` for volume and `<progress>` for progress and well, `<button>`s for buttons. There's no
|
||||||
`<span>` or `<a href="#">` button hacks
|
`<span>` or `<a href="#">` button hacks
|
||||||
* **Responsive** - works with any screen size
|
- **Responsive** - works with any screen size
|
||||||
* **HTML Video & Audio** - support for both formats
|
- **HTML Video & Audio** - support for both formats
|
||||||
* **[Embedded Video](#embeds)** - support for YouTube and Vimeo video playback
|
- **[Embedded Video](#embeds)** - support for YouTube and Vimeo video playback
|
||||||
* **[Monetization](#ads)** - make money from your videos
|
- **[Monetization](#ads)** - make money from your videos
|
||||||
* **[Streaming](#try-plyr-online)** - support for hls.js, Shaka and dash.js streaming playback
|
- **[Streaming](#try-plyr-online)** - support for hls.js, Shaka and dash.js streaming playback
|
||||||
* **[API](#api)** - toggle playback, volume, seeking, and more through a standardized API
|
- **[API](#api)** - toggle playback, volume, seeking, and more through a standardized API
|
||||||
* **[Events](#events)** - no messing around with Vimeo and YouTube APIs, all events are standardized across formats
|
- **[Events](#events)** - no messing around with Vimeo and YouTube APIs, all events are standardized across formats
|
||||||
* **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes
|
- **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes
|
||||||
* **[Shortcuts](#shortcuts)** - supports keyboard shortcuts
|
- **[Shortcuts](#shortcuts)** - supports keyboard shortcuts
|
||||||
* **Picture-in-Picture** - supports Safari's picture-in-picture mode
|
- **Picture-in-Picture** - supports Safari's picture-in-picture mode
|
||||||
* **Playsinline** - supports the `playsinline` attribute
|
- **Playsinline** - supports the `playsinline` attribute
|
||||||
* **Speed controls** - adjust speed on the fly
|
- **Speed controls** - adjust speed on the fly
|
||||||
* **Multiple captions** - support for multiple caption tracks
|
- **Multiple captions** - support for multiple caption tracks
|
||||||
* **i18n support** - support for internationalization of controls
|
- **i18n support** - support for internationalization of controls
|
||||||
* **No dependencies** - written in "vanilla" ES6 JavaScript, no jQuery required
|
- **No dependencies** - written in "vanilla" ES6 JavaScript, no jQuery required
|
||||||
* **SASS** - to include in your build processes
|
- **SASS** - to include in your build processes
|
||||||
|
|
||||||
Oh and yes, it works with Bootstrap.
|
Oh and yes, it works with Bootstrap.
|
||||||
|
|
||||||
@@ -132,13 +132,13 @@ See [initialising](#initialising) for more information on advanced setups.
|
|||||||
You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills seperately as part of your application but to make life easier you can use the polyfilled build.
|
You can use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills seperately as part of your application but to make life easier you can use the polyfilled build.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="https://cdn.plyr.io/3.4.0-beta.2/plyr.js"></script>
|
<script src="https://cdn.plyr.io/3.4.7/plyr.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
...or...
|
...or...
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="https://cdn.plyr.io/3.4.0-beta.2/plyr.polyfilled.js"></script>
|
<script src="https://cdn.plyr.io/3.4.7/plyr.polyfilled.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### CSS
|
### CSS
|
||||||
@@ -152,21 +152,21 @@ Include the `plyr.css` stylsheet into your `<head>`
|
|||||||
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
|
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<link rel="stylesheet" href="https://cdn.plyr.io/3.4.0-beta.2/plyr.css">
|
<link rel="stylesheet" href="https://cdn.plyr.io/3.4.7/plyr.css">
|
||||||
```
|
```
|
||||||
|
|
||||||
### SVG Sprite
|
### SVG Sprite
|
||||||
|
|
||||||
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
|
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
|
||||||
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.4.0-beta.2/plyr.svg`.
|
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.4.7/plyr.svg`.
|
||||||
|
|
||||||
## Ads
|
## Ads
|
||||||
|
|
||||||
Plyr has partnered up with [vi.ai](https://vi.ai/publisher-video-monetization/?aid=plyrio) to offer monetization options for your videos. Getting setup is easy:
|
Plyr has partnered up with [vi.ai](https://vi.ai/publisher-video-monetization/?aid=plyrio) to offer monetization options for your videos. Getting setup is easy:
|
||||||
|
|
||||||
* [Sign up for a vi.ai account](https://vi.ai/publisher-video-monetization/?aid=plyrio)
|
- [Sign up for a vi.ai account](https://vi.ai/publisher-video-monetization/?aid=plyrio)
|
||||||
* Grab your publisher ID from the code snippet
|
- Grab your publisher ID from the code snippet
|
||||||
* Enable ads in the [config options](#options) and enter your publisher ID
|
- Enable ads in the [config options](#options) and enter your publisher ID
|
||||||
|
|
||||||
Any questions regarding the ads can be sent straight to vi.ai and any issues with rendering raised through GitHub issues.
|
Any questions regarding the ads can be sent straight to vi.ai and any issues with rendering raised through GitHub issues.
|
||||||
|
|
||||||
@@ -175,7 +175,7 @@ Any questions regarding the ads can be sent straight to vi.ai and any issues wit
|
|||||||
### SASS
|
### SASS
|
||||||
|
|
||||||
You can use `bundle.scss` file included in `/src` as part of your build and change variables to suit your design. The SASS require you to
|
You can use `bundle.scss` file included in `/src` as part of your build and change variables to suit your design. The SASS require you to
|
||||||
use the [autoprefixer](https://www.npmjs.com/package/gulp-autoprefixer) plugin (you be should already!) as all declarations use the W3C definitions.
|
use the [autoprefixer](https://www.npmjs.com/package/gulp-autoprefixer) plugin (you should be already!) as all declarations use the W3C definitions.
|
||||||
|
|
||||||
The HTML markup uses the BEM methodology with `plyr` as the block, e.g. `.plyr__controls`. You can change the class hooks in the options to match any custom CSS
|
The HTML markup uses the BEM methodology with `plyr` as the block, e.g. `.plyr__controls`. You can change the class hooks in the options to match any custom CSS
|
||||||
you write. Check out the JavaScript source for more on this.
|
you write. Check out the JavaScript source for more on this.
|
||||||
@@ -213,10 +213,10 @@ WebVTT captions are supported. To add a caption track, check the HTML example ab
|
|||||||
|
|
||||||
You can specify a range of arguments for the constructor to use:
|
You can specify a range of arguments for the constructor to use:
|
||||||
|
|
||||||
* A CSS string selector that's compatible with [`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
|
- A CSS string selector that's compatible with [`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
|
||||||
* A [`HTMLElement`](https://developer.mozilla.org/en/docs/Web/API/HTMLElement)
|
- A [`HTMLElement`](https://developer.mozilla.org/en/docs/Web/API/HTMLElement)
|
||||||
* A [`NodeList`](https://developer.mozilla.org/en-US/docs/Web/API/NodeList)
|
- A [`NodeList`](https://developer.mozilla.org/en-US/docs/Web/API/NodeList)
|
||||||
* A [jQuery](https://jquery.com) object
|
- A [jQuery](https://jquery.com) object
|
||||||
|
|
||||||
_Note_: If a `NodeList`, `Array`, or jQuery object are passed, the first element will be used for setup. To setup multiple players, see [setting up multiple players](#setting-up-multiple-players) below.
|
_Note_: If a `NodeList`, `Array`, or jQuery object are passed, the first element will be used for setup. To setup multiple players, see [setting up multiple players](#setting-up-multiple-players) below.
|
||||||
|
|
||||||
@@ -286,11 +286,11 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke
|
|||||||
| `debug` | Boolean | `false` | Display debugging information in the console |
|
| `debug` | Boolean | `false` | Display debugging information in the console |
|
||||||
| `controls` | Array, Function or Element | `['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen']` | If a function is passed, it is assumed your method will return either an element or HTML string for the controls. Three arguments will be passed to your function; `id` (the unique id for the player), `seektime` (the seektime step in seconds), and `title` (the media title). See [controls.md](controls.md) for more info on how the html needs to be structured. |
|
| `controls` | Array, Function or Element | `['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen']` | If a function is passed, it is assumed your method will return either an element or HTML string for the controls. Three arguments will be passed to your function; `id` (the unique id for the player), `seektime` (the seektime step in seconds), and `title` (the media title). See [controls.md](controls.md) for more info on how the html needs to be structured. |
|
||||||
| `settings` | Array | `['captions', 'quality', 'speed', 'loop']` | If you're using the default controls are used then you can specify which settings to show in the menu |
|
| `settings` | Array | `['captions', 'quality', 'speed', 'loop']` | If you're using the default controls are used then you can specify which settings to show in the menu |
|
||||||
| `i18n` | Object | See [defaults.js](/src/js/config/defaults.js) | Used for internationalization (i18n) of the text within the UI. |
|
| `i18n` | Object | See [defaults.js](/src/js/config/defaults.js) | Used for internationalization (i18n) of the text within the UI. |
|
||||||
| `loadSprite` | Boolean | `true` | Load the SVG sprite specified as the `iconUrl` option (if a URL). If `false`, it is assumed you are handling sprite loading yourself. |
|
| `loadSprite` | Boolean | `true` | Load the SVG sprite specified as the `iconUrl` option (if a URL). If `false`, it is assumed you are handling sprite loading yourself. |
|
||||||
| `iconUrl` | String | `null` | Specify a URL or path to the SVG sprite. See the [SVG section](#svg) for more info. |
|
| `iconUrl` | String | `null` | Specify a URL or path to the SVG sprite. See the [SVG section](#svg) for more info. |
|
||||||
| `iconPrefix` | String | `plyr` | Specify the id prefix for the icons used in the default controls (e.g. "plyr-play" would be "plyr"). This is to prevent clashes if you're using your own SVG sprite but with the default controls. Most people can ignore this option. |
|
| `iconPrefix` | String | `plyr` | Specify the id prefix for the icons used in the default controls (e.g. "plyr-play" would be "plyr"). This is to prevent clashes if you're using your own SVG sprite but with the default controls. Most people can ignore this option. |
|
||||||
| `blankUrl` | String | `https://cdn.plyr.io/static/blank.mp4` | Specify a URL or path to a blank video file used to properly cancel network requests. |
|
| `blankVideo` | String | `https://cdn.plyr.io/static/blank.mp4` | Specify a URL or path to a blank video file used to properly cancel network requests. |
|
||||||
| `autoplay` | Boolean | `false` | Autoplay the media on load. This is generally advised against on UX grounds. It is also disabled by default in some browsers. If the `autoplay` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true. |
|
| `autoplay` | Boolean | `false` | Autoplay the media on load. This is generally advised against on UX grounds. It is also disabled by default in some browsers. If the `autoplay` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true. |
|
||||||
| `autopause`¹ | Boolean | `true` | Only allow one player playing at once. |
|
| `autopause`¹ | Boolean | `true` | Only allow one player playing at once. |
|
||||||
| `seekTime` | Number | `10` | The time, in seconds, to seek when a user hits fast forward or rewind. |
|
| `seekTime` | Number | `10` | The time, in seconds, to seek when a user hits fast forward or rewind. |
|
||||||
@@ -307,7 +307,7 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke
|
|||||||
| `invertTime` | Boolean | `true` | Display the current time as a countdown rather than an incremental counter. |
|
| `invertTime` | Boolean | `true` | Display the current time as a countdown rather than an incremental counter. |
|
||||||
| `toggleInvert` | Boolean | `true` | Allow users to click to toggle the above. |
|
| `toggleInvert` | Boolean | `true` | Allow users to click to toggle the above. |
|
||||||
| `listeners` | Object | `null` | Allows binding of event listeners to the controls before the default handlers. See the `defaults.js` for available listeners. If your handler prevents default on the event (`event.preventDefault()`), the default handler will not fire. |
|
| `listeners` | Object | `null` | Allows binding of event listeners to the controls before the default handlers. See the `defaults.js` for available listeners. If your handler prevents default on the event (`event.preventDefault()`), the default handler will not fire. |
|
||||||
| `captions` | Object | `{ active: false, language: 'auto', update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). 'auto' uses the browser language. `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). |
|
| `captions` | Object | `{ active: false, language: 'auto', update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). 'auto' uses the browser language. `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). |
|
||||||
| `fullscreen` | Object | `{ enabled: true, fallback: true, iosNative: false }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution. `iosNative`: whether to use native iOS fullscreen when entering fullscreen (no custom controls) |
|
| `fullscreen` | Object | `{ enabled: true, fallback: true, iosNative: false }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution. `iosNative`: whether to use native iOS fullscreen when entering fullscreen (no custom controls) |
|
||||||
| `ratio` | String | `16:9` | The aspect ratio you want to use for embedded players. |
|
| `ratio` | String | `16:9` | The aspect ratio you want to use for embedded players. |
|
||||||
| `storage` | Object | `{ enabled: true, key: 'plyr' }` | `enabled`: Allow use of local storage to store user settings. `key`: The key name to use. |
|
| `storage` | Object | `{ enabled: true, key: 'plyr' }` | `enabled`: Allow use of local storage to store user settings. `key`: The key name to use. |
|
||||||
@@ -315,6 +315,7 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke
|
|||||||
| `quality` | Object | `{ default: 'default', options: ['hd2160', 'hd1440', 'hd1080', 'hd720', 'large', 'medium', 'small', 'tiny', 'default'] }` | Currently only supported by YouTube. `default` is the default quality level, determined by YouTube. `options` are the options to display. |
|
| `quality` | Object | `{ default: 'default', options: ['hd2160', 'hd1440', 'hd1080', 'hd720', 'large', 'medium', 'small', 'tiny', 'default'] }` | Currently only supported by YouTube. `default` is the default quality level, determined by YouTube. `options` are the options to display. |
|
||||||
| `loop` | Object | `{ active: false }` | `active`: Whether to loop the current video. If the `loop` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true This is an object to support future functionality. |
|
| `loop` | Object | `{ active: false }` | `active`: Whether to loop the current video. If the `loop` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true This is an object to support future functionality. |
|
||||||
| `ads` | Object | `{ enabled: false, publisherId: '' }` | `enabled`: Whether to enable vi.ai ads. `publisherId`: Your unique vi.ai publisher ID. |
|
| `ads` | Object | `{ enabled: false, publisherId: '' }` | `enabled`: Whether to enable vi.ai ads. `publisherId`: Your unique vi.ai publisher ID. |
|
||||||
|
| `urls` | Object | See source. | If you wish to override any API URLs then you can do so here. You can also set a custom download URL for the download button. |
|
||||||
|
|
||||||
1. Vimeo only
|
1. Vimeo only
|
||||||
|
|
||||||
@@ -365,9 +366,9 @@ player.fullscreen.enter(); // Enter fullscreen
|
|||||||
| `fullscreen.exit()` | - | Exit fullscreen. |
|
| `fullscreen.exit()` | - | Exit fullscreen. |
|
||||||
| `fullscreen.toggle()` | - | Toggle fullscreen. |
|
| `fullscreen.toggle()` | - | Toggle fullscreen. |
|
||||||
| `airplay()` | - | Trigger the airplay dialog on supported devices. |
|
| `airplay()` | - | Trigger the airplay dialog on supported devices. |
|
||||||
| `toggleControls(toggle)` | Boolean | Toggle the controls (video only). Takes optional truthy value to force it on/off. |
|
| `toggleControls(toggle)` | Boolean | Toggle the controls (video only). Takes optional truthy value to force it on/off. |
|
||||||
| `on(event, function)` | String, Function | Add an event listener for the specified event. |
|
| `on(event, function)` | String, Function | Add an event listener for the specified event. |
|
||||||
| `once(event, function)` | String, Function | Add an event listener for the specified event once. |
|
| `once(event, function)` | String, Function | Add an event listener for the specified event once. |
|
||||||
| `off(event, function)` | String, Function | Remove an event listener for the specified event. |
|
| `off(event, function)` | String, Function | Remove an event listener for the specified event. |
|
||||||
| `supports(type)` | String | Check support for a mime type. |
|
| `supports(type)` | String | Check support for a mime type. |
|
||||||
| `destroy()` | - | Destroy the instance and garbage collect any elements. |
|
| `destroy()` | - | Destroy the instance and garbage collect any elements. |
|
||||||
@@ -391,32 +392,32 @@ player.currentTime; // 10
|
|||||||
player.fullscreen.active; // false;
|
player.fullscreen.active; // false;
|
||||||
```
|
```
|
||||||
|
|
||||||
| Property | Getter | Setter | Description |
|
| Property | Getter | Setter | Description |
|
||||||
| -------------------- | ------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| -------------------- | ------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `isHTML5` | ✓ | - | Returns a boolean indicating if the current player is HTML5. |
|
| `isHTML5` | ✓ | - | Returns a boolean indicating if the current player is HTML5. |
|
||||||
| `isEmbed` | ✓ | - | Returns a boolean indicating if the current player is an embedded player. |
|
| `isEmbed` | ✓ | - | Returns a boolean indicating if the current player is an embedded player. |
|
||||||
| `playing` | ✓ | - | Returns a boolean indicating if the current player is playing. |
|
| `playing` | ✓ | - | Returns a boolean indicating if the current player is playing. |
|
||||||
| `paused` | ✓ | - | Returns a boolean indicating if the current player is paused. |
|
| `paused` | ✓ | - | Returns a boolean indicating if the current player is paused. |
|
||||||
| `stopped` | ✓ | - | Returns a boolean indicating if the current player is stopped. |
|
| `stopped` | ✓ | - | Returns a boolean indicating if the current player is stopped. |
|
||||||
| `ended` | ✓ | - | Returns a boolean indicating if the current player has finished playback. |
|
| `ended` | ✓ | - | Returns a boolean indicating if the current player has finished playback. |
|
||||||
| `buffered` | ✓ | - | Returns a float between 0 and 1 indicating how much of the media is buffered |
|
| `buffered` | ✓ | - | Returns a float between 0 and 1 indicating how much of the media is buffered |
|
||||||
| `currentTime` | ✓ | ✓ | Gets or sets the currentTime for the player. The setter accepts a float in seconds. |
|
| `currentTime` | ✓ | ✓ | Gets or sets the currentTime for the player. The setter accepts a float in seconds. |
|
||||||
| `seeking` | ✓ | - | Returns a boolean indicating if the current player is seeking. |
|
| `seeking` | ✓ | - | Returns a boolean indicating if the current player is seeking. |
|
||||||
| `duration` | ✓ | - | Returns the duration for the current media. |
|
| `duration` | ✓ | - | Returns the duration for the current media. |
|
||||||
| `volume` | ✓ | ✓ | Gets or sets the volume for the player. The setter accepts a float between 0 and 1. |
|
| `volume` | ✓ | ✓ | Gets or sets the volume for the player. The setter accepts a float between 0 and 1. |
|
||||||
| `muted` | ✓ | ✓ | Gets or sets the muted state of the player. The setter accepts a boolean. |
|
| `muted` | ✓ | ✓ | Gets or sets the muted state of the player. The setter accepts a boolean. |
|
||||||
| `hasAudio` | ✓ | - | Returns a boolean indicating if the current media has an audio track. |
|
| `hasAudio` | ✓ | - | Returns a boolean indicating if the current media has an audio track. |
|
||||||
| `speed` | ✓ | ✓ | Gets or sets the speed for the player. The setter accepts a value in the options specified in your config. Generally the minimum should be 0.5. |
|
| `speed` | ✓ | ✓ | Gets or sets the speed for the player. The setter accepts a value in the options specified in your config. Generally the minimum should be 0.5. |
|
||||||
| `quality`¹ | ✓ | ✓ | Gets or sets the quality for the player. The setter accepts a value from the options specified in your config. |
|
| `quality`¹ | ✓ | ✓ | Gets or sets the quality for the player. The setter accepts a value from the options specified in your config. |
|
||||||
| `loop` | ✓ | ✓ | Gets or sets the current loop state of the player. The setter accepts a boolean. |
|
| `loop` | ✓ | ✓ | Gets or sets the current loop state of the player. The setter accepts a boolean. |
|
||||||
| `source` | ✓ | ✓ | Gets or sets the current source for the player. The setter accepts an object. See [source setter](#the-source-setter) below for examples. |
|
| `source` | ✓ | ✓ | Gets or sets the current source for the player. The setter accepts an object. See [source setter](#the-source-setter) below for examples. |
|
||||||
| `poster` | ✓ | ✓ | Gets or sets the current poster image for the player. The setter accepts a string; the URL for the updated poster image. |
|
| `poster` | ✓ | ✓ | Gets or sets the current poster image for the player. The setter accepts a string; the URL for the updated poster image. |
|
||||||
| `autoplay` | ✓ | ✓ | Gets or sets the autoplay state of the player. The setter accepts a boolean. |
|
| `autoplay` | ✓ | ✓ | Gets or sets the autoplay state of the player. The setter accepts a boolean. |
|
||||||
| `currentTrack` | ✓ | ✓ | Gets or sets the caption track by index. `-1` means the track is missing or captions is not active |
|
| `currentTrack` | ✓ | ✓ | Gets or sets the caption track by index. `-1` means the track is missing or captions is not active |
|
||||||
| `language` | ✓ | ✓ | Gets or sets the preferred captions language for the player. The setter accepts an ISO two-letter language code. Support for the languages is dependent on the captions you include. If your captions don't have any language data, or if you have multiple tracks with the same language, you may want to use `currentTrack` instead. |
|
| `language` | ✓ | ✓ | Gets or sets the preferred captions language for the player. The setter accepts an ISO two-letter language code. Support for the languages is dependent on the captions you include. If your captions don't have any language data, or if you have multiple tracks with the same language, you may want to use `currentTrack` instead. |
|
||||||
| `fullscreen.active` | ✓ | - | Returns a boolean indicating if the current player is in fullscreen mode. |
|
| `fullscreen.active` | ✓ | - | Returns a boolean indicating if the current player is in fullscreen mode. |
|
||||||
| `fullscreen.enabled` | ✓ | - | Returns a boolean indicating if the current player has fullscreen enabled. |
|
| `fullscreen.enabled` | ✓ | - | Returns a boolean indicating if the current player has fullscreen enabled. |
|
||||||
| `pip` | ✓ | ✓ | Gets or sets the picture-in-picture state of the player. The setter accepts a boolean. This currently only supported on Safari 10+ on MacOS Sierra+ and iOS 10+. |
|
| `pip`² | ✓ | ✓ | Gets or sets the picture-in-picture state of the player. The setter accepts a boolean. This currently only supported on Safari 10+ (on MacOS Sierra+ and iOS 10+) and Chrome 70+. |
|
||||||
|
|
||||||
1. YouTube only. HTML5 will follow.
|
1. YouTube only. HTML5 will follow.
|
||||||
2. HTML5 only
|
2. HTML5 only
|
||||||
@@ -565,6 +566,7 @@ player.on('ready', event => {
|
|||||||
| `loadstart` | Sent when loading of the media begins. |
|
| `loadstart` | Sent when loading of the media begins. |
|
||||||
| `loadeddata` | The first frame of the media has finished loading. |
|
| `loadeddata` | The first frame of the media has finished loading. |
|
||||||
| `loadedmetadata` | The media's metadata has finished loading; all attributes now contain as much useful information as they're going to. |
|
| `loadedmetadata` | The media's metadata has finished loading; all attributes now contain as much useful information as they're going to. |
|
||||||
|
| `qualitychange` | The quality of playback has changed. |
|
||||||
| `canplay` | Sent when enough data is available that the media can be played, at least for a couple of frames. This corresponds to the `HAVE_ENOUGH_DATA` `readyState`. |
|
| `canplay` | Sent when enough data is available that the media can be played, at least for a couple of frames. This corresponds to the `HAVE_ENOUGH_DATA` `readyState`. |
|
||||||
| `canplaythrough` | Sent when the ready state changes to `CAN_PLAY_THROUGH`, indicating that the entire media can be played without interruption, assuming the download rate remains at least at the current level. _Note:_ Manually setting the `currentTime` will eventually fire a `canplaythrough` event in firefox. Other browsers might not fire this event. |
|
| `canplaythrough` | Sent when the ready state changes to `CAN_PLAY_THROUGH`, indicating that the entire media can be played without interruption, assuming the download rate remains at least at the current level. _Note:_ Manually setting the `currentTime` will eventually fire a `canplaythrough` event in firefox. Other browsers might not fire this event. |
|
||||||
| `stalled` | Sent when the user agent is trying to fetch media data, but data is unexpectedly not forthcoming. |
|
| `stalled` | Sent when the user agent is trying to fetch media data, but data is unexpectedly not forthcoming. |
|
||||||
@@ -575,11 +577,9 @@ player.on('ready', event => {
|
|||||||
|
|
||||||
#### YouTube only
|
#### YouTube only
|
||||||
|
|
||||||
| Event Type | Description |
|
| Event Type | Description |
|
||||||
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `statechange` | The state of the player has changed. The code can be accessed via `event.detail.code`. Possible values are `-1`: Unstarted, `0`: Ended, `1`: Playing, `2`: Paused, `3`: Buffering, `5`: Video cued. See the [YouTube Docs](https://developers.google.com/youtube/iframe_api_reference#onStateChange) for more information. |
|
| `statechange` | The state of the player has changed. The code can be accessed via `event.detail.code`. Possible values are `-1`: Unstarted, `0`: Ended, `1`: Playing, `2`: Paused, `3`: Buffering, `5`: Video cued. See the [YouTube Docs](https://developers.google.com/youtube/iframe_api_reference#onStateChange) for more information. |
|
||||||
| `qualitychange` | The quality of playback has changed. |
|
|
||||||
| `qualityrequested` | A change to playback quality has been requested. _Note:_ A change to quality can only be _requested_ via the API. There is no guarantee the quality will change to the level requested. You should listen to the `qualitychange` event for true changes. |
|
|
||||||
|
|
||||||
_Note:_ These events also bubble up the DOM. The event target will be the container element.
|
_Note:_ These events also bubble up the DOM. The event target will be the container element.
|
||||||
|
|
||||||
@@ -591,8 +591,8 @@ YouTube and Vimeo are currently supported and function much like a HTML5 video.
|
|||||||
to access the API's directly. You can do so via the `embed` property of your player object - e.g. `player.embed`. You can then use the relevant methods from the
|
to access the API's directly. You can do so via the `embed` property of your player object - e.g. `player.embed`. You can then use the relevant methods from the
|
||||||
third party APIs. More info on the respective API's here:
|
third party APIs. More info on the respective API's here:
|
||||||
|
|
||||||
* [YouTube iframe API Reference](https://developers.google.com/youtube/iframe_api_reference)
|
- [YouTube iframe API Reference](https://developers.google.com/youtube/iframe_api_reference)
|
||||||
* [Vimeo player.js Reference](https://github.com/vimeo/player.js)
|
- [Vimeo player.js Reference](https://github.com/vimeo/player.js)
|
||||||
|
|
||||||
_Note_: Not all API methods may work 100%. Your mileage may vary. It's better to use the Plyr API where possible.
|
_Note_: Not all API methods may work 100%. Your mileage may vary. It's better to use the Plyr API where possible.
|
||||||
|
|
||||||
@@ -652,9 +652,9 @@ const supported = Plyr.supported('video', 'html5', true);
|
|||||||
|
|
||||||
The arguments are:
|
The arguments are:
|
||||||
|
|
||||||
* Media type (`audio` or `video`)
|
- Media type (`audio` or `video`)
|
||||||
* Provider (`html5`, `youtube` or `vimeo`)
|
- Provider (`html5`, `youtube` or `vimeo`)
|
||||||
* Whether the player has the `playsinline` attribute (only applicable to iOS 10+)
|
- Whether the player has the `playsinline` attribute (only applicable to iOS 10+)
|
||||||
|
|
||||||
### Disable support programatically
|
### Disable support programatically
|
||||||
|
|
||||||
@@ -687,33 +687,34 @@ Plyr is developed by [@sam_potts](https://twitter.com/sam_potts) / [sampotts.me]
|
|||||||
|
|
||||||
Plyr costs money to run, not only my time. I donate my time for free as I enjoy building Plyr but unfortunately have to pay for domains, hosting, and more. Any help with costs is appreciated...
|
Plyr costs money to run, not only my time. I donate my time for free as I enjoy building Plyr but unfortunately have to pay for domains, hosting, and more. Any help with costs is appreciated...
|
||||||
|
|
||||||
* [Donate via Patron](https://www.patreon.com/plyr)
|
- [Donate via Patreon](https://www.patreon.com/plyr)
|
||||||
* [Donate via PayPal](https://www.paypal.me/pottsy/20usd)
|
- [Donate via PayPal](https://www.paypal.me/pottsy/20usd)
|
||||||
|
|
||||||
## Mentions
|
## Mentions
|
||||||
|
|
||||||
* [ProductHunt](https://www.producthunt.com/tech/plyr)
|
- [ProductHunt](https://www.producthunt.com/tech/plyr)
|
||||||
* [The Changelog](http://thechangelog.com/plyr-simple-html5-media-player-custom-controls-webvtt-captions/)
|
- [The Changelog](http://thechangelog.com/plyr-simple-html5-media-player-custom-controls-webvtt-captions/)
|
||||||
* [HTML5 Weekly #177](http://html5weekly.com/issues/177)
|
- [HTML5 Weekly #177](http://html5weekly.com/issues/177)
|
||||||
* [Responsive Design #149](http://us4.campaign-archive2.com/?u=559bc631fe5294fc66f5f7f89&id=451a61490f)
|
- [Responsive Design #149](http://us4.campaign-archive2.com/?u=559bc631fe5294fc66f5f7f89&id=451a61490f)
|
||||||
* [Web Design Weekly #174](https://web-design-weekly.com/2015/02/24/web-design-weekly-174/)
|
- [Web Design Weekly #174](https://web-design-weekly.com/2015/02/24/web-design-weekly-174/)
|
||||||
* [Hacker News](https://news.ycombinator.com/item?id=9136774)
|
- [Hacker News](https://news.ycombinator.com/item?id=9136774)
|
||||||
* [Web Platform Daily](http://webplatformdaily.org/releases/2015-03-04)
|
- [Web Platform Daily](http://webplatformdaily.org/releases/2015-03-04)
|
||||||
* [LayerVault Designer News](https://news.layervault.com/stories/45394-plyr--a-simple-html5-media-player)
|
- [LayerVault Designer News](https://news.layervault.com/stories/45394-plyr--a-simple-html5-media-player)
|
||||||
* [The Treehouse Show #131](https://teamtreehouse.com/library/episode-131-origami-react-responsive-hero-images)
|
- [The Treehouse Show #131](https://teamtreehouse.com/library/episode-131-origami-react-responsive-hero-images)
|
||||||
* [noupe.com](http://www.noupe.com/design/html5-plyr-is-a-responsive-and-accessible-video-player-94389.html)
|
- [noupe.com](http://www.noupe.com/design/html5-plyr-is-a-responsive-and-accessible-video-player-94389.html)
|
||||||
|
|
||||||
## Used by
|
## Used by
|
||||||
|
|
||||||
* [Selz.com](https://selz.com)
|
- [Selz.com](https://selz.com)
|
||||||
* [Peugeot.fr](http://www.peugeot.fr/marque-et-technologie/technologies/peugeot-i-cockpit.html)
|
- [Peugeot.fr](http://www.peugeot.fr/marque-et-technologie/technologies/peugeot-i-cockpit.html)
|
||||||
* [Peugeot.de](http://www.peugeot.de/modelle/modellberater/208-3-turer/fotos-videos.html)
|
- [Peugeot.de](http://www.peugeot.de/modelle/modellberater/208-3-turer/fotos-videos.html)
|
||||||
* [TomTom.com](http://prioritydriving.tomtom.com/)
|
- [TomTom.com](http://prioritydriving.tomtom.com/)
|
||||||
* [DIGBMX](http://digbmx.com/)
|
- [DIGBMX](http://digbmx.com/)
|
||||||
* [Grime Archive](https://grimearchive.com/)
|
- [Grime Archive](https://grimearchive.com/)
|
||||||
* [koel - A personal music streaming server that works.](http://koel.phanan.net/)
|
- [koel - A personal music streaming server that works.](http://koel.phanan.net/)
|
||||||
* [Oscar Radio](http://oscar-radio.xyz/)
|
- [Oscar Radio](http://oscar-radio.xyz/)
|
||||||
* [Sparkk TV](https://www.sparkktv.com/)
|
- [Sparkk TV](https://www.sparkktv.com/)
|
||||||
|
- [@halfhalftravel](https://www.halfhalftravel.com/)
|
||||||
|
|
||||||
Let me know on [Twitter](https://twitter.com/sam_potts) I can add you to the above list. It'd be awesome to see how you're using Plyr :-)
|
Let me know on [Twitter](https://twitter.com/sam_potts) I can add you to the above list. It'd be awesome to see how you're using Plyr :-)
|
||||||
|
|
||||||
@@ -721,8 +722,8 @@ Let me know on [Twitter](https://twitter.com/sam_potts) I can add you to the abo
|
|||||||
|
|
||||||
Credit to the PayPal HTML5 Video player from which Plyr's caption functionality was originally ported from:
|
Credit to the PayPal HTML5 Video player from which Plyr's caption functionality was originally ported from:
|
||||||
|
|
||||||
* [PayPal's Accessible HTML5 Video Player](https://github.com/paypal/accessible-html5-video-player)
|
- [PayPal's Accessible HTML5 Video Player](https://github.com/paypal/accessible-html5-video-player)
|
||||||
* [An awesome guide for Plyr in Japanese!](http://syncer.jp/how-to-use-plyr-io) by [@arayutw](https://twitter.com/arayutw)
|
- [An awesome guide for Plyr in Japanese!](http://syncer.jp/how-to-use-plyr-io) by [@arayutw](https://twitter.com/arayutw)
|
||||||
|
|
||||||
## Thanks
|
## Thanks
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
import controls from './controls';
|
import controls from './controls';
|
||||||
import i18n from './i18n';
|
|
||||||
import support from './support';
|
import support from './support';
|
||||||
import { dedupe } from './utils/arrays';
|
import { dedupe } from './utils/arrays';
|
||||||
import browser from './utils/browser';
|
import browser from './utils/browser';
|
||||||
@@ -18,6 +17,7 @@ import {
|
|||||||
} from './utils/elements';
|
} from './utils/elements';
|
||||||
import { on, triggerEvent } from './utils/events';
|
import { on, triggerEvent } from './utils/events';
|
||||||
import fetch from './utils/fetch';
|
import fetch from './utils/fetch';
|
||||||
|
import i18n from './utils/i18n';
|
||||||
import is from './utils/is';
|
import is from './utils/is';
|
||||||
import { getHTML } from './utils/strings';
|
import { getHTML } from './utils/strings';
|
||||||
import { parseUrl } from './utils/urls';
|
import { parseUrl } from './utils/urls';
|
||||||
@@ -83,11 +83,8 @@ const captions = {
|
|||||||
// * active: The state preferred by user settings or config
|
// * active: The state preferred by user settings or config
|
||||||
// * toggled: The real captions state
|
// * toggled: The real captions state
|
||||||
|
|
||||||
const languages = dedupe(
|
const browserLanguages = navigator.languages || [navigator.language || navigator.userLanguage || 'en'];
|
||||||
Array.from(navigator.languages || navigator.language || navigator.userLanguage).map(
|
const languages = dedupe(browserLanguages.map(language => language.split('-')[0]));
|
||||||
language => language.split('-')[0],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
let language = (this.storage.get('language') || this.config.captions.language || 'auto').toLowerCase();
|
let language = (this.storage.get('language') || this.config.captions.language || 'auto').toLowerCase();
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ const defaults = {
|
|||||||
// Sprite (for icons)
|
// Sprite (for icons)
|
||||||
loadSprite: true,
|
loadSprite: true,
|
||||||
iconPrefix: 'plyr',
|
iconPrefix: 'plyr',
|
||||||
iconUrl: 'https://cdn.plyr.io/3.3.12/plyr.svg',
|
iconUrl: 'https://cdn.plyr.io/3.4.7/plyr.svg',
|
||||||
|
|
||||||
// Blank video (used to prevent errors on source change)
|
// Blank video (used to prevent errors on source change)
|
||||||
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
|
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
|
||||||
@@ -68,19 +68,7 @@ const defaults = {
|
|||||||
// Quality default
|
// Quality default
|
||||||
quality: {
|
quality: {
|
||||||
default: 576,
|
default: 576,
|
||||||
options: [
|
options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240],
|
||||||
4320,
|
|
||||||
2880,
|
|
||||||
2160,
|
|
||||||
1440,
|
|
||||||
1080,
|
|
||||||
720,
|
|
||||||
576,
|
|
||||||
480,
|
|
||||||
360,
|
|
||||||
240,
|
|
||||||
'default', // YouTube's "auto"
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Set loops
|
// Set loops
|
||||||
@@ -145,6 +133,7 @@ const defaults = {
|
|||||||
'settings',
|
'settings',
|
||||||
'pip',
|
'pip',
|
||||||
'airplay',
|
'airplay',
|
||||||
|
// 'download',
|
||||||
'fullscreen',
|
'fullscreen',
|
||||||
],
|
],
|
||||||
settings: ['captions', 'quality', 'speed'],
|
settings: ['captions', 'quality', 'speed'],
|
||||||
@@ -167,6 +156,7 @@ const defaults = {
|
|||||||
unmute: 'Unmute',
|
unmute: 'Unmute',
|
||||||
enableCaptions: 'Enable captions',
|
enableCaptions: 'Enable captions',
|
||||||
disableCaptions: 'Disable captions',
|
disableCaptions: 'Disable captions',
|
||||||
|
download: 'Download',
|
||||||
enterFullscreen: 'Enter fullscreen',
|
enterFullscreen: 'Enter fullscreen',
|
||||||
exitFullscreen: 'Exit fullscreen',
|
exitFullscreen: 'Exit fullscreen',
|
||||||
frameTitle: 'Player for {title}',
|
frameTitle: 'Player for {title}',
|
||||||
@@ -196,6 +186,7 @@ const defaults = {
|
|||||||
|
|
||||||
// URLs
|
// URLs
|
||||||
urls: {
|
urls: {
|
||||||
|
download: null,
|
||||||
vimeo: {
|
vimeo: {
|
||||||
sdk: 'https://player.vimeo.com/api/player.js',
|
sdk: 'https://player.vimeo.com/api/player.js',
|
||||||
iframe: 'https://player.vimeo.com/video/{0}?{1}',
|
iframe: 'https://player.vimeo.com/video/{0}?{1}',
|
||||||
@@ -222,6 +213,7 @@ const defaults = {
|
|||||||
mute: null,
|
mute: null,
|
||||||
volume: null,
|
volume: null,
|
||||||
captions: null,
|
captions: null,
|
||||||
|
download: null,
|
||||||
fullscreen: null,
|
fullscreen: null,
|
||||||
pip: null,
|
pip: null,
|
||||||
airplay: null,
|
airplay: null,
|
||||||
@@ -257,6 +249,7 @@ const defaults = {
|
|||||||
'cuechange',
|
'cuechange',
|
||||||
|
|
||||||
// Custom events
|
// Custom events
|
||||||
|
'download',
|
||||||
'enterfullscreen',
|
'enterfullscreen',
|
||||||
'exitfullscreen',
|
'exitfullscreen',
|
||||||
'captionsenabled',
|
'captionsenabled',
|
||||||
@@ -268,8 +261,9 @@ const defaults = {
|
|||||||
|
|
||||||
// YouTube
|
// YouTube
|
||||||
'statechange',
|
'statechange',
|
||||||
|
|
||||||
|
// Quality
|
||||||
'qualitychange',
|
'qualitychange',
|
||||||
'qualityrequested',
|
|
||||||
|
|
||||||
// Ads
|
// Ads
|
||||||
'adsloaded',
|
'adsloaded',
|
||||||
@@ -301,6 +295,7 @@ const defaults = {
|
|||||||
fastForward: '[data-plyr="fast-forward"]',
|
fastForward: '[data-plyr="fast-forward"]',
|
||||||
mute: '[data-plyr="mute"]',
|
mute: '[data-plyr="mute"]',
|
||||||
captions: '[data-plyr="captions"]',
|
captions: '[data-plyr="captions"]',
|
||||||
|
download: '[data-plyr="download"]',
|
||||||
fullscreen: '[data-plyr="fullscreen"]',
|
fullscreen: '[data-plyr="fullscreen"]',
|
||||||
pip: '[data-plyr="pip"]',
|
pip: '[data-plyr="pip"]',
|
||||||
airplay: '[data-plyr="airplay"]',
|
airplay: '[data-plyr="airplay"]',
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// ==========================================================================
|
||||||
|
// Plyr states
|
||||||
|
// ==========================================================================
|
||||||
|
|
||||||
|
export const pip = {
|
||||||
|
active: 'picture-in-picture',
|
||||||
|
inactive: 'inline',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default { pip };
|
||||||
@@ -15,7 +15,7 @@ export const types = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get provider by URL
|
* Get provider by URL
|
||||||
* @param {string} url
|
* @param {String} url
|
||||||
*/
|
*/
|
||||||
export function getProviderByUrl(url) {
|
export function getProviderByUrl(url) {
|
||||||
// YouTube
|
// YouTube
|
||||||
|
|||||||
@@ -5,13 +5,13 @@
|
|||||||
|
|
||||||
import captions from './captions';
|
import captions from './captions';
|
||||||
import html5 from './html5';
|
import html5 from './html5';
|
||||||
import i18n from './i18n';
|
|
||||||
import support from './support';
|
import support from './support';
|
||||||
import { repaint, transitionEndEvent } from './utils/animation';
|
import { repaint, transitionEndEvent } from './utils/animation';
|
||||||
import { dedupe } from './utils/arrays';
|
import { dedupe } from './utils/arrays';
|
||||||
import browser from './utils/browser';
|
import browser from './utils/browser';
|
||||||
import { createElement, emptyElement, getAttributesFromSelector, getElement, getElements, hasClass, matches, removeElement, setAttributes, setFocus, toggleClass, toggleHidden } from './utils/elements';
|
import { createElement, emptyElement, getAttributesFromSelector, getElement, getElements, hasClass, matches, removeElement, setAttributes, setFocus, toggleClass, toggleHidden } from './utils/elements';
|
||||||
import { off, on } from './utils/events';
|
import { off, on } from './utils/events';
|
||||||
|
import i18n from './utils/i18n';
|
||||||
import is from './utils/is';
|
import is from './utils/is';
|
||||||
import loadSprite from './utils/loadSprite';
|
import loadSprite from './utils/loadSprite';
|
||||||
import { extend } from './utils/objects';
|
import { extend } from './utils/objects';
|
||||||
@@ -111,10 +111,11 @@ const controls = {
|
|||||||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href
|
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href
|
||||||
if ('href' in use) {
|
if ('href' in use) {
|
||||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path);
|
use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path);
|
||||||
} else {
|
|
||||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always set the older attribute even though it's "deprecated" (it'll be around for ages)
|
||||||
|
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path);
|
||||||
|
|
||||||
// Add <use> to <svg>
|
// Add <use> to <svg>
|
||||||
icon.appendChild(use);
|
icon.appendChild(use);
|
||||||
|
|
||||||
@@ -122,17 +123,13 @@ const controls = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Create hidden text label
|
// Create hidden text label
|
||||||
createLabel(type, attr = {}) {
|
createLabel(key, attr = {}) {
|
||||||
// Skip i18n for abbreviations and brand names
|
const text = i18n.get(key, this.config);
|
||||||
const universals = {
|
|
||||||
pip: 'PIP',
|
|
||||||
airplay: 'AirPlay',
|
|
||||||
};
|
|
||||||
const text = universals[type] || i18n.get(type, this.config);
|
|
||||||
|
|
||||||
const attributes = Object.assign({}, attr, {
|
const attributes = Object.assign({}, attr, {
|
||||||
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' '),
|
class: [attr.class, this.config.classNames.hidden].filter(Boolean).join(' '),
|
||||||
});
|
});
|
||||||
|
|
||||||
return createElement('span', attributes, text);
|
return createElement('span', attributes, text);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -161,21 +158,32 @@ const controls = {
|
|||||||
|
|
||||||
// Create a <button>
|
// Create a <button>
|
||||||
createButton(buttonType, attr) {
|
createButton(buttonType, attr) {
|
||||||
const button = createElement('button');
|
|
||||||
const attributes = Object.assign({}, attr);
|
const attributes = Object.assign({}, attr);
|
||||||
let type = toCamelCase(buttonType);
|
let type = toCamelCase(buttonType);
|
||||||
|
|
||||||
let toggle = false;
|
const props = {
|
||||||
let label;
|
element: 'button',
|
||||||
let icon;
|
toggle: false,
|
||||||
let labelPressed;
|
label: null,
|
||||||
let iconPressed;
|
icon: null,
|
||||||
|
labelPressed: null,
|
||||||
|
iconPressed: null,
|
||||||
|
};
|
||||||
|
|
||||||
if (!('type' in attributes)) {
|
['element', 'icon', 'label'].forEach(key => {
|
||||||
|
if (Object.keys(attributes).includes(key)) {
|
||||||
|
props[key] = attributes[key];
|
||||||
|
delete attributes[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Default to 'button' type to prevent form submission
|
||||||
|
if (props.element === 'button' && !Object.keys(attributes).includes('type')) {
|
||||||
attributes.type = 'button';
|
attributes.type = 'button';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('class' in attributes) {
|
// Set class name
|
||||||
|
if (Object.keys(attributes).includes('class')) {
|
||||||
if (!attributes.class.includes(this.config.classNames.control)) {
|
if (!attributes.class.includes(this.config.classNames.control)) {
|
||||||
attributes.class += ` ${this.config.classNames.control}`;
|
attributes.class += ` ${this.config.classNames.control}`;
|
||||||
}
|
}
|
||||||
@@ -186,82 +194,87 @@ const controls = {
|
|||||||
// Large play button
|
// Large play button
|
||||||
switch (buttonType) {
|
switch (buttonType) {
|
||||||
case 'play':
|
case 'play':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'play';
|
props.label = 'play';
|
||||||
labelPressed = 'pause';
|
props.labelPressed = 'pause';
|
||||||
icon = 'play';
|
props.icon = 'play';
|
||||||
iconPressed = 'pause';
|
props.iconPressed = 'pause';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'mute':
|
case 'mute':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'mute';
|
props.label = 'mute';
|
||||||
labelPressed = 'unmute';
|
props.labelPressed = 'unmute';
|
||||||
icon = 'volume';
|
props.icon = 'volume';
|
||||||
iconPressed = 'muted';
|
props.iconPressed = 'muted';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'captions':
|
case 'captions':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'enableCaptions';
|
props.label = 'enableCaptions';
|
||||||
labelPressed = 'disableCaptions';
|
props.labelPressed = 'disableCaptions';
|
||||||
icon = 'captions-off';
|
props.icon = 'captions-off';
|
||||||
iconPressed = 'captions-on';
|
props.iconPressed = 'captions-on';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'fullscreen':
|
case 'fullscreen':
|
||||||
toggle = true;
|
props.toggle = true;
|
||||||
label = 'enterFullscreen';
|
props.label = 'enterFullscreen';
|
||||||
labelPressed = 'exitFullscreen';
|
props.labelPressed = 'exitFullscreen';
|
||||||
icon = 'enter-fullscreen';
|
props.icon = 'enter-fullscreen';
|
||||||
iconPressed = 'exit-fullscreen';
|
props.iconPressed = 'exit-fullscreen';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'play-large':
|
case 'play-large':
|
||||||
attributes.class += ` ${this.config.classNames.control}--overlaid`;
|
attributes.class += ` ${this.config.classNames.control}--overlaid`;
|
||||||
type = 'play';
|
type = 'play';
|
||||||
label = 'play';
|
props.label = 'play';
|
||||||
icon = 'play';
|
props.icon = 'play';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
label = type;
|
if (is.empty(props.label)) {
|
||||||
icon = buttonType;
|
props.label = type;
|
||||||
|
}
|
||||||
|
if (is.empty(props.icon)) {
|
||||||
|
props.icon = buttonType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const button = createElement(props.element);
|
||||||
|
|
||||||
// Setup toggle icon and labels
|
// Setup toggle icon and labels
|
||||||
if (toggle) {
|
if (props.toggle) {
|
||||||
// Icon
|
// Icon
|
||||||
button.appendChild(
|
button.appendChild(
|
||||||
controls.createIcon.call(this, iconPressed, {
|
controls.createIcon.call(this, props.iconPressed, {
|
||||||
class: 'icon--pressed',
|
class: 'icon--pressed',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
button.appendChild(
|
button.appendChild(
|
||||||
controls.createIcon.call(this, icon, {
|
controls.createIcon.call(this, props.icon, {
|
||||||
class: 'icon--not-pressed',
|
class: 'icon--not-pressed',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Label/Tooltip
|
// Label/Tooltip
|
||||||
button.appendChild(
|
button.appendChild(
|
||||||
controls.createLabel.call(this, labelPressed, {
|
controls.createLabel.call(this, props.labelPressed, {
|
||||||
class: 'label--pressed',
|
class: 'label--pressed',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
button.appendChild(
|
button.appendChild(
|
||||||
controls.createLabel.call(this, label, {
|
controls.createLabel.call(this, props.label, {
|
||||||
class: 'label--not-pressed',
|
class: 'label--not-pressed',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
button.appendChild(controls.createIcon.call(this, icon));
|
button.appendChild(controls.createIcon.call(this, props.icon));
|
||||||
button.appendChild(controls.createLabel.call(this, label));
|
button.appendChild(controls.createLabel.call(this, props.label));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge attributes
|
// Merge and set attributes
|
||||||
extend(attributes, getAttributesFromSelector(this.config.selectors.buttons[type], attributes));
|
extend(attributes, getAttributesFromSelector(this.config.selectors.buttons[type], attributes));
|
||||||
|
|
||||||
setAttributes(button, attributes);
|
setAttributes(button, attributes);
|
||||||
|
|
||||||
// We have multiple play buttons
|
// We have multiple play buttons
|
||||||
@@ -275,18 +288,6 @@ const controls = {
|
|||||||
this.elements.buttons[type] = button;
|
this.elements.buttons[type] = button;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle classname when pressed property is set
|
|
||||||
const className = this.config.classNames.controlPressed;
|
|
||||||
Object.defineProperty(button, 'pressed', {
|
|
||||||
enumerable: true,
|
|
||||||
get() {
|
|
||||||
return hasClass(button, className);
|
|
||||||
},
|
|
||||||
set(pressed = false) {
|
|
||||||
toggleClass(button, className, pressed);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return button;
|
return button;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1142,9 +1143,8 @@ const controls = {
|
|||||||
// Focus the first item if key interaction
|
// Focus the first item if key interaction
|
||||||
if (show && is.keyboardEvent(input)) {
|
if (show && is.keyboardEvent(input)) {
|
||||||
controls.focusFirstMenuItem.call(this, null, true);
|
controls.focusFirstMenuItem.call(this, null, true);
|
||||||
}
|
} else if (!show && !hidden) {
|
||||||
// If closing, re-focus the button
|
// If closing, re-focus the button
|
||||||
else if (!show && !hidden) {
|
|
||||||
setFocus.call(this, button, is.keyboardEvent(input));
|
setFocus.call(this, button, is.keyboardEvent(input));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1227,6 +1227,19 @@ const controls = {
|
|||||||
controls.focusFirstMenuItem.call(this, target, tabFocus);
|
controls.focusFirstMenuItem.call(this, target, tabFocus);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Set the download link
|
||||||
|
setDownloadLink() {
|
||||||
|
const button = this.elements.buttons.download;
|
||||||
|
|
||||||
|
// Bail if no button
|
||||||
|
if (!is.element(button)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set download link
|
||||||
|
button.setAttribute('href', this.download);
|
||||||
|
},
|
||||||
|
|
||||||
// Build the default HTML
|
// Build the default HTML
|
||||||
// TODO: Set order based on order in the config.controls array?
|
// TODO: Set order based on order in the config.controls array?
|
||||||
create(data) {
|
create(data) {
|
||||||
@@ -1297,36 +1310,39 @@ const controls = {
|
|||||||
container.appendChild(controls.createTime.call(this, 'duration'));
|
container.appendChild(controls.createTime.call(this, 'duration'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle mute button
|
// Volume controls
|
||||||
if (this.config.controls.includes('mute')) {
|
if (this.config.controls.includes('mute') || this.config.controls.includes('volume')) {
|
||||||
container.appendChild(controls.createButton.call(this, 'mute'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Volume range control
|
|
||||||
if (this.config.controls.includes('volume')) {
|
|
||||||
const volume = createElement('div', {
|
const volume = createElement('div', {
|
||||||
class: 'plyr__volume',
|
class: 'plyr__volume',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set the attributes
|
// Toggle mute button
|
||||||
const attributes = {
|
if (this.config.controls.includes('mute')) {
|
||||||
max: 1,
|
volume.appendChild(controls.createButton.call(this, 'mute'));
|
||||||
step: 0.05,
|
}
|
||||||
value: this.config.volume,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create the volume range slider
|
// Volume range control
|
||||||
volume.appendChild(
|
if (this.config.controls.includes('volume')) {
|
||||||
controls.createRange.call(
|
// Set the attributes
|
||||||
this,
|
const attributes = {
|
||||||
'volume',
|
max: 1,
|
||||||
extend(attributes, {
|
step: 0.05,
|
||||||
id: `plyr-volume-${data.id}`,
|
value: this.config.volume,
|
||||||
}),
|
};
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.elements.volume = volume;
|
// Create the volume range slider
|
||||||
|
volume.appendChild(
|
||||||
|
controls.createRange.call(
|
||||||
|
this,
|
||||||
|
'volume',
|
||||||
|
extend(attributes, {
|
||||||
|
id: `plyr-volume-${data.id}`,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
this.elements.volume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
container.appendChild(volume);
|
container.appendChild(volume);
|
||||||
}
|
}
|
||||||
@@ -1500,6 +1516,26 @@ const controls = {
|
|||||||
container.appendChild(controls.createButton.call(this, 'airplay'));
|
container.appendChild(controls.createButton.call(this, 'airplay'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Download button
|
||||||
|
if (this.config.controls.includes('download')) {
|
||||||
|
const attributes = {
|
||||||
|
element: 'a',
|
||||||
|
href: this.download,
|
||||||
|
target: '_blank',
|
||||||
|
};
|
||||||
|
|
||||||
|
const { download } = this.config.urls;
|
||||||
|
|
||||||
|
if (!is.url(download) && this.isEmbed) {
|
||||||
|
extend(attributes, {
|
||||||
|
icon: `logo-${this.provider}`,
|
||||||
|
label: this.provider,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
container.appendChild(controls.createButton.call(this, 'download', attributes));
|
||||||
|
}
|
||||||
|
|
||||||
// Toggle fullscreen button
|
// Toggle fullscreen button
|
||||||
if (this.config.controls.includes('fullscreen')) {
|
if (this.config.controls.includes('fullscreen')) {
|
||||||
container.appendChild(controls.createButton.call(this, 'fullscreen'));
|
container.appendChild(controls.createButton.call(this, 'fullscreen'));
|
||||||
@@ -1512,6 +1548,7 @@ const controls = {
|
|||||||
|
|
||||||
this.elements.controls = container;
|
this.elements.controls = container;
|
||||||
|
|
||||||
|
// Set available quality levels
|
||||||
if (this.isHTML5) {
|
if (this.isHTML5) {
|
||||||
controls.setQualityMenu.call(this, html5.getQualityOptions.call(this));
|
controls.setQualityMenu.call(this, html5.getQualityOptions.call(this));
|
||||||
}
|
}
|
||||||
@@ -1617,6 +1654,33 @@ const controls = {
|
|||||||
controls.findElements.call(this);
|
controls.findElements.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add pressed property to buttons
|
||||||
|
if (!is.empty(this.elements.buttons)) {
|
||||||
|
const addProperty = button => {
|
||||||
|
const className = this.config.classNames.controlPressed;
|
||||||
|
Object.defineProperty(button, 'pressed', {
|
||||||
|
enumerable: true,
|
||||||
|
get() {
|
||||||
|
return hasClass(button, className);
|
||||||
|
},
|
||||||
|
set(pressed = false) {
|
||||||
|
toggleClass(button, className, pressed);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Toggle classname when pressed property is set
|
||||||
|
Object.values(this.elements.buttons)
|
||||||
|
.filter(Boolean)
|
||||||
|
.forEach(button => {
|
||||||
|
if (is.array(button) || is.nodeList(button)) {
|
||||||
|
Array.from(button).filter(Boolean).forEach(addProperty);
|
||||||
|
} else {
|
||||||
|
addProperty(button);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Edge sometimes doesn't finish the paint so force a redraw
|
// Edge sometimes doesn't finish the paint so force a redraw
|
||||||
if (window.navigator.userAgent.includes('Edge')) {
|
if (window.navigator.userAgent.includes('Edge')) {
|
||||||
repaint(target);
|
repaint(target);
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Fullscreen wrapper
|
// Fullscreen wrapper
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API#prefixing
|
// https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API#prefixing
|
||||||
|
// https://webkit.org/blog/7929/designing-websites-for-iphone-x/
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
|
import { repaint } from './utils/animation';
|
||||||
import browser from './utils/browser';
|
import browser from './utils/browser';
|
||||||
import { hasClass, toggleClass, trapFocus } from './utils/elements';
|
import { hasClass, toggleClass, trapFocus } from './utils/elements';
|
||||||
import { on, triggerEvent } from './utils/events';
|
import { on, triggerEvent } from './utils/events';
|
||||||
@@ -45,6 +47,37 @@ function toggleFallback(toggle = false) {
|
|||||||
// Toggle class hook
|
// Toggle class hook
|
||||||
toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle);
|
toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle);
|
||||||
|
|
||||||
|
// Force full viewport on iPhone X+
|
||||||
|
if (browser.isIos) {
|
||||||
|
let viewport = document.head.querySelector('meta[name="viewport"]');
|
||||||
|
const property = 'viewport-fit=cover';
|
||||||
|
|
||||||
|
// Inject the viewport meta if required
|
||||||
|
if (!viewport) {
|
||||||
|
viewport = document.createElement('meta');
|
||||||
|
viewport.setAttribute('name', 'viewport');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the property already exists
|
||||||
|
const hasProperty = is.string(viewport.content) && viewport.content.includes(property);
|
||||||
|
|
||||||
|
if (toggle) {
|
||||||
|
this.cleanupViewport = !hasProperty;
|
||||||
|
|
||||||
|
if (!hasProperty) {
|
||||||
|
viewport.content += `,${property}`;
|
||||||
|
}
|
||||||
|
} else if (this.cleanupViewport) {
|
||||||
|
viewport.content = viewport.content
|
||||||
|
.split(',')
|
||||||
|
.filter(part => part.trim() !== property)
|
||||||
|
.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force a repaint as sometimes Safari doesn't want to fill the screen
|
||||||
|
setTimeout(() => repaint(this.target), 100);
|
||||||
|
}
|
||||||
|
|
||||||
// Toggle button and fire events
|
// Toggle button and fire events
|
||||||
onChange.call(this);
|
onChange.call(this);
|
||||||
}
|
}
|
||||||
@@ -177,9 +210,7 @@ class Fullscreen {
|
|||||||
|
|
||||||
// iOS native fullscreen doesn't need the request step
|
// iOS native fullscreen doesn't need the request step
|
||||||
if (browser.isIos && this.player.config.fullscreen.iosNative) {
|
if (browser.isIos && this.player.config.fullscreen.iosNative) {
|
||||||
if (this.player.playing) {
|
this.target.webkitEnterFullscreen();
|
||||||
this.target.webkitEnterFullscreen();
|
|
||||||
}
|
|
||||||
} else if (!Fullscreen.native) {
|
} else if (!Fullscreen.native) {
|
||||||
toggleFallback.call(this, true);
|
toggleFallback.call(this, true);
|
||||||
} else if (!this.prefix) {
|
} else if (!this.prefix) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import controls from './controls';
|
|||||||
import ui from './ui';
|
import ui from './ui';
|
||||||
import { repaint } from './utils/animation';
|
import { repaint } from './utils/animation';
|
||||||
import browser from './utils/browser';
|
import browser from './utils/browser';
|
||||||
import { getElement, getElements, hasClass, matches, toggleClass, toggleHidden } from './utils/elements';
|
import { getElement, getElements, matches, toggleClass, toggleHidden } from './utils/elements';
|
||||||
import { on, once, toggleListener, triggerEvent } from './utils/events';
|
import { on, once, toggleListener, triggerEvent } from './utils/events';
|
||||||
import is from './utils/is';
|
import is from './utils/is';
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ class Listeners {
|
|||||||
// Seek by the number keys
|
// Seek by the number keys
|
||||||
const seekByKey = () => {
|
const seekByKey = () => {
|
||||||
// Divide the max duration into 10th's and times by the number value
|
// Divide the max duration into 10th's and times by the number value
|
||||||
player.currentTime = player.duration / 10 * (code - 48);
|
player.currentTime = (player.duration / 10) * (code - 48);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle the key on keydown
|
// Handle the key on keydown
|
||||||
@@ -146,7 +146,7 @@ class Listeners {
|
|||||||
player.loop = !player.loop;
|
player.loop = !player.loop;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* case 73:
|
/* case 73:
|
||||||
this.setLoop('start');
|
this.setLoop('start');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -277,7 +277,7 @@ class Listeners {
|
|||||||
const { controls } = elements;
|
const { controls } = elements;
|
||||||
|
|
||||||
// Remove button states for fullscreen
|
// Remove button states for fullscreen
|
||||||
if (event.type === 'enterfullscreen') {
|
if (controls && event.type === 'enterfullscreen') {
|
||||||
controls.pressed = false;
|
controls.pressed = false;
|
||||||
controls.hover = false;
|
controls.hover = false;
|
||||||
}
|
}
|
||||||
@@ -317,7 +317,7 @@ class Listeners {
|
|||||||
|
|
||||||
// Check for audio tracks on load
|
// Check for audio tracks on load
|
||||||
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
|
// We can't use `loadedmetadata` as it doesn't seem to have audio tracks at that point
|
||||||
on.call(player, player.media, 'canplay', () => {
|
on.call(player, player.media, 'canplay loadeddata', () => {
|
||||||
toggleHidden(elements.volume, !player.hasAudio);
|
toggleHidden(elements.volume, !player.hasAudio);
|
||||||
toggleHidden(elements.buttons.mute, !player.hasAudio);
|
toggleHidden(elements.buttons.mute, !player.hasAudio);
|
||||||
});
|
});
|
||||||
@@ -371,8 +371,8 @@ class Listeners {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// On click play, pause ore restart
|
// On click play, pause or restart
|
||||||
on.call(player, elements.container, 'click touchstart', event => {
|
on.call(player, elements.container, 'click', event => {
|
||||||
const targets = [elements.container, wrapper];
|
const targets = [elements.container, wrapper];
|
||||||
|
|
||||||
// Ignore if click if not container or in video wrapper
|
// Ignore if click if not container or in video wrapper
|
||||||
@@ -380,13 +380,8 @@ class Listeners {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First touch on touch devices will just show controls (if we're hiding controls)
|
// Touch devices will just show controls (if hidden)
|
||||||
// If controls are shown then it'll toggle like a pointer device
|
if (player.touch && player.config.hideControls) {
|
||||||
if (
|
|
||||||
player.config.hideControls &&
|
|
||||||
player.touch &&
|
|
||||||
hasClass(elements.container, player.config.classNames.hideControls)
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,18 +425,17 @@ class Listeners {
|
|||||||
player.storage.set({ speed: player.speed });
|
player.storage.set({ speed: player.speed });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Quality request
|
|
||||||
on.call(player, player.media, 'qualityrequested', event => {
|
|
||||||
// Save to storage
|
|
||||||
player.storage.set({ quality: event.detail.quality });
|
|
||||||
});
|
|
||||||
|
|
||||||
// Quality change
|
// Quality change
|
||||||
on.call(player, player.media, 'qualitychange', event => {
|
on.call(player, player.media, 'qualitychange', event => {
|
||||||
// Update UI
|
// Update UI
|
||||||
controls.updateSetting.call(player, 'quality', null, event.detail.quality);
|
controls.updateSetting.call(player, 'quality', null, event.detail.quality);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Update download link when ready and if quality changes
|
||||||
|
on.call(player, player.media, 'ready qualitychange', () => {
|
||||||
|
controls.setDownloadLink.call(player);
|
||||||
|
});
|
||||||
|
|
||||||
// Proxy events to container
|
// Proxy events to container
|
||||||
// Bubble up key events for Edge
|
// Bubble up key events for Edge
|
||||||
const proxyEvents = player.config.events.concat(['keyup', 'keydown']).join(' ');
|
const proxyEvents = player.config.events.concat(['keyup', 'keydown']).join(' ');
|
||||||
@@ -528,6 +522,16 @@ class Listeners {
|
|||||||
// Captions toggle
|
// Captions toggle
|
||||||
this.bind(elements.buttons.captions, 'click', () => player.toggleCaptions());
|
this.bind(elements.buttons.captions, 'click', () => player.toggleCaptions());
|
||||||
|
|
||||||
|
// Download
|
||||||
|
this.bind(
|
||||||
|
elements.buttons.download,
|
||||||
|
'click',
|
||||||
|
() => {
|
||||||
|
triggerEvent.call(player, player.media, 'download');
|
||||||
|
},
|
||||||
|
'download',
|
||||||
|
);
|
||||||
|
|
||||||
// Fullscreen toggle
|
// Fullscreen toggle
|
||||||
this.bind(
|
this.bind(
|
||||||
elements.buttons.fullscreen,
|
elements.buttons.fullscreen,
|
||||||
@@ -602,7 +606,7 @@ class Listeners {
|
|||||||
// Set range input alternative "value", which matches the tooltip time (#954)
|
// Set range input alternative "value", which matches the tooltip time (#954)
|
||||||
this.bind(elements.inputs.seek, 'mousedown mousemove', event => {
|
this.bind(elements.inputs.seek, 'mousedown mousemove', event => {
|
||||||
const rect = elements.progress.getBoundingClientRect();
|
const rect = elements.progress.getBoundingClientRect();
|
||||||
const percent = 100 / rect.width * (event.pageX - rect.left);
|
const percent = (100 / rect.width) * (event.pageX - rect.left);
|
||||||
event.currentTarget.setAttribute('seek-value', percent);
|
event.currentTarget.setAttribute('seek-value', percent);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -616,6 +620,9 @@ class Listeners {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record seek time so we can prevent hiding controls for a few seconds after seek
|
||||||
|
player.lastSeekTime = Date.now();
|
||||||
|
|
||||||
// Was playing before?
|
// Was playing before?
|
||||||
const play = seek.hasAttribute(attribute);
|
const play = seek.hasAttribute(attribute);
|
||||||
|
|
||||||
@@ -656,7 +663,7 @@ class Listeners {
|
|||||||
|
|
||||||
seek.removeAttribute('seek-value');
|
seek.removeAttribute('seek-value');
|
||||||
|
|
||||||
player.currentTime = seekTo / seek.max * player.duration;
|
player.currentTime = (seekTo / seek.max) * player.duration;
|
||||||
},
|
},
|
||||||
'seek',
|
'seek',
|
||||||
);
|
);
|
||||||
@@ -708,33 +715,29 @@ class Listeners {
|
|||||||
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
|
elements.controls.pressed = ['mousedown', 'touchstart'].includes(event.type);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Focus in/out on controls
|
// Show controls when they receive focus (e.g., when using keyboard tab key)
|
||||||
this.bind(elements.controls, 'focusin focusout', event => {
|
this.bind(elements.controls, 'focusin', () => {
|
||||||
const { config, elements, timers } = player;
|
const { config, elements, timers } = player;
|
||||||
const isFocusIn = event.type === 'focusin';
|
|
||||||
|
|
||||||
// Skip transition to prevent focus from scrolling the parent element
|
// Skip transition to prevent focus from scrolling the parent element
|
||||||
toggleClass(elements.controls, config.classNames.noTransition, isFocusIn);
|
toggleClass(elements.controls, config.classNames.noTransition, true);
|
||||||
|
|
||||||
// Toggle
|
// Toggle
|
||||||
ui.toggleControls.call(player, isFocusIn);
|
ui.toggleControls.call(player, true);
|
||||||
|
|
||||||
// If focusin, hide again after delay
|
// Restore transition
|
||||||
if (isFocusIn) {
|
setTimeout(() => {
|
||||||
// Restore transition
|
toggleClass(elements.controls, config.classNames.noTransition, false);
|
||||||
setTimeout(() => {
|
}, 0);
|
||||||
toggleClass(elements.controls, config.classNames.noTransition, false);
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
// Delay a little more for keyboard users
|
// Delay a little more for mouse users
|
||||||
const delay = this.touch ? 3000 : 4000;
|
const delay = this.touch ? 3000 : 4000;
|
||||||
|
|
||||||
// Clear timer
|
// Clear timer
|
||||||
clearTimeout(timers.controls);
|
clearTimeout(timers.controls);
|
||||||
|
|
||||||
// Hide
|
// Hide again after delay
|
||||||
timers.controls = setTimeout(() => ui.toggleControls.call(player, false), delay);
|
timers.controls = setTimeout(() => ui.toggleControls.call(player, false), delay);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mouse wheel for volume
|
// Mouse wheel for volume
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
/* global google */
|
/* global google */
|
||||||
|
|
||||||
import i18n from '../i18n';
|
|
||||||
import { createElement } from '../utils/elements';
|
import { createElement } from '../utils/elements';
|
||||||
import { triggerEvent } from '../utils/events';
|
import { triggerEvent } from '../utils/events';
|
||||||
|
import i18n from '../utils/i18n';
|
||||||
import is from '../utils/is';
|
import is from '../utils/is';
|
||||||
import loadScript from '../utils/loadScript';
|
import loadScript from '../utils/loadScript';
|
||||||
import { formatTime } from '../utils/time';
|
import { formatTime } from '../utils/time';
|
||||||
|
|||||||
@@ -70,8 +70,9 @@ const vimeo = {
|
|||||||
// Set aspect ratio
|
// Set aspect ratio
|
||||||
// For Vimeo we have an extra 300% height <div> to hide the standard controls and UI
|
// For Vimeo we have an extra 300% height <div> to hide the standard controls and UI
|
||||||
setAspectRatio(input) {
|
setAspectRatio(input) {
|
||||||
const [x, y] = (is.string(input) ? input : this.config.ratio).split(':');
|
const [x, y] = (is.string(input) ? input : this.config.ratio).split(':').map(Number);
|
||||||
const padding = 100 / x * y;
|
const padding = (100 / x) * y;
|
||||||
|
vimeo.padding = padding;
|
||||||
this.elements.wrapper.style.paddingBottom = `${padding}%`;
|
this.elements.wrapper.style.paddingBottom = `${padding}%`;
|
||||||
|
|
||||||
if (this.supported.ui) {
|
if (this.supported.ui) {
|
||||||
@@ -278,6 +279,7 @@ const vimeo = {
|
|||||||
.getVideoUrl()
|
.getVideoUrl()
|
||||||
.then(value => {
|
.then(value => {
|
||||||
currentSrc = value;
|
currentSrc = value;
|
||||||
|
controls.setDownloadLink.call(player);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.debug.warn(error);
|
this.debug.warn(error);
|
||||||
@@ -298,8 +300,8 @@ const vimeo = {
|
|||||||
|
|
||||||
// Set aspect ratio based on video size
|
// Set aspect ratio based on video size
|
||||||
Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(dimensions => {
|
Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(dimensions => {
|
||||||
const ratio = getAspectRatio(dimensions[0], dimensions[1]);
|
vimeo.ratio = getAspectRatio(dimensions[0], dimensions[1]);
|
||||||
vimeo.setAspectRatio.call(this, ratio);
|
vimeo.setAspectRatio.call(this, vimeo.ratio);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set autopause
|
// Set autopause
|
||||||
@@ -403,6 +405,22 @@ const vimeo = {
|
|||||||
triggerEvent.call(player, player.media, 'error');
|
triggerEvent.call(player, player.media, 'error');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Set height/width on fullscreen
|
||||||
|
player.on('enterfullscreen exitfullscreen', event => {
|
||||||
|
const { target } = player.fullscreen;
|
||||||
|
|
||||||
|
// Ignore for iOS native
|
||||||
|
if (target !== player.elements.container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggle = event.type === 'enterfullscreen';
|
||||||
|
const [x, y] = vimeo.ratio.split(':').map(Number);
|
||||||
|
const dimension = x > y ? 'width' : 'height';
|
||||||
|
|
||||||
|
target.style[dimension] = toggle ? `${vimeo.padding}%` : null;
|
||||||
|
});
|
||||||
|
|
||||||
// Rebuild UI
|
// Rebuild UI
|
||||||
setTimeout(() => ui.build.call(player), 0);
|
setTimeout(() => ui.build.call(player), 0);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
// YouTube plugin
|
// YouTube plugin
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
import controls from '../controls';
|
|
||||||
import ui from '../ui';
|
import ui from '../ui';
|
||||||
import { dedupe } from '../utils/arrays';
|
|
||||||
import { createElement, replaceElement, toggleClass } from '../utils/elements';
|
import { createElement, replaceElement, toggleClass } from '../utils/elements';
|
||||||
import { triggerEvent } from '../utils/events';
|
import { triggerEvent } from '../utils/events';
|
||||||
import fetch from '../utils/fetch';
|
import fetch from '../utils/fetch';
|
||||||
@@ -23,37 +21,6 @@ function parseId(url) {
|
|||||||
return url.match(regex) ? RegExp.$2 : url;
|
return url.match(regex) ? RegExp.$2 : url;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standardise YouTube quality unit
|
|
||||||
function mapQualityUnit(input) {
|
|
||||||
const qualities = {
|
|
||||||
hd2160: 2160,
|
|
||||||
hd1440: 1440,
|
|
||||||
hd1080: 1080,
|
|
||||||
hd720: 720,
|
|
||||||
large: 480,
|
|
||||||
medium: 360,
|
|
||||||
small: 240,
|
|
||||||
tiny: 144,
|
|
||||||
};
|
|
||||||
|
|
||||||
const entry = Object.entries(qualities).find(entry => entry.includes(input));
|
|
||||||
|
|
||||||
if (entry) {
|
|
||||||
// Get the match corresponding to the input
|
|
||||||
return entry.find(value => value !== input);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapQualityUnits(levels) {
|
|
||||||
if (is.empty(levels)) {
|
|
||||||
return levels;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dedupe(levels.map(level => mapQualityUnit(level)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set playback state and trigger change (only on actual change)
|
// Set playback state and trigger change (only on actual change)
|
||||||
function assurePlaybackState(play) {
|
function assurePlaybackState(play) {
|
||||||
if (play && !this.embed.hasPlayed) {
|
if (play && !this.embed.hasPlayed) {
|
||||||
@@ -225,11 +192,6 @@ const youtube = {
|
|||||||
triggerEvent.call(player, player.media, 'error');
|
triggerEvent.call(player, player.media, 'error');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPlaybackQualityChange() {
|
|
||||||
triggerEvent.call(player, player.media, 'qualitychange', false, {
|
|
||||||
quality: player.media.quality,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onPlaybackRateChange(event) {
|
onPlaybackRateChange(event) {
|
||||||
// Get the instance
|
// Get the instance
|
||||||
const instance = event.target;
|
const instance = event.target;
|
||||||
@@ -299,16 +261,6 @@ const youtube = {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Quality
|
|
||||||
Object.defineProperty(player.media, 'quality', {
|
|
||||||
get() {
|
|
||||||
return mapQualityUnit(instance.getPlaybackQuality());
|
|
||||||
},
|
|
||||||
set(input) {
|
|
||||||
instance.setPlaybackQuality(mapQualityUnit(input));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Volume
|
// Volume
|
||||||
let { volume } = player.config;
|
let { volume } = player.config;
|
||||||
Object.defineProperty(player.media, 'volume', {
|
Object.defineProperty(player.media, 'volume', {
|
||||||
@@ -457,12 +409,6 @@ const youtube = {
|
|||||||
player.media.duration = instance.getDuration();
|
player.media.duration = instance.getDuration();
|
||||||
triggerEvent.call(player, player.media, 'durationchange');
|
triggerEvent.call(player, player.media, 'durationchange');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get quality
|
|
||||||
controls.setQualityMenu.call(
|
|
||||||
player,
|
|
||||||
mapQualityUnits(instance.getAvailableQualityLevels()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Plyr
|
// Plyr
|
||||||
// plyr.js v3.4.0-beta.2
|
// plyr.js v3.4.7
|
||||||
// https://github.com/sampotts/plyr
|
// https://github.com/sampotts/plyr
|
||||||
// License: The MIT License (MIT)
|
// License: The MIT License (MIT)
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
import captions from './captions';
|
import captions from './captions';
|
||||||
import defaults from './config/defaults';
|
import defaults from './config/defaults';
|
||||||
|
import { pip } from './config/states';
|
||||||
import { getProviderByUrl, providers, types } from './config/types';
|
import { getProviderByUrl, providers, types } from './config/types';
|
||||||
import Console from './console';
|
import Console from './console';
|
||||||
import controls from './controls';
|
import controls from './controls';
|
||||||
@@ -302,6 +303,9 @@ class Plyr {
|
|||||||
if (this.config.autoplay) {
|
if (this.config.autoplay) {
|
||||||
this.play();
|
this.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek
|
||||||
|
this.lastSeekTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
@@ -692,22 +696,27 @@ class Plyr {
|
|||||||
config.default,
|
config.default,
|
||||||
].find(is.number);
|
].find(is.number);
|
||||||
|
|
||||||
|
let updateStorage = true;
|
||||||
|
|
||||||
if (!options.includes(quality)) {
|
if (!options.includes(quality)) {
|
||||||
const value = closest(options, quality);
|
const value = closest(options, quality);
|
||||||
this.debug.warn(`Unsupported quality option: ${quality}, using ${value} instead`);
|
this.debug.warn(`Unsupported quality option: ${quality}, using ${value} instead`);
|
||||||
quality = value;
|
quality = value;
|
||||||
}
|
|
||||||
|
|
||||||
// Trigger request event
|
// Don't update storage if quality is not supported
|
||||||
triggerEvent.call(this, this.media, 'qualityrequested', false, {
|
updateStorage = false;
|
||||||
quality,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Update config
|
// Update config
|
||||||
config.selected = quality;
|
config.selected = quality;
|
||||||
|
|
||||||
// Set quality
|
// Set quality
|
||||||
this.media.quality = quality;
|
this.media.quality = quality;
|
||||||
|
|
||||||
|
// Save to storage
|
||||||
|
if (updateStorage) {
|
||||||
|
this.storage.set({ quality });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -793,6 +802,15 @@ class Plyr {
|
|||||||
return this.media.currentSrc;
|
return this.media.currentSrc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a download URL (either source or custom)
|
||||||
|
*/
|
||||||
|
get download() {
|
||||||
|
const { download } = this.config.urls;
|
||||||
|
|
||||||
|
return is.url(download) ? download : this.source;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the poster image for a video
|
* Set the poster image for a video
|
||||||
* @param {input} - the URL for the new poster image
|
* @param {input} - the URL for the new poster image
|
||||||
@@ -879,21 +897,28 @@ class Plyr {
|
|||||||
* TODO: detect outside changes
|
* TODO: detect outside changes
|
||||||
*/
|
*/
|
||||||
set pip(input) {
|
set pip(input) {
|
||||||
const states = {
|
|
||||||
pip: 'picture-in-picture',
|
|
||||||
inline: 'inline',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Bail if no support
|
// Bail if no support
|
||||||
if (!support.pip) {
|
if (!support.pip) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle based on current state if not passed
|
// Toggle based on current state if not passed
|
||||||
const toggle = is.boolean(input) ? input : this.pip === states.inline;
|
const toggle = is.boolean(input) ? input : !this.pip;
|
||||||
|
|
||||||
// Toggle based on current state
|
// Toggle based on current state
|
||||||
this.media.webkitSetPresentationMode(toggle ? states.pip : states.inline);
|
// Safari
|
||||||
|
if (is.function(this.media.webkitSetPresentationMode)) {
|
||||||
|
this.media.webkitSetPresentationMode(toggle ? pip.active : pip.inactive);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chrome
|
||||||
|
if (is.function(this.media.requestPictureInPicture)) {
|
||||||
|
if (!this.pip && toggle) {
|
||||||
|
this.media.requestPictureInPicture();
|
||||||
|
} else if (this.pip && !toggle) {
|
||||||
|
document.exitPictureInPicture();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -904,7 +929,13 @@ class Plyr {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.media.webkitPresentationMode;
|
// Safari
|
||||||
|
if (!is.empty(this.media.webkitPresentationMode)) {
|
||||||
|
return this.media.webkitPresentationMode === pip.active;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chrome
|
||||||
|
return this.media === document.pictureInPictureElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Plyr Polyfilled Build
|
// Plyr Polyfilled Build
|
||||||
// plyr.js v3.4.0-beta.2
|
// plyr.js v3.4.7
|
||||||
// https://github.com/sampotts/plyr
|
// https://github.com/sampotts/plyr
|
||||||
// License: The MIT License (MIT)
|
// License: The MIT License (MIT)
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
import 'babel-polyfill';
|
|
||||||
import 'custom-event-polyfill';
|
import 'custom-event-polyfill';
|
||||||
import 'url-polyfill';
|
import 'url-polyfill';
|
||||||
import Plyr from './plyr';
|
import Plyr from './plyr';
|
||||||
|
|||||||
@@ -114,12 +114,9 @@ const source = {
|
|||||||
// HTML5 stuff
|
// HTML5 stuff
|
||||||
if (this.isHTML5) {
|
if (this.isHTML5) {
|
||||||
// Setup captions
|
// Setup captions
|
||||||
if ('tracks' in input) {
|
if (Object.keys(input).includes('tracks')) {
|
||||||
source.insertElements.call(this, 'track', input.tracks);
|
source.insertElements.call(this, 'track', input.tracks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load HTML5 sources
|
|
||||||
this.media.load();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If HTML5 or embed but not fully supported, setupInterface and call ready now
|
// If HTML5 or embed but not fully supported, setupInterface and call ready now
|
||||||
@@ -128,6 +125,11 @@ const source = {
|
|||||||
ui.build.call(this);
|
ui.build.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isHTML5) {
|
||||||
|
// Load HTML5 sources
|
||||||
|
this.media.load();
|
||||||
|
}
|
||||||
|
|
||||||
// Update the fullscreen support
|
// Update the fullscreen support
|
||||||
this.fullscreen.update();
|
this.fullscreen.update();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -36,8 +36,26 @@ const support = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Picture-in-picture support
|
// Picture-in-picture support
|
||||||
// Safari only currently
|
// Safari & Chrome only currently
|
||||||
pip: (() => !browser.isIPhone && is.function(createElement('video').webkitSetPresentationMode))(),
|
pip: (() => {
|
||||||
|
if (browser.isIPhone) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safari
|
||||||
|
// https://developer.apple.com/documentation/webkitjs/adding_picture_in_picture_to_your_safari_media_controls
|
||||||
|
if (is.function(createElement('video').webkitSetPresentationMode)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chrome
|
||||||
|
// https://developers.google.com/web/updates/2018/10/watch-video-using-picture-in-picture
|
||||||
|
if (document.pictureInPictureEnabled && !createElement('video').disablePictureInPicture) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
})(),
|
||||||
|
|
||||||
// Airplay support
|
// Airplay support
|
||||||
// Safari only currently
|
// Safari only currently
|
||||||
@@ -52,25 +70,21 @@ const support = {
|
|||||||
// Related: http://www.leanbackplayer.com/test/h5mt.html
|
// Related: http://www.leanbackplayer.com/test/h5mt.html
|
||||||
mime(inputType) {
|
mime(inputType) {
|
||||||
const [mediaType] = inputType.split('/');
|
const [mediaType] = inputType.split('/');
|
||||||
|
let type = inputType;
|
||||||
|
|
||||||
|
// Verify we're using HTML5 and there's no media type mismatch
|
||||||
if (!this.isHTML5 || mediaType !== this.type) {
|
if (!this.isHTML5 || mediaType !== this.type) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let type;
|
// Add codec if required
|
||||||
if (inputType && inputType.includes('codecs=')) {
|
if (Object.keys(defaultCodecs).includes(type)) {
|
||||||
// Use input directly
|
type += `; codecs="${defaultCodecs[inputType]}"`;
|
||||||
type = inputType;
|
|
||||||
} else if (inputType === 'audio/mpeg') {
|
|
||||||
// Skip codec
|
|
||||||
type = 'audio/mpeg;';
|
|
||||||
} else if (inputType in defaultCodecs) {
|
|
||||||
// Use codec
|
|
||||||
type = `${inputType}; codecs="${defaultCodecs[inputType]}"`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Boolean(type && this.media.canPlayType(type).replace(/no/, ''));
|
return Boolean(type && this.media.canPlayType(type).replace(/no/, ''));
|
||||||
} catch (err) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,11 +4,11 @@
|
|||||||
|
|
||||||
import captions from './captions';
|
import captions from './captions';
|
||||||
import controls from './controls';
|
import controls from './controls';
|
||||||
import i18n from './i18n';
|
|
||||||
import support from './support';
|
import support from './support';
|
||||||
import browser from './utils/browser';
|
import browser from './utils/browser';
|
||||||
import { getElement, toggleClass } from './utils/elements';
|
import { getElement, toggleClass } from './utils/elements';
|
||||||
import { ready, triggerEvent } from './utils/events';
|
import { ready, triggerEvent } from './utils/events';
|
||||||
|
import i18n from './utils/i18n';
|
||||||
import is from './utils/is';
|
import is from './utils/is';
|
||||||
import loadImage from './utils/loadImage';
|
import loadImage from './utils/loadImage';
|
||||||
|
|
||||||
@@ -247,8 +247,11 @@ const ui = {
|
|||||||
const { controls } = this.elements;
|
const { controls } = this.elements;
|
||||||
|
|
||||||
if (controls && this.config.hideControls) {
|
if (controls && this.config.hideControls) {
|
||||||
// Show controls if force, loading, paused, or button interaction, otherwise hide
|
// Don't hide controls if a touch-device user recently seeked. (Must be limited to touch devices, or it occasionally prevents desktop controls from hiding.)
|
||||||
this.toggleControls(Boolean(force || this.loading || this.paused || controls.pressed || controls.hover));
|
const recentTouchSeek = (this.touch && this.lastSeekTime + 2000 > Date.now());
|
||||||
|
|
||||||
|
// Show controls if force, loading, paused, button interaction, or recent seek, otherwise hide
|
||||||
|
this.toggleControls(Boolean(force || this.loading || this.paused || controls.pressed || controls.hover || recentTouchSeek));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ export const transitionEndEvent = (() => {
|
|||||||
transition: 'transitionend',
|
transition: 'transitionend',
|
||||||
};
|
};
|
||||||
|
|
||||||
const type = Object.keys(events).find(
|
const type = Object.keys(events).find(event => element.style[event] !== undefined);
|
||||||
event => element.style[event] !== undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
return is.string(type) ? events[type] : false;
|
return is.string(type) ? events[type] : false;
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ export function setFocus(element = null, tabFocus = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set regular focus
|
// Set regular focus
|
||||||
element.focus();
|
element.focus({ preventScroll: true });
|
||||||
|
|
||||||
// If we want to mimic keyboard focus via tab
|
// If we want to mimic keyboard focus via tab
|
||||||
if (tabFocus) {
|
if (tabFocus) {
|
||||||
|
|||||||
@@ -2,9 +2,18 @@
|
|||||||
// Plyr internationalization
|
// Plyr internationalization
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
import is from './utils/is';
|
import is from './is';
|
||||||
import { getDeep } from './utils/objects';
|
import { getDeep } from './objects';
|
||||||
import { replaceAll } from './utils/strings';
|
import { replaceAll } from './strings';
|
||||||
|
|
||||||
|
// Skip i18n for abbreviations and brand names
|
||||||
|
const resources = {
|
||||||
|
pip: 'PIP',
|
||||||
|
airplay: 'AirPlay',
|
||||||
|
html5: 'HTML5',
|
||||||
|
vimeo: 'Vimeo',
|
||||||
|
youtube: 'YouTube',
|
||||||
|
};
|
||||||
|
|
||||||
const i18n = {
|
const i18n = {
|
||||||
get(key = '', config = {}) {
|
get(key = '', config = {}) {
|
||||||
@@ -15,6 +24,10 @@ const i18n = {
|
|||||||
let string = getDeep(config.i18n, key);
|
let string = getDeep(config.i18n, key);
|
||||||
|
|
||||||
if (is.empty(string)) {
|
if (is.empty(string)) {
|
||||||
|
if (Object.keys(resources).includes(key)) {
|
||||||
|
return resources[key];
|
||||||
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,6 +31,11 @@ const isUrl = input => {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must be string from here
|
||||||
|
if (!isString(input)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Add the protocol if required
|
// Add the protocol if required
|
||||||
let string = input;
|
let string = input;
|
||||||
if (!input.startsWith('http://') || !input.startsWith('https://')) {
|
if (!input.startsWith('http://') || !input.startsWith('https://')) {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function getPercentage(current, max) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (current / max * 100).toFixed(2);
|
return ((current / max) * 100).toFixed(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace all occurances of a string in a string
|
// Replace all occurances of a string in a string
|
||||||
|
|||||||
@@ -33,6 +33,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove any link styling
|
||||||
|
a.plyr__control {
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&::after,
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Change icons on state change
|
// Change icons on state change
|
||||||
.plyr__control:not(.plyr__control--pressed) .icon--pressed,
|
.plyr__control:not(.plyr__control--pressed) .icon--pressed,
|
||||||
.plyr__control.plyr__control--pressed .icon--not-pressed,
|
.plyr__control.plyr__control--pressed .icon--not-pressed,
|
||||||
@@ -81,11 +91,10 @@
|
|||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
||||||
|
// Offset icon to make the play button look right
|
||||||
svg {
|
svg {
|
||||||
height: $plyr-control-icon-size-large;
|
left: 2px;
|
||||||
left: 2px; // Offset to make the play button look right
|
|
||||||
position: relative;
|
position: relative;
|
||||||
width: $plyr-control-icon-size-large;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
|
|||||||
@@ -18,20 +18,24 @@
|
|||||||
> .plyr__control,
|
> .plyr__control,
|
||||||
.plyr__progress,
|
.plyr__progress,
|
||||||
.plyr__time,
|
.plyr__time,
|
||||||
.plyr__menu {
|
.plyr__menu,
|
||||||
margin-left: ($plyr-control-spacing / 2);
|
|
||||||
|
|
||||||
&:first-child,
|
|
||||||
&:first-child + [data-plyr='pause'] {
|
|
||||||
margin-left: 0;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.plyr__volume {
|
.plyr__volume {
|
||||||
margin-left: ($plyr-control-spacing / 2);
|
margin-left: ($plyr-control-spacing / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.plyr__menu + .plyr__control,
|
||||||
|
> .plyr__control + .plyr__menu,
|
||||||
|
> .plyr__control + .plyr__control,
|
||||||
|
.plyr__progress + .plyr__control {
|
||||||
|
margin-left: floor($plyr-control-spacing / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .plyr__control:first-child,
|
||||||
|
> .plyr__control:first-child + [data-plyr='pause'] {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
// Hide empty controls
|
// Hide empty controls
|
||||||
&:empty {
|
&:empty {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -39,17 +43,12 @@
|
|||||||
|
|
||||||
@media (min-width: $plyr-bp-sm) {
|
@media (min-width: $plyr-bp-sm) {
|
||||||
> .plyr__control,
|
> .plyr__control,
|
||||||
|
.plyr__menu,
|
||||||
.plyr__progress,
|
.plyr__progress,
|
||||||
.plyr__time,
|
.plyr__time,
|
||||||
.plyr__menu {
|
.plyr__volume {
|
||||||
margin-left: $plyr-control-spacing;
|
margin-left: $plyr-control-spacing;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .plyr__control + .plyr__control,
|
|
||||||
.plyr__menu + .plyr__control,
|
|
||||||
> .plyr__control + .plyr__menu {
|
|
||||||
margin-left: ($plyr-control-spacing / 2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,12 +71,15 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
color: $plyr-video-control-color;
|
color: $plyr-video-control-color;
|
||||||
left: 0;
|
left: 0;
|
||||||
padding: ($plyr-control-spacing * 3.5) $plyr-control-spacing
|
padding: ($plyr-control-spacing * 2) ($plyr-control-spacing / 2) ($plyr-control-spacing / 2);
|
||||||
$plyr-control-spacing;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
transition: opacity 0.4s ease-in-out, transform 0.4s ease-in-out;
|
transition: opacity 0.4s ease-in-out, transform 0.4s ease-in-out;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
|
|
||||||
|
@media (min-width: $plyr-bp-sm) {
|
||||||
|
padding: ($plyr-control-spacing * 3.5) $plyr-control-spacing $plyr-control-spacing;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide video controls
|
// Hide video controls
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
color: $plyr-tooltip-color;
|
color: $plyr-tooltip-color;
|
||||||
font-size: $plyr-font-size-small;
|
font-size: $plyr-font-size-small;
|
||||||
font-weight: $plyr-font-weight-regular;
|
font-weight: $plyr-font-weight-regular;
|
||||||
|
left: 50%;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
margin-bottom: ($plyr-tooltip-padding * 2);
|
margin-bottom: ($plyr-tooltip-padding * 2);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -64,6 +65,7 @@
|
|||||||
|
|
||||||
// Last tooltip
|
// Last tooltip
|
||||||
.plyr__controls > .plyr__control:last-child .plyr__tooltip {
|
.plyr__controls > .plyr__control:last-child .plyr__tooltip {
|
||||||
|
left: auto;
|
||||||
right: 0;
|
right: 0;
|
||||||
transform: translate(0, 10px) scale(0.8);
|
transform: translate(0, 10px) scale(0.8);
|
||||||
transform-origin: 100% 100%;
|
transform-origin: 100% 100%;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
|
|
||||||
.plyr--video {
|
.plyr--video {
|
||||||
|
background: #000;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
// Menu open
|
// Menu open
|
||||||
|
|||||||
@@ -3,20 +3,23 @@
|
|||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
|
|
||||||
.plyr__volume {
|
.plyr__volume {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
input[type='range'] {
|
input[type='range'] {
|
||||||
|
margin-left: ($plyr-control-spacing / 2);
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: $plyr-bp-sm) {
|
@media (min-width: $plyr-bp-sm) {
|
||||||
max-width: 50px;
|
max-width: 90px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: $plyr-bp-md) {
|
@media (min-width: $plyr-bp-md) {
|
||||||
max-width: 80px;
|
max-width: 110px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
$plyr-control-icon-size: 18px !default;
|
$plyr-control-icon-size: 18px !default;
|
||||||
$plyr-control-icon-size-large: 20px !default;
|
|
||||||
$plyr-control-spacing: 10px !default;
|
$plyr-control-spacing: 10px !default;
|
||||||
$plyr-control-padding: ($plyr-control-spacing * 0.7) !default;
|
$plyr-control-padding: ($plyr-control-spacing * 0.7) !default;
|
||||||
$plyr-control-radius: 3px !default;
|
$plyr-control-radius: 3px !default;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ $plyr-range-thumb-border: 2px solid transparent !default;
|
|||||||
$plyr-range-thumb-shadow: 0 1px 1px rgba(#000, 0.15), 0 0 0 1px rgba($plyr-color-gunmetal, 0.2) !default;
|
$plyr-range-thumb-shadow: 0 1px 1px rgba(#000, 0.15), 0 0 0 1px rgba($plyr-color-gunmetal, 0.2) !default;
|
||||||
|
|
||||||
// Track
|
// Track
|
||||||
$plyr-range-track-height: 6px !default;
|
$plyr-range-track-height: 4px !default;
|
||||||
$plyr-range-max-height: ($plyr-range-thumb-active-shadow-width * 2) + $plyr-range-thumb-height !default;
|
$plyr-range-max-height: ($plyr-range-thumb-active-shadow-width * 2) + $plyr-range-thumb-height !default;
|
||||||
|
|
||||||
// Fill
|
// Fill
|
||||||
|
|||||||
@@ -17,4 +17,4 @@ $plyr-font-weight-bold: 600 !default;
|
|||||||
|
|
||||||
$plyr-line-height: 1.7 !default;
|
$plyr-line-height: 1.7 !default;
|
||||||
|
|
||||||
$plyr-font-smoothing: true !default;
|
$plyr-font-smoothing: false !default;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<g>
|
<g>
|
||||||
<path d="M16,1 L2,1 C1.447,1 1,1.447 1,2 L1,12 C1,12.553 1.447,13 2,13 L5,13 L5,11 L3,11 L3,3 L15,3 L15,11 L13,11 L13,13 L16,13 C16.553,13 17,12.553 17,12 L17,2 C17,1.447 16.553,1 16,1 L16,1 Z"></path>
|
<path d="M16,1 L2,1 C1.447,1 1,1.447 1,2 L1,12 C1,12.553 1.447,13 2,13 L5,13 L5,11 L3,11 L3,3 L15,3 L15,11 L13,11 L13,13 L16,13 C16.553,13 17,12.553 17,12 L17,2 C17,1.447 16.553,1 16,1 L16,1 Z"></path>
|
||||||
<polygon points="4 17 14 17 9 11"></polygon>
|
<polygon points="4 17 14 17 9 11"></polygon>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 374 B |
@@ -1,6 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
|
|
||||||
<g fill-rule="evenodd" fill-opacity="0.5">
|
<g fill-rule="evenodd" fill-opacity="0.5">
|
||||||
<path d="M1,1 C0.4,1 0,1.4 0,2 L0,13 C0,13.6 0.4,14 1,14 L5.6,14 L8.3,16.7 C8.5,16.9 8.7,17 9,17 C9.3,17 9.5,16.9 9.7,16.7 L12.4,14 L17,14 C17.6,14 18,13.6 18,13 L18,2 C18,1.4 17.6,1 17,1 L1,1 Z M5.52,11.15 C7.51,11.15 8.53,9.83 8.8,8.74 L7.51,8.35 C7.32,9.01 6.73,9.8 5.52,9.8 C4.38,9.8 3.32,8.97 3.32,7.46 C3.32,5.85 4.44,5.09 5.5,5.09 C6.73,5.09 7.28,5.84 7.45,6.52 L8.75,6.11 C8.47,4.96 7.46,3.76 5.5,3.76 C3.6,3.76 1.89,5.2 1.89,7.46 C1.89,9.72 3.54,11.15 5.52,11.15 Z M13.09,11.15 C15.08,11.15 16.1,9.83 16.37,8.74 L15.08,8.35 C14.89,9.01 14.3,9.8 13.09,9.8 C11.95,9.8 10.89,8.97 10.89,7.46 C10.89,5.85 12.01,5.09 13.07,5.09 C14.3,5.09 14.85,5.84 15.02,6.52 L16.32,6.11 C16.04,4.96 15.03,3.76 13.07,3.76 C11.17,3.76 9.46,5.2 9.46,7.46 C9.46,9.72 11.11,11.15 13.09,11.15 Z"></path>
|
<path d="M1,1 C0.4,1 0,1.4 0,2 L0,13 C0,13.6 0.4,14 1,14 L5.6,14 L8.3,16.7 C8.5,16.9 8.7,17 9,17 C9.3,17 9.5,16.9 9.7,16.7 L12.4,14 L17,14 C17.6,14 18,13.6 18,13 L18,2 C18,1.4 17.6,1 17,1 L1,1 Z M5.52,11.15 C7.51,11.15 8.53,9.83 8.8,8.74 L7.51,8.35 C7.32,9.01 6.73,9.8 5.52,9.8 C4.38,9.8 3.32,8.97 3.32,7.46 C3.32,5.85 4.44,5.09 5.5,5.09 C6.73,5.09 7.28,5.84 7.45,6.52 L8.75,6.11 C8.47,4.96 7.46,3.76 5.5,3.76 C3.6,3.76 1.89,5.2 1.89,7.46 C1.89,9.72 3.54,11.15 5.52,11.15 Z M13.09,11.15 C15.08,11.15 16.1,9.83 16.37,8.74 L15.08,8.35 C14.89,9.01 14.3,9.8 13.09,9.8 C11.95,9.8 10.89,8.97 10.89,7.46 C10.89,5.85 12.01,5.09 13.07,5.09 C14.3,5.09 14.85,5.84 15.02,6.52 L16.32,6.11 C16.04,4.96 15.03,3.76 13.07,3.76 C11.17,3.76 9.46,5.2 9.46,7.46 C9.46,9.72 11.11,11.15 13.09,11.15 Z"></path>
|
||||||
</g>
|
</g>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 945 B |
@@ -1,6 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
|
|
||||||
<g fill-rule="evenodd">
|
<g fill-rule="evenodd">
|
||||||
<path d="M1,1 C0.4,1 0,1.4 0,2 L0,13 C0,13.6 0.4,14 1,14 L5.6,14 L8.3,16.7 C8.5,16.9 8.7,17 9,17 C9.3,17 9.5,16.9 9.7,16.7 L12.4,14 L17,14 C17.6,14 18,13.6 18,13 L18,2 C18,1.4 17.6,1 17,1 L1,1 Z M5.52,11.15 C7.51,11.15 8.53,9.83 8.8,8.74 L7.51,8.35 C7.32,9.01 6.73,9.8 5.52,9.8 C4.38,9.8 3.32,8.97 3.32,7.46 C3.32,5.85 4.44,5.09 5.5,5.09 C6.73,5.09 7.28,5.84 7.45,6.52 L8.75,6.11 C8.47,4.96 7.46,3.76 5.5,3.76 C3.6,3.76 1.89,5.2 1.89,7.46 C1.89,9.72 3.54,11.15 5.52,11.15 Z M13.09,11.15 C15.08,11.15 16.1,9.83 16.37,8.74 L15.08,8.35 C14.89,9.01 14.3,9.8 13.09,9.8 C11.95,9.8 10.89,8.97 10.89,7.46 C10.89,5.85 12.01,5.09 13.07,5.09 C14.3,5.09 14.85,5.84 15.02,6.52 L16.32,6.11 C16.04,4.96 15.03,3.76 13.07,3.76 C11.17,3.76 9.46,5.2 9.46,7.46 C9.46,9.72 11.11,11.15 13.09,11.15 Z"></path>
|
<path d="M1,1 C0.4,1 0,1.4 0,2 L0,13 C0,13.6 0.4,14 1,14 L5.6,14 L8.3,16.7 C8.5,16.9 8.7,17 9,17 C9.3,17 9.5,16.9 9.7,16.7 L12.4,14 L17,14 C17.6,14 18,13.6 18,13 L18,2 C18,1.4 17.6,1 17,1 L1,1 Z M5.52,11.15 C7.51,11.15 8.53,9.83 8.8,8.74 L7.51,8.35 C7.32,9.01 6.73,9.8 5.52,9.8 C4.38,9.8 3.32,8.97 3.32,7.46 C3.32,5.85 4.44,5.09 5.5,5.09 C6.73,5.09 7.28,5.84 7.45,6.52 L8.75,6.11 C8.47,4.96 7.46,3.76 5.5,3.76 C3.6,3.76 1.89,5.2 1.89,7.46 C1.89,9.72 3.54,11.15 5.52,11.15 Z M13.09,11.15 C15.08,11.15 16.1,9.83 16.37,8.74 L15.08,8.35 C14.89,9.01 14.3,9.8 13.09,9.8 C11.95,9.8 10.89,8.97 10.89,7.46 C10.89,5.85 12.01,5.09 13.07,5.09 C14.3,5.09 14.85,5.84 15.02,6.52 L16.32,6.11 C16.04,4.96 15.03,3.76 13.07,3.76 C11.17,3.76 9.46,5.2 9.46,7.46 C9.46,9.72 11.11,11.15 13.09,11.15 Z"></path>
|
||||||
</g>
|
</g>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 926 B |
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="translate(2 1)">
|
||||||
|
<path d="M7,12 C7.3,12 7.5,11.9 7.7,11.7 L13.4,6 L12,4.6 L8,8.6 L8,0 L6,0 L6,8.6 L2,4.6 L0.6,6 L6.3,11.7 C6.5,11.9 6.7,12 7,12 Z" />
|
||||||
|
<rect width="14" height="2" y="14" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 325 B |
@@ -1,7 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<polygon points="10 3 13.6 3 9.6 7 11 8.4 15 4.4 15 8 17 8 17 1 10 1"></polygon>
|
||||||
<g>
|
<polygon points="7 9.6 3 13.6 3 10 1 10 1 17 8 17 8 15 4.4 15 8.4 11"></polygon>
|
||||||
<polygon points="10 3 13.6 3 9.6 7 11 8.4 15 4.4 15 8 17 8 17 1 10 1"></polygon>
|
|
||||||
<polygon points="7 9.6 3 13.6 3 10 1 10 1 17 8 17 8 15 4.4 15 8.4 11"></polygon>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 401 B After Width: | Height: | Size: 264 B |
@@ -1,7 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<polygon points="1 12 4.6 12 0.6 16 2 17.4 6 13.4 6 17 8 17 8 10 1 10"></polygon>
|
||||||
<g>
|
<polygon points="16 0.6 12 4.6 12 1 10 1 10 8 17 8 17 6 13.4 6 17.4 2"></polygon>
|
||||||
<polygon points="1 12 4.6 12 0.6 16 2 17.4 6 13.4 6 17 8 17 8 10 1 10"></polygon>
|
|
||||||
<polygon points="16 0.6 12 4.6 12 1 10 1 10 8 17 8 17 6 13.4 6 17.4 2"></polygon>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 266 B |
@@ -1,6 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<polygon points="7.875 7.17142857 0 1 0 17 7.875 10.8285714 7.875 17 18 9 7.875 1"></polygon>
|
||||||
<g>
|
|
||||||
<polygon points="7.875 7.17142857 0 1 0 17 7.875 10.8285714 7.875 17 18 9 7.875 1"></polygon>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 325 B After Width: | Height: | Size: 192 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||||
|
<path d="M16,3.3 C15.9,4.9 14.8,7 12.7,9.7 C10.5,12.5 8.7,13.9 7.2,13.9 C6.3,13.9 5.5,13 4.8,11.3 C4,8.9 3.4,4 2,4 C1.9,4 1.5,4.3 0.8,4.8 L0,3.8 C0.8,3.1 3.5,0.4 4.7,0.3 C5.9,0.2 6.7,1 7,2.8 C7.3,4.8 7.8,8.9 8.8,8.9 C9.7,8.9 11.3,5.5 11.4,4.9 C11.5,4 11.1,3 9.1,3.8 C9.9,1.2 11.4,-8.8817842e-16 13.6,-8.8817842e-16 C15.3,0.1 16.1,1.2 16,3.3 Z"
|
||||||
|
transform="translate(1 2)" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 470 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18">
|
||||||
|
<path d="M15.8,2.8 C15.6,1.5 15,0.6 13.6,0.4 C11.4,0 8,0 8,0 C8,0 4.6,0 2.4,0.4 C1,0.6 0.3,1.5 0.2,2.8 C0,4.1 0,6 0,6 C0,6 0,7.9 0.2,9.2 C0.4,10.5 1,11.4 2.4,11.6 C4.6,12 8,12 8,12 C8,12 11.4,12 13.6,11.6 C15,11.3 15.6,10.5 15.8,9.2 C16,7.9 16,6 16,6 C16,6 16,4.1 15.8,2.8 Z M6,9 L6,3 L11,6 L6,9 Z"
|
||||||
|
transform="translate(1 3)" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 425 B |
@@ -1,7 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<polygon points="12.4 12.5 14.5 10.4 16.6 12.5 18 11.1 15.9 9 18 6.9 16.6 5.5 14.5 7.6 12.4 5.5 11 6.9 13.1 9 11 11.1"></polygon>
|
||||||
<g>
|
<path d="M3.78571429,6.00820648 L0.714285714,6.00820648 C0.285714286,6.00820648 0,6.30901277 0,6.76022222 L0,11.2723167 C0,11.7235261 0.285714286,12.0243324 0.714285714,12.0243324 L3.78571429,12.0243324 L7.85714286,15.8819922 C8.35714286,16.1827985 9,15.8819922 9,15.2803796 L9,2.75215925 C9,2.15054666 8.35714286,1.77453879 7.85714286,2.15054666 L3.78571429,6.00820648 Z"></path>
|
||||||
<polygon points="12.4 12.5 14.5 10.4 16.6 12.5 18 11.1 15.9 9 18 6.9 16.6 5.5 14.5 7.6 12.4 5.5 11 6.9 13.1 9 11 11.1"></polygon>
|
|
||||||
<path d="M3.78571429,6.00820648 L0.714285714,6.00820648 C0.285714286,6.00820648 0,6.30901277 0,6.76022222 L0,11.2723167 C0,11.7235261 0.285714286,12.0243324 0.714285714,12.0243324 L3.78571429,12.0243324 L7.85714286,15.8819922 C8.35714286,16.1827985 9,15.8819922 9,15.2803796 L9,2.75215925 C9,2.15054666 8.35714286,1.77453879 7.85714286,2.15054666 L3.78571429,6.00820648 Z"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 613 B |
@@ -1,7 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<path d="M6,1 L3,1 C2.4,1 2,1.4 2,2 L2,16 C2,16.6 2.4,17 3,17 L6,17 C6.6,17 7,16.6 7,16 L7,2 C7,1.4 6.6,1 6,1 L6,1 Z"></path>
|
||||||
<g>
|
<path d="M12,1 C11.4,1 11,1.4 11,2 L11,16 C11,16.6 11.4,17 12,17 L15,17 C15.6,17 16,16.6 16,16 L16,2 C16,1.4 15.6,1 15,1 L12,1 Z"></path>
|
||||||
<path d="M6,1 L3,1 C2.4,1 2,1.4 2,2 L2,16 C2,16.6 2.4,17 3,17 L6,17 C6.6,17 7,16.6 7,16 L7,2 C7,1.4 6.6,1 6,1 L6,1 Z"></path>
|
|
||||||
<path d="M12,1 C11.4,1 11,1.4 11,2 L11,16 C11,16.6 11.4,17 12,17 L15,17 C15.6,17 16,16.6 16,16 L16,2 C16,1.4 15.6,1 15,1 L12,1 Z"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 366 B |
@@ -1,7 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<polygon points="13.293 3.293 7.022 9.564 8.436 10.978 14.707 4.707 17 7 17 1 11 1"></polygon>
|
||||||
<g>
|
<path d="M13,15 L3,15 L3,5 L8,5 L8,3 L2,3 C1.448,3 1,3.448 1,4 L1,16 C1,16.552 1.448,17 2,17 L14,17 C14.552,17 15,16.552 15,16 L15,10 L13,10 L13,15 L13,15 Z"></path>
|
||||||
<polygon points="13.293 3.293 7.022 9.564 8.436 10.978 14.707 4.707 17 7 17 1 11 1"></polygon>
|
|
||||||
<path d="M13,15 L3,15 L3,5 L8,5 L8,3 L2,3 C1.448,3 1,3.448 1,4 L1,16 C1,16.552 1.448,17 2,17 L14,17 C14.552,17 15,16.552 15,16 L15,10 L13,10 L13,15 L13,15 Z"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 500 B After Width: | Height: | Size: 363 B |
@@ -1,6 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<path d="M15.5615866,8.10002147 L3.87056367,0.225209313 C3.05219207,-0.33727727 2,0.225209313 2,1.12518784 L2,16.8748122 C2,17.7747907 3.05219207,18.3372773 3.87056367,17.7747907 L15.5615866,9.89997853 C16.1461378,9.44998927 16.1461378,8.55001073 15.5615866,8.10002147 L15.5615866,8.10002147 Z"></path>
|
||||||
<g>
|
|
||||||
<path d="M15.5615866,8.10002147 L3.87056367,0.225209313 C3.05219207,-0.33727727 2,0.225209313 2,1.12518784 L2,16.8748122 C2,17.7747907 3.05219207,18.3372773 3.87056367,17.7747907 L15.5615866,9.89997853 C16.1461378,9.44998927 16.1461378,8.55001073 15.5615866,8.10002147 L15.5615866,8.10002147 Z"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 534 B After Width: | Height: | Size: 401 B |
@@ -1,6 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<path d="M9.7,1.2 L10.4,7.6 L12.5,5.5 C14.4,7.4 14.4,10.6 12.5,12.5 C11.6,13.5 10.3,14 9,14 C7.7,14 6.4,13.5 5.5,12.5 C3.6,10.6 3.6,7.4 5.5,5.5 C6.1,4.9 6.9,4.4 7.8,4.2 L7.2,2.3 C6,2.6 4.9,3.2 4,4.1 C1.3,6.8 1.3,11.2 4,14 C5.3,15.3 7.1,16 8.9,16 C10.8,16 12.5,15.3 13.8,14 C16.5,11.3 16.5,6.9 13.8,4.1 L16,1.9 L9.7,1.2 L9.7,1.2 Z"></path>
|
||||||
<g>
|
|
||||||
<path d="M9.7,1.2 L10.4,7.6 L12.5,5.5 C14.4,7.4 14.4,10.6 12.5,12.5 C11.6,13.5 10.3,14 9,14 C7.7,14 6.4,13.5 5.5,12.5 C3.6,10.6 3.6,7.4 5.5,5.5 C6.1,4.9 6.9,4.4 7.8,4.2 L7.2,2.3 C6,2.6 4.9,3.2 4,4.1 C1.3,6.8 1.3,11.2 4,14 C5.3,15.3 7.1,16 8.9,16 C10.8,16 12.5,15.3 13.8,14 C16.5,11.3 16.5,6.9 13.8,4.1 L16,1.9 L9.7,1.2 L9.7,1.2 Z"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 570 B After Width: | Height: | Size: 437 B |
@@ -1,6 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<polygon points="10.125 1 0 9 10.125 17 10.125 10.8285714 18 17 18 1 10.125 7.17142857"></polygon>
|
||||||
<g>
|
|
||||||
<polygon points="10.125 1 0 9 10.125 17 10.125 10.8285714 18 17 18 1 10.125 7.17142857"></polygon>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 330 B After Width: | Height: | Size: 197 B |
@@ -1,6 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<path d="M16.135,7.784 C14.832,7.458 14.214,5.966 14.905,4.815 C15.227,4.279 15.13,3.817 14.811,3.499 L14.501,3.189 C14.183,2.871 13.721,2.774 13.185,3.095 C12.033,3.786 10.541,3.168 10.216,1.865 C10.065,1.258 9.669,1 9.219,1 L8.781,1 C8.331,1 7.936,1.258 7.784,1.865 C7.458,3.168 5.966,3.786 4.815,3.095 C4.279,2.773 3.816,2.87 3.498,3.188 L3.188,3.498 C2.87,3.816 2.773,4.279 3.095,4.815 C3.786,5.967 3.168,7.459 1.865,7.784 C1.26,7.935 1,8.33 1,8.781 L1,9.219 C1,9.669 1.258,10.064 1.865,10.216 C3.168,10.542 3.786,12.034 3.095,13.185 C2.773,13.721 2.87,14.183 3.189,14.501 L3.499,14.811 C3.818,15.13 4.281,15.226 4.815,14.905 C5.967,14.214 7.459,14.832 7.784,16.135 C7.935,16.742 8.331,17 8.781,17 L9.219,17 C9.669,17 10.064,16.742 10.216,16.135 C10.542,14.832 12.034,14.214 13.185,14.905 C13.72,15.226 14.182,15.13 14.501,14.811 L14.811,14.501 C15.129,14.183 15.226,13.72 14.905,13.185 C14.214,12.033 14.832,10.541 16.135,10.216 C16.742,10.065 17,9.669 17,9.219 L17,8.781 C17,8.33 16.74,7.935 16.135,7.784 L16.135,7.784 Z M9,12 C7.343,12 6,10.657 6,9 C6,7.343 7.343,6 9,6 C10.657,6 12,7.343 12,9 C12,10.657 10.657,12 9,12 L9,12 Z"></path>
|
||||||
<g>
|
|
||||||
<path d="M16.135,7.784 C14.832,7.458 14.214,5.966 14.905,4.815 C15.227,4.279 15.13,3.817 14.811,3.499 L14.501,3.189 C14.183,2.871 13.721,2.774 13.185,3.095 C12.033,3.786 10.541,3.168 10.216,1.865 C10.065,1.258 9.669,1 9.219,1 L8.781,1 C8.331,1 7.936,1.258 7.784,1.865 C7.458,3.168 5.966,3.786 4.815,3.095 C4.279,2.773 3.816,2.87 3.498,3.188 L3.188,3.498 C2.87,3.816 2.773,4.279 3.095,4.815 C3.786,5.967 3.168,7.459 1.865,7.784 C1.26,7.935 1,8.33 1,8.781 L1,9.219 C1,9.669 1.258,10.064 1.865,10.216 C3.168,10.542 3.786,12.034 3.095,13.185 C2.773,13.721 2.87,14.183 3.189,14.501 L3.499,14.811 C3.818,15.13 4.281,15.226 4.815,14.905 C5.967,14.214 7.459,14.832 7.784,16.135 C7.935,16.742 8.331,17 8.781,17 L9.219,17 C9.669,17 10.064,16.742 10.216,16.135 C10.542,14.832 12.034,14.214 13.185,14.905 C13.72,15.226 14.182,15.13 14.501,14.811 L14.811,14.501 C15.129,14.183 15.226,13.72 14.905,13.185 C14.214,12.033 14.832,10.541 16.135,10.216 C16.742,10.065 17,9.669 17,9.219 L17,8.781 C17,8.33 16.74,7.935 16.135,7.784 L16.135,7.784 Z M9,12 C7.343,12 6,10.657 6,9 C6,7.343 7.343,6 9,6 C10.657,6 12,7.343 12,9 C12,10.657 10.657,12 9,12 L9,12 Z"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -1,8 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<svg width="18px" height="18px" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
<path d="M15.5999996,3.3 C15.1999996,2.9 14.5999996,2.9 14.1999996,3.3 C13.7999996,3.7 13.7999996,4.3 14.1999996,4.7 C15.3999996,5.9 15.9999996,7.4 15.9999996,9 C15.9999996,10.6 15.3999996,12.1 14.1999996,13.3 C13.7999996,13.7 13.7999996,14.3 14.1999996,14.7 C14.3999996,14.9 14.6999996,15 14.8999996,15 C15.1999996,15 15.3999996,14.9 15.5999996,14.7 C17.0999996,13.2 17.9999996,11.2 17.9999996,9 C17.9999996,6.8 17.0999996,4.8 15.5999996,3.3 L15.5999996,3.3 Z"></path>
|
||||||
<g>
|
<path d="M11.2819745,5.28197449 C10.9060085,5.65794047 10.9060085,6.22188944 11.2819745,6.59785542 C12.0171538,7.33303477 12.2772954,8.05605449 12.2772954,9.00000021 C12.2772954,9.93588462 11.851678,10.9172014 11.2819745,11.4869049 C10.9060085,11.8628709 10.9060085,12.4268199 11.2819745,12.8027859 C11.4271642,12.9479755 11.9176724,13.0649528 12.2998149,12.9592565 C12.4124479,12.9281035 12.5156669,12.8776063 12.5978555,12.8027859 C13.773371,11.732654 14.1311161,10.1597914 14.1312523,9.00000021 C14.1312723,8.8299555 14.1286311,8.66015647 14.119665,8.4897429 C14.0674781,7.49784946 13.8010171,6.48513613 12.5978554,5.28197449 C12.2218894,4.9060085 11.6579405,4.9060085 11.2819745,5.28197449 Z"></path>
|
||||||
<path d="M15.5999996,3.3 C15.1999996,2.9 14.5999996,2.9 14.1999996,3.3 C13.7999996,3.7 13.7999996,4.3 14.1999996,4.7 C15.3999996,5.9 15.9999996,7.4 15.9999996,9 C15.9999996,10.6 15.3999996,12.1 14.1999996,13.3 C13.7999996,13.7 13.7999996,14.3 14.1999996,14.7 C14.3999996,14.9 14.6999996,15 14.8999996,15 C15.1999996,15 15.3999996,14.9 15.5999996,14.7 C17.0999996,13.2 17.9999996,11.2 17.9999996,9 C17.9999996,6.8 17.0999996,4.8 15.5999996,3.3 L15.5999996,3.3 Z"></path>
|
<path d="M3.78571429,6.00820648 L0.714285714,6.00820648 C0.285714286,6.00820648 0,6.30901277 0,6.76022222 L0,11.2723167 C0,11.7235261 0.285714286,12.0243324 0.714285714,12.0243324 L3.78571429,12.0243324 L7.85714286,15.8819922 C8.35714286,16.1827985 9,15.8819922 9,15.2803796 L9,2.75215925 C9,2.15054666 8.35714286,1.77453879 7.85714286,2.15054666 L3.78571429,6.00820648 Z"></path>
|
||||||
<path d="M11.2819745,5.28197449 C10.9060085,5.65794047 10.9060085,6.22188944 11.2819745,6.59785542 C12.0171538,7.33303477 12.2772954,8.05605449 12.2772954,9.00000021 C12.2772954,9.93588462 11.851678,10.9172014 11.2819745,11.4869049 C10.9060085,11.8628709 10.9060085,12.4268199 11.2819745,12.8027859 C11.4271642,12.9479755 11.9176724,13.0649528 12.2998149,12.9592565 C12.4124479,12.9281035 12.5156669,12.8776063 12.5978555,12.8027859 C13.773371,11.732654 14.1311161,10.1597914 14.1312523,9.00000021 C14.1312723,8.8299555 14.1286311,8.66015647 14.119665,8.4897429 C14.0674781,7.49784946 13.8010171,6.48513613 12.5978554,5.28197449 C12.2218894,4.9060085 11.6579405,4.9060085 11.2819745,5.28197449 Z"></path>
|
|
||||||
<path d="M3.78571429,6.00820648 L0.714285714,6.00820648 C0.285714286,6.00820648 0,6.30901277 0,6.76022222 L0,11.2723167 C0,11.7235261 0.285714286,12.0243324 0.714285714,12.0243324 L3.78571429,12.0243324 L7.85714286,15.8819922 C8.35714286,16.1827985 9,15.8819922 9,15.2803796 L9,2.75215925 C9,2.15054666 8.35714286,1.77453879 7.85714286,2.15054666 L3.78571429,6.00820648 Z"></path>
|
|
||||||
</g>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.6 KiB |