1/** 2 * Pinterest Feed Load More Functionality 3 * 4 * Handles dynamic loading of additional Pinterest 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_pn_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 let boardId = button.data("board-id"); 39 40 // AJAX request to load more posts 41 $.ajax({
42 url: dsf_pn_ajax.ajax_url, 43 type: "POST", 44 data: { 45 action: "dsf_pn_load_more", 46 nonce: dsf_pn_ajax.nonce, 47 block_id: blockId, 48 perpage: perPage, 49 next_cursor: nextCursor, 50 board_id: boardId || "", 51 }, 52 53 /** 54 * Before sending the request 55 */ 56 beforeSend: function () { 57 console.log(`Loading more posts for ${blockId}...`); 58 button.prop("disabled", true).text("Loading..."); 59 }, 60 61 /** 62 * Successful response handler 63 * @param {object} response AJAX response containing new posts 64 */ 65 success: function (response) { 66 if (response.success && response.data.media.length > 0) { 67 // Initialize media array if it doesn't exist 68 if (!window[blockId]) { 69 window[blockId] = { media: [] }; 70 } 71 72 // Append new media to existing array 73 window[blockId].media = window[blockId].media.concat(response.data.media); 74 75 // Update the next cursor for pagination 76 feedContainer.data("nextCursor", response.data.next_cursor); 77 78 // Get the first article as template for cloning 79 let existingArticle = feedContainer.find(".dsf_pn_feed_item").first(); 80 81 if (existingArticle.length) { 82 // Process each new post 83 $.each(response.data.media, function (index, post) { 84 let newArticle = existingArticle.clone(); 85 86 // Important: remove inline styles from the clone 87 newArticle.removeAttr("style"); 88 89 // Set custom attributes 90 newArticle.attr("data-id", post.id); 91 newArticle.attr("data-link", post.pin_url); 92 newArticle.attr("data-object", blockId); 93 94 // Update media source using fallback resolutions 95 let img = newArticle.find("img.Pinterest-feed__media"); 96 if (img.length) { 97 let media_url = ''; 98 99 if (post.media?.images?.['1200x']?.url) { 100 media_url = post.media.images['1200x'].url; 101 } else if (post.media?.images?.['600x']?.url) { 102 media_url = post.media.images['600x'].url; 103 } else if (post.media?.images?.['400x300']?.url) { 104 media_url = post.media.images['400x300'].url; 105 } else if (post.media?.images?.['150x150']?.url) { 106 media_url = post.media.images['150x150'].url; 107 } 108 109 if (media_url) { 110 img.attr("src", media_url); 111 } 112 } 113 114 115 // Update Post Date 116 let postDateDisplay = newArticle.find(".dsf_pn_post_date"); 117 if (postDateDisplay.length) { 118 postDateDisplay.text(post.created_at || ""); 119 } 120 121 // Update Post Title 122 let posTitleDisplay = newArticle.find(".dsf_pn_post_title"); 123 if (posTitleDisplay.length) { 124 posTitleDisplay.text(post.title || ""); 125 } 126 127 // Update Post Caption with truncation and line breaks 128 129 let captionElement = newArticle.find(".dsf_pn_post_caption"); 130 if (captionElement.length) { 131 captionElement.empty(); // clear old caption 132 133 let fullCaption = post.description || ""; 134 let trimmedCaption = fullCaption.length > captionLength 135 ? fullCaption.substring(0, captionLength) + "â¦" 136 : fullCaption; 137 138 let formattedCaption = trimmedCaption.replace(/\n/g, "<br>"); 139 captionElement.html(formattedCaption); 140 } 141 142 // Append the new post to the container 143 feedContainer.append(newArticle); 144 145 // Trigger Isotope/Masonry layout update only if it's masonry layout 146 if (feedContainer.hasClass("dsf_pn_grid_masonry")) { 147 feedContainer.imagesLoaded(function () { 148 feedContainer.masonry("appended", newArticle); 149 }); 150 } 151 152 }); 153 154 } else { 155 console.warn(`No existing article found to clone for ${blockId}.`); 156 } 157 158 // Hide the button if no more data is available 159 if (!response.data.next_cursor) { 160 console.log(`No more posts for ${blockId}.`); 161 button.hide(); 162 } else { 163 button.text(originalText); 164 } 165 } else { 166 console.warn(`No data received for ${blockId}.`); 167 button.text(originalText); 168 button.hide(); 169 } 170 }, 171 172 /** 173 * Error handler 174 * @param {object} error Error object from AJAX request 175 */ 176 error: function (error) { 177 console.error(`Error loading posts for ${blockId}:`, error); 178 button.text(originalText); 179 }, 180 181 /** 182 * Always execute after request completes 183 */ 184 complete: function () { 185 button.prop("disabled", false); 186 }, 187 }); 188 }); 189 });
190 191 /** 192 * Handle click events on Pinterest posts 193 * 194 * Opens post in new tab when clicked (if popup is disabled) 195 */ 196 $(document).on("click", ".dsf_pn_feed_inner article[data-link]", function () { 197 if ($(this).attr("data-popup") === "off") { 198 var url = $(this).attr("data-link"); 199 if (url) { 200 window.open(url, "_blank"); // Open in a new tab 201 } 202 } 203 }); 204});
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.