/** * Product archive — cat filter + sort + AJAX load more (zibll ajax-next UX). * * Uses REST /shop/frontend/products/archive and optional AksrtAjaxList events. * * @package AKSRT */ (function () { "use strict"; var root = document.querySelector("[data-aksrt-product-archive]"); if (!root) return; function restBase() { var site = window.aksrtData && aksrtData.siteUrl ? String(aksrtData.siteUrl).replace(/\/$/, "") : ""; return site + "/wp-json/aksrt/v1"; } function toast(msg) { if (typeof window.aksrtToast === "function") { window.aksrtToast(msg); return; } if (window.console) console.log(msg); } var grid = root.querySelector("[data-aksrt-product-grid], #aksrt-product-grid"); var pager = root.querySelector("[data-aksrt-ajaxpager], .ajaxpager"); var countEl = root.querySelector("[data-aksrt-archive-count]"); var headerHost = root.querySelector("[data-aksrt-ajax-header]"); var loadMoreBtn = root.querySelector("[data-aksrt-shop-load-more]"); var loader = root.querySelector("[data-aksrt-ajax-loader], .post_ajax_loader"); var state = { taxonomy: root.getAttribute("data-taxonomy") || "", termId: Number(root.getAttribute("data-term-id") || 0), orderby: root.getAttribute("data-orderby") || "date", order: (root.getAttribute("data-order") || "DESC").toUpperCase(), page: Number(root.getAttribute("data-page") || 1), maxPages: Number(root.getAttribute("data-max-pages") || 1), paging: root.getAttribute("data-paging") || "number", ias: root.getAttribute("data-ias") === "1", iasMax: Number(root.getAttribute("data-ias-max") || 5), loading: false, autoLoads: 0, }; function showLoader(on) { if (loader) { loader.hidden = !on; loader.style.display = on ? "" : "none"; loader.classList.toggle("is-loading", !!on); } if (pager) pager.classList.toggle("is-ajax-loading", !!on); } function updateLoadMore(hasMore) { var pag = root.querySelector(".ajax-pag"); if (loadMoreBtn) { loadMoreBtn.style.display = hasMore ? "" : "none"; loadMoreBtn.disabled = false; loadMoreBtn.textContent = hasMore ? "加载更多" : "没有更多了"; if (!hasMore) loadMoreBtn.classList.add("ajax-nomore"); else loadMoreBtn.classList.remove("ajax-nomore"); } if (pag) pag.style.display = hasMore ? "" : "none"; } function syncOrderbyActive() { root.querySelectorAll("[data-aksrt-shop-orderby]").forEach(function (a) { var key = a.getAttribute("data-aksrt-shop-orderby") || ""; var active = key === state.orderby; a.classList.toggle("is-active", active); a.classList.toggle("focus-color", active && key === "price"); }); } function fetchArchive(page, orderby, order, replace, termId) { if (state.loading) return Promise.resolve(); state.loading = true; showLoader(true); if (loadMoreBtn) { loadMoreBtn.disabled = true; loadMoreBtn.textContent = "加载中…"; } var qs = new URLSearchParams(); qs.set("taxonomy", state.taxonomy); qs.set("term_id", String(typeof termId === "number" ? termId : state.termId)); qs.set("page", String(page)); qs.set("orderby", orderby || state.orderby); if ((orderby || state.orderby) === "price") { qs.set("order", order || state.order || "ASC"); } var container = pager || grid || root; try { container.dispatchEvent( new CustomEvent("post_ajax.before", { bubbles: true, detail: { page: page, orderby: orderby || state.orderby, replace: !!replace }, }) ); } catch (e0) {} return fetch(restBase() + "/shop/frontend/products/archive?" + qs.toString(), { method: "GET", credentials: "same-origin", headers: { Accept: "application/json", "X-WP-Nonce": (window.aksrtData && aksrtData.restNonce) || "", "X-AKSRT-Ajax": "list", }, }) .then(function (r) { return r.json(); }) .then(function (result) { state.loading = false; showLoader(false); if (!result || !result.success || !result.data) { toast((result && result.message) || "加载失败"); updateLoadMore(state.page < state.maxPages); return; } var data = result.data; state.page = Number(data.page || page); state.maxPages = Number(data.max_pages || 1); state.orderby = data.orderby || state.orderby; state.order = String(data.order || state.order || "DESC").toUpperCase(); if (typeof termId === "number" && termId > 0) { state.termId = termId; } if (data.term && data.term.id) { state.termId = Number(data.term.id); } root.setAttribute("data-page", String(state.page)); root.setAttribute("data-max-pages", String(state.maxPages)); root.setAttribute("data-orderby", state.orderby); root.setAttribute("data-order", state.order); root.setAttribute("data-term-id", String(state.termId)); if (countEl && typeof data.total !== "undefined") { countEl.textContent = "共 " + data.total + " 件商品"; } var titleEl = root.querySelector("[data-aksrt-archive-title]"); if (titleEl && data.term && data.term.name) { titleEl.textContent = data.term.name; } var descEl = root.querySelector("[data-aksrt-archive-desc]"); if (descEl && data.term) { descEl.textContent = data.term.description || ""; descEl.style.display = data.term.description ? "" : "none"; } if (headerHost && data.header_html) { // Keep title/count; replace filter widget if present. var existingFilter = headerHost.querySelector("[data-aksrt-shop-cat-filter], .shop-cat-filter"); var tmp = document.createElement("div"); tmp.innerHTML = data.header_html; var nextFilter = tmp.querySelector("[data-aksrt-shop-cat-filter], .shop-cat-filter") || tmp.firstElementChild; if (existingFilter && nextFilter) { existingFilter.replaceWith(nextFilter); } else if (!existingFilter && nextFilter) { headerHost.appendChild(nextFilter); } } var html = data.html || data.empty_html || ""; if (grid) { if (replace) { grid.innerHTML = html; } else if (html) { grid.insertAdjacentHTML("beforeend", html); } } if (data.paginate_html) { var oldPag = root.querySelector(".ajax-pag"); var wrap = document.createElement("div"); wrap.innerHTML = data.paginate_html; var nextPag = wrap.firstElementChild; if (oldPag && nextPag) oldPag.replaceWith(nextPag); else if (!oldPag && nextPag && pager) pager.appendChild(nextPag); loadMoreBtn = root.querySelector("[data-aksrt-shop-load-more]"); } syncOrderbyActive(); updateLoadMore(!!data.has_more || state.page < state.maxPages); try { container.dispatchEvent( new CustomEvent("post_ajax.ed", { bubbles: true, detail: { page: state.page, replace: !!replace, data: data }, }) ); document.dispatchEvent( new CustomEvent("aksrt:posts:rendered", { bubbles: true, detail: { container: grid, replace: !!replace }, }) ); } catch (e1) {} }) .catch(function () { state.loading = false; showLoader(false); updateLoadMore(state.page < state.maxPages); toast("网络错误"); }); } function pushUrl(orderby, order, termUrl) { try { var u = termUrl ? new URL(termUrl, window.location.origin) : new URL(window.location.href); if (orderby) u.searchParams.set("orderby", orderby); else u.searchParams.delete("orderby"); u.searchParams.delete("shop_orderby"); if (orderby === "price" && order) u.searchParams.set("order", order); else u.searchParams.delete("order"); u.searchParams.delete("paged"); window.history.replaceState({}, "", u.toString()); } catch (err) {} } root.addEventListener("click", function (e) { // Category / sort links with ajax-next — intercept for REST (richer payload). var termLink = e.target.closest("[data-aksrt-shop-term].ajax-next, a.ajax-next[data-aksrt-shop-term]"); if (termLink && root.contains(termLink)) { e.preventDefault(); e.stopPropagation(); var tid = Number(termLink.getAttribute("data-aksrt-shop-term") || 0); var href = termLink.getAttribute("href") || ""; state.autoLoads = 0; if (tid > 0) state.termId = tid; pushUrl(state.orderby, state.order, href); var rt = termLink.getAttribute("route-title"); if (rt) { try { document.title = rt; } catch (e2) {} } fetchArchive(1, state.orderby, state.order, true, tid); return; } var sort = e.target.closest("[data-aksrt-shop-orderby]"); if (sort && root.contains(sort) && sort.tagName === "A") { e.preventDefault(); e.stopPropagation(); var orderby = sort.getAttribute("data-aksrt-shop-orderby") || "date"; var nextOrder = sort.getAttribute("data-aksrt-shop-order") || ""; if (orderby === "price") { if (state.orderby === "price" && nextOrder) { state.order = nextOrder.toUpperCase(); } else if (state.orderby !== "price") { state.order = "ASC"; } else { state.order = state.order === "ASC" ? "DESC" : "ASC"; } } if (orderby === state.orderby && orderby !== "price" && state.paging === "ajax") return; state.orderby = orderby; state.autoLoads = 0; pushUrl(orderby, state.order); fetchArchive(1, orderby, state.order, true); return; } var more = e.target.closest("[data-aksrt-shop-load-more]"); if (more && root.contains(more)) { e.preventDefault(); e.stopPropagation(); if (state.page >= state.maxPages) return; fetchArchive(state.page + 1, state.orderby, state.order, false); } }); if (state.paging === "ajax" && state.ias && "IntersectionObserver" in window && loadMoreBtn) { var io = new IntersectionObserver( function (entries) { entries.forEach(function (entry) { if (!entry.isIntersecting) return; if (state.loading || state.page >= state.maxPages) return; if (state.iasMax > 0 && state.autoLoads >= state.iasMax) return; state.autoLoads += 1; fetchArchive(state.page + 1, state.orderby, state.order, false); }); }, { rootMargin: "240px" } ); io.observe(loadMoreBtn); } })();