Progressively enhance <iframe> embeds
This commit is contained in:
		
							
								
								
									
										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 | ||||||
|  |         if (!utils.is.element(this.elements.container)) { | ||||||
|             this.elements.container = utils.createElement('div'); |             this.elements.container = utils.createElement('div'); | ||||||
|             utils.wrap(this.media, this.elements.container); |             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 | ||||||
|  |                 if (this.embed !== null) { | ||||||
|                     this.embed.destroy(); |                     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) | ||||||
|  |                 if (this.embed !== null) { | ||||||
|                     this.embed.unload().then(done); |                     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; | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user