/** * AKSRT AJAX Pagination & Lazy Load */ (function () { "use strict"; function hasAjaxConfig() { return !!(window.aksrtData && aksrtData.ajaxUrl && aksrtData.nonce); } function initAjaxPagination(root) { var containers = (root || document).querySelectorAll("[data-aksrt-pagination], .aksrt-comment-pagination"); if (!containers.length) return; var triggerText = (window.aksrtData && aksrtData.ajaxTrigger) || "加载更多"; var nomoreText = (window.aksrtData && aksrtData.ajaxNomore) || "没有更多了"; var errorText = (window.aksrtData && aksrtData.ajaxLoadError) || "加载失败,请重试"; containers.forEach(function (container) { if (container.dataset.aksrtPaginationBound === "1") return; container.dataset.aksrtPaginationBound = "1"; var mode = container.dataset.mode || "load_more"; var nextPage = parseInt(container.dataset.nextPage, 10) || 2; var maxPages = parseInt(container.dataset.maxPages, 10) || 1; var isComment = container.classList.contains("aksrt-comment-pagination"); var postId = container.dataset.postId || ""; var commentOrder = container.dataset.commentOrder || "newest"; var loading = false; var failed = false; var clickTarget = container.querySelector(".next-page.ajax-next, .ias-btn") || container; var loadMoreBtn = container.querySelector("[data-aksrt-load-more]") || clickTarget; var successfulAutoLoads = 0; var iasMax = isComment ? ((window.aksrtData && typeof aksrtData.commentIasMax !== "undefined") ? parseInt(aksrtData.commentIasMax, 10) : 0) : ((window.aksrtData && typeof aksrtData.iasMax !== "undefined") ? parseInt(aksrtData.iasMax, 10) : 0); // legacy 契约:分页容器与列表同处一个 .ajaxpager 内(首页 tab pane、归档流), // 此时追加目标就是列表容器本身,新卡片插到分页控件之前。 var listContainer = container.closest(".ajaxpager, .posts-row"); var target = container.previousElementSibling; if (listContainer && container.parentNode === listContainer) { target = listContainer; } if (isComment) { var commentList = container.closest("#comment-list, .commentlist, ol.commentlist") || document.getElementById("comment-list"); if (commentList) target = commentList; } if (!target && container.parentElement) { target = container.parentElement.querySelector(".post-grid, .comment-list, .commentlist, .content-stream__list"); } // 文章骨架:与 runtime/main.js 的 .ajax-next 处理器、aksrt_render_placeholder() // 同源同款(1:1 对齐参考主题 zib_get_post_placeholder)。刻意不写 ajax-item: // 否则会被 inject() / find('.ajax-item') 当成真实条目删除或定位。 var CARD_ITEM = '
' + '
' + '

' + '

' + '
'; var LIST_ITEM = '
' + '
' + '

' + '

' + '
'; // 视口阈值与参考主题 .ajax-next 处理器一致:宽 + 无侧栏 → 8,宽 + 有侧栏 → 6, // 窄屏 → 4;list 模式宽屏 4、窄屏 3。 function skeletonCount(style) { var wide = window.innerWidth > 766; if (style === "card") { if (!wide) return 4; return window.innerWidth > 992 && !document.querySelector(".sidebar") ? 8 : 6; } return wide ? 4 : 3; } // 从列表首张真实卡片判断形状;无卡片时退化为 list。 function postSkeleton() { var first = target && target.querySelector ? target.querySelector(".posts-item.card, .posts-item.list") : null; var isCard = !!first && /(^|\s)card(\s|$)/.test(first.className || ""); var tpl = isCard ? CARD_ITEM : LIST_ITEM; var out = ""; for (var i = 0, n = skeletonCount(isCard ? "card" : "list"); i < n; i++) { out += tpl; } return '
' + out + '
'; } // 兜底(非文章列表面,例如评论区没有 .posts-item 时):转圈 + 文案, // 绝不用按钮自身文字来表达加载态。 function fallbackLoader() { var text = (window.aksrtData && aksrtData.ajaxLoading) || "加载中..."; return ( '
' + '' + text + '
' ); } function showSkeleton() { if (!target) return; var loader = target.querySelector(".post_ajax_loader"); if (!loader) { loader = document.createElement("div"); loader.className = "post_ajax_loader"; loader.innerHTML = postSkeleton() || fallbackLoader(); } // 参考主题 beforeSend:骨架每次翻页都挪到列表末尾(分页控件之前), // 紧贴最后一张卡片、位于「加载更多」按钮上方。 target.insertBefore( loader, container.parentNode === target ? container : null ); loader.classList.add("is-loading"); } function hideSkeleton() { if (!target) return; var loader = target.querySelector(".post_ajax_loader"); if (loader) { loader.classList.remove("is-loading"); } } function stopInfinite(markNomore) { container.classList.remove("ias-pending"); if (markNomore && loadMoreBtn) { loadMoreBtn.innerHTML = nomoreText; loadMoreBtn.classList.add("ajax-nomore"); var nextWrap = loadMoreBtn.closest(".next-page"); if (nextWrap) nextWrap.classList.add("no-more"); loadMoreBtn.style.pointerEvents = "none"; } else if (loadMoreBtn && !loadMoreBtn.classList.contains("ajax-nomore")) { if (failed) { loadMoreBtn.textContent = errorText; } else { loadMoreBtn.innerHTML = triggerText; } loadMoreBtn.style.pointerEvents = ""; } } function markFailed(message) { failed = true; stopInfinite(false); if (loadMoreBtn) { loadMoreBtn.textContent = message || errorText; loadMoreBtn.classList.remove("ajax-nomore"); loadMoreBtn.style.pointerEvents = ""; } } function loadNextPage(fromAuto) { if (loading || failed || nextPage > maxPages || !target) return Promise.resolve(false); if (!hasAjaxConfig()) { markFailed(errorText); return Promise.resolve(false); } loading = true; // 加载态的呈现完全交给列表末尾的骨架(参考主题 post_ajax beforeSend // 只操作 .post_ajax_loader,不触碰「加载更多」按钮自身的文字)。 showSkeleton(); var action = isComment ? "aksrt_load_more_comments" : "aksrt_load_more_posts"; var queryContext = container.dataset.queryContext || ""; var queryVars = container.dataset.queryVars || ""; var renderConfig = container.dataset.renderConfig || ""; var body = new URLSearchParams(); body.set("action", action); body.set("nonce", aksrtData.nonce); body.set("page", String(nextPage)); if (queryContext) { body.set("query_context", queryContext); } if (!isComment && queryVars) { body.set("query_vars", queryVars); } if (!isComment && renderConfig) { body.set("render_config", renderConfig); } if (isComment && postId) { body.set("post_id", postId); body.set("comment_order", commentOrder); var authorOnly = container.dataset.authorOnly || "0"; if (authorOnly === "1") body.set("author_only", "1"); } return fetch(aksrtData.ajaxUrl, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" }, credentials: "same-origin", body: body.toString(), }) .then(function (r) { if (!r.ok) { throw new Error("HTTP " + r.status); } return r.text().then(function (text) { try { return JSON.parse(text); } catch (err) { throw new Error("Invalid JSON"); } }); }) .then(function (res) { if (!(res && res.success && res.data && typeof res.data.html === "string")) { var msg = res && res.data && res.data.message ? res.data.message : errorText; markFailed(msg); return false; } var temp = document.createElement("div"); temp.innerHTML = res.data.html; var newItems = temp.children; while (newItems.length > 0) { if (container.parentNode === target) { target.insertBefore(newItems[0], container); } else { target.appendChild(newItems[0]); } } // legacy 事件顺序:先 post_ajax.ed,再全局 auto_fun 重扫 target.dispatchEvent(new CustomEvent("post_ajax.ed", { bubbles: true })); if (typeof window.auto_fun === "function") window.auto_fun(); nextPage++; container.dataset.nextPage = String(nextPage); if (fromAuto) { successfulAutoLoads++; } if (nextPage > maxPages || !res.data.has_more) { stopInfinite(true); if (!loadMoreBtn) { container.innerHTML = '' + nomoreText + ''; } } return true; }) .catch(function () { markFailed(errorText); return false; }) .finally(function () { loading = false; hideSkeleton(); if (loadMoreBtn && !loadMoreBtn.classList.contains("ajax-nomore") && !failed) { loadMoreBtn.innerHTML = triggerText; if (mode === "infinite") { var lazyTrigger = clickTarget.closest(".next-page") || clickTarget; if (iasMax > 0 && successfulAutoLoads >= iasMax) { container.classList.remove("ias-pending"); lazyTrigger.classList.remove("lazyload", "lazyloaded", "lazyloading"); lazyTrigger.removeAttribute("lazyload-action"); } else { lazyTrigger.classList.remove("lazyloaded", "lazyloading"); lazyTrigger.classList.add("lazyload"); if (window.lazySizes && lazySizes.loader) { lazySizes.loader.checkElems(); } } } } }); } if (loadMoreBtn) { clickTarget.addEventListener("click", function(e) { // 排序菜单(.ajax-option)与 tab pane 触发器(.post_ajax_trigger)归 legacy // runtime post_ajax() 管线;本引擎避开,否则一次点击发两个请求。 if (e.target.closest && e.target.closest(".ajax-option, .post_ajax_trigger")) { return; } e.preventDefault(); if (loadMoreBtn.dataset.jumpComments === "1") { var jumpInput = container.querySelector(".jump-input"); if (!jumpInput) return; var targetPage = parseInt(jumpInput.value, 10) || 1; var max = parseInt(jumpInput.dataset.maxPages || String(maxPages), 10) || maxPages; targetPage = Math.max(1, Math.min(max, targetPage)); // 评论跳页:本地 DOM 刷新,地址栏不携带 ? 参数。 if (window.AksrtComments && typeof window.AksrtComments.loadPage === "function") { e.preventDefault(); window.AksrtComments.loadPage(targetPage); return; } window.location.hash = "comments"; window.location.reload(); return; } if (loadMoreBtn.dataset.jumpArchive === "1") { var archiveJumpInput = container.querySelector(".jump-input"); if (!archiveJumpInput) return; var archiveTargetPage = parseInt(archiveJumpInput.value, 10) || 1; var archiveMax = parseInt(archiveJumpInput.dataset.maxPages || String(maxPages), 10) || maxPages; archiveTargetPage = Math.max(1, Math.min(archiveMax, archiveTargetPage)); var archiveUrl = new URL(window.location.href); archiveUrl.searchParams.set(loadMoreBtn.dataset.pageVar || "paged", String(archiveTargetPage)); window.location.href = archiveUrl.toString(); return; } var fromAuto = clickTarget.dataset.lazyAuto === "1"; delete clickTarget.dataset.lazyAuto; if (fromAuto && iasMax > 0 && successfulAutoLoads >= iasMax) { container.classList.remove("ias-pending"); return; } // Manual click can recover from a previous auto-load failure. failed = false; loadNextPage(fromAuto); }); } // The runtime lazySizes dispatches lazybeforeunveil natively but follows up // with a jQuery-simulated click, which native listeners never receive. // Route the unveil trigger through the same click pipeline. if (container.classList.contains("lazyload") || clickTarget.classList.contains("lazyload")) { container.addEventListener("lazybeforeunveil", function (e) { if (e.target !== container && e.target !== clickTarget) return; clickTarget.dataset.lazyAuto = "1"; clickTarget.click(); }); } if (mode === "infinite") { if ((isComment && window.aksrtData && aksrtData.commentIasAuto === false) || !hasAjaxConfig()) { var autoTrigger = clickTarget.closest(".next-page") || clickTarget; autoTrigger.classList.remove("lazyload"); autoTrigger.removeAttribute("lazyload-action"); return; } container.classList.add("ias-pending"); } }); } function init(root) { initAjaxPagination(root); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", function () { init(); }); } else { init(); } // AJAX 注入新内容后由 auto_fun 重扫(container 级 data 标记保证幂等)。 if (window.jQuery) { window.jQuery(document).on("auto_fun", function () { init(); }); } })();