1(function($) { 2 "use strict"; 3 4 window.NextGEN_Video = { 5 /** 6 * Detect video platform from URL 7 * @param {string} url 8 * @returns {string|null} Platform name or null if not a video URL 9 */ 10 detect_platform: function(url) { 11 if (!url) return null; 12 13 url = url.trim().toLowerCase(); 14 15 // YouTube patterns 16 if (url.match(/youtube\.com|youtu\.be|youtube-nocookie\.com/)) { 17 return 'youtube'; 18 } 19 20 // Vimeo patterns 21 if (url.match(/vimeo\.com/)) { 22 return 'vimeo'; 23 } 24 25 // Dailymotion patterns 26 if (url.match(/dailymotion\.com|dai\.ly/)) { 27 return 'dailymotion'; 28 } 29 30 // Twitch patterns 31 if (url.match(/twitch\.tv/)) { 32 return 'twitch'; 33 } 34 35 // VideoPress patterns 36 if (url.match(/videopress\.com|video\.wordpress\.com/)) { 37 return 'videopress'; 38 } 39
40 // Wistia patterns 41 if (url.match(/wistia\.com|wistia\.net/)) { 42 return 'wistia'; 43 } 44 45 // Local video patterns (direct video file URLs) 46 if (url.match(/\.(mp4|webm|ogg|ogv|mov|avi|wmv|flv|mkv)(\?|$)/i)) { 47 return 'local'; 48 } 49 50 return null; 51 }, 52 53 /** 54 * Extract video ID from YouTube URL 55 * @param {string} url 56 * @returns {string|null} 57 */ 58 extract_youtube_id: function(url) { 59 if (!url) return null; 60 61 var patterns = [ 62 /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube-nocookie\.com\/embed\/)([^&\n?#]+)/, 63 /youtube\.com\/.*[?&]v=([^&\n?#]+)/, 64 ]; 65 66 for (var i = 0; i < patterns.length; i++) { 67 var match = url.match(patterns[i]); 68 if (match && match[1]) { 69 return match[1]; 70 } 71 } 72 73 return null; 74 }, 75 76 /** 77 * Extract video ID from Vimeo URL 78 * @param {string} url 79 * @returns {string|null} 80 */ 81 extract_vimeo_id: function(url) { 82 if (!url) return null; 83 84 var patterns = [ 85 /vimeo\.com\/(\d+)/, 86 /vimeo\.com\/.*\/(\d+)/, 87 /player\.vimeo\.com\/video\/(\d+)/, 88 ]; 89 90 for (var i = 0; i < patterns.length; i++) { 91 var match = url.match(patterns[i]); 92 if (match && match[1]) { 93 return match[1]; 94 } 95 } 96 97 return null; 98 }, 99 100 /** 101 * Extract video ID from Dailymotion URL 102 * @param {string} url 103 * @returns {string|null} 104 */ 105 extract_dailymotion_id: function(url) { 106 if (!url) return null; 107 108 var patterns = [ 109 /dailymotion\.com\/video\/([^/?]+)/, 110 /dai\.ly\/([^/?]+)/, 111 /dailymotion\.com\/embed\/video\/([^/?]+)/, 112 ]; 113 114 for (var i = 0; i < patterns.length; i++) { 115 var match = url.match(patterns[i]); 116 if (match && match[1]) { 117 return match[1]; 118 } 119 } 120 121 return null; 122 }, 123 124 /** 125 * Extract video ID from Twitch URL 126 * @param {string} url 127 * @returns {Object|null} Object with videoId and type (video/clip) 128 */ 129 extract_twitch_id: function(url) { 130 if (!url) return null; 131 132 // Twitch video pattern: twitch.tv/videos/VIDEO_ID 133 var videoMatch = url.match(/twitch\.tv\/videos\/(\d+)/); 134 if (videoMatch && videoMatch[1]) { 135 return { videoId: videoMatch[1], type: 'video' }; 136 } 137 138 // Twitch clip pattern: twitch.tv/CLIP_ID or clips.twitch.tv/CLIP_ID
139 var clipMatch = url.match(/(?:twitch\.tv\/|clips\.twitch\.tv\/)([^/?]+)/); 140 if (clipMatch && clipMatch[1]) { 141 return { videoId: clipMatch[1], type: 'clip' }; 142 } 143 144 return null; 145 }, 146 147 /** 148 * Extract video ID from VideoPress URL 149 * @param {string} url 150 * @returns {string|null} 151 */ 152 extract_videopress_id: function(url) { 153 if (!url) return null; 154 155 var patterns = [ 156 /videopress\.com\/v\/([^/?]+)/, 157 /video\.wordpress\.com\/v\/([^/?]+)/, 158 ]; 159 160 for (var i = 0; i < patterns.length; i++) { 161 var match = url.match(patterns[i]); 162 if (match && match[1]) { 163 return match[1]; 164 } 165 } 166 167 return null; 168 }, 169 170 /** 171 * Extract video ID from Wistia URL 172 * @param {string} url 173 * @returns {string|null} 174 */ 175 extract_wistia_id: function(url) { 176 if (!url) return null; 177 178 var patterns = [ 179 /wistia\.(?:com|net)\/medias\/([^/?]+)/, 180 /wistia\.(?:com|net)\/embed\/([^/?]+)/, 181 ]; 182 183 for (var i = 0; i < patterns.length; i++) { 184 var match = url.match(patterns[i]); 185 if (match && match[1]) { 186 return match[1]; 187 } 188 } 189 190 return null; 191 }, 192 193 /** 194 * Get video embed URL for a platform 195 * @param {string} platform 196 * @param {string|Object} videoId 197 * @param {Object} settings Video settings (showVideoControls, autoplayVideos) 198 * @returns {string|null} 199 */ 200 get_embed_url: function(platform, videoId, settings) { 201 if (!platform || !videoId) return null; 202 203 settings = settings || {}; 204 var autoplay = settings.autoplay_videos ? 1 : 0; 205 var controls = settings.show_video_controls !== false ? 1 : 0; 206 207 switch (platform) { 208 case 'youtube': 209 var youtubeId = typeof videoId === 'string' ? videoId : videoId.videoId; 210 return 'https://www.youtube.com/embed/' + youtubeId + 211 '?autoplay=' + autoplay + 212 '&controls=' + controls + 213 '&rel=0&modestbranding=1'; 214 215 case 'vimeo': 216 var vimeoId = typeof videoId === 'string' ? videoId : videoId.videoId; 217 return 'https://player.vimeo.com/video/' + vimeoId + 218 '?autoplay=' + autoplay + 219 '&controls=' + controls; 220 221 case 'dailymotion': 222 var dmId = typeof videoId === 'string' ? videoId : videoId.videoId; 223 return 'https://www.dailymotion.com/embed/video/' + dmId + 224 '?autoplay=' + autoplay + 225 '&controls=' + controls; 226 227 case 'twitch': 228 var twitchData = typeof videoId === 'object' ? videoId : { videoId: videoId, type: 'video' }; 229 if (twitchData.type === 'clip') { 230 return 'https://clips.twitch.tv/embed?clip=' + twitchData.videoId + 231 '&autoplay=' + autoplay + 232 '&parent=' + window.location.hostname; 233 } else { 234 return 'https://player.twitch.tv/?video=v' + twitchData.videoId + 235 '&autoplay=' + autoplay + 236 '&parent=' + window.location.hostname; 237 } 238 239 case 'videopress': 240 var vpId = typeof videoId === 'string' ? videoId : videoId.videoId; 241 return 'https://videopress.com/embed/' + vpId + 242 '?autoplay=' + autoplay + 243 '&controls=' + controls; 244 245 case 'wistia': 246 var wistiaId = typeof videoId === 'string' ? videoId : videoId.videoId; 247 return 'https://fast.wistia.net/embed/iframe/' + wistiaId + 248 '?autoplay=' + autoplay +
249 '&controlsVisibleOnLoad=' + controls; 250 251 case 'local': 252 return typeof videoId === 'string' ? videoId : null; 253 254 default: 255 return null; 256 } 257 }, 258 259 /** 260 * Create HTML5 video player for local videos 261 * @param {string} videoUrl 262 * @param {Object} settings Video settings 263 * @param {string} containerClass 264 * @param {string} videoClass 265 * @returns {HTMLElement} 266 */ 267 create_local_player: function(videoUrl, settings, containerClass, videoClass) { 268 var container = document.createElement("div"); 269 container.className = containerClass || "ngg-video-container"; 270 271 var video = document.createElement("video"); 272 video.className = videoClass || "ngg-video-player"; 273 video.controls = settings.show_video_controls !== false; 274 video.autoplay = settings.autoplay_videos === true; 275 video.playsInline = true; 276 video.preload = "auto"; 277 video.setAttribute("playsinline", ""); 278 video.setAttribute("webkit-playsinline", ""); 279 video.src = videoUrl; 280 281 // Global error handler for video loading failures (CORS, network, unsupported formats) 282 video.addEventListener("error", function(e) { 283 console.error("Video player error:", { 284 error: e, 285 videoUrl: videoUrl, 286 errorCode: video.error ? video.error.code : "unknown", 287 errorMessage: video.error ? video.error.message : "Unknown error" 288 }); 289 }); 290 291 // Size video to match lightbox image sizing behavior 292 video.addEventListener("loadedmetadata", function () { 293 var naturalWidth = video.videoWidth; 294 var naturalHeight = video.videoHeight; 295
296 if (naturalWidth && naturalHeight) { 297 var container = video.closest('.ngg-video-container'); 298 var displayWidth = naturalWidth; 299 var displayHeight = naturalHeight; 300 301 // Detect lightbox type and apply appropriate sizing logic 302 if (container) { 303 var fancyboxContent = container.closest('#fancybox-content'); 304 var tbWindow = container.closest('#TB_window'); 305 var slImage = container.closest('.sl-image'); 306 var shWrap = container.closest('#shWrap'); 307 308 if (shWrap) { 309 // Shutter/Shutter Reloaded 310 var wiH = window.innerHeight || 0; 311 var dbH = document.body.clientHeight || 0; 312 var deH = document.documentElement ? document.documentElement.clientHeight : 0; 313 var wHeight; 314 315 if (wiH > 0) { 316 wHeight = ((wiH - dbH) > 1 && (wiH - dbH) < 30) ? dbH : wiH; 317 wHeight = ((wHeight - deH) > 1 && (wHeight - deH) < 30) ? deH : wHeight; 318 } else { 319 wHeight = (deH > 0) ? deH : dbH; 320 } 321 322 if (document.getElementsByTagName("body")[0].className.match(/admin-bar/) 323 && document.getElementById('wpadminbar') !== null) { 324 wHeight = wHeight - document.getElementById('wpadminbar').offsetHeight; 325 } 326 327 var shHeight = wHeight - 50; 328 var deW = document.documentElement ? document.documentElement.clientWidth : 0; 329 var dbW = window.innerWidth || document.body.clientWidth; 330 var wWidth = (deW > 1) ? deW : dbW; 331 332 if (displayHeight > shHeight) { 333 displayWidth = displayWidth * (shHeight / displayHeight); 334 displayHeight = shHeight; 335 } 336 if (displayWidth > (wWidth - 16)) { 337 displayHeight = displayHeight * ((wWidth - 16) / displayWidth); 338 displayWidth = wWidth - 16; 339 } 340 341 video.style.width = displayWidth + "px"; 342 video.style.height = displayHeight + "px"; 343 video.style.maxWidth = "none"; 344 video.style.maxHeight = "none"; 345 video.setAttribute("width", displayWidth); 346 video.setAttribute("height", displayHeight); 347 } else if (fancyboxContent) { 348 // Fancybox 349 setTimeout(function() { 350 var contentRect = fancyboxContent.getBoundingClientRect(); 351 if (contentRect.width > 10 && contentRect.height > 10) { 352 var maxW = contentRect.width; 353 var maxH = contentRect.height; 354 355 if (displayWidth > maxW || displayHeight > maxH) { 356 var ratio = displayWidth / displayHeight > maxW / maxH 357 ? displayWidth / maxW 358 : displayHeight / maxH; 359 displayWidth = displayWidth / ratio; 360 displayHeight = displayHeight / ratio; 361 } 362 363 video.style.width = displayWidth + "px"; 364 video.style.height = displayHeight + "px"; 365 video.setAttribute("width", displayWidth); 366 video.setAttribute("height", displayHeight); 367 } 368 }, 50); 369 return; 370 } else if (tbWindow) { 371 // Thickbox 372 var pageWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; 373 var pageHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; 374 var x = pageWidth - 150; 375 var y = pageHeight - 150; 376 377 if (displayWidth > x) { 378 displayHeight = displayHeight * (x / displayWidth); 379 displayWidth = x; 380 if (displayHeight > y) { 381 displayWidth = displayWidth * (y / displayHeight); 382 displayHeight = y; 383 } 384 } else if (displayHeight > y) { 385 displayWidth = displayWidth * (y / displayHeight); 386 displayHeight = y; 387 if (displayWidth > x) { 388 displayHeight = displayHeight * (x / displayWidth); 389 displayWidth = x; 390 } 391 } 392 393 video.style.width = displayWidth + "px"; 394 video.style.height = displayHeight + "px"; 395 video.setAttribute("width", displayWidth); 396 video.setAttribute("height", displayHeight); 397 } else if (slImage) { 398 // SimpleLightbox 399 var widthRatio = 0.8; 400 var heightRatio = 0.9; 401 var windowWidth = window.innerWidth; 402 var windowHeight = window.innerHeight; 403 404 var maxWidth = windowWidth * widthRatio; 405 var maxHeight = windowHeight * heightRatio; 406 407 if (displayWidth > maxWidth || displayHeight > maxHeight) { 408 var ratio = displayWidth / displayHeight > maxWidth / maxHeight 409 ? displayWidth / maxWidth 410 : displayHeight / maxHeight; 411 displayWidth /= ratio; 412 displayHeight /= ratio; 413 } 414 415 video.style.width = displayWidth + "px"; 416 video.style.height = displayHeight + "px"; 417 video.style.maxWidth = maxWidth + "px"; 418 video.style.maxHeight = maxHeight + "px"; 419 } 420 } 421 } 422 }); 423 424 // Attempt autoplay when ready 425 if (settings.autoplay_videos) { 426 video.addEventListener("canplay", function () { 427 video.play().catch(function (error) {
428 console.error("Video autoplay failed:", error); 429 }); 430 }); 431 } 432 433 container.appendChild(video); 434 return container; 435 }, 436 437 /** 438 * Create iframe embed for platform videos 439 * @param {string} embedUrl 440 * @param {Object} settings Video settings 441 * @param {string} containerClass 442 * @returns {HTMLElement} 443 */ 444 create_embed_player: function(embedUrl, settings, containerClass) { 445 var container = document.createElement("div"); 446 container.className = containerClass || "ngg-video-container"; 447 448 var iframe = document.createElement("iframe"); 449 iframe.src = embedUrl; 450 iframe.frameBorder = "0"; 451 iframe.allowFullscreen = true; 452 iframe.setAttribute("allow", "autoplay; encrypted-media"); 453 iframe.style.width = "100%"; 454 iframe.style.height = "100%"; 455 iframe.style.border = "none"; 456 457 // Global error handler for iframe loading failures (CORS, network, etc.) 458 iframe.addEventListener("error", function(e) { 459 console.error("Video iframe error:", { 460 error: e, 461 embedUrl: embedUrl 462 }); 463 }); 464 465 // Handle iframe load errors (some browsers don't fire error event on iframe) 466 iframe.addEventListener("load", function() { 467 // Check if iframe loaded successfully by trying to access content 468 try { 469 // This will throw if cross-origin restrictions prevent access 470 var iframeDoc = iframe.contentDocument || iframe.contentWindow.document; 471 } catch (e) { 472 // Cross-origin restriction is normal, not an error 473 // Only log actual errors 474 if (e.name !== "SecurityError") { 475 console.error("Video iframe load error:", e); 476 } 477 } 478 }); 479 480 // Size iframe to match lightbox image sizing behavior 481 // Use responsive aspect ratio (16:9 default for most platforms) 482 var aspectRatio = 16 / 9; 483 var maxWidth = window.innerWidth * 0.9; 484 var maxHeight = window.innerHeight * 0.9; 485 var width = Math.min(maxWidth, 1080); 486 var height = width / aspectRatio; 487 488 if (height > maxHeight) { 489 height = maxHeight; 490 width = height * aspectRatio; 491 } 492 493 container.style.width = width + "px"; 494 container.style.height = height + "px"; 495 container.style.maxWidth = "100%"; 496 container.style.maxHeight = "90vh"; 497 498 container.appendChild(iframe); 499 return container; 500 }, 501 502 /** 503 * Generic content handler to inject video into a container 504 * @param {Object} options 505 * @param {string} options.videoUrl - Video URL 506 * @param {HTMLElement|jQuery} options.container - Container to append content to 507 * @param {Object} options.settings - Video settings (showVideoControls, showPlayPauseControls, autoplayVideos) 508 * @param {string} [options.containerClass] - CSS class for player container 509 * @param {string} [options.videoClass] - CSS class for video element 510 * @param {Function} [options.onBeforeAppend] - Callback before appending 511 * @returns {HTMLElement|null} 512 */ 513 handle_content: function(options) { 514 var self = this; 515 var videoUrl = options.videoUrl; 516 var $targetContainer = $(options.container); 517 var settings = options.settings || {}; 518 519 if (!videoUrl) { 520 console.error("Video URL is required"); 521 return null; 522 } 523 524 try { 525 var platform = self.detect_platform(videoUrl); 526 if (!platform) { 527 // Not a recognized video URL 528 console.warn("Unrecognized video URL:", videoUrl); 529 return null; 530 } 531 } catch (error) { 532 console.error("Error detecting video platform:", error); 533 return null; 534 } 535 536 var videoContent = null; 537 var videoId = null; 538 539 // Extract video ID based on platform 540 switch (platform) { 541 case 'youtube': 542 videoId = self.extract_youtube_id(videoUrl); 543 break; 544 case 'vimeo':
545 videoId = self.extract_vimeo_id(videoUrl); 546 break; 547 case 'dailymotion': 548 videoId = self.extract_dailymotion_id(videoUrl); 549 break; 550 case 'twitch': 551 videoId = self.extract_twitch_id(videoUrl); 552 break; 553 case 'videopress': 554 videoId = self.extract_videopress_id(videoUrl); 555 break; 556 case 'wistia': 557 videoId = self.extract_wistia_id(videoUrl); 558 break; 559 case 'local': 560 videoId = videoUrl; // For local videos, use the URL directly 561 break; 562 } 563 564 if (!videoId) { 565 var errorMsg = self.create_error("Could not extract video ID from URL", options.errorClass); 566 if (typeof options.onBeforeAppend === "function") options.onBeforeAppend(errorMsg); 567 $targetContainer.append(errorMsg); 568 return errorMsg; 569 } 570 571 // Create player based on platform 572 try { 573 if (platform === 'local') { 574 videoContent = self.create_local_player(videoId, settings, options.containerClass, options.videoClass); 575 } else { 576 var embedUrl = self.get_embed_url(platform, videoId, settings); 577 if (embedUrl) { 578 videoContent = self.create_embed_player(embedUrl, settings, options.containerClass); 579 } else { 580 var errorMsg = self.create_error("Could not generate embed URL", options.errorClass); 581 if (typeof options.onBeforeAppend === "function") options.onBeforeAppend(errorMsg); 582 $targetContainer.append(errorMsg); 583 return errorMsg; 584 } 585 } 586 587 if (videoContent) { 588 // Handle errors for local videos 589 if (platform === 'local') { 590 var video = videoContent.querySelector("video"); 591 if (video) { 592 video.onerror = function () { 593 $(videoContent).remove(); 594 var errorMsg = self.create_error("Video failed to load", options.errorClass); 595 if (typeof options.onBeforeAppend === "function") options.onBeforeAppend(errorMsg); 596 $targetContainer.append(errorMsg); 597 }; 598 } 599 } 600 601 if (typeof options.onBeforeAppend === "function") options.onBeforeAppend(videoContent); 602 $targetContainer.append(videoContent); 603 } 604 } catch (error) { 605 console.error("Error creating video player:", error); 606 var errorMsg = self.create_error("Video player creation failed", options.errorClass); 607 if (typeof options.onBeforeAppend === "function") options.onBeforeAppend(errorMsg); 608 $targetContainer.append(errorMsg); 609 return errorMsg; 610 } 611 612 return videoContent; 613 }, 614 615 /** 616 * Create error message element 617 * @param {string} message 618 * @param {string} containerClass 619 * @returns {HTMLElement} 620 */ 621 create_error: function(message, containerClass) { 622 var container = document.createElement("div"); 623 container.className = containerClass || "ngg-video-error"; 624 container.innerHTML = 625 '<div class="ngg-video-error-content">' + 626 '<span class="ngg-video-error-icon">⚠</span>' + 627 '<span class="ngg-video-error-text">' + 628 (message || "Video failed to load") + 629 "</span>" + 630 "</div>"; 631 return container; 632 } 633 }; 634})(jQuery); 635
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.