From fd49b08c08cce6bc2413b72dddf3fab2cedd7606 Mon Sep 17 00:00:00 2001 From: dekun Date: Fri, 26 Jun 2026 22:05:04 +0800 Subject: [PATCH] Disable turbo client navigation to fix broken page layouts. Turbo swapping broke page CSS/JS across stats, settings, market, and trade. Restore full page loads; keep external base.css and link prefetch. Co-authored-by: Cursor --- static/js/nav.js | 4 - static/js/settings.js | 3 +- static/js/stats.js | 4 +- static/js/trade.js | 12 +- static/js/turbonav.js | 276 ------------------------------------------ static/sw.js | 6 +- templates/base.html | 2 - 7 files changed, 15 insertions(+), 292 deletions(-) delete mode 100644 static/js/turbonav.js diff --git a/static/js/nav.js b/static/js/nav.js index e9ebf4c..d643240 100644 --- a/static/js/nav.js +++ b/static/js/nav.js @@ -42,10 +42,6 @@ } function prefetchNav(href) { - if (window.qihuoPrefetchPage) { - window.qihuoPrefetchPage(href); - return; - } if (!href || href.indexOf(window.location.origin) !== 0) return; if (href === window.location.href) return; var links = document.head.querySelectorAll('link[rel="prefetch"]'); diff --git a/static/js/settings.js b/static/js/settings.js index 8e684f2..c252c5f 100644 --- a/static/js/settings.js +++ b/static/js/settings.js @@ -122,5 +122,6 @@ } if (window.qihuoPageBoot) window.qihuoPageBoot(bootSettingsPage, '.settings-page'); - else document.addEventListener('DOMContentLoaded', bootSettingsPage); + else if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bootSettingsPage); + else bootSettingsPage(); })(); diff --git a/static/js/stats.js b/static/js/stats.js index e278365..be1cc45 100644 --- a/static/js/stats.js +++ b/static/js/stats.js @@ -163,6 +163,6 @@ } if (window.qihuoPageBoot) window.qihuoPageBoot(bootStatsPage, '#stats-summary'); - else if (window.qihuoOnPageLoad) window.qihuoOnPageLoad(bootStatsPage); - else document.addEventListener('DOMContentLoaded', bootStatsPage); + else if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bootStatsPage); + else bootStatsPage(); })(); diff --git a/static/js/trade.js b/static/js/trade.js index 11d5cc3..281f54e 100644 --- a/static/js/trade.js +++ b/static/js/trade.js @@ -1664,9 +1664,13 @@ } }); - if (window.qihuoPageBoot) window.qihuoPageBoot(bootTradePage, '.trade-page'); - else if (window.qihuoOnPageLoad) window.qihuoOnPageLoad(bootTradePage); - else bootTradePage(); - if (window.qihuoOnPageLeave) window.qihuoOnPageLeave(cleanupTradePage); + function startTradePage() { + if (!document.querySelector('.trade-page')) return; + bootTradePage(); + } + + if (window.qihuoPageBoot) window.qihuoPageBoot(startTradePage, '.trade-page'); + else if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', startTradePage); + else startTradePage(); window.addEventListener('pagehide', cleanupTradePage); })(); diff --git a/static/js/turbonav.js b/static/js/turbonav.js deleted file mode 100644 index 368c3b0..0000000 --- a/static/js/turbonav.js +++ /dev/null @@ -1,276 +0,0 @@ -/* Copyright (c) 2025-2026 马建军. All rights reserved. - * 专有软件 — 未经授权禁止复制、传播、转售。 - * 详见 LICENSE.zh-CN.txt - */ -(function () { - var PERMANENT_CSS = ['/static/css/base.css', '/static/css/tech.css', '/static/css/responsive.css']; - var CORE_SCRIPT_RE = /theme\.js|symbol\.js|page\.js|nav\.js|pwa\.js|turbonav\.js/; - var pageCache = new Map(); - var MAX_CACHE = 10; - var inflight = null; - var currentUrl = normalizeUrl(window.location.href); - - function normalizeUrl(href) { - var u = new URL(href, window.location.origin); - u.hash = ''; - return u.href; - } - - function isPermanentStylesheet(el) { - var href = el.getAttribute('href') || ''; - return PERMANENT_CSS.some(function (p) { return href.indexOf(p) !== -1; }); - } - - function shouldTurboClick(e, link) { - if (!link || !link.href) return false; - if (e.defaultPrevented) return false; - if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return false; - if (link.target && link.target !== '_self') return false; - if (link.hasAttribute('download')) return false; - if (link.origin !== window.location.origin) return false; - if (normalizeUrl(link.href) === currentUrl) return false; - return true; - } - - function parseHtml(html) { - return new DOMParser().parseFromString(html, 'text/html'); - } - - function fetchPage(url, signal) { - var key = normalizeUrl(url); - if (pageCache.has(key)) { - return Promise.resolve(pageCache.get(key)); - } - return fetch(key, { - credentials: 'same-origin', - headers: { Accept: 'text/html' }, - signal: signal - }).then(function (res) { - if (!res.ok) throw new Error('HTTP ' + res.status); - return res.text(); - }).then(function (html) { - var pack = { doc: parseHtml(html), html: html }; - pageCache.set(key, pack); - if (pageCache.size > MAX_CACHE) { - var first = pageCache.keys().next().value; - pageCache.delete(first); - } - return pack; - }); - } - - function collectPageCss(doc) { - var out = []; - doc.querySelectorAll('head link[rel="stylesheet"], head style').forEach(function (el) { - if (el.tagName === 'LINK' && isPermanentStylesheet(el)) return; - out.push(el); - }); - doc.querySelectorAll('.main style').forEach(function (el) { - out.push(el); - }); - return out; - } - - function prepareMainHtml(doc) { - var fetchedMain = doc.querySelector('.main'); - if (!fetchedMain) return ''; - fetchedMain.querySelectorAll('style').forEach(function (el) { el.remove(); }); - return fetchedMain.innerHTML; - } - - /** DOMParser strips inline script bodies — parse from raw HTML instead. */ - function collectPageScripts(rawHtml) { - var scripts = []; - var bodyMatch = /([\s\S]*)<\/body>/i.exec(rawHtml); - if (!bodyMatch) return scripts; - var body = bodyMatch[1]; - var re = /]*)?>([\s\S]*?)<\/script>/gi; - var pastPwa = false; - var m; - while ((m = re.exec(body)) !== null) { - var attrs = m[1] || ''; - var text = m[2]; - var srcMatch = /\ssrc=["']([^"']+)["']/i.exec(attrs); - var typeMatch = /\stype=["']([^"']+)["']/i.exec(attrs); - var idMatch = /\sid=["']([^"']+)["']/i.exec(attrs); - var src = srcMatch ? srcMatch[1] : ''; - var type = typeMatch ? typeMatch[1].toLowerCase() : 'text/javascript'; - var id = idMatch ? idMatch[1] : ''; - if (src) { - if (/pwa\.js/.test(src)) pastPwa = true; - if (CORE_SCRIPT_RE.test(src)) continue; - if (!pastPwa) continue; - scripts.push({ src: src, text: '', type: type, id: id }); - continue; - } - if (!pastPwa) continue; - scripts.push({ src: '', text: text, type: type, id: id }); - } - return scripts; - } - - function removePageAssets() { - document.querySelectorAll('[data-page-css]').forEach(function (el) { el.remove(); }); - document.querySelectorAll('body script[data-page-js], body script[data-page-data]').forEach(function (el) { - el.remove(); - }); - } - - function applyPageCss(items) { - return items.reduce(function (chain, srcEl) { - return chain.then(function () { - return new Promise(function (resolve) { - var el = srcEl.cloneNode(true); - el.setAttribute('data-page-css', ''); - if (el.tagName === 'LINK') { - el.onload = function () { resolve(); }; - el.onerror = function () { resolve(); }; - document.head.appendChild(el); - } else { - document.head.appendChild(el); - resolve(); - } - }); - }); - }, Promise.resolve()); - } - - function runPageScripts(items) { - return items.reduce(function (chain, item) { - return chain.then(function () { - return new Promise(function (resolve) { - var s = document.createElement('script'); - if (item.src) { - s.setAttribute('data-page-js', ''); - var bust = (item.src.indexOf('?') >= 0 ? '&' : '?') + '_turbo=' + Date.now(); - s.src = item.src + bust; - s.async = false; - s.onload = function () { resolve(); }; - s.onerror = function () { resolve(); }; - document.body.appendChild(s); - return; - } - if (item.type === 'application/json') { - s.type = 'application/json'; - if (item.id) s.id = item.id; - s.setAttribute('data-page-data', ''); - s.textContent = item.text; - document.body.appendChild(s); - resolve(); - return; - } - if (item.type && item.type !== 'text/javascript' && item.type !== 'module') { - resolve(); - return; - } - s.setAttribute('data-page-js', ''); - s.textContent = item.text; - document.body.appendChild(s); - resolve(); - }); - }); - }, Promise.resolve()); - } - - function syncNavActive(doc) { - var nav = document.getElementById('site-nav'); - var fetchedNav = doc.getElementById('site-nav'); - if (!nav || !fetchedNav) return; - nav.querySelectorAll('a.active').forEach(function (a) { a.classList.remove('active'); }); - fetchedNav.querySelectorAll('a[href].active').forEach(function (fa) { - var href = fa.getAttribute('href'); - if (!href) return; - var local = nav.querySelector('a[href="' + href + '"]') - || nav.querySelector('a[href="' + new URL(href, window.location.origin).pathname + '"]'); - if (local) local.classList.add('active'); - }); - } - - function setLoading(on) { - var main = document.querySelector('.main'); - if (main) main.classList.toggle('nav-loading', on); - } - - function applyDocument(pack, url, fromPopstate) { - var doc = pack.doc; - var rawHtml = pack.html; - var main = document.querySelector('.main'); - if (!main || !doc.querySelector('.main')) { - window.location.href = url; - return Promise.resolve(); - } - - window.dispatchEvent(new Event('qihuo:page-leave')); - removePageAssets(); - - var cssItems = collectPageCss(doc); - var mainHtml = prepareMainHtml(doc); - var scriptItems = collectPageScripts(rawHtml); - - return applyPageCss(cssItems).then(function () { - main.innerHTML = mainHtml; - document.title = doc.title || document.title; - syncNavActive(doc, url); - return runPageScripts(scriptItems); - }).then(function () { - currentUrl = normalizeUrl(url); - if (!fromPopstate) { - history.pushState({ turbo: true }, '', currentUrl); - } - setLoading(false); - window.scrollTo(0, 0); - if (window.qihuoEmitPageLoad) window.qihuoEmitPageLoad(); - }); - } - - function navigateTo(url, opts) { - opts = opts || {}; - var target = normalizeUrl(url); - if (target === currentUrl && !opts.force) return Promise.resolve(); - - if (inflight) inflight.abort(); - var ctrl = new AbortController(); - inflight = ctrl; - setLoading(true); - - return fetchPage(target, ctrl.signal).then(function (pack) { - if (ctrl.signal.aborted) return; - inflight = null; - return applyDocument(pack, target, !!opts.fromPopstate); - }).catch(function () { - if (ctrl.signal.aborted) return; - inflight = null; - setLoading(false); - window.location.href = target; - }); - } - - function prefetchPage(href) { - var target = normalizeUrl(href); - if (target === currentUrl || pageCache.has(target)) return; - fetchPage(target).catch(function () { /* ignore */ }); - } - - window.qihuoNavigate = navigateTo; - window.qihuoPrefetchPage = prefetchPage; - - document.addEventListener('click', function (e) { - var link = e.target.closest('#site-nav a[href]'); - if (!link || !shouldTurboClick(e, link)) return; - e.preventDefault(); - var navEl = document.getElementById('site-nav'); - if (navEl) { - navEl.querySelectorAll('a.active').forEach(function (a) { a.classList.remove('active'); }); - link.classList.add('active'); - } - navigateTo(link.href); - }, true); - - window.addEventListener('popstate', function () { - var target = normalizeUrl(window.location.href); - if (target === currentUrl) return; - navigateTo(target, { fromPopstate: true, force: true }); - }); - - if (window.qihuoEmitPageLoad) window.qihuoEmitPageLoad(); -})(); diff --git a/static/sw.js b/static/sw.js index 8ba0787..892a171 100644 --- a/static/sw.js +++ b/static/sw.js @@ -2,7 +2,7 @@ * 专有软件 — 未经授权禁止复制、传播、转售。 * 详见 LICENSE.zh-CN.txt */ -var CACHE_VERSION = 'qihuo-v8'; +var CACHE_VERSION = 'qihuo-v9'; var STATIC_CACHE = CACHE_VERSION + '-static'; var STATIC_ASSETS = [ '/static/css/base.css', @@ -10,12 +10,12 @@ var STATIC_ASSETS = [ '/static/css/responsive.css', '/static/css/trade.css', '/static/js/theme.js', - '/static/js/page.js', '/static/js/nav.js', - '/static/js/turbonav.js', '/static/js/pwa.js', '/static/js/symbol.js', '/static/js/trade.js', + '/static/js/stats.js', + '/static/js/settings.js', '/static/icons/icon-192.png', '/static/icons/icon-512.png', '/static/icons/icon.svg', diff --git a/templates/base.html b/templates/base.html index 03b0ffd..82d6e8a 100644 --- a/templates/base.html +++ b/templates/base.html @@ -73,10 +73,8 @@ - {% block extra_js %}{% endblock %} -