Progressively enhance <iframe> embeds

This commit is contained in:
Sam Potts 2018-01-12 19:35:46 +11:00
parent 2e5d06ad85
commit d9ec1d1b8e
15 changed files with 204 additions and 110 deletions

View File

@ -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

File diff suppressed because one or more lines are too long

2
dist/plyr.css vendored

File diff suppressed because one or more lines are too long

2
dist/plyr.js vendored

File diff suppressed because one or more lines are too long

2
dist/plyr.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -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"

View File

@ -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&amp;iv_load_policy=3&amp;modestbranding=1&amp;playsinline=1&amp;showinfo=0&amp;rel=0&amp;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&amp;byline=false&amp;portrait=false&amp;title=false&amp;speed=true&amp;transparent=0&amp;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:

View File

@ -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');

View File

@ -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,

View File

@ -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

View File

@ -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 = () => {

View File

@ -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);

View File

@ -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,
); );
}, },
}; };

View File

@ -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 '';
} }

View File

@ -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;