1/** 2 * YouTube Feed Load More Functionality 3 * 4 * This script handles the "Load More" functionality for YouTube feed blocks. 5 * It makes AJAX requests to fetch additional videos and appends them to the feed. 6 * Also includes playlist-specific functionality with similar behavior. 7 */ 8 9jQuery(document).ready(function ($) { 10 // ============================================= 11 // YouTube Video Feed Load More Implementation 12 // ============================================= 13 14 /** 15 * Initialize load more functionality for each YouTube feed block 16 */ 17 $(".dsf_yt_load_more").each(function () { 18 let button = $(this); 19 let blockId = button.data("object"); 20 let feedContainer = $("#" + blockId); 21 22 // Check if feed container exists 23 if (!feedContainer.length) { 24 console.warn(`Feed container with ID '${blockId}' not found.`); 25 return; 26 } 27 28 // Initialize nextCursor for pagination 29 feedContainer.data("nextCursor", ""); 30 31 /** 32 * Click handler for load more button 33 */ 34 button.on("click", function () { 35 let perPage = button.data("per-page"); 36 let captionLength = button.data("caption-length"); 37 let originalText = button.text(); 38 let nextCursor = feedContainer.data("nextCursor"); // Get stored cursor 39 40 // AJAX request to fetch more videos 41 $.ajax({
42 url: dsf_yt_ajax.ajax_url, 43 type: "POST", 44 data: { 45 action: "dsf_yt_load_more", 46 nonce: dsf_yt_ajax.nonce, 47 block_id: blockId, 48 perpage: perPage, 49 next_cursor: nextCursor, 50 }, 51 beforeSend: function () { 52 button.prop("disabled", true).text("Loading..."); 53 }, 54 success: function (response) { 55 if (response.success && response.data.media.length > 0) { 56 // Initialize media array if it doesn't exist 57 if (!window[blockId]) { 58 window[blockId] = { media: [] }; 59 } 60 window[blockId].media = window[blockId].media.concat(response.data.media); 61 62 // If initial cursor was empty, hide button immediately 63 if (response.data.initial_empty) { 64 button.hide(); 65 return; 66 } 67 68 // Update next cursor for pagination 69 feedContainer.data("nextCursor", response.data.next_cursor); 70 71 let existingArticle = feedContainer.find(".dsf_yt_feed_item").first(); 72 73 if (existingArticle.length) { 74 // Process each new video in the response 75 $.each(response.data.media, function (index, post) { 76 let newArticle = existingArticle.clone(); 77 78 // Update article attributes 79 newArticle.attr("data-id", post.video_id); 80 newArticle.attr("data-link", post.video_url); 81 newArticle.attr("data-object", blockId); 82 83 // Update thumbnail image 84 let img = newArticle.find("img.youtube_feed_media"); 85 if (img.length) { 86 img.attr("src", post.thumbnails.url || ''); 87 } 88 89 // Update video duration 90 let duration = newArticle.find(".dsf_yt_post_duration"); 91 if (duration.length) { 92 duration.text(post.duration || "0"); 93 } 94 95 // Update video title 96 let videoTitle = newArticle.find(".dsf_yt_video_title"); 97 if (videoTitle.length) { 98 videoTitle.text(post.title || "Unknown Title"); 99 } 100 101 // Update like count 102 let likeCountElement = newArticle.find(".dsf_yt_like_count .like-count"); 103 if (likeCountElement.length) { 104 likeCountElement.text(post.likes || "0"); 105 } 106 107 // Update comment count 108 let commentCountElement = newArticle.find(".dsf_yt_comment_count .comment-count"); 109 if (commentCountElement.length) { 110 commentCountElement.text(post.comments || "0"); 111 } 112 113 // Update view count 114 let viewsCountElement = newArticle.find(".dsf_yt_vidio_views_count .views-count"); 115 if (viewsCountElement.length) { 116 viewsCountElement.text(post.views || "0"); 117 } 118 119 // Update publish date 120 let videoPublishDate = newArticle.find(".dsf_yt_publish_date"); 121 if (videoPublishDate.length) { 122 videoPublishDate.text(post.published_at || ""); 123 } 124 125 // Update video caption (trimmed to specified length) 126 127 let captionElement = newArticle.find(".dsf_yt_post_caption"); 128 if (captionElement.length) { 129 captionElement.empty(); // clear old caption 130 131 let fullCaption = post.description || ""; 132 let trimmedCaption = fullCaption.length > captionLength 133 ? fullCaption.substring(0, captionLength) + "â¦" 134 : fullCaption; 135 136 let formattedCaption = trimmedCaption.replace(/\n/g, "<br>"); 137 captionElement.html(formattedCaption); 138 } 139 140 // Append new article to feed 141 feedContainer.append(newArticle); 142 143 // Trigger Isotope/Masonry layout update only if it's masonry layout 144 if (feedContainer.hasClass("dsf_yt_grid_masonry")) { 145 feedContainer.imagesLoaded(function () { 146 feedContainer.masonry("appended", newArticle); 147 }); 148 } 149 }); 150 151 } else { 152 console.warn(`No existing article found to clone for ${blockId}.`); 153 } 154 155 // Hide button if no more data available 156 if (!response.data.next_cursor) { 157 console.log(`No more Videos for ${blockId}.`); 158 button.hide(); 159 } else { 160 button.text(originalText); 161 } 162 } else { 163 console.warn(`No data received for ${blockId}.`); 164 button.text(originalText); 165 button.hide(); 166 } 167 }, 168 error: function (error) { 169 console.error(`Error loading videos for ${blockId}:`, error); 170 button.text(originalText); 171 }, 172 complete: function () { 173 button.prop("disabled", false); 174 }, 175 }); 176 }); 177 });
178 179 /** 180 * Click handler for video items - opens video in new tab when popup is disabled 181 */ 182 $(document).on("click", ".dsf_yt_feed_inner article[data-link]", function () { 183 if ($(this).attr("data-popup") === "off") { 184 var url = $(this).attr("data-link"); 185 if (url) { 186 window.open(url, "_blank"); 187 } 188 } 189 }); 190 191 // ============================================= 192 // YouTube Playlist Feed Load More Implementation 193 // ============================================= 194 195 /** 196 * Initialize load more functionality for each YouTube playlist feed block 197 * (Similar structure to video feed but with playlist-specific elements) 198 */ 199 $(".dsf_ytpl_load_more").each(function () { 200 let button = $(this); 201 let blockId = button.data("object"); 202 let feedContainer = $("#" + blockId); 203 204 if (!feedContainer.length) { 205 console.warn(`Feed container with ID '${blockId}' not found.`); 206 return; 207 } 208 209 // Initialize nextCursor for pagination 210 feedContainer.data("nextCursor", ""); 211 212 /** 213 * Click handler for playlist load more button 214 */ 215 button.on("click", function () { 216 let perPage = button.data("per-page"); 217 let captionLength = button.data("caption-length"); 218 let originalText = button.text(); 219 let nextCursor = feedContainer.data("nextCursor"); 220 221 // AJAX request to fetch more playlist items 222 $.ajax({
223 url: dsf_ytpl_ajax.ajax_url, 224 type: "POST", 225 data: { 226 action: "dsf_ytpl_load_more", 227 nonce: dsf_ytpl_ajax.nonce, 228 block_id: blockId, 229 perpage: perPage, 230 next_cursor: nextCursor, 231 }, 232 beforeSend: function () { 233 button.prop("disabled", true).text("Loading..."); 234 }, 235 success: function (response) { 236 if (response.success && response.data.media.length > 0) { 237 // Initialize media array if it doesn't exist 238 if (!window[blockId]) { 239 window[blockId] = { media: [] }; 240 } 241 window[blockId].media = window[blockId].media.concat(response.data.media); 242 243 // If initial cursor was empty, hide button immediately 244 if (response.data.initial_empty) { 245 button.hide(); 246 return; 247 } 248 249 // Update next cursor for pagination 250 feedContainer.data("nextCursor", response.data.next_cursor); 251 252 let existingArticle = feedContainer.find(".dsf_ytpl_feed_item").first(); 253 254 if (existingArticle.length) { 255 // Process each new playlist item in the response 256 $.each(response.data.media, function (index, post) { 257 let newArticle = existingArticle.clone(); 258 259 // Update article attributes 260 newArticle.attr("data-id", post.video_id); 261 newArticle.attr("data-link", post.video_url); 262 newArticle.attr("data-object", blockId); 263 264 // Update thumbnail image 265 let img = newArticle.find("img.youtube_feed_media"); 266 if (img.length) { 267 img.attr("src", post.thumbnails || ''); 268 } 269 270 // Update video duration 271 let duration = newArticle.find(".dsf_ytpl_post_duration"); 272 if (duration.length) { 273 duration.text(post.duration || "0"); 274 } 275 276 // Update video title 277 let videoTitle = newArticle.find(".dsf_ytpl_video_title"); 278 if (videoTitle.length) { 279 videoTitle.text(post.title || "Unknown Title"); 280 } 281 282 // Update like count 283 let likeCountElement = newArticle.find(".dsf_ytpl_like_count .like-count"); 284 if (likeCountElement.length) { 285 likeCountElement.text(post.likes || "0"); 286 } 287 288 // Update channel name 289 let channelName = newArticle.find(".dsf_ytpl_channel_title"); 290 if (channelName.length) { 291 channelName.text(post.channel_title || "Uknown Channel name"); 292 } 293 294 // Update comment count 295 let commentCountElement = newArticle.find(".dsf_ytpl_comments_count .comments-count"); 296 if (commentCountElement.length) { 297 commentCountElement.text(post.comments || "0"); 298 } 299 300 // Update view count 301 let viewsCountElement = newArticle.find(".dsf_ytpl_views_count .views-count"); 302 if (viewsCountElement.length) { 303 viewsCountElement.text(post.views || "0"); 304 } 305 306 // Update publish date 307 let postDate = newArticle.find(".dsf_ytpl_publish_date"); 308 if (postDate.length) { 309 postDate.text(post.published_at || "Unknown Date"); 310 } 311 312 // Update video caption (trimmed to specified length) 313 314 let captionElement = newArticle.find(".dsf_ytpl_post_c
314aption"); 315 if (captionElement.length) { 316 captionElement.empty(); // clear old caption 317 318 let fullCaption = post.description || ""; 319 let trimmedCaption = fullCaption.length > captionLength 320 ? fullCaption.substring(0, captionLength) + "â¦" 321 : fullCaption; 322 323 let formattedCaption = trimmedCaption.replace(/\n/g, "<br>"); 324 captionElement.html(formattedCaption); 325 } 326 327 // Append new article to feed 328 feedContainer.append(newArticle); 329 330 // Trigger Isotope/Masonry layout update only if it's masonry layout 331 if (feedContainer.hasClass("dsf_ytpl_grid_masonry")) { 332 feedContainer.imagesLoaded(function () { 333 feedContainer.masonry("appended", newArticle); 334 }); 335 } 336 }); 337 338 } else { 339 console.warn(`No existing article found to clone for ${blockId}.`); 340 } 341 342 // Hide button if no more data available 343 if (!response.data.next_cursor) { 344 console.log(`No more posts for ${blockId}.`); 345 button.hide(); 346 } else { 347 button.text(originalText); 348 } 349 } else { 350 console.warn(`No data received for ${blockId}.`); 351 button.text(originalText); 352 button.hide(); 353 } 354 }, 355 error: function (error) { 356 console.error(`Error loading posts for ${blockId}:`, error); 357 button.text(originalText); 358 }, 359 complete: function () { 360 button.prop("disabled", false); 361 }, 362 }); 363 }); 364 }); 365 366 /** 367 * Click handler for playlist items - opens video in new tab when popup is disabled 368 */ 369 $(document).on("click", ".dsf_ytpl_feed_inner article[data-link]", function () { 370 if ($(this).attr("data-popup") === "off") { 371 var url = $(this).attr("data-link"); 372 if (url) { 373 window.open(url, "_blank"); 374 } 375 } 376 }); 377});
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.