1const VIDEO_MIMES = { 2 "video/webm": {ext: ["webm"], index: 0}, 3 "video/mp4": {ext: ["mp4"], index: 1}, 4 "video/mov": {ext: ["mov"], index: 2}, 5 "video/ogg": {ext: ["ogv", "ogg"], index: 3}, 6} 7 8const SPECS_MAPPER = { 9 a: {htmlAttr: "autoplay", jsAttr: "autoplay"}, 10 c: {htmlAttr: "controls", jsAttr: "controls"}, 11 m: {htmlAttr: "muted", jsAttr: "muted"}, 12 p: {htmlAttr: "playsinline", jsAttr: "playsInline"}, 13 l: {htmlAttr: "loop", jsAttr: "loop"}, 14} 15 16function detectVideoInViewport(edge){ 17 let videoInViewport = null; 18 if(window.scrollY > (edge * window.window.innerHeight)){ 19 videoInViewport = false; 20 } 21 else{ 22 videoInViewport = true; 23 } 24 return videoInViewport; 25} 26 27function videoControlBinder(videoContainer){ 28 29 let video = videoContainer.querySelector("video"); 30 31 const videoEdge = .35; 32 let videoInViewport = detectVideoInViewport(videoEdge); 33 34 function playPause(){ 35 if(video.paused){ 36 video.play(); 37 } 38 else{ 39 video.pause(); 40 } 41 } 42 43 function toggleMute(){ 44 video.muted = !video.muted; 45 } 46 47 document.addEventListener("keyup", event => { 48 event.preventDefault(); 49 if(event.isComposing || event.keyCode === 77){ 50 toggleMute(); 51 return; 52 } 53 // do something 54 }); 55 56 document.addEventListener("keydown", (event) => { 57 if(event.keyCode === 32 && videoInViewport){ 58 event.preventDefault(); 59 event.stopPropagation(); 60 } 61 }) 62 63 document.addEventListener("keyup", (event) => { 64 if(event.keyCode === 32 && videoInViewport){ 65 event.preventDefault(); 66 playPause(); 67 return; 68 } 69 }); 70 71 72 let triggerElements = []; 73 triggerElements.push(video, videoContainer); 74 if(e = videoContainer.querySelector(".playLabel")){ 75 triggerElements.push(e); 76 } 77 if(e = videoContainer.querySelector(".videoOverlay")){ 78 triggerElements.push(e); 79 } 80 81 triggerElements.forEach(elem => { 82 elem.addEventListener("click", (event) => { 83 event.preventDefault(); 84 event.stopPropagation(); 85 playPause(); 86 return false; 87 }); 88 }); 89 90 video.addEventListener("play", (event) => { 91 videoContainer.classList.remove("show"); 92 }); 93 94 video.addEventListener("pause", (event) => { 95 videoContainer.classList.add("show"); 96 }); 97 98 video.addEventListener("ended", (event) => { 99 event.target.currentTime = 0; 100 videoContainer.classList.add("show"); 101 }); 102 103 if(videoContainer.dataset.hasOwnProperty("pauseOnScoll")){ 104 document.addEventListener('scroll', function(e){ 105 videoInViewport = detectVideoInViewport(videoEdge); 106 if(!videoInViewport && !video.paused){ 107 video.pause(); 108 } 109 }); 110 } 111 112} 113 114function videoLoad(sourceData, video, sourcesNode){ 115 return fetch(sourceData.source) 116 .then(response => { 117 const reader = response.body.getReader(); 118 return new ReadableStream({ 119 start(controller) { 120 return pump(); 121 function pump() { 122 return reader.read().then(({ done, value }) => { 123 // When no more data needs to be consumed, close the stream 124 if (done) { 125 controller.close(); 126 return; 127 } 128 // Enqueue the next data chunk into our target stream 129 controller.enqueue(value); 130 return pump(); 131 }); 132 } 133 } 134 }) 135 }) 136 .then(stream => new Response(stream)) 137 .then(response => response.blob()) 138 .then(blob => URL.createObjectURL(blob)) 139 .then(url => { 140 let source = document.createElement('source'); 141 source.setAttribute("type", sourceData.type); 142 source.src = url; 143 144 source.dataset.index = VIDEO_MIMES[sourceData.type].index; 145 sourcesNode.push(source); 146 147 // video.appendChild(source); 148 }) 149 .catch(err => console.error(err)); 150}; 151 152 153//add source to video tag 154function addSources(videoContainer, dataSrcs, callback){ 155 156 if(dataSrcs.hasOwnProperty("srcs") && dataSrcs.srcs != undefined){ 157 let srcs = dataSrcs.srcs.split(";"); 158 let promises = []; 159 let specs = []; 160 161 let video = document.createElement('video'); 162 if(dataSrcs.specs !== undefined && dataSrcs.specs != ""){ 163 specs = dataSrcs.specs.split("-"); 164 specs.forEach(index => { 165 if(SPECS_MAPPER.hasOwnProperty(index)){ 166 video[SPECS_MAPPER[index].jsAttr] = true; 167 video.setAttribute(SPECS_MAPPER[index].jsAttr, true); 168 } 169 }); 170 } 171 172 173 srcs.sort(function(a, b){ 174 let a_ext = a.split("."); 175 let b_ext = b.split("."); 176 a_ext = a_ext[a_ext.length - 1]; 177 b_ext = b_ext[b_ext.length - 1]; 178 179 let a_mime = ""; 180 let b_mime = "";
181 Object.keys(VIDEO_MIMES).forEach(mime => { 182 if(VIDEO_MIMES[mime].ext.inArray(a_ext)){ 183 a_mime = mime; 184 } 185 if(VIDEO_MIMES[mime].ext.inArray(b_ext)){ 186 b_mime = mime; 187 } 188 }); 189 190 if(VIDEO_MIMES[a_mime].index > VIDEO_MIMES[b_mime].index) return 1; 191 if(VIDEO_MIMES[a_mime].index < VIDEO_MIMES[b_mime].index) return -1; 192 return 0; 193 }) 194 195 // video.setAttribute("poster", dataSrcs.poster); 196 fetch(dataSrcs.poster) 197 .then(response => { 198 const reader = response.body.getReader(); 199 return new ReadableStream({ 200 start(controller) { 201 return pump(); 202 function pump() { 203 return reader.read().then(({ done, value }) => { 204 // When no more data needs to be consumed, close the stream 205 if (done) { 206 controller.close(); 207 return; 208 } 209 // Enqueue the next data chunk into our target stream 210 controller.enqueue(value); 211 return pump(); 212 }); 213 } 214 } 215 }) 216 }) 217 .then(stream => new Response(stream)) 218 .then(response => response.blob()) 219 .then(blob => URL.createObjectURL(blob)) 220 .then(url => { 221 video.setAttribute("poster", url); 222 videoContainer.append(video); 223 224 let sourcesNode = []; 225 226 //inizializzo tutte le sorgenti con promise 227 for(i in srcs){ 228 let src = srcs[i]; 229 let ext = src.split("."); 230 ext = ext[ext.length - 1]; 231 232 let foundMime = ""; 233 Object.keys(VIDEO_MIMES).forEach(mime => { 234 if(VIDEO_MIMES[mime].ext.inArray(ext)){ 235 foundMime = mime; 236 } 237 }); 238 239 if(video.canPlayType(foundMime)){ 240 let sourceData = { 241 source: dataSrcs.base + src, 242 type: foundMime, 243 } 244 promises.push(videoLoad(sourceData, video, sourcesNode)); 245 break; 246 } 247 } 248 249 // attendo che tutte le promise siano complete 250 Promise.all(promises).then(response => { 251 // videoContainer.append(video); 252 253 if(sourcesNode.length > 1){ 254 sourcesNode.sort(function(a, b){ 255 if(a.dataset.index > b.dataset.index) return 1; 256 if(a.dataset.index < b.dataset.index) return -1; 257 return 0; 258 }) 259 } 260 261 sourcesNode.forEach(source => { 262 source.removeAttribute("data-index"); 263 video.appendChild(source); 264 }) 265 266 if(videoContainer.dataset.hasOwnProperty("controls") && videoContainer.dataset.controls === "default"){ 267 videoControlBinder(videoContainer); 268 } 269 270 callback(); 271 272 if((specs.includes('a') || specs.includes('p')) && video.paused){ 273 let playAttempt = setInterval(() => { 274 video.play() 275 .then(() => { 276 clearInterval(playAttempt); 277 }) 278 .catch(error => { 279 console.log('Unable to play the video, User has not interacted yet.'); 280 }); 281 }, 500); 282 } 283 }, 284 error => { 285 }); 286 287 }) 288 .catch(err => console.error(err)); 289 290 } 291 292} 293 294//determine screen size and select mobile or desktop vid 295//videoContainers NodeList | CSS selector 296function initVideo(videoContainers){ 297 298 if(typeof(videoContainers) === "string"){ 299 videoContainers = document.querySelectorAll(videoContainers); 300 } 301 else{ 302 videoContainers = document.querySelectorAll('.videoAutoInit') 303 } 304 305 306 videoContainers.forEach(videoContainer => { 307 let windowWidth = window.innerWidth ? window.innerWidth : $(window).width(); 308 let windowHeigth = window.innerHeight ? window.innerHeight : $(window).height(); 309 let vidDataSet = videoContainer.dataset; 310 let base = vidDataSet.base; 311 312 let dataSource = { 313 base: vidDataSet.base, 314 srcs: vidDataSet.desktopSrc, // video di default 315 specs: vidDataSet.specs, 316 poster: null, 317 }; 318 319 //seleziona della sorgente in base al dispositi 320 321 let ratio = windowWidth / windowHeigth; // =1 -> square; >1 oriz rect; <1 vert rect; 322 323 324 if(windowWidth >= 2600){ 325 if(vidDataSet.d4kSrc != undefined){ 326 dataSource.srcs = vidDataSet.d4kSrc; 327 } 328 329 if(vidDataSet.d4kPoster != undefined){ 330 dataSource.poster = base + vidDataSet.d4kPoster; 331 } 332 } 333 else if(windowWidth >= 1800){ 334 if(vidDataSet.desktopPoster != undefined){ 335 dataSource.poster = base + vidDataSet.desktopPoster; 336 } 337 } 338 else{ 339 340 if(windowWidth >= parseInt(getComputedStyle(document.documentElement).getPropertyValue('--breakpoint-sm')) && windowWidth < parseInt(getComputedStyle(document.documentElement).getPropertyValue('--breakpoint-lg'))){ // 576 - 992; tablet piccoli 341 342 if(ratio > 1){ 343 344 if(vidDataSet.tabletSrc != undefined){ 345 dataSource.srcs = vidDataSet.tabletSrc; 346 } 347 348 if(vidDataSet.tabletPoster != undefined){ 349 dataSource.poster = base + vidDataSet.tabletPoster; 350 } 351 } 352 else{ 353 if(vidDataSet.mobileSrc != undefined){ 354 dataSource.srcs = vidDataSet.mobileSrc; 355 } 356 357 if(vidDataSet.mobilePoster != undefined){ 358 dataSource.poster = base + vidDataSet.mobilePoster; 359 } 360 } 361 } 362 else if(windowWidth < parseInt(getComputedStyle(document.documentElement).getPropertyValue('--breakpoint-sm'))){ 363 //mobile 364 365 if(vidDataSet.mobilePoster != undefined){ 366 dataSource.poster = base + vidDataSet.mobilePoster; 367 } 368 } 369 else{ //tablet grandi qualsiasi orientamento, tablet piccoli orizzontali 370 if(vidDataSet.tabletSrc != undefined){ 371 dataSource.srcs = vidDataSet.tabletSrc; 372 } 373 374 if(vidDataSet.tabletPoster != undefined){ 375 dataSource.poster = base + vidDataSet.tabletPoster; 376 } 377 } 378 379 } 380
381 let callback = function(){}; 382 if(arguments.length > 1){ 383 callback = arguments[1]; 384 } 385 386 if(vidDataSet.hasOwnProperty("playLabel")){ 387 let playLabel = document.createElement('span'); 388 389 let classes = ["playLabel"]; 390 if(vidDataSet.hasOwnProperty("playLabelClass") && vidDataSet.playLabelClass != ""){ 391 vidDataSet.playLabelClass.split(" ").forEach(cssClass => { 392 classes.push(cssClass); 393 }); 394 } 395 playLabel.textContent = vidDataSet.playLabel; 396 playLabel.classList.add(...classes); 397 videoContainer.append(playLabel); 398 } 399 400 addSources(videoContainer, dataSource, callback); 401 }) 402} 403 404 405document.addEventListener("DOMContentLoaded", function(){ 406 407 //get all vids 408 let videoContainers = document.querySelectorAll('.videoAutoInit'); 409 if(videoContainers && videoContainers.length > 0){ 410 initVideo(videoContainers); 411 } 412 413})
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.