1/** 2 * Tiktok Feed Load More Functionality 3 * 4 * Handles dynamic loading of additional Tiktok posts with: 5 * - AJAX loading of additional posts 6 * - Masonry layout support 7 * - Clone-based template system 8 * - Individual module state management 9 * - Comprehensive error handling 10 * 11 * @since 1.0.0 12 * @package DiviSocialFeed 13 */ 14 15jQuery(document).ready(function ($) { 16 // Initialize load more functionality for each instance 17 $(".dsf_tt_load_more").each(function () { 18 let button = $(this); 19 let blockId = button.data("object"); 20 let feedContainer = $("#" + blockId); 21 22 // Validate container existence 23 if (!feedContainer.length) { 24 console.warn(`Feed container with ID '${blockId}' not found.`); 25 return; 26 } 27 28 // Store nextCursor individually for each module 29 feedContainer.data("nextCursor", ""); 30 31 // Click handler for load more button 32 button.on("click", function () { 33 // Get configuration parameters 34 let perPage = button.data("per-page"); 35 let captionLength = button.data("caption-length"); 36 let originalText = button.text(); 37 let nextCursor = feedContainer.data("nextCursor"); // Get stored cursor 38 39 // AJAX request to load more posts 40 $.ajax({
41 url: dsf_tt_ajax.ajax_url, 42 type: "POST", 43 data: { 44 action: "dsf_tt_load_more", 45 nonce: dsf_tt_ajax.nonce, 46 block_id: blockId, 47 perpage: perPage, 48 next_cursor: nextCursor, 49 }, 50 51 /** 52 * Before sending the request 53 */ 54 beforeSend: function () { 55 console.log(`Loading more posts for ${blockId}...`); 56 button.prop("disabled", true).text("Loading..."); 57 }, 58 59 /** 60 * Successful response handler 61 * @param {object} response AJAX response containing new posts 62 */ 63 success: function (response) { 64 if (response.success && response.data.media.length > 0) { 65 // Initialize media array if it doesn't exist 66 if (!window[blockId]) { 67 window[blockId] = { media: [] }; 68 } 69 70 // Append new media to existing array 71 window[blockId].media = window[blockId].media.concat(response.data.media); 72 73 // Update the next cursor for pagination 74 feedContainer.data("nextCursor", response.data.next_cursor); 75 76 // Get the first article as template for cloning 77 let existingArticle = feedContainer.find(".dsf_tt_feed_item").first(); 78 79 if (existingArticle.length) { 80 // Process each new post 81 $.each(response.data.media, function (index, post) { 82 let newArticle = existingArticle.clone(); 83 84 // Important: remove inline styles from the clone 85 newArticle.removeAttr("style"); 86 87 // Set custom attributes 88 newArticle.attr("data-id", post.id); 89 newArticle.attr("data-link", post.share_url); 90 newArticle.attr("data-object", blockId); 91 92 // Update media source (handles both images and videos) 93 let img = newArticle.find("img.tiktok-feed__media"); 94 if (img.length) { 95 img.attr("src", post.cover_image_url || ""); 96 } 97 98 // Update like count display 99 let likeCountElement = newArticle.find(".dsf_tt_like_count .like-count"); 100 if (likeCountElement.length) { 101 likeCountElement.text(post.like_count || "0"); 102 } 103 104 // Update comment count display 105 let commentCountElement = newArticle.find(".dsf_tt_comment_count .comment-count"); 106 if (commentCountElement.length) { 107 commentCountElement.text(post.comment_count || "0"); 108 } 109 110 // Update Views count display 111 let viewsCountElement = newArticle.find(".dsf_tt_views_count .views-count"); 112 if (viewsCountElement.length) { 113 viewsCountElement.text(post.view_count || "0"); 114 } 115 116 // Update Post Date 117 let postDateDisplay = newArticle.find(".dsf_tt_post_date"); 118 if (postDateDisplay.length) { 119 postDateDisplay.text(post.create_time || "Unknown date"); 120 } 121 122 // Duration Update 123 124 let postDurationDisplay = newArticle.find(".dsf_tt_post_duration"); 125 if (postDurationDisplay.length) { 126 postDurationDisplay.text(post.duration || "0 sec"); 127 } 128 129 // Update View Post button link 130 let viewPostButton = newArticle.find(".dsf_fb_view_post_button"); 131 if (viewPostButton.length && post.permalink_url) { 132 viewPostButton.attr("href", post.permalink_url); 133 } else if (viewPostButton.length) { 134 viewPostButton.attr("href", "#"); 135 } 136 137 let captionElement = newArticle.find(".dsf_tt_post_c
137aption"); 138 if (captionElement.length) { 139 captionElement.empty(); // clear old caption 140 141 let fullCaption = post.video_description || ""; 142 let trimmedCaption = fullCaption.length > captionLength 143 ? fullCaption.substring(0, captionLength) + "â¦" 144 : fullCaption; 145 146 let formattedCaption = trimmedCaption.replace(/\n/g, "<br>"); 147 captionElement.html(formattedCaption); 148 } 149 150 151 // Append the new post to the container 152 feedContainer.append(newArticle); 153 154 // Trigger Isotope/Masonry layout update only if it's masonry layout 155 if (feedContainer.hasClass("dsf_tt_grid_masonry")) { 156 feedContainer.imagesLoaded(function () { 157 feedContainer.masonry("appended", newArticle); 158 }); 159 } 160 161 }); 162 163 } else { 164 console.warn(`No existing article found to clone for ${blockId}.`); 165 } 166 // Hide the button if no more data is available 167 if (!response.data.next_cursor) { 168 console.log(`No more posts for ${blockId}.`); 169 button.hide(); 170 } else { 171 button.text(originalText); 172 } 173 } else { 174 console.warn(`No data received for ${blockId}.`); 175 button.text(originalText); 176 button.hide(); 177 } 178 }, 179 180 /** 181 * Error handler 182 * @param {object} error Error object from AJAX request 183 */ 184 error: function (error) { 185 console.error(`Error loading posts for ${blockId}:`, error); 186 button.text(originalText); 187 }, 188 189 /** 190 * Always execute after request completes 191 */ 192 complete: function () { 193 button.prop("disabled", false); 194 }, 195 }); 196 }); 197 }); 198 199});
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.