AutoPlayable = true; function checkEditorStateVideo($that) { if (jQuery($that).parents('div[contenteditable]').attr('contenteditable') == 'false') { let hasBeenAnimated = false; jQuery(window).scroll(function() { if (jQuery($that).isInViewportVideo($that) && !hasBeenAnimated) { hasBeenAnimated = true; $that.dispatchEvent(new CustomEvent('click')); } }); window.dispatchEvent(new CustomEvent('scroll')); } } jQuery.fn.isInViewportVideo = function ($that) { if (typeof jQuery($that)[0] != 'undefined' && jQuery($that)[0].length != 0) { let elementTop = jQuery($that).offset().top; let elementBottom = elementTop + jQuery($that).outerHeight(); let viewportTop = jQuery(window).scrollTop(); let viewportBottom = viewportTop + window.innerHeight; return elementBottom > viewportTop && elementTop < viewportBottom; } else return true; }; class LiteYTEmbed extends HTMLElement { connectedCallback() { window.videoApiLoaded = []; window.videoApiLoaded.youtube = false; this.videoId = this.getAttribute('videoid'); this.height = jQuery(this).css('height'); this.autoPlay = this.getAttribute('autoplaycheck'); var imageSrc = ''; var $that = this; if (typeof jQuery(this).attr('data-thumbnail-link') != 'undefined' && jQuery(this).attr('data-thumbnail-link') != 'undefined') { imageSrc = jQuery(this).attr('data-thumbnail-link'); } //jQuery(this).empty(); let playBtnEl = this.querySelector('.lty-playbtn'); // A label for the button takes priority over a [playlabel] attribute on the custom-element this.playLabel = (playBtnEl && playBtnEl.textContent.trim()) || this.getAttribute('playlabel') || 'Play'; var widget = jQuery('.widget'); widget.each(function () { var _this = this; jQuery(_this).children().children().first().find('.insertedImageContainer').each(function() { if (jQuery(this).parent().prop("tagName") != 'LITE-YOUTUBE' && jQuery(this).parent().prop("tagName") != 'LITE-VIMEO') jQuery(this).remove(); }); }); if (jQuery(this).find('img').length == 0) { if (imageSrc == '') this.innerHTML = `
`; else this.innerHTML = `
`; } // Set up play button, and its visually hidden label if (!playBtnEl) { playBtnEl = document.createElement('button'); playBtnEl.type = 'button'; playBtnEl.classList.add('lty-playbtn'); jQuery(this).find('.insertedImageContainer')[0].append(playBtnEl); } if (!playBtnEl.textContent) { const playBtnLabelEl = document.createElement('span'); playBtnLabelEl.className = 'lyt-visually-hidden'; playBtnLabelEl.textContent = this.playLabel; if (jQuery(this).find('button').length == 0) { jQuery(this).find('.insertedImageContainer')[0].append(playBtnEl); playBtnEl.append(playBtnLabelEl); } } if (jQuery(this).find('button').length == 0) { jQuery(this).find('.insertedImageContainer')[0].append(playBtnEl); playBtnEl.append(playBtnLabelEl); } // On hover (or tap), warm up the TCP connections we're (likely) about to use. this.addEventListener('pointerover', LiteYTEmbed.warmConnections, {once: true}); this.addEventListener('click', this.addIframe); if (this.autoPlay == 'true' && jQuery(this).attr('playvideolocation') != 'videoModal'){ setTimeout(function () { if (jQuery(window).width() >= 782) { checkEditorStateVideo($that); } }, 500); } } static addPrefetch(kind, url, as) { const linkEl = document.createElement('link'); linkEl.rel = kind; linkEl.href = url; if (as) { linkEl.as = as; } document.head.append(linkEl); } static warmConnections() { if (LiteYTEmbed.preconnected) return; // The iframe document and most of its subresources come right off youtube.com LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube-nocookie.com'); // The botguard script is fetched off from google.com LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com'); // Not certain if these ad related domains are in the critical path. Could verify with domain-specific throttling. LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net'); LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net'); LiteYTEmbed.preconnected = true; } addIframe(e) { if (this.classList.contains('lyt-activated')) return; this.style.backgroundImage = ''; e.preventDefault(); this.classList.add('lyt-activated'); const params = new URLSearchParams(this.getAttribute('params') || []); params.append('autoplay', '1'); //params.append('allow', 'autoplay'); const iframeEl = document.createElement('div'); //iframeEl.width = 560; //iframeEl.height = 315; iframeEl.id = 'player'+this.videoId; //iframeEl.style.padding = jQuery(this).css('padding'); // No encoding necessary as [title] is safe. https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#:~:text=Safe%20HTML%20Attributes%20include //iframeEl.title = this.playLabel; //iframeEl.allow = 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture'; // iframeEl.allowFullscreen = true; // AFAIK, the encoding here isn't necessary for XSS, but we'll do it only because this is a URL // https://stackoverflow.com/q/64959723/89484 // iframeEl.src = `https://www.youtube.com/embed/${encodeURIComponent(this.videoId)}?${params.toString()}`; if (jQuery(this).attr('playvideolocation') == 'videoModal') { this.classList.remove('lyt-activated'); //iframeEl.style = "aspect-ratio: 16/9;width: 100%;height:auto;border:none;background:black;"; //loadYoutubeVideo(this.videoId); var YTPlayer = jQuery("lite-youtube[videoid='"+this.videoId+"']").YTPlayer({ videoURL: 'http://youtu.be/'+this.videoId, containment:jQuery('#siteVideosModal').find('.siteVideosBody'), mute:true, autoPlay:true, loop:false, ratio:'16/9', stopMovieOnBlur: false, }); jQuery('#player'+this.videoId).attr('style', 'width:100%;aspect-ratio:16/9;height:auto;'); jQuery("lite-youtube[videoid='"+this.videoId+"']").show(); jQuery('#siteVideosModal').modal('show'); jQuery('#closeSiteVideosModal').click(function(e) { YTPlayer.YTPPlayerDestroy(); }) jQuery('#siteVideosModal').on('hidden.bs.modal', function (e) { YTPlayer.YTPPlayerDestroy(); }); } else { jQuery(this).attr('style', jQuery(this).attr('style') + ';height:'+this.height+'!important;'); this.append(iframeEl); let myPlayer = jQuery("lite-youtube[videoid='"+this.videoId+"']").YTPlayer({ videoURL: 'http://youtu.be/'+this.videoId, containment:'self', mute:true, autoPlay:true, loop:false, ratio:'16/9', stopMovieOnBlur: false, }); //loadYoutubeVideo(this.videoId); jQuery('#player'+this.videoId).attr('style', 'padding:'+jQuery(this).css('padding')+';width:100%;height:'+this.height+';aspect-ratio:16/9;'); var _that = this; setTimeout(function() { //jQuery(_that).find('img').attr('style', 'display:none;'); }, 1500); setTimeout(function() { jQuery(_that).find('button').fadeOut(); jQuery(_that).find('.insertedImageContainer').append('') }, 300); myPlayer.on("YTPReady",function(e){ jQuery(_that).find('img').fadeOut(); }); } // Set focus for a11y iframeEl.focus(); } } var ytLoaded = false; function loadYoutubeVideo(videoId) { window.onYouTubeIframeAPIReady = function() { document.dispatchEvent(new CustomEvent('onYouTubeIframeAPIReady', {})) }; var player; if(ytLoaded === false) { var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); ytLoaded = true; } else { player = new YT.Player('player'+videoId, { height: '400', width: '600', videoId: videoId, playerVars: { 'playsinline': 1, 'autoplay': 1 }, events: { 'onReady': onPlayerReady, } }); } document.addEventListener('onYouTubeIframeAPIReady', function (e) { player = new YT.Player('player'+videoId, { height: '400', width: '600', videoId: videoId, playerVars: { 'playsinline': 1, 'autoplay': 1, 'allow': 'autoplay' }, events: { 'onReady': onPlayerReady, } }); }, false); jQuery('#player'+videoId).attr('style', 'width:100%;'); function onPlayerReady(event) { player.playVideo(); } } // Register custom element customElements.define('lite-youtube', LiteYTEmbed); class LiteVimeo extends HTMLElement { constructor() { super(); // TODO: support dynamically setting the attribute via attributeChangedCallback } connectedCallback() { // Gotta encode the untrusted value // https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-2---attribute-escape-before-inserting-untrusted-data-into-html-common-attributes this.videoId = encodeURIComponent(this.getAttribute('videoid')); this.height = jQuery(this).css('height'); var imageSrc = ''; var $that = this; this.autoPlay = this.getAttribute('autoplaycheck'); if (typeof jQuery(this).attr('data-thumbnail-link') != 'undefined' && jQuery(this).attr('data-thumbnail-link') != 'undefined') { imageSrc = jQuery(this).attr('data-thumbnail-link'); } jQuery(this).empty(); /** * Lo, the vimeo placeholder image! (aka the thumbnail, poster image, etc) * We have to use the Vimeo API. */ let { width, height } = getThumbnailDimensions(this.getBoundingClientRect()); const devicePixelRatio = window.devicePixelRatio || 1; width *= devicePixelRatio; height *= devicePixelRatio; var widget = jQuery('.widget'); widget.each(function () { var _this = this; jQuery(_this).children().children().first().find('.insertedImageContainer').each(function() { if (jQuery(this).parent().prop("tagName") != 'LITE-YOUTUBE' && jQuery(this).parent().prop("tagName") != 'LITE-VIMEO') jQuery(this).remove(); }); }); //this.style.backgroundImage = `url("${thumbnailUrl}")`; if (jQuery(this).find('img').length == 0) { if (imageSrc == '') this.innerHTML = `
`; else this.innerHTML = `
`; } const playBtn = document.createElement('button'); playBtn.type = 'button'; playBtn.classList.add('ltv-playbtn'); if (jQuery(this).find('button').length == 0) { jQuery(this).find('.insertedImageContainer')[0].append(playBtn); } jQuery(this).css('background', 'transparent'); // On hover (or tap), warm up the TCP connections we're (likely) about to use. this.addEventListener('pointerover', LiteVimeo._warmConnections, { once: true }); this.addEventListener('click', () => this._addIframe()); if (this.autoPlay == 'true' && jQuery(this).attr('playvideolocation') != 'videoModal'){ setTimeout(function () { if (jQuery(window).width() >= 782) { checkEditorStateVideo($that); } }, 500); } } static _warmConnections() { if (LiteVimeo.preconnected) return; // The iframe document and most of its subresources come right off player.vimeo.com addPrefetch('preconnect', 'https://player.vimeo.com'); // Images addPrefetch('preconnect', 'https://i.vimeocdn.com'); // Files .js, .css addPrefetch('preconnect', 'https://f.vimeocdn.com'); // Metrics addPrefetch('preconnect', 'https://fresnel.vimeocdn.com'); LiteVimeo.preconnected = true; } _addIframe() { this.classList.add('ltv-activated'); jQuery(this).find('img').css('object-fit','contain') const iframeHTML = ` `; if (jQuery(this).attr('playvideolocation') == 'videoModal') { this.classList.remove('ltv-activated'); const iframeHTML = ` `; jQuery('#siteVideosModal').find('.siteVideosBody').html(iframeHTML); jQuery('#siteVideosModal').modal('show'); } else { jQuery(this).attr('style', jQuery(this).attr('style') + ';height:'+this.height+'!important;'); this.insertAdjacentHTML('beforeend', iframeHTML); } } } // Register custome element customElements.define('lite-vimeo', LiteVimeo); /** * Add a to the head */ function addPrefetch(kind, url, as) { const linkElem = document.createElement('link'); linkElem.rel = kind; linkElem.href = url; if (as) { linkElem.as = as; } linkElem.crossorigin = true; document.head.appendChild(linkElem); } function canUseWebP() { var elem = document.createElement('canvas'); if (elem.getContext && elem.getContext('2d')) { // was able or not to get WebP representation return elem.toDataURL('image/webp').indexOf('data:image/webp') === 0; } // very old browser like IE 8, canvas not supported return false; } /** * Get the thumbnail dimensions to use for a given player size. * * @param {Object} options * @param {number} options.width The width of the player * @param {number} options.height The height of the player * @return {Object} The width and height */ function getThumbnailDimensions({ width, height }) { let roundedWidth = width; let roundedHeight = height; if (roundedWidth % 320 !== 0) { roundedWidth = Math.ceil(width / 100) * 100; roundedHeight = Math.round((roundedWidth / width) * height); } return { width: roundedWidth, height: roundedHeight }; }