- assets/vendor 全量迁移: fontawesome5→assets/fontawesome5, css→assets/css, js→assets/js/runtime(避开 14 个同名自有文件), img 合并去重→assets/img, fonts→assets/fonts; 删除 assets/vendor - libs 五个与 widgets/third-party 字节相同的库不重复入库, loader paths 改指向 - 运行时切换: _win.uri=assets 根, loader baseUrl=/js/runtime, enlighter 注入 路径同步; PHP 7 个引用文件切换并改名 $zibll_*→$runtime_* - 去品牌: Qinver 头注释/品牌 console.log/演示链接清除, 默认文案中性化 - 功能标识符改名: zib__→aksrt__, zib_ajax→aksrt_ajax, window.zib→ window.aksrtLegacy, zibllblock/*→aksrtblock/*, .zib-widget→.aksrt-panel, .zib-slider→.aksrt-slider, zib_menu_options meta 键等 - 自有 CSS 改名: legacy-utilities/compat-vars/footer shell - admin SPA 重建; 契约测试同步(路径/断言/文件更名) - 例外保留: tests/visual 对照环境与 fixtures 前缀(外部 Zibll 产品引用) 验证: php -l 133 个改动文件通过; runtime 86 个 JS node --check 通过; 契约测试 68/70(2 个失败经 stash 对照确认为存量问题)
148 lines
4.2 KiB
JavaScript
148 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.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();
|
||
}
|
||
|
||
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
|
||
window.AksrtFrontend.register('aksrt-section-navs', function () {
|
||
init();
|
||
});
|
||
} else if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
} else {
|
||
init();
|
||
}
|
||
})();
|