1/** 2 * Instagram Feed Load More Functionality 3 * 4 * Handles dynamic loading of additional Instagram 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_ig_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_ig_ajax.ajax_url, 42 type: "POST", 43 data: { 44 action: "dsf_ig_load_more", 45 nonce: dsf_ig_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_ig_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.permalink); 90 newArticle.attr("data-object", blockId); 91 92 // Update media source (handles both images and videos) 93 let img = newArticle.find("img.instagram-feed__media"); 94 if (img.length) { 95 img.attr("src", post.media_type === "VIDEO" ? post.thumbnail_url : post.media_url); 96 } 97 98 // Update like count display 99 let likeCountElement = newArticle.find(".dsf_ig_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_ig_comment_count .comment-count"); 106 if (commentCountElement.length) { 107 commentCountElement.text(post.comments_count || "0"); 108 } 109 110 // Update Post Date 111 let postDateDisplay = newArticle.find(".dsf_ig_post_date"); 112 if (postDateDisplay.length) { 113 postDateDisplay.text(post.timestamp || "Unknown date"); 114 } 115 116 117 118 let captionElement = newArticle.find(".dsf_ig_post_caption"); 119 if (captionElement.length) { 120 captionElement.empty(); // clear old caption 121 122 let fullCaption = post.caption || ""; 123 let trimmedCaption = fullCaption.length > captionLength 124 ? fullCaption.substring(0, captionLength) + "â¦" 125 : fullCaption; 126 127 let formattedCaption = trimmedCaption.replace(/\n/g, "<br>"); 128 captionElement.html(formattedCaption); 129 } 130 131 132 // Append the new post to the container 133 feedContainer.append(newArticle); 134 135 // Trigger Isotope/Masonry layout update only if it's masonry layout 136 if (feedContainer.hasClass("dsf_ig_grid_masonry")) { 137 feedContainer.imagesLoaded(function () { 138 feedContainer.masonry("appended", newArticle); 139 }); 140 } 141 142 }); 143 144 } else { 145 console.warn(`No existing article found to clone for ${blockId}.`); 146 } 147 148 // Hide the button if no more data is available 149 if (!response.data.next_cursor) { 150 console.log(`No more posts for ${blockId}.`); 151 button.hide(); 152 } else { 153 button.text(originalText); 154 } 155 } else { 156 console.warn(`No data received for ${blockId}.`); 157 button.text(originalText); 158 button.hide(); 159 } 160 }, 161 162 /** 163 * Error handler 164 * @param {object} error Error object from AJAX request 165 */ 166 error: function (error) { 167 console.error(`Error loading posts for ${blockId}:`, error); 168 button.text(originalText); 169 }, 170 171 /** 172 * Always execute after request completes 173 */ 174 complete: function () { 175 button.prop("disabled", false); 176 }, 177 }); 178 }); 179 });
180 181 /** 182 * Handle click events on Instagram posts 183 * 184 * Opens post in new tab when clicked (if popup is disabled) 185 */ 186 $(document).on("click", ".dsf_ig_feed_inner article[data-link]", function () { 187 if ($(this).attr("data-popup") === "off") { 188 var url = $(this).attr("data-link"); 189 if (url) { 190 window.open(url, "_blank"); // Open in a new tab 191 } 192 } 193 }); 194});
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.