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 `playsinline` support for iOS 10+ | ||||
| * Soundcloud removed until I can work on a plugin framework | ||||
| * Embedded players are now progressively enhanced - no more empty `<div>`s! | ||||
|  | ||||
| ### 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). | ||||
|  | ||||
| ## v2.0.18 | ||||
| - Fix for YouTube .getVideoData() issue (fixes #709) | ||||
|  | ||||
| * Fix for YouTube .getVideoData() issue (fixes #709) | ||||
|  | ||||
| ## v2.0.17 | ||||
| - Vimeo controls fix (fixes #697) | ||||
| - SVG4everybody compatibility fix | ||||
| - Allow Plyr.setup event listeners to be set up as separate event listeners (https://github.com/sampotts/plyr/pull/703) | ||||
| - Added title to the layer html template (for custom controls) (https://github.com/sampotts/plyr/pull/649) | ||||
| - 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) | ||||
|  | ||||
| * Vimeo controls fix (fixes #697) | ||||
| * SVG4everybody compatibility fix | ||||
| * Allow Plyr.setup event listeners to be set up as separate event listeners (https://github.com/sampotts/plyr/pull/703) | ||||
| * Added title to the layer html template (for custom controls) (https://github.com/sampotts/plyr/pull/649) | ||||
| * 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 | ||||
| - Fullscreen bug fix (fixes #664) | ||||
|  | ||||
| * Fullscreen bug fix (fixes #664) | ||||
|  | ||||
| ## v2.0.15 | ||||
| - Demo fix | ||||
|  | ||||
| * Demo fix | ||||
|  | ||||
| ## 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 | ||||
| - Repo moved and Vimeo demo fix | ||||
|  | ||||
| * Repo moved and Vimeo demo fix | ||||
|  | ||||
| ## 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", | ||||
|     "version": "3.0.0-beta.1", | ||||
|     "version": "3.0.0-beta.2", | ||||
|     "description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player", | ||||
|     "homepage": "https://plyr.io", | ||||
|     "main": "./dist", | ||||
| @ -34,7 +34,7 @@ | ||||
|         "gulp-util": "^3.0.8", | ||||
|         "rollup-plugin-babel": "^3.0.3", | ||||
|         "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", | ||||
|         "run-sequence": "^2.2.1", | ||||
|         "stylelint": "^8.4.0", | ||||
| @ -46,15 +46,7 @@ | ||||
|         "stylelint-selector-bem-pattern": "^2.0.0", | ||||
|         "uglify-es": "^3.3.5" | ||||
|     }, | ||||
|     "keywords": [ | ||||
|         "HTML5 Video", | ||||
|         "HTML5 Audio", | ||||
|         "Media Player", | ||||
|         "DASH", | ||||
|         "Shaka", | ||||
|         "WordPress", | ||||
|         "HLS" | ||||
|     ], | ||||
|     "keywords": ["HTML5 Video", "HTML5 Audio", "Media Player", "DASH", "Shaka", "WordPress", "HLS"], | ||||
|     "repository": { | ||||
|         "type": "git", | ||||
|         "url": "git://github.com/sampotts/plyr.git" | ||||
|  | ||||
							
								
								
									
										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> | ||||
| ``` | ||||
|  | ||||
| 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 | ||||
|  | ||||
| ```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 | ||||
|  | ||||
| ```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 | ||||
|  | ||||
| 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: | ||||
|  | ||||
| ```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 | ||||
| @ -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: | ||||
|  | ||||
| ```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 | ||||
|  | ||||
| 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 | ||||
|  | ||||
| @ -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')); | ||||
| ``` | ||||
|  | ||||
| The NodeList, HTMLElement or string selector can be the target `<video>`, `<audio>` or `[data-plyr-provider]` (for embeds) element itself or a container | ||||
| element. | ||||
| The NodeList, HTMLElement or string selector can be the target `<video>`, `<audio>`, or `<div>` wrapper for embeds | ||||
|  | ||||
| The second argument for the constructor is the [#options](options) object: | ||||
|  | ||||
|  | ||||
| @ -5,8 +5,8 @@ | ||||
| const noop = () => {}; | ||||
|  | ||||
| export default class Console { | ||||
|     constructor(player) { | ||||
|         this.enabled = window.console && player.config.debug; | ||||
|     constructor(enabled = false) { | ||||
|         this.enabled = window.console && enabled; | ||||
|  | ||||
|         if (this.enabled) { | ||||
|             this.log('Debugging enabled'); | ||||
|  | ||||
| @ -50,7 +50,7 @@ const media = { | ||||
|         } | ||||
|  | ||||
|         // Inject the player wrapper | ||||
|         if (this.isVideo || this.isYouTube || this.isVimeo) { | ||||
|         if (this.isVideo) { | ||||
|             // Create the wrapper div | ||||
|             this.elements.wrapper = utils.createElement('div', { | ||||
|                 class: this.config.classNames.video, | ||||
|  | ||||
| @ -8,19 +8,12 @@ import ui from './../ui'; | ||||
|  | ||||
| const vimeo = { | ||||
|     setup() { | ||||
|         // Remove old containers | ||||
|         const containers = utils.getElements.call(this, `[id^="${this.provider}-"]`); | ||||
|         Array.from(containers).forEach(utils.removeElement); | ||||
|  | ||||
|         // Add embed class for responsive | ||||
|         utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true); | ||||
|  | ||||
|         // Set intial ratio | ||||
|         vimeo.setAspectRatio.call(this); | ||||
|  | ||||
|         // Set ID | ||||
|         this.media.setAttribute('id', utils.generateId(this.provider)); | ||||
|  | ||||
|         // Load the API if not already | ||||
|         if (!utils.is.object(window.Vimeo)) { | ||||
|             utils.loadScript(this.config.urls.vimeo.api, () => { | ||||
| @ -57,15 +50,21 @@ const vimeo = { | ||||
|             transparent: 0, | ||||
|             gesture: 'media', | ||||
|         }; | ||||
|         const params = utils.buildUrlParameters(options); | ||||
|         const id = utils.parseVimeoId(player.embedId); | ||||
|         const params = utils.buildUrlParams(options); | ||||
|         const id = utils.parseVimeoId(player.media.getAttribute('src')); | ||||
|  | ||||
|         // Build an iframe | ||||
|         const iframe = utils.createElement('iframe'); | ||||
|         const src = `https://player.vimeo.com/video/${id}?${params}`; | ||||
|         iframe.setAttribute('src', src); | ||||
|         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 | ||||
|         // https://github.com/vimeo/player.js | ||||
|  | ||||
| @ -8,24 +8,15 @@ import ui from './../ui'; | ||||
|  | ||||
| const youtube = { | ||||
|     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 | ||||
|         utils.toggleClass(this.elements.wrapper, this.config.classNames.embed, true); | ||||
|  | ||||
|         // Set aspect ratio | ||||
|         youtube.setAspectRatio.call(this); | ||||
|  | ||||
|         // Set ID | ||||
|         this.media.setAttribute('id', utils.generateId(this.provider)); | ||||
|  | ||||
|         // Setup API | ||||
|         if (utils.is.object(window.YT)) { | ||||
|             youtube.ready.call(this, videoId); | ||||
|         if (utils.is.object(window.YT) && utils.is.function(window.YT.Player)) { | ||||
|             youtube.ready.call(this); | ||||
|         } else { | ||||
|             // Load the API | ||||
|             utils.loadScript(this.config.urls.youtube.api); | ||||
| @ -36,7 +27,7 @@ const youtube = { | ||||
|  | ||||
|             // Add to queue | ||||
|             window.onYouTubeReadyCallbacks.push(() => { | ||||
|                 youtube.ready.call(this, videoId); | ||||
|                 youtube.ready.call(this); | ||||
|             }); | ||||
|  | ||||
|             // Set callback to process queue | ||||
| @ -49,7 +40,7 @@ const youtube = { | ||||
|     }, | ||||
|  | ||||
|     // Get the media title | ||||
|     getTitle() { | ||||
|     getTitle(videoId) { | ||||
|         // Try via undocumented API method first | ||||
|         // This method disappears now and then though... | ||||
|         // https://github.com/sampotts/plyr/issues/709 | ||||
| @ -65,7 +56,6 @@ const youtube = { | ||||
|  | ||||
|         // Or via Google API | ||||
|         const key = this.config.keys.google; | ||||
|         const videoId = utils.parseYouTubeId(this.embedId); | ||||
|         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`; | ||||
|  | ||||
| @ -88,12 +78,24 @@ const youtube = { | ||||
|     }, | ||||
|  | ||||
|     // API ready | ||||
|     ready(videoId) { | ||||
|     ready() { | ||||
|         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 | ||||
|         // 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, | ||||
|             playerVars: { | ||||
|                 autoplay: player.config.autoplay ? 1 : 0, // Autoplay | ||||
| @ -110,8 +112,8 @@ const youtube = { | ||||
|                 widget_referrer: window && window.location.href, | ||||
|  | ||||
|                 // Captions are flaky on YouTube | ||||
|                 cc_load_policy: this.captions.active ? 1 : 0, | ||||
|                 cc_lang_pref: this.config.captions.language, | ||||
|                 cc_load_policy: player.captions.active ? 1 : 0, | ||||
|                 cc_lang_pref: player.config.captions.language, | ||||
|             }, | ||||
|             events: { | ||||
|                 onError(event) { | ||||
| @ -179,7 +181,7 @@ const youtube = { | ||||
|                     const instance = event.target; | ||||
|  | ||||
|                     // Get the title | ||||
|                     youtube.getTitle.call(player); | ||||
|                     youtube.getTitle.call(player, videoId); | ||||
|  | ||||
|                     // Create a faux HTML5 API using the YouTube API | ||||
|                     player.media.play = () => { | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| // ========================================================================== | ||||
| // Plyr | ||||
| // plyr.js v3.0.0-beta.1 | ||||
| // plyr.js v3.0.0-beta.2 | ||||
| // https://github.com/sampotts/plyr | ||||
| // License: The MIT License (MIT) | ||||
| // ========================================================================== | ||||
| @ -66,7 +66,7 @@ class Plyr { | ||||
|                 } catch (e) { | ||||
|                     return {}; | ||||
|                 } | ||||
|             })() | ||||
|             })(), | ||||
|         ); | ||||
|  | ||||
|         // Elements cache | ||||
| @ -103,7 +103,7 @@ class Plyr { | ||||
|  | ||||
|         // Debugging | ||||
|         // TODO: move to globals | ||||
|         this.debug = new Console(this); | ||||
|         this.debug = new Console(this.config.debug); | ||||
|  | ||||
|         // Log config options and support | ||||
|         this.debug.log('Config', this.config); | ||||
| @ -141,35 +141,61 @@ class Plyr { | ||||
|         // Supported: video, audio, vimeo, youtube | ||||
|         const type = this.media.tagName.toLowerCase(); | ||||
|  | ||||
|         // Embed attributes | ||||
|         const attributes = { | ||||
|             provider: 'data-plyr-provider', | ||||
|             id: 'data-plyr-embed-id', | ||||
|         }; | ||||
|         // Embed properties | ||||
|         let iframe = null; | ||||
|         let url = null; | ||||
|         let params = null; | ||||
|  | ||||
|         // Different setup based on type | ||||
|         switch (type) { | ||||
|             // TODO: Handle passing an iframe for true progressive enhancement | ||||
|             // case 'iframe': | ||||
|             case 'div': | ||||
|                 this.type = types.video; // Audio will come later for external providers | ||||
|                 this.provider = this.media.getAttribute(attributes.provider); | ||||
|                 this.embedId = this.media.getAttribute(attributes.id); | ||||
|                 // Find the frame | ||||
|                 iframe = this.media.querySelector('iframe'); | ||||
|  | ||||
|                 // <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)) { | ||||
|                     this.debug.error('Setup failed: Invalid provider'); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 // Try and get the embed id | ||||
|                 if (utils.is.empty(this.embedId)) { | ||||
|                     this.debug.error('Setup failed: Embed ID or URL missing'); | ||||
|                     return; | ||||
|                 } | ||||
|                 // Rework elements | ||||
|                 this.elements.container = this.media; | ||||
|                 this.media = iframe; | ||||
|  | ||||
|                 // Clean up | ||||
|                 this.media.removeAttribute(attributes.provider); | ||||
|                 this.media.removeAttribute(attributes.id); | ||||
|                 // Reset classname | ||||
|                 this.elements.container.className = ''; | ||||
|  | ||||
|                 break; | ||||
|  | ||||
| @ -178,22 +204,19 @@ class Plyr { | ||||
|                 this.type = type; | ||||
|                 this.provider = providers.html5; | ||||
|  | ||||
|                 // Get config from attributes | ||||
|                 if (this.media.hasAttribute('crossorigin')) { | ||||
|                     this.config.crossorigin = true; | ||||
|                 } | ||||
|  | ||||
|                 if (this.media.hasAttribute('autoplay')) { | ||||
|                     this.config.autoplay = true; | ||||
|                 } | ||||
|  | ||||
|                 if (this.media.hasAttribute('playsinline')) { | ||||
|                     this.config.inline = true; | ||||
|                 } | ||||
|  | ||||
|                 if (this.media.hasAttribute('muted')) { | ||||
|                     this.config.muted = true; | ||||
|                 } | ||||
|  | ||||
|                 if (this.media.hasAttribute('loop')) { | ||||
|                     this.config.loop.active = true; | ||||
|                 } | ||||
| @ -221,8 +244,10 @@ class Plyr { | ||||
|         this.media.plyr = this; | ||||
|  | ||||
|         // Wrap media | ||||
|         if (!utils.is.element(this.elements.container)) { | ||||
|             this.elements.container = utils.createElement('div'); | ||||
|             utils.wrap(this.media, this.elements.container); | ||||
|         } | ||||
|  | ||||
|         // Allow focus to be captured | ||||
|         this.elements.container.setAttribute('tabindex', 0); | ||||
| @ -1054,7 +1079,6 @@ class Plyr { | ||||
|  | ||||
|             // GC for embed | ||||
|             this.embed = null; | ||||
|             this.embedId = null; | ||||
|  | ||||
|             // If it's a soft destroy, make minimal changes | ||||
|             if (soft) { | ||||
| @ -1082,11 +1106,7 @@ class Plyr { | ||||
|                 } | ||||
|             } else { | ||||
|                 // Replace the container with the original element provided | ||||
|                 const parent = this.elements.container.parentNode; | ||||
|  | ||||
|                 if (utils.is.element(parent)) { | ||||
|                     parent.replaceChild(this.elements.original, this.elements.container); | ||||
|                 } | ||||
|                 utils.replaceElement(this.elements.original, this.elements.container); | ||||
|  | ||||
|                 // Event | ||||
|                 utils.dispatchEvent.call(this, this.elements.original, 'destroyed', true); | ||||
| @ -1119,7 +1139,9 @@ class Plyr { | ||||
|                 window.clearInterval(this.timers.playing); | ||||
|  | ||||
|                 // Destroy YouTube API | ||||
|                 if (this.embed !== null) { | ||||
|                     this.embed.destroy(); | ||||
|                 } | ||||
|  | ||||
|                 // Clean up | ||||
|                 done(); | ||||
| @ -1129,7 +1151,9 @@ class Plyr { | ||||
|             case 'vimeo:video': | ||||
|                 // Destroy Vimeo API | ||||
|                 // then clean up (wait, to prevent postmessage errors) | ||||
|                 if (this.embed !== null) { | ||||
|                     this.embed.unload().then(done); | ||||
|                 } | ||||
|  | ||||
|                 // Vimeo does not always return | ||||
|                 window.setTimeout(done, 200); | ||||
|  | ||||
| @ -67,8 +67,9 @@ const source = { | ||||
|  | ||||
|                     case 'youtube:video': | ||||
|                     case 'vimeo:video': | ||||
|                         this.media = utils.createElement('div'); | ||||
|                         this.embedId = input.sources[0].src; | ||||
|                         this.media = utils.createElement('div', { | ||||
|                             src: input.sources[0].src, | ||||
|                         }); | ||||
|                         break; | ||||
|  | ||||
|                     default: | ||||
| @ -136,7 +137,7 @@ const source = { | ||||
|                     ui.build.call(this); | ||||
|                 } | ||||
|             }, | ||||
|             true | ||||
|             true, | ||||
|         ); | ||||
|     }, | ||||
| }; | ||||
|  | ||||
| @ -3,6 +3,7 @@ | ||||
| // ========================================================================== | ||||
|  | ||||
| import support from './support'; | ||||
| import { providers } from './types'; | ||||
|  | ||||
| const utils = { | ||||
|     // Check variable types | ||||
| @ -103,7 +104,7 @@ const utils = { | ||||
|                     element.callbacks.forEach(cb => cb.call(null, event)); | ||||
|                     element.callbacks = null; | ||||
|                 }, | ||||
|                 false | ||||
|                 false, | ||||
|             ); | ||||
|         } | ||||
|  | ||||
| @ -168,7 +169,7 @@ const utils = { | ||||
|                             prefix + id, | ||||
|                             JSON.stringify({ | ||||
|                                 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 | ||||
|     setAttributes(element, attributes) { | ||||
|         if (!utils.is.element(element) || utils.is.empty(attributes)) { | ||||
| @ -491,7 +503,7 @@ const utils = { | ||||
|                     event.preventDefault(); | ||||
|                 } | ||||
|             }, | ||||
|             false | ||||
|             false, | ||||
|         ); | ||||
|     }, | ||||
|  | ||||
| @ -617,14 +629,37 @@ const utils = { | ||||
|         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 | ||||
|     parseYouTubeId(url) { | ||||
|         if (utils.is.empty(url)) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         const regex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/; | ||||
|         return url.match(regex) ? RegExp.$2 : url; | ||||
|     }, | ||||
|  | ||||
|     // Parse Vimeo ID from URL | ||||
|     parseVimeoId(url) { | ||||
|         if (utils.is.empty(url)) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         if (utils.is.number(Number(url))) { | ||||
|             return url; | ||||
|         } | ||||
| @ -633,8 +668,40 @@ const utils = { | ||||
|         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 | ||||
|     buildUrlParameters(input) { | ||||
|     buildUrlParams(input) { | ||||
|         if (!utils.is.object(input)) { | ||||
|             return ''; | ||||
|         } | ||||
|  | ||||
| @ -11,6 +11,7 @@ | ||||
|  | ||||
|     height: 0; | ||||
|     padding-bottom: to-percentage($padding); | ||||
|     position: relative; | ||||
|  | ||||
|     iframe { | ||||
|         border: 0; | ||||
|  | ||||
		Reference in New Issue
	
	Block a user