diff --git a/README.md b/README.md index 1eea92d..64558d0 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,8 @@ lazyframe(elements, { debounce: 100, lazyload: true, autoplay: true, - + youtubeThumbnailQuality: 'hq', + youtubeThumbnailImage: 'default', // Callbacks onLoad: (lazyframe) => console.log(lazyframe), onAppend: (iframe) => console.log(iframe), @@ -108,6 +109,14 @@ Set this to `false` if you want all API calls and local images to be loaded on p Set this to `false` to remove autoplay from the `allow` attribute on the iframe tag i.e if set this to `false` if you want don't want your Youtube video to automatically start playing once the user clicks on the play icon. +### youtubeThumbnailQuality + +Defines the thumbnail quality to use from Youtubes thumbnail service. Possible values are '', 'sd', 'mq', 'hq' and 'maxres' + +### youtubeThumbnailImage + +Defines the thumbnail image to use from Youtubes thumbnail service. Possible values are 'default', '1', '2' and '3' + ### `onLoad` Callback function for when a element is initialized. @@ -131,6 +140,8 @@ Callback function with the thumbnail URL data-src="" data-ratio="1:1" data-initinview="false" + data-youtube-thumbnail-image="default" + data-youtube-thumbnail-quality="hq" > ``` @@ -158,6 +169,14 @@ The ratio of the lazyframe. Possible values: 16:9, 4:3, 1:1 Set this to true if you want the resource to execute (for example video to play) when the element is in view. +### data-youtube-thumbnail-image + +Defines the thumbnail image to use from Youtubes thumbnail service. Possible values are 'default', '1', '2' and '3'. + +### data-youtube-thumbnail-quality + +Defines the thumbnail quality to use from Youtubes thumbnail service. Possible values are '', 'sd', 'mq', 'hq' and 'maxres' + ## License [MIT](https://opensource.org/licenses/MIT). © 2016 Viktor Bergehall diff --git a/src/lazyframe.ts b/src/lazyframe.ts index 2c4960f..19ca16c 100644 --- a/src/lazyframe.ts +++ b/src/lazyframe.ts @@ -4,7 +4,22 @@ interface LazyframeObject extends LazyframeSettings { node: HTMLElement } -interface LazyframeSettings { +interface LazyframeOptions { + debounce: number + lazyload: boolean + autoplay: boolean + youtubeThumbnailQuality: '' | 'sd' | 'mq' | 'hq' | 'maxres' + youtubeThumbnailImage: 'default' | '1' | '2' | '3' + onLoad: (node: HTMLElement) => void + onAppend: (node: HTMLElement | null) => void + onThumbnailLoad: (url: string) => void +} + +type OverrideAbleSettings = Pick< + LazyframeOptions, + 'youtubeThumbnailImage' | 'youtubeThumbnailQuality' +> +interface LazyframeSettings extends OverrideAbleSettings { src: string embed: string title: string | null @@ -18,23 +33,27 @@ interface LazyframeSettings { iframeNode: DocumentFragment } -interface LazyframeOptions { - debounce: number - lazyload: boolean - autoplay: boolean - onLoad: (node: HTMLElement) => void - onAppend: (node: HTMLElement | null) => void - onThumbnailLoad: (url: string) => void -} - const Lazyframe = () => { const findVendorIdAndQuery = new RegExp( /^(?:https?:\/\/)?(?:www\.)?(youtube-nocookie|youtube|vimeo)(?:\.com)?\/?.*(?:watch|embed)?(?:.*v=|v\/|\/)([\w-_]+)(?:\&|\?|\/\?)?(.+)?$/ ) - const classNames = { - loaded: 'lazyframe--loaded', - title: 'lazyframe__title', + const youtube = { + quality: { + values: ['', 'sd', 'mq', 'hq', 'maxres'], + default: 'hq' as LazyframeSettings['youtubeThumbnailQuality'], + }, + image: { + values: ['default', '1', '2', '3'], + default: 'default' as LazyframeSettings['youtubeThumbnailImage'], + }, + isYoutube: (vendor: LazyframeSettings['vendor']) => + vendor && vendor.indexOf('youtube') > -1, + } + + enum Classes { + LOADED = 'lazyframe--loaded', + TITLE = 'lazyframe__title', } const nodes: LazyframeObject[] = [] @@ -43,6 +62,8 @@ const Lazyframe = () => { debounce: 100, lazyload: true, autoplay: true, + youtubeThumbnailQuality: youtube.quality.default, + youtubeThumbnailImage: youtube.image.default, onLoad: () => {}, onAppend: () => {}, onThumbnailLoad: () => {}, @@ -75,7 +96,7 @@ const Lazyframe = () => { } function create(node: HTMLElement) { - if (node.classList.contains(classNames.loaded)) { + if (node.classList.contains(Classes.LOADED)) { return } @@ -106,7 +127,7 @@ const Lazyframe = () => { const fragment = document.createDocumentFragment() const titleNode = document.createElement('span') - titleNode.className = classNames.title + titleNode.className = Classes.TITLE fragment.appendChild(titleNode) if (lazyframe.title) { @@ -122,6 +143,8 @@ const Lazyframe = () => { const thumbnail = node.getAttribute('data-thumbnail') const initinview = node.getAttribute('data-initinview') === 'true' + const youtube = getYoutubeSettings(node) + if (!src) { throw new Error('You must supply a data-src on the node') } @@ -152,6 +175,23 @@ const Lazyframe = () => { query, initialized: false, iframeNode: createIframeNode(vendor, query, src, id), + youtubeThumbnailQuality: youtube.quality, + youtubeThumbnailImage: youtube.image, + } + } + + function getYoutubeSettings(node: HTMLElement) { + let quality = node.getAttribute('data-youtube-thumbnail-quality') + if (!quality || !youtube.quality.values.includes(quality)) { + quality = settings.youtubeThumbnailQuality + } + let image = node.getAttribute('data-youtube-thumbnail-image') + if (!image || !youtube.image.values.includes(image)) { + image = settings.youtubeThumbnailImage + } + return { + image: image as LazyframeSettings['youtubeThumbnailImage'], + quality: quality as LazyframeSettings['youtubeThumbnailQuality'], } } @@ -163,7 +203,21 @@ const Lazyframe = () => { lazyframe.title = json.title } if (json.thumbnail_url) { - lazyframe.thumbnail = json.thumbnail_url + if (youtube.isYoutube(lazyframe.vendor)) { + if ( + lazyframe.youtubeThumbnailImage === youtube.image.default && + lazyframe.youtubeThumbnailQuality === youtube.quality.default + ) { + lazyframe.thumbnail = json.thumbnail_url + } + lazyframe.thumbnail = json.thumbnail_url.replace( + new RegExp(youtube.quality.default + youtube.image.default), + lazyframe.youtubeThumbnailQuality + + lazyframe.youtubeThumbnailImage + ) + } else { + lazyframe.thumbnail = json.thumbnail_url + } } return lazyframe }) @@ -178,7 +232,7 @@ const Lazyframe = () => { } function populatePlaceholder(lazyframe: LazyframeObject) { - const titleNode = lazyframe.node.querySelector('.lazyframe__title') + const titleNode = lazyframe.node.querySelector(`.${Classes.TITLE}`) if (lazyframe.title && titleNode && titleNode.innerHTML === '') { titleNode.innerHTML = lazyframe.title @@ -191,7 +245,7 @@ const Lazyframe = () => { } function onLoad(lazyframe: LazyframeObject) { - lazyframe.node.classList.add(classNames.loaded) + lazyframe.node.classList.add(Classes.LOADED) lazyframe.initialized = true if (lazyframe.initinview) { lazyframe.node.click() @@ -242,7 +296,7 @@ const Lazyframe = () => { } function createIframeNode( - vendor: LazyframeObject['vendor'] | '' = '', + vendor: LazyframeSettings['vendor'], query: string, src: string, id: string @@ -251,7 +305,7 @@ const Lazyframe = () => { const iframeNode = document.createElement('iframe') let embed = src - if (vendor && vendor.indexOf('youtube') > -1) { + if (youtube.isYoutube(vendor)) { embed = `https://www.${vendor}.com/embed/${id}/?${query}` } else if (vendor === 'vimeo') { embed = `https://player.vimeo.com/video/${id}/?${query}` diff --git a/test/browser.js b/test/browser.js index 20b13b0..f4a5d46 100644 --- a/test/browser.js +++ b/test/browser.js @@ -171,7 +171,7 @@ test('should append optional query params from data-src', async (t) => { node.click() const iframe = node.querySelector('iframe') const src = iframe.getAttribute('src') - const [, iframQuery] = src.split('?autoplay=1&') + const [, iframeQuery] = src.split('?autoplay=1&') - t.is(iframQuery, query) + t.is(iframeQuery, query) })