1/** 2 * Pure JavaScript Video Popup Library - Developed by Alobaidi. 3 * Core library that manages the popup display, video embedding, and all related functionality 4 * 5 * @author Alobaidi 6 * @version 2.0.4 7 */ 8 9 10/** 11 * Main VideoPopup class 12 * Manages popup creation, video embedding and all popup behaviors 13 */ 14class VideoPopup { 15 /** 16 * Initializes plugin with customizable options 17 * @param {Object} options Configuration options 18 */ 19 constructor(options = {}) { 20 this.defaults = { 21 autoplay: this.validateBoolean(options.autoplay, true), 22 wrapClose: this.validateBoolean(options.wrapClose, false), 23 noCookie: this.validateBoolean(options.noCookie, false), 24 handleVars: this.validateBoolean(options.handleVars, false), 25 onPageLoad: this.validateBoolean(options.onPageLoad, false), 26 oplCookie: options.oplCookie || false, 27 debug: this.validateBoolean(options.debug, false) 28 }; 29 30 this.init(); 31 } 32 33 /** 34 * Initializes event listeners 35 */ 36 init() { 37 this.addClickListener(); 38 } 39 40 /** 41 * Adds click event listeners to video links 42 */ 43 addClickListener() { 44 document.querySelectorAll('a.vp-a, a.vp-s').forEach(link => { 45 link.addEventListener('click', async (e) => { 46 47 if ( link.classList.contains('vp-a') ) { 48 e.preventDefault(); 49 link.blur(); 50 await this.openPopup(link, true); 51 } 52 53 else if ( link.classList.contains('vp-s') ) { 54 e.preventDefault(); 55 link.blur(); 56 await this.openPopup(link, false); 57 } 58 59 }); 60 }); 61 } 62 63 /** 64 * Validates boolean values with various formats 65 * @param {*} value Value to validate as boolean 66 * @param {boolean} defaultValue Default if invalid 67 * @return {boolean} Normalized boolean value 68 */ 69 validateBoolean(value, defaultValue) { 70 if (value === undefined || value === null) return defaultValue; 71 72 if (value === true || value === false) return value; 73 74 if (value && typeof value === 'string') { 75 const cleanValue = value.trim().toLowerCase(); 76 if (cleanValue === 'true' || cleanValue === '1') return true; 77 if (cleanValue === 'false' || cleanValue === '0') return false; 78 } 79 80 if (typeof value === 'number') { 81 if (value === 1) return true; 82 if (value === 0) return false; 83 } 84 85 return defaultValue; 86 } 87 88 /** 89 * Trims string values if they exist 90 * @param {*} value Value to trim if string 91 * @return {*} Trimmed value or original value 92 */ 93 trimIfExists(value) { 94 if ( value && (typeof value === 'string' || typeof value === 'number') ) { 95 const trimmed = typeof value === 'string' ? value.trim() : value; 96 return trimmed === '' ? null : trimmed; 97 } 98 return value; 99 } 100 101 /** 102 * Opens popup with video content 103 * @param {HTMLElement} element Link element that was clicked 104 * @param {boolean} autoplay Whether video should autoplay 105 */ 106 async openPopup(element, autoplay) { 107 const data = this.extractData(element); 108 const embedHTML = this.generateEmbed(data, autoplay); 109 await this.createPopup(embedHTML, data); 110 } 111 112 /** 113 * Sets cookie with expiration time 114 * @param {string} name Cookie name 115 * @param {number} days Days until expiration 116 */
117 setCookie(name, days) { 118 if ( !name || !days ) return; 119 const expirySec = Math.floor(Date.now() / 1000) + (days * 24 * 60 * 60); 120 const expiryDate = new Date(expirySec * 1000); 121 document.cookie = `${name}=${expirySec};expires=${expiryDate.toUTCString()};path=/`; 122 } 123 124 /** 125 * Checks if cookie exists 126 * @param {string} name Cookie name 127 * @return {boolean} True if cookie exists 128 */ 129 getCookie(name) { 130 if ( !name ) return; 131 const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); 132 return match ? true : false; 133 } 134 135 /** 136 * Removes cookie if it exists 137 * @param {string} name Cookie name 138 */ 139 unsetCookie(name){ 140 if ( !name ) return; 141 if ( this.getCookie(name) ) { 142 document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/`; 143 } 144 } 145 146 /** 147 * Extracts data attributes and settings from link element 148 * @param {HTMLElement} element Link element 149 * @return {Object} Extracted and validated data 150 */ 151 extractData(element) { 152 const url = element.href; 153 154 let autoplay_status; 155 if ( element.classList.contains('vp-a') ) { 156 autoplay_status = 'true (vp-a class)'; 157 } else if ( element.classList.contains('vp-s') ) { 158 autoplay_status = 'false (vp-s class)'; 159 } else { 160 autoplay_status = 'the link are not contains vp-s or vp-a class!'; 161 } 162 163 const noCookie = this.trimIfExists(element.getAttribute('data-cookie')); 164 const wrapClose = this.trimIfExists(element.getAttribute('data-wrap-c')); 165 166 return { 167 url: url, 168 noCookie: this.validateBoolean(noCookie, this.defaults.noCookie), 169 wrapClose: this.validateBoolean(wrapClose, this.defaults.wrapClose), 170 171 videoType: this.detectVideoType(url), 172 videoMimeType: this.getDirectVideoMimeType(url), 173 174 isAutoplay: autoplay_status, 175 mute: this.trimIfExists(element.getAttribute('data-mute')), 176 177 linkClasses: element.classList, 178 handleVars: this.defaults.handleVars, 179 onPageLoad: this.defaults.onPageLoad, 180 oplCookie: this.defaults.oplCookie 181 }; 182 } 183 184 /** 185 * Generates appropriate embed code based on URL type 186 * @param {Object} data Video data object 187 * @param {boolean} autoplay Whether video should autoplay 188 * @return {string|number|null} HTML embed code or error indicator 189 */ 190 generateEmbed(data, autoplay) { 191 const url = data.url ? data.url.trim() : null; 192 if ( !url ) return null; 193 194 if (this.isYouTube(url)) { 195 return this.createYouTubeEmbed(url, data, autoplay); 196 } else if (this.isVimeo(url)) { 197 return this.createVimeoEmbed(url, data, autoplay); 198 } else if (this.isDirectVideo(url)) { 199 return this.createDirectVideoEmbed(url, data, autoplay); 200 } 201 202 return 0; 203 } 204 205 /** 206 * Checks if URL is from YouTube 207 * @param {string} url URL to check 208 * @return {boolean} True if YouTube URL 209 */ 210 isYouTube(url) { 211 return /youtube\.com|youtu\.be|youtube-nocookie\.com/i.test(url); 212 } 213 214 /** 215 * Checks if URL is from Vimeo 216 * @param {string} url URL to check 217 * @return {boolean} True if Vimeo URL 218 */ 219 isVimeo(url) { 220 return /vimeo\.com/i.test(url); 221 } 222 223 /** 224 * Checks if URL is direct video file 225 * @param {string} url URL to check 226 * @return {boolean} True if direct video file 227 */ 228 isDirectVideo(url) { 229 return /\.(mp4|webm)(\?.*)?$/i.test(url); 230 } 231 232 /** 233 * Determines MIME type for direct video files 234 * @param {string} url Video URL 235 * @return {string|null} MIME type or null 236 */ 237 getDirectVideoMimeType(url) { 238 if (/\.mp4(\?.*)?$/i.test(url)) { 239 return 'video/mp4'; 240 } 241 else if (/\.webm(\?.*)?$/i.test(url)) { 242 return 'video/webm'; 243 } 244 else { 245 return null; 246 } 247 } 248 249 /** 250 * Detects video provider type from URL 251 * @param {string} url Video URL 252 * @return {string|boolean} CSS class name for video type or false 253 */ 254 detectVideoType(url) { 255 if (this.isYouTube(url)) { 256 return 'vp-video-type-youtube'; 257 } 258 259 else if (this.isVimeo(url)) { 260 return 'vp-video-type-vimeo'; 261 } 262 263 else if (this.isDirectVideo(url)) { 264 const getVideoMimeType = this.getDirectVideoMimeType(url); 265 266 if ( getVideoMimeType == 'video/mp4') { 267 return 'vp-video-type-mp4'; 268 } 269 else if ( getVideoMimeType == 'video/webm' ) { 270 return 'vp-video-type-webm'; 271 } 272 return null; 273 } 274 275 return false; 276 } 277 278 /** 279 * Extracts YouTube video ID from URL 280 * @param {string} url YouTube URL 281 * @return {string|null} Video ID or null 282 */ 283 extractYouTubeId(url) { 284 const match = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/|v\/|shorts\/)|youtu\.be\/|youtube-nocookie\.com\/(?:watch\?v=|embed\/|v\/)|m\.youtube\.com\/watch\?v=)([^&\n?#]+)/); 285 return match ? match[1] : null; 286 } 287 288 /** 289 * Extracts Vimeo video ID from URL 290 * @param {string} url Vimeo URL 291 * @return {string|null} Video ID or null 292 */ 293 extractVimeoId(url) { 294 const match = url.match(/vimeo\.com\/(?:channels\/[^\/]+\/|video\/)?(\d+)(?:\/|\?|$)/); 295 return match ? match[1] : null; 296 } 297 298 /** 299 * Creates YouTube embed HTML 300 * @param {string} url YouTube URL 301 * @param {Object} data Video settings 302 * @param {boolean} autoplay Whether video should autoplay 303 * @return {string|boolean|null} Embed HTML or false/null on error 304 */ 305 createYouTubeEmbed(url, data, autoplay) { 306 if ( !url ) return null; 307 308 url = url.trim(); 309 const videoId = this.extractYouTubeId(url); 310 if ( !videoId ) return false; 311 312 const youtubeDomain = data.noCookie === true || data.noCookie == 1 ? 'youtube-nocookie.com' : 'youtube.com'; 313 314 let embedUrl = `https://www.${youtubeDomain}/embed/${videoId}?`; 315 316 embedUrl += `autoplay=${autoplay ? 1 : 0}`; 317 318 if (data.mute == 1) embedUrl += `&mute=1`; 319 320 return `<iframe src="${embedUrl}" frameborder="0" allow="autoplay; fullscreen"></iframe>`; 321 } 322 323 /** 324 * Creates Vimeo embed HTML 325 * @param {string} url Vimeo URL 326 * @param {Object} data Video settings 327 * @param {boolean} autoplay Whether video should autoplay 328 * @return {string|boolean|null} Embed HTML or false/null on error 329 */ 330 createVimeoEmbed(url, data, autoplay) { 331 if ( !url ) return null; 332 333 url = url.trim(); 334 const videoId = this.extractVimeoId(url); 335 if ( !videoId ) return false; 336 337 let embedUrl = `https://player.vimeo.com/video/${videoId}?`; 338 339 embedUrl += `autoplay=${autoplay ? 1 : 0}`; 340 341 if (data.mute == 1) embedUrl += `&muted=1`; 342 if (data.noCookie === true || data.noCookie == 1) embedUrl += `&dnt=1`; 343 344 return `<iframe src="${embedUrl}" frameborder="0" allow="autoplay; fullscreen"></iframe>`; 345 } 346 347 /** 348 * Creates direct video embed HTML 349 * @param {string} url Direct video URL 350 * @param {Object} data Video settings 351 * @param {boolean} autoplay Whether video should autoplay 352 * @return {string|boolean|null} Embed HTML or false/null on error 353 */ 354 createDirectVideoEmbed(url, data, autoplay) { 355 if ( !url ) return null; 356 357 url = url.trim(); 358 359 if ( !this.isDirectVideo(url) ) return false; 360 361 const autoplayAttr = autoplay ? ' autoplay' : ''; 362 const mutedAttr = data.mute ? ' muted' : ''; 363 364 const getVideoMimeType = this.getDirectVideoMimeType(url) || 'video/mp4'; 365 366 return `<video${mutedAttr}${autoplayAttr}
366 controls playsInline controlslist="nodownload" disablePictureInPicture="true"> 367 <source src="${url}" type="${getVideoMimeType}"> 368 Your browser does not support the video tag. 369 </video>`; 370 } 371 372 /** 373 * Removes any existing popup from the page 374 */ 375 deleteCurrentPopup() { 376 const currentOldPopup = document.querySelector('.YouTubePopUp-Wrap'); 377 if ( currentOldPopup ) { 378 currentOldPopup.remove(); 379 } 380 381 const currentPopup = document.querySelector('.vp-show'); 382 if ( currentPopup ) { 383 currentPopup.classList.remove('vp-show'); 384 currentPopup.remove(); 385 } 386 } 387 388 /** 389 * Creates and displays the popup with video content 390 * @param {string} embedHTML HTML for video embed 391 * @param {Object} data Video settings 392 */ 393 async createPopup(embedHTML, data) { 394 this.deleteCurrentPopup(); 395 396 const popup = document.createElement('div'); 397 const modalType = this.defaults.onPageLoad ? 'vp-modal vp-modal_type_onpageload' : 'vp-modal vp-modal_type_click'; 398 popup.className = modalType; 399 400 const videoTypeClass = data.videoType ? ' ' + data.videoType : ''; 401 const iframe = embedHTML ? embedHTML : ''; 402 403 popup.innerHTML = ` 404 <div class="vp-container${videoTypeClass}"> 405 <div class="vp-video" style="width: 800px; height: 450px;"> 406 <span class="vp-close"></span> 407 ${iframe} 408 </div> 409 </div> 410 `; 411 412 const popupContainer = popup.querySelector('.vp-container'); 413 document.body.appendChild(popup); 414 415 await new Promise(resolve => setTimeout(resolve, 30)); 416 this.deleteCurrentPopup(); 417 popup.classList.add('vp-show'); 418 popup.focus(); 419 data['iframe'] = iframe ? 'appear' : 'no iframe'; 420 421 if ( this.defaults.onPageLoad && data.oplCookie ) { 422 const cookieName = data.oplCookie["name"]; 423 const cookieDays = data.oplCookie["days"]; 424 if ( cookieName && cookieDays ) { 425 this.setCookie(cookieName, parseInt(cookieDays)); 426 } 427 } 428 429 popup.addEventListener('click', async (e) => { 430 if ( data.wrapClose != 1 && data.wrapClose !== true ) { 431 if ( popup && e.target === popup ) { 432 await this.closePopup(); 433 } 434 435 if ( popupContainer && e.target === popupContainer ) { 436 await this.closePopup(); 437 } 438 } 439 }); 440 441 popup.querySelector('.vp-close').addEventListener('click', async () => { 442 await this.closePopup(); 443 }); 444 445 // Allows closing popup by ESC key 446 document.addEventListener('keydown', async (e) => { 447 const isShowPopup = document.querySelector('.vp-show'); // Current Popup 448 if ( e.key === 'Escape' && isShowPopup ) { 449 await this.closePopup(); 450 } 451 }); 452 453 this.debug(data); 454 } 455 456 /** 457 * Closes and removes the popup 458 */ 459 async closePopup() { 460 const popup = document.querySelector('.vp-show'); 461 if (popup) { 462 popup.classList.remove('vp-show'); 463 await new Promise(resolve => setTimeout(resolve, 500)); 464 popup.remove(); 465 } 466 } 467 468 /** 469 * Outputs debug information to console 470 * @param {Object} data Debug data to display 471 */ 472 debug(data) { 473 if ( !this.defaults.handleVars ) { 474 console.log("videoPopup.js: General options object not found. Will use default options."); 475 } 476 477 const debugLogTitle = this.defaults.onPageLoad ? 'Video Popup - On Page Load Modal Debug' : 'Video Popup Modal Debug'; 478 479 if ( this.defaults.debug ) { 480 const videoURL = data.url ? data.url.trim() : null; 481 if ( !videoURL ) { 482 console.log(`=== ${debugLogTitle} - Start ===`); 483 console.log(' No video URL! Please enter a video URL to display the debug result.'); 484 console.log(`=== ${debugLogTitle} - End ===`); 485 console.log(''); // an empty line 486 return; 487 } 488 489 console.log(`=== ${debugLogTitle} - Start ===`); 490 491 console.log('Link Properties:'); 492 for (const [key, value] of Object.entries(data)) { 493 if (key === 'oplCookie' && typeof value === 'object' && value !== null) { 494 console.log(` ${key}: {`); 495 for (const [cookieKey, cookieValue] of Object.entries(value)) { 496 console.log(` ${cookieKey}: "${cookieValue}"`); 497 } 498 console.log(` }`); 499 } else { 500 console.log(` ${key}: ${value}`); 501 } 502 } 503 504 console.log('Functions Status:'); 505 506 console.log(' If YouTube Video:'); 507 const isYouTubeResult = this.isYouTube(videoURL); 508 console.log(` isYouTube: ${isYouTubeResult ? 'true' : isYouTubeResult}`); 509 const youtubeVideoId = this.extractYouTubeId(videoURL); 510 console.log(` extractYouTubeId: ${youtubeVideoId ? `Works Good - ${youtubeVideoId}` : youtubeVideoId}`); 511 const youtubeEmbedResult = this.createYouTubeEmbed(videoURL, data, true); 512 console.log(` createYouTubeEmbed: ${youtubeEmbedResult ? 'Works Good' : youtubeEmbedResult}`); 513 514 console.log(' If Vimeo Video:'); 515 const isVimeoResult = this.isVimeo(videoURL); 516 console.log(` isVimeo: ${isVimeoResult ? 'true' : isVimeoResult}`); 517 const vimeoVideoId = this.extractVimeoId(videoURL); 518 console.log(` extractVimeoId: ${vimeoVideoId ? `Works Good - ${vimeoVideoId}` : vimeoVideoId}`); 519 const vimeoEmbedResult = this.createVimeoEmbed(videoURL, data, true); 520 console.log(` createVimeoEmbed: ${vimeoEmbedResult ? 'Works Good' : vimeoEmbedResult}`); 521 522 console.log(' If Direct Video:'); 523 const isDirectVideoResult = this.isDirectVideo(videoURL); 524 console.log(` isDirectVideo: ${isDirectVideoResult ? 'true' : isDirectVideoResult}`); 525 const directEmbedResult = this.createDirectVideoEmbed(videoURL, data, true); 526 console.log(` createDirectVideoEmbed: ${directEmbedResult ? 'Works Good' : directEmbedResult}`); 527 528 const generateEmbedResult = this.generateEmbed(data, true); 529 console.log(` generateEmbed: ${generateEmbedResult ? 'Works Good' : generateEmbedResult}`); 530 531 console.log(`=== ${debugLogTitle} - End ===`); 532 console.log(''); // an empty line 533 } 534 } 535} 536 537 538/** 539 * VideoPopupOnPageLoad class extends core popup functionality 540 * with specialized behavior for automatic page load display 541 * 542 * @extends VideoPopup 543 */ 544class VideoPopupOnPageLoad extends VideoPopup { 545 constructor(options = {}) { 546 super(options); 547 } 548 549 init(){} 550 551 async closePopup() { 552 await super.closePopup(); 553 delete this; 554 } 555} 556 557 558/** 559 * Auto-initialization when DOM is ready 560 * Creates VideoPopup instance with default or custom settings 561 */ 562document.addEventListener('DOMContentLoaded', () => { 563 if ( typeof theVideoPopupGeneralOptions === 'undefined' ) { 564 new VideoPopup( { handleVars: false } ); 565 return; 566 } 567 568 new VideoPopup({ 569 autoplay: true, 570 onPageLoad: false, 571 oplCookie: false, 572 wrapClose: theVideoPopupGeneralOptions.wrap_close, 573 noCookie: theVideoPopupGeneralOptions.no_cookie, 574 handleVars: true, 575 debug: theVideoPopupGeneralOptions.debug 576 });
vendor: 3 bytes, line 577
577});
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.