| 1 |
(function ($) { |
| 2 |
("use strict"); |
| 3 |
$.fn.UltpInfiniteScroll = async function ( |
| 4 |
block, // Current Block |
| 5 |
setSession, // set Unique Post Id |
| 6 |
getUltpNonce, // Get Nonce |
| 7 |
paginationAjaxCall, // Pagination API Call Function |
| 8 |
) { |
| 9 |
const ultpNonce = await getUltpNonce(); |
| 10 |
|
| 11 |
const wrap = block.find(".ultp-block-wrapper"); |
| 12 |
const exclude = wrap.data("expost"); |
| 13 |
const builder = wrap.data("builder"); |
| 14 |
const blockid = wrap.attr("data-blockid"); |
| 15 |
const blockName = wrap.data("blockname"); |
| 16 |
const filterType = wrap.attr("data-filter_type") || ""; |
| 17 |
|
| 18 |
let filterValue = []; |
| 19 |
|
| 20 |
// pageNum starts at 2 — page 1 is already rendered |
| 21 |
let pageNum = 2; |
| 22 |
let noMoreData = false; |
| 23 |
let isLoading = false; // <-- ADD THIS |
| 24 |
|
| 25 |
// Post Id |
| 26 |
let post_ID = |
| 27 |
block.parents(".ultp-shortcode").length !== 0 && |
| 28 |
wrap.data("selfpostid") === "no" |
| 29 |
? block.parents(".ultp-shortcode").data("postid") |
| 30 |
: wrap.data("postid"); |
| 31 |
|
| 32 |
if (block.find(".ultp-builder-content").length > 0) { |
| 33 |
post_ID = block.find(".ultp-builder-content").data("postid"); |
| 34 |
} |
| 35 |
|
| 36 |
// Widget Block ID |
| 37 |
let widgetBlockId = ""; |
| 38 |
const widgetBlock = block.parents(".widget_block:first"); |
| 39 |
if (widgetBlock.length > 0) { |
| 40 |
const widget_items = widgetBlock.attr("id").split("-"); |
| 41 |
widgetBlockId = widget_items[widget_items.length - 1]; |
| 42 |
} |
| 43 |
|
| 44 |
// Unique Post |
| 45 |
const ultpCurrentUniquePosts = JSON.stringify( |
| 46 |
wrap.find(".ultp-current-unique-posts").data("current-unique-posts"), |
| 47 |
); |
| 48 |
|
| 49 |
// Window Scroll |
| 50 |
let scrollTimer = null; |
| 51 |
function onScroll() { |
| 52 |
if (scrollTimer) return; |
| 53 |
scrollTimer = setTimeout(function () { |
| 54 |
scrollTimer = null; |
| 55 |
if (isEndReached() && !noMoreData) { |
| 56 |
loadContent(); |
| 57 |
} |
| 58 |
}, 150); |
| 59 |
} |
| 60 |
|
| 61 |
// Remove any previous handler for this block before adding a new one |
| 62 |
$(window).off("scroll.ultp-iscroll-" + blockid); |
| 63 |
$(window).on("scroll.ultp-iscroll-" + blockid, onScroll); |
| 64 |
|
| 65 |
// Initial check in case content is smaller than viewport height |
| 66 |
onScroll(); |
| 67 |
|
| 68 |
// Load Scrolled Content |
| 69 |
function loadContent() { |
| 70 |
if (isLoading) return; // <-- ADD THIS |
| 71 |
isLoading = true; // <-- ADD THIS |
| 72 |
// Build adv filter data from block-wrapper element |
| 73 |
filterValue = |
| 74 |
typeof wrap.attr("data-filter_value") === "string" |
| 75 |
? JSON.parse(wrap.attr("data-filter_value")) |
| 76 |
: (wrap.attr("data-filter_value") ?? []); |
| 77 |
|
| 78 |
const advFilterData = {}; |
| 79 |
|
| 80 |
if (Array.isArray(filterValue) && filterValue.length > 0) { |
| 81 |
advFilterData.isAdv = true; |
| 82 |
advFilterData.filterShow = true; |
| 83 |
advFilterData.checkFilter = true; |
| 84 |
advFilterData.order = wrap.attr("data-filter_order") || ""; |
| 85 |
advFilterData.orderby = wrap.attr("data-filter_orderby") || ""; |
| 86 |
advFilterData.adv_sort = wrap.attr("data-filter_queryQuick") || ""; |
| 87 |
const authorData = |
| 88 |
typeof wrap.attr("data-filter_author") === "string" |
| 89 |
? (JSON.parse(wrap.attr("data-filter_author")) ?? []) |
| 90 |
: (wrap.attr("data-filter_author") ?? []); |
| 91 |
if (Array.isArray(authorData) && authorData.length > 0) { |
| 92 |
advFilterData.author = authorData; |
| 93 |
} |
| 94 |
} |
| 95 |
|
| 96 |
// Forward active search term to paginated scroll requests |
| 97 |
const searchVal = wrap.attr("data-filter_search") || ""; |
| 98 |
if (searchVal) { |
| 99 |
advFilterData.search = searchVal; |
| 100 |
if (!advFilterData.isAdv) { |
| 101 |
advFilterData.isAdv = true; |
| 102 |
advFilterData.filterShow = true; |
| 103 |
advFilterData.checkFilter = true; |
| 104 |
} |
| 105 |
} |
| 106 |
|
| 107 |
// Get the LATEST unique posts for this specific block on each call |
| 108 |
const ultpCurrentUniquePosts = JSON.stringify( |
| 109 |
wrap.find(".ultp-current-unique-posts").data("current-unique-posts"), |
| 110 |
); |
| 111 |
|
| 112 |
console.log(pageNum, "pageNum"); |
| 113 |
|
| 114 |
paginationAjaxCall({ |
| 115 |
wrap, // .ultp-block-wrapper Selector |
| 116 |
pageNum, |
| 117 |
blockid, |
| 118 |
ultpNonce, |
| 119 |
blockName, |
| 120 |
post_ID, |
| 121 |
filterType, |
| 122 |
ultpCurrentUniquePosts, |
| 123 |
widgetBlockId, |
| 124 |
exclude, // exclude post ids |
| 125 |
builder, // builder type/data |
| 126 |
filterValue, |
| 127 |
advFilterData, |
| 128 |
onBeforeSend() { |
| 129 |
return; |
| 130 |
}, |
| 131 |
onSuccess(data) { |
| 132 |
isLoading = false; // <-- ADD THIS |
| 133 |
if (!data || data.trim() === "") { |
| 134 |
noMoreData = true; |
| 135 |
$(window).off("scroll.ultp-iscroll-" + blockid); |
| 136 |
return; |
| 137 |
} |
| 138 |
if (!data || data.trim() === "") { |
| 139 |
noMoreData = true; |
| 140 |
$(window).off("scroll.ultp-iscroll-" + blockid); |
| 141 |
return; |
| 142 |
} |
| 143 |
let incoming = $(data); |
| 144 |
const uniquePostsDataEl = incoming.filter( |
| 145 |
".ultp-current-unique-posts", |
| 146 |
); |
| 147 |
|
| 148 |
if (uniquePostsDataEl.length > 0) { |
| 149 |
// Update global session unique IDs from the server response |
| 150 |
const newGlobalUniqueIds = |
| 151 |
uniquePostsDataEl.data("ultp-unique-ids"); |
| 152 |
setSession("ultp_uniqueIds", JSON.stringify(newGlobalUniqueIds)); |
| 153 |
|
| 154 |
// Update the block's own list of current posts |
| 155 |
const mainUniquePostsEl = wrap |
| 156 |
.find(".ultp-current-unique-posts") |
| 157 |
.first(); |
| 158 |
const existingBlockIds = |
| 159 |
mainUniquePostsEl.data("current-unique-posts") || []; |
| 160 |
const newBlockIds = |
| 161 |
uniquePostsDataEl.data("current-unique-posts") || []; |
| 162 |
const combinedBlockIds = [...existingBlockIds, ...newBlockIds]; |
| 163 |
mainUniquePostsEl.attr( |
| 164 |
"data-current-unique-posts", |
| 165 |
JSON.stringify(combinedBlockIds), |
| 166 |
); |
| 167 |
|
| 168 |
// Remove the data carrier div from the incoming content |
| 169 |
incoming = incoming.not(uniquePostsDataEl); |
| 170 |
} |
| 171 |
|
| 172 |
const itemsWrap = wrap.find(".ultp-block-items-wrap"); |
| 173 |
itemsWrap.append(incoming); |
| 174 |
|
| 175 |
// Animate only the newly added items |
| 176 |
const newItems = itemsWrap.children().slice(-incoming.length); |
| 177 |
animateBlockItem(newItems); |
| 178 |
|
| 179 |
pageNum++; |
| 180 |
|
| 181 |
// Check again if more content is needed to fill the viewport |
| 182 |
onScroll(); |
| 183 |
}, |
| 184 |
}); |
| 185 |
} |
| 186 |
|
| 187 |
// Calculate if user has scrolled near the end of the block |
| 188 |
function isEndReached() { |
| 189 |
const gridBottom = block.offset().top + block.outerHeight(); |
| 190 |
const scrollBottom = $(window).scrollTop() + $(window).height(); |
| 191 |
return scrollBottom >= gridBottom - 100; |
| 192 |
} |
| 193 |
}; |
| 194 |
|
| 195 |
// Card Item Animation Config. |
| 196 |
function animateBlockItem(blockItems) { |
| 197 |
if (blockItems.length > 0) { |
| 198 |
blockItems.addClass("ultp-infscroll-animate-in"); |
| 199 |
if (!window.IntersectionObserver) { |
| 200 |
blockItems.each(function (i) { |
| 201 |
const block = $(this); |
| 202 |
setTimeout(function () { |
| 203 |
block.addClass("ultp-infscroll-visible"); |
| 204 |
}, i * 80); |
| 205 |
}); |
| 206 |
return; |
| 207 |
} |
| 208 |
|
| 209 |
// Fire the animation only when each card actually enters the viewport |
| 210 |
const observer = new IntersectionObserver( |
| 211 |
function (entries) { |
| 212 |
const visible = entries.filter(function (e) { |
| 213 |
return e.isIntersecting; |
| 214 |
}); |
| 215 |
visible.forEach(function (entry, i) { |
| 216 |
setTimeout(function () { |
| 217 |
$(entry.target).addClass("ultp-infscroll-visible"); |
| 218 |
}, i * 70); // stagger within each visible batch |
| 219 |
observer.unobserve(entry.target); |
| 220 |
}); |
| 221 |
}, |
| 222 |
{ threshold: 0.08, rootMargin: "0px 0px -20px 0px" }, |
| 223 |
); |
| 224 |
|
| 225 |
blockItems.each(function () { |
| 226 |
observer.observe(this); |
| 227 |
}); |
| 228 |
} |
| 229 |
} |
| 230 |
})(jQuery); |
| 231 |
|