feat(runtime): 全站前端脚本统一接入 AksrtFrontend 全局 DOM 生命周期

- rehydrate.js 新增 boot()/aksrt:ready 事件,支持 boot 后晚注册立即以 init 触发
- aksrt-runtime.js 初始加载改走 AksrtFrontend.boot,tooltip/倒计时注册为组件
- 约 30 个前端脚本(首页、评论、支付、路由、页面叶子、widgets)
  从直接 DOMContentLoaded 迁移为 AksrtFrontend.register,并加幂等标记
- section-navs 依赖改为 aksrt-runtime,保证注册时序
- 清理死代码:删除孤儿 loader.js/main.js 及 runtime/page-navs 遗留副本
- loader.js 移除已迁移模块的 tbquire 路径映射,main.js 相应瘦身
- build-public-assets.mjs 增加 runtime/main.min.js 自动压缩步骤
- 新增 4 个契约测试(homepage-dom/rehydrate-runtime/global-dom-init/
  public-bundle-parity),3 个既有 harness 适配注册表初始化方式
This commit is contained in:
2026-09-02 21:48:56 +08:00
parent 5984d4e42d
commit 1f4a39d8f8
60 changed files with 1810 additions and 1742 deletions
+4
View File
@@ -45,6 +45,10 @@
!/tests/widget-system/front-submit-editor-contract.test.cjs !/tests/widget-system/front-submit-editor-contract.test.cjs
!/tests/widget-system/comment-smilies-removal-contract.test.cjs !/tests/widget-system/comment-smilies-removal-contract.test.cjs
!/tests/widget-system/lightbox-contract.test.cjs !/tests/widget-system/lightbox-contract.test.cjs
!/tests/widget-system/homepage-dom-contract.test.cjs
!/tests/widget-system/rehydrate-runtime-contract.test.cjs
!/tests/widget-system/global-dom-init-contract.test.cjs
!/tests/widget-system/public-bundle-parity-contract.test.cjs
!/tests/widget-system/comment-children-list-reset-contract.test.cjs !/tests/widget-system/comment-children-list-reset-contract.test.cjs
!/tests/widget-system/pay-card-contract.test.cjs !/tests/widget-system/pay-card-contract.test.cjs
!/tests/widget-system/circular-button-icon-margin-contract.test.cjs !/tests/widget-system/circular-button-icon-margin-contract.test.cjs
+19 -67
View File
@@ -269,64 +269,7 @@
}); });
}, true); }, true);
// ══════════════════════════════════════════════════════════════ // Vendor modules are no longer dynamically loaded by the canonical frontend path.
// §10.10a — 拦截 vendor 评论脚本 & 解绑 vendor ajax 委托
// ══════════════════════════════════════════════════════════════
// vendor 链路:loader.js(tbquire,改造版 RequireJS)→ main.min.js 检测到
// #commentform/#postcomments 即 tbquire(["comment"]) 懒加载 comment.min.js;
// 检测到 .dropdown-code/.dropdown-image 即 tbquire(["input-expand"])。
// AKSRT 自有评论脚本(comments/comment-tools/comment-fixed-input/
// comment-move-form/comment-ajax)已承担全部行为,vendor comment.min.js 的
// 7 条 body 委托会双路触发(提交 legacy action=submit_comment 无处理器,
// 即控制台「ajax请求错误」400 "0" 的来源;回复/排序/分页同理);
// vendor input-expand.min.js(表情/快捷回复旧链路,源文件已删除)同理不再下载。
//
// 双层防御:
// 1) tbfine 预注册空 'comment'/'input-expand' 模块 → tbquire 不再下载;
// 2) 若模块抢在注册前加载,退化为对 7 条委托做幂等 .off。
function stubVendorCommentModule() {
if (!(window.tbfine && window.tbquire && window.tbquire.defined)) return false;
try {
['comment', 'input-expand'].forEach(function (name) {
if (!window.tbquire.defined(name)) {
window.tbfine(name, [], function () { return {}; });
}
});
return true;
} catch (e) {
return false;
}
}
// main.min.js 的 body 级 legacy 委托同理摘除(vendor 只保留 UI 行为)。
function unbindVendorAjaxDelegates() {
stubVendorCommentModule();
if (!window.jQuery) return;
try {
var $body = jQuery('body');
$body.off('click', '[data-action]');
$body.off('click', '[ajax-action]');
$body.off('click', '.wp-ajax-submit,[zibajax="submit"]');
$body.off('click', '.initiate-checkin');
$body.off('click', '.oauth-untying');
// comment.min.js 的 7 条委托(legacy/js/comment.js 原始选择器)
$body.off('click', '[fixed-input]');
$body.off('click', '.commentlist .pagenav a,.comment-orderby');
$body.off('click', '.comment-edit-link,.comment-reply-link');
$body.off('keydown', '#comment');
$body.off('click', '.comment-trash-link');
$body.off('click', '.comment-approve-link');
$body.off('click', '#commentform #submit');
} catch (e) { /* vendor 运行时未就绪时忽略 */ }
}
var unbindVendorAjaxLoop = function () {
unbindVendorAjaxDelegates();
if (++unbindVendorAjaxLoop.tries >= 50) return; // ~10s @200ms 覆盖异步加载窗口
setTimeout(unbindVendorAjaxLoop, 200);
};
unbindVendorAjaxLoop.tries = 0;
unbindVendorAjaxLoop();
// ══════════════════════════════════════════════════════════════ // ══════════════════════════════════════════════════════════════
// §10.11 — [data-action] / [ajax-action] universal buttons // §10.11 — [data-action] / [ajax-action] universal buttons
// ══════════════════════════════════════════════════════════════ // ══════════════════════════════════════════════════════════════
@@ -533,13 +476,7 @@
// §10.11 — auto_fun: extended rehydration coverage // §10.11 — auto_fun: extended rehydration coverage
// ══════════════════════════════════════════════════════════════ // ══════════════════════════════════════════════════════════════
aksrt.autoFun = function (root) { function initTooltips(root) {
root = root || document;
if (window.AksrtFrontend) {
AksrtFrontend.rehydrate(root, 'ajax');
}
// Tooltips
root.querySelectorAll('[data-tooltip]').forEach(function (el) { root.querySelectorAll('[data-tooltip]').forEach(function (el) {
if (el.dataset.tooltipBound) return; if (el.dataset.tooltipBound) return;
el.dataset.tooltipBound = '1'; el.dataset.tooltipBound = '1';
@@ -557,8 +494,9 @@
if (el._aksrtTooltip) { el._aksrtTooltip.remove(); el._aksrtTooltip = null; } if (el._aksrtTooltip) { el._aksrtTooltip.remove(); el._aksrtTooltip = null; }
}); });
}); });
}
// Countdowns function initCountdowns(root) {
root.querySelectorAll('[data-countdown]').forEach(function (el) { root.querySelectorAll('[data-countdown]').forEach(function (el) {
if (el.dataset.countdownBound) return; if (el.dataset.countdownBound) return;
el.dataset.countdownBound = '1'; el.dataset.countdownBound = '1';
@@ -572,6 +510,20 @@
el.textContent = (m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s; el.textContent = (m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s;
}, 1000); }, 1000);
}); });
}
AksrtFrontend.register('aksrt-tooltips', initTooltips);
AksrtFrontend.register('aksrt-countdowns', initCountdowns);
aksrt.autoFun = function (root, reason) {
root = root || document;
reason = reason || 'ajax';
if (!window.AksrtFrontend) return;
if (reason === 'init' && typeof AksrtFrontend.boot === 'function') {
AksrtFrontend.boot(root);
} else {
AksrtFrontend.rehydrate(root, reason);
}
}; };
// ══════════════════════════════════════════════════════════════ // ══════════════════════════════════════════════════════════════
@@ -590,7 +542,7 @@
// ── DOMContentLoaded ── // ── DOMContentLoaded ──
function initRuntime() { function initRuntime() {
aksrt.autoFun(document); aksrt.autoFun(document, 'init');
} }
if (document.readyState === 'loading') { if (document.readyState === 'loading') {
+42 -16
View File
@@ -8,6 +8,8 @@
var modal = null; var modal = null;
var lastModalTrigger = null; var lastModalTrigger = null;
var qrStates = []; var qrStates = [];
var globalEventsBound = false;
var oauthResultChecked = false;
var oauthErrorMessages = { var oauthErrorMessages = {
access_denied: '已取消第三方授权', access_denied: '已取消第三方授权',
cancelled: '已取消第三方授权', cancelled: '已取消第三方授权',
@@ -507,6 +509,17 @@
} }
function initRoot(root) { function initRoot(root) {
root = root || document;
if (root.nodeType === 1 && root.matches('[data-aksrt-auth]')) {
initAuthRoot(root);
return;
}
if (root.querySelectorAll) root.querySelectorAll('[data-aksrt-auth]').forEach(initAuthRoot);
}
function initAuthRoot(root) {
if (root.dataset.aksrtAuthInitialized === '1') return;
root.dataset.aksrtAuthInitialized = '1';
root.querySelectorAll('input').forEach(function (input) { root.querySelectorAll('input').forEach(function (input) {
if (!input.hasAttribute('placeholder')) input.setAttribute('placeholder', ' '); if (!input.hasAttribute('placeholder')) input.setAttribute('placeholder', ' ');
}); });
@@ -662,10 +675,14 @@
} }
} }
function init() { function initGlobal() {
showOAuthResultToast();
document.querySelectorAll('[data-aksrt-auth]').forEach(initRoot);
modal = document.querySelector('[data-auth-modal]'); modal = document.querySelector('[data-auth-modal]');
if (!oauthResultChecked) {
oauthResultChecked = true;
showOAuthResultToast();
}
if (globalEventsBound) return;
globalEventsBound = true;
document.addEventListener('click', function (event) { document.addEventListener('click', function (event) {
var trigger = event.target.closest('[data-aksrt-auth-required], .aksrt-login-trigger, #nav-login-btn, .signin-loader, .signup-loader, .qrcode-signin'); var trigger = event.target.closest('[data-aksrt-auth-required], .aksrt-login-trigger, #nav-login-btn, .signin-loader, .signup-loader, .qrcode-signin');
if (!trigger) return; if (!trigger) return;
@@ -673,17 +690,7 @@
var tab = trigger.classList.contains('signup-loader') ? 'signup' : (trigger.classList.contains('qrcode-signin') ? 'oauth' : 'signin'); var tab = trigger.classList.contains('signup-loader') ? 'signup' : (trigger.classList.contains('qrcode-signin') ? 'oauth' : 'signin');
openModal(tab, trigger.dataset.authRedirect || window.location.href, trigger); openModal(tab, trigger.dataset.authRedirect || window.location.href, trigger);
}); });
if (modal) { document.addEventListener('keydown', handleModalKeydown);
modal.addEventListener('click', function (event) {
if (event.target.closest('[data-auth-close]')) closeModal(true);
});
document.addEventListener('keydown', handleModalKeydown);
if (modal.dataset.auto === '1' && Number(window.localStorage.getItem('aksrtAuthAutoUntil') || 0) < Date.now()) {
window.setTimeout(function () {
openModal(modal.querySelector('[data-aksrt-auth]').dataset.initialTab || 'signin', window.location.href, null);
}, Math.max(1, Number(modal.dataset.autoWait) || 5) * 1000);
}
}
document.addEventListener('visibilitychange', function () { document.addEventListener('visibilitychange', function () {
qrStates.forEach(function (state) { qrStates.forEach(function (state) {
if (document.visibilityState !== 'visible') stopQr(state.root); if (document.visibilityState !== 'visible') stopQr(state.root);
@@ -692,6 +699,25 @@
}); });
} }
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); function initModal() {
else init(); if (!modal || modal.dataset.aksrtAuthModalInitialized === '1') return;
modal.dataset.aksrtAuthModalInitialized = '1';
modal.addEventListener('click', function (event) {
if (event.target.closest('[data-auth-close]')) closeModal(true);
});
if (modal.dataset.auto === '1' && Number(window.localStorage.getItem('aksrtAuthAutoUntil') || 0) < Date.now()) {
window.setTimeout(function () {
var root = modal.querySelector('[data-aksrt-auth]');
if (root) openModal(root.dataset.initialTab || 'signin', window.location.href, null);
}, Math.max(1, Number(modal.dataset.autoWait) || 5) * 1000);
}
}
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
window.AksrtFrontend.register('aksrt-auth', function (root) {
initGlobal();
initRoot(root);
initModal();
});
}
}()); }());
+63 -27
View File
@@ -13,14 +13,30 @@
(function () { (function () {
'use strict'; 'use strict';
var globalEventsBound = false;
function elementsIn(root, selector) {
var elements = [];
root = root || document;
if (root.nodeType === 1 && root.matches(selector)) elements.push(root);
if (root.querySelectorAll) root.querySelectorAll(selector).forEach(function (element) { elements.push(element); });
return elements;
}
function elementById(root, id) {
root = root || document;
if (root.nodeType === 1 && root.id === id) return root;
return root.querySelector ? root.querySelector('#' + id) : null;
}
function notifyLoadError() { function notifyLoadError() {
if (typeof notyf === 'function') { if (typeof notyf === 'function') {
notyf('加载失败,请重试', 'danger'); notyf('加载失败,请重试', 'danger');
} }
} }
function initPublicDashboards() { function initPublicDashboards(scope) {
document.querySelectorAll('.dashboard--public').forEach(function (root) { elementsIn(scope, '.dashboard--public').forEach(function (root) {
if (root.dataset.aksrtAuthorBound === '1') return; if (root.dataset.aksrtAuthorBound === '1') return;
root.dataset.aksrtAuthorBound = '1'; root.dataset.aksrtAuthorBound = '1';
@@ -138,13 +154,13 @@
}); });
} }
function initCoverUpload() { function initCoverUpload(root) {
var cfg = window.aksrtAuthorPage || {}; var cfg = window.aksrtAuthorPage || {};
var upload = cfg.coverUpload || null; var upload = cfg.coverUpload || null;
if (!upload || !upload.enabled || !upload.restUrl) return; if (!upload || !upload.enabled || !upload.restUrl) return;
var btn = document.getElementById(upload.buttonId || 'aksrt-upload-bg-btn'); var btn = elementById(root, upload.buttonId || 'aksrt-upload-bg-btn');
var input = document.getElementById(upload.inputId || 'aksrt-bg-file-input'); var input = elementById(root, upload.inputId || 'aksrt-bg-file-input');
if (!btn || !input || btn.dataset.aksrtCoverBound === '1') return; if (!btn || !input || btn.dataset.aksrtCoverBound === '1') return;
btn.dataset.aksrtCoverBound = '1'; btn.dataset.aksrtCoverBound = '1';
@@ -238,13 +254,13 @@
}); });
} }
function initAvatarUpload() { function initAvatarUpload(root) {
var upload = (window.aksrtUserCenter || {}).avatarUpload; var upload = (window.aksrtUserCenter || {}).avatarUpload;
if (!upload || !upload.enabled || !upload.restUrl) return; if (!upload || !upload.enabled || !upload.restUrl) return;
var button = document.getElementById(upload.buttonId || 'aksrt-upload-avatar-btn'); var button = elementById(root, upload.buttonId || 'aksrt-upload-avatar-btn');
var input = document.getElementById(upload.inputId || 'aksrt-avatar-file-input'); var input = elementById(root, upload.inputId || 'aksrt-avatar-file-input');
var image = document.getElementById(upload.imageId || 'aksrt-user-avatar-image'); var image = elementById(root, upload.imageId || 'aksrt-user-avatar-image');
if (!button || !input || button.dataset.aksrtAvatarBound === '1') return; if (!button || !input || button.dataset.aksrtAvatarBound === '1') return;
button.dataset.aksrtAvatarBound = '1'; button.dataset.aksrtAvatarBound = '1';
@@ -297,8 +313,8 @@
}); });
} }
function initCoverMenus() { function initCoverMenus(root) {
document.querySelectorAll('[data-aksrt-menu]').forEach(function (menu) { elementsIn(root, '[data-aksrt-menu]').forEach(function (menu) {
if (menu.dataset.aksrtMenuBound === '1') return; if (menu.dataset.aksrtMenuBound === '1') return;
menu.dataset.aksrtMenuBound = '1'; menu.dataset.aksrtMenuBound = '1';
var trigger = menu.querySelector('.aksrt-cover-menu__trigger'); var trigger = menu.querySelector('.aksrt-cover-menu__trigger');
@@ -320,34 +336,54 @@
} }
trigger.addEventListener('click', function () { panel.hidden ? open() : close(); }); trigger.addEventListener('click', function () { panel.hidden ? open() : close(); });
menu.addEventListener('keydown', function (event) { if (event.key === 'Escape') { close(); trigger.focus(); } }); menu.addEventListener('keydown', function (event) { if (event.key === 'Escape') { close(); trigger.focus(); } });
document.addEventListener('pointerdown', function (event) { if (!menu.contains(event.target)) close(); });
}); });
} }
function initProfileDetailsModal() { function initProfileDetailsModal(root) {
var modal = document.getElementById('aksrt-profile-details-modal'); var modal = elementById(root, 'aksrt-profile-details-modal');
if (!modal) return; if (!modal || modal.dataset.aksrtProfileModalBound === '1') return;
modal.dataset.aksrtProfileModalBound = '1';
var dialog = modal.querySelector('.aksrt-profile-details-modal__dialog'); var dialog = modal.querySelector('.aksrt-profile-details-modal__dialog');
var returnFocus = null; var returnFocus = null;
function close() { modal.hidden = true; if (returnFocus) returnFocus.focus(); } function close() { modal.hidden = true; if (returnFocus) returnFocus.focus(); }
document.querySelectorAll('[data-aksrt-profile-details]').forEach(function (button) { elementsIn(root, '[data-aksrt-profile-details]').forEach(function (button) {
button.addEventListener('click', function () { returnFocus = button; modal.hidden = false; if (dialog) dialog.focus(); }); button.addEventListener('click', function () { returnFocus = button; modal.hidden = false; if (dialog) dialog.focus(); });
}); });
modal.querySelectorAll('[data-aksrt-modal-close]').forEach(function (button) { button.addEventListener('click', close); }); modal.querySelectorAll('[data-aksrt-modal-close]').forEach(function (button) { button.addEventListener('click', close); });
document.addEventListener('keydown', function (event) { if (event.key === 'Escape' && !modal.hidden) close(); });
} }
function init() { function initGlobalEvents() {
initPublicDashboards(); if (globalEventsBound) return;
initCoverMenus(); globalEventsBound = true;
initProfileDetailsModal(); document.addEventListener('pointerdown', function (event) {
initCoverUpload(); document.querySelectorAll('[data-aksrt-menu]').forEach(function (menu) {
initAvatarUpload(); if (menu.contains(event.target)) return;
var panel = menu.querySelector('.aksrt-cover-menu__panel');
var trigger = menu.querySelector('.aksrt-cover-menu__trigger');
if (panel) panel.hidden = true;
if (trigger) trigger.setAttribute('aria-expanded', 'false');
});
});
document.addEventListener('keydown', function (event) {
if (event.key !== 'Escape') return;
var modal = document.getElementById('aksrt-profile-details-modal');
if (modal && !modal.hidden) {
var close = modal.querySelector('[data-aksrt-modal-close]');
if (close) close.click();
}
});
} }
if (document.readyState === 'loading') { function init(root) {
document.addEventListener('DOMContentLoaded', init); initGlobalEvents();
} else { initPublicDashboards(root);
init(); initCoverMenus(root);
initProfileDetailsModal(root);
initCoverUpload(root);
initAvatarUpload(root);
}
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
window.AksrtFrontend.register('aksrt-author', init);
} }
})(); })();
+7 -5
View File
@@ -9,9 +9,12 @@
if (window._win && window._win.ajax_url) return window._win.ajax_url; if (window._win && window._win.ajax_url) return window._win.ajax_url;
return '/wp-admin/admin-ajax.php'; return '/wp-admin/admin-ajax.php';
} }
function init() { function init(root) {
var form = document.querySelector('.aksrt-ban-appeal-form'); root = root || document;
if (!form) return; var form = root.nodeType === 1 && root.matches('.aksrt-ban-appeal-form')
? root : root.querySelector('.aksrt-ban-appeal-form');
if (!form || form.dataset.aksrtBanAppealBound === '1') return;
form.dataset.aksrtBanAppealBound = '1';
form.addEventListener('submit', function (e) { form.addEventListener('submit', function (e) {
e.preventDefault(); e.preventDefault();
var btn = form.querySelector('button[type="submit"]'); var btn = form.querySelector('button[type="submit"]');
@@ -32,6 +35,5 @@
.finally(function () { if (btn) btn.disabled = false; }); .finally(function () { if (btn) btn.disabled = false; });
}); });
} }
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); window.AksrtFrontend.register('aksrt-ban-appeal-page', init);
else init();
})(); })();
+436 -517
View File
File diff suppressed because one or more lines are too long
+6 -11
View File
@@ -121,8 +121,11 @@
TOKEN_FIELDS: TOKEN_FIELDS TOKEN_FIELDS: TOKEN_FIELDS
}); });
function scan() { function scan(root, reason) {
var mounts = document.querySelectorAll('[data-aksrt-captcha-config]'); var scope = root && root.querySelectorAll ? root : document;
var mounts = [];
if (scope.nodeType === 1 && scope.matches('[data-aksrt-captcha-config]')) mounts.push(scope);
Array.prototype.push.apply(mounts, scope.querySelectorAll('[data-aksrt-captcha-config]'));
Array.prototype.forEach.call(mounts, function (mount) { Array.prototype.forEach.call(mounts, function (mount) {
if (mount.dataset.aksrtCaptchaBound) return; if (mount.dataset.aksrtCaptchaBound) return;
var config; var config;
@@ -545,13 +548,5 @@
} }
} }
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', scan); window.AksrtFrontend.register('aksrt-captcha-front', scan);
else setTimeout(scan, 0);
if (window.MutationObserver) {
new MutationObserver(function (mutations) {
var shouldScan = mutations.some(function (mutation) { return mutation.addedNodes && mutation.addedNodes.length; });
if (shouldScan) scan();
}).observe(document.documentElement, { childList: true, subtree: true });
}
})(); })();
+7 -8
View File
@@ -99,7 +99,8 @@
} }
function processImage(img) { function processImage(img) {
if (img.dataset.aksrtColorDone) return; if (img.dataset.aksrtColorDone || img.dataset.aksrtColorBound) return;
img.dataset.aksrtColorBound = '1';
// Need crossOrigin for canvas access // Need crossOrigin for canvas access
if (!img.crossOrigin && img.src.indexOf(window.location.origin) !== 0) { if (!img.crossOrigin && img.src.indexOf(window.location.origin) !== 0) {
@@ -127,8 +128,10 @@
} }
} }
function init() { function init(root, reason) {
document.querySelectorAll('[data-aksrt-color-extract]').forEach(processImage); var scope = root && root.querySelectorAll ? root : document;
if (scope.nodeType === 1 && scope.matches('[data-aksrt-color-extract]')) processImage(scope);
scope.querySelectorAll('[data-aksrt-color-extract]').forEach(processImage);
} }
// Public API // Public API
@@ -139,9 +142,5 @@
init: init init: init
}; };
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-color-extract', init);
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})(); })();
+38 -26
View File
@@ -39,19 +39,6 @@
} }
} }
document.addEventListener('click', function (e) {
var trigger = e.target && e.target.closest ? e.target.closest('[fixed-input]') : null;
if (!trigger) return;
e.preventDefault();
var targetSel = trigger.getAttribute('fixed-input') || '#respond';
var panel = document.querySelector(targetSel);
var comments = document.getElementById('comments');
if (comments && comments.scrollIntoView) {
comments.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
FixedInput(panel, 'show');
});
// Autoheight textareas. // Autoheight textareas.
function bindAutoheight(ta) { function bindAutoheight(ta) {
if (!ta || ta.dataset.autoheightBound) return; if (!ta || ta.dataset.autoheightBound) return;
@@ -67,24 +54,49 @@
resize(); resize();
} }
function scan() { function scopeFor(root) {
document.querySelectorAll('textarea[autoheight], textarea.grin').forEach(bindAutoheight); return root && root.querySelectorAll ? root : document;
} }
document.addEventListener('DOMContentLoaded', scan); function scan(root) {
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') { var scope = scopeFor(root);
window.AksrtFrontend.register('aksrt-comment-fixed-input', function (root) { if (scope.nodeType === 1 && scope.matches('textarea[autoheight], textarea.grin')) {
scan(); bindAutoheight(scope);
// Expose for submit hide. }
root = root || document; scope.querySelectorAll('textarea[autoheight], textarea.grin').forEach(bindAutoheight);
var respond = root.querySelector ? root.querySelector('#respond') : document.getElementById('respond'); }
if (respond) {
respond.aksrtFixedInput = function (action) { var globalsBound = false;
FixedInput(respond, action); function bindGlobals() {
}; if (globalsBound) return;
globalsBound = true;
document.addEventListener('click', function (e) {
var trigger = e.target && e.target.closest ? e.target.closest('[fixed-input]') : null;
if (!trigger) return;
e.preventDefault();
var targetSel = trigger.getAttribute('fixed-input') || '#respond';
var panel = document.querySelector(targetSel);
var comments = document.getElementById('comments');
if (comments && comments.scrollIntoView) {
comments.scrollIntoView({ behavior: 'smooth', block: 'start' });
} }
FixedInput(panel, 'show');
}); });
} }
window.AksrtFrontend.register('aksrt-comment-fixed-input', function (root, reason) {
bindGlobals();
scan(root);
root = root || document;
var respond = root.nodeType === 1 && root.matches && root.matches('#respond')
? root
: (root.querySelector ? root.querySelector('#respond') : document.getElementById('respond'));
if (respond) {
respond.aksrtFixedInput = function (action) {
FixedInput(respond, action);
};
}
});
window.AksrtFixedInput = FixedInput; window.AksrtFixedInput = FixedInput;
})(); })();
+48 -59
View File
@@ -4,6 +4,18 @@
(function () { (function () {
"use strict"; "use strict";
var globalsBound = false;
function scopeFor(root) {
return root && root.querySelectorAll ? root : document;
}
function eachMatch(root, selector, callback) {
var scope = scopeFor(root);
if (scope.nodeType === 1 && scope.matches(selector)) callback(scope);
scope.querySelectorAll(selector).forEach(callback);
}
function renderGradientBackground(container) { function renderGradientBackground(container) {
if (!container || container.dataset.aksrtGradientState || typeof window.Grade !== "function") return; if (!container || container.dataset.aksrtGradientState || typeof window.Grade !== "function") return;
@@ -32,26 +44,15 @@
} }
function initGradientBackgrounds(root) { function initGradientBackgrounds(root) {
var scope = root || document; eachMatch(root, ".gradient-bg", renderGradientBackground);
if (scope.nodeType === 1 && scope.matches(".gradient-bg")) {
renderGradientBackground(scope);
}
scope.querySelectorAll(".gradient-bg").forEach(renderGradientBackground);
} }
document.addEventListener("lazyloaded", function (event) {
if (!(event.target instanceof Element) || !event.target.matches(".gradient-bg img")) return;
renderGradientBackground(event.target.closest(".gradient-bg"));
}, true);
function initPostThumbMedia(root) { function initPostThumbMedia(root) {
var scope = root || document; eachMatch(root, "[data-post-thumb-video]", function (box) {
scope.querySelectorAll("[data-post-thumb-video]").forEach(function (box) {
if (box.dataset.aksrtThumbVideoBound === "1") return; if (box.dataset.aksrtThumbVideoBound === "1") return;
box.dataset.aksrtThumbVideoBound = "1";
var video = box.querySelector("video"); var video = box.querySelector("video");
if (!video) return; if (!video) return;
box.dataset.aksrtThumbVideoBound = "1";
function loadVideo() { function loadVideo() {
if (!video.getAttribute("src") && video.dataset.src) { if (!video.getAttribute("src") && video.dataset.src) {
@@ -82,25 +83,17 @@
}); });
} }
window.AksrtFrontend.register("aksrt-post-thumb-media", function (root) { function initSlideCovers(root) {
initPostThumbMedia(root); eachMatch(root, ".single-cover--slide", function (cover) {
initGradientBackgrounds(root); if (cover.dataset.aksrtSlideCoverBound === "1") return;
});
})();
(function () {
"use strict";
function initSlideCovers() {
var covers = document.querySelectorAll(".single-cover--slide");
covers.forEach(function (cover) {
var slides = cover.querySelectorAll(".single-cover__slide"); var slides = cover.querySelectorAll(".single-cover__slide");
var dots = cover.querySelectorAll(".single-cover__dot"); var dots = cover.querySelectorAll(".single-cover__dot");
if (slides.length < 2) return; if (slides.length < 2) return;
cover.dataset.aksrtSlideCoverBound = "1";
var current = 0; var current = 0;
var interval = 5000; var interval = 5000;
var timer = null;
function goTo(idx) { function goTo(idx) {
slides[current].classList.remove("is-active"); slides[current].classList.remove("is-active");
@@ -110,33 +103,32 @@
if (dots[current]) dots[current].classList.add("is-active"); if (dots[current]) dots[current].classList.add("is-active");
} }
var timer = setInterval(function () { function startTimer() {
goTo((current + 1) % slides.length); timer = window.setInterval(function () {
}, interval); goTo((current + 1) % slides.length);
}, interval);
}
startTimer();
dots.forEach(function (dot, i) { dots.forEach(function (dot, i) {
dot.addEventListener("click", function () { dot.addEventListener("click", function () {
clearInterval(timer); window.clearInterval(timer);
goTo(i); goTo(i);
timer = setInterval(function () { startTimer();
goTo((current + 1) % slides.length);
}, interval);
}); });
}); });
}); });
} }
if (document.readyState === "loading") { function bindGlobals() {
document.addEventListener("DOMContentLoaded", initSlideCovers); if (globalsBound) return;
} else { globalsBound = true;
initSlideCovers();
}
})();
(function () { document.addEventListener("lazyloaded", function (event) {
"use strict"; if (!(event.target instanceof Element) || !event.target.matches(".gradient-bg img")) return;
renderGradientBackground(event.target.closest(".gradient-bg"));
}, true);
function initHiddenContent() {
document.addEventListener("click", function (e) { document.addEventListener("click", function (e) {
var btn = e.target.closest(".aksrt-hidden-content__submit-btn"); var btn = e.target.closest(".aksrt-hidden-content__submit-btn");
if (!btn) return; if (!btn) return;
@@ -157,7 +149,7 @@
var formData = new FormData(); var formData = new FormData();
formData.append("action", "aksrt_check_visibility"); formData.append("action", "aksrt_check_visibility");
formData.append("_nonce", (window.aksrtData && aksrtData.visibilityNonce) || ""); formData.append("_nonce", (window.aksrtData && window.aksrtData.visibilityNonce) || "");
formData.append("mode", mode); formData.append("mode", mode);
formData.append("pid", pid); formData.append("pid", pid);
if (password) formData.append("password", password); if (password) formData.append("password", password);
@@ -165,7 +157,7 @@
btn.disabled = true; btn.disabled = true;
btn.textContent = "..."; btn.textContent = "...";
fetch((window.aksrtData && aksrtData.ajaxUrl) || "/wp-admin/admin-ajax.php", { fetch((window.aksrtData && window.aksrtData.ajaxUrl) || "/wp-admin/admin-ajax.php", {
method: "POST", method: "POST",
body: formData, body: formData,
credentials: "same-origin", credentials: "same-origin",
@@ -188,22 +180,19 @@
}); });
}); });
// Handle Enter key on password input
document.addEventListener("keydown", function (e) { document.addEventListener("keydown", function (e) {
if (e.key === "Enter" && e.target.classList.contains("aksrt-hidden-content__password-input")) { if (e.key !== "Enter" || !e.target.classList.contains("aksrt-hidden-content__password-input")) return;
e.preventDefault(); e.preventDefault();
var wrapper = e.target.closest(".aksrt-hidden-content--locked"); var wrapper = e.target.closest(".aksrt-hidden-content--locked");
if (wrapper) { var btn = wrapper ? wrapper.querySelector(".aksrt-hidden-content__submit-btn") : null;
var btn = wrapper.querySelector(".aksrt-hidden-content__submit-btn"); if (btn) btn.click();
if (btn) btn.click();
}
}
}); });
} }
if (document.readyState === "loading") { window.AksrtFrontend.register("aksrt-misc", function (root, reason) {
document.addEventListener("DOMContentLoaded", initHiddenContent); bindGlobals();
} else { initPostThumbMedia(root);
initHiddenContent(); initGradientBackgrounds(root);
} initSlideCovers(root);
});
})(); })();
+68 -55
View File
@@ -15,6 +15,21 @@
return; return;
} }
var activePoll = null; var activePoll = null;
var balancesRefreshed = false;
function elementsIn(root, selector) {
var elements = [];
root = root || document;
if (root.nodeType === 1 && root.matches(selector)) elements.push(root);
if (root.querySelectorAll) root.querySelectorAll(selector).forEach(function (element) { elements.push(element); });
return elements;
}
function firstIn(root, selector) {
root = root || document;
if (root.nodeType === 1 && root.matches(selector)) return root;
return root.querySelector ? root.querySelector(selector) : null;
}
function operationKey(owner) { function operationKey(owner) {
if (!owner.dataset.aksrtOperationKey) { if (!owner.dataset.aksrtOperationKey) {
@@ -89,8 +104,12 @@
} }
// Task-list "去签到" → scroll + click the real asset-card sign-in button. // Task-list "去签到" → scroll + click the real asset-card sign-in button.
function initSigninGoto() { function initSigninGoto(root) {
document.querySelectorAll('[data-aksrt-signin-goto]').forEach(function (el) { root = root || document;
var items = [];
if (root.nodeType === 1 && root.matches('[data-aksrt-signin-goto]')) items.push(root);
if (root.querySelectorAll) root.querySelectorAll('[data-aksrt-signin-goto]').forEach(function (el) { items.push(el); });
items.forEach(function (el) {
if (el.dataset.aksrtSigninGotoInitialized === '1') return; if (el.dataset.aksrtSigninGotoInitialized === '1') return;
el.dataset.aksrtSigninGotoInitialized = '1'; el.dataset.aksrtSigninGotoInitialized = '1';
el.addEventListener('click', function (e) { el.addEventListener('click', function (e) {
@@ -113,8 +132,8 @@
} }
// ===== Forward-only affiliate revenue center ===== // ===== Forward-only affiliate revenue center =====
function initAffiliateRevenue() { function initAffiliateRevenue(scope) {
var root = document.querySelector('[data-aksrt-revenue-page]'); var root = firstIn(scope, '[data-aksrt-revenue-page]');
if (!root || root.dataset.affiliateBound) return; if (!root || root.dataset.affiliateBound) return;
root.dataset.affiliateBound = '1'; root.dataset.affiliateBound = '1';
var input = root.querySelector('[data-affiliate-link]'); var input = root.querySelector('[data-affiliate-link]');
@@ -149,9 +168,10 @@
// ===== VIP 积分兑换 ===== // ===== VIP 积分兑换 =====
function initVipPointsExchange() { function initVipPointsExchange(scope) {
var root = document.querySelector('[data-aksrt-vip-points]'); var root = firstIn(scope, '[data-aksrt-vip-points]');
if (!root) return; if (!root || root.dataset.aksrtVipPointsBound === '1') return;
root.dataset.aksrtVipPointsBound = '1';
var tip = root.querySelector('[data-aksrt-vip-points-tip]'); var tip = root.querySelector('[data-aksrt-vip-points-tip]');
var busy = false; var busy = false;
var coordinator = window.AksrtVipPurchaseCoordinator; var coordinator = window.AksrtVipPurchaseCoordinator;
@@ -191,9 +211,10 @@
} }
// ===== VIP 开通 ===== // ===== VIP 开通 =====
function initVipShop() { function initVipShop(scope) {
var root = document.querySelector('[data-aksrt-vip-shop]'); var root = firstIn(scope, '[data-aksrt-vip-shop]');
if (!root) return; if (!root || root.dataset.aksrtVipShopBound === '1') return;
root.dataset.aksrtVipShopBound = '1';
var tip = root.querySelector('[data-aksrt-vip-tip]'); var tip = root.querySelector('[data-aksrt-vip-tip]');
var busy = false; var busy = false;
var coordinator = window.AksrtVipPurchaseCoordinator; var coordinator = window.AksrtVipPurchaseCoordinator;
@@ -265,11 +286,13 @@
} }
// ===== 卡密兑换(余额 / 会员) ===== // ===== 卡密兑换(余额 / 会员) =====
function initCardPass() { function initCardPass(scope) {
var roots = document.querySelectorAll('[data-aksrt-card-pass]'); var roots = elementsIn(scope, '[data-aksrt-card-pass]');
if (!roots || !roots.length) return; if (!roots || !roots.length) return;
Array.prototype.forEach.call(roots, function (root) { Array.prototype.forEach.call(roots, function (root) {
if (root.dataset.aksrtCardPassBound === '1') return;
root.dataset.aksrtCardPassBound = '1';
var tip = root.querySelector('[data-aksrt-card-tip]'); var tip = root.querySelector('[data-aksrt-card-tip]');
var busy = false; var busy = false;
resetOperationKeyOnChange(root); resetOperationKeyOnChange(root);
@@ -380,8 +403,8 @@
return handle; return handle;
} }
function initBalanceCharge() { function initBalanceCharge(scope) {
var modal = document.querySelector('[data-aksrt-charge-modal]'); var modal = firstIn(scope, '[data-aksrt-charge-modal]');
if (!modal || modal.dataset.aksrtChargeInitialized === '1') return; if (!modal || modal.dataset.aksrtChargeInitialized === '1') return;
modal.dataset.aksrtChargeInitialized = '1'; modal.dataset.aksrtChargeInitialized = '1';
var form = modal.querySelector('[data-aksrt-charge-form]'); var form = modal.querySelector('[data-aksrt-charge-form]');
@@ -458,8 +481,8 @@
}); });
} }
function initTransfer() { function initTransfer(scope) {
var modal = document.querySelector('[data-aksrt-transfer-modal]'); var modal = firstIn(scope, '[data-aksrt-transfer-modal]');
if (!modal || modal.dataset.aksrtTransferInitialized === '1') return; if (!modal || modal.dataset.aksrtTransferInitialized === '1') return;
modal.dataset.aksrtTransferInitialized = '1'; modal.dataset.aksrtTransferInitialized = '1';
var form = modal.querySelector('[data-aksrt-transfer-form]'); var form = modal.querySelector('[data-aksrt-transfer-form]');
@@ -590,8 +613,8 @@
}); });
} }
function initPointsExchange() { function initPointsExchange(scope) {
var modal = document.querySelector('[data-aksrt-points-exchange-modal]'); var modal = firstIn(scope, '[data-aksrt-points-exchange-modal]');
if (!modal || modal.dataset.aksrtPointsExchangeInitialized === '1') return; if (!modal || modal.dataset.aksrtPointsExchangeInitialized === '1') return;
modal.dataset.aksrtPointsExchangeInitialized = '1'; modal.dataset.aksrtPointsExchangeInitialized = '1';
var form = modal.querySelector('[data-aksrt-points-exchange-form]'); var form = modal.querySelector('[data-aksrt-points-exchange-form]');
@@ -650,8 +673,8 @@
}); });
} }
function initWithdraw() { function initWithdraw(scope) {
var modal = document.querySelector('[data-aksrt-withdraw-modal]'); var modal = firstIn(scope, '[data-aksrt-withdraw-modal]');
if (!modal || modal.dataset.aksrtWithdrawInitialized === '1') return; if (!modal || modal.dataset.aksrtWithdrawInitialized === '1') return;
modal.dataset.aksrtWithdrawInitialized = '1'; modal.dataset.aksrtWithdrawInitialized = '1';
var form = modal.querySelector('[data-aksrt-withdraw-form]'); var form = modal.querySelector('[data-aksrt-withdraw-form]');
@@ -735,27 +758,28 @@
}); });
} }
function initAssetTabs() { function initAssetTabs(root) {
var nav = document.querySelectorAll('[data-aksrt-asset-tab]'); var nav = elementsIn(root, '[data-aksrt-asset-tab]');
if (!nav.length) return; if (!nav.length) return;
var scope = root || document;
nav.forEach(function (link) { nav.forEach(function (link) {
if (link.dataset.aksrtAssetTabBound === 'true') return; if (link.dataset.aksrtAssetTabBound === 'true') return;
link.dataset.aksrtAssetTabBound = 'true'; link.dataset.aksrtAssetTabBound = 'true';
link.addEventListener('click', function (e) { link.addEventListener('click', function (e) {
e.preventDefault(); e.preventDefault();
var key = link.getAttribute('data-aksrt-asset-tab'); var key = link.getAttribute('data-aksrt-asset-tab');
document.querySelectorAll('.uc-asset-tabs__nav [data-aksrt-asset-tab]').forEach(function (a) { elementsIn(scope, '[data-aksrt-asset-tab]').forEach(function (a) {
var li = a.closest('li'); var li = a.closest('li');
if (!li) return; if (!li) return;
var aKey = a.getAttribute('data-aksrt-asset-tab'); var aKey = a.getAttribute('data-aksrt-asset-tab');
var on = aKey === key || (key === 'points_date' && aKey === 'points_free'); var on = aKey === key || (key === 'points_date' && aKey === 'points_free');
li.classList.toggle('active', on); li.classList.toggle('active', on);
}); });
var tabs = document.querySelector('.uc-asset-tabs'); var tabs = firstIn(scope, '.uc-asset-tabs');
if (tabs && typeof tabs.scrollIntoView === 'function') { if (tabs && typeof tabs.scrollIntoView === 'function') {
tabs.scrollIntoView({ behavior: 'smooth', block: 'start' }); tabs.scrollIntoView({ behavior: 'smooth', block: 'start' });
} }
document.querySelectorAll('[data-aksrt-asset-pane]').forEach(function (pane) { elementsIn(scope, '[data-aksrt-asset-pane]').forEach(function (pane) {
var on = pane.getAttribute('data-aksrt-asset-pane') === key; var on = pane.getAttribute('data-aksrt-asset-pane') === key;
pane.classList.toggle('is-active', on); pane.classList.toggle('is-active', on);
pane.hidden = !on; pane.hidden = !on;
@@ -765,8 +789,8 @@
} }
function initWithdrawHistory() { function initWithdrawHistory(scope) {
var modal = document.querySelector('[data-aksrt-withdraw-history-modal]'); var modal = firstIn(scope, '[data-aksrt-withdraw-history-modal]');
if (!modal || modal.dataset.aksrtWithdrawHistoryInitialized === '1') return; if (!modal || modal.dataset.aksrtWithdrawHistoryInitialized === '1') return;
modal.dataset.aksrtWithdrawHistoryInitialized = '1'; modal.dataset.aksrtWithdrawHistoryInitialized = '1';
function open() { modal.hidden = false; document.body.classList.add('uc-modal-open'); } function open() { modal.hidden = false; document.body.classList.add('uc-modal-open'); }
@@ -785,36 +809,25 @@
refresh: refreshAssetBalances refresh: refreshAssetBalances
}; };
function bootAssets() { function bootAssets(root, reason) {
refreshAssetBalances(); if (!balancesRefreshed && reason === 'init') {
initSigninGoto(); balancesRefreshed = true;
initAffiliateRevenue(); refreshAssetBalances();
initVipShop(); }
initVipPointsExchange(); initSigninGoto(root);
initCardPass(); initAffiliateRevenue(root);
initBalanceCharge(); initVipShop(root);
initTransfer(); initVipPointsExchange(root);
initPointsExchange(); initCardPass(root);
initWithdraw(); initBalanceCharge(root);
initWithdrawHistory(); initTransfer(root);
initAssetTabs(); initPointsExchange(root);
} initWithdraw(root);
initWithdrawHistory(root);
if (document.readyState === 'loading') { initAssetTabs(root);
document.addEventListener('DOMContentLoaded', bootAssets);
} else {
bootAssets();
} }
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') { if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
window.AksrtFrontend.register('credits', function () { window.AksrtFrontend.register('credits', bootAssets);
initSigninGoto();
initBalanceCharge();
initTransfer();
initPointsExchange();
initWithdraw();
initWithdrawHistory();
initAssetTabs();
});
} }
})(); })();
+9 -8
View File
@@ -18,9 +18,12 @@
searchInput: null, searchInput: null,
items: [], items: [],
init: function () { init: function (root) {
this.sidebar = document.querySelector('.aksrt-doc-sidebar'); root = root || document;
if (!this.sidebar) return; this.sidebar = root.nodeType === 1 && root.matches('.aksrt-doc-sidebar')
? root : root.querySelector('.aksrt-doc-sidebar');
if (!this.sidebar || this.sidebar.dataset.aksrtDocsNavBound === '1') return;
this.sidebar.dataset.aksrtDocsNavBound = '1';
this.items = Array.from(this.sidebar.querySelectorAll('.aksrt-doc-sidebar__item')); this.items = Array.from(this.sidebar.querySelectorAll('.aksrt-doc-sidebar__item'));
this.searchInput = this.sidebar.querySelector('.aksrt-doc-sidebar__search-input'); this.searchInput = this.sidebar.querySelector('.aksrt-doc-sidebar__search-input');
@@ -129,9 +132,7 @@
}, },
}; };
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-docs-navigation', function (root) {
document.addEventListener('DOMContentLoaded', function () { DocsNav.init(); }); DocsNav.init(root);
} else { });
DocsNav.init();
}
})(); })();
+13 -8
View File
@@ -13,7 +13,8 @@
'use strict'; 'use strict';
var DownloadStatus = { var DownloadStatus = {
init: function () { init: function (root) {
this.root = root || document;
this.initFilterHighlight(); this.initFilterHighlight();
this.initCardHover(); this.initCardHover();
}, },
@@ -22,10 +23,13 @@
* Animate filter button active state * Animate filter button active state
*/ */
initFilterHighlight: function () { initFilterHighlight: function () {
var filters = document.querySelectorAll('.aksrt-download-hero__filter'); var filters = this.root.nodeType === 1 && this.root.matches('.aksrt-download-hero__filter')
? [this.root] : this.root.querySelectorAll('.aksrt-download-hero__filter');
if (filters.length === 0) return; if (filters.length === 0) return;
filters.forEach(function (btn) { filters.forEach(function (btn) {
if (btn.dataset.aksrtDownloadHoverBound === '1') return;
btn.dataset.aksrtDownloadHoverBound = '1';
btn.addEventListener('mouseenter', function () { btn.addEventListener('mouseenter', function () {
btn.style.transform = 'translateY(-1px)'; btn.style.transform = 'translateY(-1px)';
}); });
@@ -39,10 +43,13 @@
* Card hover depth effect * Card hover depth effect
*/ */
initCardHover: function () { initCardHover: function () {
var cards = document.querySelectorAll('.aksrt-download-card'); var cards = this.root.nodeType === 1 && this.root.matches('.aksrt-download-card')
? [this.root] : this.root.querySelectorAll('.aksrt-download-card');
if (cards.length === 0) return; if (cards.length === 0) return;
cards.forEach(function (card) { cards.forEach(function (card) {
if (card.dataset.aksrtDownloadHoverBound === '1') return;
card.dataset.aksrtDownloadHoverBound = '1';
card.addEventListener('mouseenter', function () { card.addEventListener('mouseenter', function () {
card.style.transform = 'translateY(-2px)'; card.style.transform = 'translateY(-2px)';
}); });
@@ -53,9 +60,7 @@
}, },
}; };
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-download-status', function (root) {
document.addEventListener('DOMContentLoaded', function () { DownloadStatus.init(); }); DownloadStatus.init(root);
} else { });
DownloadStatus.init();
}
})(); })();
+91 -114
View File
@@ -10,108 +10,101 @@
'use strict'; 'use strict';
var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var countUpObserver = null;
var countdownElements = [];
var countdownTimer = null;
var fpsDetectionStarted = false;
/* ============================================================ function scopeFor(root) {
* 1. 数字递增 (count-up) — IntersectionObserver 触发 return root && root.querySelectorAll ? root : document;
* 用法:<span data-aksrt-countup data-target="1234" data-duration="2000">0</span> }
* ============================================================ */
function initCountUp() {
if (reducedMotion) return;
if (!('IntersectionObserver' in window)) return;
var els = document.querySelectorAll('[data-aksrt-countup]'); function eachMatch(root, selector, callback) {
if (!els.length) return; var scope = scopeFor(root);
if (scope.nodeType === 1 && scope.matches(selector)) callback(scope);
scope.querySelectorAll(selector).forEach(callback);
}
var observer = new IntersectionObserver(function (entries) { function animateCountUp(el) {
entries.forEach(function (entry) { if (el.dataset.aksrtCountupBound === '1') return;
if (!entry.isIntersecting) return; el.dataset.aksrtCountupBound = '1';
var el = entry.target; countUpObserver.observe(el);
observer.unobserve(el); }
var target = parseFloat(el.dataset.target || '0'); function initCountUp(root) {
var duration = parseInt(el.dataset.duration || '2000', 10); if (reducedMotion || !('IntersectionObserver' in window)) return;
var decimals = (el.dataset.decimals || '0|').split('|'); if (!countUpObserver) {
var prefix = decimals[1] || ''; countUpObserver = new IntersectionObserver(function (entries) {
var dec = parseInt(decimals[0], 10) || 0; entries.forEach(function (entry) {
var start = 0; if (!entry.isIntersecting) return;
var startTime = null; var el = entry.target;
countUpObserver.unobserve(el);
var target = parseFloat(el.dataset.target || '0');
var duration = parseInt(el.dataset.duration || '2000', 10);
var decimals = (el.dataset.decimals || '0|').split('|');
var prefix = decimals[1] || '';
var dec = parseInt(decimals[0], 10) || 0;
var startTime = null;
function step(timestamp) { function step(timestamp) {
if (!startTime) startTime = timestamp; if (!startTime) startTime = timestamp;
var progress = Math.min((timestamp - startTime) / duration, 1); var progress = Math.min((timestamp - startTime) / duration, 1);
// ease-out cubic var current = target * (1 - Math.pow(1 - progress, 3));
var eased = 1 - Math.pow(1 - progress, 3); el.textContent = prefix + current.toFixed(dec).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
var current = start + (target - start) * eased; if (progress < 1) requestAnimationFrame(step);
el.textContent = prefix + current.toFixed(dec).replace(/\B(?=(\d{3})+(?!\d))/g, ','); else el.textContent = prefix + target.toFixed(dec).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
if (progress < 1) {
requestAnimationFrame(step);
} else {
el.textContent = prefix + target.toFixed(dec).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
} }
}
requestAnimationFrame(step); requestAnimationFrame(step);
}); });
}, { threshold: 0.3 }); }, { threshold: 0.3 });
els.forEach(function (el) { observer.observe(el); });
}
/* ============================================================
* 2. 倒计时组件
* 用法:<span data-aksrt-countdown data-end="2026-12-31T23:59:59">--:--:--</span>
* ============================================================ */
function initCountdown() {
var els = document.querySelectorAll('[data-aksrt-countdown]');
if (!els.length) return;
function pad(n) { return n < 10 ? '0' + n : '' + n; }
function update() {
var now = Date.now();
els.forEach(function (el) {
var end = new Date(el.dataset.end).getTime();
var diff = end - now;
if (diff <= 0) {
el.textContent = el.dataset.expired || '已结束';
return;
}
var d = Math.floor(diff / 86400000);
var h = Math.floor((diff % 86400000) / 3600000);
var m = Math.floor((diff % 3600000) / 60000);
var s = Math.floor((diff % 60000) / 1000);
var format = el.dataset.format || 'dhms';
if (format === 'dhms') {
el.textContent = (d > 0 ? d + '天 ' : '') + pad(h) + ':' + pad(m) + ':' + pad(s);
} else if (format === 'hms') {
el.textContent = pad(h + d * 24) + ':' + pad(m) + ':' + pad(s);
} else {
el.textContent = (d > 0 ? d + '天 ' : '') + pad(h) + '时' + pad(m) + '分' + pad(s) + '秒';
}
});
} }
eachMatch(root, '[data-aksrt-countup]', animateCountUp);
update();
setInterval(update, 1000);
} }
/* ============================================================ function pad(n) { return n < 10 ? '0' + n : '' + n; }
* 4. 3D 悬停倾斜 (vanilla tilt)
* 用法:<div data-aksrt-tilt data-tilt-max="15">...</div>
* ============================================================ */
function initTilt() {
if (reducedMotion) return;
document.querySelectorAll('[data-aksrt-tilt]').forEach(function (el) { function updateCountdowns() {
var now = Date.now();
countdownElements = countdownElements.filter(function (el) { return document.documentElement.contains(el); });
countdownElements.forEach(function (el) {
var end = new Date(el.dataset.end).getTime();
var diff = end - now;
if (diff <= 0) {
el.textContent = el.dataset.expired || '已结束';
return;
}
var d = Math.floor(diff / 86400000);
var h = Math.floor((diff % 86400000) / 3600000);
var m = Math.floor((diff % 3600000) / 60000);
var s = Math.floor((diff % 60000) / 1000);
var format = el.dataset.format || 'dhms';
if (format === 'dhms') el.textContent = (d > 0 ? d + '天 ' : '') + pad(h) + ':' + pad(m) + ':' + pad(s);
else if (format === 'hms') el.textContent = pad(h + d * 24) + ':' + pad(m) + ':' + pad(s);
else el.textContent = (d > 0 ? d + '天 ' : '') + pad(h) + '时' + pad(m) + '分' + pad(s) + '秒';
});
}
function initCountdown(root) {
eachMatch(root, '[data-aksrt-countdown]', function (el) {
if (el.dataset.aksrtCountdownBound === '1') return;
el.dataset.aksrtCountdownBound = '1';
countdownElements.push(el);
});
updateCountdowns();
if (!countdownTimer && countdownElements.length) countdownTimer = window.setInterval(updateCountdowns, 1000);
}
function initTilt(root) {
if (reducedMotion) return;
eachMatch(root, '[data-aksrt-tilt]', function (el) {
if (el.dataset.tiltBound) return; if (el.dataset.tiltBound) return;
el.dataset.tiltBound = '1'; el.dataset.tiltBound = '1';
var maxDeg = parseFloat(el.dataset.tiltMax || '10'); var maxDeg = parseFloat(el.dataset.tiltMax || '10');
var perspective = parseFloat(el.dataset.tiltPerspective || '800'); var perspective = parseFloat(el.dataset.tiltPerspective || '800');
var scale = parseFloat(el.dataset.tiltScale || '1.03'); var scale = parseFloat(el.dataset.tiltScale || '1.03');
var glare = el.dataset.tiltGlare === '1'; var glare = el.dataset.tiltGlare === '1';
var glareEl = null; var glareEl = null;
if (glare) { if (glare) {
glareEl = document.createElement('div'); glareEl = document.createElement('div');
glareEl.style.cssText = 'position:absolute;inset:0;border-radius:inherit;pointer-events:none;background:linear-gradient(135deg,rgba(255,255,255,0) 0%,rgba(255,255,255,0.25) 100%);opacity:0;transition:opacity 0.2s;'; glareEl.style.cssText = 'position:absolute;inset:0;border-radius:inherit;pointer-events:none;background:linear-gradient(135deg,rgba(255,255,255,0) 0%,rgba(255,255,255,0.25) 100%);opacity:0;transition:opacity 0.2s;';
@@ -119,17 +112,13 @@
el.style.overflow = 'hidden'; el.style.overflow = 'hidden';
el.appendChild(glareEl); el.appendChild(glareEl);
} }
el.addEventListener('mousemove', function (e) { el.addEventListener('mousemove', function (e) {
var rect = el.getBoundingClientRect(); var rect = el.getBoundingClientRect();
var x = (e.clientX - rect.left) / rect.width; var x = (e.clientX - rect.left) / rect.width;
var y = (e.clientY - rect.top) / rect.height; var y = (e.clientY - rect.top) / rect.height;
var rotateX = (0.5 - y) * maxDeg; el.style.transform = 'perspective(' + perspective + 'px) rotateX(' + ((0.5 - y) * maxDeg) + 'deg) rotateY(' + ((x - 0.5) * maxDeg) + 'deg) scale3d(' + scale + ',' + scale + ',' + scale + ')';
var rotateY = (x - 0.5) * maxDeg;
el.style.transform = 'perspective(' + perspective + 'px) rotateX(' + rotateX + 'deg) rotateY(' + rotateY + 'deg) scale3d(' + scale + ',' + scale + ',' + scale + ')';
if (glareEl) glareEl.style.opacity = '1'; if (glareEl) glareEl.style.opacity = '1';
}); });
el.addEventListener('mouseleave', function () { el.addEventListener('mouseleave', function () {
el.style.transform = 'perspective(' + perspective + 'px) rotateX(0deg) rotateY(0deg) scale3d(1,1,1)'; el.style.transform = 'perspective(' + perspective + 'px) rotateX(0deg) rotateY(0deg) scale3d(1,1,1)';
if (glareEl) glareEl.style.opacity = '0'; if (glareEl) glareEl.style.opacity = '0';
@@ -137,42 +126,30 @@
}); });
} }
/* ============================================================ function initFpsDetection(root, reason) {
* 5. FPS 检测 + 低端设备降级 if (fpsDetectionStarted || !('requestAnimationFrame' in window)) return;
* ============================================================ */ fpsDetectionStarted = true;
function initFpsDetection() {
if (!('requestAnimationFrame' in window)) return;
var frames = 0; var frames = 0;
var startTime = performance.now(); var startTime = performance.now();
function countFrames() { function countFrames() {
frames++; frames++;
var elapsed = performance.now() - startTime; var elapsed = performance.now() - startTime;
if (elapsed >= 1000) { if (elapsed >= 1000) {
var fps = Math.round(frames * 1000 / elapsed); if (Math.round(frames * 1000 / elapsed) < 24 && document.body) document.body.classList.add('fps-accelerat');
if (fps < 24) { return;
document.body.classList.add('fps-accelerat');
}
return; // 只检测一次
} }
requestAnimationFrame(countFrames); requestAnimationFrame(countFrames);
} }
requestAnimationFrame(countFrames); requestAnimationFrame(countFrames);
} }
/* ============================================================ function init(root, reason) {
* 初始化入口 initCountUp(root);
* ============================================================ */ initCountdown(root);
document.addEventListener('DOMContentLoaded', function () { if (typeof initMarquee === 'function') initMarquee(root, reason);
initCountUp(); initTilt(root);
initCountdown(); initFpsDetection(root, reason);
if (typeof initMarquee === 'function') { }
initMarquee();
}
initTilt();
initFpsDetection();
});
window.AksrtFrontend.register('aksrt-effects', init);
})(); })();
+11 -5
View File
@@ -21,6 +21,8 @@
var isAutoplay = false; var isAutoplay = false;
var lastTrigger = null; var lastTrigger = null;
var previousBodyOverflow = ''; var previousBodyOverflow = '';
var globalsBound = false;
var scrollDirectionBound = false;
var EXCLUDE_SEL = '.no-imgbox,.no-lightbox,.avatar,.img-icon,.avatar-badge,.aksrt-avatar,[data-no-lightbox],[data-aksrt-lightbox="0"]'; var EXCLUDE_SEL = '.no-imgbox,.no-lightbox,.avatar,.img-icon,.avatar-badge,.aksrt-avatar,[data-no-lightbox],[data-aksrt-lightbox="0"]';
var TRIGGER_SEL = '[data-aksrt-lightbox="1"] img,.aksrt-lightbox-trigger img,.imgbox-container img,.comment-content .box-img'; var TRIGGER_SEL = '[data-aksrt-lightbox="1"] img,.aksrt-lightbox-trigger img,.imgbox-container img,.comment-content .box-img';
var ANCHOR_TRIGGER_SEL = 'a[data-aksrt-lightbox-trigger]'; var ANCHOR_TRIGGER_SEL = 'a[data-aksrt-lightbox-trigger]';
@@ -496,7 +498,9 @@
return openLightbox(item.fullSrc, item.caption, gallery.items, gallery.index, img); return openLightbox(item.fullSrc, item.caption, gallery.items, gallery.index, img);
} }
function initLightbox() { function initLightbox(root, reason) {
if (globalsBound) return;
globalsBound = true;
document.addEventListener('click', function (event) { document.addEventListener('click', function (event) {
if (!lightboxEnabled() || !(event.target instanceof Element)) return; if (!lightboxEnabled() || !(event.target instanceof Element)) return;
@@ -527,7 +531,9 @@
// Scroll direction is handled by aksrt-runtime.js. Keep the prefixed // Scroll direction is handled by aksrt-runtime.js. Keep the prefixed
// classes in sync for the remaining CSS consumers. // classes in sync for the remaining CSS consumers.
function initScrollDirection() { function initScrollDirection(root, reason) {
if (scrollDirectionBound || !document.body) return;
scrollDirectionBound = true;
var body = document.body; var body = document.body;
function sync() { function sync() {
@@ -547,8 +553,8 @@
}, { passive: true }); }, { passive: true });
} }
document.addEventListener('DOMContentLoaded', function () { window.AksrtFrontend.register('aksrt-enhancements', function (root, reason) {
initLightbox(); initLightbox(root, reason);
initScrollDirection(); initScrollDirection(root, reason);
}); });
})(); })();
+164 -194
View File
@@ -9,11 +9,27 @@
(function () { (function () {
'use strict'; 'use strict';
/* ============================================================ var animateObserver = null;
* 1. 3D 悬停倾斜效果 (vanilla-tilt 轻量版) var rippleBound = false;
* ============================================================ */ var stickyScrollBound = false;
function initTilt() { var parallaxScrollBound = false;
document.querySelectorAll('[data-aksrt-tilt]').forEach(function (el) { var modalBound = false;
var stickyElements = [];
var parallaxElements = [];
var parallaxTicking = false;
function scopeFor(root) {
return root && root.querySelectorAll ? root : document;
}
function eachMatch(root, selector, callback) {
var scope = scopeFor(root);
if (scope.nodeType === 1 && scope.matches(selector)) callback(scope);
scope.querySelectorAll(selector).forEach(callback);
}
function initTilt(root) {
eachMatch(root, '[data-aksrt-tilt]', function (el) {
if (el.dataset.tiltInit) return; if (el.dataset.tiltInit) return;
el.dataset.tiltInit = '1'; el.dataset.tiltInit = '1';
@@ -58,230 +74,184 @@
}); });
} }
/* ============================================================ function prepareAnimation(el) {
* 2. 滚动触发动画 (IntersectionObserver) if (el.dataset.aksrtAnimateBound === '1') return;
* ============================================================ */ el.dataset.aksrtAnimateBound = '1';
function initScrollAnimations() { var animation = el.dataset.aksrtAnimate || 'fadeInUp';
el.style.opacity = '0';
switch (animation) {
case 'fadeIn': el.style.transform = 'none'; break;
case 'fadeInUp': el.style.transform = 'translateY(30px)'; break;
case 'fadeInDown': el.style.transform = 'translateY(-30px)'; break;
case 'fadeInLeft': el.style.transform = 'translateX(-30px)'; break;
case 'fadeInRight': el.style.transform = 'translateX(30px)'; break;
case 'zoomIn': el.style.transform = 'scale(0.9)'; break;
default: el.style.transform = 'translateY(30px)';
}
animateObserver.observe(el);
}
function initScrollAnimations(root) {
if (!('IntersectionObserver' in window)) { if (!('IntersectionObserver' in window)) {
// 不支持时直接显示所有元素 eachMatch(root, '[data-aksrt-animate]', function (el) {
document.querySelectorAll('[data-aksrt-animate]').forEach(function (el) {
el.style.opacity = '1'; el.style.opacity = '1';
el.style.transform = 'none'; el.style.transform = 'none';
}); });
return; return;
} }
var observer = new IntersectionObserver(function (entries) { if (!animateObserver) {
entries.forEach(function (entry) { animateObserver = new IntersectionObserver(function (entries) {
if (!entry.isIntersecting) return; entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
var el = entry.target; var el = entry.target;
var animation = el.dataset.aksrtAnimate || 'fadeInUp'; var delay = parseInt(el.dataset.aksrtAnimateDelay || '0', 10);
var delay = parseInt(el.dataset.aksrtAnimateDelay || '0', 10); var duration = parseInt(el.dataset.aksrtAnimateDuration || '600', 10);
var duration = parseInt(el.dataset.aksrtAnimateDuration || '600', 10); animateObserver.unobserve(el);
window.setTimeout(function () {
setTimeout(function () { el.style.transition = 'opacity ' + duration + 'ms ease, transform ' + duration + 'ms ease';
el.style.transition = 'opacity ' + duration + 'ms ease, transform ' + duration + 'ms ease'; el.style.opacity = '1';
el.style.opacity = '1'; el.style.transform = 'translateY(0) translateX(0) scale(1)';
el.style.transform = 'translateY(0) translateX(0) scale(1)'; el.classList.add('aksrt-animated');
el.classList.add('aksrt-animated'); }, delay);
}, delay); });
}, { threshold: 0.1, rootMargin: '0px 0px -50px 0px' });
observer.unobserve(el);
});
}, {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px',
});
document.querySelectorAll('[data-aksrt-animate]').forEach(function (el) {
var animation = el.dataset.aksrtAnimate || 'fadeInUp';
// 设置初始状态
el.style.opacity = '0';
switch (animation) {
case 'fadeIn':
el.style.transform = 'none';
break;
case 'fadeInUp':
el.style.transform = 'translateY(30px)';
break;
case 'fadeInDown':
el.style.transform = 'translateY(-30px)';
break;
case 'fadeInLeft':
el.style.transform = 'translateX(-30px)';
break;
case 'fadeInRight':
el.style.transform = 'translateX(30px)';
break;
case 'zoomIn':
el.style.transform = 'scale(0.9)';
break;
default:
el.style.transform = 'translateY(30px)';
}
observer.observe(el);
});
}
/* ============================================================
* 5. 粘性元素 (Sticky)
* ============================================================ */
function initSticky() {
document.querySelectorAll('[data-aksrt-sticky]').forEach(function (el) {
var top = parseInt(el.dataset.aksrtStickyTop || '80', 10);
var offset = parseInt(el.dataset.aksrtStickyOffset || '0', 10);
function update() {
var rect = el.getBoundingClientRect();
var parentRect = el.parentElement.getBoundingClientRect();
if (parentRect.top < top && parentRect.bottom > el.offsetHeight + top) {
el.style.position = 'sticky';
el.style.top = top + 'px';
}
}
window.addEventListener('scroll', update, { passive: true });
});
}
/* ============================================================
* 6. 视差滚动
* ============================================================ */
function initParallax() {
var elements = document.querySelectorAll('[data-aksrt-parallax]');
if (elements.length === 0) return;
var ticking = false;
function update() {
var scrollTop = window.pageYOffset;
elements.forEach(function (el) {
var speed = parseFloat(el.dataset.aksrtParallax || '0.3');
var rect = el.getBoundingClientRect();
var center = rect.top + rect.height / 2;
var windowCenter = window.innerHeight / 2;
var offset = (center - windowCenter) * speed;
el.style.transform = 'translateY(' + offset + 'px)';
});
ticking = false;
} }
window.addEventListener('scroll', function () { eachMatch(root, '[data-aksrt-animate]', prepareAnimation);
if (!ticking) {
requestAnimationFrame(update);
ticking = true;
}
}, { passive: true });
} }
/* ============================================================ function updateSticky() {
* 7. 涟漪点击效果 stickyElements = stickyElements.filter(function (el) { return document.documentElement.contains(el); });
* ============================================================ */ stickyElements.forEach(function (el) {
function initRipple() { var top = parseInt(el.dataset.aksrtStickyTop || '80', 10);
var parent = el.parentElement;
if (!parent) return;
var parentRect = parent.getBoundingClientRect();
if (parentRect.top < top && parentRect.bottom > el.offsetHeight + top) {
el.style.position = 'sticky';
el.style.top = top + 'px';
}
});
}
function initSticky(root) {
eachMatch(root, '[data-aksrt-sticky]', function (el) {
if (el.dataset.aksrtStickyBound === '1') return;
el.dataset.aksrtStickyBound = '1';
stickyElements.push(el);
});
if (!stickyScrollBound) {
stickyScrollBound = true;
window.addEventListener('scroll', updateSticky, { passive: true });
}
updateSticky();
}
function updateParallax() {
parallaxElements = parallaxElements.filter(function (el) { return document.documentElement.contains(el); });
parallaxElements.forEach(function (el) {
var speed = parseFloat(el.dataset.aksrtParallax || '0.3');
var rect = el.getBoundingClientRect();
var center = rect.top + rect.height / 2;
var offset = (center - window.innerHeight / 2) * speed;
el.style.transform = 'translateY(' + offset + 'px)';
});
parallaxTicking = false;
}
function initParallax(root) {
eachMatch(root, '[data-aksrt-parallax]', function (el) {
if (el.dataset.aksrtParallaxBound === '1') return;
el.dataset.aksrtParallaxBound = '1';
parallaxElements.push(el);
});
if (!parallaxScrollBound) {
parallaxScrollBound = true;
window.addEventListener('scroll', function () {
if (parallaxTicking) return;
parallaxTicking = true;
requestAnimationFrame(updateParallax);
}, { passive: true });
}
updateParallax();
}
function initRipple(root, reason) {
if (rippleBound) return;
rippleBound = true;
document.addEventListener('click', function (e) { document.addEventListener('click', function (e) {
var el = e.target.closest('.aksrt-ripple'); var el = e.target.closest('.aksrt-ripple');
if (!el) return; if (!el) return;
var rect = el.getBoundingClientRect(); var rect = el.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
var size = Math.max(rect.width, rect.height) * 2; var size = Math.max(rect.width, rect.height) * 2;
var wave = document.createElement('span'); var wave = document.createElement('span');
wave.className = 'aksrt-ripple__wave'; wave.className = 'aksrt-ripple__wave';
wave.style.width = wave.style.height = size + 'px'; wave.style.width = wave.style.height = size + 'px';
wave.style.left = (x - size / 2) + 'px'; wave.style.left = (e.clientX - rect.left - size / 2) + 'px';
wave.style.top = (y - size / 2) + 'px'; wave.style.top = (e.clientY - rect.top - size / 2) + 'px';
el.appendChild(wave); el.appendChild(wave);
wave.addEventListener('animationend', function () { wave.remove(); });
wave.addEventListener('animationend', function () {
wave.remove();
});
}); });
} }
/* ============================================================ function initFloatLabels(root) {
* 8. 表单浮动标签自动初始化 eachMatch(root, '.aksrt-float-label__input', function (input) {
* ============================================================ */ if (input.value) input.setAttribute('data-has-value', '1');
function initFloatLabels() { if (input.dataset.aksrtFloatLabelBound === '1') return;
document.querySelectorAll('.aksrt-float-label__input').forEach(function (input) { input.dataset.aksrtFloatLabelBound = '1';
// 检查初始值
if (input.value) {
input.setAttribute('data-has-value', '1');
}
input.addEventListener('input', function () { input.addEventListener('input', function () {
if (this.value) { if (this.value) this.setAttribute('data-has-value', '1');
this.setAttribute('data-has-value', '1'); else this.removeAttribute('data-has-value');
} else {
this.removeAttribute('data-has-value');
}
}); });
}); });
} }
/* ============================================================ function initSigninCalendar(root) {
* 8. 签到日历可视化 eachMatch(root, '[data-aksrt-signin-calendar]', function (calendar) {
* ============================================================ */ if (calendar.dataset.aksrtSigninCalendarBound === '1') return;
function initSigninCalendar() { calendar.dataset.aksrtSigninCalendarBound = '1';
var calendar = document.querySelector('[data-aksrt-signin-calendar]'); var data = [];
if (!calendar) return; try { data = JSON.parse(calendar.dataset.aksrtSigninCalendar || '[]'); } catch (e) {}
var today = new Date();
var data = []; var html = '<div class="aksrt-signin-calendar__grid">';
try { for (var i = 29; i >= 0; i--) {
data = JSON.parse(calendar.dataset.aksrtSigninCalendar || '[]'); var d = new Date(today);
} catch (e) {} d.setDate(d.getDate() - i);
var dateStr = d.toISOString().split('T')[0];
// 生成最近 30 天的格子 var isSigned = data.indexOf(dateStr) !== -1;
var today = new Date(); html += '<div class="aksrt-signin-calendar__day' + (isSigned ? ' is-signed' : '') + (i === 0 ? ' is-today' : '') +
var html = '<div class="aksrt-signin-calendar__grid">'; '" title="' + dateStr + (isSigned ? ' ✓' : '') + '"><span>' + d.getDate() + '</span></div>';
}
for (var i = 29; i >= 0; i--) { calendar.innerHTML = html + '</div>';
var d = new Date(today); });
d.setDate(d.getDate() - i);
var dateStr = d.toISOString().split('T')[0];
var isSigned = data.indexOf(dateStr) !== -1;
var isToday = i === 0;
html += '<div class="aksrt-signin-calendar__day' +
(isSigned ? ' is-signed' : '') +
(isToday ? ' is-today' : '') +
'" title="' + dateStr + (isSigned ? ' ✓' : '') + '">' +
'<span>' + d.getDate() + '</span>' +
'</div>';
}
html += '</div>';
calendar.innerHTML = html;
} }
/* ============================================================ function bindModalLifecycle() {
* 初始化入口 if (modalBound) return;
* ============================================================ */ modalBound = true;
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('aksrt:modal:open', function (event) {
initTilt(); var root = event.target && event.target.querySelectorAll ? event.target : document;
initScrollAnimations(); window.setTimeout(function () { initSigninCalendar(root); }, 30);
initRipple();
initSticky();
initParallax();
initFloatLabels();
initSigninCalendar();
document.addEventListener('aksrt:modal:open', function () {
// Remote modal may inject calendar markup after open.
window.setTimeout(initSigninCalendar, 30);
}); });
}); }
function init(root, reason) {
initTilt(root);
initScrollAnimations(root);
initRipple(root, reason);
initSticky(root);
initParallax(root);
initFloatLabels(root);
initSigninCalendar(root);
bindModalLifecycle();
}
window.AksrtFrontend.register('aksrt-enhancements-advanced', init);
// 暴露到全局供动态内容使用
window.AksrtEnhancements = { window.AksrtEnhancements = {
initTilt: initTilt, initTilt: initTilt,
initScrollAnimations: initScrollAnimations, initScrollAnimations: initScrollAnimations,
initRipple: initRipple, initRipple: initRipple,
}; };
})(); })();
+6 -6
View File
@@ -44,7 +44,11 @@
} }
} }
function initFollowButtons() { var globalsBound = false;
function initFollowButtons(root, reason) {
if (globalsBound) return;
globalsBound = true;
document.addEventListener('click', function (event) { document.addEventListener('click', function (event) {
var button = event.target.closest('[data-aksrt-follow]'); var button = event.target.closest('[data-aksrt-follow]');
if (!button) return; if (!button) return;
@@ -92,9 +96,5 @@
}); });
} }
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-follow', initFollowButtons);
document.addEventListener('DOMContentLoaded', initFollowButtons);
} else {
initFollowButtons();
}
})(); })();
+11 -10
View File
@@ -80,11 +80,14 @@
isSubmitting: false, isSubmitting: false,
featured: { type: '', data: {} }, featured: { type: '', data: {} },
init: function () { init: function (root) {
this.form = document.getElementById('aksrt-submit-form'); this.root = root || document;
if (!this.form) { this.form = this.root.nodeType === 1 && this.root.matches('#aksrt-submit-form')
? this.root : this.root.querySelector('#aksrt-submit-form');
if (!this.form || this.form.dataset.aksrtFrontSubmitBound === '1') {
return; return;
} }
this.form.dataset.aksrtFrontSubmitBound = '1';
this.bindDirty(); this.bindDirty();
this.bindAutoHeight(); this.bindAutoHeight();
this.bindSubmitButtons(); this.bindSubmitButtons();
@@ -311,6 +314,8 @@
}, },
bindDraftDelete: function () { bindDraftDelete: function () {
if (document.__aksrtFrontSubmitDraftDeleteBound) return;
document.__aksrtFrontSubmitDraftDeleteBound = true;
document.addEventListener('click', function (e) { document.addEventListener('click', function (e) {
var btn = e.target.closest('.aksrt-submit-drafts__delete'); var btn = e.target.closest('.aksrt-submit-drafts__delete');
if (!btn) { if (!btn) {
@@ -645,11 +650,7 @@
}, },
}; };
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-front-submit', function (root) {
document.addEventListener('DOMContentLoaded', function () { App.init(root);
App.init(); });
});
} else {
App.init();
}
})(); })();
+21 -17
View File
@@ -424,6 +424,7 @@
var tablists = (scope || document).querySelectorAll('[data-aksrt-home-tabs]'); var tablists = (scope || document).querySelectorAll('[data-aksrt-home-tabs]');
tablists.forEach(function (list, listIndex) { tablists.forEach(function (list, listIndex) {
if (list.dataset.aksrtHomeTabsBound === '1') return;
var stream = list.closest('.aksrt-homepage__stream'); var stream = list.closest('.aksrt-homepage__stream');
if (!stream) return; if (!stream) return;
@@ -431,6 +432,7 @@
var panels = Array.from(stream.querySelectorAll('[data-home-tab-panel]')); var panels = Array.from(stream.querySelectorAll('[data-home-tab-panel]'));
if (buttons.length < 2 || !panels.length) return; if (buttons.length < 2 || !panels.length) return;
list.dataset.aksrtHomeTabsBound = '1';
function getPanel(button) { function getPanel(button) {
var target = button.getAttribute('data-home-tab-target'); var target = button.getAttribute('data-home-tab-target');
@@ -539,12 +541,14 @@
* Sortbar Dropdown Toggle * Sortbar Dropdown Toggle
* ============================================================ */ * ============================================================ */
function initSortbarDropdown() { function initSortbarDropdown(scope) {
document.querySelectorAll('[data-sort-dropdown-trigger]').forEach(function (trigger) { (scope || document).querySelectorAll('[data-sort-dropdown-trigger]').forEach(function (trigger) {
if (trigger.dataset.aksrtSortDropdownBound === '1') return;
var wrapper = trigger.closest('.aksrt-homepage__sortbar-dropdown'); var wrapper = trigger.closest('.aksrt-homepage__sortbar-dropdown');
if (!wrapper) return; if (!wrapper) return;
var menu = wrapper.querySelector('.aksrt-homepage__sortbar-dropdown-menu'); var menu = wrapper.querySelector('.aksrt-homepage__sortbar-dropdown-menu');
if (!menu) return; if (!menu) return;
trigger.dataset.aksrtSortDropdownBound = '1';
function close() { function close() {
menu.setAttribute('hidden', ''); menu.setAttribute('hidden', '');
@@ -599,10 +603,11 @@
* Sortbar AJAX * Sortbar AJAX
* ============================================================ */ * ============================================================ */
function initSortbar() { function initSortbar(scope) {
document.querySelectorAll('.aksrt-homepage__stream').forEach(function (stream) { (scope || document).querySelectorAll('.aksrt-homepage__stream').forEach(function (stream) {
var sortbar = stream.querySelector('.aksrt-homepage__sortbar'); var sortbar = stream.querySelector('.aksrt-homepage__sortbar');
if (!sortbar || !isSortAjaxEnabled(sortbar)) return; if (!sortbar || !isSortAjaxEnabled(sortbar) || sortbar.dataset.aksrtSortbarBound === '1') return;
sortbar.dataset.aksrtSortbarBound = '1';
sortbar.addEventListener('click', function (event) { sortbar.addEventListener('click', function (event) {
var link = event.target.closest('.aksrt-homepage__sortbar-link, .aksrt-homepage__sortbar-dropdown-item'); var link = event.target.closest('.aksrt-homepage__sortbar-link, .aksrt-homepage__sortbar-dropdown-item');
@@ -649,10 +654,11 @@
return isNaN(offset) ? 60 : offset; return isNaN(offset) ? 60 : offset;
} }
function initStickyTabs() { function initStickyTabs(scope) {
if (!isStickyTabsEnabled()) return; if (!isStickyTabsEnabled()) return;
document.querySelectorAll('.aksrt-homepage__tabs.home-tab-nav-box').forEach(function (tabs) { (scope || document).querySelectorAll('.aksrt-homepage__tabs.home-tab-nav-box').forEach(function (tabs) {
if (tabs.dataset.aksrtStickyTabsBound === '1') return;
var parent = tabs.parentNode; var parent = tabs.parentNode;
var sentinel = tabs.previousElementSibling; var sentinel = tabs.previousElementSibling;
var canUseSticky = supportsStickyPosition(); var canUseSticky = supportsStickyPosition();
@@ -660,6 +666,7 @@
var observer = null; var observer = null;
if (!parent || tabs.getAttribute('data-sticky-tabs') === 'false') return; if (!parent || tabs.getAttribute('data-sticky-tabs') === 'false') return;
tabs.dataset.aksrtStickyTabsBound = '1';
if (!sentinel || !sentinel.classList.contains('aksrt-homepage__tabs-sentinel')) { if (!sentinel || !sentinel.classList.contains('aksrt-homepage__tabs-sentinel')) {
sentinel = document.createElement('div'); sentinel = document.createElement('div');
@@ -718,16 +725,13 @@
* Init * Init
* ============================================================ */ * ============================================================ */
function init() { function init(root) {
initHomeTabs(); var scope = root || document;
initSortbarDropdown(); initHomeTabs(scope);
initSortbar(); initSortbarDropdown(scope);
initStickyTabs(); initSortbar(scope);
initStickyTabs(scope);
} }
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-homepage', init);
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})(); })();
+24 -15
View File
@@ -37,9 +37,17 @@
// ── Auto-resize Textarea ── // ── Auto-resize Textarea ──
function initAutoResize() { function queryAll(root, selector) {
var textareas = document.querySelectorAll('textarea[data-aksrt-autoresize]'); var nodes = Array.prototype.slice.call(root.querySelectorAll(selector));
if (root.nodeType === 1 && root.matches(selector)) nodes.unshift(root);
return nodes;
}
function initAutoResize(root) {
var textareas = queryAll(root, 'textarea[data-aksrt-autoresize]');
textareas.forEach(function (ta) { textareas.forEach(function (ta) {
if (ta.dataset.aksrtAutoresizeBound === '1') return;
ta.dataset.aksrtAutoresizeBound = '1';
function resize() { function resize() {
ta.style.height = 'auto'; ta.style.height = 'auto';
ta.style.height = ta.scrollHeight + 'px'; ta.style.height = ta.scrollHeight + 'px';
@@ -52,9 +60,11 @@
// ── Character Counter ── // ── Character Counter ──
function initCounters() { function initCounters(root) {
var fields = document.querySelectorAll('[data-aksrt-counter]'); var fields = queryAll(root, '[data-aksrt-counter]');
fields.forEach(function (field) { fields.forEach(function (field) {
if (field.dataset.aksrtCounterBound === '1') return;
field.dataset.aksrtCounterBound = '1';
var max = parseInt(field.getAttribute('data-aksrt-maxlength') || field.getAttribute('maxlength') || '0', 10); var max = parseInt(field.getAttribute('data-aksrt-maxlength') || field.getAttribute('maxlength') || '0', 10);
var counter = document.createElement('span'); var counter = document.createElement('span');
counter.className = 'aksrt-counter'; counter.className = 'aksrt-counter';
@@ -79,9 +89,11 @@
// ── Validation Feedback ── // ── Validation Feedback ──
function initValidation() { function initValidation(root) {
var fields = document.querySelectorAll('[data-aksrt-validate]'); var fields = queryAll(root, '[data-aksrt-validate]');
fields.forEach(function (field) { fields.forEach(function (field) {
if (field.dataset.aksrtValidateBound === '1') return;
field.dataset.aksrtValidateBound = '1';
var minLen = parseInt(field.getAttribute('data-aksrt-minlength') || field.getAttribute('minlength') || '0', 10); var minLen = parseInt(field.getAttribute('data-aksrt-minlength') || field.getAttribute('minlength') || '0', 10);
var maxLen = parseInt(field.getAttribute('data-aksrt-maxlength') || field.getAttribute('maxlength') || '0', 10); var maxLen = parseInt(field.getAttribute('data-aksrt-maxlength') || field.getAttribute('maxlength') || '0', 10);
var required = field.hasAttribute('required'); var required = field.hasAttribute('required');
@@ -115,16 +127,13 @@
// ── Main Init ── // ── Main Init ──
function init() { function init(root) {
root = root || document;
injectStyle(); injectStyle();
initAutoResize(); initAutoResize(root);
initCounters(); initCounters(root);
initValidation(); initValidation(root);
} }
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-input-expand', init);
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})(); })();
-172
View File
@@ -1,172 +0,0 @@
/**
* AKSRT Page Loader
*
* Lightweight top progress bar for page transitions and AJAX requests.
* No external dependencies — pure vanilla JS.
*
* @package AKSRT
*/
(function () {
'use strict';
var STYLE_ID = 'aksrt-loader-style';
var BAR_ID = 'aksrt-loader-bar';
var CSS =
'#' + BAR_ID + '{' +
'position:fixed;top:0;left:0;width:0;height:3px;z-index:99999;' +
'background:linear-gradient(90deg,#1890ff,#36cfc9);' +
'transition:width 0.3s ease,opacity 0.3s ease;' +
'pointer-events:none;opacity:0;' +
'}' +
'#' + BAR_ID + '.is-active{opacity:1;}' +
'#' + BAR_ID + '.is-done{' +
'width:100%!important;opacity:0;' +
'}';
var bar = null;
var timer = null;
var progress = 0;
function injectStyle() {
if (document.getElementById(STYLE_ID)) return;
var style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = CSS;
document.head.appendChild(style);
}
function createBar() {
if (bar) return;
bar = document.createElement('div');
bar.id = BAR_ID;
document.body.appendChild(bar);
}
function start() {
if (!bar) createBar();
clearTimeout(timer);
bar.classList.remove('is-done');
bar.classList.add('is-active');
progress = 0;
bar.style.width = '0%';
trickle();
}
function trickle() {
if (progress >= 90) return;
var increment = progress < 30 ? 8 : progress < 60 ? 4 : 2;
progress = Math.min(90, progress + increment * (0.8 + Math.random() * 0.4));
bar.style.width = progress + '%';
timer = setTimeout(trickle, 200 + Math.random() * 300);
}
function done() {
if (!bar) return;
clearTimeout(timer);
bar.style.width = '100%';
bar.classList.add('is-done');
setTimeout(function () {
bar.classList.remove('is-active', 'is-done');
bar.style.width = '0%';
progress = 0;
}, 400);
}
// Intercept link clicks for same-origin page transitions
function onLinkClick(e) {
if (e.defaultPrevented) return;
if (e.button !== 0) return; // left click only
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
var link = e.target.closest('a');
if (!link || !link.href) return;
if (link.target === '_blank') return;
if (link.hasAttribute('download')) return;
if (link.href.indexOf('#') === 0) return;
// Same-origin only
try {
var url = new URL(link.href);
if (url.origin !== window.location.origin) return;
if (url.protocol !== "http:" && url.protocol !== "https:") return;
if (url.pathname === window.location.pathname && url.search === window.location.search) return;
} catch (ex) { return; }
start();
}
// Intercept form submissions
function onFormSubmit(e) {
var form = e.target;
if (!form || form.tagName !== 'FORM') return;
if (form.target === '_blank') return;
start();
}
// Hook into AJAX (fetch) if available
function hookFetch() {
if (!window.fetch) return;
var origFetch = window.fetch;
window.fetch = function () {
start();
return origFetch.apply(this, arguments).then(function (response) {
done();
return response;
}).catch(function (err) {
done();
throw err;
});
};
}
// Hook into XMLHttpRequest
function hookXHR() {
if (!window.XMLHttpRequest) return;
var origOpen = XMLHttpRequest.prototype.open;
var origSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function () {
this._aksrtLoader = true;
return origOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function () {
if (this._aksrtLoader) {
start();
var self = this;
this.addEventListener('loadend', function () { done(); });
}
return origSend.apply(this, arguments);
};
}
function init() {
injectStyle();
createBar();
document.addEventListener('click', onLinkClick, true);
document.addEventListener('submit', onFormSubmit, true);
// Done on page load (handles bfcache)
window.addEventListener('pageshow', function (e) {
if (e.persisted) done();
});
window.addEventListener('load', done);
hookFetch();
hookXHR();
}
// Public API
window.AksrtLoader = {
start: start,
done: done
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
-27
View File
@@ -1,27 +0,0 @@
/**
* AKSRT Frontend Shell
*
* All init modules have been extracted to assets/js/core/*.js.
* This file remains as the enqueue anchor (handle: aksrt-script) that
* other scripts depend on, and provides the AksrtFrontend fallback
* for edge cases where rehydrate.js is not loaded first.
*
* @package AKSRT
* @since 5.0.0
*/
(function () {
"use strict";
if (!window.AksrtFrontend) {
window.AksrtFrontend = {
register: function (name, fn) {
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", function () { fn(document, "init"); });
} else {
fn(document, "init");
}
},
rehydrate: function () {}
};
}
})();
+8 -2
View File
@@ -6,8 +6,11 @@
(function () { (function () {
'use strict'; 'use strict';
var root = document.querySelector('[data-message-center]'); function init(scope) {
if (!root) return; scope = scope || document;
var root = scope.nodeType === 1 && scope.matches('[data-message-center]') ? scope : scope.querySelector('[data-message-center]');
if (!root || root.dataset.aksrtMessagesBound === '1') return;
root.dataset.aksrtMessagesBound = '1';
var nonce = typeof aksrtData !== 'undefined' ? aksrtData.nonce : ''; var nonce = typeof aksrtData !== 'undefined' ? aksrtData.nonce : '';
var ajaxUrl = typeof aksrtData !== 'undefined' ? aksrtData.ajaxUrl : '/wp-admin/admin-ajax.php'; var ajaxUrl = typeof aksrtData !== 'undefined' ? aksrtData.ajaxUrl : '/wp-admin/admin-ajax.php';
@@ -225,4 +228,7 @@
if (result.success && result.data) updateHeaderBadge(result.data.unread); if (result.success && result.data) updateHeaderBadge(result.data.unread);
}).catch(function () {}); }).catch(function () {});
}, pollInterval); }, pollInterval);
}
window.AksrtFrontend.register('aksrt-messages', init);
})(); })();
+6 -7
View File
@@ -307,8 +307,11 @@
}; };
// 自动初始化 // 自动初始化
function autoInit() { function autoInit(root) {
document.querySelectorAll('[data-aksrt-mini-upload]').forEach(function (el) { var containers = [];
if (root && root.nodeType === 1 && root.matches('[data-aksrt-mini-upload]')) containers.push(root);
Array.prototype.push.apply(containers, (root || document).querySelectorAll('[data-aksrt-mini-upload]'));
containers.forEach(function (el) {
if (el.dataset.miniUploadInit) return; if (el.dataset.miniUploadInit) return;
el.dataset.miniUploadInit = '1'; el.dataset.miniUploadInit = '1';
@@ -321,11 +324,7 @@
}); });
} }
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-mini-upload', autoInit);
document.addEventListener('DOMContentLoaded', autoInit);
} else {
autoInit();
}
// 暴露到全局 // 暴露到全局
window.AksrtMiniUpload = MiniUpload; window.AksrtMiniUpload = MiniUpload;
-1
View File
@@ -473,5 +473,4 @@
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === "function") { if (window.AksrtFrontend && typeof window.AksrtFrontend.register === "function") {
window.AksrtFrontend.register("aksrt-order-center", init); window.AksrtFrontend.register("aksrt-order-center", init);
} }
init(document);
}()); }());
+7 -9
View File
@@ -21,6 +21,7 @@
*/ */
function PageEdit(options) { function PageEdit(options) {
this.options = Object.assign({ this.options = Object.assign({
form: null,
formSelector: 'form[data-aksrt-edit]', formSelector: 'form[data-aksrt-edit]',
titleSelector: 'input[name="title"], input[name="topic_title"]', titleSelector: 'input[name="title"], input[name="topic_title"]',
contentSelector: 'textarea[name="content"], textarea[name="topic_content"]', contentSelector: 'textarea[name="content"], textarea[name="topic_content"]',
@@ -29,7 +30,7 @@
draftKey: '', draftKey: '',
}, options || {}); }, options || {});
this.form = document.querySelector(this.options.formSelector); this.form = this.options.form || document.querySelector(this.options.formSelector);
if (!this.form) return; if (!this.form) return;
this.autoSaveTimer = null; this.autoSaveTimer = null;
@@ -189,18 +190,15 @@
}; };
// 自动初始化 // 自动初始化
function autoInit() { function autoInit(root) {
var form = document.querySelector('form[data-aksrt-edit]'); var form = root && root.nodeType === 1 && root.matches('form[data-aksrt-edit]')
? root : (root || document).querySelector('form[data-aksrt-edit]');
if (form) { if (form) {
new PageEdit(); new PageEdit({ form: form });
} }
} }
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-page-edit', autoInit);
document.addEventListener('DOMContentLoaded', autoInit);
} else {
autoInit();
}
window.AksrtPageEdit = PageEdit; window.AksrtPageEdit = PageEdit;
})(); })();
+24 -20
View File
@@ -28,7 +28,10 @@
searchQuery: '', searchQuery: '',
ajaxInFlight: null, ajaxInFlight: null,
init: function () { init: function (root) {
this.root = root || document;
if (this.root.__aksrtPageNavInitialized) return;
this.root.__aksrtPageNavInitialized = true;
this.initTocHighlight(); this.initTocHighlight();
this.initSmoothScroll(); this.initSmoothScroll();
this.initDocSearch(); this.initDocSearch();
@@ -40,7 +43,8 @@
* TOC 高亮(文档阅读页) * TOC 高亮(文档阅读页)
* ============================================================ */ * ============================================================ */
initTocHighlight: function () { initTocHighlight: function () {
var toc = document.querySelector('.aksrt-doc-toc'); var toc = this.root.querySelector('.aksrt-doc-toc');
if (this.root.nodeType === 1 && this.root.matches('.aksrt-doc-toc')) toc = this.root;
if (!toc) return; if (!toc) return;
this.tocItems = Array.from(toc.querySelectorAll('.aksrt-doc-toc__item a')); this.tocItems = Array.from(toc.querySelectorAll('.aksrt-doc-toc__item a'));
@@ -92,7 +96,9 @@
* 平滑滚动 * 平滑滚动
* ============================================================ */ * ============================================================ */
initSmoothScroll: function () { initSmoothScroll: function () {
document.addEventListener('click', function (e) { if (this.root.__aksrtPageNavSmoothBound) return;
this.root.__aksrtPageNavSmoothBound = true;
this.root.addEventListener('click', function (e) {
var link = e.target.closest('a[href^="#"]'); var link = e.target.closest('a[href^="#"]');
if (!link) return; if (!link) return;
@@ -113,11 +119,11 @@
* 文档搜索(AJAX 接管) * 文档搜索(AJAX 接管)
* ============================================================ */ * ============================================================ */
initDocSearch: function () { initDocSearch: function () {
var searchInput = document.querySelector('.aksrt-doc-search__input'); var searchInput = this.root.querySelector('.aksrt-doc-search__input');
if (!searchInput) return; if (!searchInput) return;
var resultsContainer = document.querySelector('.aksrt-doc-search__results'); var resultsContainer = this.root.querySelector('.aksrt-doc-search__results');
var takeover = document.querySelector('.aksrt-doc-takeover'); var takeover = this.root.querySelector('.aksrt-doc-takeover');
var self = this; var self = this;
var debounceTimer = null; var debounceTimer = null;
@@ -206,7 +212,7 @@
resultsContainer.innerHTML = ''; resultsContainer.innerHTML = '';
} }
// 显示所有卡片 // 显示所有卡片
var cards = document.querySelectorAll('.aksrt-doc-index-card'); var cards = this.root.querySelectorAll('.aksrt-doc-index-card');
cards.forEach(function (card) { card.style.display = ''; }); cards.forEach(function (card) { card.style.display = ''; });
}, },
@@ -231,7 +237,7 @@
* 分类切换 — AJAX 加载 + 分页 * 分类切换 — AJAX 加载 + 分页
* ============================================================ */ * ============================================================ */
initCategoryChips: function () { initCategoryChips: function () {
var toolbar = document.querySelector('.aksrt-doc-toolbar'); var toolbar = this.root.querySelector('.aksrt-doc-toolbar');
if (!toolbar) return; if (!toolbar) return;
var chips = toolbar.querySelectorAll('.aksrt-doc-toolbar__chip[data-cat-id]'); var chips = toolbar.querySelectorAll('.aksrt-doc-toolbar__chip[data-cat-id]');
@@ -260,7 +266,7 @@
} }
var self = this; var self = this;
var grid = document.querySelector('.aksrt-doc-grid'); var grid = this.root.querySelector('.aksrt-doc-grid');
if (!grid) return; if (!grid) return;
// 显示加载状态 // 显示加载状态
@@ -305,7 +311,7 @@
}, },
updateMetaCount: function (total) { updateMetaCount: function (total) {
var meta = document.querySelector('.aksrt-doc-toolbar__meta'); var meta = this.root.querySelector('.aksrt-doc-toolbar__meta');
if (meta) { if (meta) {
meta.textContent = total + ' 篇文档'; meta.textContent = total + ' 篇文档';
} }
@@ -316,7 +322,7 @@
* ============================================================ */ * ============================================================ */
initPagination: function () { initPagination: function () {
var self = this; var self = this;
var paginationContainer = document.querySelector('.aksrt-doc-pagination'); var paginationContainer = this.root.querySelector('.aksrt-doc-pagination');
if (!paginationContainer) return; if (!paginationContainer) return;
paginationContainer.addEventListener('click', function (e) { paginationContainer.addEventListener('click', function (e) {
@@ -331,7 +337,7 @@
self.loadCategoryDocs(self.activeCategory, page); self.loadCategoryDocs(self.activeCategory, page);
// 滚动到顶部 // 滚动到顶部
var grid = document.querySelector('.aksrt-doc-grid'); var grid = self.root.querySelector('.aksrt-doc-grid');
if (grid) { if (grid) {
grid.scrollIntoView({ behavior: 'smooth', block: 'start' }); grid.scrollIntoView({ behavior: 'smooth', block: 'start' });
} }
@@ -342,7 +348,7 @@
this.totalPages = totalPages; this.totalPages = totalPages;
this.currentPage = currentPage; this.currentPage = currentPage;
var container = document.querySelector('.aksrt-doc-pagination'); var container = this.root.querySelector('.aksrt-doc-pagination');
if (!container) return; if (!container) return;
if (totalPages <= 1) { if (totalPages <= 1) {
@@ -394,7 +400,7 @@
}, },
filterDocItems: function (query) { filterDocItems: function (query) {
var cards = document.querySelectorAll('.aksrt-doc-index-card'); var cards = this.root.querySelectorAll('.aksrt-doc-index-card');
if (cards.length > 0) { if (cards.length > 0) {
cards.forEach(function (card) { cards.forEach(function (card) {
var title = card.querySelector('.aksrt-doc-index-card__title'); var title = card.querySelector('.aksrt-doc-index-card__title');
@@ -408,7 +414,7 @@
}); });
} }
var sidebarItems = document.querySelectorAll('.aksrt-doc-sidebar__item'); var sidebarItems = this.root.querySelectorAll('.aksrt-doc-sidebar__item');
if (sidebarItems.length > 0) { if (sidebarItems.length > 0) {
sidebarItems.forEach(function (item) { sidebarItems.forEach(function (item) {
var link = item.querySelector('.aksrt-doc-sidebar__link'); var link = item.querySelector('.aksrt-doc-sidebar__link');
@@ -446,9 +452,7 @@
} }
}; };
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-page-navs', function (root) {
document.addEventListener('DOMContentLoaded', function () { PAGE_NAVS.init(); }); PAGE_NAVS.init(root);
} else { });
PAGE_NAVS.init();
}
})(); })();
+22 -13
View File
@@ -10,9 +10,15 @@
(function () { (function () {
'use strict'; 'use strict';
function initContentExpand() { function queryAll(root, selector) {
var nodes = Array.prototype.slice.call((root || document).querySelectorAll(selector));
if (root && root.nodeType === 1 && root.matches(selector)) nodes.unshift(root);
return nodes;
}
function initContentExpand(root) {
// legacy-style: inject .read-more inside .limit-height and expand on click. // legacy-style: inject .read-more inside .limit-height and expand on click.
document.querySelectorAll('.wp-posts-content.limit-height, .limit-height[data-nav]').forEach(function (content) { queryAll(root, '.wp-posts-content.limit-height, .limit-height[data-nav]').forEach(function (content) {
if (content.dataset.aksrtExpandBound === '1') return; if (content.dataset.aksrtExpandBound === '1') return;
content.dataset.aksrtExpandBound = '1'; content.dataset.aksrtExpandBound = '1';
@@ -58,8 +64,13 @@
wrap.remove(); wrap.remove();
}); });
}); });
}
// Compatibility: old external expand buttons. var expandDelegateBound = false;
function bindExpandDelegate() {
if (expandDelegateBound) return;
expandDelegateBound = true;
document.addEventListener('click', function (event) { document.addEventListener('click', function (event) {
var open = event.target.closest && event.target.closest('.read-more-open'); var open = event.target.closest && event.target.closest('.read-more-open');
if (!open) return; if (!open) return;
@@ -73,8 +84,8 @@
}); });
} }
function initFlashQueryCleanup() { function initFlashQueryCleanup(root) {
document.querySelectorAll('[data-aksrt-flash-query]').forEach(function (node) { queryAll(root, '[data-aksrt-flash-query]').forEach(function (node) {
var queryKey = node.getAttribute('data-aksrt-flash-query'); var queryKey = node.getAttribute('data-aksrt-flash-query');
if (!queryKey || !window.history || typeof window.history.replaceState !== 'function') return; if (!queryKey || !window.history || typeof window.history.replaceState !== 'function') return;
@@ -86,14 +97,12 @@
}); });
} }
function init() { function init(root) {
initContentExpand(); root = root || document;
initFlashQueryCleanup(); bindExpandDelegate();
initContentExpand(root);
initFlashQueryCleanup(root);
} }
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-page-template', init);
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})(); })();
+1 -5
View File
@@ -567,9 +567,5 @@
document.addEventListener("click", onDocClick, true); document.addEventListener("click", onDocClick, true);
} }
if (document.readyState === "loading") { window.AksrtFrontend.register("aksrt-pay-vip", bindOnce);
document.addEventListener("DOMContentLoaded", bindOnce, { once: true });
} else {
bindOnce();
}
})(); })();
+6 -4
View File
@@ -271,7 +271,11 @@
return Promise.resolve({ mode: 'window' }); return Promise.resolve({ mode: 'window' });
} }
var genericShareBound = false;
function bindGenericShareButtons() { function bindGenericShareButtons() {
if (genericShareBound) return;
genericShareBound = true;
document.addEventListener('click', function (event) { document.addEventListener('click', function (event) {
var node = event.target.closest('[data-share-platform]'); var node = event.target.closest('[data-share-platform]');
if (!node || node.closest('#share-dropdown')) return; if (!node || node.closest('#share-dropdown')) return;
@@ -299,9 +303,7 @@
bindGenericShareButtons(); bindGenericShareButtons();
} }
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-post-share', function () {
document.addEventListener('DOMContentLoaded', init);
} else {
init(); init();
} });
})(); })();
+16 -16
View File
@@ -37,7 +37,11 @@
PosterShare.prototype.init = function () { PosterShare.prototype.init = function () {
var self = this; var self = this;
document.querySelectorAll(this.options.triggerSelector).forEach(function (btn) { var scope = this.options.root || document;
var triggers = [];
if (scope.nodeType === 1 && scope.matches(this.options.triggerSelector)) triggers.push(scope);
Array.prototype.push.apply(triggers, scope.querySelectorAll(this.options.triggerSelector));
triggers.forEach(function (btn) {
btn.addEventListener('click', function (e) { btn.addEventListener('click', function (e) {
e.preventDefault(); e.preventDefault();
@@ -360,25 +364,21 @@
}; };
// 自动初始化 // 自动初始化
function autoInit() { function autoInit(root) {
document.querySelectorAll('[data-aksrt-poster]').forEach(function (el) { var triggers = [];
if (el.dataset.posterInit) return; if (root && root.nodeType === 1 && root.matches('[data-aksrt-poster]')) triggers.push(root);
Array.prototype.push.apply(triggers, (root || document).querySelectorAll('[data-aksrt-poster]'));
if (!triggers.length) return;
var pending = triggers.filter(function (el) { return el.dataset.posterInit !== '1'; });
if (!pending.length) return;
var instance = new PosterShare({ root: root || document, triggerSelector: '[data-aksrt-poster]' });
pending.forEach(function (el) {
el.dataset.posterInit = '1'; el.dataset.posterInit = '1';
el._aksrtPosterShare = instance;
var config = {};
try {
config = JSON.parse(el.dataset.aksrtPoster || '{}');
} catch (e) {}
new PosterShare({ triggerSelector: '[data-aksrt-poster]' });
}); });
} }
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-poster-share', autoInit);
document.addEventListener('DOMContentLoaded', autoInit);
} else {
autoInit();
}
/** /**
* Generate a poster directly from a unified share payload, without binding * Generate a poster directly from a unified share payload, without binding
+26 -23
View File
@@ -8,11 +8,14 @@
'use strict'; 'use strict';
function qs(sel, root) { function qs(sel, root) {
if (root && root.nodeType === 1 && root.matches(sel)) return root;
return (root || document).querySelector(sel); return (root || document).querySelector(sel);
} }
function qsa(sel, root) { function qsa(sel, root) {
return Array.prototype.slice.call((root || document).querySelectorAll(sel)); var nodes = Array.prototype.slice.call((root || document).querySelectorAll(sel));
if (root && root.nodeType === 1 && root.matches(sel)) nodes.unshift(root);
return nodes;
} }
function ajaxUrl() { function ajaxUrl() {
@@ -21,10 +24,13 @@
return '/wp-admin/admin-ajax.php'; return '/wp-admin/admin-ajax.php';
} }
function initModal() { function initModal(root) {
var modal = qs('#post-navs-sz'); var modal = qs('#post-navs-sz', root);
if (!modal) return; if (!modal) return;
if (modal.dataset.aksrtPostsNavModalBound === '1') return;
modal.dataset.aksrtPostsNavModalBound = '1';
function openModal(e) { function openModal(e) {
if (e) e.preventDefault(); if (e) e.preventDefault();
modal.classList.add('is-open', 'show'); modal.classList.add('is-open', 'show');
@@ -37,11 +43,11 @@
modal.setAttribute('aria-hidden', 'true'); modal.setAttribute('aria-hidden', 'true');
} }
qsa('[data-toggle="modal"][data-target="#post-navs-sz"], a[href="#post-navs-sz"]').forEach(function (el) { qsa('[data-toggle="modal"][data-target="#post-navs-sz"], a[href="#post-navs-sz"]', root).forEach(function (el) {
el.addEventListener('click', openModal); el.addEventListener('click', openModal);
}); });
// Title [设置] uses data-target. // Title [设置] uses data-target.
qsa('[data-target="#post-navs-sz"]').forEach(function (el) { qsa('[data-target="#post-navs-sz"]', root).forEach(function (el) {
el.addEventListener('click', openModal); el.addEventListener('click', openModal);
}); });
qsa('[data-dismiss="modal"]', modal).forEach(function (btn) { qsa('[data-dismiss="modal"]', modal).forEach(function (btn) {
@@ -55,9 +61,9 @@
}); });
} }
function initDragSort() { function initDragSort(root) {
var left = qs('#cat-sortable'); var left = qs('#cat-sortable', root);
var right = qs('#cat-sortable-ok'); var right = qs('#cat-sortable-ok', root);
if (!left || !right) return; if (!left || !right) return;
var dragEl = null; var dragEl = null;
@@ -123,8 +129,8 @@
} }
} }
function initSave() { function initSave(root) {
var form = qs('#aksrt-posts-navs-settings'); var form = qs('#aksrt-posts-navs-settings', root);
if (!form) return; if (!form) return;
form.addEventListener('submit', function (e) { form.addEventListener('submit', function (e) {
@@ -179,9 +185,9 @@
* Build simple TOC into .posts-nav-box if section-navs hasn't already. * Build simple TOC into .posts-nav-box if section-navs hasn't already.
* Uses category h2 headings inside .posts-navs. * Uses category h2 headings inside .posts-navs.
*/ */
function initFallbackToc() { function initFallbackToc(scope) {
var box = qs('.posts-nav-box'); var box = qs('.posts-nav-box', scope);
var root = qs('.posts-navs[data-nav]'); var root = qs('.posts-navs[data-nav]', scope);
if (!box || !root) return; if (!box || !root) return;
if (box.querySelector('.posts-nav-lists, .aksrt-section-nav, .toc-list')) return; if (box.querySelector('.posts-nav-lists, .aksrt-section-nav, .toc-list')) return;
@@ -230,17 +236,14 @@
highlight(); highlight();
} }
function init() { function init(root) {
initModal(); root = root || document;
initDragSort(); initModal(root);
initSave(); initDragSort(root);
initSave(root);
// Defer so section-navs.js can own TOC first. // Defer so section-navs.js can own TOC first.
window.setTimeout(initFallbackToc, 50); window.setTimeout(function () { initFallbackToc(root); }, 50);
} }
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-posts-navs-page', init);
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})(); })();
+2 -4
View File
@@ -218,9 +218,7 @@
loadEnlighterJS(initEnlighter); loadEnlighterJS(initEnlighter);
} }
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-precode', function () {
document.addEventListener('DOMContentLoaded', init);
} else {
init(); init();
} });
})(); })();
+45 -17
View File
@@ -6,8 +6,9 @@
* to initialize or re-initialize them on a given root element. * to initialize or re-initialize them on a given root element.
* *
* Usage: * Usage:
* AksrtFrontend.register('name', initFn); // Register a component * AksrtFrontend.register('name', initFn); // Register a component
* AksrtFrontend.rehydrate(root, 'init'); // Initialize all components * AksrtFrontend.boot(document); // One-time initial page load
* AksrtFrontend.rehydrate(root, 'ajax'); // Initialize dynamic content
* *
* @package AKSRT * @package AKSRT
* @since 5.0.0 * @since 5.0.0
@@ -16,7 +17,25 @@
'use strict'; 'use strict';
var _registry = []; var _registry = [];
var _initialized = []; var _booted = false;
var _bootRoot = null;
function invoke(entry, root, reason) {
try {
entry.initFn(root, reason);
} catch (err) {
console.error('[AKSRT Rehydrate] Error initializing "' + entry.name + '":', err);
}
}
function dispatch(root, reason, eventName) {
try {
root.dispatchEvent(new CustomEvent(eventName, {
bubbles: true,
detail: { reason: reason }
}));
} catch (e) { /* IE fallback */ }
}
window.AksrtFrontend = { window.AksrtFrontend = {
@@ -32,10 +51,26 @@
for (var i = 0; i < _registry.length; i++) { for (var i = 0; i < _registry.length; i++) {
if (_registry[i].name === name) { if (_registry[i].name === name) {
_registry[i].initFn = initFn; _registry[i].initFn = initFn;
if (_booted) invoke(_registry[i], _bootRoot || document, 'init');
return; return;
} }
} }
_registry.push({ name: name, initFn: initFn }); var entry = { name: name, initFn: initFn };
_registry.push(entry);
if (_booted) invoke(entry, _bootRoot || document, 'init');
},
/**
* Run the one-time initial page boot.
*
* @param {Element|Document} root The document root to initialize
*/
boot: function (root) {
if (_booted) return;
_booted = true;
_bootRoot = root || document;
this.rehydrate(_bootRoot, 'init');
dispatch(_bootRoot, 'init', 'aksrt:ready');
}, },
/** /**
@@ -53,21 +88,10 @@
reason = reason || 'init'; reason = reason || 'init';
for (var i = 0; i < _registry.length; i++) { for (var i = 0; i < _registry.length; i++) {
var entry = _registry[i]; invoke(_registry[i], root, reason);
try {
entry.initFn(root, reason);
} catch (err) {
console.error('[AKSRT Rehydrate] Error initializing "' + entry.name + '":', err);
}
} }
// Dispatch event so external scripts can hook in dispatch(root, reason, 'aksrt:rehydrated');
try {
root.dispatchEvent(new CustomEvent('aksrt:rehydrated', {
bubbles: true,
detail: { reason: reason }
}));
} catch (e) { /* IE fallback */ }
}, },
/** /**
@@ -75,6 +99,10 @@
*/ */
getRegistered: function () { getRegistered: function () {
return _registry.map(function (r) { return r.name; }); return _registry.map(function (r) { return r.name; });
},
isBooted: function () {
return _booted;
} }
}; };
+4 -5
View File
@@ -141,6 +141,8 @@
} }
function initReportButtons() { function initReportButtons() {
if (document.documentElement.dataset.aksrtReportInitialized === '1') return;
document.documentElement.dataset.aksrtReportInitialized = '1';
document.addEventListener('click', function (e) { document.addEventListener('click', function (e) {
var btn = e.target.closest('[data-aksrt-report]'); var btn = e.target.closest('[data-aksrt-report]');
if (!btn) return; if (!btn) return;
@@ -157,10 +159,7 @@
}); });
} }
// Auto-init on DOMContentLoaded if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-report', initReportButtons);
document.addEventListener('DOMContentLoaded', initReportButtons);
} else {
initReportButtons();
} }
})(); })();
+4 -5
View File
@@ -8,7 +8,10 @@
var cfg = window.aksrtData || {}; var cfg = window.aksrtData || {};
function initReward() { function initReward(root) {
if (root && root.nodeType === 1 && !root.matches('[data-aksrt-reward-root], body, html')) return;
if (document.documentElement.dataset.aksrtRewardInitialized === '1') return;
document.documentElement.dataset.aksrtRewardInitialized = '1';
var activeTrigger = null; var activeTrigger = null;
function operationKey(modal) { function operationKey(modal) {
@@ -125,9 +128,5 @@
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') { if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
window.AksrtFrontend.register('aksrt-reward', initReward); window.AksrtFrontend.register('aksrt-reward', initReward);
} else if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initReward);
} else {
initReward();
} }
})(); })();
+41 -58
View File
@@ -41,9 +41,11 @@
} }
/* ==================== Dashboard 导航 ==================== */ /* ==================== Dashboard 导航 ==================== */
function initDashboardNavigation() { function initDashboardNavigation() {
var tabContainer = document.querySelector('.uc-mobile-tabs'); if (document.documentElement.dataset.aksrtUserCenterNavigationInitialized === 'true') return;
if (!tabContainer) return; var tabContainer = document.querySelector('.uc-mobile-tabs');
if (!tabContainer) return;
document.documentElement.dataset.aksrtUserCenterNavigationInitialized = 'true';
var tabs = tabContainer.querySelectorAll('[data-tab]'); var tabs = tabContainer.querySelectorAll('[data-tab]');
var sidebarBtns = document.querySelectorAll('.uc-sidebar [data-tab]'); var sidebarBtns = document.querySelectorAll('.uc-sidebar [data-tab]');
@@ -322,22 +324,18 @@
}); });
} }
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') { function initAuthForm(root) {
window.AksrtFrontend.register('aksrt-profile-form', function(root) { root = root || document;
initProfileForm(root); var identityRoot = root.matches && root.matches('[data-identity-root]') ? root : root.querySelector('[data-identity-root]');
}); if (identityRoot && window.AKSRTUserCenterAuth && typeof window.AKSRTUserCenterAuth.init === 'function') {
} window.AKSRTUserCenterAuth.init(identityRoot);
}
function initAuthForm() {
var root = document.querySelector('[data-identity-root]');
if (root && window.AKSRTUserCenterAuth && typeof window.AKSRTUserCenterAuth.init === 'function') {
window.AKSRTUserCenterAuth.init(root);
} }
}
/* ==================== 列表内取消收藏 ==================== */ /* ==================== 列表内取消收藏 ==================== */
function initUnfavorite() { function initUnfavorite(root) {
var list = document.querySelector('.profile-list[data-list="favorites"]'); root = root || document;
var list = root.matches && root.matches('.profile-list[data-list="favorites"]') ? root : root.querySelector('.profile-list[data-list="favorites"]');
if (!list) return; if (!list) return;
list.addEventListener('click', function(e) { list.addEventListener('click', function(e) {
@@ -395,9 +393,9 @@
}); });
} }
function initPostDeletion() { function initPostDeletion(root) {
if (document.documentElement.dataset.aksrtPostDeletionBound === 'true') return; if (document.documentElement.dataset.aksrtPostDeletionBound === 'true') return;
document.documentElement.dataset.aksrtPostDeletionBound = 'true'; document.documentElement.dataset.aksrtPostDeletionBound = 'true';
document.addEventListener('click', function(e) { document.addEventListener('click', function(e) {
var btn = e.target && e.target.closest ? e.target.closest('[data-action="delete-post"]') : null; var btn = e.target && e.target.closest ? e.target.closest('[data-action="delete-post"]') : null;
if (!btn || btn.disabled || !confirm('确定删除这篇文章?')) return; if (!btn || btn.disabled || !confirm('确定删除这篇文章?')) return;
@@ -423,12 +421,14 @@
} }
/* ==================== 通知中心 ==================== */ /* ==================== 通知中心 ==================== */
function initNotifications() { function initNotifications(root) {
var list = document.getElementById('aksrt-notification-list'); root = root || document;
var markAllBtn = document.getElementById('aksrt-mark-all-notifications-read'); var list = root.matches && root.matches('#aksrt-notification-list') ? root : root.querySelector('#aksrt-notification-list');
var markSelectedBtn = document.getElementById('aksrt-mark-selected-notifications-read'); var markAllBtn = list && list.ownerDocument.getElementById('aksrt-mark-all-notifications-read');
var deleteSelectedBtn = document.getElementById('aksrt-delete-selected-notifications'); var markSelectedBtn = list && list.ownerDocument.getElementById('aksrt-mark-selected-notifications-read');
if (!list) return; var deleteSelectedBtn = list && list.ownerDocument.getElementById('aksrt-delete-selected-notifications');
if (!list || list.dataset.aksrtNotificationsInitialized === 'true') return;
list.dataset.aksrtNotificationsInitialized = 'true';
function markRead(notificationId, markAll) { function markRead(notificationId, markAll) {
var body = new URLSearchParams(); var body = new URLSearchParams();
@@ -1033,42 +1033,25 @@
} }
} }
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
window.AksrtFrontend.register('aksrt-settings', function(root) {
initSettings(root);
initBindPhone(root);
});
window.AksrtFrontend.register('aksrt-notification-preferences', function(root) {
initNotificationPreferences(root);
});
}
function initNavigationOnce() {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
initDashboardNavigation(); initDashboardNavigation();
initProfileForm();
initAuthForm();
initUnfavorite();
initPostDeletion(); initPostDeletion();
initNotifications(); }
initNotificationPreferences();
initSettings(); if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
initBindPhone(); window.AksrtFrontend.register('aksrt-user-center', function(root) {
initSettingsCallbackToast(); initNavigationOnce();
}); initProfileForm(root);
} else { initAuthForm(root);
initDashboardNavigation(); initUnfavorite(root);
initProfileForm(); initNotifications(root);
initAuthForm(); initNotificationPreferences(root);
initUnfavorite(); initSettings(root);
initPostDeletion(); initBindPhone(root);
initNotifications(); initSettingsCallbackToast();
initNotificationPreferences(); });
initSettings(); }
initBindPhone();
initSettingsCallbackToast();
}
// 绑定手机 // 绑定手机
function initBindPhone(root) { function initBindPhone(root) {
-9
View File
@@ -59,17 +59,8 @@ tbquire.config({
'translate' : 'libs/translate.min', 'translate' : 'libs/translate.min',
'waterfall' : 'waterfall.min', 'waterfall' : 'waterfall.min',
'enlighterjs' : 'enlighter/enlighterjs.min', 'enlighterjs' : 'enlighter/enlighterjs.min',
'section-navs' : 'section-navs.min',
'poster-share' : 'poster-share.min',
'comment' : 'comment.min',
'page-template' : 'page-template.min',
'page-edit' : 'page-edit.min',
'author' : 'author.min',
'page-navs' : 'page-navs.min',
'input-expand' : 'input-expand.min',
'mini-touch' : 'mini-touch.min', 'mini-touch' : 'mini-touch.min',
'mini-upload' : 'mini-upload.min',
'editextend' : 'editextend.min', 'editextend' : 'editextend.min',
'petite-vue' : 'libs/petite-vue.iife', 'petite-vue' : 'libs/petite-vue.iife',
'svg-icon' : 'svg-icon', 'svg-icon' : 'svg-icon',
+16 -39
View File
@@ -1872,7 +1872,7 @@ function post_ajax(_this, con, jcon, item, loader, pag, next, trigger, replace,
} }
swiper_tab_AutoHeight($con); swiper_tab_AutoHeight($con);
auto_fun(); window.AksrtFrontend.rehydrate($con[0], 'ajax');
}, },
}); });
return !1; return !1;
@@ -1884,7 +1884,7 @@ _win.bd.on('click', '[remote-box]', function () {
if (remote) { if (remote) {
jQuery.post(remote, null, function (data) { jQuery.post(remote, null, function (data) {
_this.html(data).removeAttr('remote-box'); _this.html(data).removeAttr('remote-box');
auto_fun(); window.AksrtFrontend.rehydrate(_this[0], 'ajax');
_this.trigger('aksrt:remote-loaded'); _this.trigger('aksrt:remote-loaded');
}); });
} }
@@ -2176,7 +2176,7 @@ _win.bd.on('show.bs.modal loaded.bs.modal', '.modal:not(.flex)', function () {
}); });
_win.bd.on('show.bs.modal loaded.bs.modal', '.modal', function () { _win.bd.on('show.bs.modal loaded.bs.modal', '.modal', function () {
auto_fun(); window.AksrtFrontend.rehydrate(this, 'modal');
}); });
_win.modal_z_index = 1050; _win.modal_z_index = 1050;
@@ -2420,7 +2420,9 @@ function z_auto() {
} }
} }
window.auto_fun = debounce(z_auto_fun, 100); window.auto_fun = debounce(function (root) {
window.AksrtFrontend.rehydrate(root && root.nodeType ? root : document, 'ajax');
}, 100);
function z_auto_fun() { function z_auto_fun() {
jQuery(document).trigger('auto_fun'); jQuery(document).trigger('auto_fun');
@@ -2433,9 +2435,6 @@ function z_auto_fun() {
//可复制 //可复制
jQuery('.cloneable').cloneable(); jQuery('.cloneable').cloneable();
//倒计时
jQuery('[data-countdown]').countdown();
auto_maxHeight(); auto_maxHeight();
//兼容性图片懒加载 //兼容性图片懒加载
jQuery('img[data-src]:not(.lazyload,.lazyloaded,.lazyloading,.swiper-lazy)').addClass('lazyload'); jQuery('img[data-src]:not(.lazyload,.lazyloaded,.lazyloading,.swiper-lazy)').addClass('lazyload');
@@ -2446,13 +2445,6 @@ function z_auto_fun() {
}); });
*/ */
jQuery('.dropdown-code,.dropdown-image,.dropdown-quick-often').length && tbquire(['input-expand']);
/**上传图片 */
jQuery('[aksrtupload]').length && tbquire(['mini-upload']);
//海报分享
jQuery('[poster-share]').length && tbquire(['poster-share']); //海报分享
/* 提示工具*/ /* 提示工具*/
jQuery("[data-toggle='tooltip']").tooltip({ jQuery("[data-toggle='tooltip']").tooltip({
container: 'body', container: 'body',
@@ -3095,21 +3087,6 @@ _win.bd.on('input focus', 'textarea[autoHeight]', function () {
_win.bd.on('click', '.rewards', function () { _win.bd.on('click', '.rewards', function () {
jQuery('#rewards-popover').modal('show'); jQuery('#rewards-popover').modal('show');
}); });
//用户中心
_win.bd.hasClass('author') && tbquire(['author']);
//文章导航
_win.bd.hasClass('page-template-postsnavs') && _win.bd.hasClass('logged-admin') && tbquire(['page-navs']);
//前台编辑
jQuery('#modal_admin_set').length && tbquire(['page-edit']);
//评论
jQuery('#commentform,#postcomments').length && tbquire(['comment']);
//通用模板js文件
_win.bd.hasClass('page-template') && tbquire(['page-template']);
//微信分享
window.WeChatShareDate && tbquire(['weixin-share']);
//搜索关键词高亮 //搜索关键词高亮
if (_win.bd.hasClass('search')) { if (_win.bd.hasClass('search')) {
var val = jQuery('.search-desc-text .search-key').text(); var val = jQuery('.search-desc-text .search-key').text();
@@ -3520,7 +3497,7 @@ jQuery.fn.AutoSearch = function () {
_this.removeClass(loading); _this.removeClass(loading);
_remind.html(result.remind || ''); _remind.html(result.remind || '');
_icon.html(_icon_html); _icon.html(_icon_html);
auto_fun(); window.AksrtFrontend.rehydrate(_container[0], 'ajax');
}, },
'json' 'json'
); );
@@ -3969,12 +3946,6 @@ function posts_limit_height() {
} }
} }
auto_fun();
thumb_dplayer(_wid > 640 ? '.posts-item .item-thumbnail,.forum-lists-cover' : '.posts-item,.forum-posts', '.video-thumb-box[video-url]');
//页面加载之后自动点击
jQuery('[load-click]').attr('no-scroll', true).click();
//图片延迟懒加载-ias自动加载 //图片延迟懒加载-ias自动加载
jQuery(document).on('lazybeforeunveil', function (e) { jQuery(document).on('lazybeforeunveil', function (e) {
var _this = jQuery(e.target); var _this = jQuery(e.target);
@@ -4553,7 +4524,7 @@ _win.bd.on('click', '.customize-preview-widget-edit', function (e) {
}); });
//页面加载完毕之后加载的函数 //页面加载完毕之后加载的函数
jQuery(document).ready(function () { function initLegacyDocumentReady() {
setTimeout(function () { setTimeout(function () {
//页面加载完毕之后再延迟两秒加载的函数 //页面加载完毕之后再延迟两秒加载的函数
@@ -4700,8 +4671,6 @@ jQuery(document).ready(function () {
}); });
/*文章目录*/ /*文章目录*/
jQuery('[data-nav] h1,[data-nav] h2,[data-nav] h3,[data-nav] h4').length > 2 && tbquire(['section-navs']);
//手势控制 //手势控制
jQuery('[mini-touch]').each(function () { jQuery('[mini-touch]').each(function () {
var _this = jQuery(this); var _this = jQuery(this);
@@ -4718,4 +4687,12 @@ jQuery(document).ready(function () {
}, },
}); });
}); });
}
window.AksrtFrontend.register('aksrt-legacy-main', function (root, reason) {
z_auto_fun();
if (reason !== 'init') return;
initLegacyDocumentReady();
thumb_dplayer(_wid > 640 ? '.posts-item .item-thumbnail,.forum-lists-cover' : '.posts-item,.forum-posts', '.video-thumb-box[video-url]');
jQuery('[load-click]').attr('no-scroll', true).click();
}); });
+1 -1
View File
File diff suppressed because one or more lines are too long
-46
View File
@@ -1,46 +0,0 @@
//文章导航
jQuery("button[evt='action']").on("click", function (t) {
t = t || window.event;
var a = t.target || t.srcElement,
e = jQuery(a),
n = e.parents("form"),
r = n.serializeObject(),
c = [];
return jQuery("#cat-sortable-ok .sortable-cat-item").each(function () {
c.push(jQuery(this).attr("data-id"));
}), r.navs_show_cat_id = c, save_userdata(r, aksrt__("save_success"), "reload"), !1;
});
function save_userdata(b, c, d) {
notyf(aksrt__("processing_wait"), "load", "", "user_ajax"),
c = c || aksrt__("done");
jQuery.ajax({
type: "POST",
url: _win.uri + '/action/post_navs.php',
data: b,
dataType: "json",
success: function (n) {
if (n.error) {
n.msg && notyf(n.msg, (n.error ? 'danger' : ""), "", "user_ajax");
} else {
notyf(n.msg || c, "", "", "user_ajax");
"reload" == d && location.reload();
}
}
});
}
jQuery(document).ready(function () {
jQuery("#cat-sortable").length && tbquire(["sortablejs"], function () {
var e = document.getElementById("cat-sortable"),
t = document.getElementById("cat-sortable-ok"),
a = {
group: "cat_shared",
multiDrag: !0,
chosenClass: "chosen",
fallbackClass: !0,
animation: 400
};
Sortable.create(e, a), Sortable.create(t, a);
});
});
-1
View File
@@ -1 +0,0 @@
function save_userdata(a,t,e){notyf(aksrt__("processing_wait"),"load","","user_ajax"),t=t||aksrt__("done"),jQuery.ajax({type:"POST",url:_win.uri+"/action/post_navs.php",data:a,dataType:"json",success:function(a){a.error?a.msg&&notyf(a.msg,a.error?"danger":"","","user_ajax"):(notyf(a.msg||t,"","","user_ajax"),"reload"==e&&location.reload())}})}jQuery("button[evt='action']").on("click",function(a){a=a||window.event;var t=a.target||a.srcElement,e=jQuery(t),o=e.parents("form"),r=o.serializeObject(),s=[];return jQuery("#cat-sortable-ok .sortable-cat-item").each(function(){s.push(jQuery(this).attr("data-id"))}),r.navs_show_cat_id=s,save_userdata(r,aksrt__("save_success"),"reload"),!1}),jQuery(document).ready(function(){jQuery("#cat-sortable").length&&tbquire(["sortablejs"],function(){var a=document.getElementById("cat-sortable"),t=document.getElementById("cat-sortable-ok"),e={group:"cat_shared",multiDrag:!0,chosenClass:"chosen",fallbackClass:!0,animation:400};Sortable.create(a,e),Sortable.create(t,e)})});
+27 -22
View File
@@ -40,8 +40,10 @@
return count >= MIN_HEADINGS ? html : ''; return count >= MIN_HEADINGS ? html : '';
} }
function fillBoxes(listsHtml) { function fillBoxes(listsHtml, scope) {
document.querySelectorAll(NAVBOX).forEach(function (box) { var boxes = Array.prototype.slice.call(scope.querySelectorAll(NAVBOX));
if (scope.nodeType === 1 && scope.matches(NAVBOX)) boxes.unshift(scope);
boxes.forEach(function (box) {
var existing = box.querySelector('.' + NAV_CLASS); var existing = box.querySelector('.' + NAV_CLASS);
if (existing) { if (existing) {
existing.innerHTML = listsHtml; existing.innerHTML = listsHtml;
@@ -75,7 +77,13 @@
window.scrollTo({ top: top, behavior: 'smooth' }); window.scrollTo({ top: top, behavior: 'smooth' });
} }
var clicksBound = false;
var scrollspyBound = false;
var autoHashHandled = false;
function bindClicks() { function bindClicks() {
if (clicksBound) return;
clicksBound = true;
document.addEventListener('click', function (e) { document.addEventListener('click', function (e) {
var a = e.target && e.target.closest ? e.target.closest('.' + NAV_CLASS + ' a[href^="#"]') : null; var a = e.target && e.target.closest ? e.target.closest('.' + NAV_CLASS + ' a[href^="#"]') : null;
if (!a) return; if (!a) return;
@@ -87,9 +95,11 @@
}); });
} }
function scrollspy() { function scrollspy(scope) {
var links = document.querySelectorAll('.' + NAV_CLASS + ' a[href^="#"]'); if (scrollspyBound) return;
var links = scope.querySelectorAll('.' + NAV_CLASS + ' a[href^="#"]');
if (!links.length) return; if (!links.length) return;
scrollspyBound = true;
var map = []; var map = [];
links.forEach(function (a) { links.forEach(function (a) {
var id = a.getAttribute('href').slice(1); var id = a.getAttribute('href').slice(1);
@@ -111,37 +121,32 @@
} }
function autoHash() { function autoHash() {
if (window.location.hash) { if (autoHashHandled || !window.location.hash) return;
setTimeout(function () { autoHashHandled = true;
smoothTo(window.location.hash); setTimeout(function () {
}, 40); smoothTo(window.location.hash);
} }, 40);
} }
function init() { function init(scope) {
scope = scope || document;
if (document.querySelector('.posts-nav-box .toc-list, .posts-nav-box.toc-widget .toc-list')) { if (document.querySelector('.posts-nav-box .toc-list, .posts-nav-box.toc-widget .toc-list')) {
return; return;
} }
var root = document.querySelector('[data-nav], .article-content, .entry-content, main.article'); var root = scope.nodeType === 1 && scope.matches('[data-nav], .article-content, .entry-content, main.article')
? scope
: scope.querySelector('[data-nav], .article-content, .entry-content, main.article');
if (!root) return; if (!root) return;
var listsHtml = buildLists(root); var listsHtml = buildLists(root);
if (!listsHtml) return; if (!listsHtml) return;
if (!document.querySelector(NAVBOX) && !document.querySelector('.' + NAV_CLASS)) { if (!document.querySelector(NAVBOX) && !document.querySelector('.' + NAV_CLASS)) {
return; return;
} }
fillBoxes(listsHtml); fillBoxes(listsHtml, document);
bindClicks(); bindClicks();
scrollspy(); scrollspy(document);
autoHash(); autoHash();
} }
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') { window.AksrtFrontend.register('aksrt-section-navs', init);
window.AksrtFrontend.register('aksrt-section-navs', function () {
init();
});
} else if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})(); })();
+17 -12
View File
@@ -29,8 +29,11 @@
}); });
} }
function initSignin() { function initSignin(scope) {
var buttons = Array.prototype.slice.call(document.querySelectorAll('[data-signin-btn]')); scope = scope || document;
var buttons = [];
if (scope.nodeType === 1 && scope.matches('[data-signin-btn]')) buttons.push(scope);
if (scope.querySelectorAll) scope.querySelectorAll('[data-signin-btn]').forEach(function (button) { buttons.push(button); });
var freshButtons = buttons.filter(function (button) { var freshButtons = buttons.filter(function (button) {
return button.dataset.aksrtSigninInitialized !== '1'; return button.dataset.aksrtSigninInitialized !== '1';
}); });
@@ -40,12 +43,19 @@
button.dataset.aksrtSigninInitialized = '1'; button.dataset.aksrtSigninInitialized = '1';
}); });
function elements(selector) {
var items = [];
if (scope.nodeType === 1 && scope.matches(selector)) items.push(scope);
if (scope.querySelectorAll) scope.querySelectorAll(selector).forEach(function (item) { items.push(item); });
return items;
}
function statuses() { function statuses() {
return document.querySelectorAll('[data-signin-status]'); return elements('[data-signin-status]');
} }
function currentButtons() { function currentButtons() {
return document.querySelectorAll('[data-signin-btn]'); return elements('[data-signin-btn]');
} }
function setStatus(text) { function setStatus(text) {
@@ -98,7 +108,8 @@
} }
function captchaMount() { function captchaMount() {
return document.querySelector('[data-signin-captcha]'); if (scope.nodeType === 1 && scope.matches('[data-signin-captcha]')) return scope;
return scope.querySelector ? scope.querySelector('[data-signin-captcha]') : null;
} }
function doSignin(captchaPayload) { function doSignin(captchaPayload) {
@@ -115,7 +126,7 @@
} }
notyf(result.json.message || '签到成功', 'success', 5, 'aksrt-balance-signin-toast'); notyf(result.json.message || '签到成功', 'success', 5, 'aksrt-balance-signin-toast');
document.querySelectorAll('[data-credits-value]').forEach(function (element) { elements('[data-credits-value]').forEach(function (element) {
element.textContent = Number(result.json.balance || 0).toLocaleString(); element.textContent = Number(result.json.balance || 0).toLocaleString();
}); });
setButtonState('done'); setButtonState('done');
@@ -149,12 +160,6 @@
refresh(); refresh();
} }
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSignin);
} else {
initSignin();
}
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') { if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
window.AksrtFrontend.register('signin', initSignin); window.AksrtFrontend.register('signin', initSignin);
} }
+6 -2
View File
@@ -10,8 +10,11 @@
(function () { (function () {
'use strict'; 'use strict';
var el = document.getElementById('aksrt-site-notice'); window.AksrtFrontend.register('aksrt-site-notice', function (root) {
if (!el) return; root = root || document;
var el = root.nodeType === 1 && root.matches('#aksrt-site-notice') ? root : root.querySelector('#aksrt-site-notice');
if (!el || el.dataset.aksrtNoticeBound === '1') return;
el.dataset.aksrtNoticeBound = '1';
// 弹窗周期(小时),0=永不过期 // 弹窗周期(小时),0=永不过期
var expiresHours = parseInt(el.dataset.expires || '24', 10) || 0; var expiresHours = parseInt(el.dataset.expires || '24', 10) || 0;
@@ -88,4 +91,5 @@
// Delay slightly so it doesn't flash on fast navigations // Delay slightly so it doesn't flash on fast navigations
setTimeout(show, 500); setTimeout(show, 500);
} }
});
})(); })();
+8 -2
View File
@@ -19,7 +19,10 @@
if (typeof window.notyf === 'function') window.notyf(message, type); if (typeof window.notyf === 'function') window.notyf(message, type);
} }
document.addEventListener('click', function (event) { function bindOnce() {
if (document.__aksrtUserBadgeActionsBound) return;
document.__aksrtUserBadgeActionsBound = true;
document.addEventListener('click', function (event) {
var button = event.target.closest('[data-aksrt-badge-action]'); var button = event.target.closest('[data-aksrt-badge-action]');
if (!button || button.classList.contains('is-loading')) return; if (!button || button.classList.contains('is-loading')) return;
@@ -56,5 +59,8 @@
button.removeAttribute('aria-busy'); button.removeAttribute('aria-busy');
notice(error.message || '网络错误', 'danger'); notice(error.message || '网络错误', 'danger');
}); });
}); });
}
window.AksrtFrontend.register('aksrt-user-badge-actions', bindOnce);
})(window, document); })(window, document);
+14 -11
View File
@@ -219,7 +219,10 @@
}; };
// ── 剧集切换按钮事件委托 ── // ── 剧集切换按钮事件委托 ──
document.addEventListener('click', function (e) { function bindEpisodeSwitch() {
if (document.__aksrtVideoEpisodeBound) return;
document.__aksrtVideoEpisodeBound = true;
document.addEventListener('click', function (e) {
var switchBtn = e.target.closest('.switch-video, .switch-iframe'); var switchBtn = e.target.closest('.switch-video, .switch-iframe');
if (!switchBtn) return; if (!switchBtn) return;
@@ -256,24 +259,24 @@
}); });
switchBtn.classList.add('is-active'); switchBtn.classList.add('is-active');
switchBtn.classList.add('active'); switchBtn.classList.add('active');
}); });
}
// ── Public API ── // ── Public API ──
window.AksrtVideoPlayer = AksrtVideoPlayer; window.AksrtVideoPlayer = AksrtVideoPlayer;
// ── Auto-init ── function init(root) {
root = root || document;
function init() {
injectStyle(); injectStyle();
document.querySelectorAll('[data-aksrt-video]').forEach(function (el) { bindEpisodeSwitch();
var elements = [];
if (root && root.nodeType === 1 && root.matches('[data-aksrt-video]')) elements.push(root);
Array.prototype.push.apply(elements, root.querySelectorAll('[data-aksrt-video]'));
elements.forEach(function (el) {
new AksrtVideoPlayer(el); new AksrtVideoPlayer(el);
}); });
} }
if (document.readyState === 'loading') { window.AksrtFrontend.register('aksrt-video-player', init);
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})(); })();
+19 -11
View File
@@ -154,24 +154,32 @@
}); });
} }
function autoInit() { function autoInit(root) {
document.querySelectorAll('[data-aksrt-waterfall]').forEach(function (el) { var elements = [];
if (root && root.nodeType === 1 && root.matches('[data-aksrt-waterfall]')) elements.push(root);
Array.prototype.push.apply(elements, (root || document).querySelectorAll('[data-aksrt-waterfall]'));
elements.forEach(function (el) {
if (el._aksrtWaterfall) return;
var cols = parseInt(el.dataset.aksrtWaterfallColumns) || 0; var cols = parseInt(el.dataset.aksrtWaterfallColumns) || 0;
var gap = parseInt(el.dataset.aksrtWaterfallGap) || 16; var gap = parseInt(el.dataset.aksrtWaterfallGap) || 16;
waterfallInstances.push(new Waterfall(el, { columns: cols, gap: gap })); el._aksrtWaterfall = new Waterfall(el, { columns: cols, gap: gap });
waterfallInstances.push(el._aksrtWaterfall);
}); });
} }
document.addEventListener('post_ajax.ed', relayoutAll); function bindGlobalEvents() {
document.addEventListener('aksrt:posts:rendered', relayoutAll); if (document.__aksrtWaterfallEventsBound) return;
document.addEventListener('lazyloaded', relayoutAll, true); document.__aksrtWaterfallEventsBound = true;
document.addEventListener('post_ajax.ed', relayoutAll);
if (document.readyState === 'loading') { document.addEventListener('aksrt:posts:rendered', relayoutAll);
document.addEventListener('DOMContentLoaded', autoInit); document.addEventListener('lazyloaded', relayoutAll, true);
} else {
autoInit();
} }
window.AksrtFrontend.register('aksrt-waterfall', function (root) {
bindGlobalEvents();
autoInit(root);
});
// Export to global // Export to global
window.AksrtWaterfall = Waterfall; window.AksrtWaterfall = Waterfall;
window.AksrtInfiniteScroll = InfiniteScroll; window.AksrtInfiniteScroll = InfiniteScroll;
+5 -2
View File
@@ -249,7 +249,10 @@
}); });
} }
document.addEventListener('DOMContentLoaded', function () { window.AksrtFrontend.register('aksrt-yiyan', function (root) {
document.querySelectorAll('[data-aksrt-yiyan]').forEach(initYiyan); var elements = [];
if (root && root.nodeType === 1 && root.matches('[data-aksrt-yiyan]')) elements.push(root);
Array.prototype.push.apply(elements, (root || document).querySelectorAll('[data-aksrt-yiyan]'));
elements.forEach(initYiyan);
}); });
})(); })();
+21 -21
View File
@@ -166,7 +166,7 @@
} }
if (data.paginate_html) container.insertAdjacentHTML('beforeend', data.paginate_html); if (data.paginate_html) container.insertAdjacentHTML('beforeend', data.paginate_html);
container.dataset.page = String(data.page || page); container.dataset.page = String(data.page || page);
scan(container); window.AksrtFrontend.rehydrate(container, 'ajax');
}) })
.catch(function () { container.innerHTML = errorMarkup(); }) .catch(function () { container.innerHTML = errorMarkup(); })
.finally(function () { .finally(function () {
@@ -262,28 +262,28 @@
requestAnimationFrame(frame); requestAnimationFrame(frame);
} }
function elementsIn(root, selector) {
var scope = root || document;
var elements = [];
if (scope.matches && scope.matches(selector)) elements.push(scope);
if (scope.querySelectorAll) {
scope.querySelectorAll(selector).forEach(function (element) { elements.push(element); });
}
return elements;
}
function scan(root) { function scan(root) {
var scope = root || document; var scope = root || document;
scope.querySelectorAll('.aksrt-widget-video[data-video-url]').forEach(initVideo); elementsIn(scope, '.aksrt-widget-video[data-video-url]').forEach(initVideo);
scope.querySelectorAll('.aksrt-widget-toc').forEach(initToc); elementsIn(scope, '.aksrt-widget-toc').forEach(initToc);
scope.querySelectorAll('.aksrt-widget-quote').forEach(initQuote); elementsIn(scope, '.aksrt-widget-quote').forEach(initQuote);
scope.querySelectorAll('[data-widget-items]').forEach(initItems); elementsIn(scope, '[data-widget-items]').forEach(initItems);
scope.querySelectorAll('.aksrt-widget-tabs, .widget-tab-post').forEach(initTabs); elementsIn(scope, '.aksrt-widget-tabs, .widget-tab-post').forEach(initTabs);
scope.querySelectorAll('.aksrt-faq').forEach(initFaq); elementsIn(scope, '.aksrt-faq').forEach(initFaq);
scope.querySelectorAll('.aksrt-feature-intro').forEach(initFeatures); elementsIn(scope, '.aksrt-feature-intro').forEach(initFeatures);
scope.querySelectorAll('[data-count-up]').forEach(initCount); elementsIn(scope, '[data-count-up]').forEach(initCount);
if (typeof window.VanillaTilt !== 'undefined') scope.querySelectorAll('[data-aksrt-tilt]').forEach(function (el) { if (once(el, 'Tilt')) window.VanillaTilt.init(el); }); if (typeof window.VanillaTilt !== 'undefined') elementsIn(scope, '[data-aksrt-tilt]').forEach(function (el) { if (once(el, 'Tilt')) window.VanillaTilt.init(el); });
} }
function ready() { window.AksrtFrontend.register('aksrt-widgets', scan);
scan(document);
if ('MutationObserver' in window) {
new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) { mutation.addedNodes.forEach(function (node) { if (node.nodeType === 1) scan(node); }); });
}).observe(document.body, { childList: true, subtree: true });
}
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', ready);
else ready();
})(); })();
+1 -1
View File
@@ -469,7 +469,7 @@ trait ScriptsPart1b {
wp_enqueue_script( wp_enqueue_script(
'aksrt-section-navs', 'aksrt-section-navs',
$base_uri . '/assets/js/section-navs.js', $base_uri . '/assets/js/section-navs.js',
['jquery'], ['aksrt-runtime'],
(string) filemtime($section_navs), (string) filemtime($section_navs),
true true
); );
+3 -3
View File
@@ -58,8 +58,8 @@ if (file_exists($js)) {
wp_enqueue_script( wp_enqueue_script(
'aksrt-front-submit-pay-download', 'aksrt-front-submit-pay-download',
get_template_directory_uri() . '/assets/js/front-submit-pay-download.js', get_template_directory_uri() . '/assets/js/front-submit-pay-download.js',
[], ['aksrt-runtime'],
(string) filemtime($pay_download_js), (string) filemtime($pay_download_js),
true true
); );
wp_enqueue_script( wp_enqueue_script(
@@ -74,7 +74,7 @@ if (file_exists($js)) {
wp_enqueue_script( wp_enqueue_script(
'aksrt-mini-upload', 'aksrt-mini-upload',
get_template_directory_uri() . '/assets/js/mini-upload.js', get_template_directory_uri() . '/assets/js/mini-upload.js',
[], ['aksrt-runtime'],
(string) filemtime($mini), (string) filemtime($mini),
true true
); );
@@ -72,7 +72,11 @@ function createHarness() {
}); });
let providerCallback; let providerCallback;
let captchaInit = null;
const window = { const window = {
AksrtFrontend: {
register(_name, init) { captchaInit = init; },
},
fetch(url) { fetch(url) {
if (url === '/challenge') return Promise.resolve({ ok: true, json: () => Promise.resolve({ success: true, data: { challenge: 'challenge-token' } }) }); if (url === '/challenge') return Promise.resolve({ ok: true, json: () => Promise.resolve({ success: true, data: { challenge: 'challenge-token' } }) });
if (url === '/exchange') return Promise.resolve({ ok: true, json: () => Promise.resolve({ success: true, data: { proof: 'proof-token' } }) }); if (url === '/exchange') return Promise.resolve({ ok: true, json: () => Promise.resolve({ success: true, data: { proof: 'proof-token' } }) });
@@ -87,6 +91,7 @@ function createHarness() {
Promise, Promise,
}; };
vm.runInNewContext(runtime, { window, document, Promise, MutationObserver: undefined, setTimeout, clearTimeout, console }); vm.runInNewContext(runtime, { window, document, Promise, MutationObserver: undefined, setTimeout, clearTimeout, console });
if (captchaInit) captchaInit(document, 'init');
return { api: window.AKSRTCaptcha, container }; return { api: window.AKSRTCaptcha, container };
} }
@@ -0,0 +1,117 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
const publicSources = [
'assets/js/core/comment-fixed-input.js',
'assets/js/core/misc.js',
'assets/js/follow.js',
'assets/js/enhancements.js',
'assets/js/enhancements2.js',
'assets/js/color-extract.js',
'assets/js/effects.js',
'assets/js/captcha-front.js',
'assets/widgets/frontend.js',
];
const routedSources = [
'assets/js/auth.js',
'assets/js/author.js',
'assets/js/routes/user-center.js',
'assets/js/order-center.js',
'assets/js/credits.js',
'assets/js/signin.js',
'assets/js/reward.js',
'assets/js/report.js',
'assets/js/page-navs.js',
'assets/js/section-navs.js',
'assets/js/posts-navs-page.js',
'assets/js/front-submit.js',
'assets/js/mini-upload.js',
'assets/js/page-template.js',
'assets/js/page-edit.js',
'assets/js/precode.js',
'assets/js/post-share.js',
'assets/js/poster-share.js',
'assets/js/pay-vip.js',
'assets/js/video-player.js',
'assets/js/download-status.js',
'assets/js/ban-appeal-page.js',
'assets/js/docs-navigation.js',
'assets/js/input-expand.js',
'assets/js/waterfall.js',
'assets/js/yiyan.js',
'assets/js/messages.js',
'assets/js/site-notice.js',
'assets/js/user-badge-actions.js',
];
const readyPattern = /(?:document\.addEventListener\(\s*['"]DOMContentLoaded['"]|document\.readyState\s*===|jQuery\(document\)\.ready\s*\()/;
test('global DOM boot is the only initial page lifecycle owner', () => {
const runtime = read('assets/js/aksrt-runtime.js');
const registry = read('assets/js/rehydrate.js');
assert.match(registry, /boot:\s*function/);
assert.match(registry, /register:[\s\S]*?_booted/);
assert.match(runtime, /AksrtFrontend\.boot\(root\)/);
assert.match(runtime, /aksrt\.autoFun\(document, 'init'\)/);
assert.match(runtime, /reason === 'init'[\s\S]*?AksrtFrontend\.boot\(root\)[\s\S]*?else[\s\S]*?AksrtFrontend\.rehydrate\(root, reason\)/);
});
test('public bundle components register instead of owning DOM ready', () => {
for (const file of publicSources) {
const source = read(file);
assert.doesNotMatch(source, readyPattern, `${file} still owns a DOM-ready boot`);
assert.match(source, /AksrtFrontend\.register\(/, `${file} must register with the global lifecycle`);
}
});
test('active routed UI scripts register with the global lifecycle', () => {
for (const file of routedSources) {
const source = read(file);
if (/API|coordinator|runtime/i.test(source.slice(0, 500)) && !/querySelector|addEventListener/.test(source)) continue;
assert.doesNotMatch(source, readyPattern, `${file} still owns a DOM-ready boot`);
assert.match(source, /AksrtFrontend\.register\(/, `${file} must register with the global lifecycle`);
}
});
test('high-risk routed components remain idempotent under global rehydrate', () => {
const pageNavs = read('assets/js/page-navs.js');
const sectionNavs = read('assets/js/section-navs.js');
const poster = read('assets/js/poster-share.js');
assert.match(pageNavs, /__aksrtPageNavInitialized/);
assert.match(pageNavs, /var grid = self\.root\.querySelector\('\.aksrt-doc-grid'\)/);
assert.match(sectionNavs, /if \(scrollspyBound\) return/);
assert.match(sectionNavs, /if \(autoHashHandled \|\| !window\.location\.hash\) return/);
assert.match(poster, /triggers\.filter\(function \(el\) \{ return el\.dataset\.posterInit !== '1'; \}\)/);
assert.doesNotMatch(poster, /triggers\.some\(function \(el\) \{ return el\.dataset\.posterInit === '1'; \}\)/);
});
test('resource and SDK load listeners are not mistaken for page boot', () => {
const captcha = read('assets/js/captcha-front.js');
const color = read('assets/js/color-extract.js');
assert.match(captcha, /\.onload\s*=|addEventListener\(['"]load['"]/);
assert.match(color, /addEventListener\(['"]load['"]/);
assert.doesNotMatch(captcha, /DOMContentLoaded/);
assert.doesNotMatch(color, /DOMContentLoaded/);
});
test('route scripts that use the registry load after the public runtime', () => {
const scripts = read('src/Core/ScriptsPart1b.php') + read('src/Core/ScriptsPart1c.php');
const files = ['section-navs.js', 'page-navs.js', 'posts-navs-page.js', 'author.js', 'auth.js'];
for (const file of files) {
const uri = scripts.indexOf("/assets/js/" + file);
assert.notEqual(uri, -1, `${file} must be enqueued`);
const blockStart = scripts.lastIndexOf('wp_enqueue_script(', uri);
const blockEnd = scripts.indexOf(');', uri);
const block = scripts.slice(blockStart, blockEnd + 2);
assert.match(block, /aksrt-runtime/, `${file} must depend on aksrt-runtime`);
}
});
@@ -0,0 +1,85 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
test('homepage uses the global DOM lifecycle without a second ready path', () => {
const runtime = read('assets/js/aksrt-runtime.js');
const homepage = read('assets/js/homepage-fixed.js');
assert.match(runtime, /aksrt\.autoFun\s*=\s*function\s*\(root, reason\)/);
assert.match(runtime, /function initRuntime\(\)\s*\{\s*aksrt\.autoFun\(document, 'init'\);/s);
assert.doesNotMatch(runtime, /function initRuntime\(\)\s*\{\s*aksrt\.autoFun\(document\);/s);
assert.match(homepage, /window\.AksrtFrontend\.register\('aksrt-homepage', init\)/);
assert.doesNotMatch(homepage, /DOMContentLoaded/);
assert.doesNotMatch(homepage, /document\.querySelectorAll\('\[data-sort-dropdown-trigger\]'\)/);
assert.doesNotMatch(homepage, /document\.querySelectorAll\('\.aksrt-homepage__stream'\)/);
assert.doesNotMatch(homepage, /document\.querySelectorAll\('\.aksrt-homepage__tabs\.home-tab-nav-box'\)/);
assert.match(homepage, /dataset\.aksrtHomeTabsBound === '1'/);
assert.match(homepage, /dataset\.aksrtSortDropdownBound === '1'/);
assert.match(homepage, /dataset\.aksrtSortbarBound === '1'/);
assert.match(homepage, /dataset\.aksrtStickyTabsBound === '1'/);
});
test('homepage initializer is registered once and remains idempotent across rehydrate calls', () => {
const source = read('assets/js/homepage-fixed.js');
const registrations = [];
let sortbarClickBindings = 0;
const sortbar = {
dataset: {},
getAttribute() {
return null;
},
addEventListener(type) {
if (type === 'click') sortbarClickBindings++;
},
};
const stream = {
querySelector(selector) {
return selector === '.aksrt-homepage__sortbar' ? sortbar : null;
},
};
const scope = {
querySelectorAll(selector) {
return selector === '.aksrt-homepage__stream' ? [stream] : [];
},
};
const context = {
window: {
AksrtFrontend: {
register(name, init) {
registrations.push({ name, init });
},
},
},
document: scope,
console,
URLSearchParams,
URL,
setTimeout,
clearTimeout,
};
context.window.window = context.window;
context.window.document = scope;
vm.runInNewContext(source, context, { filename: 'homepage-fixed.js' });
assert.equal(registrations.length, 1);
assert.equal(registrations[0].name, 'aksrt-homepage');
assert.equal(typeof registrations[0].init, 'function');
registrations[0].init(scope, 'init');
registrations[0].init(scope, 'ajax');
assert.equal(sortbarClickBindings, 1);
assert.equal(sortbar.dataset.aksrtSortbarBound, '1');
});
test('homepage AJAX content continues to use the shared ajax rehydrate contract', () => {
const homepage = read('assets/js/homepage-fixed.js');
assert.match(homepage, /window\.AksrtFrontend\.rehydrate\(panel, 'ajax'\)/);
});
@@ -149,7 +149,9 @@ test('OAuth result handler maps safe errors and preserves unrelated URL state',
activeElement: null, activeElement: null,
body: { classList: { add() {}, remove() {} } }, body: { classList: { add() {}, remove() {} } },
} }
let authInit = null
const window = { const window = {
AksrtFrontend: { register: (_name, init) => { authInit = init } },
aksrtData: {}, aksrtData: {},
URL, URL,
location, location,
@@ -175,6 +177,7 @@ test('OAuth result handler maps safe errors and preserves unrelated URL state',
Math, Math,
Promise, Promise,
}) })
authInit(document, 'init')
assert.deepEqual(notices, [{ message: '第三方授权失败,请重试', type: 'danger' }]) assert.deepEqual(notices, [{ message: '第三方授权失败,请重试', type: 'danger' }])
assert.equal(replaced, '/sign/?keep=1#section') assert.equal(replaced, '/sign/?keep=1#section')
assert.equal(replaced.includes('script'), false) assert.equal(replaced.includes('script'), false)
@@ -0,0 +1,44 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
test('public bundle has one source list and one generated legacy main artifact', () => {
const build = read('tools/build-public-assets.mjs');
assert.match(build, /'assets\/js\/rehydrate\.js'[\s\S]*?'assets\/js\/aksrt-runtime\.js'/);
assert.match(build, /'assets\/js\/runtime\/main\.min\.js'/);
assert.match(build, /async function buildRuntimeMainMin\(\)/);
assert.match(build, /assets\/js\/runtime\/main\.js/);
assert.match(build, /await buildRuntimeMainMin\(\)/);
});
test('global lifecycle source precedes every registered public component', () => {
const build = read('tools/build-public-assets.mjs');
const registry = build.indexOf("'assets/js/rehydrate.js'");
const runtime = build.indexOf("'assets/js/aksrt-runtime.js'");
const legacy = build.indexOf("'assets/js/runtime/main.min.js'");
assert.ok(registry >= 0 && registry < runtime && runtime < legacy);
});
test('deleted orphan entry files do not return to enqueue or build graphs', () => {
const graph = [
read('tools/build-public-assets.mjs'),
read('src/Core/ScriptsPart1b.php'),
read('src/Core/ScriptsPart1c.php'),
].join('\n');
assert.equal(fs.existsSync(path.join(root, 'assets/js/main.js')), false);
assert.equal(fs.existsSync(path.join(root, 'assets/js/loader.js')), false);
assert.doesNotMatch(graph, /assets\/js\/(?:main|loader)\.js/);
});
test('built public JavaScript contains the canonical global boot', () => {
const bundle = read('assets/js/build/public.js');
assert.match(bundle, /AksrtFrontend\.boot\(root\)/);
assert.match(bundle, /aksrt\.autoFun\(document, 'init'\)/);
assert.match(bundle, /aksrt:ready/);
});
@@ -0,0 +1,97 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const source = fs.readFileSync(path.join(root, 'assets/js/rehydrate.js'), 'utf8');
function createRuntime() {
const events = [];
const document = {
dispatchEvent(event) {
events.push(event);
return true;
},
};
function CustomEvent(type, options) {
this.type = type;
this.bubbles = Boolean(options && options.bubbles);
this.detail = options && options.detail;
}
const errors = [];
const context = {
window: {},
document,
CustomEvent,
console: {
error(...args) {
errors.push(args);
},
},
};
context.window.window = context.window;
context.window.document = document;
vm.runInNewContext(source, context, { filename: 'rehydrate.js' });
return { api: context.window.AksrtFrontend, document, events, errors };
}
test('global DOM boot runs registered components once with init reason', () => {
const runtime = createRuntime();
const calls = [];
runtime.api.register('first', (rootNode, reason) => calls.push([rootNode, reason]));
runtime.api.boot(runtime.document);
runtime.api.boot(runtime.document);
assert.equal(runtime.api.isBooted(), true);
assert.deepEqual(calls, [[runtime.document, 'init']]);
assert.deepEqual(runtime.events.map((event) => event.type), ['aksrt:rehydrated', 'aksrt:ready']);
assert.equal(runtime.events[0].detail.reason, 'init');
assert.equal(runtime.events[1].detail.reason, 'init');
});
test('late registrations initialize immediately after global boot', () => {
const runtime = createRuntime();
const calls = [];
runtime.api.boot(runtime.document);
runtime.api.register('late', (rootNode, reason) => calls.push([rootNode, reason]));
assert.deepEqual(calls, [[runtime.document, 'init']]);
assert.deepEqual(Array.from(runtime.api.getRegistered()), ['late']);
});
test('same-name registration replaces the owner without duplicating it', () => {
const runtime = createRuntime();
const calls = [];
runtime.api.register('owner', () => calls.push('old'));
runtime.api.register('owner', () => calls.push('new'));
runtime.api.boot(runtime.document);
assert.deepEqual(Array.from(runtime.api.getRegistered()), ['owner']);
assert.deepEqual(calls, ['new']);
});
test('rehydrate preserves root and reason while isolating component failures', () => {
const runtime = createRuntime();
const fragment = {
dispatchEvent(event) {
runtime.events.push(event);
return true;
},
};
const calls = [];
runtime.api.register('broken', () => { throw new Error('broken'); });
runtime.api.register('healthy', (rootNode, reason) => calls.push([rootNode, reason]));
runtime.api.rehydrate(fragment, 'modal');
assert.deepEqual(calls, [[fragment, 'modal']]);
assert.equal(runtime.errors.length, 1);
assert.equal(runtime.events.at(-1).type, 'aksrt:rehydrated');
assert.equal(runtime.events.at(-1).detail.reason, 'modal');
assert.equal(runtime.events.at(-1).bubbles, true);
});
+4 -3
View File
@@ -240,7 +240,8 @@ test('balance center actions use direct fixed-id notyf flows', () => {
assert.match(signinController, /querySelectorAll\('\[data-signin-btn\]'\)/); assert.match(signinController, /querySelectorAll\('\[data-signin-btn\]'\)/);
assert.match(signinController, /freshButtons\.forEach[\s\S]*?addEventListener\('click'/); assert.match(signinController, /freshButtons\.forEach[\s\S]*?addEventListener\('click'/);
assert.match(credits, /onTimeout:[\s\S]*?notyf\([^;]+?'danger'[^;]+?'aksrt-balance-charge-payment-toast'\)/); assert.match(credits, /onTimeout:[\s\S]*?notyf\([^;]+?'danger'[^;]+?'aksrt-balance-charge-payment-toast'\)/);
assert.match(credits, /register\('credits',[\s\S]*?initSigninGoto\(\);[\s\S]*?initBalanceCharge\(\);[\s\S]*?initTransfer\(\);[\s\S]*?initPointsExchange\(\);[\s\S]*?initWithdraw\(\);/); assert.match(credits, /function bootAssets\(root, reason\)[\s\S]*?initSigninGoto\(root\);[\s\S]*?initBalanceCharge\(root\);[\s\S]*?initTransfer\(root\);[\s\S]*?initPointsExchange\(root\);[\s\S]*?initWithdraw\(root\);/);
assert.match(credits, /register\('credits', bootAssets\)/);
assert.doesNotMatch(credits, /function initSignin\(|\/signin\/today|POST \/signin/); assert.doesNotMatch(credits, /function initSignin\(|\/signin\/today|POST \/signin/);
assert.match(signinController, /aksrtSigninInitialized/); assert.match(signinController, /aksrtSigninInitialized/);
assert.match(credits, /aksrtPointsExchangeInitialized[\s\S]*?aksrtWithdrawInitialized/); assert.match(credits, /aksrtPointsExchangeInitialized[\s\S]*?aksrtWithdrawInitialized/);
@@ -321,7 +322,7 @@ test('profile save uses AJAX rehydrate and a fixed-id notyf flow without query f
const scripts = read('src/Core/ScriptsPart1b.php'); const scripts = read('src/Core/ScriptsPart1b.php');
assert.match(profile, /function initProfileForm\(root\)[\s\S]*?aksrtProfileInitialized[\s\S]*?addEventListener\('submit'/); assert.match(profile, /function initProfileForm\(root\)[\s\S]*?aksrtProfileInitialized[\s\S]*?addEventListener\('submit'/);
assert.match(profile, /register\('aksrt-profile-form',[\s\S]*?initProfileForm\(root\)/); assert.match(profile, /register\('aksrt-user-center',[\s\S]*?initProfileForm\(root\)/);
assert.match(profile, /notyf\([^;]+?'load'[^;]+?'aksrt-profile-save-toast'\);/); assert.match(profile, /notyf\([^;]+?'load'[^;]+?'aksrt-profile-save-toast'\);/);
assert.match(profile, /notyf\([^;]+?'success'[^;]+?'aksrt-profile-save-toast'\);/); assert.match(profile, /notyf\([^;]+?'success'[^;]+?'aksrt-profile-save-toast'\);/);
assert.match(profile, /notyf\([^;]+?'danger'[^;]+?'aksrt-profile-save-toast'\);/); assert.match(profile, /notyf\([^;]+?'danger'[^;]+?'aksrt-profile-save-toast'\);/);
@@ -350,7 +351,7 @@ test('settings actions use rehydrated fixed-id notyf flows', () => {
assert.match(userCenter, /notyf\([^;]+?'load'[^;]+?toastId\);/); assert.match(userCenter, /notyf\([^;]+?'load'[^;]+?toastId\);/);
assert.match(userCenter, /notyf\([^;]+?'success'[^;]+?toastId\);/); assert.match(userCenter, /notyf\([^;]+?'success'[^;]+?toastId\);/);
assert.match(userCenter, /notyf\([^;]+?'danger'[^;]+?toastId\);/); assert.match(userCenter, /notyf\([^;]+?'danger'[^;]+?toastId\);/);
assert.match(userCenter, /register\('aksrt-settings',[\s\S]*?initSettings\(root\);[\s\S]*?initBindPhone\(root\);/); assert.match(userCenter, /register\('aksrt-user-center',[\s\S]*?initSettings\(root\);[\s\S]*?initBindPhone\(root\);/);
assert.match(userCenter, /aksrtSettingsInitialized/); assert.match(userCenter, /aksrtSettingsInitialized/);
assert.match(userCenter, /oauth_action[\s\S]*?notyf\(message, type\)[\s\S]*?history\.replaceState/); assert.match(userCenter, /oauth_action[\s\S]*?notyf\(message, type\)[\s\S]*?history\.replaceState/);
assert.doesNotMatch(userCenter, /oauth_unbound/); assert.doesNotMatch(userCenter, /oauth_unbound/);
+17
View File
@@ -173,6 +173,22 @@ async function buildScriptBundle() {
await writeFile(path.join(jsBuildDir, 'public.js'), `${sections.join('\n\n')}\n`, 'utf8'); await writeFile(path.join(jsBuildDir, 'public.js'), `${sections.join('\n\n')}\n`, 'utf8');
} }
async function buildRuntimeMainMin() {
const sourcePath = path.join(repoRoot, 'assets/js/runtime/main.js');
const outputPath = path.join(repoRoot, 'assets/js/runtime/main.min.js');
const source = await readFile(sourcePath, 'utf8');
const minified = await transform(source, {
charset: 'utf8',
legalComments: 'inline',
loader: 'js',
minifyIdentifiers: false,
minifySyntax: true,
minifyWhitespace: true,
sourcefile: 'runtime/main.js',
});
await writeFile(outputPath, minified.code, 'utf8');
}
// 评论区硬切换后 main.min.css 由 main.css 重新生成,保持两者同步。 // 评论区硬切换后 main.min.css 由 main.css 重新生成,保持两者同步。
async function buildMainMin() { async function buildMainMin() {
const sourcePath = path.join(repoRoot, 'assets/css/main.css'); const sourcePath = path.join(repoRoot, 'assets/css/main.css');
@@ -190,6 +206,7 @@ async function buildMainMin() {
await mkdir(cssBuildDir, { recursive: true }); await mkdir(cssBuildDir, { recursive: true });
await mkdir(jsBuildDir, { recursive: true }); await mkdir(jsBuildDir, { recursive: true });
await buildRuntimeMainMin();
// 先刷新 main.min.css,public.css 打包时才能读到最新内容。 // 先刷新 main.min.css,public.css 打包时才能读到最新内容。
await buildMainMin(); await buildMainMin();
for (const [outputName, sources] of Object.entries(cssBundles)) { for (const [outputName, sources] of Object.entries(cssBundles)) {