- 删除 AksrtFrontend registry/rehydrate 双路架构及 build/public.js 拼包入口,
前台唯一初始路径为 PHP 本地栈(jQuery 1.9.1 → Bootstrap 3.4.1)
→ loader_js(_win/aksrtData 唯一注入)→ tbquire(['main']) → runtime/main.min.js
- window.auto_fun = debounce(z_auto_fun, 100) 为唯一重扫入口;所有模块改为
执行一次 + off/on('auto_fun.aksrt-*') 命名空间重扫,data/class 标记幂等
- 局部更新协议统一:HTML 注入 → post_ajax.ed → auto_fun();
远程 modal → loaded.bs.modal → auto_fun();JSON 成功 → zib_ajax.success
- 灯箱恢复 Miniimgbox/imgbox 唯一 owner(imgbox-container、a[data-imgbox]、
imgbox_data),删除 AksrtLightbox/data-aksrt-lightbox 第二实现及 CSS/overlay,
_win.imgbox* 重新接上 post-display 设置
- 死代码清理:runtime 下 11 组被自有实现替代的旧模块副本、无消费 vendored 库、
顶层 grade.min.js、revenue.js、无调用方的 aksrt_get_win_runtime_config(),
以及 loader 中 16 条死 mapping
- PHP 入队改为本地栈 + loader_js,routed/widget/submit 依赖统一换绑;
用户卡片发布按钮恢复 nav_user_newposts_btn 设置门控
- 测试对齐:新增 frontend-lifecycle-contract,重写 navigation/lazyload/toast/
auth-source/comment/page-display/post-visual 等断言为目标架构契约
验证:node --test widget-system+navigation 223 通过 0 失败;
构建脚本重建 main/imgbox min 与 CSS bundles;改动 PHP php -l、JS node --check
全部通过;git diff --check 干净;credits 重入 harness 通过。
142 lines
4.2 KiB
JavaScript
142 lines
4.2 KiB
JavaScript
/**
|
||
* AKSRT Section Navs — legacy section_navs parity (posts-nav-lists + scrollspy).
|
||
*
|
||
* Builds TOC from [data-nav] h1–h4 into .posts-nav-box / .posts-nav-lists.
|
||
*
|
||
* @package AKSRT
|
||
*/
|
||
(function () {
|
||
'use strict';
|
||
|
||
var HEADING_SEL = 'h1, h2, h3, h4';
|
||
var MIN_HEADINGS = 2;
|
||
var NAVBOX = '.posts-nav-box';
|
||
var NAV_CLASS = 'posts-nav-lists';
|
||
|
||
function isExcluded(el) {
|
||
return el.classList.contains('item-heading') || !!el.closest('.no-nav');
|
||
}
|
||
|
||
function ensureId(el, index) {
|
||
if (!el.id) {
|
||
el.id = 'section-nav-' + index;
|
||
}
|
||
return el.id;
|
||
}
|
||
|
||
function buildLists(root) {
|
||
var headings = root.querySelectorAll(HEADING_SEL);
|
||
var html = '';
|
||
var count = 0;
|
||
headings.forEach(function (h, i) {
|
||
if (isExcluded(h)) return;
|
||
var id = ensureId(h, i);
|
||
var tag = h.tagName.toUpperCase();
|
||
var text = (h.textContent || '').trim();
|
||
if (!text) return;
|
||
html += '<li class="n-' + tag + '"><a href="#' + id + '">' + text + '</a></li>';
|
||
count++;
|
||
});
|
||
return count >= MIN_HEADINGS ? html : '';
|
||
}
|
||
|
||
function fillBoxes(listsHtml) {
|
||
document.querySelectorAll(NAVBOX).forEach(function (box) {
|
||
var existing = box.querySelector('.' + NAV_CLASS);
|
||
if (existing) {
|
||
existing.innerHTML = listsHtml;
|
||
return;
|
||
}
|
||
var title = box.getAttribute('data-title') || '';
|
||
var titleHtml = title
|
||
? '<div class="box-body notop"><div class="title-theme">' + title + '</div></div>'
|
||
: '';
|
||
box.insertAdjacentHTML(
|
||
'beforeend',
|
||
titleHtml +
|
||
'<div class="aksrt-panel"><div class="' +
|
||
NAV_CLASS +
|
||
' scroll-y mini-scrollbar list-unstyled">' +
|
||
listsHtml +
|
||
'</div></div>'
|
||
);
|
||
});
|
||
}
|
||
|
||
function smoothTo(hash) {
|
||
var el = document.querySelector(hash);
|
||
if (!el) return;
|
||
var offset = 0;
|
||
var header = document.querySelector('.header');
|
||
if (document.body.classList.contains('nav-fixed') && header) {
|
||
offset = header.offsetHeight + 12;
|
||
}
|
||
var top = el.getBoundingClientRect().top + window.pageYOffset - offset;
|
||
window.scrollTo({ top: top, behavior: 'smooth' });
|
||
}
|
||
|
||
function bindClicks() {
|
||
document.addEventListener('click', function (e) {
|
||
var a = e.target && e.target.closest ? e.target.closest('.' + NAV_CLASS + ' a[href^="#"]') : null;
|
||
if (!a) return;
|
||
var href = a.getAttribute('href');
|
||
if (!href || href.length < 2) return;
|
||
e.preventDefault();
|
||
history.replaceState(null, '', href);
|
||
smoothTo(href);
|
||
});
|
||
}
|
||
|
||
function scrollspy() {
|
||
var links = document.querySelectorAll('.' + NAV_CLASS + ' a[href^="#"]');
|
||
if (!links.length) return;
|
||
var map = [];
|
||
links.forEach(function (a) {
|
||
var id = a.getAttribute('href').slice(1);
|
||
var el = document.getElementById(id);
|
||
if (el) map.push({ el: el, a: a, li: a.parentElement });
|
||
});
|
||
function update() {
|
||
var y = window.scrollY + 110;
|
||
var current = null;
|
||
for (var i = 0; i < map.length; i++) {
|
||
if (map[i].el.offsetTop <= y) current = map[i];
|
||
}
|
||
map.forEach(function (m) {
|
||
if (m.li) m.li.classList.toggle('active', m === current);
|
||
});
|
||
}
|
||
window.addEventListener('scroll', update, { passive: true });
|
||
update();
|
||
}
|
||
|
||
function autoHash() {
|
||
if (window.location.hash) {
|
||
setTimeout(function () {
|
||
smoothTo(window.location.hash);
|
||
}, 40);
|
||
}
|
||
}
|
||
|
||
function init() {
|
||
if (document.documentElement.dataset.aksrtSectionNavInitialized === '1') return;
|
||
document.documentElement.dataset.aksrtSectionNavInitialized = '1';
|
||
if (document.querySelector('.posts-nav-box .toc-list, .posts-nav-box.toc-widget .toc-list')) {
|
||
return;
|
||
}
|
||
var root = document.querySelector('[data-nav], .article-content, .entry-content, main.article');
|
||
if (!root) return;
|
||
var listsHtml = buildLists(root);
|
||
if (!listsHtml) return;
|
||
if (!document.querySelector(NAVBOX) && !document.querySelector('.' + NAV_CLASS)) {
|
||
return;
|
||
}
|
||
fillBoxes(listsHtml);
|
||
bindClicks();
|
||
scrollspy();
|
||
autoHash();
|
||
}
|
||
|
||
jQuery(document).off('auto_fun.aksrt-section-navs').on('auto_fun.aksrt-section-navs', init);
|
||
})();
|