1/** 2 * YouTube Content Display System 3 * 4 * This script provides comprehensive functionality for: 5 * - Responsive YouTube video sliders (both channel and playlist) 6 * - Masonry grid layouts for YouTube content 7 * - Interactive popup modals for video playback 8 * - Playlist-style video display with dynamic content loading 9 */ 10 11jQuery(document).ready(function($) { 12 // ============================================= 13 // YouTube Channel Slider Configuration 14 // ============================================= 15 16 /** 17 * Initializes responsive Owl Carousel sliders for YouTube channel content 18 * with configurable columns and spacing across different screen sizes. 19 */ 20 $('.dsf_yt_slider_wrapper').each(function() { 21 let $this = $(this); 22 23 // Parse spacing values with fallback to 20px 24 let spacingDesktop = parseInt($this.data('spacing-desktop')) || 20; 25 let spacingTablet = parseInt($this.data('spacing-tablet')) || 20; 26 let spacingMobile = parseInt($this.data('spacing-mobile')) || 20; 27 28 // Get column configuration from data attributes 29 let columnDesktop = $this.data('desktop-column'); 30 let columnTablet = $this.data('tablet-column'); 31 let columnPhone = $this.data('phone-column'); 32 33 // Initialize Owl Carousel with responsive settings 34 $this.find('.owl-carousel.dsf_yt_slider').owlCarousel({ 35 items: 1, 36 loop: true, 37 nav: true, 38 dots: true, 39 autoplay: false, 40 autoplayTimeout: 3000, 41 navText: ['<span class="prev-arrow">â¹</span>', '<span class="next-arrow">âº</span>'], 42 responsive: { 43 0: { 44 items: columnPhone || 1, 45 margin: spacingMobile 46 }, 47 768: { 48 items: columnTablet || 2, 49 margin: spacingTablet 50 }, 51 981: { 52 items: columnDesktop || 3, 53 margin: spacingDesktop 54 } 55 } 56 }); 57 }); 58 59 60 61 62 // ============================================= 63 // YouTube Playlist Slider Configuration 64 // ============================================= 65 66 /** 67 * Initializes responsive Owl Carousel sliders for YouTube playlist content 68 * with identical configuration options as channel sliders. 69 */ 70 $('.dsf_ytpl_slider_wrapper').each(function() { 71 let $this = $(this); 72 73 // Parse spacing values with fallback to 20px 74 let spacingDesktop = parseInt($this.data('spacing-desktop')) || 20; 75 let spacingTablet = parseInt($this.data('spacing-tablet')) || 20; 76 let spacingMobile = parseInt($this.data('spacing-mobile')) || 20; 77 78 // Get column configuration from data attributes 79 let columnDesktop = $this.data('desktop-column'); 80 let columnTablet = $this.data('tablet-column'); 81 let columnPhone = $this.data('phone-column'); 82 83 // Initialize Owl Carousel with responsive settings 84 $this.find('.owl-carousel.dsf_ytpl_slider').owlCarousel({ 85 items: 1, 86 loop: true, 87 nav: true, 88 dots: true, 89 autoplay: false, 90 autoplayTimeout: 3000, 91 navText: ['<span class="prev-arrow">â¹</span>', '<span class="next-arrow">âº</span>'], 92 responsive: { 93 0: { 94 items: columnPhone || 1, 95 margin: spacingMobile 96 }, 97 768: { 98 items: columnTablet || 2, 99 margin: spacingTablet 100 }, 101 981: { 102 items: columnDesktop || 3, 103 margin: spacingDesktop 104 } 105 } 106 }); 107 }); 108 109 // ============================================= 110 // Masonry Grid Layout Configuration 111 // ============================================= 112 113 114 let $ytGrid = $('.dsf_yt_grid_masonry'); 115 $ytGrid.imagesLoaded(function () { 116 $ytGrid.masonry({ 117 itemSelector: '.dsf_yt_feed_item', 118 columnWidth: '.grid-sizer', 119 gutter: '.gutter-sizer', 120 percentPosition: true 121 }); 122 });
123 124 /** 125 * Initializes Masonry grid layout for YouTube playlist content 126 */ 127 let $ytplGrid = $('.dsf_ytpl_grid_masonry'); 128 $ytplGrid.imagesLoaded(function () { 129 $ytplGrid.masonry({ 130 itemSelector: '.dsf_ytpl_feed_item', 131 columnWidth: '.grid-sizer', 132 gutter: '.gutter-sizer', 133 percentPosition: true 134 }); 135 }); 136 137 138 $(".dsf_yt_post_caption, .dsf_ytpl_post_caption").each(function () { 139 let text = $(this).text(); 140 // Replace the literal <br /> (or <br>) text with actual line breaks 141 text = text.replace(/<br\s*\/?>/gi, "<br>"); 142 $(this).html(text); 143 }); 144 145 $('.dsf_yt_grid_masonry').masonry('layout'); 146 $('.dsf_ytpl_grid_masonry').masonry('layout'); 147 148 149 // ============================================= 150 // YouTube Channel Popup Functionality 151 // ============================================= 152 153 /** 154 * Handles the YouTube channel video popup functionality including: 155 * - Opening popup with video and metadata 156 * - Closing popup and stopping video playback 157 * - Handling click events inside/outside popup 158 */ 159 160 $(document).on("click", ".dsf_yt_feed_item", function() { 161 let postId = $(this).data("id"); 162 let postObject = $(this).data("object"); 163 164 // Validate media data exists 165 if (typeof window[postObject] === "undefined") { 166 console.error("YouTube data object not found:", postObject); 167 return; 168 } 169 170 // Find specific video data 171 let postData = window[postObject].media.find(post => String(post.video_id) === String(postId)); 172 if (!postData) { 173 console.error("Post data not found for ID:", postId); 174 return; 175 } 176 177 // Get popup elements 178 let popupContainer = $(".dsf_yt_popup_container"); 179 let popupLeft = $(".dsf_yt_popup_left"); 180 let popupRight = $(".dsf_yt_popup_right"); 181 let popupImage = popupLeft.find("img"); 182 let popupVideoContainer = popupLeft.find(".dsf_popup_video_container"); 183 let carousel = popupLeft.find(".dsf_popup_carousel"); 184 185 // Clear previous content 186 popupVideoContainer.empty().hide(); 187 popupImage.hide(); 188 carousel.empty().hide(); 189 190 // Update popup content with video metadata 191 popupRight.find(".dsf_yt_popup_video_title").text(postData.title || "Unknown Title"); 192 popupRight.find(".dsf_yt_popup_data").text(postData.published_at || "Unknown Date"); 193 194 // Format caption with line breaks 195 let formattedCaption = (postData.description || "No caption available.").replace(/\n/g, "<br>"); 196 popupRight.find(".dsf_yt_popup_caption").html(formattedCaption); 197 198 // Update engagement metrics 199 popupRight.find(".dsf_yt_popup_meta .dsf_yt_like_count .like-count").text(postData.likes || 0); 200 popupRight.find(".dsf_yt_popup_meta .dsf_yt_comment_count .comment-count").text(postData.comments || 0); 201 popupRight.find(".dsf_yt_popup_meta .dsf_yt_vidio_views_count .views-count").text(postData.views || 0); 202 203 // Embed YouTube video if available 204 if (postData.video_url) { 205 let videoEmbedUrl = postData.video_url.replace("watch?v=", "embed/") + "?autoplay=0&rel=0&modestbranding=1&controls=1"; 206 let iframe = `<iframe width="100%" height="auto" style="aspect-ratio: 16/9;" src="${videoEmbedUrl}" frameborder="0" allowfullscreen></iframe>`; 207 popupVideoContainer.html(iframe).show(); 208 } 209 210 // Display the popup 211 $(".dsf_yt_popup").fadeIn(function () { 212 $("body").addClass("dsf-popup-open"); //Add class when popup opens 213 }); 214 }); 215 216 217 // /** 218 // * Closes the YouTube playlist popup and stops video playback 219 // */ 220 221 $(document).on("click", ".dsf_yt_popup_close", function () { 222 $(".dsf_yt_popup").fadeOut(function () { 223 stopVideoPlayback(); 224 $("body").removeClass("dsf-popup-open"); // Remove class when popup closes 225 }); 226 });
227 228 $(document).on("click", function (e) { 229 if ($(e.target).hasClass("dsf_yt_popup")) { 230 stopVideoPlayback(); 231 $(".dsf_yt_popup").fadeOut(function () { 232 $("body").removeClass("dsf-popup-open"); // Remove class when popup closes 233 }); 234 } 235 }); 236 237 /** 238 * Helper function to stop video playback by removing the iframe 239 */ 240 function stopVideoPlayback() { 241 let popupVideoContainer = $(".dsf_popup_video_container"); 242 popupVideoContainer.empty(); 243 } 244 245 // ============================================= 246 // YouTube Playlist Popup Functionality 247 // ============================================= 248 249 /** 250 * Handles the YouTube playlist video popup functionality with: 251 * - Similar structure to channel popups but with playlist-specific elements 252 */ 253 $(document).on("click", ".has_popup.dsf_ytpl_feed_item", function() { 254 let postId = $(this).data("id"); 255 let postObject = $(this).data("object"); 256 257 // Validate media data exists 258 if (typeof window[postObject] === "undefined") { 259 console.error("YouTube data object not found:", postObject); 260 return; 261 } 262 263 // Find specific video data 264 let postData = window[postObject].media.find(post => String(post.video_id) === String(postId)); 265 if (!postData) { 266 console.error("Post data not found for ID:", postId); 267 return; 268 } 269 270 // Get popup elements 271 let popupContainer = $(".dsf_ytpl_popup_container"); 272 let popupLeft = $(".dsf_ytpl_popup_left"); 273 let popupRight = $(".dsf_ytpl_popup_right"); 274 let popupImage = popupLeft.find("img"); 275 let popupVideoContainer = popupLeft.find(".dsf_popup_video_container"); 276 let carousel = popupLeft.find(".dsf_popup_carousel"); 277 278 // Clear previous content 279 popupVideoContainer.empty().hide(); 280 popupImage.hide(); 281 carousel.empty().hide(); 282 283 // Update popup content with video metadata 284 popupRight.find(".dsf_ytpl_popup_video_title").text(postData.title || "Unknown User"); 285 popupRight.find(".dsf_ytpl_popup_data").text(postData.published_at || "Unknown Date"); 286 popupRight.find(".dsf_ytpl_popup_channel_title").text(postData.channel_title || "Unknown Channel title"); 287 288 // Format caption with line breaks 289 let formattedCaption = (postData.description || "No caption available.").replace(/\n/g, "<br>"); 290 popupRight.find(".dsf_ytpl_popup_caption").html(formattedCaption); 291 292 // Update engagement metrics 293 popupRight.find(".dsf_ytpl_popup_meta .dsf_ytpl_like_count .like-count").text(postData.likes || 0); 294 popupRight.find(".dsf_ytpl_popup_meta .dsf_ytpl_views_count .views-count").text(postData.views || 0); 295 popupRight.find(".dsf_ytpl_popup_meta .dsf_ytpl_comments_count .comments-count").text(postData.comments || 0); 296 297 // Embed YouTube video with additional sandbox permissions 298 if (postData.video_url) { 299 let videoEmbedUrl = postData.video_url.replace("watch?v=", "embed/") + "?autoplay=0&rel=0&modestbranding=1&controls=1"; 300 let iframe = `<iframe sandbox="allow-same-origin allow-scripts allow-presentation allow-popups" width="100%" height="auto" style="aspect-ratio: 16/9;" src="${videoEmbedUrl}" frameborder="0" allowfullscreen></iframe>`; 301 popupVideoContainer.html(iframe).show(); 302 } 303 304 // Show the popup 305 $(".dsf_ytpl_popup").fadeIn(function () { 306 $("body").addClass("dsf-popup-open"); //Add class when popup opens 307 }); 308 }); 309 310 // /** 311 // * Closes the YouTube playlist popup and stops video playback 312 // */ 313 314 $(document).on("click", ".dsf_ytpl_popup_close", function () { 315 $(".dsf_ytpl_popup").fadeOut(function () { 316 stopVideoPlayback(); 317 $("body").removeClass("dsf-popup-open"); // Remove class when popup closes 318 }); 319 });
320 321 $(document).on("click", function (e) { 322 if ($(e.target).hasClass("dsf_ytpl_popup")) { 323 stopVideoPlayback(); 324 $(".dsf_ytpl_popup").fadeOut(function () { 325 $("body").removeClass("dsf-popup-open"); // Remove class when popup closes 326 }); 327 } 328 }); 329 330 // ============================================= 331 // Playlist Style Dynamic Video Loading 332 // ============================================= 333 334 /** 335 * Handles the playlist-style video display functionality including: 336 * - Loading the first video on page load 337 * - Updating video content when playlist items are clicked 338 */ 339 340 // Cache DOM references once 341 const $videoContainer = $(".dsf_ytpl_playlist_left .dsf_ytpl_video_container"); 342 const $likeCount = $(".dsf_ytpl_playlist_left .dsf_ytpl_like_count .like-count"); 343 const $viewsCount = $(".dsf_ytpl_playlist_left .dsf_ytpl_views_count .views-count"); 344 const $commentsCount = $(".dsf_ytpl_playlist_left .dsf_ytpl_comments_count .comments-count"); 345 const $videoTitle = $(".dsf_ytpl_playlist_left .dsf_ytpl_video_title"); 346 const $channelName = $(".dsf_ytpl_playlist_left .dsf_ytpl_channel_title"); 347 const $postCaption = $(".dsf_ytpl_playlist_left .dsf_ytpl_post_caption"); 348 349 function updateVideoContent(postData) { 350 if (!postData) return; 351 352 // Replace only iframe src instead of re-creating iframe 353 let videoEmbedUrl = postData.video_url.replace("watch?v=", "embed/") + "?autoplay=0&rel=0&modestbranding=1&controls=1"; 354 let iframe = $videoContainer.find("iframe"); 355 356 if (iframe.length) { 357 iframe.attr("src", videoEmbedUrl); 358 } else { 359 $videoContainer.html( 360 `<iframe sandbox="allow-same-origin allow-scripts allow-presentation allow-popups" 361 width="100%" height="auto" style="aspect-ratio: 16/9;" 362 src="${videoEmbedUrl}" frameborder="0" allowfullscreen></iframe>` 363 ); 364 } 365 366 // Update metadata 367 $likeCount.text(postData.likes || 0); 368 $viewsCount.text(postData.views || 0); 369 $commentsCount.text(postData.comments || 0); 370 $videoTitle.text(postData.title || "Untitled Video Title"); 371 $channelName.text(postData.channel_title || "Untitled Channel Name"); 372 $postCaption.html((postData.description || "No description available.").replace(/\n/g, "<br>")); 373 } 374 375 376 // Load first video in playlist on page load 377 let firstFeedItem = $(".dsf_ytpl_feed_style_playlist.dsf_ytpl_feed_item").first(); 378 if (firstFeedItem.length) { 379 let postId = firstFeedItem.data("id"); 380 let postObject = firstFeedItem.data("object"); 381 382 if (typeof window[postObject] !== "undefined") { 383 let postData = window[postObject].media.find(post => String(post.video_id) === String(postId));; 384 if (postData) { 385 updateVideoContent(postData); 386 } 387 } 388 } 389 390 // Handle clicks on playlist items to change the displayed video 391 $(document).on("click", ".dsf_ytpl_feed_style_playlist.dsf_ytpl_feed_item", function() { 392 let postId = $(this).data("id"); 393 let postObject = $(this).data("object"); 394 395 if (typeof window[postObject] === "undefined") { 396 console.error("YouTube data object not found:", postObject); 397 return; 398 } 399 400 let postData = window[postObject].media.find(post => String(post.video_id) === String(postId));; 401 if (!postData) { 402 console.error("Post data not found for ID:", postId); 403 return; 404 } 405 406 updateVideoContent(postData); 407 }); 408});
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.