Merge branch 'beta' into gulp-unminified-js-output
This commit is contained in:
commit
7ac732f45b
2
demo/dist/demo.css
vendored
2
demo/dist/demo.css
vendored
File diff suppressed because one or more lines are too long
@ -10,7 +10,6 @@
|
|||||||
<link rel="stylesheet" href="dist/error.css">
|
<link rel="stylesheet" href="dist/error.css">
|
||||||
|
|
||||||
<!-- Preload -->
|
<!-- Preload -->
|
||||||
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-light.woff2">
|
|
||||||
<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-medium.woff2">
|
||||||
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-bold.woff2">
|
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-bold.woff2">
|
||||||
</head>
|
</head>
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
<link rel="stylesheet" href="dist/demo.css">
|
<link rel="stylesheet" href="dist/demo.css">
|
||||||
|
|
||||||
<!-- Preload -->
|
<!-- Preload -->
|
||||||
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-light.woff2">
|
|
||||||
<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-medium.woff2">
|
||||||
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-bold.woff2">
|
<link rel="preload" as="font" crossorigin type="font/woff2" href="https://cdn.plyr.io/static/fonts/gordita-bold.woff2">
|
||||||
</head>
|
</head>
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
.button,
|
.button,
|
||||||
.button__count {
|
.button__count {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: #fff;
|
background: $color-button-background;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: $border-radius-base;
|
border-radius: $border-radius-base;
|
||||||
box-shadow: 0 1px 1px rgba(#000, 0.1);
|
box-shadow: 0 1px 1px rgba(#000, 0.1);
|
||||||
color: $gray;
|
color: $color-button-text;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
padding: ($spacing-base * 0.75);
|
padding: ($spacing-base * 0.75);
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -71,7 +71,7 @@
|
|||||||
&::before {
|
&::before {
|
||||||
border: $arrow-size solid transparent;
|
border: $arrow-size solid transparent;
|
||||||
border-left-width: 0;
|
border-left-width: 0;
|
||||||
border-right-color: #fff;
|
border-right-color: $color-button-background;
|
||||||
content: '';
|
content: '';
|
||||||
height: 0;
|
height: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -28,6 +28,7 @@ body {
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
padding-bottom: 1px; // Collapsing margins
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,5 +24,9 @@ $color-vimeo: #19b7ed;
|
|||||||
$color-link: #fff;
|
$color-link: #fff;
|
||||||
$color-background: $color-brand-primary;
|
$color-background: $color-brand-primary;
|
||||||
|
|
||||||
|
// Buttons
|
||||||
|
$color-button-background: #fff;
|
||||||
|
$color-button-text: $gray;
|
||||||
|
|
||||||
// Focus
|
// Focus
|
||||||
$tab-focus-default-color: #fff;
|
$tab-focus-default-color: #fff;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
@include font-size($font-size-h1);
|
@include font-size($font-size-h1);
|
||||||
font-weight: $font-weight-light;
|
font-weight: $font-weight-bold;
|
||||||
letter-spacing: $letter-spacing-headings;
|
letter-spacing: $letter-spacing-headings;
|
||||||
margin: 0 0 ($spacing-base / 2);
|
margin: 0 0 ($spacing-base / 2);
|
||||||
}
|
}
|
||||||
|
2
dist/plyr.css
vendored
2
dist/plyr.css
vendored
File diff suppressed because one or more lines are too long
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
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "plyr",
|
"name": "plyr",
|
||||||
"version": "3.0.0-beta.12",
|
"version": "3.0.0-beta.13",
|
||||||
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
|
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
|
||||||
"homepage": "https://plyr.io",
|
"homepage": "https://plyr.io",
|
||||||
"main": "./dist/plyr.js",
|
"main": "./dist/plyr.js",
|
||||||
@ -44,7 +44,8 @@
|
|||||||
"stylelint-config-standard": "^18.0.0",
|
"stylelint-config-standard": "^18.0.0",
|
||||||
"stylelint-order": "^0.8.0",
|
"stylelint-order": "^0.8.0",
|
||||||
"stylelint-scss": "^2.3.0",
|
"stylelint-scss": "^2.3.0",
|
||||||
"stylelint-selector-bem-pattern": "^2.0.0"
|
"stylelint-selector-bem-pattern": "^2.0.0",
|
||||||
|
"uglify-es": "^3.3.10"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"HTML5 Video",
|
"HTML5 Video",
|
||||||
|
124
readme.md
124
readme.md
@ -35,35 +35,21 @@ Oh and yes, it works with Bootstrap.
|
|||||||
|
|
||||||
Check out the [changelog](changelog.md) to see what's new with Plyr.
|
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))
|
| Type | Maintainer | Link |
|
||||||
|
| --------- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||||
### [Neos](https://packagist.org/packages/jonnitto/plyr)
|
| 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) |
|
||||||
Created and maintained by Jon Uhlmann ([@jonnitto](https://github.com/jonnitto))
|
| 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](https://github.com/dpschen/kirby-plyrtag)
|
| Kirby | Dominik Pschenitschni ([@dpschen](https://github.com/dpschen)) | [https://github.com/dpschen/kirby-plyrtag](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)
|
|
||||||
|
|
||||||
## Quick setup
|
## 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
|
### HTML
|
||||||
|
|
||||||
@ -142,7 +128,7 @@ 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:
|
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the JavaScript, you can use the following:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="https://cdn.plyr.io/3.0.0-beta.12/plyr.js"></script>
|
<script src="https://cdn.plyr.io/3.0.0-beta.13/plyr.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
_Note_: Be sure to read the [polyfills](#polyfills) section below about browser compatibility
|
_Note_: Be sure to read the [polyfills](#polyfills) section below about browser compatibility
|
||||||
@ -158,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:
|
If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for the default CSS, you can use the following:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<link rel="stylesheet" href="https://cdn.plyr.io/3.0.0-beta.12/plyr.css">
|
<link rel="stylesheet" href="https://cdn.plyr.io/3.0.0-beta.13/plyr.css">
|
||||||
```
|
```
|
||||||
|
|
||||||
### SVG Sprite
|
### SVG Sprite
|
||||||
|
|
||||||
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
|
The SVG sprite is loaded automatically from our CDN (provided by [Fastly](https://www.fastly.com/)). To change this, see the [options](#options) below. For
|
||||||
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.0.0-beta.12/plyr.svg`.
|
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.0.0-beta.13/plyr.svg`.
|
||||||
|
|
||||||
## Advanced
|
## Advanced
|
||||||
|
|
||||||
@ -258,10 +244,10 @@ 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.
|
Note the single quotes encapsulating the JSON and double quotes on the object keys. Only string values need double quotes.
|
||||||
|
|
||||||
| Option | Type | Default | Description |
|
| 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. |
|
| `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 |
|
| `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. |
|
| `controls` | Array, Function or Element | `['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen']` | If a function is passed, it is assumed your method will return either an element or HTML string for the controls. Three arguments will be passed to your function; `id` (the unique id for the player), `seektime` (the seektime step in seconds), and `title` (the media title). See [controls.md](controls.md) for more info on how the html needs to be structured. |
|
||||||
| `settings` | Array | `['captions', 'quality', 'speed', 'loop']` | If you're using the default controls are used then you can specify which settings to show in the menu |
|
| `settings` | Array | `['captions', 'quality', 'speed', 'loop']` | If you're using the default controls are used then you can specify which settings to show in the menu |
|
||||||
| `i18n` | Object | See [defaults.js](/src/js/defaults.js) | Used for internationalization (i18n) of the text within the UI. |
|
| `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. |
|
| `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. |
|
||||||
@ -318,14 +304,15 @@ element.addEventListener('ready', event => {
|
|||||||
|
|
||||||
### Methods
|
### Methods
|
||||||
|
|
||||||
Methods are not chainable. An example use of a method:
|
Example method use:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
player.play();
|
player.play(); // Start playback
|
||||||
|
player.fullscreen.enter(); // Enter fullscreen
|
||||||
```
|
```
|
||||||
|
|
||||||
| Method | Parameters | Description |
|
| Method | Parameters | Description |
|
||||||
| ------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------- |
|
| ------------------------ | ---------------- | ---------------------------------------------------------------------------------------------------------- |
|
||||||
| `play()`¹ | - | Start playback. |
|
| `play()`¹ | - | Start playback. |
|
||||||
| `pause()` | - | Pause playback. |
|
| `pause()` | - | Pause playback. |
|
||||||
| `togglePlay(toggle)` | Boolean | Toggle playback, if no parameters are passed, it will toggle based on current status. |
|
| `togglePlay(toggle)` | Boolean | Toggle playback, if no parameters are passed, it will toggle based on current status. |
|
||||||
@ -336,7 +323,9 @@ player.play();
|
|||||||
| `increaseVolume(step)` | Number | Increase volume by the specified step. If no parameter is passed, the default step 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. |
|
| `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. |
|
| `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. |
|
| `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. |
|
| `airplay()` | - | Trigger the airplay dialog on supported devices. |
|
||||||
| `toggleControls(toggle)` | Boolean | Toggle the controls based on the specified boolean. |
|
| `toggleControls(toggle)` | Boolean | Toggle the controls based on the specified boolean. |
|
||||||
| `on(event, function)` | String, Function | Add an event listener for the specified event. |
|
| `on(event, function)` | String, Function | Add an event listener for the specified event. |
|
||||||
@ -348,39 +337,44 @@ player.play();
|
|||||||
|
|
||||||
### Getters and Setters
|
### Getters and Setters
|
||||||
|
|
||||||
An example setter:
|
Example setters:
|
||||||
|
|
||||||
```javascript
|
```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
|
```javascript
|
||||||
player.volume; // returns 0.5;
|
player.volume; // 0.5;
|
||||||
|
player.currentTime; // 10
|
||||||
|
player.fullscreen.active; // false;
|
||||||
```
|
```
|
||||||
|
|
||||||
| Property | Getter | Setter | Description |
|
| Property | Getter | Setter | Description |
|
||||||
| --------------- | ------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| -------------------- | ------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| `isHTML5` | ✔ | - | Returns a boolean indicating if the current player is HTML5. |
|
| `isHTML5` | ✓ | - | Returns a boolean indicating if the current player is HTML5. |
|
||||||
| `isEmbed` | ✔ | - | Returns a boolean indicating if the current player is an embedded player. |
|
| `isEmbed` | ✓ | - | Returns a boolean indicating if the current player is an embedded player. |
|
||||||
| `paused` | ✔ | - | Returns a boolean indicating if the current player is paused. |
|
| `paused` | ✓ | - | Returns a boolean indicating if the current player is paused. |
|
||||||
| `playing` | ✔ | - | Returns a boolean indicating if the current player is playing. |
|
| `playing` | ✓ | - | Returns a boolean indicating if the current player is playing. |
|
||||||
| `ended` | ✔ | - | Returns a boolean indicating if the current player has finished playback. |
|
| `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. |
|
| `currentTime` | ✓ | ✓ | Gets or sets the currentTime for the player. The setter accepts a float in seconds. |
|
||||||
| `seeking` | ✔ | - | Returns a boolean indicating if the current player is seeking. |
|
| `seeking` | ✓ | - | Returns a boolean indicating if the current player is seeking. |
|
||||||
| `duration` | ✔ | - | Returns the duration for the current media. |
|
| `duration` | ✓ | - | Returns the duration for the current media. |
|
||||||
| `volume` | ✔ | ✔ | Gets or sets the volume for the player. The setter accepts a float between 0 and 1. |
|
| `volume` | ✓ | ✓ | Gets or sets the volume for the player. The setter accepts a float between 0 and 1. |
|
||||||
| `muted` | ✔ | ✔ | Gets or sets the muted state of the player. The setter accepts a boolean. |
|
| `muted` | ✓ | ✓ | Gets or sets the muted state of the player. The setter accepts a boolean. |
|
||||||
| `hasAudio` | ✔ | - | Returns a boolean indicating if the current media has an audio track. |
|
| `hasAudio` | ✓ | - | Returns a boolean indicating if the current media has an audio track. |
|
||||||
| `speed` | ✔ | ✔ | Gets or sets the speed for the player. The setter accepts a value in the options specified in your config. Generally the minimum should be 0.5. |
|
| `speed` | ✓ | ✓ | Gets or sets the speed for the player. The setter accepts a value in the options specified in your config. Generally the minimum should be 0.5. |
|
||||||
| `quality`¹ | ✔ | ✔ | Gets or sets the quality for the player. The setter accepts a value from the options specified in your config. |
|
| `quality`¹ | ✓ | ✓ | Gets or sets the quality for the player. The setter accepts a value from the options specified in your config. |
|
||||||
| `loop` | ✔ | ✔ | Gets or sets the current loop state of the player. The setter accepts a boolean. |
|
| `loop` | ✓ | ✓ | Gets or sets the current loop state of the player. The setter accepts a boolean. |
|
||||||
| `source` | ✔ | ✔ | Gets or sets the current source for the player. The setter accepts an object. See [source setter](#source-setter) below for examples. |
|
| `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. |
|
| `poster`² | ✓ | ✓ | Gets or sets the current poster image for the player. The setter accepts a string; the URL for the updated poster image. |
|
||||||
| `autoplay` | ✔ | ✔ | Gets or sets the autoplay state of the player. The setter accepts a boolean. |
|
| `autoplay` | ✓ | ✓ | Gets or sets the autoplay state of the player. The setter accepts a boolean. |
|
||||||
| `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. |
|
| `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+. |
|
| `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.
|
1. YouTube only. HTML5 will follow.
|
||||||
2. HTML5 only
|
2. HTML5 only
|
||||||
@ -596,14 +590,14 @@ Plyr supports the last 2 versions of most _modern_ browsers.
|
|||||||
|
|
||||||
| Browser | Supported |
|
| Browser | Supported |
|
||||||
| ------------- | --------- |
|
| ------------- | --------- |
|
||||||
| Safari | ✔ |
|
| Safari | ✓ |
|
||||||
| Mobile Safari | ✔¹ |
|
| Mobile Safari | ✓¹ |
|
||||||
| Firefox | ✔ |
|
| Firefox | ✓ |
|
||||||
| Chrome | ✔ |
|
| Chrome | ✓ |
|
||||||
| Opera | ✔ |
|
| Opera | ✓ |
|
||||||
| Edge | ✔ |
|
| Edge | ✓ |
|
||||||
| IE11 | ✔ |
|
| IE11 | ✓ |
|
||||||
| IE10 | ✔² |
|
| IE10 | ✓² |
|
||||||
|
|
||||||
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.
|
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))
|
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))
|
||||||
|
@ -39,7 +39,7 @@ const captions = {
|
|||||||
// Only Vimeo and HTML5 video supported at this point
|
// Only Vimeo and HTML5 video supported at this point
|
||||||
if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) {
|
if (!this.isVideo || this.isYouTube || (this.isHTML5 && !support.textTracks)) {
|
||||||
// Clear menu and hide
|
// 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);
|
controls.setCaptionsMenu.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ const captions = {
|
|||||||
captions.show.call(this);
|
captions.show.call(this);
|
||||||
|
|
||||||
// Set available languages in list
|
// 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);
|
controls.setCaptionsMenu.call(this);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -78,7 +78,7 @@ const captions = {
|
|||||||
// Setup HTML5 track rendering
|
// Setup HTML5 track rendering
|
||||||
if (this.isHTML5 && this.isVideo) {
|
if (this.isHTML5 && this.isVideo) {
|
||||||
captions.getTracks.call(this).forEach(track => {
|
captions.getTracks.call(this).forEach(track => {
|
||||||
// Remove previous bindings
|
// Show track
|
||||||
utils.on(track, 'cuechange', event => captions.setCue.call(this, event));
|
utils.on(track, 'cuechange', event => captions.setCue.call(this, event));
|
||||||
|
|
||||||
// Turn off native caption rendering to avoid double captions
|
// Turn off native caption rendering to avoid double captions
|
||||||
@ -124,7 +124,8 @@ const captions = {
|
|||||||
setCue(input) {
|
setCue(input) {
|
||||||
// Get the track from the event if needed
|
// Get the track from the event if needed
|
||||||
const track = utils.is.event(input) ? input.target : input;
|
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);
|
const currentTrack = captions.getCurrentTrack.call(this);
|
||||||
|
|
||||||
// Only display current track
|
// Only display current track
|
||||||
|
17
src/js/controls.js
vendored
17
src/js/controls.js
vendored
@ -215,7 +215,16 @@ const controls = {
|
|||||||
|
|
||||||
utils.setAttributes(button, attributes);
|
utils.setAttributes(button, attributes);
|
||||||
|
|
||||||
|
// 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;
|
this.elements.buttons[type] = button;
|
||||||
|
}
|
||||||
|
|
||||||
return button;
|
return button;
|
||||||
},
|
},
|
||||||
@ -893,7 +902,6 @@ const controls = {
|
|||||||
// Play/Pause button
|
// Play/Pause button
|
||||||
if (this.config.controls.includes('play')) {
|
if (this.config.controls.includes('play')) {
|
||||||
container.appendChild(controls.createButton.call(this, 'play'));
|
container.appendChild(controls.createButton.call(this, 'play'));
|
||||||
// container.appendChild(controls.createButton.call(this, 'pause'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast forward button
|
// Fast forward button
|
||||||
@ -1147,9 +1155,10 @@ const controls = {
|
|||||||
|
|
||||||
// Null by default
|
// Null by default
|
||||||
let container = null;
|
let container = null;
|
||||||
|
this.elements.controls = null;
|
||||||
|
|
||||||
// HTML passed as the option
|
// HTML or Element passed as the option
|
||||||
if (utils.is.string(this.config.controls)) {
|
if (utils.is.string(this.config.controls) || utils.is.element(this.config.controls)) {
|
||||||
container = this.config.controls;
|
container = this.config.controls;
|
||||||
} else if (utils.is.function(this.config.controls)) {
|
} else if (utils.is.function(this.config.controls)) {
|
||||||
// A custom function to build controls
|
// A custom function to build controls
|
||||||
@ -1193,7 +1202,7 @@ const controls = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the elements if need be
|
// Find the elements if need be
|
||||||
if (utils.is.element(this.elements.controls)) {
|
if (!utils.is.element(this.elements.controls)) {
|
||||||
utils.findElements.call(this);
|
utils.findElements.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ const defaults = {
|
|||||||
// Sprite (for icons)
|
// Sprite (for icons)
|
||||||
loadSprite: true,
|
loadSprite: true,
|
||||||
iconPrefix: 'plyr',
|
iconPrefix: 'plyr',
|
||||||
iconUrl: 'https://cdn.plyr.io/3.0.0-beta.12/plyr.svg',
|
iconUrl: 'https://cdn.plyr.io/3.0.0-beta.13/plyr.svg',
|
||||||
|
|
||||||
// Blank video (used to prevent errors on source change)
|
// Blank video (used to prevent errors on source change)
|
||||||
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
|
blankVideo: 'https://cdn.plyr.io/static/blank.mp4',
|
||||||
@ -120,6 +120,7 @@ const defaults = {
|
|||||||
fullscreen: {
|
fullscreen: {
|
||||||
enabled: true, // Allow fullscreen?
|
enabled: true, // Allow fullscreen?
|
||||||
fallback: true, // Fallback for vintage browsers
|
fallback: true, // Fallback for vintage browsers
|
||||||
|
iosNative: false, // Use the native fullscreen in iOS (disables custom controls)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Local storage
|
// Local storage
|
||||||
|
@ -1,127 +1,204 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Plyr fullscreen API
|
// Fullscreen wrapper
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
|
|
||||||
// Determine the prefix
|
const browser = utils.getBrowser();
|
||||||
const prefix = (() => {
|
|
||||||
let value = false;
|
|
||||||
|
|
||||||
if (utils.is.function(document.cancelFullScreen)) {
|
function onChange() {
|
||||||
value = '';
|
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 {
|
} 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
|
// Check for fullscreen support by vendor prefix
|
||||||
[
|
let value = '';
|
||||||
|
const prefixes = [
|
||||||
'webkit',
|
'webkit',
|
||||||
'o',
|
|
||||||
'moz',
|
'moz',
|
||||||
'ms',
|
'ms',
|
||||||
'khtml',
|
];
|
||||||
].some(pre => {
|
|
||||||
|
prefixes.some(pre => {
|
||||||
if (utils.is.function(document[`${pre}CancelFullScreen`])) {
|
if (utils.is.function(document[`${pre}CancelFullScreen`])) {
|
||||||
value = pre;
|
value = pre;
|
||||||
return true;
|
return true;
|
||||||
} else if (utils.is.function(document.msExitFullscreen) && document.msFullscreenEnabled) {
|
} else if (utils.is.function(document.msExitFullscreen)) {
|
||||||
// Special case for MS (when isn't it?)
|
|
||||||
value = 'ms';
|
value = 'ms';
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
})();
|
}
|
||||||
|
|
||||||
// Fullscreen API
|
// Determine if fullscreen is enabled
|
||||||
const fullscreen = {
|
get enabled() {
|
||||||
// Get the prefix
|
const fallback = this.player.config.fullscreen.fallback && !utils.inFrame();
|
||||||
prefix,
|
|
||||||
|
|
||||||
// Check if we can use it
|
return (Fullscreen.native || fallback) && this.player.config.fullscreen.enabled && this.player.supported.ui && this.player.isVideo;
|
||||||
enabled: document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled,
|
}
|
||||||
|
|
||||||
// Yet again Microsoft awesomeness,
|
// Get active state
|
||||||
// Sometimes the prefix is 'ms', sometimes 'MS' to keep you on your toes
|
get active() {
|
||||||
eventType: prefix === 'ms' ? 'MSFullscreenChange' : `${prefix}fullscreenchange`,
|
if (!this.enabled) {
|
||||||
|
|
||||||
// Is an element fullscreen
|
|
||||||
isFullScreen(element) {
|
|
||||||
if (!fullscreen.enabled) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const target = utils.is.nullOrUndefined(element) ? document.body : element;
|
// Fallback using classname
|
||||||
|
if (!Fullscreen.native) {
|
||||||
switch (prefix) {
|
return utils.hasClass(this.target, this.player.config.classNames.fullscreen.fallback);
|
||||||
case '':
|
}
|
||||||
return document.fullscreenElement === target;
|
|
||||||
|
const element = !this.prefix ? document.fullscreenElement : document[`${this.prefix}FullscreenElement`];
|
||||||
case 'moz':
|
|
||||||
return document.mozFullScreenElement === target;
|
return element === this.target;
|
||||||
|
}
|
||||||
default:
|
|
||||||
return document[`${prefix}FullscreenElement`] === 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
|
// Make an element fullscreen
|
||||||
requestFullScreen(element) {
|
enter() {
|
||||||
if (!fullscreen.enabled) {
|
if (!this.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) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for native support
|
// iOS native fullscreen doesn't need the request step
|
||||||
const nativeSupport = fullscreen.enabled;
|
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())) {
|
// Bail from fullscreen
|
||||||
this.debug.log(`${nativeSupport ? 'Native' : 'Fallback'} fullscreen enabled`);
|
exit() {
|
||||||
|
if (!this.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Add styling hook to show button
|
// iOS native fullscreen
|
||||||
utils.toggleClass(this.elements.container, this.config.classNames.fullscreen.enabled, true);
|
if (browser.isIos && this.player.config.fullscreen.iosNative) {
|
||||||
} else {
|
this.target.webkitExitFullscreen();
|
||||||
this.debug.log('Fullscreen not supported and fallback disabled');
|
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 state
|
||||||
if (this.elements.buttons && this.elements.buttons.fullscreen) {
|
toggle() {
|
||||||
utils.toggleState(this.elements.buttons.fullscreen, false);
|
if (!this.active) {
|
||||||
|
this.enter();
|
||||||
|
} else {
|
||||||
|
this.exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trap focus in container
|
export default Fullscreen;
|
||||||
utils.trapFocus.call(this);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default fullscreen;
|
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
import support from './support';
|
import support from './support';
|
||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
import controls from './controls';
|
import controls from './controls';
|
||||||
import fullscreen from './fullscreen';
|
|
||||||
import ui from './ui';
|
import ui from './ui';
|
||||||
|
|
||||||
// Sniff out the browser
|
// Sniff out the browser
|
||||||
@ -138,7 +137,7 @@ const listeners = {
|
|||||||
|
|
||||||
case 70:
|
case 70:
|
||||||
// F key
|
// F key
|
||||||
this.toggleFullscreen();
|
this.fullscreen.toggle();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 67:
|
case 67:
|
||||||
@ -171,8 +170,8 @@ const listeners = {
|
|||||||
|
|
||||||
// Escape is handle natively when in full screen
|
// Escape is handle natively when in full screen
|
||||||
// So we only need to worry about non native
|
// So we only need to worry about non native
|
||||||
if (!fullscreen.enabled && this.fullscreen.active && code === 27) {
|
if (!this.fullscreen.enabled && this.fullscreen.active && code === 27) {
|
||||||
this.toggleFullscreen();
|
this.fullscreen.toggle();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store last code for next cycle
|
// Store last code for next cycle
|
||||||
@ -215,18 +214,6 @@ const listeners = {
|
|||||||
this.toggleControls(event);
|
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
|
// Listen for media events
|
||||||
@ -266,7 +253,7 @@ const listeners = {
|
|||||||
utils.on(this.media, 'playing play pause ended', event => ui.checkPlaying.call(this, event));
|
utils.on(this.media, 'playing play pause ended', event => ui.checkPlaying.call(this, event));
|
||||||
|
|
||||||
// Loading
|
// 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
|
// Check if media failed to load
|
||||||
// utils.on(this.media, 'play', event => ui.checkFailed.call(this, event));
|
// utils.on(this.media, 'play', event => ui.checkFailed.call(this, event));
|
||||||
@ -307,7 +294,7 @@ const listeners = {
|
|||||||
event => {
|
event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,63 +381,63 @@ const listeners = {
|
|||||||
utils.on(this.elements.buttons.play, 'click', event =>
|
utils.on(this.elements.buttons.play, 'click', event =>
|
||||||
proxy(event, 'play', () => {
|
proxy(event, 'play', () => {
|
||||||
this.togglePlay();
|
this.togglePlay();
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Pause
|
// Pause
|
||||||
utils.on(this.elements.buttons.restart, 'click', event =>
|
utils.on(this.elements.buttons.restart, 'click', event =>
|
||||||
proxy(event, 'restart', () => {
|
proxy(event, 'restart', () => {
|
||||||
this.restart();
|
this.restart();
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rewind
|
// Rewind
|
||||||
utils.on(this.elements.buttons.rewind, 'click', event =>
|
utils.on(this.elements.buttons.rewind, 'click', event =>
|
||||||
proxy(event, 'rewind', () => {
|
proxy(event, 'rewind', () => {
|
||||||
this.rewind();
|
this.rewind();
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Rewind
|
// Rewind
|
||||||
utils.on(this.elements.buttons.forward, 'click', event =>
|
utils.on(this.elements.buttons.forward, 'click', event =>
|
||||||
proxy(event, 'forward', () => {
|
proxy(event, 'forward', () => {
|
||||||
this.forward();
|
this.forward();
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mute toggle
|
// Mute toggle
|
||||||
utils.on(this.elements.buttons.mute, 'click', event =>
|
utils.on(this.elements.buttons.mute, 'click', event =>
|
||||||
proxy(event, 'mute', () => {
|
proxy(event, 'mute', () => {
|
||||||
this.muted = !this.muted;
|
this.muted = !this.muted;
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Captions toggle
|
// Captions toggle
|
||||||
utils.on(this.elements.buttons.captions, 'click', event =>
|
utils.on(this.elements.buttons.captions, 'click', event =>
|
||||||
proxy(event, 'captions', () => {
|
proxy(event, 'captions', () => {
|
||||||
this.toggleCaptions();
|
this.toggleCaptions();
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fullscreen toggle
|
// Fullscreen toggle
|
||||||
utils.on(this.elements.buttons.fullscreen, 'click', event =>
|
utils.on(this.elements.buttons.fullscreen, 'click', event =>
|
||||||
proxy(event, 'fullscreen', () => {
|
proxy(event, 'fullscreen', () => {
|
||||||
this.toggleFullscreen();
|
this.fullscreen.toggle();
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Picture-in-Picture
|
// Picture-in-Picture
|
||||||
utils.on(this.elements.buttons.pip, 'click', event =>
|
utils.on(this.elements.buttons.pip, 'click', event =>
|
||||||
proxy(event, 'pip', () => {
|
proxy(event, 'pip', () => {
|
||||||
this.pip = 'toggle';
|
this.pip = 'toggle';
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Airplay
|
// Airplay
|
||||||
utils.on(this.elements.buttons.airplay, 'click', event =>
|
utils.on(this.elements.buttons.airplay, 'click', event =>
|
||||||
proxy(event, 'airplay', () => {
|
proxy(event, 'airplay', () => {
|
||||||
this.airplay();
|
this.airplay();
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Settings menu
|
// Settings menu
|
||||||
@ -489,7 +476,7 @@ const listeners = {
|
|||||||
utils.on(this.elements.inputs.seek, inputEvent, event =>
|
utils.on(this.elements.inputs.seek, inputEvent, event =>
|
||||||
proxy(event, 'seek', () => {
|
proxy(event, 'seek', () => {
|
||||||
this.currentTime = event.target.value / event.target.max * this.duration;
|
this.currentTime = event.target.value / event.target.max * this.duration;
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Current time invert
|
// Current time invert
|
||||||
@ -510,7 +497,7 @@ const listeners = {
|
|||||||
utils.on(this.elements.inputs.volume, inputEvent, event =>
|
utils.on(this.elements.inputs.volume, inputEvent, event =>
|
||||||
proxy(event, 'volume', () => {
|
proxy(event, 'volume', () => {
|
||||||
this.volume = event.target.value;
|
this.volume = event.target.value;
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Polyfill for lower fill in <input type="range"> for webkit
|
// Polyfill for lower fill in <input type="range"> for webkit
|
||||||
@ -583,7 +570,7 @@ const listeners = {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -86,7 +86,7 @@ const media = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove child sources
|
// Remove child sources
|
||||||
Array.from(this.media.querySelectorAll('source')).forEach(utils.removeElement);
|
utils.removeElement(this.media.querySelectorAll('source'));
|
||||||
|
|
||||||
// Set blank video src attribute
|
// Set blank video src attribute
|
||||||
// This is to prevent a MEDIA_ERR_SRC_NOT_SUPPORTED error
|
// This is to prevent a MEDIA_ERR_SRC_NOT_SUPPORTED error
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Plyr
|
// Plyr
|
||||||
// plyr.js v3.0.0-beta.12
|
// plyr.js v3.0.0-beta.13
|
||||||
// https://github.com/sampotts/plyr
|
// https://github.com/sampotts/plyr
|
||||||
// License: The MIT License (MIT)
|
// License: The MIT License (MIT)
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
@ -11,12 +11,12 @@ import support from './support';
|
|||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
|
|
||||||
import Console from './console';
|
import Console from './console';
|
||||||
|
import Fullscreen from './fullscreen';
|
||||||
import Storage from './storage';
|
import Storage from './storage';
|
||||||
import Ads from './plugins/ads';
|
import Ads from './plugins/ads';
|
||||||
|
|
||||||
import captions from './captions';
|
import captions from './captions';
|
||||||
import controls from './controls';
|
import controls from './controls';
|
||||||
import fullscreen from './fullscreen';
|
|
||||||
import listeners from './listeners';
|
import listeners from './listeners';
|
||||||
import media from './media';
|
import media from './media';
|
||||||
import source from './source';
|
import source from './source';
|
||||||
@ -26,12 +26,6 @@ import ui from './ui';
|
|||||||
// TODO: Use a WeakMap for private globals
|
// TODO: Use a WeakMap for private globals
|
||||||
// const globals = new WeakMap();
|
// const globals = new WeakMap();
|
||||||
|
|
||||||
// Globals
|
|
||||||
let scrollPosition = {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Plyr instance
|
// Plyr instance
|
||||||
class Plyr {
|
class Plyr {
|
||||||
constructor(target, options) {
|
constructor(target, options) {
|
||||||
@ -232,9 +226,6 @@ class Plyr {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup local storage for user settings
|
|
||||||
this.storage = new Storage(this);
|
|
||||||
|
|
||||||
// Check for support again but with type
|
// Check for support again but with type
|
||||||
this.supported = support.check(this.type, this.provider, this.config.inline);
|
this.supported = support.check(this.type, this.provider, this.config.inline);
|
||||||
|
|
||||||
@ -244,6 +235,9 @@ class Plyr {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup local storage for user settings
|
||||||
|
this.storage = new Storage(this);
|
||||||
|
|
||||||
// Store reference
|
// Store reference
|
||||||
this.media.plyr = this;
|
this.media.plyr = this;
|
||||||
|
|
||||||
@ -278,6 +272,9 @@ class Plyr {
|
|||||||
ui.build.call(this);
|
ui.build.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup fullscreen
|
||||||
|
this.fullscreen = new Fullscreen(this);
|
||||||
|
|
||||||
// Setup ads if provided
|
// Setup ads if provided
|
||||||
this.ads = new Ads(this);
|
this.ads = new Ads(this);
|
||||||
}
|
}
|
||||||
@ -850,62 +847,6 @@ class Plyr {
|
|||||||
return this.captions.language;
|
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
|
* Toggle picture-in-picture playback on WebKit/MacOS
|
||||||
* TODO: update player with state, support, enabled
|
* TODO: update player with state, support, enabled
|
||||||
@ -1101,12 +1042,8 @@ class Plyr {
|
|||||||
// If it's a soft destroy, make minimal changes
|
// If it's a soft destroy, make minimal changes
|
||||||
if (soft) {
|
if (soft) {
|
||||||
if (Object.keys(this.elements).length) {
|
if (Object.keys(this.elements).length) {
|
||||||
// Remove buttons
|
// Remove elements
|
||||||
if (this.elements.buttons && this.elements.buttons.play) {
|
utils.removeElement(this.elements.buttons.play);
|
||||||
Array.from(this.elements.buttons.play).forEach(button => utils.removeElement(button));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove others
|
|
||||||
utils.removeElement(this.elements.captions);
|
utils.removeElement(this.elements.captions);
|
||||||
utils.removeElement(this.elements.controls);
|
utils.removeElement(this.elements.controls);
|
||||||
utils.removeElement(this.elements.wrapper);
|
utils.removeElement(this.elements.wrapper);
|
||||||
|
@ -136,6 +136,9 @@ const source = {
|
|||||||
// Setup interface
|
// Setup interface
|
||||||
ui.build.call(this);
|
ui.build.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the fullscreen support
|
||||||
|
this.fullscreen.update();
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
10
src/js/ui.js
10
src/js/ui.js
@ -5,7 +5,6 @@
|
|||||||
import utils from './utils';
|
import utils from './utils';
|
||||||
import captions from './captions';
|
import captions from './captions';
|
||||||
import controls from './controls';
|
import controls from './controls';
|
||||||
import fullscreen from './fullscreen';
|
|
||||||
import listeners from './listeners';
|
import listeners from './listeners';
|
||||||
|
|
||||||
const ui = {
|
const ui = {
|
||||||
@ -33,12 +32,6 @@ const ui = {
|
|||||||
if (!this.supported.ui) {
|
if (!this.supported.ui) {
|
||||||
this.debug.warn(`Basic support only for ${this.provider} ${this.type}`);
|
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
|
// Restore native controls
|
||||||
ui.toggleNativeControls.call(this, true);
|
ui.toggleNativeControls.call(this, true);
|
||||||
|
|
||||||
@ -63,9 +56,6 @@ const ui = {
|
|||||||
// Remove native controls
|
// Remove native controls
|
||||||
ui.toggleNativeControls.call(this);
|
ui.toggleNativeControls.call(this);
|
||||||
|
|
||||||
// Setup fullscreen
|
|
||||||
fullscreen.setup.call(this);
|
|
||||||
|
|
||||||
// Captions
|
// Captions
|
||||||
captions.setup.call(this);
|
captions.setup.call(this);
|
||||||
|
|
||||||
|
@ -306,12 +306,15 @@ const utils = {
|
|||||||
// Remove an element
|
// Remove an element
|
||||||
removeElement(element) {
|
removeElement(element) {
|
||||||
if (!utils.is.element(element) || !utils.is.element(element.parentNode)) {
|
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);
|
element.parentNode.removeChild(element);
|
||||||
|
|
||||||
return element;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Remove all child elements
|
// Remove all child elements
|
||||||
@ -525,17 +528,18 @@ const utils = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Trap focus inside container
|
// 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 focusable = utils.getElements.call(this, 'button:not(:disabled), input:not(:disabled), [tabindex]');
|
||||||
const first = focusable[0];
|
const first = focusable[0];
|
||||||
const last = focusable[focusable.length - 1];
|
const last = focusable[focusable.length - 1];
|
||||||
|
|
||||||
utils.on(
|
const trap = event => {
|
||||||
this.elements.container,
|
|
||||||
'keydown',
|
|
||||||
event => {
|
|
||||||
// Bail if not tab key or not fullscreen
|
// Bail if not tab key or not fullscreen
|
||||||
if (event.key !== 'Tab' || event.keyCode !== 9 || !this.fullscreen.active) {
|
if (event.key !== 'Tab' || event.keyCode !== 9) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,20 +555,24 @@ const utils = {
|
|||||||
last.focus();
|
last.focus();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
false,
|
|
||||||
);
|
if (toggle) {
|
||||||
|
utils.on(this.elements.container, 'keydown', trap, false);
|
||||||
|
} else {
|
||||||
|
utils.off(this.elements.container, 'keydown', trap, false);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Toggle event listener
|
// Toggle event listener
|
||||||
toggleListener(elements, event, callback, toggle, passive, capture) {
|
toggleListener(elements, event, callback, toggle, passive, capture) {
|
||||||
// Bail if no elements
|
// Bail if no elemetns, event, or callback
|
||||||
if (utils.is.nullOrUndefined(elements)) {
|
if (utils.is.empty(elements) || utils.is.empty(event) || !utils.is.function(callback)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a nodelist is passed, call itself on each node
|
// 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
|
// Create listener for each node
|
||||||
Array.from(elements).forEach(element => {
|
Array.from(elements).forEach(element => {
|
||||||
if (element instanceof Node) {
|
if (element instanceof Node) {
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
|
|
||||||
.plyr__badge {
|
.plyr__badge {
|
||||||
background: $plyr-menu-color;
|
background: $plyr-badge-bg;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
color: $plyr-menu-bg;
|
color: $plyr-badge-color;
|
||||||
font-size: $plyr-font-size-badge;
|
font-size: $plyr-font-size-badge;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
padding: 3px 4px;
|
padding: 3px 4px;
|
||||||
|
@ -59,6 +59,14 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: $plyr-control-padding;
|
padding: $plyr-control-padding;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-top: 2px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options
|
// Options
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
@import 'settings/cosmetics';
|
@import 'settings/cosmetics';
|
||||||
@import 'settings/type';
|
@import 'settings/type';
|
||||||
|
|
||||||
|
@import 'settings/badges';
|
||||||
@import 'settings/captions';
|
@import 'settings/captions';
|
||||||
@import 'settings/controls';
|
@import 'settings/controls';
|
||||||
@import 'settings/helpers';
|
@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;
|
Loading…
x
Reference in New Issue
Block a user