Compare commits
33 Commits
v3.0.0-bet
...
v3.0.0-bet
Author | SHA1 | Date | |
---|---|---|---|
f1895a4cce | |||
c2a6306d46 | |||
7ac732f45b | |||
c90f1bdf08 | |||
6a9be8d16b | |||
58c2c52c95 | |||
73a39769d4 | |||
7221e26eca | |||
98adb8e784 | |||
a59dcb2f53 | |||
d21b58e1c9 | |||
d6e84cbabb | |||
fcccf1d479 | |||
211db12a3d | |||
ce1d5a60d6 | |||
f67315e20c | |||
2150c44036 | |||
70c9fbdde3 | |||
f3ea31c515 | |||
1ee88cba16 | |||
af3ae75229 | |||
d76ef3ff91 | |||
2691c7c9d6 | |||
26b1d8ce8f | |||
6fae148fc1 | |||
bb51647fe2 | |||
71efbe7a92 | |||
afd695cb39 | |||
c4eb4c97ac | |||
8f7a8940f3 | |||
5e68f8c8dd | |||
b4e22e2e7b | |||
d4234da9aa |
@ -8,6 +8,7 @@
|
||||
"globals": { "Plyr": false, "jQuery": false },
|
||||
"rules": {
|
||||
"no-const-assign": 1,
|
||||
"no-shadow": 0,
|
||||
"no-this-before-super": 1,
|
||||
"no-undef": 1,
|
||||
"no-unreachable": 1,
|
||||
|
4
.npmignore
Normal file
4
.npmignore
Normal file
@ -0,0 +1,4 @@
|
||||
demo
|
||||
.github
|
||||
.vscode
|
||||
*.code-workspace
|
@ -4,7 +4,8 @@
|
||||
"plyr.css": "src/sass/plyr.scss"
|
||||
},
|
||||
"js": {
|
||||
"plyr.js": "src/js/plyr.js"
|
||||
"plyr.js": "src/js/plyr.js",
|
||||
"plyr.polyfilled.js": "src/js/plyr.polyfilled.js"
|
||||
}
|
||||
},
|
||||
"demo": {
|
||||
|
@ -17,8 +17,8 @@ This is a massive release. A _mostly_ complete rewrite in ES6. What started out
|
||||
|
||||
### Other stuff
|
||||
|
||||
* Now using SASS exclusively. Sorry, LESS folk it just made sense to maintain one method as SASS is what the cool kids use
|
||||
* Moved to ES6. All the rage these days
|
||||
* Now using SASS exclusively. Sorry, LESS folk it just made sense to maintain one method as SASS is what the cool kids use. It may come back if we work out an automated way to convert the SASS
|
||||
* Moved to ES6. All the rage these days. You'll need to look at polyfills. The demo uses [polyfill.io](https://polyfill.io)
|
||||
* Added basic looping support
|
||||
* Added an aspect ratio option for those that can't leave the 90s and want 4:3
|
||||
* `controlshidden` and `controlsshown` events added for when the controls show or hide
|
||||
@ -44,6 +44,7 @@ You gotta break eggs to make an omelette. Sadly, there's quite a few breaking ch
|
||||
|
||||
* Setup now uses proper constructor, accepts a single selector/element/node and returns a single instance - much simpler than before
|
||||
* Much of the API is now using getters and setters rather than methods (where it makes sense) to match the HTML5 API - see the docs for more info
|
||||
* The data attributes for the embeds are now `data-plyr-provider` and `data-plyr-embed-id` to prevent compatibility issues. These can be changed under `config.attributes.embed` if required
|
||||
* `blankUrl` -> `blankVideo`
|
||||
* `volume` is now `0` to `1` as per HTML5 spec
|
||||
* `keyboardShorcuts` (typo) is now just `keyboard`
|
||||
@ -66,7 +67,7 @@ You gotta break eggs to make an omelette. Sadly, there's quite a few breaking ch
|
||||
|
||||
### Polyfilling
|
||||
|
||||
Because we're using the fancy new ES6 syntax, you will need to polyfill for vintage browsers if you want to use Plyr and still support them. Luckily there's a decent service for this that makes it painless, [https://polyfill.io](polyfill.io).
|
||||
Because we're using the fancy new ES6 syntax, you will need to polyfill for vintage browsers if you want to use Plyr and still support them. Luckily there's a decent service for this that makes it painless, [polyfill.io](https://polyfill.io).
|
||||
|
||||
## v2.0.18
|
||||
|
||||
|
2
demo/dist/demo.css
vendored
2
demo/dist/demo.css
vendored
File diff suppressed because one or more lines are too long
241
demo/dist/demo.js
vendored
241
demo/dist/demo.js
vendored
@ -1,3 +1,242 @@
|
||||
!function(){"use strict";var e,t,o,i,a,r;document.addEventListener("DOMContentLoaded",function(){window.shr&&window.shr.setup({count:{classname:"button__count"}});document.addEventListener("focusout",function(e){e.target.classList.remove("tab-focus")}),document.addEventListener("keydown",function(e){9===e.keyCode&&window.setTimeout(function(){document.activeElement.classList.add("tab-focus")},0)});var e=new window.Plyr("#player",{debug:!0,title:"View From A Blue Moon",iconUrl:"../dist/plyr.svg",keyboard:{global:!0},tooltips:{controls:!0},captions:{active:!0},keys:{google:"AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c"},ads:{tag:"https://go.aniview.com/api/adserver6/vast/?AV_PUBLISHERID=58c25bb0073ef448b1087ad6&AV_CHANNELID=5a0458dc28a06145e4519d21&AV_URL=127.0.0.1:3000&cb=1&AV_WIDTH=640&AV_HEIGHT=480"}});window.player=e;var t=document.querySelectorAll("[data-source]"),o={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},i=window.location.hash.replace("#",""),a=window.history&&window.history.pushState;function r(e,t,o){e&&e.classList[o?"add":"remove"](t)}function n(a,n){if(a in o&&(n||a!==i)&&(i.length||a!==o.video)){switch(a){case o.video:e.source={type:"video",title:"View From A Blue Moon",sources:[{src:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"}],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:!0},{kind:"captions",label:"French",srclang:"fr",src:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt"}]};break;case o.audio:e.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 o.youtube:e.source={type:"video",title:"View From A Blue Moon",sources:[{src:"https://youtube.com/watch?v=bTqVqk7FSmY",provider:"youtube"}]};break;case o.vimeo:e.source={type:"video",sources:[{src:"https://vimeo.com/76979871",provider:"vimeo"}]}}i=a,Array.from(t).forEach(function(e){return r(e.parentElement,"active",!1)}),r(document.querySelector('[data-source="'+a+'"]'),"active",!0),Array.from(document.querySelectorAll(".plyr__cite")).forEach(function(e){e.setAttribute("hidden","")}),document.querySelector(".plyr__cite--"+a).removeAttribute("hidden")}}if(Array.from(t).forEach(function(e){e.addEventListener("click",function(){var t=e.getAttribute("data-source");n(t),a&&window.history.pushState({type:t},"","#"+t)})}),window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&n(e.state.type)}),a){var s=!i.length;s&&(i=o.video),i in o&&window.history.replaceState({type:i},"",s?"":"#"+i),i!==o.video&&n(i,!0)}}),"plyr.io"===window.location.host&&(e=window,t=document,o="script",i="ga",e.GoogleAnalyticsObject=i,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,a=t.createElement(o),r=t.getElementsByTagName(o)[0],a.async=1,a.src="//www.google-analytics.com/analytics.js",r.parentNode.insertBefore(a,r),window.ga("create","UA-40881672-11","auto"),window.ga("send","pageview"))}();
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// ==========================================================================
|
||||
// Plyr.io demo
|
||||
// This code is purely for the https://plyr.io website
|
||||
// Please see readme.md in the root or github.com/sampotts/plyr
|
||||
// ==========================================================================
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
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) {
|
||||
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
|
||||
window.setTimeout(function () {
|
||||
document.activeElement.classList.add(tabClassName);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
// Setup the player
|
||||
var player = new Plyr('#player', {
|
||||
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: true
|
||||
}
|
||||
});
|
||||
|
||||
// Expose for testing
|
||||
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-HD.mp4',
|
||||
type: 'video/mp4'
|
||||
}],
|
||||
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',
|
||||
title: 'View From A Blue Moon',
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Google analytics
|
||||
// For demo site (https://plyr.io) only
|
||||
/* eslint-disable */
|
||||
if (window.location.host === 'plyr.io') {
|
||||
(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', '//www.google-analytics.com/analytics.js', 'ga');
|
||||
window.ga('create', 'UA-40881672-11', 'auto');
|
||||
window.ga('send', 'pageview');
|
||||
}
|
||||
/* eslint-enable */
|
||||
|
||||
}());
|
||||
|
||||
//# sourceMappingURL=demo.js.map
|
||||
|
2
demo/dist/demo.js.map
vendored
2
demo/dist/demo.js.map
vendored
File diff suppressed because one or more lines are too long
2
demo/dist/demo.min.js
vendored
Normal file
2
demo/dist/demo.min.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
!function(){"use strict";var e,t,o,i,r,a;document.addEventListener("DOMContentLoaded",function(){window.shr&&window.shr.setup({count:{classname:"button__count"}});document.addEventListener("focusout",function(e){e.target.classList.remove("tab-focus")}),document.addEventListener("keydown",function(e){9===e.keyCode&&window.setTimeout(function(){document.activeElement.classList.add("tab-focus")},0)});var e=new Plyr("#player",{debug:!0,title:"View From A Blue Moon",keyboard:{global:!0},tooltips:{controls:!0},captions:{active:!0},keys:{google:"AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c"},ads:{enabled:!0}});window.player=e;var t=document.querySelectorAll("[data-source]"),o={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},i=window.location.hash.replace("#",""),r=window.history&&window.history.pushState;function a(e,t,o){e&&e.classList[o?"add":"remove"](t)}function n(r,n){if(r in o&&(n||r!==i)&&(i.length||r!==o.video)){switch(r){case o.video:e.source={type:"video",title:"View From A Blue Moon",sources:[{src:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"}],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:!0},{kind:"captions",label:"French",srclang:"fr",src:"https://cdn.plyr.io/static/demo/View_From_A_Blue_Moon_Trailer-HD.fr.vtt"}]};break;case o.audio:e.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 o.youtube:e.source={type:"video",title:"View From A Blue Moon",sources:[{src:"https://youtube.com/watch?v=bTqVqk7FSmY",provider:"youtube"}]};break;case o.vimeo:e.source={type:"video",sources:[{src:"https://vimeo.com/76979871",provider:"vimeo"}]}}i=r,Array.from(t).forEach(function(e){return a(e.parentElement,"active",!1)}),a(document.querySelector('[data-source="'+r+'"]'),"active",!0),Array.from(document.querySelectorAll(".plyr__cite")).forEach(function(e){e.setAttribute("hidden","")}),document.querySelector(".plyr__cite--"+r).removeAttribute("hidden")}}if(Array.from(t).forEach(function(e){e.addEventListener("click",function(){var t=e.getAttribute("data-source");n(t),r&&window.history.pushState({type:t},"","#"+t)})}),window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&n(e.state.type)}),r){var s=!i.length;s&&(i=o.video),i in o&&window.history.replaceState({type:i},"",s?"":"#"+i),i!==o.video&&n(i,!0)}}),"plyr.io"===window.location.host&&(e=window,t=document,o="script",i="ga",e.GoogleAnalyticsObject=i,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,r=t.createElement(o),a=t.getElementsByTagName(o)[0],r.async=1,r.src="//www.google-analytics.com/analytics.js",a.parentNode.insertBefore(r,a),window.ga("create","UA-40881672-11","auto"),window.ga("send","pageview"))}();
|
||||
//# sourceMappingURL=demo.min.js.map
|
1
demo/dist/demo.min.js.map
vendored
Normal file
1
demo/dist/demo.min.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -65,8 +65,11 @@
|
||||
</svg>Vimeo</button>
|
||||
</p>
|
||||
|
||||
<p>Advertisement service provided by
|
||||
<a href="https://vi.ai" target="_blank">vi.ai</a>
|
||||
<p>Premium video monitization from
|
||||
<a href="https://vi.ai/publisher-video-monetization/" target="_blank" class="no-border">
|
||||
<img src="https://cdn.plyr.io/static/vi-logo-24x24.svg" alt="ai.vi">
|
||||
<span class="sr-only">ai.vi</span>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div class="call-to-action">
|
||||
@ -166,7 +169,7 @@
|
||||
</aside>
|
||||
|
||||
<!-- Polyfills -->
|
||||
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=es6,Array.prototype.includes,CustomEvent,fetch"></script>
|
||||
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=es6,Array.prototype.includes,CustomEvent"></script>
|
||||
|
||||
<!-- Plyr core script -->
|
||||
<script src="../dist/plyr.js"></script>
|
||||
|
@ -1,183 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Plyr - A simple, customizable HTML5 Video, Audio, YouTube and Vimeo player</title>
|
||||
<meta name="description" property="og:description" content="A simple HTML5 media player with custom controls and WebVTT captions.">
|
||||
<meta name="author" content="Sam Potts">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- Icons -->
|
||||
<link rel="icon" href="https://cdn.plyr.io/static/icons/favicon.ico">
|
||||
<link rel="icon" type="image/png" href="https://cdn.plyr.io/static/icons/32x32.png" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="https://cdn.plyr.io/static/icons/16x16.png" sizes="16x16">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="https://cdn.plyr.io/static/icons/180x180.png">
|
||||
|
||||
<!-- Opengraph -->
|
||||
<meta property="og:title" content="Plyr - A simple, customizable HTML5 Video, Audio, YouTube and Vimeo player">
|
||||
<meta property="og:site_name" content="Plyr">
|
||||
<meta property="og:url" content="https://plyr.io">
|
||||
<meta property="og:image" content="https://cdn.plyr.io/static/icons/1200x630.png">
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:site" content="@sam_potts">
|
||||
<meta name="twitter:creator" content="@sam_potts">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
|
||||
<!-- Docs styles -->
|
||||
<link rel="stylesheet" href="dist/demo.css">
|
||||
|
||||
<!-- Preload -->
|
||||
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-medium.woff2">
|
||||
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-bold.woff2">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="grid">
|
||||
<header>
|
||||
<h1>Plyr</h1>
|
||||
<p>A simple, accessible and customisable media player for
|
||||
<button type="button" class="faux-link" data-source="video">
|
||||
<svg class="icon">
|
||||
<title>HTML5</title>
|
||||
<path d="M14.738.326C14.548.118 14.28 0 14 0H2c-.28 0-.55.118-.738.326S.98.81 1.004 1.09l1 11c.03.317.208.603.48.767l5 3c.16.095.338.143.516.143s.356-.048.515-.143l5-3c.273-.164.452-.45.48-.767l1-11c.026-.28-.067-.557-.257-.764zM12 4H6v2h6v5.72l-4 1.334-4-1.333V9h2v1.28l2 .666 2-.667V8H4V2h8v2z"></path>
|
||||
</svg>Video</button>,
|
||||
<button type="button" class="faux-link" data-source="audio">
|
||||
<svg class="icon">
|
||||
<title>HTML5</title>
|
||||
<path d="M14.738.326C14.548.118 14.28 0 14 0H2c-.28 0-.55.118-.738.326S.98.81 1.004 1.09l1 11c.03.317.208.603.48.767l5 3c.16.095.338.143.516.143s.356-.048.515-.143l5-3c.273-.164.452-.45.48-.767l1-11c.026-.28-.067-.557-.257-.764zM12 4H6v2h6v5.72l-4 1.334-4-1.333V9h2v1.28l2 .666 2-.667V8H4V2h8v2z"></path>
|
||||
</svg>Audio</button>,
|
||||
<button type="button" class="faux-link" data-source="youtube">
|
||||
<svg class="icon" role="presentation">
|
||||
<title>YouTube</title>
|
||||
<path d="M15.8,4.8c-0.2-1.3-0.8-2.2-2.2-2.4C11.4,2,8,2,8,2S4.6,2,2.4,2.4C1,2.6,0.3,3.5,0.2,4.8C0,6.1,0,8,0,8
|
||||
s0,1.9,0.2,3.2c0.2,1.3,0.8,2.2,2.2,2.4C4.6,14,8,14,8,14s3.4,0,5.6-0.4c1.4-0.3,2-1.1,2.2-2.4C16,9.9,16,8,16,8S16,6.1,15.8,4.8z
|
||||
M6,11V5l5,3L6,11z"></path>
|
||||
</svg>YouTube</button> and
|
||||
<button type="button" class="faux-link" data-source="vimeo">
|
||||
<svg class="icon" role="presentation">
|
||||
<title>Vimeo</title>
|
||||
<path d="M16,4.3c-0.1,1.6-1.2,3.7-3.3,6.4c-2.2,2.8-4,4.2-5.5,4.2c-0.9,0-1.7-0.9-2.4-2.6C4,9.9,3.4,5,2,5
|
||||
C1.9,5,1.5,5.3,0.8,5.8L0,4.8c0.8-0.7,3.5-3.4,4.7-3.5C5.9,1.2,6.7,2,7,3.8c0.3,2,0.8,6.1,1.8,6.1c0.9,0,2.5-3.4,2.6-4
|
||||
c0.1-0.9-0.3-1.9-2.3-1.1c0.8-2.6,2.3-3.8,4.5-3.8C15.3,1.1,16.1,2.2,16,4.3z"></path>
|
||||
</svg>Vimeo</button>
|
||||
</p>
|
||||
|
||||
<!--<p>Monetization options provided by
|
||||
<a href="https://vi.ai" target="_blank">vi.ai</a>
|
||||
</p>-->
|
||||
|
||||
<div class="call-to-action">
|
||||
<span class="button--with-count">
|
||||
<a href="https://github.com/sampotts/plyr" target="_blank" class="button" data-shr-network="github">
|
||||
<svg class="icon" role="presentation">
|
||||
<title>GitHub</title>
|
||||
<path d="M8,0.2c-4.4,0-8,3.6-8,8c0,3.5,2.3,6.5,5.5,7.6
|
||||
C5.9,15.9,6,15.6,6,15.4c0-0.2,0-0.7,0-1.4C3.8,14.5,3.3,13,3.3,13c-0.4-0.9-0.9-1.2-0.9-1.2c-0.7-0.5,0.1-0.5,0.1-0.5
|
||||
c0.8,0.1,1.2,0.8,1.2,0.8C4.4,13.4,5.6,13,6,12.8c0.1-0.5,0.3-0.9,0.5-1.1c-1.8-0.2-3.6-0.9-3.6-4c0-0.9,0.3-1.6,0.8-2.1
|
||||
c-0.1-0.2-0.4-1,0.1-2.1c0,0,0.7-0.2,2.2,0.8c0.6-0.2,1.3-0.3,2-0.3c0.7,0,1.4,0.1,2,0.3c1.5-1,2.2-0.8,2.2-0.8
|
||||
c0.4,1.1,0.2,1.9,0.1,2.1c0.5,0.6,0.8,1.3,0.8,2.1c0,3.1-1.9,3.7-3.7,3.9C9.7,12,10,12.5,10,13.2c0,1.1,0,1.9,0,2.2
|
||||
c0,0.2,0.1,0.5,0.6,0.4c3.2-1.1,5.5-4.1,5.5-7.6C16,3.8,12.4,0.2,8,0.2z"></path>
|
||||
</svg>
|
||||
Download on GitHub
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<video controls crossorigin playsinline poster="media/View_From_A_Blue_Moon_Trailer-HD.jpg" id="player">
|
||||
<!-- Video files -->
|
||||
<source src="media/View_From_A_Blue_Moon_Trailer-HD.mp4" type="video/mp4">
|
||||
<!--<source src="media/View_From_A_Blue_Moon_Trailer-UHD.mp4" type="video/mp4">-->
|
||||
|
||||
<!-- Text track file -->
|
||||
<track kind="captions" label="English" srclang="en" src="media/View_From_A_Blue_Moon_Trailer-HD.en.vtt" default>
|
||||
<track kind="captions" label="Français" srclang="fr" src="media/View_From_A_Blue_Moon_Trailer-HD.fr.vtt">
|
||||
|
||||
<!-- Fallback for browsers that don't support the <video> element -->
|
||||
<a href="media/View_From_A_Blue_Moon_Trailer-HD.mp4" download>Download</a>
|
||||
</video>
|
||||
|
||||
<ul>
|
||||
<li class="plyr__cite plyr__cite--video" hidden>
|
||||
<small>
|
||||
<svg class="icon">
|
||||
<title>HTML5</title>
|
||||
<path d="M14.738.326C14.548.118 14.28 0 14 0H2c-.28 0-.55.118-.738.326S.98.81 1.004 1.09l1 11c.03.317.208.603.48.767l5 3c.16.095.338.143.516.143s.356-.048.515-.143l5-3c.273-.164.452-.45.48-.767l1-11c.026-.28-.067-.557-.257-.764zM12 4H6v2h6v5.72l-4 1.334-4-1.333V9h2v1.28l2 .666 2-.667V8H4V2h8v2z"></path>
|
||||
</svg>
|
||||
<a href="http://viewfromabluemoon.com/" target="_blank">View From A Blue Moon</a> © Brainfarm
|
||||
</small>
|
||||
</li>
|
||||
<li class="plyr__cite plyr__cite--audio" hidden>
|
||||
<small>
|
||||
<svg class="icon" title="HTML5">
|
||||
<title>HTML5</title>
|
||||
<path d="M14.738.326C14.548.118 14.28 0 14 0H2c-.28 0-.55.118-.738.326S.98.81 1.004 1.09l1 11c.03.317.208.603.48.767l5 3c.16.095.338.143.516.143s.356-.048.515-.143l5-3c.273-.164.452-.45.48-.767l1-11c.026-.28-.067-.557-.257-.764zM12 4H6v2h6v5.72l-4 1.334-4-1.333V9h2v1.28l2 .666 2-.667V8H4V2h8v2z"></path>
|
||||
</svg>
|
||||
<a href="http://www.kishibashi.com/" target="_blank">Kishi Bashi – “It All Began With A Burst”</a> © Kishi Bashi
|
||||
</small>
|
||||
</li>
|
||||
<li class="plyr__cite plyr__cite--youtube" hidden>
|
||||
<small>
|
||||
<a href="https://www.youtube.com/watch?v=bTqVqk7FSmY" target="_blank">View From A Blue Moon</a> on
|
||||
<span class="color--youtube">
|
||||
<svg class="icon" role="presentation">
|
||||
<title>YouTube</title>
|
||||
<path d="M15.8,4.8c-0.2-1.3-0.8-2.2-2.2-2.4C11.4,2,8,2,8,2S4.6,2,2.4,2.4C1,2.6,0.3,3.5,0.2,4.8C0,6.1,0,8,0,8
|
||||
s0,1.9,0.2,3.2c0.2,1.3,0.8,2.2,2.2,2.4C4.6,14,8,14,8,14s3.4,0,5.6-0.4c1.4-0.3,2-1.1,2.2-2.4C16,9.9,16,8,16,8S16,6.1,15.8,4.8z
|
||||
M6,11V5l5,3L6,11z"></path>
|
||||
</svg>YouTube
|
||||
</span>
|
||||
</small>
|
||||
</li>
|
||||
<li class="plyr__cite plyr__cite--vimeo" hidden>
|
||||
<small>
|
||||
<a href="https://vimeo.com/ondemand/viewfromabluemoon4k" target="_blank">View From A Blue Moon</a> on
|
||||
<span class="color--vimeo">
|
||||
<svg class="icon" role="presentation">
|
||||
<title>Vimeo</title>
|
||||
<path d="M16,4.3c-0.1,1.6-1.2,3.7-3.3,6.4c-2.2,2.8-4,4.2-5.5,4.2c-0.9,0-1.7-0.9-2.4-2.6C4,9.9,3.4,5,2,5
|
||||
C1.9,5,1.5,5.3,0.8,5.8L0,4.8c0.8-0.7,3.5-3.4,4.7-3.5C5.9,1.2,6.7,2,7,3.8c0.3,2,0.8,6.1,1.8,6.1c0.9,0,2.5-3.4,2.6-4
|
||||
c0.1-0.9-0.3-1.9-2.3-1.1c0.8-2.6,2.3-3.8,4.5-3.8C15.3,1.1,16.1,2.2,16,4.3z"></path>
|
||||
</svg>Vimeo
|
||||
</span>
|
||||
</small>
|
||||
</li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<aside>
|
||||
<svg class="icon">
|
||||
<title>Twitter</title>
|
||||
<path d="M16,3c-0.6,0.3-1.2,0.4-1.9,0.5c0.7-0.4,1.2-1,1.4-1.8c-0.6,0.4-1.3,0.6-2.1,0.8c-0.6-0.6-1.5-1-2.4-1
|
||||
C9.3,1.5,7.8,3,7.8,4.8c0,0.3,0,0.5,0.1,0.7C5.2,5.4,2.7,4.1,1.1,2.1c-0.3,0.5-0.4,1-0.4,1.7c0,1.1,0.6,2.1,1.5,2.7
|
||||
c-0.5,0-1-0.2-1.5-0.4c0,0,0,0,0,0c0,1.6,1.1,2.9,2.6,3.2C3,9.4,2.7,9.4,2.4,9.4c-0.2,0-0.4,0-0.6-0.1c0.4,1.3,1.6,2.3,3.1,2.3
|
||||
c-1.1,0.9-2.5,1.4-4.1,1.4c-0.3,0-0.5,0-0.8,0c1.5,0.9,3.2,1.5,5,1.5c6,0,9.3-5,9.3-9.3c0-0.1,0-0.3,0-0.4C15,4.3,15.6,3.7,16,3z"></path>
|
||||
</svg>
|
||||
<p>If you think Plyr's good,
|
||||
<a href="https://twitter.com/intent/tweet?text=A+simple+HTML5+media+player+with+custom+controls+and+WebVTT+captions.&url=http%3A%2F%2Fplyr.io&via=Sam_Potts"
|
||||
target="_blank" data-shr-network="twitter">tweet it</a>
|
||||
</p>
|
||||
</aside>
|
||||
|
||||
<!-- Polyfills -->
|
||||
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=es6,Array.prototype.includes,CustomEvent,fetch"></script>
|
||||
|
||||
<!-- Plyr core script -->
|
||||
<script src="../dist/plyr.js"></script>
|
||||
|
||||
<!-- Sharing libary (https://shr.one) -->
|
||||
<script src="https://cdn.shr.one/1.0.1/shr.js"></script>
|
||||
|
||||
<!-- Rangetouch to fix <input type="range"> on touch devices (see https://rangetouch.com) -->
|
||||
<script src="https://cdn.rangetouch.com/1.0.1/rangetouch.js" async></script>
|
||||
|
||||
<!-- Docs script -->
|
||||
<script src="dist/demo.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -35,10 +35,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
|
||||
// Setup the player
|
||||
const player = new window.Plyr('#player', {
|
||||
const player = new Plyr('#player', {
|
||||
debug: true,
|
||||
title: 'View From A Blue Moon',
|
||||
iconUrl: '../dist/plyr.svg',
|
||||
// iconUrl: '../dist/plyr.svg',
|
||||
keyboard: {
|
||||
global: true,
|
||||
},
|
||||
@ -52,11 +52,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c',
|
||||
},
|
||||
ads: {
|
||||
tag:
|
||||
'https://go.aniview.com/api/adserver6/vast/?AV_PUBLISHERID=58c25bb0073ef448b1087ad6&AV_CHANNELID=5a0458dc28a06145e4519d21&AV_URL=127.0.0.1:3000&cb=1&AV_WIDTH=640&AV_HEIGHT=480',
|
||||
// Test tags
|
||||
// tag: 'https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dlinear&correlator=',
|
||||
// tag: 'https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator=',
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -34,8 +34,13 @@
|
||||
@import '../components/icons';
|
||||
@import '../components/links';
|
||||
@import '../components/lists';
|
||||
@import '../components/media';
|
||||
@import '../components/navigation';
|
||||
@import '../components/players';
|
||||
|
||||
// Plyr
|
||||
@import '../../../../src/sass/plyr';
|
||||
|
||||
// Utils
|
||||
@import '../utilities/cosmetic';
|
||||
@import '../utilities/hidden';
|
||||
|
@ -6,11 +6,11 @@
|
||||
.button,
|
||||
.button__count {
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
background: $color-button-background;
|
||||
border: 0;
|
||||
border-radius: $border-radius-base;
|
||||
box-shadow: 0 1px 1px rgba(#000, 0.1);
|
||||
color: $gray;
|
||||
color: $color-button-text;
|
||||
display: inline-flex;
|
||||
padding: ($spacing-base * 0.75);
|
||||
position: relative;
|
||||
@ -71,7 +71,7 @@
|
||||
&::before {
|
||||
border: $arrow-size solid transparent;
|
||||
border-left-width: 0;
|
||||
border-right-color: #fff;
|
||||
border-right-color: $color-button-background;
|
||||
content: '';
|
||||
height: 0;
|
||||
position: absolute;
|
||||
|
@ -42,4 +42,8 @@ a {
|
||||
&.tab-focus {
|
||||
@include tab-focus();
|
||||
}
|
||||
|
||||
&.no-border::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
// ==========================================================================
|
||||
// Color
|
||||
// Basic media
|
||||
// ==========================================================================
|
||||
|
||||
.color--vimeo {
|
||||
color: $color-vimeo;
|
||||
}
|
||||
|
||||
.color--youtube {
|
||||
color: $color-youtube;
|
||||
img,
|
||||
video,
|
||||
audio {
|
||||
max-width: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
@ -28,6 +28,7 @@ body {
|
||||
|
||||
main {
|
||||
margin: auto;
|
||||
padding-bottom: 1px; // Collapsing margins
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -24,5 +24,9 @@ $color-vimeo: #19b7ed;
|
||||
$color-link: #fff;
|
||||
$color-background: $color-brand-primary;
|
||||
|
||||
// Buttons
|
||||
$color-button-background: #fff;
|
||||
$color-button-text: $gray;
|
||||
|
||||
// Focus
|
||||
$tab-focus-default-color: #fff;
|
||||
|
7
demo/src/sass/utilities/cosmetic.scss
Normal file
7
demo/src/sass/utilities/cosmetic.scss
Normal file
@ -0,0 +1,7 @@
|
||||
// ==========================================================================
|
||||
// Misc cosmetic
|
||||
// ==========================================================================
|
||||
|
||||
.no-border {
|
||||
border: 0;
|
||||
}
|
@ -5,3 +5,16 @@
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// Hide only visually, but have it available for screen readers: h5bp.com/v
|
||||
.sr-only {
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
opacity: 0.001;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
|
2
dist/plyr.css
vendored
2
dist/plyr.css
vendored
File diff suppressed because one or more lines are too long
7191
dist/plyr.js
vendored
7191
dist/plyr.js
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.js.map
vendored
2
dist/plyr.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.min.js
vendored
Normal file
2
dist/plyr.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/plyr.min.js.map
vendored
Normal file
1
dist/plyr.min.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
12390
dist/plyr.polyfilled.js
vendored
Normal file
12390
dist/plyr.polyfilled.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
dist/plyr.polyfilled.js.map
vendored
Normal file
1
dist/plyr.polyfilled.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2
dist/plyr.polyfilled.min.js
vendored
Normal file
2
dist/plyr.polyfilled.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/plyr.polyfilled.min.js.map
vendored
Normal file
1
dist/plyr.polyfilled.min.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
61
gulpfile.js
61
gulpfile.js
@ -9,6 +9,7 @@ const path = require('path');
|
||||
const gulp = require('gulp');
|
||||
const gutil = require('gulp-util');
|
||||
const concat = require('gulp-concat');
|
||||
const filter = require('gulp-filter');
|
||||
const sass = require('gulp-sass');
|
||||
const cleancss = require('gulp-clean-css');
|
||||
const run = require('run-sequence');
|
||||
@ -24,8 +25,7 @@ const size = require('gulp-size');
|
||||
const rollup = require('gulp-better-rollup');
|
||||
const babel = require('rollup-plugin-babel');
|
||||
const sourcemaps = require('gulp-sourcemaps');
|
||||
const uglify = require('rollup-plugin-uglify');
|
||||
const { minify } = require('uglify-es');
|
||||
const uglify = require('gulp-uglify-es').default;
|
||||
const commonjs = require('rollup-plugin-commonjs');
|
||||
const resolve = require('rollup-plugin-node-resolve');
|
||||
|
||||
@ -40,6 +40,8 @@ try {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
const minSuffix = '.min';
|
||||
|
||||
// Paths
|
||||
const root = __dirname;
|
||||
const paths = {
|
||||
@ -68,7 +70,9 @@ const paths = {
|
||||
root: path.join(root, 'demo/'),
|
||||
},
|
||||
upload: [
|
||||
path.join(root, 'dist/**'),
|
||||
path.join(root, `dist/*${minSuffix}.js`),
|
||||
path.join(root, 'dist/*.css'),
|
||||
path.join(root, 'dist/*.svg'),
|
||||
path.join(root, 'demo/dist/**'),
|
||||
],
|
||||
};
|
||||
@ -122,6 +126,7 @@ const build = {
|
||||
Object.keys(files).forEach(key => {
|
||||
const name = `js:${key}`;
|
||||
tasks.js.push(name);
|
||||
const { output } = paths[bundle];
|
||||
|
||||
gulp.task(name, () =>
|
||||
gulp
|
||||
@ -135,15 +140,19 @@ const build = {
|
||||
resolve(),
|
||||
commonjs(),
|
||||
babel(babelrc),
|
||||
uglify({}, minify),
|
||||
],
|
||||
},
|
||||
options,
|
||||
),
|
||||
)
|
||||
.pipe(size(sizeOptions))
|
||||
.pipe(sourcemaps.write(''))
|
||||
.pipe(gulp.dest(paths[bundle].output)),
|
||||
.pipe(gulp.dest(output))
|
||||
.pipe(filter('**/*.js'))
|
||||
.pipe(uglify())
|
||||
.pipe(size(sizeOptions))
|
||||
.pipe(rename({ suffix: minSuffix }))
|
||||
.pipe(sourcemaps.write(''))
|
||||
.pipe(gulp.dest(output)),
|
||||
);
|
||||
});
|
||||
},
|
||||
@ -266,6 +275,23 @@ if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
|
||||
const semver = new RegExp(`v${regex}`, 'gi');
|
||||
const localPath = new RegExp('(../)?dist', 'gi');
|
||||
const versionPath = `https://${aws.cdn.domain}/${version}`;
|
||||
const cdnpath = new RegExp(`${aws.cdn.domain}/${regex}/`, 'gi');
|
||||
|
||||
gulp.task('version', () => {
|
||||
console.log(`Updating versions to '${version}'...`);
|
||||
|
||||
// Replace versioned URLs in source
|
||||
const files = [
|
||||
'plyr.js',
|
||||
'plyr.polyfilled.js',
|
||||
'defaults.js',
|
||||
];
|
||||
gulp
|
||||
.src(files.map(file => path.join(root, `src/js/${file}`)))
|
||||
.pipe(replace(semver, `v${version}`))
|
||||
.pipe(replace(cdnpath, `${aws.cdn.domain}/${version}/`))
|
||||
.pipe(gulp.dest(path.join(root, 'src/js/')));
|
||||
});
|
||||
|
||||
// Publish version to CDN bucket
|
||||
gulp.task('cdn', () => {
|
||||
@ -280,15 +306,15 @@ if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
|
||||
return gulp
|
||||
.src(paths.upload)
|
||||
.pipe(
|
||||
size({
|
||||
showFiles: true,
|
||||
gzip: true,
|
||||
rename(p => {
|
||||
p.basename = p.basename.replace(minSuffix, ''); // eslint-disable-line
|
||||
p.dirname = p.dirname.replace('.', version); // eslint-disable-line
|
||||
}),
|
||||
)
|
||||
.pipe(
|
||||
rename(p => {
|
||||
// eslint-disable-next-line
|
||||
p.dirname = p.dirname.replace('.', version);
|
||||
size({
|
||||
showFiles: true,
|
||||
gzip: true,
|
||||
}),
|
||||
)
|
||||
.pipe(replace(localPath, versionPath))
|
||||
@ -304,21 +330,12 @@ if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
|
||||
|
||||
console.log(`Uploading '${version}' demo to ${aws.demo.domain}...`);
|
||||
|
||||
const cdnpath = new RegExp(`${aws.cdn.domain}/${regex}/`, 'gi');
|
||||
|
||||
// Replace versioned files in readme.md
|
||||
gulp
|
||||
.src([`${root}/readme.md`])
|
||||
.pipe(replace(cdnpath, `${aws.cdn.domain}/${version}/`))
|
||||
.pipe(gulp.dest(root));
|
||||
|
||||
// Replace versioned files in plyr.js
|
||||
gulp
|
||||
.src(path.join(root, 'src/js/plyr.js'))
|
||||
.pipe(replace(semver, `v${version}`))
|
||||
.pipe(replace(cdnpath, `${aws.cdn.domain}/${version}/`))
|
||||
.pipe(gulp.dest(path.join(root, 'src/js/')));
|
||||
|
||||
// Replace local file paths with remote paths in demo HTML
|
||||
// e.g. "../dist/plyr.js" to "https://cdn.plyr.io/x.x.x/plyr.js"
|
||||
const index = `${paths.demo.root}index.html`;
|
||||
@ -385,6 +402,6 @@ if (Object.keys(aws).includes('cdn') && Object.keys(aws).includes('demo')) {
|
||||
|
||||
// Do everything
|
||||
gulp.task('publish', () => {
|
||||
run(tasks.clean, tasks.js, tasks.sass, tasks.sprite, 'cdn', 'demo');
|
||||
run('version', tasks.clean, tasks.js, tasks.sass, tasks.sprite, 'cdn', 'demo');
|
||||
});
|
||||
}
|
||||
|
36
package.json
36
package.json
@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "plyr",
|
||||
"version": "3.0.0-beta.6",
|
||||
"version": "3.0.0-beta.14",
|
||||
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
|
||||
"homepage": "https://plyr.io",
|
||||
"main": "./dist",
|
||||
"main": "./dist/plyr.js",
|
||||
"browser": "./dist/plyr.min.js",
|
||||
"sass": "./src/sass/plyr.scss",
|
||||
"style": "./dist/plyr.css",
|
||||
"devDependencies": {
|
||||
@ -12,7 +13,7 @@
|
||||
"babel-plugin-external-helpers": "^6.22.0",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"del": "^3.0.0",
|
||||
"eslint": "^4.16.0",
|
||||
"eslint": "^4.18.0",
|
||||
"eslint-config-airbnb-base": "^12.1.0",
|
||||
"eslint-config-prettier": "^2.9.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
@ -22,31 +23,39 @@
|
||||
"gulp-better-rollup": "^3.0.0",
|
||||
"gulp-clean-css": "^3.9.2",
|
||||
"gulp-concat": "^2.6.1",
|
||||
"gulp-filter": "^5.1.0",
|
||||
"gulp-open": "^2.1.0",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-replace": "^0.6.1",
|
||||
"gulp-s3": "^0.11.0",
|
||||
"gulp-sass": "^3.1.0",
|
||||
"gulp-size": "^3.0.0",
|
||||
"gulp-sourcemaps": "^1.12.1",
|
||||
"gulp-sourcemaps": "^2.6.4",
|
||||
"gulp-svgmin": "^1.2.4",
|
||||
"gulp-svgstore": "^6.1.1",
|
||||
"gulp-uglify-es": "^1.0.0",
|
||||
"gulp-util": "^3.0.8",
|
||||
"rollup-plugin-babel": "^3.0.3",
|
||||
"rollup-plugin-commonjs": "^8.2.6",
|
||||
"rollup-plugin-node-resolve": "^3.0.2",
|
||||
"rollup-plugin-uglify": "^3.0.0",
|
||||
"rollup-plugin-commonjs": "^8.3.0",
|
||||
"rollup-plugin-node-resolve": "^3.0.3",
|
||||
"run-sequence": "^2.2.1",
|
||||
"stylelint": "^8.4.0",
|
||||
"stylelint-config-prettier": "^2.0.0",
|
||||
"stylelint-config-sass-guidelines": "^4.1.0",
|
||||
"stylelint-config-standard": "^18.0.0",
|
||||
"stylelint-order": "^0.8.0",
|
||||
"stylelint-scss": "^2.2.0",
|
||||
"stylelint-selector-bem-pattern": "^2.0.0",
|
||||
"uglify-es": "^3.3.8"
|
||||
"stylelint-scss": "^2.3.0",
|
||||
"stylelint-selector-bem-pattern": "^2.0.0"
|
||||
},
|
||||
"keywords": ["HTML5 Video", "HTML5 Audio", "Media Player", "DASH", "Shaka", "WordPress", "HLS"],
|
||||
"keywords": [
|
||||
"HTML5 Video",
|
||||
"HTML5 Audio",
|
||||
"Media Player",
|
||||
"DASH",
|
||||
"Shaka",
|
||||
"WordPress",
|
||||
"HLS"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/sampotts/plyr.git"
|
||||
@ -62,5 +71,8 @@
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Sam Potts <sam@potts.es>",
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"custom-event-polyfill": "^0.3.0"
|
||||
}
|
||||
}
|
||||
|
265
readme.md
265
readme.md
@ -1,12 +1,12 @@
|
||||
---
|
||||
Beware: This branch is currently in beta and not production-ready
|
||||
Beware: This version is currently in beta and not production-ready
|
||||
---
|
||||
|
||||
# Plyr
|
||||
|
||||
A simple, lightweight, accessible and customizable HTML5, YouTube and Vimeo media player that supports [_modern_](#browser-support) browsers.
|
||||
|
||||
[Checkout the demo](https://plyr.io) - [Donate to support Plyr](#donate)
|
||||
[Checkout the demo](https://plyr.io) - [Donate to support Plyr](#donate) - [Chat on Slack](https://bit.ly/plyr-slack)
|
||||
|
||||
[](https://plyr.io)
|
||||
|
||||
@ -35,35 +35,21 @@ Oh and yes, it works with Bootstrap.
|
||||
|
||||
Check out the [changelog](changelog.md) to see what's new with Plyr.
|
||||
|
||||
## CMS plugins
|
||||
## Plugins & Components
|
||||
|
||||
### [WordPress](https://wordpress.org/plugins/plyr/)
|
||||
Some awesome folks have made plugins for CMSs and Components for JavaScript frameworks:
|
||||
|
||||
Created and maintained by Ryan Anthony Drake ([@iamryandrake](https://github.com/iamryandrake))
|
||||
|
||||
### [Neos](https://packagist.org/packages/jonnitto/plyr)
|
||||
|
||||
Created and maintained by Jon Uhlmann ([@jonnitto](https://github.com/jonnitto))
|
||||
|
||||
### [Kirby](https://github.com/dpschen/kirby-plyrtag)
|
||||
|
||||
Created and maintained by Dominik Pschenitschni ([@dpschen](https://github.com/dpschen))
|
||||
|
||||
## Using package managers
|
||||
|
||||
You can grab the source using one of the following package managers.
|
||||
|
||||
### npm
|
||||
|
||||
```
|
||||
npm install plyr
|
||||
```
|
||||
|
||||
[https://www.npmjs.com/package/plyr](https://www.npmjs.com/package/plyr)
|
||||
| Type | Maintainer | Link |
|
||||
| --------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| WordPress | Ryan Anthony Drake ([@iamryandrake](https://github.com/iamryandrake)) | [https://wordpress.org/plugins/plyr/](https://wordpress.org/plugins/plyr/) |
|
||||
| React | Jose Miguel Bejarano ([@xDae](https://github.com/xDae)) | [https://github.com/xDae/react-plyr](https://github.com/xDae/react-plyr) |
|
||||
| Vue | Gabe Dunn ([@redxtech](https://github.com/redxtech)) | [https://github.com/redxtech/vue-plyr](https://github.com/redxtech/vue-plyr) |
|
||||
| Neos | Jon Uhlmann ([@jonnitto](https://github.com/jonnitto)) | [https://packagist.org/packages/jonnitto/plyr](https://packagist.org/packages/jonnitto/plyr) |
|
||||
| Kirby | Dominik Pschenitschni ([@dpschen](https://github.com/dpschen)) | [https://github.com/dpschen/kirby-plyrtag](https://github.com/dpschen/kirby-plyrtag) |
|
||||
|
||||
## Quick setup
|
||||
|
||||
Here's a quick run through on getting up and running. There's also a [demo on Codepen](http://codepen.io/sampotts/pen/jARJYp).
|
||||
Here's a quick run through on getting up and running. There's also a [demo on Codepen](http://codepen.io/sampotts/pen/jARJYp). You can grab all of the source with [NPM](https://www.npmjs.com/package/plyr) using `npm install plyr`.
|
||||
|
||||
### HTML
|
||||
|
||||
@ -95,20 +81,40 @@ For YouTube and Vimeo players, Plyr uses progressive enhancement to enhance the
|
||||
|
||||
#### YouTube embed
|
||||
|
||||
We recommend [progressive enhancement](https://www.smashingmagazine.com/2009/04/progressive-enhancement-what-it-is-and-how-to-use-it/) with the embedded players. You can elect to use an `<iframe>` as the source element (which Plyr will progressively enhance) or a bog standard `<div>` with two essential data attributes - `data-plyr-provider` and `data-plyr-embed-id`.
|
||||
|
||||
```html
|
||||
<div class="plyr__video-embed" id="player">
|
||||
<iframe src="https://www.youtube.com/embed/bTqVqk7FSmY?origin=https://plyr.io&iv_load_policy=3&modestbranding=1&playsinline=1&showinfo=0&rel=0&enablejsapi=1" allowfullscreen allowtransparency allow="autoplay"></iframe>
|
||||
</div>
|
||||
```
|
||||
|
||||
_Note_: The `plyr__video-embed` classname will make the player a responsive 16:9 (most common) iframe embed. When plyr itself kicks in, your custom `ratio` config option will be used.
|
||||
|
||||
Or the `<div>` non progressively enhanced method:
|
||||
|
||||
```html
|
||||
<div id="player" data-plyr-provider="youtube" data-plyr-embed-id="bTqVqk7FSmY"></div>
|
||||
```
|
||||
|
||||
_Note_: The `data-plyr-embed-id` can either be the video ID or URL for the media.
|
||||
|
||||
#### Vimeo embed
|
||||
|
||||
Much the same as YouTube above.
|
||||
|
||||
```html
|
||||
<div class="plyr__video-embed" id="player">
|
||||
<iframe src="https://player.vimeo.com/video/76979871?loop=false&byline=false&portrait=false&title=false&speed=true&transparent=0&gesture=media" allowfullscreen allowtransparency allow="autoplay"></iframe>
|
||||
</div>
|
||||
```
|
||||
|
||||
Or the `<div>` non progressively enhanced method:
|
||||
|
||||
```html
|
||||
<div id="player" data-plyr-provider="vimeo" data-plyr-embed-id="76979871"></div>
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
|
||||
Include the `plyr.js` script before the closing `</body>` tag and then call `plyr.setup()`. More info on `setup()` can be found under
|
||||
@ -122,9 +128,11 @@ Include the `plyr.js` script before the closing `</body>` tag and then call `ply
|
||||
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript, you can use the following:
|
||||
|
||||
```html
|
||||
<script src="https://cdn.plyr.io/3.0.0-beta.6/plyr.js"></script>
|
||||
<script src="https://cdn.plyr.io/3.0.0-beta.14/plyr.js"></script>
|
||||
```
|
||||
|
||||
_Note_: Be sure to read the [polyfills](#polyfills) section below about browser compatibility
|
||||
|
||||
### CSS
|
||||
|
||||
Include the `plyr.css` stylsheet into your `<head>`
|
||||
@ -136,13 +144,13 @@ 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:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdn.plyr.io/3.0.0-beta.6/plyr.css">
|
||||
<link rel="stylesheet" href="https://cdn.plyr.io/3.0.0-beta.14/plyr.css">
|
||||
```
|
||||
|
||||
### 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
|
||||
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.0.0-beta.6/plyr.svg`.
|
||||
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.0.0-beta.14/plyr.svg`.
|
||||
|
||||
## Advanced
|
||||
|
||||
@ -235,40 +243,40 @@ Options can be passed as an object to the constructor as above or as JSON in `da
|
||||
|
||||
Note the single quotes encapsulating the JSON and double quotes on the object keys. Only string values need double quotes.
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
| -------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `enabled` | Boolean | `true` | Completely disable Plyr. This would allow you to do a User Agent check or similar to programmatically enable or disable Plyr for a certain UA. Example below. |
|
||||
| `debug` | Boolean | `false` | Display debugging information in the console |
|
||||
| `controls` | Function or Array | `['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 a string of HTML 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 |
|
||||
| `i18n` | Object | See [defaults.js](/src/js/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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `seekTime` | Number | `10` | The time, in seconds, to seek when a user hits fast forward or rewind. |
|
||||
| `volume` | Number | `1` | A number, between 0 and 1, representing the initial volume of the player. |
|
||||
| `muted` | Boolean | `false` | Whether to start playback muted. If the `muted` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true. |
|
||||
| `clickToPlay` | Boolean | `true` | Click (or tap) of the video container will toggle play/pause. |
|
||||
| `disableContextMenu` | Boolean | `true` | Disable right click menu on video to <em>help</em> as very primitive obfuscation to prevent downloads of content. |
|
||||
| `hideControls` | Boolean | `true` | Hide video controls automatically after 2s of no mouse or focus movement, on control element blur (tab out), on playback start or entering fullscreen. As soon as the mouse is moved, a control element is focused or playback is paused, the controls reappear instantly. |
|
||||
| `showPosterOnEnd` | Boolean | false | This will restore and _reload_ HTML5 video once playback is complete. Note: depending on the browser caching, this may result in the video downloading again (or parts of it). Use with caution. |
|
||||
| `keyboard` | Object | `{ focused: true, global: false }` | Enable [keyboard shortcuts](#shortcuts) for focused players only or globally |
|
||||
| `tooltips` | Object | `{ controls: false, seek: true }` | `controls`: Display control labels as tooltips on `:hover` & `:focus` (by default, the labels are screen reader only). `seek`: Display a seek tooltip to indicate on click where the media would seek to. |
|
||||
| `duration` | Number | `null` | Specify a custom duration for media. |
|
||||
| `displayDuration` | Boolean | `true` | Displays the duration of the media on the "metadataloaded" event (on startup) in the current time display. This will only work if the `preload` attribute is not set to `none` (or is not set at all) and you choose not to display the duration (see `controls` option). |
|
||||
| `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. |
|
||||
| `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: window.navigator.language.split('-')[0] }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). |
|
||||
| `fullscreen` | Object | `{ enabled: true, fallback: true }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution. |
|
||||
| `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. |
|
||||
| `speed` | Object | `{ selected: 1, options: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2] }` | `selected`: The default speed for playback. `options`: Options to display in the menu. Most browsers will refuse to play slower than 0.5. |
|
||||
| `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. |
|
||||
| Option | Type | Default | Description |
|
||||
| -------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `enabled` | Boolean | `true` | Completely disable Plyr. This would allow you to do a User Agent check or similar to programmatically enable or disable Plyr for a certain UA. Example below. |
|
||||
| `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. |
|
||||
| `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/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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `seekTime` | Number | `10` | The time, in seconds, to seek when a user hits fast forward or rewind. |
|
||||
| `volume` | Number | `1` | A number, between 0 and 1, representing the initial volume of the player. |
|
||||
| `muted` | Boolean | `false` | Whether to start playback muted. If the `muted` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true. |
|
||||
| `clickToPlay` | Boolean | `true` | Click (or tap) of the video container will toggle play/pause. |
|
||||
| `disableContextMenu` | Boolean | `true` | Disable right click menu on video to <em>help</em> as very primitive obfuscation to prevent downloads of content. |
|
||||
| `hideControls` | Boolean | `true` | Hide video controls automatically after 2s of no mouse or focus movement, on control element blur (tab out), on playback start or entering fullscreen. As soon as the mouse is moved, a control element is focused or playback is paused, the controls reappear instantly. |
|
||||
| `showPosterOnEnd` | Boolean | false | This will restore and _reload_ HTML5 video once playback is complete. Note: depending on the browser caching, this may result in the video downloading again (or parts of it). Use with caution. |
|
||||
| `keyboard` | Object | `{ focused: true, global: false }` | Enable [keyboard shortcuts](#shortcuts) for focused players only or globally |
|
||||
| `tooltips` | Object | `{ controls: false, seek: true }` | `controls`: Display control labels as tooltips on `:hover` & `:focus` (by default, the labels are screen reader only). `seek`: Display a seek tooltip to indicate on click where the media would seek to. |
|
||||
| `duration` | Number | `null` | Specify a custom duration for media. |
|
||||
| `displayDuration` | Boolean | `true` | Displays the duration of the media on the "metadataloaded" event (on startup) in the current time display. This will only work if the `preload` attribute is not set to `none` (or is not set at all) and you choose not to display the duration (see `controls` option). |
|
||||
| `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. |
|
||||
| `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: window.navigator.language.split('-')[0] }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). |
|
||||
| `fullscreen` | Object | `{ enabled: true, fallback: true }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution. |
|
||||
| `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. |
|
||||
| `speed` | Object | `{ selected: 1, options: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2] }` | `selected`: The default speed for playback. `options`: Options to display in the menu. Most browsers will refuse to play slower than 0.5. |
|
||||
| `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. |
|
||||
|
||||
1. Vimeo only
|
||||
|
||||
@ -296,69 +304,77 @@ element.addEventListener('ready', event => {
|
||||
|
||||
### Methods
|
||||
|
||||
Methods are not chainable. An example use of a method:
|
||||
Example method use:
|
||||
|
||||
```javascript
|
||||
player.play();
|
||||
player.play(); // Start playback
|
||||
player.fullscreen.enter(); // Enter fullscreen
|
||||
```
|
||||
|
||||
| Method | Parameters | Description |
|
||||
| ------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `play()`¹ | - | Start playback. |
|
||||
| `pause()` | - | Pause playback. |
|
||||
| `togglePlay(toggle)` | Boolean | Toggle playback, if no parameters are passed, it will toggle based on current status. |
|
||||
| `stop()` | - | Stop playback and reset to start. |
|
||||
| `restart()` | - | Restart playback. |
|
||||
| `rewind(seekTime)` | Number | Rewind playback by the specified seek time. If no parameter is passed, the default seek time will be used. |
|
||||
| `forward(seekTime)` | Number | Fast forward by the specified seek time. If no parameter is passed, the default seek time will be used. |
|
||||
| `increaseVolume(step)` | Number | Increase volume by the specified step. If no parameter is passed, the default step will be used. |
|
||||
| `decreaseVolume(step)` | Number | Increase volume by the specified step. If no parameter is passed, the default step will be used. |
|
||||
| `toggleCaptions(toggle)` | Boolean | Toggle captions display. If no parameter is passed, it will toggle based on current status. |
|
||||
| `toggleFullscreen(event)` | Event | Toggle fullscreen. Fullscreen can only be initiated by a user event. Exit is possible without user input. |
|
||||
| `airplay()` | - | Trigger the airplay dialog on supported devices. |
|
||||
| `toggleControls(toggle)` | Boolean | Toggle the controls based on the specified boolean. |
|
||||
| `on(event, function)` | String, Function | Add 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. |
|
||||
| `destroy()` | - | Destroy the instance and garbage collect any elements. |
|
||||
| Method | Parameters | Description |
|
||||
| ------------------------ | ---------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||
| `play()`¹ | - | Start playback. |
|
||||
| `pause()` | - | Pause playback. |
|
||||
| `togglePlay(toggle)` | Boolean | Toggle playback, if no parameters are passed, it will toggle based on current status. |
|
||||
| `stop()` | - | Stop playback and reset to start. |
|
||||
| `restart()` | - | Restart playback. |
|
||||
| `rewind(seekTime)` | Number | Rewind playback by the specified seek time. If no parameter is passed, the default seek time will be used. |
|
||||
| `forward(seekTime)` | Number | Fast forward by the specified seek time. If no parameter is passed, the default seek time will be used. |
|
||||
| `increaseVolume(step)` | Number | Increase volume by the specified step. If no parameter is passed, the default step will be used. |
|
||||
| `decreaseVolume(step)` | Number | Increase volume by the specified step. If no parameter is passed, the default step will be used. |
|
||||
| `toggleCaptions(toggle)` | Boolean | Toggle captions display. If no parameter is passed, it will toggle based on current status. |
|
||||
| `fullscreen.enter()` | - | Enter fullscreen. If fullscreen is not supported, a fallback "full window/viewport" is used instead. |
|
||||
| `fullscreen.exit()` | - | Exit fullscreen. |
|
||||
| `fullscreen.toggle()` | - | Toggle fullscreen. |
|
||||
| `airplay()` | - | Trigger the airplay dialog on supported devices. |
|
||||
| `toggleControls(toggle)` | Boolean | Toggle the controls based on the specified boolean. |
|
||||
| `on(event, function)` | String, Function | Add 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. |
|
||||
| `destroy()` | - | Destroy the instance and garbage collect any elements. |
|
||||
|
||||
1. For HTML5 players, `play()` will return a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) in _some_ browsers - WebKit and Mozilla [according to MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play) at time of writing.
|
||||
|
||||
### Getters and Setters
|
||||
|
||||
An example setter:
|
||||
Example setters:
|
||||
|
||||
```javascript
|
||||
player.volume = 0.5;
|
||||
player.volume = 0.5; // Sets volume at 50%
|
||||
player.currentTime = 10; // Seeks to 10 seconds
|
||||
```
|
||||
|
||||
An example getter:
|
||||
Example getters:
|
||||
|
||||
```javascript
|
||||
player.volume; // returns 0.5;
|
||||
player.volume; // 0.5;
|
||||
player.currentTime; // 10
|
||||
player.fullscreen.active; // false;
|
||||
```
|
||||
|
||||
| Property | Getter | Setter | Description |
|
||||
| --------------- | ------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `isHTML5` | ✔ | - | Returns a boolean indicating if the current player is HTML5. |
|
||||
| `isEmbed` | ✔ | - | Returns a boolean indicating if the current player is an embedded player. |
|
||||
| `paused` | ✔ | - | Returns a boolean indicating if the current player is paused. |
|
||||
| `playing` | ✔ | - | Returns a boolean indicating if the current player is playing. |
|
||||
| `ended` | ✔ | - | Returns a boolean indicating if the current player has finished playback. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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](#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. |
|
||||
| `autoplay` | ✔ | ✔ | Gets or sets the autoplay state of the player. The setter accepts a boolean. |
|
||||
| `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. |
|
||||
| `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+. |
|
||||
| Property | Getter | Setter | Description |
|
||||
| -------------------- | ------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `isHTML5` | ✓ | - | Returns a boolean indicating if the current player is HTML5. |
|
||||
| `isEmbed` | ✓ | - | Returns a boolean indicating if the current player is an embedded player. |
|
||||
| `paused` | ✓ | - | Returns a boolean indicating if the current player is paused. |
|
||||
| `playing` | ✓ | - | Returns a boolean indicating if the current player is playing. |
|
||||
| `ended` | ✓ | - | Returns a boolean indicating if the current player has finished playback. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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](#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. |
|
||||
| `autoplay` | ✓ | ✓ | Gets or sets the autoplay state of the player. The setter accepts a boolean. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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+. |
|
||||
|
||||
1. YouTube only. HTML5 will follow.
|
||||
2. HTML5 only
|
||||
@ -570,22 +586,25 @@ Fullscreen in Plyr is supported by all browsers that [currently support it](http
|
||||
|
||||
## Browser support
|
||||
|
||||
Plyr supports the last 2 versions of most _modern_ browsers. IE11 is also supported.
|
||||
Plyr supports the last 2 versions of most _modern_ browsers.
|
||||
|
||||
| Browser | Supported |
|
||||
| ------------- | -------------- |
|
||||
| Safari | ✔ |
|
||||
| Mobile Safari | ✔¹ |
|
||||
| Firefox | ✔ |
|
||||
| Chrome | ✔ |
|
||||
| Opera | ✔ |
|
||||
| Edge | ✔ |
|
||||
| IE10+ | ✔² |
|
||||
| IE9 | API only³ |
|
||||
| Browser | Supported |
|
||||
| ------------- | --------- |
|
||||
| Safari | ✓ |
|
||||
| Mobile Safari | ✓¹ |
|
||||
| Firefox | ✓ |
|
||||
| Chrome | ✓ |
|
||||
| Opera | ✓ |
|
||||
| Edge | ✓ |
|
||||
| IE11 | ✓ |
|
||||
| IE10 | ✓² |
|
||||
|
||||
1. Mobile Safari on the iPhone forces the native player for `<video>` unless the `playsinline` attribute is present. Volume controls are also disabled.
|
||||
2. Native player used (no support for `<progress>` or `<input type="range">`) but the API is supported (v1.0.28+)
|
||||
3. IE10 has no native fullscreen support, fallback can be used (see [options](#options))
|
||||
1. Mobile Safari on the iPhone forces the native player for `<video>` unless the `playsinline` attribute is present. Volume controls are also disabled as they are handled device wide.
|
||||
2. Native player used (no support for `<progress>` or `<input type="range">`) but the API is supported. No native fullscreen support, fallback can be used (see [options](#options))
|
||||
|
||||
### Polyfills
|
||||
|
||||
Plyr uses ES6 which isn't supported in all browsers quite yet. This means some features will need to be polyfilled to be available otherwise you'll run into issues. We've elected to not burden the ~90% of users that do support these features with extra JS and instead leave polyfilling to you to work out based on your needs. The easiest method I've found is to use [polyfill.io](https://polyfill.io) which provides polyfills based on user agent. This is the method the demo uses.
|
||||
|
||||
### Checking for support
|
||||
|
||||
|
@ -39,7 +39,7 @@ const captions = {
|
||||
// Only Vimeo and HTML5 video supported at this point
|
||||
if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) {
|
||||
// Clear menu and hide
|
||||
if (this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
|
||||
if (utils.is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
|
||||
controls.setCaptionsMenu.call(this);
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ const captions = {
|
||||
captions.show.call(this);
|
||||
|
||||
// Set available languages in list
|
||||
if (this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
|
||||
if (utils.is.array(this.config.controls) && this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
|
||||
controls.setCaptionsMenu.call(this);
|
||||
}
|
||||
},
|
||||
@ -78,7 +78,7 @@ const captions = {
|
||||
// Setup HTML5 track rendering
|
||||
if (this.isHTML5 && this.isVideo) {
|
||||
captions.getTracks.call(this).forEach(track => {
|
||||
// Remove previous bindings
|
||||
// Show track
|
||||
utils.on(track, 'cuechange', event => captions.setCue.call(this, event));
|
||||
|
||||
// Turn off native caption rendering to avoid double captions
|
||||
@ -124,7 +124,8 @@ const captions = {
|
||||
setCue(input) {
|
||||
// Get the track from the event if needed
|
||||
const track = utils.is.event(input) ? input.target : input;
|
||||
const active = track.activeCues[0];
|
||||
const { activeCues } = track;
|
||||
const active = activeCues.length && activeCues[0];
|
||||
const currentTrack = captions.getCurrentTrack.call(this);
|
||||
|
||||
// Only display current track
|
||||
|
19
src/js/controls.js
vendored
19
src/js/controls.js
vendored
@ -215,7 +215,16 @@ const controls = {
|
||||
|
||||
utils.setAttributes(button, attributes);
|
||||
|
||||
this.elements.buttons[type] = button;
|
||||
// We have multiple play buttons
|
||||
if (type === 'play') {
|
||||
if (!utils.is.array(this.elements.buttons[type])) {
|
||||
this.elements.buttons[type] = [];
|
||||
}
|
||||
|
||||
this.elements.buttons[type].push(button);
|
||||
} else {
|
||||
this.elements.buttons[type] = button;
|
||||
}
|
||||
|
||||
return button;
|
||||
},
|
||||
@ -893,7 +902,6 @@ const controls = {
|
||||
// Play/Pause button
|
||||
if (this.config.controls.includes('play')) {
|
||||
container.appendChild(controls.createButton.call(this, 'play'));
|
||||
// container.appendChild(controls.createButton.call(this, 'pause'));
|
||||
}
|
||||
|
||||
// Fast forward button
|
||||
@ -1147,9 +1155,10 @@ const controls = {
|
||||
|
||||
// Null by default
|
||||
let container = null;
|
||||
this.elements.controls = null;
|
||||
|
||||
// HTML passed as the option
|
||||
if (utils.is.string(this.config.controls)) {
|
||||
// HTML or Element passed as the option
|
||||
if (utils.is.string(this.config.controls) || utils.is.element(this.config.controls)) {
|
||||
container = this.config.controls;
|
||||
} else if (utils.is.function(this.config.controls)) {
|
||||
// A custom function to build controls
|
||||
@ -1193,7 +1202,7 @@ const controls = {
|
||||
}
|
||||
|
||||
// Find the elements if need be
|
||||
if (utils.is.element(this.elements.controls)) {
|
||||
if (!utils.is.element(this.elements.controls)) {
|
||||
utils.findElements.call(this);
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ const defaults = {
|
||||
// Sprite (for icons)
|
||||
loadSprite: true,
|
||||
iconPrefix: 'plyr',
|
||||
iconUrl: 'https://cdn.plyr.io/2.0.10/plyr.svg',
|
||||
iconUrl: 'https://cdn.plyr.io/3.0.0-beta.14/plyr.svg',
|
||||
|
||||
// Blank video (used to prevent errors on source change)
|
||||
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
|
||||
@ -120,6 +120,7 @@ const defaults = {
|
||||
fullscreen: {
|
||||
enabled: true, // Allow fullscreen?
|
||||
fallback: true, // Fallback for vintage browsers
|
||||
iosNative: false, // Use the native fullscreen in iOS (disables custom controls)
|
||||
},
|
||||
|
||||
// Local storage
|
||||
@ -179,6 +180,7 @@ const defaults = {
|
||||
reset: 'Reset',
|
||||
none: 'None',
|
||||
disabled: 'Disabled',
|
||||
advertisment: 'Ad',
|
||||
},
|
||||
|
||||
// URLs
|
||||
@ -357,14 +359,23 @@ const defaults = {
|
||||
tabFocus: 'plyr__tab-focus',
|
||||
},
|
||||
|
||||
// Embed attributes
|
||||
attributes: {
|
||||
embed: {
|
||||
provider: 'data-plyr-provider',
|
||||
id: 'data-plyr-embed-id',
|
||||
},
|
||||
},
|
||||
|
||||
// API keys
|
||||
keys: {
|
||||
google: null,
|
||||
},
|
||||
|
||||
// Ads
|
||||
// Advertisements plugin
|
||||
// Tag is not required as publisher is determined by vi.ai using the domain
|
||||
ads: {
|
||||
tag: null,
|
||||
enabled: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1,127 +1,204 @@
|
||||
// ==========================================================================
|
||||
// Plyr fullscreen API
|
||||
// Fullscreen wrapper
|
||||
// ==========================================================================
|
||||
|
||||
import utils from './utils';
|
||||
|
||||
// Determine the prefix
|
||||
const prefix = (() => {
|
||||
let value = false;
|
||||
const browser = utils.getBrowser();
|
||||
|
||||
if (utils.is.function(document.cancelFullScreen)) {
|
||||
value = '';
|
||||
function onChange() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update toggle button
|
||||
const button = this.player.elements.buttons.fullscreen;
|
||||
if (utils.is.element(button)) {
|
||||
utils.toggleState(button, this.active);
|
||||
}
|
||||
|
||||
// Trigger an event
|
||||
utils.dispatchEvent(this.target, this.active ? 'enterfullscreen' : 'exitfullscreen', true);
|
||||
|
||||
// Trap focus in container
|
||||
if (!browser.isIos) {
|
||||
utils.trapFocus.call(this.player, this.target, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleFallback(toggle = false) {
|
||||
// Store or restore scroll position
|
||||
if (toggle) {
|
||||
this.scrollPosition = {
|
||||
x: window.scrollX || 0,
|
||||
y: window.scrollY || 0,
|
||||
};
|
||||
} else {
|
||||
window.scrollTo(this.scrollPosition.x, this.scrollPosition.y);
|
||||
}
|
||||
|
||||
// Toggle scroll
|
||||
document.body.style.overflow = toggle ? 'hidden' : '';
|
||||
|
||||
// Toggle class hook
|
||||
utils.toggleClass(this.target, this.player.config.classNames.fullscreen.fallback, toggle);
|
||||
|
||||
// Toggle button and fire events
|
||||
onChange.call(this);
|
||||
}
|
||||
|
||||
class Fullscreen {
|
||||
constructor(player) {
|
||||
// Keep reference to parent
|
||||
this.player = player;
|
||||
|
||||
// Get prefix
|
||||
this.prefix = Fullscreen.prefix;
|
||||
|
||||
// Scroll position
|
||||
this.scrollPosition = { x: 0, y: 0 };
|
||||
|
||||
// Register event listeners
|
||||
// Handle event (incase user presses escape etc)
|
||||
utils.on(document, this.prefix === 'ms' ? 'MSFullscreenChange' : `${this.prefix}fullscreenchange`, () => {
|
||||
// TODO: Filter for target??
|
||||
onChange.call(this);
|
||||
});
|
||||
|
||||
// Fullscreen toggle on double click
|
||||
utils.on(this.player.elements.container, 'dblclick', () => {
|
||||
this.toggle();
|
||||
});
|
||||
|
||||
// Prevent double click on controls bubbling up
|
||||
utils.on(this.player.elements.controls, 'dblclick', event => event.stopPropagation());
|
||||
|
||||
// Update the UI
|
||||
this.update();
|
||||
}
|
||||
|
||||
// Determine if native supported
|
||||
static get native() {
|
||||
return !!(document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled);
|
||||
}
|
||||
|
||||
// Get the prefix for handlers
|
||||
static get prefix() {
|
||||
// No prefix
|
||||
if (utils.is.function(document.cancelFullScreen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for fullscreen support by vendor prefix
|
||||
[
|
||||
let value = '';
|
||||
const prefixes = [
|
||||
'webkit',
|
||||
'o',
|
||||
'moz',
|
||||
'ms',
|
||||
'khtml',
|
||||
].some(pre => {
|
||||
];
|
||||
|
||||
prefixes.some(pre => {
|
||||
if (utils.is.function(document[`${pre}CancelFullScreen`])) {
|
||||
value = pre;
|
||||
return true;
|
||||
} else if (utils.is.function(document.msExitFullscreen) && document.msFullscreenEnabled) {
|
||||
// Special case for MS (when isn't it?)
|
||||
} else if (utils.is.function(document.msExitFullscreen)) {
|
||||
value = 'ms';
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return value;
|
||||
})();
|
||||
// Determine if fullscreen is enabled
|
||||
get enabled() {
|
||||
const fallback = this.player.config.fullscreen.fallback && !utils.inFrame();
|
||||
|
||||
// Fullscreen API
|
||||
const fullscreen = {
|
||||
// Get the prefix
|
||||
prefix,
|
||||
return (Fullscreen.native || fallback) && this.player.config.fullscreen.enabled && this.player.supported.ui && this.player.isVideo;
|
||||
}
|
||||
|
||||
// Check if we can use it
|
||||
enabled: document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled,
|
||||
|
||||
// Yet again Microsoft awesomeness,
|
||||
// Sometimes the prefix is 'ms', sometimes 'MS' to keep you on your toes
|
||||
eventType: prefix === 'ms' ? 'MSFullscreenChange' : `${prefix}fullscreenchange`,
|
||||
|
||||
// Is an element fullscreen
|
||||
isFullScreen(element) {
|
||||
if (!fullscreen.enabled) {
|
||||
// Get active state
|
||||
get active() {
|
||||
if (!this.enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const target = utils.is.nullOrUndefined(element) ? document.body : element;
|
||||
|
||||
switch (prefix) {
|
||||
case '':
|
||||
return document.fullscreenElement === target;
|
||||
|
||||
case 'moz':
|
||||
return document.mozFullScreenElement === target;
|
||||
|
||||
default:
|
||||
return document[`${prefix}FullscreenElement`] === target;
|
||||
// Fallback using classname
|
||||
if (!Fullscreen.native) {
|
||||
return utils.hasClass(this.target, this.player.config.classNames.fullscreen.fallback);
|
||||
}
|
||||
},
|
||||
|
||||
const element = !this.prefix ? document.fullscreenElement : document[`${this.prefix}FullscreenElement`];
|
||||
|
||||
return element === this.target;
|
||||
}
|
||||
|
||||
// Get target element
|
||||
get target() {
|
||||
return browser.isIos && this.player.config.fullscreen.iosNative ? this.player.media : this.player.elements.container;
|
||||
}
|
||||
|
||||
// Update UI
|
||||
update() {
|
||||
if (this.enabled) {
|
||||
this.player.debug.log(`${Fullscreen.native ? 'Native' : 'Fallback'} fullscreen enabled`);
|
||||
} else {
|
||||
this.player.debug.log('Fullscreen not supported and fallback disabled');
|
||||
}
|
||||
|
||||
// Add styling hook to show button
|
||||
utils.toggleClass(this.player.elements.container, this.player.config.classNames.fullscreen.enabled, this.enabled);
|
||||
}
|
||||
|
||||
// Make an element fullscreen
|
||||
requestFullScreen(element) {
|
||||
if (!fullscreen.enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const target = utils.is.nullOrUndefined(element) ? document.body : element;
|
||||
|
||||
return !prefix.length ? target.requestFullScreen() : target[prefix + (prefix === 'ms' ? 'RequestFullscreen' : 'RequestFullScreen')]();
|
||||
},
|
||||
|
||||
// Bail from fullscreen
|
||||
cancelFullScreen() {
|
||||
if (!fullscreen.enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !prefix.length ? document.cancelFullScreen() : document[prefix + (prefix === 'ms' ? 'ExitFullscreen' : 'CancelFullScreen')]();
|
||||
},
|
||||
|
||||
// Get the current element
|
||||
element() {
|
||||
if (!fullscreen.enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return !prefix.length ? document.fullscreenElement : document[`${prefix}FullscreenElement`];
|
||||
},
|
||||
|
||||
// Setup fullscreen
|
||||
setup() {
|
||||
if (!this.supported.ui || this.isAudio || !this.config.fullscreen.enabled) {
|
||||
enter() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for native support
|
||||
const nativeSupport = fullscreen.enabled;
|
||||
// iOS native fullscreen doesn't need the request step
|
||||
if (browser.isIos && this.player.config.fullscreen.iosNative) {
|
||||
if (this.player.playing) {
|
||||
this.target.webkitEnterFullscreen();
|
||||
}
|
||||
} else if (!Fullscreen.native) {
|
||||
toggleFallback.call(this, true);
|
||||
} else if (!this.prefix) {
|
||||
this.target.requestFullScreen();
|
||||
} else if (!utils.is.empty(this.prefix)) {
|
||||
this.target[`${this.prefix}${this.prefix === 'ms' ? 'RequestFullscreen' : 'RequestFullScreen'}`]();
|
||||
}
|
||||
}
|
||||
|
||||
if (nativeSupport || (this.config.fullscreen.fallback && !utils.inFrame())) {
|
||||
this.debug.log(`${nativeSupport ? 'Native' : 'Fallback'} fullscreen enabled`);
|
||||
// Bail from fullscreen
|
||||
exit() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add styling hook to show button
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.fullscreen.enabled, true);
|
||||
// iOS native fullscreen
|
||||
if (browser.isIos && this.player.config.fullscreen.iosNative) {
|
||||
this.target.webkitExitFullscreen();
|
||||
this.player.play();
|
||||
} else if (!Fullscreen.native) {
|
||||
toggleFallback.call(this, false);
|
||||
} else if (!this.prefix) {
|
||||
document.cancelFullScreen();
|
||||
} else if (!utils.is.empty(this.prefix)) {
|
||||
document[`${this.prefix}${this.prefix === 'ms' ? 'ExitFullscreen' : 'CancelFullScreen'}`]();
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle state
|
||||
toggle() {
|
||||
if (!this.active) {
|
||||
this.enter();
|
||||
} else {
|
||||
this.debug.log('Fullscreen not supported and fallback disabled');
|
||||
this.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle state
|
||||
if (this.elements.buttons && this.elements.buttons.fullscreen) {
|
||||
utils.toggleState(this.elements.buttons.fullscreen, false);
|
||||
}
|
||||
|
||||
// Trap focus in container
|
||||
utils.trapFocus.call(this);
|
||||
},
|
||||
};
|
||||
|
||||
export default fullscreen;
|
||||
export default Fullscreen;
|
||||
|
@ -5,7 +5,6 @@
|
||||
import support from './support';
|
||||
import utils from './utils';
|
||||
import controls from './controls';
|
||||
import fullscreen from './fullscreen';
|
||||
import ui from './ui';
|
||||
|
||||
// Sniff out the browser
|
||||
@ -138,7 +137,7 @@ const listeners = {
|
||||
|
||||
case 70:
|
||||
// F key
|
||||
this.toggleFullscreen();
|
||||
this.fullscreen.toggle();
|
||||
break;
|
||||
|
||||
case 67:
|
||||
@ -171,8 +170,8 @@ const listeners = {
|
||||
|
||||
// Escape is handle natively when in full screen
|
||||
// So we only need to worry about non native
|
||||
if (!fullscreen.enabled && this.fullscreen.active && code === 27) {
|
||||
this.toggleFullscreen();
|
||||
if (!this.fullscreen.enabled && this.fullscreen.active && code === 27) {
|
||||
this.fullscreen.toggle();
|
||||
}
|
||||
|
||||
// Store last code for next cycle
|
||||
@ -215,18 +214,6 @@ const listeners = {
|
||||
this.toggleControls(event);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle user exiting fullscreen by escaping etc
|
||||
if (fullscreen.enabled) {
|
||||
utils.on(document, fullscreen.eventType, event => {
|
||||
this.toggleFullscreen(event);
|
||||
});
|
||||
|
||||
// Fullscreen toggle on double click
|
||||
utils.on(this.elements.container, 'dblclick', event => {
|
||||
this.toggleFullscreen(event);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Listen for media events
|
||||
@ -266,7 +253,7 @@ const listeners = {
|
||||
utils.on(this.media, 'playing play pause ended', event => ui.checkPlaying.call(this, event));
|
||||
|
||||
// Loading
|
||||
utils.on(this.media, 'stalled waiting canplay seeked playing', event => ui.checkLoading.call(this, event));
|
||||
utils.on(this.media, 'waiting canplay seeked playing', event => ui.checkLoading.call(this, event));
|
||||
|
||||
// Check if media failed to load
|
||||
// utils.on(this.media, 'play', event => ui.checkFailed.call(this, event));
|
||||
@ -307,7 +294,7 @@ const listeners = {
|
||||
event => {
|
||||
event.preventDefault();
|
||||
},
|
||||
false
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
@ -394,63 +381,63 @@ const listeners = {
|
||||
utils.on(this.elements.buttons.play, 'click', event =>
|
||||
proxy(event, 'play', () => {
|
||||
this.togglePlay();
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Pause
|
||||
utils.on(this.elements.buttons.restart, 'click', event =>
|
||||
proxy(event, 'restart', () => {
|
||||
this.restart();
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Rewind
|
||||
utils.on(this.elements.buttons.rewind, 'click', event =>
|
||||
proxy(event, 'rewind', () => {
|
||||
this.rewind();
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Rewind
|
||||
utils.on(this.elements.buttons.forward, 'click', event =>
|
||||
proxy(event, 'forward', () => {
|
||||
this.forward();
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Mute toggle
|
||||
utils.on(this.elements.buttons.mute, 'click', event =>
|
||||
proxy(event, 'mute', () => {
|
||||
this.muted = !this.muted;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Captions toggle
|
||||
utils.on(this.elements.buttons.captions, 'click', event =>
|
||||
proxy(event, 'captions', () => {
|
||||
this.toggleCaptions();
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Fullscreen toggle
|
||||
utils.on(this.elements.buttons.fullscreen, 'click', event =>
|
||||
proxy(event, 'fullscreen', () => {
|
||||
this.toggleFullscreen();
|
||||
})
|
||||
this.fullscreen.toggle();
|
||||
}),
|
||||
);
|
||||
|
||||
// Picture-in-Picture
|
||||
utils.on(this.elements.buttons.pip, 'click', event =>
|
||||
proxy(event, 'pip', () => {
|
||||
this.pip = 'toggle';
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Airplay
|
||||
utils.on(this.elements.buttons.airplay, 'click', event =>
|
||||
proxy(event, 'airplay', () => {
|
||||
this.airplay();
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Settings menu
|
||||
@ -489,7 +476,7 @@ const listeners = {
|
||||
utils.on(this.elements.inputs.seek, inputEvent, event =>
|
||||
proxy(event, 'seek', () => {
|
||||
this.currentTime = event.target.value / event.target.max * this.duration;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Current time invert
|
||||
@ -510,7 +497,7 @@ const listeners = {
|
||||
utils.on(this.elements.inputs.volume, inputEvent, event =>
|
||||
proxy(event, 'volume', () => {
|
||||
this.volume = event.target.value;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// Polyfill for lower fill in <input type="range"> for webkit
|
||||
@ -583,7 +570,7 @@ const listeners = {
|
||||
event.preventDefault();
|
||||
}
|
||||
}),
|
||||
false
|
||||
false,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
@ -86,7 +86,7 @@ const media = {
|
||||
}
|
||||
|
||||
// Remove child sources
|
||||
Array.from(this.media.querySelectorAll('source')).forEach(utils.removeElement);
|
||||
utils.removeElement(this.media.querySelectorAll('source'));
|
||||
|
||||
// Set blank video src attribute
|
||||
// This is to prevent a MEDIA_ERR_SRC_NOT_SUPPORTED error
|
||||
|
@ -8,6 +8,22 @@
|
||||
|
||||
import utils from '../utils';
|
||||
|
||||
// Build the default tag URL
|
||||
const getTagUrl = () => {
|
||||
const params = {
|
||||
AV_PUBLISHERID: '58c25bb0073ef448b1087ad6',
|
||||
AV_CHANNELID: '5a0458dc28a06145e4519d21',
|
||||
AV_URL: '127.0.0.1:3000',
|
||||
cb: 1,
|
||||
AV_WIDTH: 640,
|
||||
AV_HEIGHT: 480,
|
||||
};
|
||||
|
||||
const base = 'https://go.aniview.com/api/adserver6/vast/';
|
||||
|
||||
return `${base}?${utils.buildUrlParams(params)}`;
|
||||
};
|
||||
|
||||
class Ads {
|
||||
/**
|
||||
* Ads constructor.
|
||||
@ -16,8 +32,10 @@ class Ads {
|
||||
*/
|
||||
constructor(player) {
|
||||
this.player = player;
|
||||
this.enabled = player.config.ads.enabled;
|
||||
this.playing = false;
|
||||
this.initialized = false;
|
||||
this.blocked = false;
|
||||
this.enabled = utils.is.url(player.config.ads.tag);
|
||||
|
||||
// Check if a tag URL is provided.
|
||||
@ -25,11 +43,19 @@ class Ads {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the Google IMA3 SDK is loaded
|
||||
// Check if the Google IMA3 SDK is loaded or load it ourselves
|
||||
if (!utils.is.object(window.google)) {
|
||||
utils.loadScript(player.config.urls.googleIMA.api, () => {
|
||||
this.ready();
|
||||
});
|
||||
utils.loadScript(
|
||||
player.config.urls.googleIMA.api,
|
||||
() => {
|
||||
this.ready();
|
||||
},
|
||||
() => {
|
||||
// Script failed to load or is blocked
|
||||
this.blocked = true;
|
||||
this.player.debug.log('Ads error: Google IMA SDK failed to load');
|
||||
},
|
||||
);
|
||||
} else {
|
||||
this.ready();
|
||||
}
|
||||
@ -122,7 +148,7 @@ class Ads {
|
||||
|
||||
// Request video ads
|
||||
const request = new google.ima.AdsRequest();
|
||||
request.adTagUrl = this.player.config.ads.tag;
|
||||
request.adTagUrl = getTagUrl();
|
||||
|
||||
// Specify the linear and nonlinear slot sizes. This helps the SDK
|
||||
// to select the correct creative if multiple are returned
|
||||
@ -155,7 +181,8 @@ class Ads {
|
||||
|
||||
const update = () => {
|
||||
const time = utils.formatTime(this.manager.getRemainingTime());
|
||||
this.elements.container.setAttribute('data-badge-text', time);
|
||||
const label = `${this.player.config.i18n.advertisment} - ${time}`;
|
||||
this.elements.container.setAttribute('data-badge-text', label);
|
||||
};
|
||||
|
||||
this.countdownTimer = window.setInterval(update, 100);
|
||||
|
@ -51,7 +51,16 @@ const vimeo = {
|
||||
gesture: 'media',
|
||||
};
|
||||
const params = utils.buildUrlParams(options);
|
||||
const id = utils.parseVimeoId(player.media.getAttribute('src'));
|
||||
|
||||
// Get the source URL or ID
|
||||
let source = player.media.getAttribute('src');
|
||||
|
||||
// Get from <div> if needed
|
||||
if (utils.is.empty(source)) {
|
||||
source = player.media.getAttribute(this.config.attributes.embed.id);
|
||||
}
|
||||
|
||||
const id = utils.parseVimeoId(source);
|
||||
|
||||
// Build an iframe
|
||||
const iframe = utils.createElement('iframe');
|
||||
|
@ -59,10 +59,10 @@ const youtube = {
|
||||
if (utils.is.string(key) && !utils.is.empty(key)) {
|
||||
const url = `https://www.googleapis.com/youtube/v3/videos?id=${videoId}&key=${key}&fields=items(snippet(title))&part=snippet`;
|
||||
|
||||
fetch(url)
|
||||
.then(response => (response.ok ? response.json() : null))
|
||||
utils
|
||||
.fetch(url)
|
||||
.then(result => {
|
||||
if (result !== null && utils.is.object(result)) {
|
||||
if (utils.is.object(result)) {
|
||||
this.config.title = result.items[0].snippet.title;
|
||||
ui.setTitle.call(this);
|
||||
}
|
||||
@ -87,8 +87,16 @@ const youtube = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the source URL or ID
|
||||
let source = player.media.getAttribute('src');
|
||||
|
||||
// Get from <div> if needed
|
||||
if (utils.is.empty(source)) {
|
||||
source = player.media.getAttribute(this.config.attributes.embed.id);
|
||||
}
|
||||
|
||||
// Replace the <iframe> with a <div> due to YouTube API issues
|
||||
const videoId = utils.parseYouTubeId(player.media.getAttribute('src'));
|
||||
const videoId = utils.parseYouTubeId(source);
|
||||
const id = utils.generateId(player.provider);
|
||||
const container = utils.createElement('div', { id });
|
||||
player.media = utils.replaceElement(container, player.media);
|
||||
@ -108,8 +116,8 @@ const youtube = {
|
||||
playsinline: 1, // Allow iOS inline playback
|
||||
|
||||
// Tracking for stats
|
||||
origin: window && window.location.hostname,
|
||||
widget_referrer: window && window.location.href,
|
||||
// origin: window ? `${window.location.protocol}//${window.location.host}` : null,
|
||||
widget_referrer: window ? window.location.href : null,
|
||||
|
||||
// Captions are flaky on YouTube
|
||||
cc_load_policy: player.captions.active ? 1 : 0,
|
||||
|
173
src/js/plyr.js
173
src/js/plyr.js
@ -1,6 +1,6 @@
|
||||
// ==========================================================================
|
||||
// Plyr
|
||||
// plyr.js v3.0.0-beta.6
|
||||
// plyr.js v3.0.0-beta.14
|
||||
// https://github.com/sampotts/plyr
|
||||
// License: The MIT License (MIT)
|
||||
// ==========================================================================
|
||||
@ -11,12 +11,12 @@ import support from './support';
|
||||
import utils from './utils';
|
||||
|
||||
import Console from './console';
|
||||
import Fullscreen from './fullscreen';
|
||||
import Storage from './storage';
|
||||
import Ads from './plugins/ads';
|
||||
|
||||
import captions from './captions';
|
||||
import controls from './controls';
|
||||
import fullscreen from './fullscreen';
|
||||
import listeners from './listeners';
|
||||
import media from './media';
|
||||
import source from './source';
|
||||
@ -26,12 +26,6 @@ import ui from './ui';
|
||||
// TODO: Use a WeakMap for private globals
|
||||
// const globals = new WeakMap();
|
||||
|
||||
// Globals
|
||||
let scrollPosition = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
|
||||
// Plyr instance
|
||||
class Plyr {
|
||||
constructor(target, options) {
|
||||
@ -153,50 +147,53 @@ class Plyr {
|
||||
// Find the frame
|
||||
iframe = this.media.querySelector('iframe');
|
||||
|
||||
// <iframe> required
|
||||
if (!utils.is.element(iframe)) {
|
||||
this.debug.error('Setup failed: <iframe> is missing');
|
||||
return;
|
||||
// <iframe> type
|
||||
if (utils.is.element(iframe)) {
|
||||
// Detect provider
|
||||
url = iframe.getAttribute('src');
|
||||
this.provider = utils.getProviderByUrl(url);
|
||||
|
||||
// Rework elements
|
||||
this.elements.container = this.media;
|
||||
this.media = iframe;
|
||||
|
||||
// Reset classname
|
||||
this.elements.container.className = '';
|
||||
|
||||
// Get attributes from URL and set config
|
||||
params = utils.getUrlParams(url);
|
||||
if (!utils.is.empty(params)) {
|
||||
const truthy = [
|
||||
'1',
|
||||
'true',
|
||||
];
|
||||
|
||||
if (truthy.includes(params.autoplay)) {
|
||||
this.config.autoplay = true;
|
||||
}
|
||||
if (truthy.includes(params.playsinline)) {
|
||||
this.config.inline = true;
|
||||
}
|
||||
if (truthy.includes(params.loop)) {
|
||||
this.config.loop.active = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// <div> with attributes
|
||||
this.provider = this.media.getAttribute(this.config.attributes.embed.provider);
|
||||
|
||||
// Remove attribute
|
||||
this.media.removeAttribute(this.config.attributes.embed.provider);
|
||||
}
|
||||
|
||||
// Audio will come later for external providers
|
||||
this.type = types.video;
|
||||
|
||||
// Detect provider
|
||||
url = iframe.getAttribute('src');
|
||||
this.provider = utils.getProviderByUrl(url);
|
||||
|
||||
// Get attributes from URL and set config
|
||||
params = utils.getUrlParams(url);
|
||||
if (!utils.is.empty(params)) {
|
||||
const truthy = [
|
||||
'1',
|
||||
'true',
|
||||
];
|
||||
|
||||
if (truthy.includes(params.autoplay)) {
|
||||
this.config.autoplay = true;
|
||||
}
|
||||
if (truthy.includes(params.playsinline)) {
|
||||
this.config.inline = true;
|
||||
}
|
||||
if (truthy.includes(params.loop)) {
|
||||
this.config.loop.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsupported provider
|
||||
// Unsupported or missing provider
|
||||
if (utils.is.empty(this.provider) || !Object.keys(providers).includes(this.provider)) {
|
||||
this.debug.error('Setup failed: Invalid provider');
|
||||
return;
|
||||
}
|
||||
|
||||
// Rework elements
|
||||
this.elements.container = this.media;
|
||||
this.media = iframe;
|
||||
|
||||
// Reset classname
|
||||
this.elements.container.className = '';
|
||||
// Audio will come later for external providers
|
||||
this.type = types.video;
|
||||
|
||||
break;
|
||||
|
||||
@ -229,9 +226,6 @@ class Plyr {
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup local storage for user settings
|
||||
this.storage = new Storage(this);
|
||||
|
||||
// Check for support again but with type
|
||||
this.supported = support.check(this.type, this.provider, this.config.inline);
|
||||
|
||||
@ -241,6 +235,9 @@ class Plyr {
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup local storage for user settings
|
||||
this.storage = new Storage(this);
|
||||
|
||||
// Store reference
|
||||
this.media.plyr = this;
|
||||
|
||||
@ -275,6 +272,9 @@ class Plyr {
|
||||
ui.build.call(this);
|
||||
}
|
||||
|
||||
// Setup fullscreen
|
||||
this.fullscreen = new Fullscreen(this);
|
||||
|
||||
// Setup ads if provided
|
||||
this.ads = new Ads(this);
|
||||
}
|
||||
@ -306,14 +306,17 @@ class Plyr {
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the media, or play the advertisement
|
||||
* Play the media, or play the advertisement (if they are not blocked)
|
||||
*/
|
||||
play() {
|
||||
if (this.ads.enabled && !this.ads.initialized) {
|
||||
// TODO: Always return a promise?
|
||||
if (this.ads.enabled && !this.ads.initialized && !this.ads.blocked) {
|
||||
this.ads.play();
|
||||
return null;
|
||||
}
|
||||
|
||||
this.media.play();
|
||||
// Return the promise (for HTML5)
|
||||
return this.media.play();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -556,6 +559,10 @@ class Plyr {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.isAudio) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get audio tracks
|
||||
return this.media.mozHasAudio || Boolean(this.media.webkitAudioDecodedByteCount) || Boolean(this.media.audioTracks && this.media.audioTracks.length);
|
||||
}
|
||||
@ -840,62 +847,6 @@ class Plyr {
|
||||
return this.captions.language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle fullscreen playback
|
||||
* Requires user input event
|
||||
* @param {event} event
|
||||
*/
|
||||
toggleFullscreen(event) {
|
||||
// Video only
|
||||
if (this.isAudio) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for native support
|
||||
if (fullscreen.enabled) {
|
||||
if (utils.is.event(event) && event.type === fullscreen.eventType) {
|
||||
// If it's a fullscreen change event, update the state
|
||||
this.fullscreen.active = fullscreen.isFullScreen(this.elements.container);
|
||||
} else {
|
||||
// Else it's a user request to enter or exit
|
||||
if (!this.fullscreen.active) {
|
||||
fullscreen.requestFullScreen(this.elements.container);
|
||||
} else {
|
||||
fullscreen.cancelFullScreen();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, it's a simple toggle
|
||||
this.fullscreen.active = !this.fullscreen.active;
|
||||
|
||||
// Add class hook
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.fullscreen.fallback, this.fullscreen.active);
|
||||
|
||||
// Make sure we don't lose scroll position
|
||||
if (this.fullscreen.active) {
|
||||
scrollPosition = {
|
||||
x: window.pageXOffset || 0,
|
||||
y: window.pageYOffset || 0,
|
||||
};
|
||||
} else {
|
||||
window.scrollTo(scrollPosition.x, scrollPosition.y);
|
||||
}
|
||||
|
||||
// Bind/unbind escape key
|
||||
document.body.style.overflow = this.fullscreen.active ? 'hidden' : '';
|
||||
}
|
||||
|
||||
// Set button state
|
||||
if (utils.is.element(this.elements.buttons.fullscreen)) {
|
||||
utils.toggleState(this.elements.buttons.fullscreen, this.fullscreen.active);
|
||||
}
|
||||
|
||||
// Trigger an event
|
||||
utils.dispatchEvent.call(this, this.media, this.fullscreen.active ? 'enterfullscreen' : 'exitfullscreen');
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle picture-in-picture playback on WebKit/MacOS
|
||||
* TODO: update player with state, support, enabled
|
||||
@ -1091,12 +1042,8 @@ class Plyr {
|
||||
// If it's a soft destroy, make minimal changes
|
||||
if (soft) {
|
||||
if (Object.keys(this.elements).length) {
|
||||
// Remove buttons
|
||||
if (this.elements.buttons && this.elements.buttons.play) {
|
||||
Array.from(this.elements.buttons.play).forEach(button => utils.removeElement(button));
|
||||
}
|
||||
|
||||
// Remove others
|
||||
// Remove elements
|
||||
utils.removeElement(this.elements.buttons.play);
|
||||
utils.removeElement(this.elements.captions);
|
||||
utils.removeElement(this.elements.controls);
|
||||
utils.removeElement(this.elements.wrapper);
|
||||
|
14
src/js/plyr.polyfilled.js
Normal file
14
src/js/plyr.polyfilled.js
Normal file
@ -0,0 +1,14 @@
|
||||
// ==========================================================================
|
||||
// Plyr Polyfilled Build
|
||||
// plyr.js v3.0.0-beta.14
|
||||
// https://github.com/sampotts/plyr
|
||||
// License: The MIT License (MIT)
|
||||
// ==========================================================================
|
||||
|
||||
import 'babel-polyfill';
|
||||
|
||||
import 'custom-event-polyfill';
|
||||
|
||||
import Plyr from './plyr';
|
||||
|
||||
export default Plyr;
|
@ -136,6 +136,9 @@ const source = {
|
||||
// Setup interface
|
||||
ui.build.call(this);
|
||||
}
|
||||
|
||||
// Update the fullscreen support
|
||||
this.fullscreen.update();
|
||||
},
|
||||
true,
|
||||
);
|
||||
|
16
src/js/ui.js
16
src/js/ui.js
@ -5,7 +5,6 @@
|
||||
import utils from './utils';
|
||||
import captions from './captions';
|
||||
import controls from './controls';
|
||||
import fullscreen from './fullscreen';
|
||||
import listeners from './listeners';
|
||||
|
||||
const ui = {
|
||||
@ -33,12 +32,6 @@ const ui = {
|
||||
if (!this.supported.ui) {
|
||||
this.debug.warn(`Basic support only for ${this.provider} ${this.type}`);
|
||||
|
||||
// Remove controls
|
||||
utils.removeElement.call(this, 'controls');
|
||||
|
||||
// Remove large play
|
||||
utils.removeElement.call(this, 'buttons.play');
|
||||
|
||||
// Restore native controls
|
||||
ui.toggleNativeControls.call(this, true);
|
||||
|
||||
@ -63,9 +56,6 @@ const ui = {
|
||||
// Remove native controls
|
||||
ui.toggleNativeControls.call(this);
|
||||
|
||||
// Setup fullscreen
|
||||
fullscreen.setup.call(this);
|
||||
|
||||
// Captions
|
||||
captions.setup.call(this);
|
||||
|
||||
@ -142,10 +132,8 @@ const ui = {
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.playing, this.playing);
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.stopped, this.paused);
|
||||
|
||||
// Set aria state
|
||||
if (utils.is.nodeList(this.elements.buttons.play)) {
|
||||
Array.from(this.elements.buttons.play).forEach(button => utils.toggleState(button, this.playing));
|
||||
}
|
||||
// Set ARIA state
|
||||
utils.toggleState(this.elements.buttons.play, this.playing);
|
||||
|
||||
// Toggle controls
|
||||
this.toggleControls(!this.playing);
|
||||
|
150
src/js/utils.js
150
src/js/utils.js
@ -9,7 +9,7 @@ const utils = {
|
||||
// Check variable types
|
||||
is: {
|
||||
plyr(input) {
|
||||
return this.instanceof(input, Plyr);
|
||||
return this.instanceof(input, window.Plyr);
|
||||
},
|
||||
object(input) {
|
||||
return this.getConstructor(input) === Object;
|
||||
@ -81,8 +81,40 @@ const utils = {
|
||||
};
|
||||
},
|
||||
|
||||
// Fetch wrapper
|
||||
// Using XHR to avoid issues with older browsers
|
||||
fetch(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const request = new XMLHttpRequest();
|
||||
|
||||
// Check for CORS support
|
||||
if (!('withCredentials' in request)) {
|
||||
return;
|
||||
}
|
||||
|
||||
request.addEventListener('load', () => {
|
||||
try {
|
||||
resolve(JSON.parse(request.responseText));
|
||||
} catch(e) {
|
||||
resolve(request.responseText);
|
||||
}
|
||||
});
|
||||
|
||||
request.addEventListener('error', () => {
|
||||
throw new Error(request.statusText);
|
||||
});
|
||||
|
||||
request.open('GET', url, true);
|
||||
request.send();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Load an external script
|
||||
loadScript(url, callback) {
|
||||
loadScript(url, callback, error) {
|
||||
const current = document.querySelector(`script[src="${url}"]`);
|
||||
|
||||
// Check script is not already referenced, if so wait for load
|
||||
@ -99,6 +131,10 @@ const utils = {
|
||||
element.callbacks = element.callbacks || [];
|
||||
element.callbacks.push(callback);
|
||||
|
||||
// Error queue
|
||||
element.errors = element.errors || [];
|
||||
element.errors.push(error);
|
||||
|
||||
// Bind callback
|
||||
if (utils.is.function(callback)) {
|
||||
element.addEventListener(
|
||||
@ -111,6 +147,16 @@ const utils = {
|
||||
);
|
||||
}
|
||||
|
||||
// Bind error handling
|
||||
element.addEventListener(
|
||||
'error',
|
||||
event => {
|
||||
element.errors.forEach(err => err.call(null, event));
|
||||
element.errors = null;
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
// Set the URL after binding callback
|
||||
element.src = url;
|
||||
|
||||
@ -160,10 +206,10 @@ const utils = {
|
||||
}
|
||||
|
||||
// Get the sprite
|
||||
fetch(url)
|
||||
.then(response => (response.ok ? response.text() : null))
|
||||
.then(text => {
|
||||
if (text === null) {
|
||||
utils
|
||||
.fetch(url)
|
||||
.then(result => {
|
||||
if (utils.is.empty(result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -171,12 +217,12 @@ const utils = {
|
||||
window.localStorage.setItem(
|
||||
prefix + id,
|
||||
JSON.stringify({
|
||||
content: text,
|
||||
content: result,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
updateSprite.call(container, text);
|
||||
updateSprite.call(container, result);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
@ -260,12 +306,15 @@ const utils = {
|
||||
// Remove an element
|
||||
removeElement(element) {
|
||||
if (!utils.is.element(element) || !utils.is.element(element.parentNode)) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (utils.is.nodeList(element) || utils.is.array(element)) {
|
||||
Array.from(element).forEach(utils.removeElement);
|
||||
return;
|
||||
}
|
||||
|
||||
element.parentNode.removeChild(element);
|
||||
|
||||
return element;
|
||||
},
|
||||
|
||||
// Remove all child elements
|
||||
@ -479,46 +528,51 @@ const utils = {
|
||||
},
|
||||
|
||||
// Trap focus inside container
|
||||
trapFocus() {
|
||||
trapFocus(element = null, toggle = false) {
|
||||
if (!utils.is.element(element)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const focusable = utils.getElements.call(this, 'button:not(:disabled), input:not(:disabled), [tabindex]');
|
||||
const first = focusable[0];
|
||||
const last = focusable[focusable.length - 1];
|
||||
|
||||
utils.on(
|
||||
this.elements.container,
|
||||
'keydown',
|
||||
event => {
|
||||
// Bail if not tab key or not fullscreen
|
||||
if (event.key !== 'Tab' || event.keyCode !== 9 || !this.fullscreen.active) {
|
||||
return;
|
||||
}
|
||||
const trap = event => {
|
||||
// Bail if not tab key or not fullscreen
|
||||
if (event.key !== 'Tab' || event.keyCode !== 9) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current focused element
|
||||
const focused = utils.getFocusElement();
|
||||
// Get the current focused element
|
||||
const focused = utils.getFocusElement();
|
||||
|
||||
if (focused === last && !event.shiftKey) {
|
||||
// Move focus to first element that can be tabbed if Shift isn't used
|
||||
first.focus();
|
||||
event.preventDefault();
|
||||
} else if (focused === first && event.shiftKey) {
|
||||
// Move focus to last element that can be tabbed if Shift is used
|
||||
last.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
false,
|
||||
);
|
||||
if (focused === last && !event.shiftKey) {
|
||||
// Move focus to first element that can be tabbed if Shift isn't used
|
||||
first.focus();
|
||||
event.preventDefault();
|
||||
} else if (focused === first && event.shiftKey) {
|
||||
// Move focus to last element that can be tabbed if Shift is used
|
||||
last.focus();
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
if (toggle) {
|
||||
utils.on(this.elements.container, 'keydown', trap, false);
|
||||
} else {
|
||||
utils.off(this.elements.container, 'keydown', trap, false);
|
||||
}
|
||||
},
|
||||
|
||||
// Toggle event listener
|
||||
toggleListener(elements, event, callback, toggle, passive, capture) {
|
||||
// Bail if no elements
|
||||
if (utils.is.nullOrUndefined(elements)) {
|
||||
// Bail if no elemetns, event, or callback
|
||||
if (utils.is.empty(elements) || utils.is.empty(event) || !utils.is.function(callback)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If a nodelist is passed, call itself on each node
|
||||
if (utils.is.nodeList(elements)) {
|
||||
if (utils.is.nodeList(elements) || utils.is.array(elements)) {
|
||||
// Create listener for each node
|
||||
Array.from(elements).forEach(element => {
|
||||
if (element instanceof Node) {
|
||||
@ -565,7 +619,7 @@ const utils = {
|
||||
// Trigger event
|
||||
dispatchEvent(element, type, bubbles, detail) {
|
||||
// Bail if no element
|
||||
if (!element || !type) {
|
||||
if (!utils.is.element(element) || !utils.is.string(type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -573,7 +627,7 @@ const utils = {
|
||||
const event = new CustomEvent(type, {
|
||||
bubbles: utils.is.boolean(bubbles) ? bubbles : false,
|
||||
detail: Object.assign({}, detail, {
|
||||
plyr: this instanceof Plyr ? this : null,
|
||||
plyr: utils.is.plyr(this) ? this : null,
|
||||
}),
|
||||
});
|
||||
|
||||
@ -584,6 +638,12 @@ const utils = {
|
||||
// Toggle aria-pressed state on a toggle button
|
||||
// http://www.ssbbartgroup.com/blog/how-not-to-misuse-aria-states-properties-and-roles
|
||||
toggleState(element, input) {
|
||||
// If multiple elements passed
|
||||
if (utils.is.array(element) || utils.is.nodeList(element)) {
|
||||
Array.from(element).forEach(target => utils.toggleState(target, input));
|
||||
return;
|
||||
}
|
||||
|
||||
// Bail if no target
|
||||
if (!utils.is.element(element)) {
|
||||
return;
|
||||
@ -606,9 +666,15 @@ const utils = {
|
||||
},
|
||||
|
||||
// Time helpers
|
||||
getHours(value) { return parseInt((value / 60 / 60) % 60, 10); },
|
||||
getMinutes(value) { return parseInt((value / 60) % 60, 10); },
|
||||
getSeconds(value) { return parseInt(value % 60, 10); },
|
||||
getHours(value) {
|
||||
return parseInt((value / 60 / 60) % 60, 10);
|
||||
},
|
||||
getMinutes(value) {
|
||||
return parseInt((value / 60) % 60, 10);
|
||||
},
|
||||
getSeconds(value) {
|
||||
return parseInt(value % 60, 10);
|
||||
},
|
||||
|
||||
// Format time to UI friendly string
|
||||
formatTime(time = 0, displayHours = false, inverted = false) {
|
||||
|
@ -3,9 +3,9 @@
|
||||
// --------------------------------------------------------------
|
||||
|
||||
.plyr__badge {
|
||||
background: $plyr-menu-color;
|
||||
background: $plyr-badge-bg;
|
||||
border-radius: 2px;
|
||||
color: $plyr-menu-bg;
|
||||
color: $plyr-badge-color;
|
||||
font-size: $plyr-font-size-badge;
|
||||
line-height: 1;
|
||||
padding: 3px 4px;
|
||||
|
@ -59,6 +59,14 @@
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: $plyr-control-padding;
|
||||
|
||||
li {
|
||||
margin-top: 2px;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Options
|
||||
|
@ -91,6 +91,21 @@
|
||||
@include plyr-tab-focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Video range inputs
|
||||
.plyr--full-ui.plyr--video input[type='range'] {
|
||||
&::-webkit-slider-runnable-track {
|
||||
background-color: $plyr-video-range-track-bg;
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
background-color: $plyr-video-range-track-bg;
|
||||
}
|
||||
|
||||
&::-ms-track {
|
||||
background-color: $plyr-video-range-track-bg;
|
||||
}
|
||||
|
||||
// Pressed styles
|
||||
&:active {
|
||||
@ -108,21 +123,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Video range inputs
|
||||
.plyr--full-ui.plyr--video input[type='range'] {
|
||||
&::-webkit-slider-runnable-track {
|
||||
background-color: $plyr-video-range-track-bg;
|
||||
}
|
||||
|
||||
&::-moz-range-track {
|
||||
background-color: $plyr-video-range-track-bg;
|
||||
}
|
||||
|
||||
&::-ms-track {
|
||||
background-color: $plyr-video-range-track-bg;
|
||||
}
|
||||
}
|
||||
|
||||
// Audio range inputs
|
||||
.plyr--full-ui.plyr--audio input[type='range'] {
|
||||
&::-webkit-slider-runnable-track {
|
||||
@ -136,4 +136,19 @@
|
||||
&::-ms-track {
|
||||
background-color: $plyr-audio-range-track-bg;
|
||||
}
|
||||
|
||||
// Pressed styles
|
||||
&:active {
|
||||
&::-webkit-slider-thumb {
|
||||
@include plyr-range-thumb-active($plyr-audio-range-thumb-shadow-color);
|
||||
}
|
||||
|
||||
&::-moz-range-thumb {
|
||||
@include plyr-range-thumb-active($plyr-audio-range-thumb-shadow-color);
|
||||
}
|
||||
|
||||
&::-ms-thumb {
|
||||
@include plyr-range-thumb-active($plyr-audio-range-thumb-shadow-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,8 +44,8 @@
|
||||
width: $plyr-range-thumb-height;
|
||||
}
|
||||
|
||||
@mixin plyr-range-thumb-active() {
|
||||
box-shadow: 0 0 0 $plyr-range-thumb-active-shadow-width transparentize($plyr-range-thumb-bg, 0.5);
|
||||
@mixin plyr-range-thumb-active($color: rgba($plyr-range-thumb-bg, 0.5)) {
|
||||
box-shadow: $plyr-range-thumb-shadow, 0 0 0 $plyr-range-thumb-active-shadow-width $color;
|
||||
}
|
||||
|
||||
// Fullscreen styles
|
||||
@ -87,6 +87,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Hide cursor in fullscreen when controls hidden
|
||||
&.plyr--hide-controls {
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
// Large captions in full screen on larger screens
|
||||
@media (min-width: $plyr-bp-lg) {
|
||||
.plyr__captions {
|
||||
|
@ -10,6 +10,7 @@
|
||||
@import 'settings/cosmetics';
|
||||
@import 'settings/type';
|
||||
|
||||
@import 'settings/badges';
|
||||
@import 'settings/captions';
|
||||
@import 'settings/controls';
|
||||
@import 'settings/helpers';
|
||||
|
6
src/sass/settings/badges.scss
Normal file
6
src/sass/settings/badges.scss
Normal file
@ -0,0 +1,6 @@
|
||||
// ==========================================================================
|
||||
// Badges
|
||||
// ==========================================================================
|
||||
|
||||
$plyr-badge-bg: $plyr-color-fiord !default;
|
||||
$plyr-badge-color: #fff !default;
|
@ -9,7 +9,7 @@ $plyr-range-thumb-active-shadow-width: 3px !default;
|
||||
$plyr-range-thumb-height: 14px !default;
|
||||
$plyr-range-thumb-bg: #fff !default;
|
||||
$plyr-range-thumb-border: 2px solid transparent !default;
|
||||
$plyr-range-thumb-shadow: 0 1px 1px rgba($plyr-video-controls-bg, 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
|
||||
$plyr-range-track-height: 6px !default;
|
||||
@ -21,3 +21,4 @@ $plyr-range-fill-bg: $plyr-color-main !default;
|
||||
// Type specific
|
||||
$plyr-video-range-track-bg: $plyr-video-progress-buffered-bg !default;
|
||||
$plyr-audio-range-track-bg: $plyr-audio-progress-buffered-bg !default;
|
||||
$plyr-audio-range-thumb-shadow-color: rgba(#000, 0.1) !default;
|
||||
|
114
yarn.lock
114
yarn.lock
@ -699,6 +699,14 @@ babel-plugin-transform-strict-mode@^6.24.1:
|
||||
babel-runtime "^6.22.0"
|
||||
babel-types "^6.24.1"
|
||||
|
||||
babel-polyfill@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153"
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
core-js "^2.5.0"
|
||||
regenerator-runtime "^0.10.5"
|
||||
|
||||
babel-preset-env@^1.6.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48"
|
||||
@ -1291,6 +1299,10 @@ currently-unhandled@^0.4.1:
|
||||
dependencies:
|
||||
array-find-index "^1.0.1"
|
||||
|
||||
custom-event-polyfill@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/custom-event-polyfill/-/custom-event-polyfill-0.3.0.tgz#99807839be62edb446b645832e0d80ead6fa1888"
|
||||
|
||||
d@1:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f"
|
||||
@ -1614,9 +1626,9 @@ eslint-visitor-keys@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d"
|
||||
|
||||
eslint@^4.16.0:
|
||||
version "4.16.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.16.0.tgz#934ada9e98715e1d7bbfd6f6f0519ed2fab35cc1"
|
||||
eslint@^4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.18.0.tgz#ebd0ba795af6dc59aa5cee17938160af5950e051"
|
||||
dependencies:
|
||||
ajv "^5.3.0"
|
||||
babel-code-frame "^6.22.0"
|
||||
@ -2269,6 +2281,14 @@ gulp-concat@^2.6.1:
|
||||
through2 "^2.0.0"
|
||||
vinyl "^2.0.0"
|
||||
|
||||
gulp-filter@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/gulp-filter/-/gulp-filter-5.1.0.tgz#a05e11affb07cf7dcf41a7de1cb7b63ac3783e73"
|
||||
dependencies:
|
||||
multimatch "^2.0.0"
|
||||
plugin-error "^0.1.2"
|
||||
streamfilter "^1.0.5"
|
||||
|
||||
gulp-open@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/gulp-open/-/gulp-open-2.1.0.tgz#ac01eee898d77a7ac0812fad4f3d53d08687d41c"
|
||||
@ -2322,9 +2342,9 @@ gulp-size@^3.0.0:
|
||||
stream-counter "^1.0.0"
|
||||
through2 "^2.0.0"
|
||||
|
||||
gulp-sourcemaps@^2.6.3:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-2.6.3.tgz#11b033f759f909e0a5f15b7bdf47ac29cc54efa4"
|
||||
gulp-sourcemaps@^2.6.4:
|
||||
version "2.6.4"
|
||||
resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-2.6.4.tgz#cbb2008450b1bcce6cd23bf98337be751bf6e30a"
|
||||
dependencies:
|
||||
"@gulp-sourcemaps/identity-map" "1.X"
|
||||
"@gulp-sourcemaps/map-sources" "1.X"
|
||||
@ -2334,7 +2354,7 @@ gulp-sourcemaps@^2.6.3:
|
||||
debug-fabulous "1.X"
|
||||
detect-newline "2.X"
|
||||
graceful-fs "4.X"
|
||||
source-map "0.X"
|
||||
source-map "~0.6.0"
|
||||
strip-bom-string "1.X"
|
||||
through2 "2.X"
|
||||
|
||||
@ -2354,6 +2374,16 @@ gulp-svgstore@^6.1.1:
|
||||
plugin-error "^0.1.2"
|
||||
vinyl "^2.1.0"
|
||||
|
||||
gulp-uglify-es@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/gulp-uglify-es/-/gulp-uglify-es-1.0.0.tgz#80b2f8e2fa7211c1706c597f08bbf620c870e545"
|
||||
dependencies:
|
||||
o-stream "^0.2.2"
|
||||
plugin-error "^0.1.2"
|
||||
uglify-es "^3.2.0"
|
||||
vinyl "^2.1.0"
|
||||
vinyl-sourcemaps-apply "^0.2.1"
|
||||
|
||||
gulp-util@^3.0, gulp-util@^3.0.0, gulp-util@^3.0.4, gulp-util@^3.0.8:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f"
|
||||
@ -3567,7 +3597,7 @@ mimic-fn@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
|
||||
|
||||
"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
|
||||
"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
dependencies:
|
||||
@ -3626,6 +3656,15 @@ ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
|
||||
multimatch@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b"
|
||||
dependencies:
|
||||
array-differ "^1.0.0"
|
||||
array-union "^1.0.1"
|
||||
arrify "^1.0.0"
|
||||
minimatch "^3.0.0"
|
||||
|
||||
multipipe@^0.1.0, multipipe@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b"
|
||||
@ -3762,6 +3801,10 @@ number-is-nan@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
|
||||
|
||||
o-stream@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/o-stream/-/o-stream-0.2.2.tgz#7fe03af870b8f9537af33b312b381b3034ab410f"
|
||||
|
||||
oauth-sign@~0.8.1, oauth-sign@~0.8.2:
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
|
||||
@ -4315,6 +4358,10 @@ regenerate@^1.2.1:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f"
|
||||
|
||||
regenerator-runtime@^0.10.5:
|
||||
version "0.10.5"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
|
||||
|
||||
regenerator-runtime@^0.11.0:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||
@ -4553,9 +4600,9 @@ rollup-plugin-babel@^3.0.3:
|
||||
dependencies:
|
||||
rollup-pluginutils "^1.5.0"
|
||||
|
||||
rollup-plugin-commonjs@^8.2.6:
|
||||
version "8.2.6"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.2.6.tgz#27e5b9069ff94005bb01e01bb46a1e4873784677"
|
||||
rollup-plugin-commonjs@^8.3.0:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.3.0.tgz#91b4ba18f340951e39ed7b1901f377a80ab3f9c3"
|
||||
dependencies:
|
||||
acorn "^5.2.1"
|
||||
estree-walker "^0.5.0"
|
||||
@ -4563,20 +4610,14 @@ rollup-plugin-commonjs@^8.2.6:
|
||||
resolve "^1.4.0"
|
||||
rollup-pluginutils "^2.0.1"
|
||||
|
||||
rollup-plugin-node-resolve@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.0.2.tgz#38babc12fd404cc2ba1ff68648fe43fa3ffee6b0"
|
||||
rollup-plugin-node-resolve@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.0.3.tgz#8f57b253edd00e5b0ad0aed7b7e9cf5982e98fa4"
|
||||
dependencies:
|
||||
builtin-modules "^1.1.0"
|
||||
is-module "^1.0.0"
|
||||
resolve "^1.1.6"
|
||||
|
||||
rollup-plugin-uglify@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup-plugin-uglify/-/rollup-plugin-uglify-3.0.0.tgz#a34eca24617709c6bf1778e9653baafa06099b86"
|
||||
dependencies:
|
||||
uglify-es "^3.3.7"
|
||||
|
||||
rollup-pluginutils@^1.5.0:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz#1e156e778f94b7255bfa1b3d0178be8f5c552408"
|
||||
@ -4791,10 +4832,6 @@ source-map@0.5.x, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6:
|
||||
version "0.5.7"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
|
||||
|
||||
source-map@0.X, source-map@^0.6.1, source-map@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
|
||||
source-map@^0.1.38:
|
||||
version "0.1.43"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346"
|
||||
@ -4807,6 +4844,10 @@ source-map@^0.4.2:
|
||||
dependencies:
|
||||
amdefine ">=0.0.4"
|
||||
|
||||
source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
|
||||
sparkles@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3"
|
||||
@ -4890,6 +4931,12 @@ stream-counter@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/stream-counter/-/stream-counter-1.0.0.tgz#91cf2569ce4dc5061febcd7acb26394a5a114751"
|
||||
|
||||
streamfilter@^1.0.5:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/streamfilter/-/streamfilter-1.0.7.tgz#ae3e64522aa5a35c061fd17f67620c7653c643c9"
|
||||
dependencies:
|
||||
readable-stream "^2.0.2"
|
||||
|
||||
string-width@^1.0.1, string-width@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
|
||||
@ -5011,9 +5058,9 @@ stylelint-order@^0.8.0:
|
||||
postcss "^6.0.14"
|
||||
postcss-sorting "^3.1.0"
|
||||
|
||||
stylelint-scss@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-2.2.0.tgz#3e324bf13346db7af21cd24ad57fe3202f7c3823"
|
||||
stylelint-scss@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-2.3.0.tgz#95089fa0c1cc5690be19ca80f901a4905c95dedc"
|
||||
dependencies:
|
||||
lodash "^4.17.4"
|
||||
postcss-media-query-parser "^0.2.3"
|
||||
@ -5280,16 +5327,9 @@ typedarray@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
|
||||
uglify-es@^3.3.7:
|
||||
version "3.3.7"
|
||||
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.7.tgz#d1249af668666aba7cb1163e277455be9eb393cf"
|
||||
dependencies:
|
||||
commander "~2.13.0"
|
||||
source-map "~0.6.1"
|
||||
|
||||
uglify-es@^3.3.8:
|
||||
version "3.3.8"
|
||||
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.8.tgz#f2c68e6cff0d0f9dc9577e4da207151c2e753b7e"
|
||||
uglify-es@^3.2.0:
|
||||
version "3.3.9"
|
||||
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
|
||||
dependencies:
|
||||
commander "~2.13.0"
|
||||
source-map "~0.6.1"
|
||||
|
Reference in New Issue
Block a user