The theme was renamed Zibll/子比 → AKSRT → Jio, but upstream branding survived the sweeps in CSS classes, the SVG sprite prefix, a persisted widget field ID, JS events, admin Vue class names, test infrastructure and docblocks. Renames: - zib-widget / zib-widget-wrap / zib-slider → jio-widget-box / jio-widget-box-wrap / jio-slider-box (jio-widget is taken by the modern widget shell, jio-box-* by the card primitives) - zib-partners-scroll + zps-* → jio-partners-scroll + jps-* - zib-combo-info-card + zci-* → jio-combo-info-card + jci-* - zibsvg- → jiosvg-, including the exported picker helpers and the fa-zibsvg- family-prefix guard - zibpay_type → pay_type; the dual-read fallback in PostQuery collapses to a single key - [zibajax="submit"] dropped (no PHP emitter); zib_ajax.success → jio_ajax.success - aks-* admin Vue classes → jio-* - zib_palette() → palette() User-facing vendor traces are gone: the floating "more" button default no longer ships a zibll.com link or 子比主题 marketing copy, the notice widget ships no preset promo entries, and the four 官网教程 links are removed from the post-list admin schema. Test infrastructure renamed to neutral names (docker-compose.reference.yml, Caddyfile, article-baseline.json, modern-widgets-contract.test.cjs) with their DB/volume/env/slug identifiers following. Kept intentionally, documented in tests/fixtures/README.md: the golden fixture reference fingerprint (a SHA-256 record of an external directory, read by validate-widget-golden / generate-widget-golden-fixture / audit-widget-system), the doesNotMatch guards that prove the old tokens are gone, and the reference theme's own meta key in the visual fixtures. No backward-compat or data migration: saved widget configs holding old icon keys or the old field ID silently lose them, per the project's rewrite-over-compatibility rule. Verified: 132/132 node contract tests, php tests/widget-system/run.php (146 assertions), audit-admin-contracts, audit-psr4-paths and validate-widget-golden all green; tracked bundles regenerated and in sync. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
187 lines
7.1 KiB
JavaScript
187 lines
7.1 KiB
JavaScript
/** Article directory: one renderer for sidebar, affix and mobile copies. */
|
|
(function () {
|
|
'use strict';
|
|
|
|
var entries = [];
|
|
var rendered = new WeakMap();
|
|
var framePending = false;
|
|
var handledHash = '';
|
|
|
|
function hashId(hash) {
|
|
try { return decodeURIComponent(hash.slice(1)); }
|
|
catch (error) { return ''; }
|
|
}
|
|
|
|
function collectHeadings() {
|
|
var found = [];
|
|
document.querySelectorAll('[data-nav] h1, [data-nav] h2, [data-nav] h3, [data-nav] h4').forEach(function (heading, index) {
|
|
var text = (heading.textContent || '').trim();
|
|
if (!text || heading.classList.contains('item-heading') || heading.closest('.no-nav')) return;
|
|
if (!heading.id || document.getElementById(heading.id) !== heading) {
|
|
var id = 'wznav_' + index;
|
|
var suffix = 0;
|
|
while (document.getElementById(id)) id = 'wznav_' + index + '_' + (++suffix);
|
|
heading.id = id;
|
|
}
|
|
found.push({ element: heading, id: heading.id, text: text, tag: heading.tagName.toUpperCase() });
|
|
});
|
|
return found;
|
|
}
|
|
|
|
function groupChildren(item) {
|
|
var children = [];
|
|
var next = item.nextElementSibling;
|
|
while (next && !next.classList.contains('n-H1') && !next.classList.contains('n-H2')) {
|
|
children.push(next);
|
|
next = next.nextElementSibling;
|
|
}
|
|
return children;
|
|
}
|
|
|
|
function collapseGroups(list) {
|
|
if (list.scrollHeight <= 380 || list.querySelector('.nav-toggle-collapse')) return;
|
|
list.querySelectorAll('.n-H1, .n-H2').forEach(function (item) {
|
|
var children = groupChildren(item);
|
|
if (!children.length) return;
|
|
children.forEach(function (child) { child.classList.add('yc'); });
|
|
var button = document.createElement('button');
|
|
button.type = 'button';
|
|
button.className = 'fa fas fa-angle-right nav-toggle-collapse';
|
|
button.setAttribute('aria-expanded', 'false');
|
|
button.setAttribute('aria-label', item.querySelector('a').textContent);
|
|
item.appendChild(button);
|
|
});
|
|
}
|
|
|
|
function fillBox(box, signature) {
|
|
if (entries.length < 3) {
|
|
if (box.dataset.jioSectionNav === '1') {
|
|
box.replaceChildren();
|
|
delete box.dataset.jioSectionNav;
|
|
rendered.delete(box);
|
|
}
|
|
return;
|
|
}
|
|
if (rendered.get(box) === signature) {
|
|
var current = box.querySelector('.bl');
|
|
if (current) collapseGroups(current);
|
|
return;
|
|
}
|
|
box.replaceChildren();
|
|
box.dataset.jioSectionNav = '1';
|
|
rendered.set(box, signature);
|
|
var title = box.getAttribute('data-title') || '';
|
|
if (title) {
|
|
var titleBody = document.createElement('div');
|
|
titleBody.className = 'box-body notop';
|
|
var titleText = document.createElement('div');
|
|
titleText.className = 'title-theme';
|
|
// data-title is escaped by the PHP renderer, including its optional small tag.
|
|
titleText.innerHTML = title;
|
|
titleBody.appendChild(titleText);
|
|
box.appendChild(titleBody);
|
|
}
|
|
var widget = document.createElement('div');
|
|
widget.className = 'jio-widget-box';
|
|
var nav = document.createElement('div');
|
|
nav.className = 'posts-nav-lists scroll-y mini-scrollbar list-unstyled';
|
|
var list = document.createElement('ul');
|
|
list.className = 'bl relative nav';
|
|
entries.forEach(function (entry) {
|
|
var item = document.createElement('li');
|
|
item.className = 'n-' + entry.tag;
|
|
var link = document.createElement('a');
|
|
link.className = 'text-ellipsis';
|
|
link.setAttribute('href', '#' + encodeURIComponent(entry.id));
|
|
link.textContent = entry.text;
|
|
item.appendChild(link);
|
|
list.appendChild(item);
|
|
});
|
|
nav.appendChild(list);
|
|
widget.appendChild(nav);
|
|
box.appendChild(widget);
|
|
collapseGroups(list);
|
|
}
|
|
|
|
function scrollspy() {
|
|
if (entries.length < 3) return;
|
|
var current = '';
|
|
entries.forEach(function (entry) {
|
|
if (!entry.element.getClientRects().length) return;
|
|
if (entry.element.getBoundingClientRect().top <= 105) current = entry.id;
|
|
});
|
|
document.querySelectorAll('.posts-nav-lists a[href^="#"]').forEach(function (link) {
|
|
var active = hashId(link.getAttribute('href')) === current;
|
|
link.parentElement.classList.toggle('active', active);
|
|
if (active) link.setAttribute('aria-current', 'location');
|
|
else link.removeAttribute('aria-current');
|
|
});
|
|
}
|
|
|
|
function scheduleSpy() {
|
|
if (framePending) return;
|
|
framePending = true;
|
|
window.requestAnimationFrame(function () {
|
|
framePending = false;
|
|
scrollspy();
|
|
});
|
|
}
|
|
|
|
function scrollToHeading(target) {
|
|
// Reference behavior: reveal content hidden by an accordion or height limit.
|
|
var panels = jQuery(target).parents('.panel').find('.collapse');
|
|
if (typeof panels.collapse === 'function') panels.collapse('show');
|
|
document.querySelectorAll('.read-more-open').forEach(function (button) { button.click(); });
|
|
document.body.classList.remove('mobile-navbar-show');
|
|
var header = document.querySelector('.header');
|
|
var offset = document.body.classList.contains('nav-fixed') && header ? header.offsetHeight + 20 : 0;
|
|
var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
window.scrollTo({ top: Math.max(0, target.getBoundingClientRect().top + window.pageYOffset - offset), behavior: reduced ? 'auto' : 'smooth' });
|
|
scheduleSpy();
|
|
}
|
|
|
|
function followHash() {
|
|
var hash = window.location.hash;
|
|
if (!hash || hash === handledHash) return;
|
|
var id = hashId(hash);
|
|
var entry = entries.find(function (item) { return item.id === id; });
|
|
if (!entry || entries.length < 3) return;
|
|
handledHash = hash;
|
|
window.setTimeout(function () { scrollToHeading(entry.element); }, 20);
|
|
}
|
|
|
|
function init() {
|
|
entries = collectHeadings();
|
|
var signature = JSON.stringify(entries.map(function (entry) { return [entry.id, entry.text, entry.tag]; }));
|
|
document.querySelectorAll('.posts-nav-box').forEach(function (box) {
|
|
fillBox(box, signature + (box.getAttribute('data-title') || ''));
|
|
});
|
|
scrollspy();
|
|
followHash();
|
|
}
|
|
|
|
document.addEventListener('click', function (event) {
|
|
var button = event.target.closest('.posts-nav-lists .nav-toggle-collapse');
|
|
if (button) {
|
|
event.preventDefault();
|
|
var expanded = button.getAttribute('aria-expanded') !== 'true';
|
|
button.setAttribute('aria-expanded', String(expanded));
|
|
button.classList.toggle('fa-rotate-90', expanded);
|
|
groupChildren(button.parentElement).forEach(function (child) { child.classList.toggle('yc', !expanded); });
|
|
return;
|
|
}
|
|
var link = event.target.closest('.posts-nav-lists a[href^="#"]');
|
|
if (!link) return;
|
|
var target = document.getElementById(hashId(link.getAttribute('href')));
|
|
if (!target) return;
|
|
event.preventDefault();
|
|
scrollToHeading(target);
|
|
});
|
|
window.addEventListener('scroll', scheduleSpy, { passive: true });
|
|
window.addEventListener('resize', init, { passive: true });
|
|
window.addEventListener('hashchange', followHash);
|
|
jQuery(document).off('auto_fun.jio-section-navs').on('auto_fun.jio-section-navs', init);
|
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init, { once: true });
|
|
else init();
|
|
})();
|