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