Files
wordpress-theme-aksrt/assets/js/yiyan.js
T
kate522 1f4a39d8f8 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 适配注册表初始化方式
2026-09-02 21:48:56 +08:00

259 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* AKSRT Yiyan — 一言轮播组件
*
* Fetches random quotes from hitokoto.cn API and auto-rotates.
* Public v1 returns one sentence per request; multi-quote carousels
* issue parallel fetches. Category `c` must be a letter a–l (or omitted).
*
* @package AKSRT
*/
(function () {
'use strict';
var API_BASE = 'https://v1.hitokoto.cn/';
var TYPE_RE = /^[a-l]$/i;
var MIN_COUNT = 1;
var MAX_COUNT = 10;
var DEFAULT_COUNT = 5;
var DEFAULT_INTERVAL_SEC = 5;
function normalizeType(raw) {
if (raw == null || raw === '') {
return '';
}
var t = String(raw).trim().toLowerCase();
return TYPE_RE.test(t) ? t : '';
}
function normalizeCount(raw) {
var n = parseInt(raw, 10);
if (isNaN(n) || n < MIN_COUNT) {
return DEFAULT_COUNT;
}
return Math.min(MAX_COUNT, n);
}
function normalizeIntervalMs(raw) {
var sec = parseInt(raw, 10);
if (isNaN(sec) || sec < 3) {
sec = DEFAULT_INTERVAL_SEC;
}
return Math.min(60, sec) * 1000;
}
function buildApiUrl(type) {
var params = new URLSearchParams();
params.set('encode', 'json');
if (type) {
params.set('c', type);
}
return API_BASE + '?' + params.toString();
}
function mapQuote(data) {
if (!data || typeof data !== 'object') {
return null;
}
var text = data.hitokoto || data.text || '';
if (!text) {
return null;
}
return {
text: String(text),
from: data.from ? String(data.from) : '',
};
}
function ensureStructure(el) {
var textEl = el.querySelector('.aksrt-yiyan__text');
if (textEl) {
return {
textEl: textEl,
fromEl: el.querySelector('.aksrt-yiyan__from'),
dotsEl: el.querySelector('.aksrt-yiyan__dots'),
};
}
var content = document.createElement('div');
content.className = 'aksrt-yiyan__content';
textEl = document.createElement('p');
textEl.className = 'aksrt-yiyan__text';
textEl.textContent = '加载中…';
content.appendChild(textEl);
var fromEl = document.createElement('p');
fromEl.className = 'aksrt-yiyan__from';
content.appendChild(fromEl);
el.appendChild(content);
var dotsEl = document.createElement('div');
dotsEl.className = 'aksrt-yiyan__dots';
dotsEl.setAttribute('aria-hidden', 'true');
el.appendChild(dotsEl);
return { textEl: textEl, fromEl: fromEl, dotsEl: dotsEl };
}
function fetchOneQuote(type) {
return fetch(buildApiUrl(type), {
credentials: 'omit',
mode: 'cors',
}).then(function (r) {
return r
.json()
.catch(function () {
return null;
})
.then(function (data) {
if (!r.ok) {
var msg =
data && data.message
? String(data.message)
: 'HTTP ' + r.status;
throw new Error(msg);
}
var quote = mapQuote(data);
if (!quote) {
throw new Error('empty');
}
return quote;
});
});
}
function fetchQuotes(type, count) {
var jobs = [];
var i;
for (i = 0; i < count; i += 1) {
jobs.push(
fetchOneQuote(type).catch(function () {
return null;
})
);
}
return Promise.all(jobs).then(function (results) {
var quotes = [];
var seen = Object.create(null);
results.forEach(function (q) {
if (!q || !q.text) {
return;
}
// Dedupe identical text from parallel random draws.
if (seen[q.text]) {
return;
}
seen[q.text] = 1;
quotes.push(q);
});
return quotes;
});
}
function initYiyan(el) {
if (el.dataset.yiyanBound) {
return;
}
el.dataset.yiyanBound = '1';
var count = normalizeCount(el.dataset.count);
var interval = normalizeIntervalMs(el.dataset.interval);
var type = normalizeType(el.dataset.type);
var parts = ensureStructure(el);
var textEl = parts.textEl;
var fromEl = parts.fromEl;
var dotsEl = parts.dotsEl;
var quotes = [];
var currentIndex = 0;
var timer = null;
function showQuote(index) {
if (!quotes.length || !textEl) {
return;
}
var q = quotes[index % quotes.length];
textEl.style.opacity = '0';
setTimeout(function () {
textEl.textContent = '"' + q.text + '"';
textEl.style.opacity = '1';
if (fromEl) {
fromEl.textContent = q.from ? '—— ' + q.from : '';
}
}, 200);
if (dotsEl) {
dotsEl.querySelectorAll('.aksrt-yiyan__dot').forEach(function (dot, i) {
dot.classList.toggle('is-active', i === index % quotes.length);
});
}
}
function nextQuote() {
if (quotes.length < 2) {
return;
}
currentIndex = (currentIndex + 1) % quotes.length;
showQuote(currentIndex);
}
function startTimer() {
if (timer) {
clearInterval(timer);
timer = null;
}
if (quotes.length > 1) {
timer = setInterval(nextQuote, interval);
}
}
function buildDots() {
if (!dotsEl || quotes.length < 2) {
if (dotsEl) {
dotsEl.innerHTML = '';
}
return;
}
dotsEl.innerHTML = '';
quotes.forEach(function (_, i) {
var dot = document.createElement('span');
dot.className = 'aksrt-yiyan__dot' + (i === 0 ? ' is-active' : '');
dotsEl.appendChild(dot);
});
}
el.addEventListener('mouseenter', function () {
if (timer) {
clearInterval(timer);
timer = null;
}
});
el.addEventListener('mouseleave', function () {
startTimer();
});
fetchQuotes(type, count)
.then(function (list) {
quotes = list;
if (!quotes.length) {
textEl.textContent = '暂无内容';
return;
}
buildDots();
showQuote(0);
startTimer();
})
.catch(function () {
textEl.textContent = '加载失败,请稍后再试';
});
}
window.AksrtFrontend.register('aksrt-yiyan', function (root) {
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);
});
})();