Progressively enhance <iframe> embeds
This commit is contained in:
parent
2e5d06ad85
commit
d9ec1d1b8e
29
changelog.md
29
changelog.md
@ -13,6 +13,7 @@ This is a massive release. A _mostly_ complete rewrite in ES6. What started out
|
|||||||
* Added AirPlay support (again, Safari only)
|
* Added AirPlay support (again, Safari only)
|
||||||
* Added `playsinline` support for iOS 10+
|
* Added `playsinline` support for iOS 10+
|
||||||
* Soundcloud removed until I can work on a plugin framework
|
* Soundcloud removed until I can work on a plugin framework
|
||||||
|
* Embedded players are now progressively enhanced - no more empty `<div>`s!
|
||||||
|
|
||||||
### Other stuff
|
### Other stuff
|
||||||
|
|
||||||
@ -68,27 +69,33 @@ You gotta break eggs to make an omelette. Sadly, there's quite a few breaking ch
|
|||||||
Because we're using the fancy new ES6 syntax, you will need to polyfill for vintage browsers if you want to use Plyr and still support them. Luckily there's a decent service for this that makes it painless, [https://polyfill.io](polyfill.io).
|
Because we're using the fancy new ES6 syntax, you will need to polyfill for vintage browsers if you want to use Plyr and still support them. Luckily there's a decent service for this that makes it painless, [https://polyfill.io](polyfill.io).
|
||||||
|
|
||||||
## v2.0.18
|
## v2.0.18
|
||||||
- Fix for YouTube .getVideoData() issue (fixes #709)
|
|
||||||
|
* Fix for YouTube .getVideoData() issue (fixes #709)
|
||||||
|
|
||||||
## v2.0.17
|
## v2.0.17
|
||||||
- Vimeo controls fix (fixes #697)
|
|
||||||
- SVG4everybody compatibility fix
|
* Vimeo controls fix (fixes #697)
|
||||||
- Allow Plyr.setup event listeners to be set up as separate event listeners (https://github.com/sampotts/plyr/pull/703)
|
* SVG4everybody compatibility fix
|
||||||
- Added title to the layer html template (for custom controls) (https://github.com/sampotts/plyr/pull/649)
|
* Allow Plyr.setup event listeners to be set up as separate event listeners (https://github.com/sampotts/plyr/pull/703)
|
||||||
- Target is null bug fix (https://github.com/sampotts/plyr/pull/617)
|
* Added title to the layer html template (for custom controls) (https://github.com/sampotts/plyr/pull/649)
|
||||||
- fix #684 memory leaks issues after destroy (https://github.com/sampotts/plyr/pull/700)
|
* Target is null bug fix (https://github.com/sampotts/plyr/pull/617)
|
||||||
|
* fix #684 memory leaks issues after destroy (https://github.com/sampotts/plyr/pull/700)
|
||||||
|
|
||||||
## v2.0.16
|
## v2.0.16
|
||||||
- Fullscreen bug fix (fixes #664)
|
|
||||||
|
* Fullscreen bug fix (fixes #664)
|
||||||
|
|
||||||
## v2.0.15
|
## v2.0.15
|
||||||
- Demo fix
|
|
||||||
|
* Demo fix
|
||||||
|
|
||||||
## v2.0.14
|
## v2.0.14
|
||||||
- CDN URL updates. Sorry, still working on V3 as hard as I can...
|
|
||||||
|
* CDN URL updates. Sorry, still working on V3 as hard as I can...
|
||||||
|
|
||||||
## v2.0.13
|
## v2.0.13
|
||||||
- Repo moved and Vimeo demo fix
|
|
||||||
|
* Repo moved and Vimeo demo fix
|
||||||
|
|
||||||
## v2.0.12
|
## v2.0.12
|
||||||
|
|
||||||
|
2
demo/dist/demo.css
vendored
2
demo/dist/demo.css
vendored
File diff suppressed because one or more lines are too long
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
14
package.json
14
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "plyr",
|
"name": "plyr",
|
||||||
"version": "3.0.0-beta.1",
|
"version": "3.0.0-beta.2",
|
||||||
"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",
|
"main": "./dist",
|
||||||
@ -34,7 +34,7 @@
|
|||||||
"gulp-util": "^3.0.8",
|
"gulp-util": "^3.0.8",
|
||||||
"rollup-plugin-babel": "^3.0.3",
|
"rollup-plugin-babel": "^3.0.3",
|
||||||
"rollup-plugin-commonjs": "^8.2.6",
|
"rollup-plugin-commonjs": "^8.2.6",
|
||||||
"rollup-plugin-node-resolve": "^3.0.0",
|
"rollup-plugin-node-resolve": "^3.0.2",
|
||||||
"rollup-plugin-uglify": "^2.0.1",
|
"rollup-plugin-uglify": "^2.0.1",
|
||||||
"run-sequence": "^2.2.1",
|
"run-sequence": "^2.2.1",
|
||||||
"stylelint": "^8.4.0",
|
"stylelint": "^8.4.0",
|
||||||
@ -46,15 +46,7 @@
|
|||||||
"stylelint-selector-bem-pattern": "^2.0.0",
|
"stylelint-selector-bem-pattern": "^2.0.0",
|
||||||
"uglify-es": "^3.3.5"
|
"uglify-es": "^3.3.5"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": ["HTML5 Video", "HTML5 Audio", "Media Player", "DASH", "Shaka", "WordPress", "HLS"],
|
||||||
"HTML5 Video",
|
|
||||||
"HTML5 Audio",
|
|
||||||
"Media Player",
|
|
||||||
"DASH",
|
|
||||||
"Shaka",
|
|
||||||
"WordPress",
|
|
||||||
"HLS"
|
|
||||||
],
|
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/sampotts/plyr.git"
|
"url": "git://github.com/sampotts/plyr.git"
|
||||||
|
21
readme.md
21
readme.md
@ -91,22 +91,24 @@ Plyr extends upon the standard HTML5 markup so that's all you need for those typ
|
|||||||
</audio>
|
</audio>
|
||||||
```
|
```
|
||||||
|
|
||||||
For YouTube and Vimeo, Plyr uses the standard YouTube API markup (an empty `<div>`):
|
For YouTube and Vimeo players, Plyr uses progressive enhancement to enhance the default `<iframe>` embeds. Below are some examples. The `plyr__video-embed` classname will make the embed responsive. You can add the `autoplay`, `loop` and `playsinline` (YouTube only) query parameters to the URL and they will be set as config options automatically. For YouTube, the `origin` should be updated to reflect the domain you're hosting the embed on, or you can opt to omit it.
|
||||||
|
|
||||||
#### YouTube embed
|
#### YouTube embed
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div id="player" data-plyr-provider="youtube" data-plyr-embed-id="bTqVqk7FSmY"></div>
|
<div class="plyr__video-embed" id="player">
|
||||||
|
<iframe src="https://www.youtube.com/embed/bTqVqk7FSmY?origin=https://plyr.io&iv_load_policy=3&modestbranding=1&playsinline=1&showinfo=0&rel=0&enablejsapi=1" allowfullscreen allowtransparency allow="autoplay"></iframe>
|
||||||
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Vimeo embed
|
#### Vimeo embed
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div id="player" data-plyr-provider="vimeo" data-plyr-embed-id="143418951"></div>
|
<div class="plyr__video-embed" id="player">
|
||||||
|
<iframe src="https://player.vimeo.com/video/76979871?loop=false&byline=false&portrait=false&title=false&speed=true&transparent=0&gesture=media" allowfullscreen allowtransparency allow="autoplay"></iframe>
|
||||||
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: In both cases, `data-plyr-embed-id` value can be the ID or URL for the media.
|
|
||||||
|
|
||||||
### JavaScript
|
### JavaScript
|
||||||
|
|
||||||
Include the `plyr.js` script before the closing `</body>` tag and then call `plyr.setup()`. More info on `setup()` can be found under
|
Include the `plyr.js` script before the closing `</body>` tag and then call `plyr.setup()`. More info on `setup()` can be found under
|
||||||
@ -120,7 +122,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.1/plyr.js"></script>
|
<script src="https://cdn.plyr.io/3.0.0-beta.2/plyr.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### CSS
|
### CSS
|
||||||
@ -134,13 +136,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.1/plyr.css">
|
<link rel="stylesheet" href="https://cdn.plyr.io/3.0.0-beta.2/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.1/plyr.svg`.
|
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.0.0-beta.2/plyr.svg`.
|
||||||
|
|
||||||
## Advanced
|
## Advanced
|
||||||
|
|
||||||
@ -211,8 +213,7 @@ Passing a [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList):
|
|||||||
const player = new Plyr(document.querySelectorAll('.js-player'));
|
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
|
The NodeList, HTMLElement or string selector can be the target `<video>`, `<audio>`, or `<div>` wrapper for embeds
|
||||||
element.
|
|
||||||
|
|
||||||
The second argument for the constructor is the [#options](options) object:
|
The second argument for the constructor is the [#options](options) object:
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
|
|
||||||
export default class Console {
|
export default class Console {
|
||||||
constructor(player) {
|
constructor(enabled = false) {
|
||||||
this.enabled = window.console && player.config.debug;
|
this.enabled = window.console && enabled;
|
||||||
|
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
this.log('Debugging enabled');
|
this.log('Debugging enabled');
|
||||||
|
@ -50,7 +50,7 @@ const media = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Inject the player wrapper
|
// Inject the player wrapper
|
||||||
if (this.isVideo || this.isYouTube || this.isVimeo) {
|
if (this.isVideo) {
|
||||||
// Create the wrapper div
|
// Create the wrapper div
|
||||||
this.elements.wrapper = utils.createElement('div', {
|
this.elements.wrapper = utils.createElement('div', {
|
||||||
class: this.config.classNames.video,
|
class: this.config.classNames.video,
|
||||||
|
@ -8,19 +8,12 @@ import ui from './../ui';
|
|||||||
|
|
||||||
const vimeo = {
|
const vimeo = {
|
||||||
setup() {
|
setup() {
|
||||||
// Remove old containers
|
|
||||||
const containers = utils.getElements.call(this, `[id^="${this.provider}-"]`);
|
|
||||||
Array.from(containers).forEach(utils.removeElement);
|
|
||||||
|
|
||||||
// Add embed class for responsive
|
// Add embed class for responsive
|
||||||
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
|
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
|
||||||
|
|
||||||
// Set intial ratio
|
// Set intial ratio
|
||||||
vimeo.setAspectRatio.call(this);
|
vimeo.setAspectRatio.call(this);
|
||||||
|
|
||||||
// Set ID
|
|
||||||
this.media.setAttribute('id', utils.generateId(this.provider));
|
|
||||||
|
|
||||||
// Load the API if not already
|
// Load the API if not already
|
||||||
if (!utils.is.object(window.Vimeo)) {
|
if (!utils.is.object(window.Vimeo)) {
|
||||||
utils.loadScript(this.config.urls.vimeo.api, () => {
|
utils.loadScript(this.config.urls.vimeo.api, () => {
|
||||||
@ -57,15 +50,21 @@ const vimeo = {
|
|||||||
transparent: 0,
|
transparent: 0,
|
||||||
gesture: 'media',
|
gesture: 'media',
|
||||||
};
|
};
|
||||||
const params = utils.buildUrlParameters(options);
|
const params = utils.buildUrlParams(options);
|
||||||
const id = utils.parseVimeoId(player.embedId);
|
const id = utils.parseVimeoId(player.media.getAttribute('src'));
|
||||||
|
|
||||||
// Build an iframe
|
// Build an iframe
|
||||||
const iframe = utils.createElement('iframe');
|
const iframe = utils.createElement('iframe');
|
||||||
const src = `https://player.vimeo.com/video/${id}?${params}`;
|
const src = `https://player.vimeo.com/video/${id}?${params}`;
|
||||||
iframe.setAttribute('src', src);
|
iframe.setAttribute('src', src);
|
||||||
iframe.setAttribute('allowfullscreen', '');
|
iframe.setAttribute('allowfullscreen', '');
|
||||||
player.media.appendChild(iframe);
|
iframe.setAttribute('allowtransparency', '');
|
||||||
|
iframe.setAttribute('allow', 'autoplay');
|
||||||
|
|
||||||
|
// Inject the package
|
||||||
|
const wrapper = utils.createElement('div');
|
||||||
|
wrapper.appendChild(iframe);
|
||||||
|
player.media = utils.replaceElement(wrapper, player.media);
|
||||||
|
|
||||||
// Setup instance
|
// Setup instance
|
||||||
// https://github.com/vimeo/player.js
|
// https://github.com/vimeo/player.js
|
||||||
|
@ -8,24 +8,15 @@ import ui from './../ui';
|
|||||||
|
|
||||||
const youtube = {
|
const youtube = {
|
||||||
setup() {
|
setup() {
|
||||||
const videoId = utils.parseYouTubeId(this.embedId);
|
|
||||||
|
|
||||||
// Remove old containers
|
|
||||||
const containers = utils.getElements.call(this, `[id^="${this.provider}-"]`);
|
|
||||||
Array.from(containers).forEach(utils.removeElement);
|
|
||||||
|
|
||||||
// Add embed class for responsive
|
// Add embed class for responsive
|
||||||
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
|
utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true);
|
||||||
|
|
||||||
// Set aspect ratio
|
// Set aspect ratio
|
||||||
youtube.setAspectRatio.call(this);
|
youtube.setAspectRatio.call(this);
|
||||||
|
|
||||||
// Set ID
|
|
||||||
this.media.setAttribute('id', utils.generateId(this.provider));
|
|
||||||
|
|
||||||
// Setup API
|
// Setup API
|
||||||
if (utils.is.object(window.YT)) {
|
if (utils.is.object(window.YT) && utils.is.function(window.YT.Player)) {
|
||||||
youtube.ready.call(this, videoId);
|
youtube.ready.call(this);
|
||||||
} else {
|
} else {
|
||||||
// Load the API
|
// Load the API
|
||||||
utils.loadScript(this.config.urls.youtube.api);
|
utils.loadScript(this.config.urls.youtube.api);
|
||||||
@ -36,7 +27,7 @@ const youtube = {
|
|||||||
|
|
||||||
// Add to queue
|
// Add to queue
|
||||||
window.onYouTubeReadyCallbacks.push(() => {
|
window.onYouTubeReadyCallbacks.push(() => {
|
||||||
youtube.ready.call(this, videoId);
|
youtube.ready.call(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set callback to process queue
|
// Set callback to process queue
|
||||||
@ -49,7 +40,7 @@ const youtube = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Get the media title
|
// Get the media title
|
||||||
getTitle() {
|
getTitle(videoId) {
|
||||||
// Try via undocumented API method first
|
// Try via undocumented API method first
|
||||||
// This method disappears now and then though...
|
// This method disappears now and then though...
|
||||||
// https://github.com/sampotts/plyr/issues/709
|
// https://github.com/sampotts/plyr/issues/709
|
||||||
@ -65,7 +56,6 @@ const youtube = {
|
|||||||
|
|
||||||
// Or via Google API
|
// Or via Google API
|
||||||
const key = this.config.keys.google;
|
const key = this.config.keys.google;
|
||||||
const videoId = utils.parseYouTubeId(this.embedId);
|
|
||||||
if (utils.is.string(key) && !utils.is.empty(key)) {
|
if (utils.is.string(key) && !utils.is.empty(key)) {
|
||||||
const url = `https://www.googleapis.com/youtube/v3/videos?id=${videoId}&key=${key}&fields=items(snippet(title))&part=snippet`;
|
const url = `https://www.googleapis.com/youtube/v3/videos?id=${videoId}&key=${key}&fields=items(snippet(title))&part=snippet`;
|
||||||
|
|
||||||
@ -88,12 +78,24 @@ const youtube = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// API ready
|
// API ready
|
||||||
ready(videoId) {
|
ready() {
|
||||||
const player = this;
|
const player = this;
|
||||||
|
|
||||||
|
// Ignore already setup (race condition)
|
||||||
|
const currentId = player.media.getAttribute('id');
|
||||||
|
if (!utils.is.empty(currentId) && currentId.startsWith('youtube-')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the <iframe> with a <div> due to YouTube API issues
|
||||||
|
const videoId = utils.parseYouTubeId(player.media.getAttribute('src'));
|
||||||
|
const id = utils.generateId(player.provider);
|
||||||
|
const container = utils.createElement('div', { id });
|
||||||
|
player.media = utils.replaceElement(container, player.media);
|
||||||
|
|
||||||
// Setup instance
|
// Setup instance
|
||||||
// https://developers.google.com/youtube/iframe_api_reference
|
// https://developers.google.com/youtube/iframe_api_reference
|
||||||
player.embed = new window.YT.Player(player.media.id, {
|
player.embed = new window.YT.Player(id, {
|
||||||
videoId,
|
videoId,
|
||||||
playerVars: {
|
playerVars: {
|
||||||
autoplay: player.config.autoplay ? 1 : 0, // Autoplay
|
autoplay: player.config.autoplay ? 1 : 0, // Autoplay
|
||||||
@ -110,8 +112,8 @@ const youtube = {
|
|||||||
widget_referrer: window && window.location.href,
|
widget_referrer: window && window.location.href,
|
||||||
|
|
||||||
// Captions are flaky on YouTube
|
// Captions are flaky on YouTube
|
||||||
cc_load_policy: this.captions.active ? 1 : 0,
|
cc_load_policy: player.captions.active ? 1 : 0,
|
||||||
cc_lang_pref: this.config.captions.language,
|
cc_lang_pref: player.config.captions.language,
|
||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
onError(event) {
|
onError(event) {
|
||||||
@ -179,7 +181,7 @@ const youtube = {
|
|||||||
const instance = event.target;
|
const instance = event.target;
|
||||||
|
|
||||||
// Get the title
|
// Get the title
|
||||||
youtube.getTitle.call(player);
|
youtube.getTitle.call(player, videoId);
|
||||||
|
|
||||||
// Create a faux HTML5 API using the YouTube API
|
// Create a faux HTML5 API using the YouTube API
|
||||||
player.media.play = () => {
|
player.media.play = () => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// Plyr
|
// Plyr
|
||||||
// plyr.js v3.0.0-beta.1
|
// plyr.js v3.0.0-beta.2
|
||||||
// https://github.com/sampotts/plyr
|
// https://github.com/sampotts/plyr
|
||||||
// License: The MIT License (MIT)
|
// License: The MIT License (MIT)
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
@ -66,7 +66,7 @@ class Plyr {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
})()
|
})(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Elements cache
|
// Elements cache
|
||||||
@ -103,7 +103,7 @@ class Plyr {
|
|||||||
|
|
||||||
// Debugging
|
// Debugging
|
||||||
// TODO: move to globals
|
// TODO: move to globals
|
||||||
this.debug = new Console(this);
|
this.debug = new Console(this.config.debug);
|
||||||
|
|
||||||
// Log config options and support
|
// Log config options and support
|
||||||
this.debug.log('Config', this.config);
|
this.debug.log('Config', this.config);
|
||||||
@ -141,35 +141,61 @@ class Plyr {
|
|||||||
// Supported: video, audio, vimeo, youtube
|
// Supported: video, audio, vimeo, youtube
|
||||||
const type = this.media.tagName.toLowerCase();
|
const type = this.media.tagName.toLowerCase();
|
||||||
|
|
||||||
// Embed attributes
|
// Embed properties
|
||||||
const attributes = {
|
let iframe = null;
|
||||||
provider: 'data-plyr-provider',
|
let url = null;
|
||||||
id: 'data-plyr-embed-id',
|
let params = null;
|
||||||
};
|
|
||||||
|
|
||||||
// Different setup based on type
|
// Different setup based on type
|
||||||
switch (type) {
|
switch (type) {
|
||||||
// TODO: Handle passing an iframe for true progressive enhancement
|
|
||||||
// case 'iframe':
|
|
||||||
case 'div':
|
case 'div':
|
||||||
this.type = types.video; // Audio will come later for external providers
|
// Find the frame
|
||||||
this.provider = this.media.getAttribute(attributes.provider);
|
iframe = this.media.querySelector('iframe');
|
||||||
this.embedId = this.media.getAttribute(attributes.id);
|
|
||||||
|
|
||||||
|
// <iframe> required
|
||||||
|
if (!utils.is.element(iframe)) {
|
||||||
|
this.debug.error('Setup failed: <iframe> is missing');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio will come later for external providers
|
||||||
|
this.type = types.video;
|
||||||
|
|
||||||
|
// Detect provider
|
||||||
|
url = iframe.getAttribute('src');
|
||||||
|
this.provider = utils.getProviderByUrl(url);
|
||||||
|
|
||||||
|
// Get attributes from URL and set config
|
||||||
|
params = utils.getUrlParams(url);
|
||||||
|
if (!utils.is.empty(params)) {
|
||||||
|
const truthy = [
|
||||||
|
'1',
|
||||||
|
'true',
|
||||||
|
];
|
||||||
|
|
||||||
|
if (truthy.includes(params.autoplay)) {
|
||||||
|
this.config.autoplay = true;
|
||||||
|
}
|
||||||
|
if (truthy.includes(params.playsinline)) {
|
||||||
|
this.config.inline = true;
|
||||||
|
}
|
||||||
|
if (truthy.includes(params.loop)) {
|
||||||
|
this.config.loop.active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsupported provider
|
||||||
if (utils.is.empty(this.provider) || !Object.keys(providers).includes(this.provider)) {
|
if (utils.is.empty(this.provider) || !Object.keys(providers).includes(this.provider)) {
|
||||||
this.debug.error('Setup failed: Invalid provider');
|
this.debug.error('Setup failed: Invalid provider');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try and get the embed id
|
// Rework elements
|
||||||
if (utils.is.empty(this.embedId)) {
|
this.elements.container = this.media;
|
||||||
this.debug.error('Setup failed: Embed ID or URL missing');
|
this.media = iframe;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up
|
// Reset classname
|
||||||
this.media.removeAttribute(attributes.provider);
|
this.elements.container.className = '';
|
||||||
this.media.removeAttribute(attributes.id);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -178,22 +204,19 @@ class Plyr {
|
|||||||
this.type = type;
|
this.type = type;
|
||||||
this.provider = providers.html5;
|
this.provider = providers.html5;
|
||||||
|
|
||||||
|
// Get config from attributes
|
||||||
if (this.media.hasAttribute('crossorigin')) {
|
if (this.media.hasAttribute('crossorigin')) {
|
||||||
this.config.crossorigin = true;
|
this.config.crossorigin = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.media.hasAttribute('autoplay')) {
|
if (this.media.hasAttribute('autoplay')) {
|
||||||
this.config.autoplay = true;
|
this.config.autoplay = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.media.hasAttribute('playsinline')) {
|
if (this.media.hasAttribute('playsinline')) {
|
||||||
this.config.inline = true;
|
this.config.inline = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.media.hasAttribute('muted')) {
|
if (this.media.hasAttribute('muted')) {
|
||||||
this.config.muted = true;
|
this.config.muted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.media.hasAttribute('loop')) {
|
if (this.media.hasAttribute('loop')) {
|
||||||
this.config.loop.active = true;
|
this.config.loop.active = true;
|
||||||
}
|
}
|
||||||
@ -221,8 +244,10 @@ class Plyr {
|
|||||||
this.media.plyr = this;
|
this.media.plyr = this;
|
||||||
|
|
||||||
// Wrap media
|
// Wrap media
|
||||||
this.elements.container = utils.createElement('div');
|
if (!utils.is.element(this.elements.container)) {
|
||||||
utils.wrap(this.media, this.elements.container);
|
this.elements.container = utils.createElement('div');
|
||||||
|
utils.wrap(this.media, this.elements.container);
|
||||||
|
}
|
||||||
|
|
||||||
// Allow focus to be captured
|
// Allow focus to be captured
|
||||||
this.elements.container.setAttribute('tabindex', 0);
|
this.elements.container.setAttribute('tabindex', 0);
|
||||||
@ -1054,7 +1079,6 @@ class Plyr {
|
|||||||
|
|
||||||
// GC for embed
|
// GC for embed
|
||||||
this.embed = null;
|
this.embed = null;
|
||||||
this.embedId = null;
|
|
||||||
|
|
||||||
// If it's a soft destroy, make minimal changes
|
// If it's a soft destroy, make minimal changes
|
||||||
if (soft) {
|
if (soft) {
|
||||||
@ -1082,11 +1106,7 @@ class Plyr {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Replace the container with the original element provided
|
// Replace the container with the original element provided
|
||||||
const parent = this.elements.container.parentNode;
|
utils.replaceElement(this.elements.original, this.elements.container);
|
||||||
|
|
||||||
if (utils.is.element(parent)) {
|
|
||||||
parent.replaceChild(this.elements.original, this.elements.container);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event
|
// Event
|
||||||
utils.dispatchEvent.call(this, this.elements.original, 'destroyed', true);
|
utils.dispatchEvent.call(this, this.elements.original, 'destroyed', true);
|
||||||
@ -1119,7 +1139,9 @@ class Plyr {
|
|||||||
window.clearInterval(this.timers.playing);
|
window.clearInterval(this.timers.playing);
|
||||||
|
|
||||||
// Destroy YouTube API
|
// Destroy YouTube API
|
||||||
this.embed.destroy();
|
if (this.embed !== null) {
|
||||||
|
this.embed.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
done();
|
done();
|
||||||
@ -1129,7 +1151,9 @@ class Plyr {
|
|||||||
case 'vimeo:video':
|
case 'vimeo:video':
|
||||||
// Destroy Vimeo API
|
// Destroy Vimeo API
|
||||||
// then clean up (wait, to prevent postmessage errors)
|
// then clean up (wait, to prevent postmessage errors)
|
||||||
this.embed.unload().then(done);
|
if (this.embed !== null) {
|
||||||
|
this.embed.unload().then(done);
|
||||||
|
}
|
||||||
|
|
||||||
// Vimeo does not always return
|
// Vimeo does not always return
|
||||||
window.setTimeout(done, 200);
|
window.setTimeout(done, 200);
|
||||||
|
@ -67,8 +67,9 @@ const source = {
|
|||||||
|
|
||||||
case 'youtube:video':
|
case 'youtube:video':
|
||||||
case 'vimeo:video':
|
case 'vimeo:video':
|
||||||
this.media = utils.createElement('div');
|
this.media = utils.createElement('div', {
|
||||||
this.embedId = input.sources[0].src;
|
src: input.sources[0].src,
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -136,7 +137,7 @@ const source = {
|
|||||||
ui.build.call(this);
|
ui.build.call(this);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true
|
true,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
|
|
||||||
import support from './support';
|
import support from './support';
|
||||||
|
import { providers } from './types';
|
||||||
|
|
||||||
const utils = {
|
const utils = {
|
||||||
// Check variable types
|
// Check variable types
|
||||||
@ -103,7 +104,7 @@ const utils = {
|
|||||||
element.callbacks.forEach(cb => cb.call(null, event));
|
element.callbacks.forEach(cb => cb.call(null, event));
|
||||||
element.callbacks = null;
|
element.callbacks = null;
|
||||||
},
|
},
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +169,7 @@ const utils = {
|
|||||||
prefix + id,
|
prefix + id,
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
content: text,
|
content: text,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +275,17 @@ const utils = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Replace element
|
||||||
|
replaceElement(newChild, oldChild) {
|
||||||
|
if (!utils.is.element(oldChild) || !utils.is.element(oldChild.parentNode) || !utils.is.element(newChild)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
oldChild.parentNode.replaceChild(newChild, oldChild);
|
||||||
|
|
||||||
|
return newChild;
|
||||||
|
},
|
||||||
|
|
||||||
// Set attributes
|
// Set attributes
|
||||||
setAttributes(element, attributes) {
|
setAttributes(element, attributes) {
|
||||||
if (!utils.is.element(element) || utils.is.empty(attributes)) {
|
if (!utils.is.element(element) || utils.is.empty(attributes)) {
|
||||||
@ -491,7 +503,7 @@ const utils = {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -617,14 +629,37 @@ const utils = {
|
|||||||
return utils.extend(target, ...sources);
|
return utils.extend(target, ...sources);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Get the provider for a given URL
|
||||||
|
getProviderByUrl(url) {
|
||||||
|
// YouTube
|
||||||
|
if (/^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/.test(url)) {
|
||||||
|
return providers.youtube;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vimeo
|
||||||
|
if (/^https?:\/\/player.vimeo.com\/video\/\d{8,}(?=\b|\/)/.test(url)) {
|
||||||
|
return providers.vimeo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
// Parse YouTube ID from URL
|
// Parse YouTube ID from URL
|
||||||
parseYouTubeId(url) {
|
parseYouTubeId(url) {
|
||||||
|
if (utils.is.empty(url)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/;
|
const regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/;
|
||||||
return url.match(regex) ? RegExp.$2 : url;
|
return url.match(regex) ? RegExp.$2 : url;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Parse Vimeo ID from URL
|
// Parse Vimeo ID from URL
|
||||||
parseVimeoId(url) {
|
parseVimeoId(url) {
|
||||||
|
if (utils.is.empty(url)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (utils.is.number(Number(url))) {
|
if (utils.is.number(Number(url))) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
@ -633,8 +668,40 @@ const utils = {
|
|||||||
return url.match(regex) ? RegExp.$2 : url;
|
return url.match(regex) ? RegExp.$2 : url;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Convert a URL to a location object
|
||||||
|
parseUrl(url) {
|
||||||
|
const parser = document.createElement('a');
|
||||||
|
parser.href = url;
|
||||||
|
return parser;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Get URL query parameters
|
||||||
|
getUrlParams(input) {
|
||||||
|
let search = input;
|
||||||
|
|
||||||
|
// Parse URL if needed
|
||||||
|
if (input.startsWith('http://') || input.startsWith('https://')) {
|
||||||
|
({ search } = this.parseUrl(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.is.empty(search)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashes = search.slice(search.indexOf('?') + 1).split('&');
|
||||||
|
|
||||||
|
return hashes.reduce((params, hash) => {
|
||||||
|
const [
|
||||||
|
key,
|
||||||
|
val,
|
||||||
|
] = hash.split('=');
|
||||||
|
|
||||||
|
return Object.assign(params, { [key]: decodeURIComponent(val) });
|
||||||
|
}, {});
|
||||||
|
},
|
||||||
|
|
||||||
// Convert object to URL parameters
|
// Convert object to URL parameters
|
||||||
buildUrlParameters(input) {
|
buildUrlParams(input) {
|
||||||
if (!utils.is.object(input)) {
|
if (!utils.is.object(input)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
height: 0;
|
height: 0;
|
||||||
padding-bottom: to-percentage($padding);
|
padding-bottom: to-percentage($padding);
|
||||||
|
position: relative;
|
||||||
|
|
||||||
iframe {
|
iframe {
|
||||||
border: 0;
|
border: 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user