1/** 2 * Video lightbox controller 3 * - Requires lib.ajax.js 4 * - Requires bootstrap.min.css(.map) 5 * - Requires web.css, web.js 6 * - Requires /img/evertz/loading.214px.webp 7 * - Requires /img/evertz/loading.428px.webp 8 * - Requires /img/evertz/loading.856px.webp 9 * 10 * @param video: must be an object containing: 11 * - 'link' => An 11 character YouTube code OR a link URL 12 * - 'resolution' => Video resolution; (req'd for link URLs) 13 * - Must be one of the following string values: 14 * - 320p, 480p, 720p, 1080p, 2160p, 4K 15 * 16 * ... OR an array of such objects. Video link types cannot be 17 * mixed within a single call; you can't use YouTube and link URLs 18 * in the same array of objects. In the case of using link URLs, the 19 * multiple videos should be different resolutions of the same video 20 * to provide a responsive video experience. See the _template.php 21 * file for examples of embedding both types of video. 22 * 23 * @param autoplay: true/false - autoplay the video when displayed 24 * 25 * - To close the lightbox, call the function with an empty string as 26 * the first argument 27 * 28 * - Example calls: 29 * PAGE_LIGHTBOX.activate({"link":"eBGIQ7ZuuiU"}, false); // YouTube 30 * PAGE_LIGHTBOX.activate([{"link":"/video/nucleus-480p.mp4","resolution":"480p"},{"link":"/video/nucleus-1080p.mp4","resolution":"1080p"}], false); 31 * 32 */ 33let PAGE_LIGHTBOX = document.getElementById('videoLightbox'); 34if (!PAGE_LIGHTBOX) { 35 // Create the lightbox and add it to the page 36 PAGE_LIGHTBOX = document.createElement('div'); 37 PAGE_LIGHTBOX.classList.add('w-100', 'h-100', 'opacity-0', 'invisible', 'position-fixed', 'top-0', 'start-0'); 38 PAGE_LIGHTBOX.style.zIndex = 2000; 39 PAGE_LIGHTBOX.style.background = 'rgba(0,0,0,0.7) none no-repeat scroll center center'; 40 PAGE_LIGHTBOX.style.backgroundSize = '50vw'; 41 PAGE_LIGHTBOX.style.transition = 'opacity 0.2s linear'; 42 PAGE_LIGHTBOX.id = 'videoLightbox'; 43 let div1 = document.createElement('div'); 44 div1.classList.add('video', 'd-table', 'w-100', 'h-100', 'opacity-0', 'position-absolute', 'top-0', 'start-0'); 45 div1.style.transition = 'opacity 0.2s linear'; 46 let div2 = document.createElement('div'); 47 div2.classList.add('d-table-cell', 'text-center', 'align-middle'); 48 div1.appendChild(div2); 49 let figure = document.createElement('figure'); 50 figure.classList.add('position-absolute'); 51 figure.style.top = figure.style.right = '1.5rem'; 52 figure.style.width = figure.style.height = '2.4rem'; 53 figure.title = 'Close video'; 54 let img = document.createElement('img'); 55 img.src = 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="%23fff"><path d="M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z"/></svg>'; 56 img.alt = 'Close'; 57 img.setAttribute('role', 'button'); 58 img.classList.add('d-block', 'w-100', 'h-100'); 59 figure.appendChild(img); 60 div1.appendChild(figure); 61 PAGE_LIGHTBOX.appendChild(div1); 62 document.body.appendChild(PAGE_LIGHTBOX); 63} 64PAGE_LIGHTBOX.container = document.querySelector('#videoLightbox .video'); 65PAGE_LIGHTBOX.player = false; 66PAGE_LIGHTBOX.video = false; 67PAGE_LIGHTBOX.addEventListener('click', function() { this.activate('', false); }, false); 68PAGE_LIGHTBOX.innerDimensions = function() { 69 if (window.innerHeight) { 70 return [window.innerWidth, window.innerHeight]; 71 } else if (document.documentElement && document.documentElement.clientHeight) { 72 return [document.documentElement.clientWidth, document.documentElement.clientHeight]; 73 } else if (document.body) 74 return [document.body.clientWidth, document.body.clientHeight]; 75 return [0, 0]; 76}; 77PAGE_LIGHTBOX.setBackground = function() { 78 let vport = this.innerDimensions(); 79 if (vport[0] < 428) { 80 this.style.backgroundImage = 'url(' + PAGE_DOWNROOT + 'img/evertz/loading.214px.webp)'; 81 } else if (vport[0] < 856) { 82 this.style.backgroundImage = 'url(' + PAGE_DOWNROOT + 'img/evertz/loading.428px.webp)'; 83 } else this.style.backgroundImage = 'url(' + PAGE_DOWNROOT + 'img/evertz/loading.856px.webp)'; 84}; 85PAGE_LIGHTBOX.activate = function(video, autoplay) { 86 87 // This is a request to raise the lightbox 88 if (video) { 89 90 this.classList.remove('opacity-0', 'invisible'); 91 document.documentElement.style.overflow = 'hidden'; 92 93 // A new video is being set 94 if (JSON.stringify(video) != this.video) { 95 96 this.container.classList.add('opacity-0'); 97 98 this.video = JSON.stringify(video);
99 100 // Empty the video container 101 let vidbox = document.querySelector('#videoLightbox .video > div'); 102 while (vidbox.firstChild) vidbox.removeChild(vidbox.firstChild); 103 104 if (typeof video != 'object') { 105 console.log('[lightboxVideo]: Function expects an object, non-object passed.'); 106 return false; 107 } 108 109 if (!Array.isArray(video)) video = [video]; 110 111 // This is a YouTube video 112 if (typeof video[0].link == 'string' && video[0].link.match(/^[0-9a-zA-Z_-]{11}$/)) { 113 114 this.player = document.createElement('iframe'); 115 this.player.classList.add('ytvideo'); 116 this.player.title = 'YouTube video player'; 117 this.player.src = '//www.youtube.com/embed/' + video[0].link + '?enablejsapi=1' + ((autoplay) ? '&autoplay=1' : ''); 118 this.player.setAttribute('frameborder', '0'); 119 this.player.setAttribute('allow', 'autoplay'); 120 this.player.setAttribute('allowfullscreen', true); 121 122 // Get aspect ratio of video 123 let http = getHTTPObject(); 124 let uri = 'https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=' + video[0].link 125 http.open('GET', uri, true); 126 http.onreadystatechange = function() { 127 if (http.readyState == 4) { 128 let response = JSON.parse(http.responseText); 129 if (response.width && response.height) { 130 PAGE_LIGHTBOX.player.aspect = response.width / response.height; 131 if (!PAGE_LIGHTBOX.classList.contains('opacity-0')) 132 PAGE_LIGHTBOX.player.resize(); 133 } else console.log('[lightboxVideo]: Could not read aspect ratio from www.youtube.com.'); 134 } 135 }; 136 http.send(null); 137 138 139 // Assume this is a video URL object or an array of such 140 } else { 141 142 this.player = document.createElement('video'); 143 this.player.title = 'HTML5 video player'; 144 if (autoplay) this.player.setAttribute('autoplay', true); 145 this.player.setAttribute('controls', true); 146 this.player.setAttribute('disablepictureinpicture', true); 147 this.player.setAttribute('controlslist', 'nodownload'); 148 149 // An array of objects such as: 150 // [ 151 // { 'link': '...', 'resolution': '480p' }, 152 // { 'link': '...', 'resolution': '1080p' } 153 // ] 154 155 // Simple sanity check 156 for (let x; x < video.length; x++) { 157 if (!video[0].link || !video[0].resolution) { 158 console.log('[lightboxVideo]: Video object is missing a "link" or "resolution" value.'); 159 return false; 160 } 161 } 162 163 // Sort the array so the largest resolution is last 164 video.sort(function(a, b) { 165 if (a.resolution == b.resolution) return 0; 166 return (parseInt(a.resolution) - parseInt(b.resolution)); 167 }); 168 169 // Since W3C has removed support for the media attribute on 170 // <source> elements, we cannot serve videos responsively by 171 // screen size. Remove all videos with the media attribute so 172 // only the largest video remains. There is a campaign on to 173 // restore the media attribute so this code might be removed 174 // someday. 175 // let source = document.createElement('source'); 176 // source.src = video[video.length - 1].link; 177 // this.player.appendChild(source); 178 179 for (let x = 0, source; x < video.length; x++) { 180 source = document.createElement('source'); 181 source.src = video[x].link; 182 if (x < video.length -1) { // Don't limit the largest video 183 switch (video[x].resolution) { 184 case '320p': 185 source.setAttribute('media', 'all and (max-width:576px)'); 186 break; 187 case '480p': 188 source.setAttribute('media', 'all and (max-width:768px)'); 189 break; 190 case '720p': 191 source.setAttribute('media', 'all and (max-width:992px)'); 192 break; 193 case '1080p': 194 source.setAttribute('media', 'all and (max-width:1440px)'); 195 break; 196 case '2160p': 197 source.setAttribute('media', 'all and (max-width:2880px)'); 198 break; 199 case '4K': // ? 200 } 201 } 202 this.player.appendChild(source); 203 } 204 205 this.player.appendChild(document.createTextNode('Your browser doesn\'t support the HTML5 video tag.')); 206 207 } 208 209 // Set default aspect ratio of 16:9 210 this.player.aspect = 1.77777778; 211 212 this.player.resize = function() { 213 let vport = PAGE_LIGHTBOX.innerDimensions(); 214 if ((vport[0] / vport[1]) > this.aspect) { // Height is limiting 215 let h = Math.round(vport[1] * .8); 216 this.setAttribute('height', h.toString()); 217 this.setAttribute('width', Math.round(h * this.aspect).toString()); 218 } else { // Width is limiting 219 let w = Math.round(vport[0] * .8); 220 this.setAttribute('width', w.toString()); 221 this.setAttribute('height', Math.round(w / this.aspect).toString()); 222 } 223 } 224 this.player.loaded = function() { 225 PAGE_LIGHTBOX.container.classList.remove('opacity-0'); 226 if (PAGE_LIGHTBOX.player.nodeName == 'VIDEO') 227 PAGE_LIGHTBOX.player.aspect = PAGE_LIGHTBOX.player.videoWidth / PAGE_LIGHTBOX.player.videoHeight; 228 PAGE_LIGHTBOX.player.resize(); 229 this.removeEventListener('load', this.loaded, false); 230 }; 231 this.player.addEventListener('load', this.player.loaded, false); 232 this.player.addEventListener('loadeddata', this.player.loaded, false); 233 234 vidbox.appendChild(this.player); 235 236 // Same video is requested, just raise the lightbox 237 } else { 238 this.classList.remove('opacity-0', 'invisible'); 239 document.documentElement.style.overflow = 'hidden'; 240 } 241 242 243 // No video specified, so lower the lightbox 244 } else { 245 this.classList.add('opacity-0', 'invisible'); 246 247 // Stop the player 248 if (this.player.nodeName != 'VIDEO') { 249 let oldsrc = this.player.src; 250 this.player.setAttribute('src', oldsrc.replace(/autoplay=1/, 'autoplay=0')); 251 } else this.player.pause(); 252 document.documentElement.style.overflow = ''; 253 } 254}; 255
256PAGE_LIGHTBOX.setBackground(); 257window.addEventListener('resize', function() { 258 if (PAGE_LIGHTBOX.player) PAGE_LIGHTBOX.player.resize(); 259 PAGE_LIGHTBOX.setBackground(); 260}, false);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.