(function () { 'use strict'; var config = window.aksrtWidgets || {}; var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; function once(el, key) { var name = 'aksrt' + key; if (el.dataset[name]) return false; el.dataset[name] = '1'; return true; } function moveTabFocus(event, root) { var current = event.target.closest('[role="tab"]'); if (!current) return; var tabs = Array.prototype.slice.call(root.querySelectorAll('[role="tab"]')); var index = tabs.indexOf(current); var next = index; if (event.key === 'ArrowRight') next = (index + 1) % tabs.length; else if (event.key === 'ArrowLeft') next = (index - 1 + tabs.length) % tabs.length; else if (event.key === 'Home') next = 0; else if (event.key === 'End') next = tabs.length - 1; else return; event.preventDefault(); tabs[next].focus(); tabs[next].click(); } function videoType(url) { if (/\.m3u8(?:\?|$)/i.test(url)) return 'hls'; if (/\.flv(?:\?|$)/i.test(url)) return 'flv'; if (/\.mpd(?:\?|$)/i.test(url)) return 'dash'; return 'auto'; } function initVideo(el) { // 幂等标记落在 data-aksrt-widget-video;data-aksrt-video 是 // AksrtVideoPlayer 的自动初始化契约,被扫到会在容器里再挂一个无源播放器。 if (!once(el, 'WidgetVideo')) return; var fallback = el.querySelector('video'); if (fallback) { fallback.addEventListener('error', function () { var message = el.querySelector('.aksrt-widget-media-error'); if (message) message.hidden = false; }); } function mountPlayer(event) { if (typeof window.DPlayer !== 'function' || el.aksrtPlayer) return; if (event) event.preventDefault(); var holder = document.createElement('div'); holder.className = 'aksrt-widget-video__player'; el.insertBefore(holder, fallback || el.firstChild); try { el.aksrtPlayer = new window.DPlayer({ container: holder, autoplay: el.dataset.autoplay === '1' && !reducedMotion, video: { url: el.dataset.videoUrl || '', pic: el.dataset.videoPoster || '', type: videoType(el.dataset.videoUrl || '') } }); if (el.dataset.muted === '1' && el.aksrtPlayer.video) el.aksrtPlayer.video.muted = true; if (fallback) fallback.hidden = true; } catch (error) { holder.remove(); } } if (fallback && fallback.classList.contains('lazyload')) { fallback.addEventListener('lazybeforeunveil', mountPlayer, { once: true }); } else { mountPlayer(); } } function initQuote(figure) { if (!once(figure, 'Quote')) return; if (window.AksrtYiyan) window.AksrtYiyan.init(figure); } function errorMarkup(page, append) { var i18n = config.i18n || {}; return ''; } function itemsUrl(widget, instance, page, segment) { var url = new URL(config.restUrl, window.location.href); var path = '/' + encodeURIComponent(widget) + '/items'; if (url.searchParams.has('rest_route')) { url.searchParams.set('rest_route', url.searchParams.get('rest_route').replace(/\/$/, '') + path); } else { url.pathname = url.pathname.replace(/\/$/, '') + path; } url.searchParams.set('instance', instance); url.searchParams.set('page', page); if (segment !== undefined && segment !== '') url.searchParams.set('segment', segment); return url.href; } function loaderOf(container) { if (!container) return null; return container.querySelector('.post_ajax_loader'); } function fallbackLoader() { var text = (window.aksrtData && aksrtData.ajaxLoading) || '加载中...'; var node = document.createElement('div'); node.className = 'post_ajax_loader'; node.setAttribute('role', 'status'); node.innerHTML = '
' + text + '
'; return node; } function setBusy(container, busy) { if (!container) return; if (busy) { container.dataset.loading = '1'; container.setAttribute('aria-busy', 'true'); // 骨架开关走 .is-loading 类(modal-runtime.css),不写 inline display。 var loader = loaderOf(container) || container.appendChild(fallbackLoader()); loader.classList.add('is-loading'); } else { delete container.dataset.loading; container.setAttribute('aria-busy', 'false'); var loader = loaderOf(container); if (loader) loader.classList.remove('is-loading'); } } // 追加时骨架紧跟已有条目;替换时先清空旧页,骨架留在新页的位置。 // 请求成功后隐藏并保留同一个骨架节点,供下一轮请求复用。 function anchorLoader(container) { var pag = container.querySelector(':scope > .ajax-pag'); var loader = loaderOf(container); if (!loader) return null; if (pag) { pag.parentNode.insertBefore(loader, pag); } else { container.appendChild(loader); } return loader; } function loadItems(container, page, append) { var owner = container.closest('[data-widget]') || container; var widget = owner.dataset.widget || container.dataset.widget; var instance = owner.dataset.instance || container.dataset.instance; var segment = container.dataset.segment || owner.dataset.segment || ''; if (!widget || !instance || !config.restUrl || container.dataset.loading === '1') return; container.querySelectorAll(':scope > .aksrt-widget-request-error').forEach(function (error) { error.remove(); }); setBusy(container, true); var previous = []; if (!append) { // 对齐参考主题 beforeSend:数字分页与跳页先移除旧内容,而非等待响应。 // 暂存原节点,只在请求失败时恢复内容和分页控件。 var loader = loaderOf(container); Array.prototype.slice.call(container.children).forEach(function (child) { if (child === loader) return; previous.push(child); child.remove(); }); } anchorLoader(container); return fetch(itemsUrl(widget, instance, page, segment), { credentials: 'same-origin', headers: { Accept: 'application/json' } }) .then(function (response) { if (!response.ok) throw new Error('items'); return response.json(); }) .then(function (data) { var fragment = document.createElement('div'); fragment.innerHTML = String(data.html || ''); var anchor = loaderOf(container) || container.querySelector(':scope > .ajax-pag'); Array.prototype.slice.call(fragment.children).forEach(function (node) { if (anchor && anchor.parentNode === container) { container.insertBefore(node, anchor); } else { container.appendChild(node); } }); Array.prototype.slice.call(container.querySelectorAll(':scope > .ajax-pag')).forEach(function (node) { node.remove(); }); if (data.paginate_html) container.insertAdjacentHTML('beforeend', data.paginate_html); container.dataset.page = String(data.page || page); container.dataset.loaded = '1'; anchorLoader(container); previous = []; scan(container); if (window.jQuery) window.jQuery(document).trigger('auto_fun'); }) .catch(function () { var loader = loaderOf(container); previous.forEach(function (node) { container.insertBefore(node, loader); }); anchorLoader(container); if (loader && loader.parentNode === container) { loader.insertAdjacentHTML('beforebegin', errorMarkup(page, append)); } else { container.insertAdjacentHTML('afterbegin', errorMarkup(page, append)); } }) .finally(function () { setBusy(container, false); }); } function initItems(container) { if (!once(container, 'Items')) return; if (container.hasAttribute('data-widget-lazy') && container.dataset.loaded !== '1') { container.addEventListener('lazybeforeunveil', function (event) { if (event.target === container && container.dataset.loaded !== '1') loadItems(container, 1, false); }); // main.js loads asynchronously through loader_js. Its lazySizes scan // may finish before this client binds, so consume an unveiled pager. if (!container.classList.contains('lazyload')) loadItems(container, 1, false); } function jumpTo(input) { var page = Math.min(Number(input.max), Math.floor(Number(input.value))); if (!Number.isFinite(page) || page < 1 || page === Number(container.dataset.page || 1)) { input.focus(); return; } loadItems(container, page, false); } container.addEventListener('input', function (event) { if (!event.target.matches('[data-widget-jump]')) return; event.stopPropagation(); var page = Math.floor(Number(event.target.value)); event.target.value = page > 0 ? String(Math.min(Number(event.target.max), page)) : ''; }); container.addEventListener('keydown', function (event) { if (event.key !== 'Enter' || !event.target.matches('[data-widget-jump]')) return; event.preventDefault(); event.stopPropagation(); jumpTo(event.target); }); container.addEventListener('click', function (event) { var jump = event.target.closest('[data-widget-jump-go]'); if (jump) { event.preventDefault(); event.stopPropagation(); jumpTo(jump.parentElement.querySelector('[data-widget-jump]')); return; } var retry = event.target.closest('[data-widget-retry]'); var trigger = event.target.closest('[data-next-page]'); var pageLink = event.target.closest('.page-numbers[data-widget-page]'); if (retry) { event.preventDefault(); loadItems(container, Number(retry.dataset.retryPage || 1), retry.dataset.retryAppend === '1'); return; } if (trigger) { event.preventDefault(); loadItems(container, Number(trigger.dataset.nextPage || 2), true); return; } if (pageLink) { event.preventDefault(); loadItems(container, Number(pageLink.dataset.widgetPage || 1), false); } }); } function initTabs(tabs) { if (!once(tabs, 'Tabs')) return; tabs.addEventListener('keydown', function (event) { moveTabFocus(event, tabs); }); tabs.addEventListener('click', function (event) { var button = event.target.closest('[role="tab"][data-segment]'); if (!button) return; event.preventDefault(); var nav = button.closest('[role="tablist"]'); if (nav && nav.scrollWidth > nav.clientWidth) { var tabBounds = button.getBoundingClientRect(); var navBounds = nav.getBoundingClientRect(); nav.scrollTo({ left: nav.scrollLeft + tabBounds.left - navBounds.left + tabBounds.width / 2 - nav.clientWidth / 2, behavior: reducedMotion ? 'auto' : 'smooth' }); } tabs.querySelectorAll('[role="tab"]').forEach(function (tab) { tab.setAttribute('aria-selected', tab === button ? 'true' : 'false'); tab.setAttribute('tabindex', tab === button ? '0' : '-1'); if (tab.parentElement) tab.parentElement.classList.toggle('active', tab === button); }); tabs.querySelectorAll('.tab-content > .tab-pane').forEach(function (item) { var active = item.id === button.getAttribute('aria-controls'); item.classList.toggle('active', active); item.classList.toggle('in', active); }); var panel = tabs.querySelector('[data-widget-items][data-segment="' + button.dataset.segment + '"]'); if (!panel) return; if (panel.dataset.loaded !== '1') loadItems(panel, 1, false); }); } function initFaq(faq) { if (!once(faq, 'Faq')) return; faq.addEventListener('click', function (event) { var button = event.target.closest('.aksrt-faq__item > button'); if (!button) return; var expanded = button.getAttribute('aria-expanded') === 'true'; button.setAttribute('aria-expanded', expanded ? 'false' : 'true'); var panel = document.getElementById(button.getAttribute('aria-controls')); if (panel) panel.hidden = expanded; }); var search = faq.querySelector('input[type="search"]'); if (search) search.addEventListener('input', function () { var needle = search.value.trim().toLowerCase(); faq.querySelectorAll('.aksrt-faq__item').forEach(function (item) { item.hidden = needle && String(item.dataset.search || '').toLowerCase().indexOf(needle) === -1; }); }); } function initFeatures(features) { if (!once(features, 'Features')) return; features.addEventListener('keydown', function (event) { moveTabFocus(event, features); }); features.addEventListener('click', function (event) { var button = event.target.closest('[data-feature-index]'); if (!button) return; var index = button.dataset.featureIndex; features.querySelectorAll('[data-feature-index]').forEach(function (tab) { tab.setAttribute('aria-selected', tab === button ? 'true' : 'false'); tab.setAttribute('tabindex', tab === button ? '0' : '-1'); }); features.querySelectorAll('[data-feature-panel]').forEach(function (panel) { panel.hidden = panel.dataset.featurePanel !== index; }); }); } function initCount(el) { if (!once(el, 'Count') || reducedMotion) return; var raw = el.dataset.countUp || ''; var match = raw.match(/-?[0-9]+(?:\.[0-9]+)?/); if (!match) return; var target = Number(match[0]); var prefix = raw.slice(0, match.index); var suffix = raw.slice(match.index + match[0].length); var start = performance.now(); function frame(now) { var progress = Math.min(1, (now - start) / 900); el.textContent = prefix + Math.round(target * (1 - Math.pow(1 - progress, 3))) + suffix; if (progress < 1) requestAnimationFrame(frame); } requestAnimationFrame(frame); } /* 幻灯片加图文视频卡片:以左侧幻灯片实际高度重算右侧卡片比例。 * 合作伙伴滚动由运行时 main.js 初始化(.zib-partners-scroll 唯一所有者)。 */ function measureSliderGraphic(widget) { if (once(widget, 'Combo')) return; var slider = widget.querySelector('.slider-col .zib-slider') || widget.querySelector('.slider-col'); var covers = widget.querySelector('.cover-col'); if (!slider || !covers) return; var row = Number(widget.dataset.row || 2); var col = Number(widget.dataset.col || 2); var gap = 10; function update() { var height = slider.offsetHeight; var width = covers.offsetWidth; if (!height || !width) return; var ratio = (((height - (col - 1) * gap) / col) / ((width - (row - 1) * gap) / row)) * 100; covers.style.setProperty('--cover-ratio', ratio.toFixed(2) + '%'); } update(); window.addEventListener('resize', update, { passive: true }); if ('ResizeObserver' in window) new ResizeObserver(update).observe(slider); } function scan(root) { var scope = root || document; scope.querySelectorAll('.aksrt-widget-video[data-video-url]').forEach(initVideo); scope.querySelectorAll('.aksrt-widget-quote').forEach(initQuote); if (scope.matches && scope.matches('[data-widget-items]')) initItems(scope); scope.querySelectorAll('[data-widget-items]').forEach(initItems); scope.querySelectorAll('.widget-tab-post').forEach(initTabs); scope.querySelectorAll('.aksrt-faq').forEach(initFaq); scope.querySelectorAll('.aksrt-feature-intro').forEach(initFeatures); scope.querySelectorAll('[data-count-up]').forEach(initCount); scope.querySelectorAll('.widget-slider-graphic').forEach(measureSliderGraphic); if (typeof window.VanillaTilt !== 'undefined') scope.querySelectorAll('[data-aksrt-tilt]').forEach(function (el) { if (once(el, 'Tilt')) window.VanillaTilt.init(el); }); } function ready() { 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(); // AJAX 注入新内容后由 auto_fun 重扫(once() data 标记保证幂等)。 if (window.jQuery) window.jQuery(document).on('auto_fun', function () { scan(document); }); })();