Moved to provider + type to make it cleaner in future, fix for multiple players
This commit is contained in:
parent
de6f0f1b77
commit
921cefd212
2
demo/dist/demo.css
vendored
2
demo/dist/demo.css
vendored
File diff suppressed because one or more lines are too long
2
demo/dist/demo.js
vendored
2
demo/dist/demo.js
vendored
@ -1,3 +1,3 @@
|
||||
document.addEventListener("DOMContentLoaded",function(){function e(e,t,o){e&&e.classList[o?"add":"remove"](t)}function t(t,r){if(t in a&&(r||t!==n)&&(n.length||t!==a.video)){switch(t){case a.video:o.source={type:"video",title:"View From A Blue Moon",sources:[{src:"media/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"}],poster:"media/View_From_A_Blue_Moon_Trailer-HD.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"media/View_From_A_Blue_Moon_Trailer-HD.en.vtt",default:!0},{kind:"captions",label:"French",srclang:"fr",src:"media/View_From_A_Blue_Moon_Trailer-HD.fr.vtt"}]};break;case a.audio:o.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 a.youtube:o.source={type:"video",title:"View From A Blue Moon",sources:[{src:"https://youtube.com/watch?v=bTqVqk7FSmY",type:"youtube"}]};break;case a.vimeo:o.source={type:"video",sources:[{src:"https://vimeo.com/76979871",type:"vimeo"}]}}n=t,Array.from(i).forEach(function(t){return e(t.parentElement,"active",!1)}),e(document.querySelector('[data-source="'+t+'"]'),"active",!0),Array.from(document.querySelectorAll(".plyr__cite")).forEach(function(e){e.setAttribute("hidden","")}),document.querySelector(".plyr__cite--"+t).removeAttribute("hidden")}}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 o=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},controls:["play-large","play","progress","current-time","mute","volume","captions","settings","fullscreen","pip","airplay"],keys:{google:"AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c"}});window.player=o;var i=document.querySelectorAll("[data-source]"),a={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},n=window.location.hash.replace("#",""),r=window.history&&window.history.pushState;if(Array.from(i).forEach(function(e){e.addEventListener("click",function(){var o=e.getAttribute("data-source");t(o),r&&window.history.pushState({type:o},"","#"+o)})}),window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&t(e.state.type)}),r){var s=!n.length;s&&(n=a.video),n in a&&window.history.replaceState({type:n},"",s?"":"#"+n),n!==a.video&&t(n,!0)}}),"plyr.io"===window.location.host&&(!function(e,t,o,i,a,n,r){e.GoogleAnalyticsObject=a,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,n=t.createElement(o),r=t.getElementsByTagName(o)[0],n.async=1,n.src="//www.google-analytics.com/analytics.js",r.parentNode.insertBefore(n,r)}(window,document,"script",0,"ga"),window.ga("create","UA-40881672-11","auto"),window.ga("send","pageview"));
|
||||
document.addEventListener("DOMContentLoaded",function(){function e(e,t,o){e&&e.classList[o?"add":"remove"](t)}function t(t,n){if(t in r&&(n||t!==a)&&(a.length||t!==r.video)){switch(t){case r.video:o.source={type:"video",title:"View From A Blue Moon",sources:[{src:"media/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"}],poster:"media/View_From_A_Blue_Moon_Trailer-HD.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"media/View_From_A_Blue_Moon_Trailer-HD.en.vtt",default:!0},{kind:"captions",label:"French",srclang:"fr",src:"media/View_From_A_Blue_Moon_Trailer-HD.fr.vtt"}]};break;case r.audio:o.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 r.youtube:o.source={type:"video",title:"View From A Blue Moon",sources:[{src:"https://youtube.com/watch?v=bTqVqk7FSmY",provider:"youtube"}]};break;case r.vimeo:o.source={type:"video",sources:[{src:"https://vimeo.com/76979871",provider:"vimeo"}]}}a=t,Array.from(i).forEach(function(t){return e(t.parentElement,"active",!1)}),e(document.querySelector('[data-source="'+t+'"]'),"active",!0),Array.from(document.querySelectorAll(".plyr__cite")).forEach(function(e){e.setAttribute("hidden","")}),document.querySelector(".plyr__cite--"+t).removeAttribute("hidden")}}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 o=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},controls:["play-large","play","progress","current-time","mute","volume","captions","settings","fullscreen","pip","airplay"],keys:{google:"AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c"}});window.player=o;var i=document.querySelectorAll("[data-source]"),r={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},a=window.location.hash.replace("#",""),n=window.history&&window.history.pushState;if(Array.from(i).forEach(function(e){e.addEventListener("click",function(){var o=e.getAttribute("data-source");t(o),n&&window.history.pushState({type:o},"","#"+o)})}),window.addEventListener("popstate",function(e){e.state&&"type"in e.state&&t(e.state.type)}),n){var s=!a.length;s&&(a=r.video),a in r&&window.history.replaceState({type:a},"",s?"":"#"+a),a!==r.video&&t(a,!0)}}),"plyr.io"===window.location.host&&(!function(e,t,o,i,r,a,n){e.GoogleAnalyticsObject=r,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,a=t.createElement(o),n=t.getElementsByTagName(o)[0],a.async=1,a.src="//www.google-analytics.com/analytics.js",n.parentNode.insertBefore(a,n)}(window,document,"script",0,"ga"),window.ga("create","UA-40881672-11","auto"),window.ga("send","pageview"));
|
||||
|
||||
//# 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
@ -52,19 +52,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
captions: {
|
||||
active: true,
|
||||
},
|
||||
controls: [
|
||||
'play-large',
|
||||
'play',
|
||||
'progress',
|
||||
'current-time',
|
||||
'mute',
|
||||
'volume',
|
||||
'captions',
|
||||
'settings',
|
||||
'fullscreen',
|
||||
'pip',
|
||||
'airplay',
|
||||
],
|
||||
controls: ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'fullscreen', 'pip', 'airplay'],
|
||||
keys: {
|
||||
google: 'AIzaSyDrNwtN3nLH_8rjCmu5Wq3ZCm4MNAVdc0c',
|
||||
},
|
||||
@ -154,7 +142,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
sources: [
|
||||
{
|
||||
src: 'https://youtube.com/watch?v=bTqVqk7FSmY',
|
||||
type: 'youtube',
|
||||
provider: 'youtube',
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -167,7 +155,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
sources: [
|
||||
{
|
||||
src: 'https://vimeo.com/76979871',
|
||||
type: 'vimeo',
|
||||
provider: 'vimeo',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -13,6 +13,10 @@ video {
|
||||
margin: @spacing-base auto;
|
||||
border-radius: @border-radius-base;
|
||||
box-shadow: 0 2px 5px fade(#000, 20%);
|
||||
|
||||
&.plyr--audio {
|
||||
max-width: 480px;
|
||||
}
|
||||
}
|
||||
|
||||
.plyr__video-wrapper::after {
|
||||
|
2
dist/plyr.js
vendored
2
dist/plyr.js
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.js.map
vendored
2
dist/plyr.js.map
vendored
File diff suppressed because one or more lines are too long
151
readme.md
151
readme.md
@ -4,8 +4,7 @@ Beware: This branch 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.
|
||||
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)
|
||||
|
||||
@ -16,8 +15,8 @@ A simple, lightweight, accessible and customizable HTML5, YouTube and Vimeo medi
|
||||
* **Accessible** - full support for VTT captions and screen readers
|
||||
* **Lightweight** - just 18KB minified and gzipped
|
||||
* **[Customisable](#html)** - make the player look how you want with the markup you want
|
||||
* **Semantic** - uses the _right_ elements. `<input type="range">` for volume and `<progress>` for progress and well,
|
||||
`<button>`s for buttons. There's no `<span>` or `<a href="#">` button hacks
|
||||
* **Semantic** - uses the _right_ elements. `<input type="range">` for volume and `<progress>` for progress and well, `<button>`s for buttons. There's no
|
||||
`<span>` or `<a href="#">` button hacks
|
||||
* **Responsive** - works with any screen size
|
||||
* **HTML Video & Audio** - support for both formats
|
||||
* **[Embedded Video](#embeds)** - support for YouTube and Vimeo video playback
|
||||
@ -64,13 +63,12 @@ npm install plyr
|
||||
|
||||
## 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).
|
||||
|
||||
### HTML
|
||||
|
||||
Plyr extends upon the standard HTML5 markup so that's all you need for those types. More info on advanced HTML markup
|
||||
can be found under [initialising](#initialising).
|
||||
Plyr extends upon the standard HTML5 markup so that's all you need for those types. More info on advanced HTML markup can be found under
|
||||
[initialising](#initialising).
|
||||
|
||||
#### HTML5 Video
|
||||
|
||||
@ -98,29 +96,28 @@ For YouTube and Vimeo, Plyr uses the standard YouTube API markup (an empty `<div
|
||||
#### YouTube embed
|
||||
|
||||
```html
|
||||
<div id="player" data-plyr-provider="youtube" data-plyr-provider-id="bTqVqk7FSmY"></div>
|
||||
<div id="player" data-plyr-provider="youtube" data-plyr-embed-id="bTqVqk7FSmY"></div>
|
||||
```
|
||||
|
||||
#### Vimeo embed
|
||||
|
||||
```html
|
||||
<div id="player" data-plyr-provider="vimeo" data-plyr-provider-id="143418951"></div>
|
||||
<div id="player" data-plyr-provider="vimeo" data-plyr-embed-id="143418951"></div>
|
||||
```
|
||||
|
||||
Note: In both cases, `data-plyr-provider-id` value can be the ID or URL for the media.
|
||||
Note: In both cases, `data-plyr-embed-id` value can be the ID or URL for the media.
|
||||
|
||||
### JavaScript
|
||||
|
||||
Include the `plyr.js` script before the closing `</body>` tag and then call `plyr.setup()`. More info on `setup()` can
|
||||
be found under [initialising](#initialising).
|
||||
Include the `plyr.js` script before the closing `</body>` tag and then call `plyr.setup()`. More info on `setup()` can be found under
|
||||
[initialising](#initialising).
|
||||
|
||||
```html
|
||||
<script src="path/to/plyr.js"></script>
|
||||
<script>const player = new Plyr('#player');</script>
|
||||
```
|
||||
|
||||
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript, you can use the
|
||||
following:
|
||||
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/2.0.13/plyr.js"></script>
|
||||
@ -134,8 +131,7 @@ Include the `plyr.css` stylsheet into your `<head>`
|
||||
<link rel="stylesheet" href="path/to/plyr.css">
|
||||
```
|
||||
|
||||
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the
|
||||
following:
|
||||
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="https://cdn.plyr.io/2.0.13/plyr.css">
|
||||
@ -143,50 +139,45 @@ following:
|
||||
|
||||
### 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/2.0.13/plyr.svg`.
|
||||
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/2.0.13/plyr.svg`.
|
||||
|
||||
## Advanced
|
||||
|
||||
### LESS & SASS/SCSS
|
||||
|
||||
You can use `plyr.less` or `plyr.scss` file included in `/src` as part of your build and change variables to suit your
|
||||
design. The LESS and SASS require you to use the [autoprefixer](https://www.npmjs.com/package/gulp-autoprefixer) plugin
|
||||
(you be should already!) as all declarations use the W3C definitions.
|
||||
You can use `plyr.less` or `plyr.scss` file included in `/src` as part of your build and change variables to suit your design. The LESS and SASS require you to
|
||||
use the [autoprefixer](https://www.npmjs.com/package/gulp-autoprefixer) plugin (you be should already!) as all declarations use the W3C definitions.
|
||||
|
||||
The HTML markup uses the BEM methodology with `plyr` as the block, e.g. `.plyr__controls`. You can change the class
|
||||
hooks in the options to match any custom CSS you write. Check out the JavaScript source for more on this.
|
||||
The HTML markup uses the BEM methodology with `plyr` as the block, e.g. `.plyr__controls`. You can change the class hooks in the options to match any custom CSS
|
||||
you write. Check out the JavaScript source for more on this.
|
||||
|
||||
### SVG
|
||||
|
||||
The icons used in the Plyr controls are loaded in an SVG sprite. The sprite is automatically loaded from our CDN by
|
||||
default. If you already have an icon build system in place, you can include the source plyr icons (see `/src/sprite` for
|
||||
source icons).
|
||||
The icons used in the Plyr controls are loaded in an SVG sprite. The sprite is automatically loaded from our CDN by default. If you already have an icon build
|
||||
system in place, you can include the source plyr icons (see `/src/sprite` for source icons).
|
||||
|
||||
#### Using the `iconUrl` option
|
||||
|
||||
You can however specify your own `iconUrl` option and Plyr will determine if the url is absolute and requires loading by
|
||||
AJAX/CORS due to current browser limitations or if it's a relative path, just use the path directly.
|
||||
You can however specify your own `iconUrl` option and Plyr will determine if the url is absolute and requires loading by AJAX/CORS due to current browser
|
||||
limitations or if it's a relative path, just use the path directly.
|
||||
|
||||
If you're using the `<base>` tag on your site, you may need to use something like this:
|
||||
[svgfixer.js](https://gist.github.com/leonderijke/c5cf7c5b2e424c0061d2)
|
||||
If you're using the `<base>` tag on your site, you may need to use something like this: [svgfixer.js](https://gist.github.com/leonderijke/c5cf7c5b2e424c0061d2)
|
||||
|
||||
More info on SVG sprites here:
|
||||
[http://css-tricks.com/svg-sprites-use-better-icon-fonts/](http://css-tricks.com/svg-sprites-use-better-icon-fonts/) and
|
||||
the AJAX technique here: [http://css-tricks.com/ajaxing-svg-sprite/](http://css-tricks.com/ajaxing-svg-sprite/)
|
||||
More info on SVG sprites here: [http://css-tricks.com/svg-sprites-use-better-icon-fonts/](http://css-tricks.com/svg-sprites-use-better-icon-fonts/) and the AJAX
|
||||
technique here: [http://css-tricks.com/ajaxing-svg-sprite/](http://css-tricks.com/ajaxing-svg-sprite/)
|
||||
|
||||
### Cross Origin (CORS)
|
||||
|
||||
You'll notice the `crossorigin` attribute on the example `<video>` elements. This is because the TextTrack captions are
|
||||
loaded from another domain. If your TextTrack captions are also hosted on another domain, you will need to add this
|
||||
attribute and make sure your host has the correct headers setup. For more info on CORS checkout the MDN docs:
|
||||
You'll notice the `crossorigin` attribute on the example `<video>` elements. This is because the TextTrack captions are loaded from another domain. If your
|
||||
TextTrack captions are also hosted on another domain, you will need to add this attribute and make sure your host has the correct headers setup. For more info
|
||||
on CORS checkout the MDN docs:
|
||||
[https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)
|
||||
|
||||
### Captions
|
||||
|
||||
WebVTT captions are supported. To add a caption track, check the HTML example above and look for the `<track>` element.
|
||||
Be sure to [validate your caption files](https://quuz.org/webvtt/).
|
||||
WebVTT captions are supported. To add a caption track, check the HTML example above and look for the `<track>` element. Be sure to
|
||||
[validate your caption files](https://quuz.org/webvtt/).
|
||||
|
||||
### JavaScript
|
||||
|
||||
@ -194,11 +185,10 @@ Be sure to [validate your caption files](https://quuz.org/webvtt/).
|
||||
|
||||
You can specify a range of arguments for the constructor to use:
|
||||
|
||||
* A CSS string selector that's compatible with
|
||||
[`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
|
||||
* A CSS string selector that's compatible with [`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
|
||||
* A [HTMLElement](https://developer.mozilla.org/en/docs/Web/API/HTMLElement)
|
||||
* A [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList) or Array of
|
||||
[HTMLElement](https://developer.mozilla.org/en/docs/Web/API/HTMLElement) - the first element will be used
|
||||
* A [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList) or Array of [HTMLElement](https://developer.mozilla.org/en/docs/Web/API/HTMLElement) -
|
||||
the first element will be used
|
||||
* A [jQuery](https://jquery.com) object - if multiple are passed, the first element will be used
|
||||
|
||||
Here's some examples
|
||||
@ -221,8 +211,8 @@ Passing a [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList):
|
||||
const player = new Plyr(document.querySelectorAll('.js-player'));
|
||||
```
|
||||
|
||||
The NodeList, HTMLElement or string selector can be the target `<video>`, `<audio>` or `[data-plyr-provider]` (for
|
||||
embeds) element itself or a container element.
|
||||
The NodeList, HTMLElement or string selector can be the target `<video>`, `<audio>` or `[data-plyr-provider]` (for embeds) element itself or a container
|
||||
element.
|
||||
|
||||
The second argument for the constructor is the [#options](options) object:
|
||||
|
||||
@ -232,20 +222,17 @@ const player = new Plyr('#player', {
|
||||
});
|
||||
```
|
||||
|
||||
The constructor will return a Plyr object that can be used with the [API](#api) methods. See the [API](#api) section for
|
||||
more info.
|
||||
The constructor will return a Plyr object that can be used with the [API](#api) methods. See the [API](#api) section for more info.
|
||||
|
||||
#### Options
|
||||
|
||||
Options can be passed as an object to the constructor as above or as JSON in `data-plyr-config` attribute on each of
|
||||
your target elements:
|
||||
Options can be passed as an object to the constructor as above or as JSON in `data-plyr-config` attribute on each of your target elements:
|
||||
|
||||
```html
|
||||
<video src="/path/to/video.mp4" id="player" controls data-plyr-config='{ "title": "This is an example video", "volume": 1, "debug": true }'></video>
|
||||
```
|
||||
|
||||
Note the single quotes encapsulating the JSON and double quotes on the object keys. Only string values need double
|
||||
quotes.
|
||||
Note the single quotes encapsulating the JSON and double quotes on the object keys. Only string values need double quotes.
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
| -------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
@ -259,7 +246,7 @@ quotes.
|
||||
| `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 | `false` | Only allow one player playing at once. |
|
||||
| `autopause`¹ | Boolean | `true` | Only allow one player playing at once. |
|
||||
| `seekTime` | Number | `10` | The time, in seconds, to seek when a user hits fast forward or rewind. |
|
||||
| `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. |
|
||||
@ -290,8 +277,7 @@ There are methods, setters and getters on a Plyr object.
|
||||
|
||||
### Object
|
||||
|
||||
The easiest way to access the Plyr object is to set the return value from your call to the constructor to a variable.
|
||||
For example:
|
||||
The easiest way to access the Plyr object is to set the return value from your call to the constructor to a variable. For example:
|
||||
|
||||
```javascript
|
||||
const player = new Plyr('#player', {
|
||||
@ -440,7 +426,7 @@ player.source = {
|
||||
sources: [
|
||||
{
|
||||
src: 'bTqVqk7FSmY',
|
||||
type: 'youtube',
|
||||
provider: 'youtube',
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -456,7 +442,7 @@ player.source = {
|
||||
sources: [
|
||||
{
|
||||
src: '143418951',
|
||||
type: 'vimeo',
|
||||
provider: 'vimeo',
|
||||
},
|
||||
],
|
||||
};
|
||||
@ -476,10 +462,9 @@ _Note:_ `src` property for YouTube and Vimeo can either be the video ID or the w
|
||||
|
||||
## Events
|
||||
|
||||
You can listen for events on the target element you setup Plyr on (see example under the table). Some events only apply
|
||||
to HTML5 audio and video. Using your reference to the instance, you can use the `on()` API method or
|
||||
`addEventListener()`. Access to the API can be obtained this way through the `event.detail.plyr` property. Here's an
|
||||
example:
|
||||
You can listen for events on the target element you setup Plyr on (see example under the table). Some events only apply to HTML5 audio and video. Using your
|
||||
reference to the instance, you can use the `on()` API method or `addEventListener()`. Access to the API can be obtained this way through the `event.detail.plyr`
|
||||
property. Here's an example:
|
||||
|
||||
```javascript
|
||||
player.on('ready', event => {
|
||||
@ -539,10 +524,9 @@ Some event details borrowed from [MDN](https://developer.mozilla.org/en-US/docs/
|
||||
|
||||
## Embeds
|
||||
|
||||
YouTube and Vimeo are currently supported and function much like a HTML5 video. Similar events and API methods are
|
||||
available for all types. However if you wish to access the API's directly. You can do so via the `embed` property of
|
||||
your player object - e.g. `player.embed`. You can then use the relevant methods from the third party APIs. More info on
|
||||
the respective API's here:
|
||||
YouTube and Vimeo are currently supported and function much like a HTML5 video. Similar events and API methods are available for all types. However if you wish
|
||||
to access the API's directly. You can do so via the `embed` property of your player object - e.g. `player.embed`. You can then use the relevant methods from the
|
||||
third party APIs. More info on the respective API's here:
|
||||
|
||||
* [YouTube iframe API Reference](https://developers.google.com/youtube/iframe_api_reference)
|
||||
* [Vimeo player.js Reference](https://github.com/vimeo/player.js)
|
||||
@ -551,9 +535,8 @@ _Note_: Not all API methods may work 100%. Your mileage may vary. It's better to
|
||||
|
||||
## Shortcuts
|
||||
|
||||
By default, a player will bind the following keyboard shortcuts when it has focus. If you have the `global` option to
|
||||
`true` and there's only one player in the document then the shortcuts will work when any element has focus, apart from
|
||||
an element that requires input.
|
||||
By default, a player will bind the following keyboard shortcuts when it has focus. If you have the `global` option to `true` and there's only one player in the
|
||||
document then the shortcuts will work when any element has focus, apart from an element that requires input.
|
||||
|
||||
| Key | Action |
|
||||
| ---------- | -------------------------------------- |
|
||||
@ -571,9 +554,8 @@ an element that requires input.
|
||||
|
||||
## Streaming
|
||||
|
||||
Because Plyr is an extension of the standard HTML5 video and audio elements, third party streaming plugins can be used
|
||||
with Plyr. Massive thanks to Matias Russitto ([@russitto](https://github.com/russitto)) for working on this. Here's a
|
||||
few examples:
|
||||
Because Plyr is an extension of the standard HTML5 video and audio elements, third party streaming plugins can be used with Plyr. Massive thanks to Matias
|
||||
Russitto ([@russitto](https://github.com/russitto)) for working on this. Here's a few examples:
|
||||
|
||||
* Using [hls.js](https://github.com/dailymotion/hls.js) - [Demo](http://codepen.io/sampotts/pen/JKEMqB)
|
||||
* Using [Shaka](https://github.com/google/shaka-player) - [Demo](http://codepen.io/sampotts/pen/zBNpVR)
|
||||
@ -598,13 +580,11 @@ Plyr supports the last 2 versions of most _modern_ browsers. IE11 is also suppor
|
||||
| IE10+ | ✔² |
|
||||
| IE9 | API only³ |
|
||||
|
||||
1. Mobile Safari on the iPhone forces the native player for `<video>` unless the `playsinline` attribute is present.
|
||||
Volume controls are also disabled.
|
||||
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))
|
||||
|
||||
The `enabled` option can be used to disable certain User Agents. For example, if you don't want to use Plyr for
|
||||
smartphones, you could use:
|
||||
The `enabled` option can be used to disable certain User Agents. For example, if you don't want to use Plyr for smartphones, you could use:
|
||||
|
||||
```javascript
|
||||
{
|
||||
@ -616,10 +596,9 @@ If a User Agent is disabled but supports `<video>` and `<audio>` natively, it wi
|
||||
|
||||
## RangeTouch
|
||||
|
||||
Some touch browsers (particularly Mobile Safari on iOS) seem to have issues with `<input type="range">` elements whereby
|
||||
touching the track to set the value doesn't work and sliding the thumb can be tricky. To combat this, I've created
|
||||
[RangeTouch](https://rangetouch.com) which I'd recommend including in your solution. It's a tiny script with a nice
|
||||
benefit for users on touch devices.
|
||||
Some touch browsers (particularly Mobile Safari on iOS) seem to have issues with `<input type="range">` elements whereby touching the track to set the value
|
||||
doesn't work and sliding the thumb can be tricky. To combat this, I've created [RangeTouch](https://rangetouch.com) which I'd recommend including in your
|
||||
solution. It's a tiny script with a nice benefit for users on touch devices.
|
||||
|
||||
## Issues
|
||||
|
||||
@ -627,13 +606,13 @@ If you find anything weird with Plyr, please let us know using the GitHub issues
|
||||
|
||||
## Author
|
||||
|
||||
Plyr is developed by [@sam_potts](https://twitter.com/sam_potts) / [sampotts.me](http://sampotts.me) with help from the
|
||||
awesome [contributors](https://github.com/sampotts/plyr/graphs/contributors)
|
||||
Plyr is developed by [@sam_potts](https://twitter.com/sam_potts) / [sampotts.me](http://sampotts.me) with help from the awesome
|
||||
[contributors](https://github.com/sampotts/plyr/graphs/contributors)
|
||||
|
||||
## Donate
|
||||
|
||||
Plyr costs money to run, not only my time - I donate that for free but domains, hosting and more. Any help is
|
||||
appreciated... [Donate to support Plyr](https://www.paypal.me/pottsy/20usd)
|
||||
Plyr costs money to run, not only my time - I donate that for free but domains, hosting and more. Any help is appreciated...
|
||||
[Donate to support Plyr](https://www.paypal.me/pottsy/20usd)
|
||||
|
||||
## Mentions
|
||||
|
||||
@ -660,16 +639,14 @@ appreciated... [Donate to support Plyr](https://www.paypal.me/pottsy/20usd)
|
||||
* [Oscar Radio](http://oscar-radio.xyz/)
|
||||
* [Sparkk TV](https://www.sparkktv.com/)
|
||||
|
||||
Let me know on [Twitter](https://twitter.com/sam_potts) I can add you to the above list. It'd be awesome to see how
|
||||
you're using Plyr :-)
|
||||
Let me know on [Twitter](https://twitter.com/sam_potts) I can add you to the above list. It'd be awesome to see how you're using Plyr :-)
|
||||
|
||||
## Useful links and credits
|
||||
|
||||
Credit to the PayPal HTML5 Video player from which Plyr's caption functionality was originally ported from:
|
||||
|
||||
* [PayPal's Accessible HTML5 Video Player](https://github.com/paypal/accessible-html5-video-player)
|
||||
* [An awesome guide for Plyr in Japanese!](http://syncer.jp/how-to-use-plyr-io) by
|
||||
[@arayutw](https://twitter.com/arayutw)
|
||||
* [An awesome guide for Plyr in Japanese!](http://syncer.jp/how-to-use-plyr-io) by [@arayutw](https://twitter.com/arayutw)
|
||||
|
||||
## Thanks
|
||||
|
||||
|
@ -32,7 +32,7 @@ const captions = {
|
||||
}
|
||||
|
||||
// Only Vimeo and HTML5 video supported at this point
|
||||
if (!['video', 'vimeo'].includes(this.type) || (this.type === 'video' && !support.textTracks)) {
|
||||
if (!this.isVideo || this.isYouTube || (this.isVideo && !support.textTracks)) {
|
||||
// Clear menu and hide
|
||||
if (this.config.controls.includes('settings') && this.config.settings.includes('captions')) {
|
||||
controls.setCaptionsMenu.call(this);
|
||||
@ -71,7 +71,7 @@ const captions = {
|
||||
// Set the captions language
|
||||
setLanguage() {
|
||||
// Setup HTML5 track rendering
|
||||
if (this.type === 'video') {
|
||||
if (this.isVideo) {
|
||||
captions.getTracks.call(this).forEach(track => {
|
||||
// Remove previous bindings
|
||||
utils.on(track, 'cuechange', event => captions.setCue.call(this, event));
|
||||
@ -91,7 +91,7 @@ const captions = {
|
||||
captions.setCue.call(this, currentTrack);
|
||||
}
|
||||
}
|
||||
} else if (this.type === 'vimeo' && this.captions.active) {
|
||||
} else if (this.isVimeo && this.captions.active) {
|
||||
this.embed.enableTextTrack(this.language);
|
||||
}
|
||||
},
|
||||
|
2
src/js/controls.js
vendored
2
src/js/controls.js
vendored
@ -445,7 +445,7 @@ const controls = {
|
||||
}
|
||||
|
||||
// Toggle the pane and tab
|
||||
const toggle = !utils.is.empty(this.options.quality) && this.type === 'youtube';
|
||||
const toggle = !utils.is.empty(this.options.quality) && this.isYouTube;
|
||||
controls.toggleTab.call(this, type, toggle);
|
||||
|
||||
// If we're hiding, nothing more to do
|
||||
|
@ -13,7 +13,7 @@ const defaults = {
|
||||
autoplay: false,
|
||||
|
||||
// Only allow one media playing at once (vimeo only)
|
||||
autopause: false,
|
||||
autopause: true,
|
||||
|
||||
// Default time to skip when rewind/fast forward
|
||||
seekTime: 10,
|
||||
@ -267,6 +267,7 @@ const defaults = {
|
||||
embed: 'plyr__video-embed',
|
||||
control: 'plyr__control',
|
||||
type: 'plyr--{0}',
|
||||
provider: 'plyr--{0}',
|
||||
stopped: 'plyr--stopped',
|
||||
playing: 'plyr--playing',
|
||||
loading: 'plyr--loading',
|
||||
|
@ -92,7 +92,7 @@ const fullscreen = {
|
||||
|
||||
// Setup fullscreen
|
||||
setup() {
|
||||
if (!this.supported.ui || this.type === 'audio' || !this.config.fullscreen.enabled) {
|
||||
if (!this.supported.ui || this.isAudio || !this.config.fullscreen.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -221,7 +221,7 @@ const listeners = {
|
||||
// Handle the media finishing
|
||||
utils.on(this.media, 'ended', () => {
|
||||
// Show poster on end
|
||||
if (this.type === 'video' && this.config.showPosterOnEnd) {
|
||||
if (this.isHTML5 && this.isVideo && this.config.showPosterOnEnd) {
|
||||
// Restart
|
||||
this.restart();
|
||||
|
||||
@ -243,7 +243,7 @@ const listeners = {
|
||||
utils.on(this.media, 'stalled waiting canplay seeked playing', event => ui.checkLoading.call(this, event));
|
||||
|
||||
// Click video
|
||||
if (this.supported.ui && this.config.clickToPlay && this.type !== 'audio') {
|
||||
if (this.supported.ui && this.config.clickToPlay && !this.isAudio) {
|
||||
// Re-fetch the wrapper
|
||||
const wrapper = utils.getElement.call(this, `.${this.config.classNames.video}`);
|
||||
|
||||
|
@ -23,6 +23,9 @@ const media = {
|
||||
// Add type class
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.type.replace('{0}', this.type), true);
|
||||
|
||||
// Add provider class
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.provider.replace('{0}', this.provider), true);
|
||||
|
||||
// Add video class for embeds
|
||||
// This will require changes if audio embeds are added
|
||||
if (this.isEmbed) {
|
||||
@ -31,7 +34,7 @@ const media = {
|
||||
|
||||
if (this.supported.ui) {
|
||||
// Check for picture-in-picture support
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.type === 'video');
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.pip.supported, support.pip && this.isHTML5 && this.isVideo);
|
||||
|
||||
// Check for airplay support
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.airplay.supported, support.airplay && this.isHTML5);
|
||||
@ -47,7 +50,7 @@ const media = {
|
||||
}
|
||||
|
||||
// Inject the player wrapper
|
||||
if (['video', 'youtube', 'vimeo'].includes(this.type)) {
|
||||
if (this.isVideo || this.isYouTube || this.isVimeo) {
|
||||
// Create the wrapper div
|
||||
this.elements.wrapper = utils.createElement('div', {
|
||||
class: this.config.classNames.video,
|
||||
@ -58,7 +61,7 @@ const media = {
|
||||
}
|
||||
|
||||
if (this.isEmbed) {
|
||||
switch (this.type) {
|
||||
switch (this.provider) {
|
||||
case 'youtube':
|
||||
youtube.setup.call(this);
|
||||
break;
|
||||
|
@ -4,13 +4,12 @@
|
||||
|
||||
import utils from './../utils';
|
||||
import captions from './../captions';
|
||||
import controls from './../controls';
|
||||
import ui from './../ui';
|
||||
|
||||
const vimeo = {
|
||||
setup() {
|
||||
// Remove old containers
|
||||
const containers = utils.getElements.call(this, `[id^="${this.type}-"]`);
|
||||
const containers = utils.getElements.call(this, `[id^="${this.provider}-"]`);
|
||||
Array.from(containers).forEach(utils.removeElement);
|
||||
|
||||
// Add embed class for responsive
|
||||
@ -20,7 +19,7 @@ const vimeo = {
|
||||
vimeo.setAspectRatio.call(this);
|
||||
|
||||
// Set ID
|
||||
this.media.setAttribute('id', utils.generateId(this.type));
|
||||
this.media.setAttribute('id', utils.generateId(this.provider));
|
||||
|
||||
// Load the API if not already
|
||||
if (!utils.is.object(window.Vimeo)) {
|
||||
|
@ -11,7 +11,7 @@ const youtube = {
|
||||
const videoId = utils.parseYouTubeId(this.embedId);
|
||||
|
||||
// Remove old containers
|
||||
const containers = utils.getElements.call(this, `[id^="${this.type}-"]`);
|
||||
const containers = utils.getElements.call(this, `[id^="${this.provider}-"]`);
|
||||
Array.from(containers).forEach(utils.removeElement);
|
||||
|
||||
// Add embed class for responsive
|
||||
@ -21,7 +21,7 @@ const youtube = {
|
||||
youtube.setAspectRatio.call(this);
|
||||
|
||||
// Set ID
|
||||
this.media.setAttribute('id', utils.generateId(this.type));
|
||||
this.media.setAttribute('id', utils.generateId(this.provider));
|
||||
|
||||
// Setup API
|
||||
if (utils.is.object(window.YT)) {
|
||||
@ -31,6 +31,7 @@ const youtube = {
|
||||
utils.loadScript(this.config.urls.youtube.api);
|
||||
|
||||
// Setup callback for the API
|
||||
// YouTube has it's own system of course...
|
||||
window.onYouTubeReadyCallbacks = window.onYouTubeReadyCallbacks || [];
|
||||
|
||||
// Add to queue
|
||||
|
101
src/js/plyr.js
101
src/js/plyr.js
@ -5,8 +5,8 @@
|
||||
// License: The MIT License (MIT)
|
||||
// ==========================================================================
|
||||
|
||||
import { providers, types } from './types';
|
||||
import defaults from './defaults';
|
||||
import types from './types';
|
||||
import support from './support';
|
||||
import utils from './utils';
|
||||
|
||||
@ -40,11 +40,7 @@ class Plyr {
|
||||
}
|
||||
|
||||
// jQuery, NodeList or Array passed, use first element
|
||||
if (
|
||||
(window.jQuery && this.media instanceof jQuery) ||
|
||||
utils.is.nodeList(this.media) ||
|
||||
utils.is.array(this.media)
|
||||
) {
|
||||
if ((window.jQuery && this.media instanceof jQuery) || utils.is.nodeList(this.media) || utils.is.array(this.media)) {
|
||||
// eslint-disable-next-line
|
||||
this.media = this.media[0];
|
||||
}
|
||||
@ -149,7 +145,7 @@ class Plyr {
|
||||
// Embed attributes
|
||||
const attributes = {
|
||||
provider: 'data-plyr-provider',
|
||||
id: 'data-plyr-provider-id',
|
||||
id: 'data-plyr-embed-id',
|
||||
};
|
||||
|
||||
// Different setup based on type
|
||||
@ -157,16 +153,18 @@ class Plyr {
|
||||
// TODO: Handle passing an iframe for true progressive enhancement
|
||||
// case 'iframe':
|
||||
case 'div':
|
||||
this.type = this.media.getAttribute(attributes.provider);
|
||||
this.type = types.video; // Audio will come later for external providers
|
||||
this.provider = this.media.getAttribute(attributes.provider);
|
||||
this.embedId = this.media.getAttribute(attributes.id);
|
||||
|
||||
if (utils.is.empty(this.type)) {
|
||||
this.console.error('Setup failed: embed type missing');
|
||||
if (utils.is.empty(this.provider) || !Object.keys(providers).includes(this.provider)) {
|
||||
this.console.error('Setup failed: Invalid provider');
|
||||
return;
|
||||
}
|
||||
|
||||
// Try and get the embed id
|
||||
if (utils.is.empty(this.embedId)) {
|
||||
this.console.error('Setup failed: video id missing');
|
||||
this.console.error('Setup failed: Embed ID or URL missing');
|
||||
return;
|
||||
}
|
||||
|
||||
@ -179,19 +177,24 @@ class Plyr {
|
||||
case 'video':
|
||||
case 'audio':
|
||||
this.type = type;
|
||||
this.provider = providers.html5;
|
||||
|
||||
if (this.media.hasAttribute('crossorigin')) {
|
||||
this.config.crossorigin = true;
|
||||
}
|
||||
|
||||
if (this.media.hasAttribute('autoplay')) {
|
||||
this.config.autoplay = true;
|
||||
}
|
||||
|
||||
if (this.media.hasAttribute('playsinline')) {
|
||||
this.config.inline = true;
|
||||
}
|
||||
|
||||
if (this.media.hasAttribute('muted')) {
|
||||
this.config.muted = true;
|
||||
}
|
||||
|
||||
if (this.media.hasAttribute('loop')) {
|
||||
this.config.loop.active = true;
|
||||
}
|
||||
@ -207,7 +210,7 @@ class Plyr {
|
||||
storage.setup.call(this);
|
||||
|
||||
// Check for support again but with type
|
||||
this.supported = support.check(this.type, this.config.inline);
|
||||
this.supported = support.check(this.type, this.provider, this.config.inline);
|
||||
|
||||
// If no support for even API, bail
|
||||
if (!this.supported.api) {
|
||||
@ -253,17 +256,25 @@ class Plyr {
|
||||
// ---------------------------------------
|
||||
|
||||
/**
|
||||
* If the player is HTML5
|
||||
* Types and provider helpers
|
||||
*/
|
||||
get isHTML5() {
|
||||
return types.html5.includes(this.type);
|
||||
return this.provider === providers.html5;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the player is an embed - e.g. YouTube or Vimeo
|
||||
*/
|
||||
get isEmbed() {
|
||||
return types.embed.includes(this.type);
|
||||
return this.isYouTube || this.isVimeo;
|
||||
}
|
||||
get isYouTube() {
|
||||
return this.provider === providers.youtube;
|
||||
}
|
||||
get isVimeo() {
|
||||
return this.provider === providers.vimeo;
|
||||
}
|
||||
get isVideo() {
|
||||
return this.type === types.video;
|
||||
}
|
||||
get isAudio() {
|
||||
return this.type === types.audio;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -518,11 +529,7 @@ class Plyr {
|
||||
}
|
||||
|
||||
// Get audio tracks
|
||||
return (
|
||||
this.media.mozHasAudio ||
|
||||
Boolean(this.media.webkitAudioDecodedByteCount) ||
|
||||
Boolean(this.media.audioTracks && this.media.audioTracks.length)
|
||||
);
|
||||
return this.media.mozHasAudio || Boolean(this.media.webkitAudioDecodedByteCount) || Boolean(this.media.audioTracks && this.media.audioTracks.length);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -683,7 +690,7 @@ class Plyr {
|
||||
* @param {input} - the URL for the new poster image
|
||||
*/
|
||||
set poster(input) {
|
||||
if (!this.isHTML5 || this.type !== 'video') {
|
||||
if (!this.isHTML5 || !this.isVideo) {
|
||||
this.console.warn('Poster can only be set on HTML5 video');
|
||||
return;
|
||||
}
|
||||
@ -697,7 +704,7 @@ class Plyr {
|
||||
* Get the current poster image
|
||||
*/
|
||||
get poster() {
|
||||
if (!this.isHTML5 || this.type !== 'video') {
|
||||
if (!this.isHTML5 || !this.isVideo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -731,9 +738,7 @@ class Plyr {
|
||||
}
|
||||
|
||||
// If the method is called without parameter, toggle based on current value
|
||||
const show = utils.is.boolean(input)
|
||||
? input
|
||||
: this.elements.container.className.indexOf(this.config.classNames.captions.active) === -1;
|
||||
const show = utils.is.boolean(input) ? input : this.elements.container.className.indexOf(this.config.classNames.captions.active) === -1;
|
||||
|
||||
// Nothing to change...
|
||||
if (this.captions.enabled === show) {
|
||||
@ -828,11 +833,7 @@ class Plyr {
|
||||
this.fullscreen.active = !this.fullscreen.active;
|
||||
|
||||
// Add class hook
|
||||
utils.toggleClass(
|
||||
this.elements.container,
|
||||
this.config.classNames.fullscreen.fallback,
|
||||
this.fullscreen.active
|
||||
);
|
||||
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) {
|
||||
@ -920,7 +921,7 @@ class Plyr {
|
||||
}
|
||||
|
||||
// Don't hide if no UI support or it's audio
|
||||
if (!this.supported.ui || this.type === 'audio') {
|
||||
if (!this.supported.ui || this.isAudio) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -980,13 +981,13 @@ class Plyr {
|
||||
// then set the timer to hide the controls
|
||||
if (!show || this.playing) {
|
||||
this.timers.controls = window.setTimeout(() => {
|
||||
console.warn({
|
||||
/* this.console.warn({
|
||||
pressed: this.elements.controls.pressed,
|
||||
hover: this.elements.controls.pressed,
|
||||
playing: this.playing,
|
||||
paused: this.paused,
|
||||
loading: this.loading,
|
||||
});
|
||||
}); */
|
||||
|
||||
// If the mouse is over the controls (and not entering fullscreen), bail
|
||||
if ((this.elements.controls.pressed || this.elements.controls.hover) && !isEnterFullscreen) {
|
||||
@ -1105,8 +1106,18 @@ class Plyr {
|
||||
};
|
||||
|
||||
// Type specific stuff
|
||||
switch (this.type) {
|
||||
case 'youtube':
|
||||
switch (`${this.provider}:${this.type}`) {
|
||||
case 'html5:video':
|
||||
case 'html5:audio':
|
||||
// Restore native video controls
|
||||
ui.toggleNativeControls.call(this, true);
|
||||
|
||||
// Clean up
|
||||
done();
|
||||
|
||||
break;
|
||||
|
||||
case 'youtube:video':
|
||||
// Clear timers
|
||||
window.clearInterval(this.timers.buffering);
|
||||
window.clearInterval(this.timers.playing);
|
||||
@ -1119,7 +1130,7 @@ class Plyr {
|
||||
|
||||
break;
|
||||
|
||||
case 'vimeo':
|
||||
case 'vimeo:video':
|
||||
// Destroy Vimeo API
|
||||
// then clean up (wait, to prevent postmessage errors)
|
||||
this.embed.unload().then(done);
|
||||
@ -1129,16 +1140,6 @@ class Plyr {
|
||||
|
||||
break;
|
||||
|
||||
case 'video':
|
||||
case 'audio':
|
||||
// Restore native video controls
|
||||
ui.toggleNativeControls.call(this, true);
|
||||
|
||||
// Clean up
|
||||
done();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Plyr source update
|
||||
// ==========================================================================
|
||||
|
||||
import types from './types';
|
||||
import { providers } from './types';
|
||||
import utils from './utils';
|
||||
import media from './media';
|
||||
import ui from './ui';
|
||||
@ -48,35 +48,25 @@ const source = {
|
||||
this.elements.container.removeAttribute('class');
|
||||
}
|
||||
|
||||
// Set the type
|
||||
if ('type' in input) {
|
||||
// Set the type and provider
|
||||
this.type = input.type;
|
||||
|
||||
// Get child type for video (it might be an embed)
|
||||
if (this.type === 'video') {
|
||||
const firstSource = input.sources[0];
|
||||
|
||||
if ('type' in firstSource && types.embed.includes(firstSource.type)) {
|
||||
this.type = firstSource.type;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.provider = !utils.is.empty(input.sources[0].provider) ? input.sources[0].provider : providers.html5;
|
||||
|
||||
// Check for support
|
||||
this.supported = support.check(this.type, this.config.inline);
|
||||
this.supported = support.check(this.type, this.provider, this.config.inline);
|
||||
|
||||
// Create new markup
|
||||
switch (this.type) {
|
||||
case 'video':
|
||||
switch (`${this.provider}:${this.type}`) {
|
||||
case 'html5:video':
|
||||
this.media = utils.createElement('video');
|
||||
break;
|
||||
|
||||
case 'audio':
|
||||
case 'html5:audio':
|
||||
this.media = utils.createElement('audio');
|
||||
break;
|
||||
|
||||
case 'youtube':
|
||||
case 'vimeo':
|
||||
case 'youtube:video':
|
||||
case 'vimeo:video':
|
||||
this.media = utils.createElement('div');
|
||||
this.embedId = input.sources[0].src;
|
||||
break;
|
||||
@ -117,7 +107,6 @@ const source = {
|
||||
|
||||
// Restore class hooks
|
||||
utils.toggleClass(this.elements.container, this.config.classNames.captions.active, this.supported.ui && this.captions.enabled);
|
||||
|
||||
ui.addStyleHook.call(this);
|
||||
|
||||
// Set new sources for html5
|
||||
|
@ -12,29 +12,29 @@ const support = {
|
||||
|
||||
// Check for support
|
||||
// Basic functionality vs full UI
|
||||
check(type, inline) {
|
||||
check(type, provider, inline) {
|
||||
let api = false;
|
||||
let ui = false;
|
||||
const browser = utils.getBrowser();
|
||||
const playsInline = browser.isIPhone && inline && support.inline;
|
||||
|
||||
switch (type) {
|
||||
case 'video':
|
||||
switch (`${provider}:${type}`) {
|
||||
case 'html5:video':
|
||||
api = support.video;
|
||||
ui = api && support.rangeInput && (!browser.isIPhone || playsInline);
|
||||
break;
|
||||
|
||||
case 'audio':
|
||||
case 'html5:audio':
|
||||
api = support.audio;
|
||||
ui = api && support.rangeInput;
|
||||
break;
|
||||
|
||||
case 'youtube':
|
||||
case 'youtube:video':
|
||||
api = true;
|
||||
ui = support.rangeInput && (!browser.isIPhone || playsInline);
|
||||
break;
|
||||
|
||||
case 'vimeo':
|
||||
case 'vimeo:video':
|
||||
api = true;
|
||||
ui = support.rangeInput && !browser.isIPhone;
|
||||
break;
|
||||
@ -92,12 +92,12 @@ const support = {
|
||||
|
||||
try {
|
||||
// Bail if no checking function
|
||||
if (!utils.is.function(media.canPlayType)) {
|
||||
if (!this.isHTML5 || !utils.is.function(media.canPlayType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Type specific checks
|
||||
if (this.type === 'video') {
|
||||
if (this.isVideo) {
|
||||
switch (type) {
|
||||
case 'video/webm':
|
||||
return media.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/, '');
|
||||
@ -111,7 +111,7 @@ const support = {
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (this.type === 'audio') {
|
||||
} else if (this.isAudio) {
|
||||
switch (type) {
|
||||
case 'audio/mpeg':
|
||||
return media.canPlayType('audio/mpeg;').replace(/no/, '');
|
||||
|
@ -1,10 +1,16 @@
|
||||
// ==========================================================================
|
||||
// Plyr supported types
|
||||
// Plyr supported types and providers
|
||||
// ==========================================================================
|
||||
|
||||
const types = {
|
||||
embed: ['youtube', 'vimeo'],
|
||||
html5: ['video', 'audio'],
|
||||
export const providers = {
|
||||
html5: 'html5',
|
||||
youtube: 'youtube',
|
||||
vimeo: 'vimeo',
|
||||
};
|
||||
|
||||
export default types;
|
||||
export const types = {
|
||||
audio: 'audio',
|
||||
video: 'video',
|
||||
};
|
||||
|
||||
export default { providers, types };
|
||||
|
@ -31,7 +31,7 @@ const ui = {
|
||||
|
||||
// Don't setup interface if no support
|
||||
if (!this.supported.ui) {
|
||||
this.console.warn(`Basic support only for ${this.type}`);
|
||||
this.console.warn(`Basic support only for ${this.provider} ${this.type}`);
|
||||
|
||||
// Remove controls
|
||||
utils.removeElement.call(this, 'controls');
|
||||
|
@ -73,24 +73,39 @@ const utils = {
|
||||
|
||||
// Load an external script
|
||||
loadScript(url, callback) {
|
||||
// Check script is not already referenced
|
||||
if (document.querySelectorAll(`script[src="${url}"]`).length) {
|
||||
const current = document.querySelector(`script[src="${url}"]`);
|
||||
|
||||
// Check script is not already referenced, if so wait for load
|
||||
if (current !== null) {
|
||||
current.callbacks = current.callbacks || [];
|
||||
current.callbacks.push(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the element
|
||||
const element = document.createElement('script');
|
||||
element.src = url;
|
||||
|
||||
// Find first script
|
||||
const first = document.getElementsByTagName('script')[0];
|
||||
// Callback queue
|
||||
element.callbacks = element.callbacks || [];
|
||||
element.callbacks.push(callback);
|
||||
|
||||
// Bind callback
|
||||
if (utils.is.function(callback)) {
|
||||
element.addEventListener('load', event => callback.call(null, event), false);
|
||||
element.addEventListener(
|
||||
'load',
|
||||
event => {
|
||||
element.callbacks.forEach(cb => cb.call(null, event));
|
||||
element.callbacks = null;
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
// Set the URL after binding callback
|
||||
element.src = url;
|
||||
|
||||
// Inject
|
||||
const first = document.getElementsByTagName('script')[0];
|
||||
first.parentNode.insertBefore(element, first);
|
||||
},
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user