1/*! 2 * iframemanager v1.0 3 * Author Orest Bida 4 * Released under the MIT License 5 */ 6(function(){ 7 'use strict'; 8 9 var module = { 10 iframes : {}, 11 preconnects : [], 12 preloads: [], 13 iframeObserver : null, 14 currLang : null, 15 services : null, 16 serviceNames : null, 17 18 _getVideoProp : function(_div){ 19 return { 20 _id: _div.dataset.id, 21 _title: _div.dataset.title, 22 thumbnail: _div.dataset['thumbnail'], 23 params: _div.dataset['params'], 24 thumbnailPreload: _div.hasAttribute('data-thumbnailpreload'), 25 div: _div, 26 backgroundDiv: null, 27 hasIframe: false, 28 hasNotice: false, 29 showNotice : true 30 } 31 }, 32 33 /** 34 * Lazy load all thumbnails of the iframes relative to specified service 35 * @param {String} service_name 36 * @param {String} thumbnail_url 37 */ 38 _lazyLoadThumnails : function(service_name, thumbnail_url){ 39 40 var videos = this.iframes[service_name]; 41 var length = videos.length; 42 43 if ("IntersectionObserver" in window) { 44 var thumbnailObserver = new IntersectionObserver(function(entries) { 45 entries.forEach(function(entry){ 46 if(entry.isIntersecting){ 47 // true index of the video in the array relative to current service 48 var _index = entry.target.dataset.index; 49 module._loadThumbnail(thumbnail_url, videos[_index]); 50 thumbnailObserver.unobserve(entry.target); 51 } 52 }); 53 }); 54 55 for(var i=0; i<length; i++){ 56 thumbnailObserver.observe(videos[i].div); 57 } 58 }else{ 59 // Fallback for old browsers 60 for(var i=0; i<length; i++){ 61 module._loadThumbnail(thumbnail_url, videos[i]); 62 } 63 } 64 }, 65 66 /** 67 * 1. Set cookie (if not alredy set) 68 * 2. show iframes (relative to the specified service) 69 * @param {String} service_name 70 */ 71 acceptService : function(service_name){ 72 if(service_name === 'all'){ 73 var length = this.serviceNames.length; 74 for(var i=0; i<length; i++){ 75 var service_name = this.serviceNames[i]; 76 _acceptService(service_name, this.services[service_name]); 77 } 78 }else{ 79 if(this.serviceNames.indexOf(service_name) > -1){ 80 _acceptService(service_name, this.services[service_name]); 81 } 82 } 83 84 function _acceptService(service_name, service){ 85 if(!module._getCookie(service['cookie']['name'])){ 86 module._setCookie(service['cookie']); 87 } 88 module._hideAllNotices(service_name, service); 89 } 90 }, 91 92 /** 93 * 1. set cookie 94 * 2. hide all notices 95 * 3. how iframes (relative to the specified service) 96 * @param {String} service_name 97 */ 98 rejectService : function(service_name){ 99 if(service_name === 'all'){ 100 this.iframeObserver = null; 101 var length = this.serviceNames.length; 102 for(var i=0; i<length; i++){ 103 var service_name = this.serviceNames[i]; 104 _rejectService(service_name, this.services[service_name]); 105 } 106 }else{ 107 if(this.serviceNames.indexOf(service_name) > -1){ 108 _rejectService(service_name, this.services[service_name]); 109 } 110 } 111 112 function _rejectService(service_name, service){ 113 if(module._getCookie(service['cookie']['name'])){ 114 module._eraseCookie(service['cookie']); 115 } 116 module._showAllNotices(service_name, service); 117 } 118 }, 119 120 /** 121 * Set image as background 122 * @param {String} url 123 * @param {Object} video 124 */ 125 _loadThumbnail: function(url, video){ 126 127 // Set custom thumbnail if provided 128 if(typeof video.thumbnail === 'string'){ 129 video.thumbnailPreload && this._thumbnailPreload(video.thumbnail);
130 video.thumbnail !== "" && _loadBackgroundImage(video.thumbnail); 131 }else{ 132 133 if(typeof url === "function"){ 134 135 url(video._id, function(src){ 136 module._preconnect(src); 137 video.thumbnailPreload && this._thumbnailPreload(src); 138 _loadBackgroundImage(src); 139 }); 140 141 }else if(typeof url === "string"){ 142 var src = url.replace('{data-id}', video._id); 143 this._preconnect(src); 144 video.thumbnailPreload && this._thumbnailPreload(src); 145 _loadBackgroundImage(src); 146 } 147 } 148 149 function _loadBackgroundImage(src){ 150 video.backgroundDiv.style.backgroundImage = "url('"+src+"')"; 151 152 var img = new Image(); 153 img.onload = function(){ 154 video.backgroundDiv.classList.add('loaded'); 155 } 156 157 img.src = src; 158 } 159 }, 160 161 /** 162 * Create iframe and append it into the specified div 163 * @param {Object} video 164 * @param {Object} service 165 */ 166 _createIframe: function(video, service) { 167 168 // Create iframe only if doesn't alredy have one 169 if(video.hasIframe) return; 170 171 video.iframe = this._createNode('iframe'); 172 var iframeParams = video.params || (service['iframe'] && service['iframe']['params']); 173 174 // Replace data-id with valid resource id 175 var src = service['embedUrl'].replace('{data-id}', video._id); 176 177 video.iframe['loading'] = 'lazy'; 178 video._title && (video.iframe.title = video._title); 179 180 // Add allow attribute to iframe 181 if(service['iframe'] && service['iframe']['allow']){ 182 video.iframe.allow = service['iframe']['allow']; 183 } 184 185 // Add parameters to src 186 if(iframeParams){ 187 if (iframeParams.substring(0, 3) === "ap:"){ 188 src += iframeParams.substring(3); 189 }else{ 190 src += "?" + iframeParams 191 } 192 } 193 194 video.iframe.src = encodeURI(src); 195 196 // When iframe is loaded => hide background image 197 video.iframe.onload = function(){ 198 video.div.classList.add('c-h-b'); 199 video.iframe.onload = undefined; 200 service['iframe'] && typeof service['iframe']['onload'] === 'function' && service['iframe']['onload'](video._id, this); 201 } 202 203 video.hasIframe = true; 204 video.div.appendChild(video.iframe); 205 }, 206 207 /** 208 * Remove iframe HTMLElement from div 209 * @param {Object} video 210 */ 211 _removeIframe: function(video){ 212 video.iframe.parentNode.removeChild(video.iframe); 213 video.hasIframe = false; 214 }, 215 216 /** 217 * Remove necessary classes to hide notice 218 * @param {Object} video 219 */ 220 _hideNotice : function(video){ 221 if(video.showNotice){ 222 video.div.classList.add('c-h-n'); 223 video.showNotice = false; 224 } 225 }, 226 227 /** 228 * Add necessary classes to show notice 229 * @param {Object} video 230 */ 231 _showNotice : function(video){ 232 if(!video.showNotice){ 233 video.div.classList.remove('c-h-n', 'c-h-b'); 234 video.showNotice = true; 235 } 236 }, 237 238 /** 239 * Get cookie by name 240 * @param {String} a cookie name 241 * @returns {String} cookie value 242 */ 243 _getCookie : function(a) { 244 return (a = document.cookie.match("(^|;)\\s*" + a + "\\s*=\\s*([^;]+)")) ? a.pop() : ""; 245 }, 246 247 /** 248 * Set cookie based on given object 249 * @param {Object} cookie 250 */ 251 _setCookie : function(cookie) { 252 253 var date = new Date(); 254 var path = cookie['path'] || "/"; 255 var expiration = cookie['expiration'] || 182; 256 var sameSite = cookie['sameSite'] || "Lax"; 257 var domain = cookie['domain'] || location.hostname; 258 259 date.setTime(date.getTime() + (1000 * ( expiration * 24 * 60 * 60))); 260 var expires = " expires=" + date.toUTCString(); 261 262 var cookieStr = cookie.name + "=1;" + expires + "; Path=" + path + ";"; 263 cookieStr += " SameSite=" + sameSite + ";"; 264 265 // assures cookie works with localhost (=> don't specify domain if on localhost) 266 if(domain.indexOf(".") > -1){ 267 cookieStr += " Domain=" + domain + ";"; 268 } 269 270 if(location.protocol === "https:") { 271 cookieStr += " Secure;"; 272 } 273 274 document.cookie = cookieStr; 275 }, 276 277 /** 278 * Delete cookie by name & path 279 * @param {Array} cookies 280 * @param {String} custom_path 281 */ 282 _eraseCookie : function(cookie) { 283 var path = cookie['path'] || "/"; 284 var domain = cookie['domain'] || location.hostname; 285 var expires = 'Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; 286 287 document.cookie = cookie['name'] +'=; Path='+ path +'; Domain=' + domain + '; ' + expires; 288 }, 289 290 /** 291 * Get all prop. keys defined inside object 292 * @param {Object} obj 293 */ 294 _getKeys : function(obj){ 295 if(typeof obj === "object"){ 296 var keys = [], i = 0; 297 for (keys[i++] in obj) {}; 298 return keys; 299 } 300 }, 301 302 /**
303 * Add link rel="preconnect" 304 * @param {String} url 305 */ 306 _preconnect: function(url){ 307 var url = url.split("://"); 308 var protocol = url[0]; 309 310 // if valid protocol 311 if( 312 protocol == 'http' || 313 protocol == 'https' 314 ){ 315 var domain = (url[1] && url[1].split("/")[0]) || false; 316 317 // if not current domain 318 if(domain && domain !== location.hostname){ 319 if(this.preconnects.indexOf(domain) === -1){ 320 var l = this._createNode('link'); 321 l.rel = 'preconnect'; 322 l.href = protocol + "://" + domain; 323 document.head.appendChild(l); 324 this.preconnects.push(domain); 325 } 326 } 327 } 328 }, 329 330 /** 331 * Add link rel="preload" 332 * @param {String} url 333 */ 334 _thumbnailPreload : function(url){ 335 if(url && this.preloads.indexOf(url) === -1){ 336 var l = this._createNode('link'); 337 l.rel = 'preload'; 338 l.as = 'image'; 339 l.href = url; 340 document.head.appendChild(l); 341 this.preloads.push(url); 342 } 343 }, 344 345 /** 346 * Create and return HTMLElement based on specified type 347 * @param {String} type 348 * @returns {HTMLElement} 349 */ 350 _createNode : function(type){ 351 return document.createElement(type); 352 }, 353 354 /** 355 * Create all notices relative to the specified service 356 * @param {String} service_name 357 * @param {Object} service 358 * @param {Boolean} hidden 359 */ 360 _createAllNotices : function(service_name, service, hidden){ 361 362 // get number of iframes of current service 363 var iframes = this.iframes[service_name]; 364 var n_iframes = iframes.length; 365 366 // for each iframe 367 for(var i=0; i<n_iframes; i++){ 368 (function(i){ 369 370 var video = iframes[i]; 371 372 if(!video.hasNotice){ 373 374 var loadBtnText = service['languages'][module.currLang]['loadBtn']; 375 var noticeText = service['languages'][module.currLang]['notice']; 376 var loadAllBtnText = service['languages'][module.currLang]['loadAllBtn']; 377 378 var fragment = document.createDocumentFragment(); 379 var notice = module._createNode('div'); 380 var span = module._createNode('span'); 381 var innerDiv = module._createNode('p'); 382 var load_button = module._createNode('button'); 383 var load_all_button = module._createNode('button'); 384 385 var notice_text = module._createNode('span'); 386 var ytVideoBackground = module._createNode('div'); 387 var loaderBg = module._createNode('div'); 388 var ytVideoBackgroundInner = module._createNode('div'); 389 var notice_text_container = module._createNode('div'); 390 var buttons = module._createNode('div'); 391 392 load_button.type = load_all_button.type = 'button'; 393 notice_text.className = 'cc-text'; 394 load_button.type = load_all_button.type = 'button'; 395 notice_text.className = 'cc-text'; 396 397 ytVideoBackgroundInner.className = 'c-bg-i'; 398 video.backgroundDiv = ytVideoBackgroundInner; 399 loaderBg.className = 'c-ld'; 400 401 if(typeof video.thumbnail !== 'string' || video.thumbnail !== ""){ 402 ytVideoBackground.className = 'c-bg'; 403 } 404 405 406 var iframeTitle = video._title; 407 var fragment_2 = document.createDocumentFragment(); 408 409 if(iframeTitle) { 410 var title_span = module._createNode('span'); 411 title_span.className = 'c-tl'; 412 title_span.insertAdjacentHTML('beforeend', iframeTitle); 413 fragment_2.appendChild(title_span); 414 } 415 416 load_button.textContent = loadBtnText; 417 load_all_button.textContent = loadAllBtnText; 418 419 notice_text.appendChild(fragment_2); 420 notice && notice_text.insertAdjacentHTML('beforeend', noticeText || ""); 421 span.appendChild(notice_text); 422 423 424 notice_text_container.className = 'c-t-cn'; 425 span.className = 'c-n-t'; 426 innerDiv.className = 'c-n-c'; 427 notice.className = 'c-nt'; 428 429 buttons.className = 'c-n-a'; 430 load_button.className = 'c-l-b'; 431 load_all_button.className = 'c-la-b'; 432 433 buttons.appendChild(load_button); 434 buttons.appendChild(load_all_button); 435 436 notice_text_container.appendChild(span); 437 notice_text_container.appendChild(buttons); 438 439 innerDiv.appendChild(notice_text_container); 440 notice.appendChild(innerDiv); 441 442 function showVideo(){ 443 module._hideNotice(video); 444 module._createIframe(video, service); 445 } 446 447 load_button.addEventListener('click', function(){ 448 showVideo(); 449 }); 450 451 load_all_button.addEventListener('click', function(){ 452 showVideo(); 453 module.acceptService(service_name); 454 }); 455
456 ytVideoBackground.appendChild(ytVideoBackgroundInner); 457 fragment.appendChild(notice); 458 (service['thumbnailUrl'] || video.thumbnail) && fragment.appendChild(ytVideoBackground); 459 fragment.appendChild(loaderBg); 460 461 hidden && video.div.classList.add('c-h-n'); 462 463 // Avoid reflow with fragment (only 1 appendChild) 464 video.div.appendChild(fragment); 465 video.hasNotice = true; 466 } 467 })(i); 468 } 469 }, 470 471 /** 472 * Hides all notices relative to the specified service 473 * and creates iframe with the video 474 * @param {String} service_name 475 * @param {Object} service 476 */ 477 _hideAllNotices : function(service_name, service){ 478 479 // get number of iframes of current service 480 var videos = this.iframes[service_name]; 481 var n_iframes = videos.length; 482 483 if ("IntersectionObserver" in window) { 484 this.iframeObserver = new IntersectionObserver(function(entries) { 485 for(var i=0; i<entries.length; ++i){ 486 if(module.iframeObserver === null){ 487 return; 488 } 489 if(entries[i].isIntersecting){ 490 (function(_index){ 491 setTimeout(function(){ 492 var index = entries[_index].target.dataset.index; 493 module._createIframe(videos[index], service); 494 module._hideNotice(videos[index]); 495 }, _index*50); 496 module.iframeObserver.unobserve(entries[_index].target); 497 })(i); 498 } 499 } 500 }); 501 502 for(var i=0; i<n_iframes; i++){ 503 if(!videos[i].hasIframe){ 504 this.iframeObserver.observe(videos[i].div); 505 } 506 } 507 }else{ 508 for(var i=0; i<n_iframes; i++){ 509 (function(index){ 510 module._createIframe(videos[i], service); 511 module._hideNotice(videos[index]) 512 })(i); 513 } 514 } 515 }, 516 517 518 /** 519 * Show all notices relative to the specified service 520 * and hides iframe with the video 521 * @param {String} service_name 522 * @param {Object} service 523 */ 524 _showAllNotices : function(service_name, service){ 525 526 // get number of iframes of current service 527 var videos = this.iframes[service_name]; 528 var n_iframes = videos.length; 529 530 for(var i=0; i<n_iframes; i++){ 531 (function(index){ 532 // if doesn't have iframe => create it 533 if(!videos[i].showNotice){ 534 if(videos[i].hasIframe){ 535 module._removeIframe(videos[i]); 536 } 537 module._showNotice(videos[index]); 538 } 539 })(i); 540 } 541 }, 542 543 /** 544 * Validate language (make sure it exists) 545 * @param {String} lang 546 * @param {Object} all_languages 547 * @returns {String} language 548 */ 549 _getValidatedLanguage : function(lang, all_languages){ 550 if(all_languages.hasOwnProperty(lang)){ 551 return lang; 552 }else if(this._getKeys(all_languages).length > 0){ 553 if(all_languages.hasOwnProperty(this.currLang)){ 554 return this.currLang ; 555 }else{ 556 return this._getKeys(all_languages)[0]; 557 } 558 } 559 }, 560 561 /** 562 * Get current client's browser language 563 * @returns {String} browser language 564 */ 565 _getBrowserLang : function(){ 566 var browser_lang = navigator.language || navigator.browserLanguage; 567 browser_lang.length > 2 && (browser_lang = browser_lang[0]+browser_lang[1]); 568 return browser_lang.toLowerCase() 569 }, 570 571 run : function(_config) { 572 /** 573 * Object with all services config. 574 */ 575 var services = _config['services']; 576 this.services = services; 577 578 /** 579 * Array containing the names of all services 580 */ 581 var service_names = this._getKeys(services); 582 this.serviceNames = service_names; 583 584 /** 585 * Number of services 586 */ 587 var n_services = service_names.length; 588 589 // if there are no services => don't do anything 590 if(n_services === 0){ 591 return; 592 } 593 594 // Set curr lang 595 this.currLang = _config['currLang']; 596 var languages = services[service_names[0]]['languages']; 597 598 if(_config['autoLang'] === true){ 599 this.currLang = this._getValidatedLanguage(this._getBrowserLang(), languages); 600 }else{ 601 if(typeof _config['currLang'] === "string"){ 602 this.currLang = this._getValidatedLanguage(_config['currLang'], languages); 603 } 604 } 605 606 // for each service 607 for(var i=0; i<n_services; i++){ 608 609 /** 610 * Name of current service 611 */ 612 var service_name = service_names[i]; 613 614 // add new empty array of videos (with current service name as property) 615 this.iframes[service_name] = []; 616 617 /** 618 * iframes/divs in the dom that have data-service value as current service name 619 */ 620 var found_iframes = document.querySelectorAll('div[data-service="' + service_name + '"]'); 621 622 /** 623 * number of iframes with current service 624 */ 625 var n_iframes = found_iframes.length; 626 627 // if no iframes found =>
627 go to next service 628 if(n_iframes === 0){ 629 continue; 630 } 631 632 // add each iframe to array of iframes of the current service 633 for(var j=0; j<n_iframes; j++){ 634 found_iframes[j].dataset.index = j; 635 this.iframes[service_name].push(this._getVideoProp(found_iframes[j])); 636 } 637 638 var curr_service = services[service_name]; 639 640 // check if cookie for current service is set 641 var cookie_name = curr_service['cookie']['name']; 642 643 // get current service's cookie value 644 var cookie = this._getCookie(cookie_name); 645 646 // if cookie is not set => show notice 647 if(cookie){ 648 this._createAllNotices(service_name, curr_service, true); 649 this._hideAllNotices(service_name, curr_service); 650 }else{ 651 this._createAllNotices(service_name, curr_service, false); 652 } 653 654 this._lazyLoadThumnails(service_name, curr_service['thumbnailUrl']); 655 } 656 } 657 }; 658 659 var fn_name = 'iframemanager'; 660 661 window[fn_name] = function(){ 662 window[fn_name] = undefined; 663 return module; 664 }; 665 666})();
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.