feat(modal): add cancellable shared runtime

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 23:24:37 +08:00
co-authored by Claude Fable 5
parent 0647c501ba
commit d79b063bf9
12 changed files with 999 additions and 392 deletions
+43
View File
@@ -83,6 +83,49 @@ body.aksrt-remote-modal-open {
font-size: 14px;
}
.aksrt-remote-modal__skeleton {
display: grid;
gap: 12px;
padding: 8px 2px;
}
.aksrt-remote-modal__skeleton-line,
.aksrt-remote-modal__skeleton-block {
display: block;
border-radius: 8px;
background: linear-gradient(90deg, rgba(148, 163, 184, 0.16), rgba(148, 163, 184, 0.3), rgba(148, 163, 184, 0.16));
background-size: 200% 100%;
animation: aksrt-modal-skeleton 1.2s ease-in-out infinite;
}
.aksrt-remote-modal__skeleton-line {
width: 72%;
height: 12px;
}
.aksrt-remote-modal__skeleton-line.is-wide {
width: 92%;
height: 18px;
}
.aksrt-remote-modal__skeleton-line.is-short {
width: 44%;
}
.aksrt-remote-modal__skeleton-block {
width: 100%;
min-height: 84px;
}
.aksrt-remote-modal__retry {
cursor: pointer;
}
@keyframes aksrt-modal-skeleton {
0% { background-position: 100% 0; }
100% { background-position: -100% 0; }
}
.aksrt-remote-modal .touch.border-title,
.aksrt-remote-modal .border-title {
font-size: 16px;
+17
View File
@@ -30,6 +30,23 @@
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.22);
}
/* VIP now uses the shared modal runtime host. */
.aksrt-remote-modal.aksrt-vip-modal-shell {
z-index: 1200;
}
.aksrt-remote-modal.aksrt-vip-modal-shell .aksrt-remote-modal__dialog {
width: min(560px, 100%);
max-height: min(90vh, 760px);
background: var(--main-bg-color, #fff);
border-radius: 12px;
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.22);
}
.aksrt-remote-modal.aksrt-vip-modal-shell .aksrt-remote-modal__content {
padding: 0;
}
.payvip-modal {
position: relative;
padding: 18px 16px 16px;
+150
View File
@@ -0,0 +1,150 @@
/**
* User-scoped in-memory modal cache with short TTLs and request deduplication.
* Private modal payloads are never persisted to browser storage.
*
* @package AKSRT
*/
(function (window, document) {
"use strict";
var DEFAULT_TTL = 15000;
var entries = Object.create(null);
var flights = Object.create(null);
function scope() {
var config = window.aksrtData || window.aksrt || {};
var userId = parseInt(config.userId || 0, 10) || 0;
return (config.isLoggedIn && userId > 0 ? "user:" + userId : "guest") + ":v1";
}
function token(key) {
return scope() + "::" + String(key || "");
}
function read(key) {
var id = token(key);
var entry = entries[id];
if (!entry) return { hit: false, value: undefined };
if (entry.expiresAt <= Date.now()) {
delete entries[id];
return { hit: false, value: undefined };
}
return { hit: true, value: entry.value };
}
function tagsOf(value) {
return Array.isArray(value) ? value.slice() : (value ? [value] : []);
}
function set(key, value, options) {
options = options || {};
var ttl = Number(options.ttl || DEFAULT_TTL);
entries[token(key)] = {
value: value,
expiresAt: Date.now() + Math.max(0, ttl),
tags: tagsOf(options.tags),
};
return value;
}
function matchesTags(entry, wanted) {
if (!wanted.length) return true;
return wanted.some(function (tag) { return entry.tags.indexOf(tag) >= 0; });
}
function invalidate(tags) {
var wanted = Array.isArray(tags) ? tags : (tags ? [tags] : []);
var removed = 0;
Object.keys(entries).forEach(function (id) {
if (!wanted.length || matchesTags(entries[id], wanted)) {
delete entries[id];
removed += 1;
}
});
Object.keys(flights).forEach(function (id) {
var flight = flights[id];
if (!wanted.length || matchesTags(flight, wanted)) {
flight.cancelled = true;
delete flights[id];
if (typeof flight.cancel === "function") flight.cancel();
}
});
return removed;
}
function clear() {
entries = Object.create(null);
Object.keys(flights).forEach(function (id) {
var flight = flights[id];
flight.cancelled = true;
if (typeof flight.cancel === "function") flight.cancel();
});
flights = Object.create(null);
}
function acquire(key, loader, options) {
options = options || {};
var id = token(key);
var cached = options.skipCache ? { hit: false } : read(key);
if (cached.hit) {
return {
cached: true,
promise: Promise.resolve(cached.value),
release: function () {},
};
}
var flight = flights[id];
if (!flight) {
flight = {
refs: 0,
settled: false,
cancelled: false,
tags: tagsOf(options.tags),
cancel: options.cancel,
};
flight.promise = Promise.resolve().then(loader).then(function (value) {
flight.settled = true;
if (!flight.cancelled && options.store !== false) set(key, value, options);
if (flights[id] === flight) delete flights[id];
return value;
}, function (error) {
flight.settled = true;
if (flights[id] === flight) delete flights[id];
throw error;
});
flights[id] = flight;
}
flight.refs += 1;
var released = false;
return {
cached: false,
promise: flight.promise,
release: function () {
if (released) return;
released = true;
flight.refs = Math.max(0, flight.refs - 1);
if (flight.refs === 0 && !flight.settled) {
flight.cancelled = true;
if (flights[id] === flight) delete flights[id];
if (typeof flight.cancel === "function") flight.cancel();
}
},
};
}
["aksrt:auth:logout", "aksrt:account-switch", "aksrt:logout", "aksrt:user:logout", "aksrt:auth:changed", "wp_logout"].forEach(function (eventName) {
document.addEventListener(eventName, clear);
});
window.AksrtModalCache = {
get: function (key) { return read(key); },
set: set,
acquire: acquire,
invalidate: invalidate,
clear: clear,
scope: scope,
};
})(window, document);
+416
View File
@@ -0,0 +1,416 @@
/**
* Canonical frontend modal runtime.
*
* Keeps the existing remote-modal shell while adding cancellable loaders,
* short in-memory caching, request deduplication, retryable errors, focus
* restoration, and modal-scoped rehydration.
*
* @package AKSRT
*/
(function (window, document) {
"use strict";
var ROOT_ID = "aksrt-remote-modal-root";
var active = null;
var sequence = 0;
var loaders = Object.create(null);
function isMobile() {
return window.matchMedia && window.matchMedia("(max-width: 768px)").matches;
}
function ensureRoot() {
var root = document.getElementById(ROOT_ID);
if (root) return root;
root = document.createElement("div");
root.id = ROOT_ID;
root.className = "aksrt-remote-modal-host";
document.body.appendChild(root);
return root;
}
function escapeHtml(value) {
return String(value == null ? "" : value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/\"/g, "&quot;")
.replace(/'/g, "&#39;");
}
function lockScroll(lock) {
document.documentElement.classList.toggle("aksrt-remote-modal-open", !!lock);
document.body.classList.toggle("aksrt-remote-modal-open", !!lock);
}
function skeleton(options) {
return (options && options.skeleton) ||
'<div class="aksrt-remote-modal__skeleton" aria-live="polite" aria-busy="true">' +
'<span class="aksrt-remote-modal__skeleton-line is-wide"></span>' +
'<span class="aksrt-remote-modal__skeleton-line"></span>' +
'<span class="aksrt-remote-modal__skeleton-line is-short"></span>' +
'<span class="aksrt-remote-modal__skeleton-block"></span>' +
'</div>';
}
function telemetryStart(kind) {
if (kind === false) return "";
var telemetry = window.AksrtModalTelemetry;
if (!telemetry || typeof telemetry.start !== "function") return "";
return telemetry.start(kind || "unknown");
}
function telemetryMark(session, event) {
var telemetry = window.AksrtModalTelemetry;
if (!session || !session.telemetryId || !telemetry || typeof telemetry.mark !== "function") return;
telemetry.mark(session.telemetryId, event);
}
function setState(session, status, error) {
session.state.status = status;
session.state.error = error || null;
session.state.updatedAt = Date.now();
if (session.shell) session.shell.setAttribute("data-aksrt-modal-state", status);
if (typeof session.options.onState === "function") {
session.options.onState(status, session);
}
}
function focusable(shell) {
return shell.querySelectorAll("button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])");
}
function closeSelector() {
return "[data-aksrt-remote-modal-close], [data-aksrt-modal-close], [data-dismiss='modal'], .aksrt-remote-modal__close, .aksrt-vip-modal-close, .payvip-modal .close";
}
function bindChrome(session) {
var shell = session.shell;
shell.addEventListener("click", function (event) {
var closeButton = event.target.closest(closeSelector());
if (closeButton) {
event.preventDefault();
close("control");
return;
}
if (event.target === shell || event.target.classList.contains("aksrt-remote-modal__overlay")) {
close("overlay");
return;
}
var retry = event.target.closest("[data-aksrt-remote-modal-retry], [data-aksrt-modal-retry]");
if (retry) {
event.preventDefault();
refresh();
}
});
shell.addEventListener("keydown", function (event) {
if (event.key === "Escape") {
event.preventDefault();
close("escape");
return;
}
if (event.key !== "Tab") return;
var nodes = focusable(shell);
if (!nodes.length) {
event.preventDefault();
shell.focus();
return;
}
var first = nodes[0];
var last = nodes[nodes.length - 1];
if (event.shiftKey && document.activeElement === first) {
event.preventDefault();
last.focus();
} else if (!event.shiftKey && document.activeElement === last) {
event.preventDefault();
first.focus();
}
});
}
function createSession(options) {
options = options || {};
var root = ensureRoot();
var shell = document.createElement("div");
var bottom = options.bottom === true || (options.mobileBottom !== false && isMobile());
var heightStyle = options.height ? "min-height:" + parseInt(options.height, 10) + "px;" : "";
var initialHtml = options.html !== undefined ? options.html : skeleton(options);
shell.className =
"aksrt-remote-modal" +
(bottom ? " aksrt-remote-modal--bottom" : "") +
(options.className ? " " + options.className : "");
shell.setAttribute("role", "dialog");
shell.setAttribute("aria-modal", "true");
shell.setAttribute("tabindex", "-1");
shell.innerHTML =
'<div class="aksrt-remote-modal__overlay" data-aksrt-remote-modal-close></div>' +
'<div class="aksrt-remote-modal__dialog" style="' + heightStyle + '">' +
'<div class="aksrt-remote-modal__content">' + initialHtml + "</div></div>";
var session = {
id: String(++sequence),
shell: shell,
content: shell.querySelector(".aksrt-remote-modal__content"),
options: options,
state: window.AksrtModalState && typeof window.AksrtModalState.create === "function"
? window.AksrtModalState.create(options.kind || "generic")
: { status: "idle", error: null },
focusReturn: document.activeElement,
controller: null,
flight: null,
closed: false,
telemetryId: telemetryStart(options.telemetry === false ? false : options.kind),
};
root.appendChild(shell);
bindChrome(session);
active = session;
lockScroll(true);
void shell.offsetWidth;
shell.classList.add("is-open");
setState(session, options.html !== undefined ? "ready" : "loading");
telemetryMark(session, "shell");
var nodes = focusable(shell);
try {
(nodes.length ? nodes[0] : shell).focus();
} catch (error) {}
document.dispatchEvent(new CustomEvent("aksrt:remote-modal:open", {
detail: { shell: shell, options: options, session: session },
}));
document.dispatchEvent(new CustomEvent("aksrt:modal:open", {
detail: { shell: shell, options: options, session: session },
}));
if (options.html !== undefined) {
rehydrate(session);
telemetryMark(session, "first-content");
telemetryMark(session, "ready");
if (typeof options.onContent === "function") options.onContent(options.html, session);
}
return session;
}
function rehydrate(session) {
if (!session || !session.content) return;
if (window.AksrtFrontend && typeof window.AksrtFrontend.rehydrate === "function") {
window.AksrtFrontend.rehydrate(session.content, "modal");
}
}
function abortSessionRequest(session) {
if (!session) return;
if (session.flight && typeof session.flight.release === "function") session.flight.release();
session.flight = null;
if (session.controller) {
try { session.controller.abort(); } catch (error) {}
}
session.controller = null;
}
function close(reason, preserveRequest) {
if (!active) return;
var session = active;
active = null;
session.closed = true;
if (!preserveRequest) abortSessionRequest(session);
session.shell.classList.remove("is-open");
if (!preserveRequest) lockScroll(false);
document.dispatchEvent(new CustomEvent("aksrt:remote-modal:close", {
detail: { shell: session.shell, reason: reason || "close", session: session },
}));
document.dispatchEvent(new CustomEvent("aksrt:modal:close", {
detail: { shell: session.shell, reason: reason || "close", session: session },
}));
var node = session.shell;
window.setTimeout(function () {
if (node && node.parentNode) node.parentNode.removeChild(node);
var target = session.focusReturn;
if (!preserveRequest && !active && target && target !== node && document.contains(target) && typeof target.focus === "function") {
try { target.focus(); } catch (error) {}
}
}, 220);
}
function responseHtml(result) {
if (!result) return "";
var json = result.json;
if (json) {
if (typeof json.html === "string") return json.html;
if (json.data && typeof json.data.html === "string") return json.data.html;
if (json.data && typeof json.data.show_modal === "string") return json.data.show_modal;
if (typeof json.data === "string") return json.data;
}
return typeof result.text === "string" ? result.text : "";
}
function errorMessage(error) {
if (error && error.code === "unauthorized") return "请先登录";
return error && error.message ? String(error.message) : "加载失败,请重试";
}
function renderError(session, error) {
if (!session || session.closed || active !== session) return;
var unauthorized = error && error.code === "unauthorized";
setState(session, unauthorized ? "unauthorized" : "error", error);
telemetryMark(session, unauthorized ? "unauthorized" : "error");
session.content.innerHTML =
'<div class="aksrt-remote-modal__error touch border-title"><b>提示</b></div>' +
'<div class="aksrt-remote-modal__error-body">' + escapeHtml(errorMessage(error)) +
'</div><div class="but-average modal-buts">' +
'<button type="button" class="but" data-aksrt-remote-modal-retry>重试</button>' +
'<button type="button" class="but" data-aksrt-remote-modal-close>关闭</button></div>';
rehydrate(session);
if (typeof session.options.onError === "function") session.options.onError(error, session);
}
function renderResult(session, result) {
if (!session || session.closed || active !== session) return;
var html = responseHtml(result);
if (!html) {
setState(session, "empty");
telemetryMark(session, "first-content");
telemetryMark(session, "ready");
session.content.innerHTML = '<div class="aksrt-remote-modal__error-body">暂无内容</div>';
return;
}
session.content.innerHTML = html;
setState(session, "ready");
telemetryMark(session, "first-content");
rehydrate(session);
telemetryMark(session, "ready");
if (typeof session.options.onContent === "function") session.options.onContent(html, session);
}
function load(session, loadOptions) {
loadOptions = loadOptions || {};
if (!session || session.closed || !session.options.url) return Promise.resolve(session && session.shell);
abortSessionRequest(session);
setState(session, "loading");
session.content.innerHTML = skeleton(session.options);
session.controller = window.AbortController ? new window.AbortController() : null;
var requestOptions = {
method: session.options.method || "GET",
headers: session.options.headers || {},
body: session.options.body,
query: session.options.query,
signal: session.controller ? session.controller.signal : undefined,
};
if (requestOptions.body === undefined) delete requestOptions.body;
if (!requestOptions.signal) delete requestOptions.signal;
var request = window.AksrtRequest && typeof window.AksrtRequest.request === "function"
? function () { return window.AksrtRequest.request(session.options.url, requestOptions); }
: function () {
return window.fetch(session.options.url, requestOptions).then(function (response) {
return response.text().then(function (text) { return { ok: response.ok, status: response.status, text: text, json: null }; });
});
};
var method = String(session.options.method || "GET").toUpperCase();
var cacheable = method === "GET" && session.options.cache !== false;
var cacheKey = session.options.cacheKey ||
method + ":" +
(window.AksrtRequest && typeof window.AksrtRequest.url === "function"
? window.AksrtRequest.url(session.options.url, session.options.query)
: session.options.url);
var cache = window.AksrtModalCache;
var acquireOptions = {
ttl: Number(session.options.cacheTtl || 15000),
tags: session.options.cacheTags || session.options.tags || [],
skipCache: !!loadOptions.force || !cacheable,
store: cacheable,
cancel: function () {
if (session.controller) {
try { session.controller.abort(); } catch (error) {}
}
},
};
session.flight = cacheable && cache && typeof cache.acquire === "function"
? cache.acquire(cacheKey, request, acquireOptions)
: { promise: request(), release: function () {} };
return session.flight.promise.then(function (result) {
if (!session.closed && active === session) renderResult(session, result);
return session.shell;
}).catch(function (error) {
var aborted = error && (error.name === "AbortError" || error.code === "ABORT_ERR");
if (!aborted && !session.closed && active === session) renderError(session, error);
return session.shell;
}).then(function (shell) {
if (session.flight && typeof session.flight.release === "function") session.flight.release();
session.flight = null;
return shell;
});
}
function open(options) {
options = options || {};
close("replace");
var session = createSession(options);
return session.shell;
}
function sessionKey(options) {
if (!options || !options.url) return "";
var method = String(options.method || "GET").toUpperCase();
var requestUrl = window.AksrtRequest && typeof window.AksrtRequest.url === "function"
? window.AksrtRequest.url(options.url, options.query)
: String(options.url);
return method + ":" + requestUrl;
}
function openRemote(options) {
options = Object.assign({}, options || {});
if (!options.url) return Promise.reject(new Error("missing url"));
var sameRequest = active && active.flight && sessionKey(active.options) === sessionKey(options);
close("replace", !!sameRequest);
var session = createSession(options);
return load(session).then(function () { return session.shell; });
}
function refresh(options) {
if (!active || !active.options.url) return Promise.resolve(null);
if (options && typeof options === "object") active.options = Object.assign({}, active.options, options);
return load(active, { force: true });
}
function registerLoader(kind, loader) {
if (!kind || typeof loader !== "function") return;
loaders[String(kind)] = loader;
}
function openLoader(kind, context) {
var loader = loaders[String(kind)];
if (!loader) return Promise.reject(new Error("missing modal loader: " + kind));
return openRemote(loader(context || {}));
}
function invalidate(tags) {
if (window.AksrtModalCache && typeof window.AksrtModalCache.invalidate === "function") {
return window.AksrtModalCache.invalidate(tags);
}
return 0;
}
window.AksrtModalRuntime = {
open: open,
openRemote: openRemote,
close: close,
refresh: refresh,
registerLoader: registerLoader,
openLoader: openLoader,
invalidate: invalidate,
getActive: function () { return active ? active.shell : null; },
getState: function () { return active ? active.state : null; },
};
document.dispatchEvent(new CustomEvent("aksrt:modal-runtime:ready"));
})(window, document);
+34
View File
@@ -0,0 +1,34 @@
/**
* Shared modal state vocabulary.
*
* @package AKSRT
*/
(function (window) {
"use strict";
var statuses = {
idle: "idle",
loading: "loading",
ready: "ready",
empty: "empty",
submitting: "submitting",
success: "success",
error: "error",
unauthorized: "unauthorized",
stale: "stale",
};
function create(kind) {
return {
kind: String(kind || "generic"),
status: statuses.idle,
updatedAt: 0,
error: null,
};
}
window.AksrtModalState = {
statuses: statuses,
create: create,
};
})(window);
+57 -212
View File
@@ -1,232 +1,77 @@
/**
* AKSRT Remote Modal — zibll RefreshModal parity.
* AKSRT Remote Modal compatibility bridge.
*
* Open a center/bottom modal with static HTML or remote fetch.
* Contract:
* - data-toggle="AksrtRemoteModal" | data-aksrt-remote-modal
* - data-remote / href → fetch HTML
* - data-class → dialog class (modal-mini full-sm …)
* - data-height → min-height hint
* - mobile-bottom="true" → bottom sheet on narrow viewports
* The lifecycle implementation lives in AksrtModalRuntime. This file keeps
* the legacy trigger contract and the AksrtRemoteModal public alias stable.
*
* @package AKSRT
*/
(function (window, document) {
"use strict";
var ROOT_ID = "aksrt-remote-modal-root";
var active = null;
function isMobile() {
return window.matchMedia && window.matchMedia("(max-width: 768px)").matches;
function runtime() {
return window.AksrtModalRuntime || null;
}
function ensureRoot() {
var root = document.getElementById(ROOT_ID);
if (root) return root;
root = document.createElement("div");
root.id = ROOT_ID;
root.className = "aksrt-remote-modal-host";
document.body.appendChild(root);
return root;
}
function fromTrigger(element) {
var modalRuntime = runtime();
if (!modalRuntime || !element) return Promise.reject(new Error("modal runtime unavailable"));
function restHeaders() {
var h = { Accept: "text/html, application/json" };
if (window.aksrtData && aksrtData.restNonce) {
h["X-WP-Nonce"] = aksrtData.restNonce;
}
return h;
}
var url = element.getAttribute("data-remote") ||
element.getAttribute("data-aksrt-remote") ||
element.getAttribute("href") || "";
if (!url || url === "#") return Promise.resolve(null);
function lockScroll(lock) {
document.documentElement.classList.toggle("aksrt-remote-modal-open", !!lock);
document.body.classList.toggle("aksrt-remote-modal-open", !!lock);
}
var ttl = parseInt(element.getAttribute("data-modal-cache-ttl") || "15000", 10);
var tags = (element.getAttribute("data-modal-cache-tags") || "").split(",").map(function (tag) {
return tag.trim();
}).filter(Boolean);
function close() {
if (!active) return;
active.classList.remove("is-open");
lockScroll(false);
var node = active;
active = null;
window.setTimeout(function () {
if (node && node.parentNode) node.parentNode.removeChild(node);
}, 220);
document.dispatchEvent(new CustomEvent("aksrt:remote-modal:close"));
}
function bindChrome(shell) {
shell.addEventListener("click", function (e) {
if (e.target.closest("[data-aksrt-remote-modal-close], [data-dismiss='modal'], .aksrt-remote-modal__close")) {
e.preventDefault();
close();
return;
}
if (e.target === shell || e.target.classList.contains("aksrt-remote-modal__overlay")) {
close();
}
});
shell.addEventListener("keydown", function (e) {
if (e.key === "Escape") close();
});
}
function open(options) {
options = options || {};
close();
var root = ensureRoot();
var shell = document.createElement("div");
var bottom = options.bottom === true || (options.mobileBottom !== false && isMobile());
shell.className =
"aksrt-remote-modal" +
(bottom ? " aksrt-remote-modal--bottom" : "") +
(options.className ? " " + options.className : "");
shell.setAttribute("role", "dialog");
shell.setAttribute("aria-modal", "true");
var heightStyle = options.height ? "min-height:" + parseInt(options.height, 10) + "px;" : "";
shell.innerHTML =
'<div class="aksrt-remote-modal__overlay" data-aksrt-remote-modal-close></div>' +
'<div class="aksrt-remote-modal__dialog" style="' + heightStyle + '">' +
'<div class="aksrt-remote-modal__content">' +
(options.html || "") +
"</div></div>";
root.appendChild(shell);
bindChrome(shell);
active = shell;
lockScroll(true);
// Force reflow then open for CSS transition.
void shell.offsetWidth;
shell.classList.add("is-open");
var focusable = shell.querySelector(
"button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])"
);
if (focusable) {
try {
focusable.focus();
} catch (err) {}
}
document.dispatchEvent(
new CustomEvent("aksrt:remote-modal:open", { detail: { shell: shell, options: options } })
);
return shell;
}
function openRemote(options) {
options = options || {};
var url = options.url || "";
if (!url) return Promise.reject(new Error("missing url"));
var loading = open({
html:
'<div class="aksrt-remote-modal__loading"><i class="fas fa-spinner fa-spin"></i> ' +
((window.aksrtData && aksrtData.ajaxLoading) || "加载中…") +
"</div>",
className: options.className || "modal-mini full-sm",
bottom: options.bottom,
mobileBottom: options.mobileBottom,
height: options.height,
});
return fetch(url, {
method: options.method || "GET",
credentials: "same-origin",
headers: Object.assign(restHeaders(), options.headers || {}),
body: options.body,
})
.then(function (res) {
var type = (res.headers.get("content-type") || "").toLowerCase();
if (type.indexOf("application/json") !== -1) {
return res.json().then(function (json) {
if (!res.ok || (json && json.success === false)) {
var msg =
(json && (json.message || (json.data && json.data.message))) ||
"加载失败";
throw new Error(msg);
}
var html =
(json && (json.html || (json.data && (json.data.html || json.data)))) || "";
if (typeof html !== "string") html = "";
return html;
});
}
return res.text().then(function (text) {
if (!res.ok) throw new Error("加载失败");
return text;
});
})
.then(function (html) {
if (loading && loading.parentNode) loading.parentNode.removeChild(loading);
if (active === loading) active = null;
return open({
html: html,
className: options.className || "modal-mini full-sm",
bottom: options.bottom,
mobileBottom: options.mobileBottom,
height: options.height,
});
})
.catch(function (err) {
if (loading && loading.parentNode) loading.parentNode.removeChild(loading);
if (active === loading) active = null;
open({
html:
'<div class="aksrt-remote-modal__error touch border-title"><b>提示</b></div>' +
'<div class="aksrt-remote-modal__error-body">' +
(err && err.message ? String(err.message) : "加载失败") +
'</div><div class="but-average modal-buts"><button type="button" class="but" data-aksrt-remote-modal-close>关闭</button></div>',
className: "modal-mini",
mobileBottom: true,
});
throw err;
});
}
function fromTrigger(el) {
if (!el) return null;
var url =
el.getAttribute("data-remote") ||
el.getAttribute("data-aksrt-remote") ||
el.getAttribute("href") ||
"";
if (!url || url === "#") return null;
var height = el.getAttribute("data-height") || el.getAttribute("data-aksrt-height");
return openRemote({
return modalRuntime.openRemote({
url: url,
className: el.getAttribute("data-class") || el.getAttribute("data-aksrt-class") || "modal-mini full-sm",
height: height ? parseInt(height, 10) : 0,
mobileBottom: el.getAttribute("mobile-bottom") !== "false",
bottom: el.getAttribute("data-bottom") === "true",
method: element.getAttribute("data-method") || "GET",
className: element.getAttribute("data-class") || element.getAttribute("data-aksrt-class") || "modal-mini full-sm",
height: parseInt(element.getAttribute("data-height") || element.getAttribute("data-aksrt-height") || "0", 10) || 0,
mobileBottom: element.getAttribute("mobile-bottom") !== "false",
bottom: element.getAttribute("data-bottom") === "true",
kind: element.getAttribute("data-modal-kind") || "generic",
cacheKey: element.getAttribute("data-modal-cache-key") || "",
cacheTtl: ttl > 0 ? ttl : 15000,
cacheTags: tags,
});
}
document.addEventListener("click", function (e) {
var trigger = e.target.closest(
'[data-toggle="AksrtRemoteModal"], [data-toggle="RefreshModal"], [data-aksrt-remote-modal], a.aksrt-remote-modal-link'
);
if (!trigger) return;
if (trigger.getAttribute("data-aksrt-remote-modal") === "off") return;
e.preventDefault();
fromTrigger(trigger);
});
function bind() {
if (window.__aksrtRemoteModalBound) return;
if (!runtime()) return;
window.__aksrtRemoteModalBound = true;
document.addEventListener("keydown", function (e) {
if (e.key === "Escape" && active) close();
});
document.addEventListener("click", function (event) {
var trigger = event.target.closest(
'[data-toggle="AksrtRemoteModal"], [data-toggle="RefreshModal"], [data-aksrt-remote-modal], a.aksrt-remote-modal-link'
);
if (!trigger || trigger.getAttribute("data-aksrt-remote-modal") === "off") return;
event.preventDefault();
fromTrigger(trigger);
});
}
window.AksrtRemoteModal = {
open: open,
openRemote: openRemote,
close: close,
fromTrigger: fromTrigger,
getActive: function () {
return active;
},
};
function expose() {
var modalRuntime = runtime();
if (!modalRuntime) return;
window.AksrtRemoteModal = {
open: modalRuntime.open,
openRemote: modalRuntime.openRemote,
close: modalRuntime.close,
refresh: modalRuntime.refresh,
fromTrigger: fromTrigger,
getActive: modalRuntime.getActive,
};
bind();
}
expose();
if (!window.AksrtModalRuntime) {
document.addEventListener("aksrt:modal-runtime:ready", expose, { once: true });
}
})(window, document);
+157
View File
@@ -0,0 +1,157 @@
/**
* AKSRT frontend request adapter.
*
* Owns the injected REST root, nonce headers, response envelopes, and
* abort-aware error normalization used by modal loaders.
*
* @package AKSRT
*/
(function (window) {
"use strict";
function data() {
return window.aksrtData || window.aksrt || {};
}
function restBase() {
var config = data();
var raw = config.restUrl || config.apiUrl || config.rest_url || "";
var siteUrl = config.siteUrl || config.homeUrl || (window.location.origin + "/");
if (window.location.protocol === "https:" && siteUrl.substring(0, 5) === "http:") {
siteUrl = "https:" + siteUrl.substring(5);
}
if (raw) return String(raw).replace(/\/$/, "");
return String(siteUrl).replace(/\/$/, "") + "/index.php?rest_route=/aksrt/v1";
}
function appendQuery(url, query) {
if (!query) return url;
var parts = [];
if (typeof query === "string") {
parts = query.replace(/^\?/, "").split("&").filter(Boolean);
} else if (typeof query === "object") {
Object.keys(query).forEach(function (key) {
if (query[key] === undefined || query[key] === null) return;
parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(String(query[key])));
});
}
if (!parts.length) return url;
return url + (url.indexOf("?") >= 0 ? "&" : "?") + parts.join("&");
}
function url(path, query) {
path = String(path || "");
var inlineQuery = "";
var queryIndex = path.indexOf("?");
if (queryIndex >= 0) {
inlineQuery = path.slice(queryIndex + 1);
path = path.slice(0, queryIndex);
}
if (/^https?:\/\//i.test(path)) {
return appendQuery(path, query || inlineQuery);
}
if (path.charAt(0) !== "/") path = "/" + path;
var base = restBase();
var result = base.indexOf("rest_route=") >= 0 ? base + path : base + path;
return appendQuery(result, query || inlineQuery);
}
function headers(options) {
var config = data();
var source = (options && options.headers) || {};
var result = {};
var nonce = config.restNonce || config.nonce || "";
Object.keys(source).forEach(function (key) {
result[key] = source[key];
});
if (!result.Accept) result.Accept = "application/json, text/html;q=0.9, */*;q=0.8";
if (nonce && !result["X-WP-Nonce"]) result["X-WP-Nonce"] = nonce;
if (options && options.body !== undefined && options.body !== null &&
!(window.FormData && options.body instanceof window.FormData) &&
!result["Content-Type"]) {
result["Content-Type"] = "application/json";
}
return result;
}
function responseError(result) {
var json = result.json;
var message = json && (json.message || (json.data && json.data.message));
var error = new Error(message || (result.status ? "请求失败(HTTP " + result.status + ")" : "请求失败"));
error.status = result.status;
error.code = result.status === 401 ? "unauthorized" : "http_error";
error.response = result;
error.json = json;
return error;
}
function parseResponse(response) {
return response.text().then(function (text) {
var type = (response.headers.get("content-type") || "").toLowerCase();
var json = null;
var trimmed = String(text || "").trim();
if (type.indexOf("application/json") >= 0 || trimmed.charAt(0) === "{" || trimmed.charAt(0) === "[") {
try {
json = trimmed ? JSON.parse(trimmed) : {};
} catch (error) {
json = null;
}
}
return {
ok: response.ok,
status: response.status,
headers: response.headers,
text: text,
json: json,
data: json && json.data !== undefined ? json.data : json,
};
});
}
function request(path, options) {
options = Object.assign({}, options || {});
var requestUrl = url(path, options.query);
delete options.query;
var fetchOptions = Object.assign({}, options, {
credentials: options.credentials || "same-origin",
headers: headers(options),
});
return window.fetch(requestUrl, fetchOptions).then(function (response) {
return parseResponse(response);
}).then(function (result) {
if (!result.ok || (result.json && result.json.success === false)) {
throw responseError(result);
}
return result;
});
}
window.AksrtRequest = {
url: url,
request: request,
get: function (path, options) {
return request(path, Object.assign({}, options || {}, { method: "GET" }));
},
post: function (path, body, options) {
return request(path, Object.assign({}, options || {}, {
method: "POST",
body: body,
}));
},
};
})(window);
+24 -18
View File
@@ -15,31 +15,31 @@
var detailFooter = page.querySelector("[data-aksrt-order-detail-footer]");
var detailOrderNumber = "";
function toast(message) {
if (window.AksrtToast && typeof window.AksrtToast.show === "function") {
window.AksrtToast.show(message);
function toast(message, type) {
if (typeof window.AksrtToast === "function") {
window.AksrtToast(message, type || "info");
return;
}
if (typeof window.aksrtNotify === "function") {
window.aksrtNotify(message, type || "info");
return;
}
window.alert(message);
}
function restRoot() {
return String(aksrtData.siteUrl || "").replace(/\/$/, "") + "/wp-json/aksrt/v1";
}
function request(path, body, method) {
var options = {
method: method || "POST",
credentials: "same-origin",
headers: { "X-WP-Nonce": aksrtData.restNonce || "" }
};
if (body !== undefined) {
options.headers["Content-Type"] = "application/json";
options.body = JSON.stringify(body || {});
if (!window.AksrtRequest || typeof window.AksrtRequest.request !== "function") {
return Promise.resolve({ success: false, message: "请求适配器未加载" });
}
return fetch(restRoot() + path, options).then(function (response) {
return response.json().catch(function () {
return { success: false, message: "请求失败(HTTP " + response.status + ")" };
var options = { method: method || "POST" };
if (body !== undefined) options.body = JSON.stringify(body || {});
return window.AksrtRequest.request(path, options).then(function (response) {
return response.json || {};
}).catch(function (error) {
var response = error && error.response ? error.response : {};
return error && error.json ? error.json : (response.json || {
success: false,
message: error && error.message ? error.message : "请求失败",
});
});
}
@@ -219,6 +219,9 @@
button.disabled = true;
request(orderPath(number, "/pay"), method ? { payment_method: method } : {}).then(function (result) {
if (!result.success) throw new Error(result.message || "支付失败");
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === "function") {
window.AksrtModalRuntime.invalidate(["orders", "vip"]);
}
var data = result.data || {};
if (data.redirect && data.payment_url) {
window.location.assign(data.payment_url);
@@ -243,6 +246,9 @@
button.disabled = true;
request(orderPath(number, "/cancel")).then(function (result) {
if (!result.success) throw new Error(result.message || "关闭订单失败");
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === "function") {
window.AksrtModalRuntime.invalidate(["orders", "vip"]);
}
toast("订单已关闭");
refreshView();
}).catch(function (error) {
+60 -135
View File
@@ -5,126 +5,51 @@
(function () {
"use strict";
function restBase() {
var aksrt = window.aksrtData || window.aksrt || {};
var siteUrl = aksrt.siteUrl || aksrt.homeUrl || (window.location.origin + "/");
if (window.location.protocol === "https:" && siteUrl.substring(0, 5) === "http:") {
siteUrl = "https:" + siteUrl.substring(5);
}
var raw = aksrt.apiUrl || aksrt.restUrl || aksrt.rest_url || "";
if (raw) {
return String(raw).replace(/\/$/, "");
}
return siteUrl.replace(/\/$/, "") + "/index.php?rest_route=/aksrt/v1";
}
/**
* Join REST root + path [+ query].
* Pretty: /wp-json/aksrt/v1/pay/vip/modal?vip_level=1
* Plain: index.php?rest_route=/aksrt/v1/pay/vip/modal&vip_level=1
* Never put a second "?" after rest_route= (that 404s).
*/
function restUrl(path, query) {
path = String(path || "");
if (path.charAt(0) !== "/") path = "/" + path;
var base = restBase();
var qstr = "";
if (query && typeof query === "object") {
var parts = [];
Object.keys(query).forEach(function (k) {
if (query[k] === undefined || query[k] === null) return;
parts.push(encodeURIComponent(k) + "=" + encodeURIComponent(String(query[k])));
});
qstr = parts.join("&");
} else if (typeof query === "string" && query) {
qstr = query.replace(/^\?/, "");
}
if (base.indexOf("rest_route=") !== -1) {
var url = base + path;
if (qstr) url += "&" + qstr;
return url;
}
var pretty = base + path;
if (qstr) pretty += (pretty.indexOf("?") >= 0 ? "&" : "?") + qstr;
return pretty;
}
function nonceHeaders() {
var aksrt = window.aksrtData || window.aksrt || {};
var n = aksrt.restNonce || aksrt.nonce || "";
return n ? { "X-WP-Nonce": n } : {};
}
function api(path, options) {
var opts = Object.assign({}, options || {});
var query = opts.query;
if (Object.prototype.hasOwnProperty.call(opts, "query")) {
delete opts.query;
}
var onlyPath = String(path || "");
var inlineQ = null;
var qi = onlyPath.indexOf("?");
if (qi >= 0) {
inlineQ = onlyPath.slice(qi + 1);
onlyPath = onlyPath.slice(0, qi);
}
var url = restUrl(onlyPath, query || inlineQ);
var merged = Object.assign(
{
credentials: "same-origin",
headers: Object.assign({ "Content-Type": "application/json" }, nonceHeaders()),
},
opts
);
return fetch(url, merged).then(function (r) {
return r
.json()
.catch(function () {
return { message: "Invalid JSON (HTTP " + r.status + ")" };
})
.then(function (j) {
return { ok: r.ok, status: r.status, json: j };
var request = window.AksrtRequest && typeof window.AksrtRequest.request === "function"
? window.AksrtRequest.request(path, opts)
: fetch(path, Object.assign({ credentials: "same-origin" }, opts)).then(function (response) {
return response.json().then(function (json) {
return { ok: response.ok, status: response.status, json: json };
});
});
return request.then(function (response) {
return {
ok: response.ok,
status: response.status,
json: response.json || {},
};
}).catch(function (error) {
var response = error && error.response ? error.response : {};
return {
ok: false,
status: error && error.status ? error.status : (response.status || 0),
json: error && error.json ? error.json : (response.json || { message: error && error.message }),
};
});
}
function ensureModalShell() {
var el = document.getElementById("modal_pay_uservip");
if (el) return el;
el = document.createElement("div");
el.id = "modal_pay_uservip";
el.className = "aksrt-vip-modal-overlay";
el.setAttribute("aria-hidden", "true");
el.innerHTML =
'<div class="aksrt-vip-modal-dialog" role="dialog" aria-modal="true">' +
'<div class="aksrt-vip-modal-content" data-aksrt-vip-modal-body></div></div>';
document.body.appendChild(el);
el.addEventListener("click", function (e) {
if (e.target === el || e.target.closest(".aksrt-vip-modal-close")) {
closeModal();
}
});
document.addEventListener("keydown", function (e) {
if (e.key === "Escape") closeModal();
});
return el;
}
var activeVipShell = null;
function openModal() {
var el = ensureModalShell();
el.classList.add("is-open");
el.setAttribute("aria-hidden", "false");
document.body.classList.add("aksrt-vip-modal-open");
if (!window.AksrtModalRuntime || typeof window.AksrtModalRuntime.open !== "function") return null;
activeVipShell = window.AksrtModalRuntime.open({
className: "aksrt-vip-modal-shell",
mobileBottom: false,
skeleton: '<div class="aksrt-vip-modal-dialog"><div class="aksrt-vip-modal-content" data-aksrt-vip-modal-body><div class="text-center padding-20 muted-2-color">加载中…</div></div></div>',
telemetry: false,
kind: "vip",
});
return activeVipShell;
}
function closeModal() {
var el = document.getElementById("modal_pay_uservip");
if (!el) return;
el.classList.remove("is-open");
el.setAttribute("aria-hidden", "true");
document.body.classList.remove("aksrt-vip-modal-open");
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.close === "function") {
window.AksrtModalRuntime.close("vip");
}
activeVipShell = null;
}
function bindModalUi(root) {
@@ -226,11 +151,17 @@
}
var data = r.json && r.json.data ? r.json.data : r.json;
if (data && data.redirect && (data.payment_url || data.qr_content)) {
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === "function") {
window.AksrtModalRuntime.invalidate(["vip", "orders"]);
}
if (data.payment_url) window.open(data.payment_url, "_blank");
setTip(pane, data.message || "请完成支付,支付成功后将自动刷新。");
if (data.order_number) pollOrder(data.order_number);
return;
}
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === "function") {
window.AksrtModalRuntime.invalidate(["vip", "orders"]);
}
setTip(pane, (data && data.message) || "开通成功,正在刷新…");
setTimeout(function () {
window.location.reload();
@@ -257,10 +188,13 @@
function telemetryMark(event) {
if (telemetry && telemetryId && typeof telemetry.mark === "function") telemetry.mark(telemetryId, event);
}
var shell = ensureModalShell();
var shell = openModal();
if (!shell) {
loading = false;
telemetryMark("error");
return;
}
var body = shell.querySelector("[data-aksrt-vip-modal-body]");
if (body) body.innerHTML = '<div class="text-center padding-20 muted-2-color">加载中…</div>';
openModal();
telemetryMark("shell");
api("/pay/vip/modal", { query: { vip_level: level } })
@@ -286,6 +220,9 @@
if (body) {
body.innerHTML = r.json.html;
telemetryMark("first-content");
if (window.AksrtFrontend && typeof window.AksrtFrontend.rehydrate === "function") {
window.AksrtFrontend.rehydrate(body, "modal");
}
bindModalUi(body);
telemetryMark("ready");
}
@@ -318,31 +255,19 @@
document.addEventListener("click", onDocClick, true);
}
// Prefer module registry, but always bind once as hard fallback
// (float rail must work even if AksrtFrontend.rehydrate is a no-op).
if (window.__aksrtPayVipBound) {
// already initialized
} else if (window.AksrtFrontend && typeof window.AksrtFrontend.register === "function") {
window.AksrtFrontend.register("aksrt-pay-vip", function () {
if (window.__aksrtPayVipBound) return;
window.__aksrtPayVipBound = true;
init();
});
// Also bind immediately — float button is in footer, available now.
if (!window.__aksrtPayVipBound) {
window.__aksrtPayVipBound = true;
init();
}
} else if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", function () {
if (window.__aksrtPayVipBound) return;
window.__aksrtPayVipBound = true;
init();
});
} else {
// This handler is delegated from document, so registry rehydration is not
// needed. Bind exactly once and keep dynamically injected triggers working.
function bindOnce() {
if (window.__aksrtPayVipBound) return;
window.__aksrtPayVipBound = true;
init();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", bindOnce, { once: true });
} else {
bindOnce();
}
window.aksrtVipPay = vipPay;
})();
+21 -20
View File
@@ -8,36 +8,31 @@
(function () {
'use strict';
var aksrt = window.aksrtData || {};
var siteUrl = aksrt.siteUrl || '';
if (window.location.protocol === 'https:' && siteUrl.substring(0, 5) === 'http:') {
siteUrl = 'https:' + siteUrl.substring(5);
}
var REST = (siteUrl || '').replace(/\/$/, '') + '/wp-json/aksrt/v1';
var NONCE = aksrt.restNonce || aksrt.nonce || '';
function api(path, options) {
var opts = Object.assign({
credentials: 'same-origin',
headers: Object.assign({ 'Content-Type': 'application/json', 'X-WP-Nonce': NONCE }, (options && options.headers) || {}),
}, options || {});
return fetch(REST + path, opts).then(function (r) {
return r.json().catch(function () { return {}; }).then(function (j) {
return { ok: r.ok, status: r.status, json: j };
});
if (!window.AksrtRequest || typeof window.AksrtRequest.request !== 'function') {
return Promise.resolve({ ok: false, status: 0, json: { message: '请求适配器未加载' } });
}
return window.AksrtRequest.request(path, options || {}).then(function (response) {
return { ok: response.ok, status: response.status, json: response.json || {} };
}).catch(function (error) {
var response = error && error.response ? error.response : {};
return {
ok: false,
status: error && error.status ? error.status : (response.status || 0),
json: error && error.json ? error.json : (response.json || { message: error && error.message }),
};
});
}
function toast(msg, type) {
if (window.AksrtToast && typeof window.AksrtToast.show === 'function') {
window.AksrtToast.show(msg, type || 'info');
if (typeof window.AksrtToast === 'function') {
window.AksrtToast(msg, type || 'info');
return;
}
if (window.aksrtNotify) {
if (typeof window.aksrtNotify === 'function') {
window.aksrtNotify(msg, type || 'info');
return;
}
// fallback
try { console.log('[AKSRT badges]', msg); } catch (_) {}
}
@@ -265,6 +260,9 @@
return;
}
toast((r.json && r.json.message) || '已佩戴徽章', 'success');
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === 'function') {
window.AksrtModalRuntime.invalidate(['badges', 'identity']);
}
state.data = (r.json && r.json.data) || state.data;
renderHead();
renderGrid();
@@ -354,6 +352,9 @@
return;
}
toast((r.json && r.json.message) || '已佩戴徽章', 'success');
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === 'function') {
window.AksrtModalRuntime.invalidate(['badges', 'identity']);
}
// Re-open the refreshed single-badge modal so state stays in sync.
var refreshed = null;
try {
+4 -4
View File
@@ -227,7 +227,7 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
wp_enqueue_script(
'aksrt-pay-vip',
$base_uri . '/assets/js/pay-vip.js',
['aksrt-script'],
['aksrt-script', 'aksrt-core-request', 'aksrt-core-modal-runtime'],
(string) filemtime($vip_js),
true
);
@@ -671,7 +671,7 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
wp_enqueue_script(
'aksrt-order-center',
$base_uri . '/assets/js/order-center.js',
['aksrt-script'],
['aksrt-script', 'aksrt-core-request', 'aksrt-core-modal-runtime'],
file_exists(get_template_directory() . '/assets/js/order-center.js')
? (string) filemtime(get_template_directory() . '/assets/js/order-center.js')
: $version,
@@ -894,7 +894,7 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
wp_enqueue_script(
'aksrt-user-badges',
$base_uri . '/assets/js/user-badges.js',
['aksrt-script'],
['aksrt-script', 'aksrt-core-request', 'aksrt-core-modal-runtime', 'aksrt-core-remote-modal'],
(string) filemtime($badges_js_path),
true
);
@@ -917,7 +917,7 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
wp_enqueue_script(
'aksrt-user-badges',
$base_uri . '/assets/js/user-badges.js',
['aksrt-script', 'aksrt-core-remote-modal'],
['aksrt-script', 'aksrt-core-request', 'aksrt-core-modal-runtime', 'aksrt-core-remote-modal'],
(string) filemtime($badges_js_path_global),
true
);
+16 -3
View File
@@ -126,10 +126,14 @@ trait ScriptsPart1c {
'aksrt-core-comment-fixed' => 'comment-fixed-input.js',
'aksrt-core-comment-move' => 'comment-move-form.js',
// Shared zibll-parity list/modal engines (before pagination consumers).
'aksrt-core-ajax-list' => 'ajax-list.js',
'aksrt-core-ajax-list' => 'ajax-list.js',
'aksrt-core-modal-telemetry' => 'modal-telemetry.js',
'aksrt-core-remote-modal' => 'remote-modal.js',
'aksrt-core-pagination' => 'pagination.js',
'aksrt-core-request' => 'request.js',
'aksrt-core-modal-cache' => 'modal-cache.js',
'aksrt-core-modal-state' => 'modal-state.js',
'aksrt-core-modal-runtime' => 'modal-runtime.js',
'aksrt-core-remote-modal' => 'remote-modal.js',
'aksrt-core-pagination' => 'pagination.js',
'aksrt-core-archive' => 'archive.js',
'aksrt-core-footer-tabbar' => 'footer-tabbar.js',
'aksrt-core-misc' => 'misc.js',
@@ -142,6 +146,15 @@ trait ScriptsPart1c {
$deps = ['aksrt-rehydrate', 'aksrt-runtime'];
if ($handle === 'aksrt-core-slider') {
$deps = array_merge($deps, $needs_swiper ? ['aksrt-swiper'] : []);
} elseif ($handle === 'aksrt-core-modal-runtime') {
$deps = array_merge($deps, [
'aksrt-core-modal-telemetry',
'aksrt-core-request',
'aksrt-core-modal-cache',
'aksrt-core-modal-state',
]);
} elseif ($handle === 'aksrt-core-remote-modal') {
$deps[] = 'aksrt-core-modal-runtime';
}
wp_enqueue_script(
$handle,