PageSourceSearch

https://www.tqhyg.net/wp-content/themes/Sakurairo/js/nav.js

js tqhyg.net collected 2026-10-04 01:42:09 UTC 54,516 bytes, 1,479 lines download raw bytes

1const nav = document.querySelector('nav');
2if (!nav.classList.contains('sakura_nav')) {
3    init_iro_nav();
4}
5function init_iro_nav() {
6// 导航栏长度限制
7    function initNavWidth() {
8        const nav = document.querySelector('nav');
9        const checkWidth = () => {
10            if (nav.offsetWidth > 1200) {
11                nav.style.overflowX = 'hidden';
12                nav.style.maxWidth = '1200px';
13            } else {
14                nav.style.overflowX = '';
15                nav.style.maxWidth = '';
16            }
17        };
18        checkWidth();
19        window.addEventListener('resize', checkWidth);
20    }
21
22    document.addEventListener('DOMContentLoaded', initNavWidth);
23    document.addEventListener('pjax:complete', initNavWidth);
24
25// 定义DOM元素
26const DOM = {
27    bgNext: document.getElementById("bg-next"),
28    navSearchWrapper: document.querySelector(".nav-search-wrapper"),
29    searchbox: document.querySelector(".searchbox.js-toggle-search"),
30    divider: document.querySelector(".nav-search-divider"),
31};
32
33if (!DOM.bgNext) {
34    DOM.bgNext = document.createElement('div'); // 给一个虚拟的元素,后面不再一个个判断元素是否存在
35}
36
37// 定义动画参数
38const ANIMATION = {
39    easing: "cubic-bezier(0.34, 1.56, 0.64, 1)",
40    duration: "0.6s",
41    durationMs: 600,
42};
43
44// 改进的状态管理器
45const StateManager = {
46    init() {
47        try {
48            const currentIsHome = location.pathname === "/" || location.pathname === "/index.php";
49        
50            // 强制重置非主页状态
51            if(!currentIsHome && sessionStorage.getItem("bgNextState")){
52                sessionStorage.removeItem("bgNextState");
53            }
54
55            if (sessionStorage.getItem("bgNextState")) {
56                return this.getState();
57            }
58            const state = {
59                lastPageWasHome: false,
60                isTransitioning: false,
61                firstLoad: true,
62                initialized: false,
63                firstHomeTransition: true, // 新增:追踪第一次主页过渡
64                initializedOutsideHome: false // 新增:追踪是否在非主页完成初始化
65            };
66            this.setState(state);
67            return state;
68        } catch (e) {
69            console.warn('StateManager initialization failed:', e);
70            return {
71                lastPageWasHome: false,
72                isTransitioning: false,
73                firstLoad: true,
74                initialized: false,
75                firstHomeTransition: true,
76                initializedOutsideHome: false
77            };
78        }
79    },
80
81    getState() {
82        return JSON.parse(sessionStorage.getItem("bgNextState"));
83    },
84
85    setState(state) {
86        sessionStorage.setItem("bgNextState", JSON.stringify(state));
87    },
88
89    update(changes) {
90        this.setState({
91            ...this.getState(),
92            ...changes,
93        });
94    },
95    
96    clear() {
97        sessionStorage.removeItem("bgNextState");
98    },
99};
100
101// 设置动画过渡
102const setTransitions = () => {
103    DOM.bgNext.style.transition = `all ${ANIMATION.duration} ${ANIMATION.easing}`;
104    DOM.navSearchWrapper.style.transition = `all ${ANIMATION.duration} ${ANIMATION.easing}`;
105
106    if (DOM.searchbox) {
107        DOM.searchbox.style.transition = `transform ${ANIMATION.duration} ${ANIMATION.easing}`;
108    }
109
110    if (DOM.divider) {
111        DOM.divider.style.transition = !DOM.searchbox
112            ? `all ${ANIMATION.duration} ${ANIMATION.easing}`
113            : `transform ${ANIMATION.duration} ${ANIMATION.easing}`;
114    }
115};
116
117// 初始化元素状态 - 优化后的版本
118const initElementStates = (isEntering, bgNextWidth, initialWidth, isFirstLoad = false) => {
119    // 确保元素可见性和位置重置
120    const resetElement = (element, styles) => {
121        if (element) {
122            element.style.cssText = styles;
123            // 强制重绘
124            void element.offsetWidth;
125        }
126    };
127
128    // 重置所有元素的过渡
129    [DOM.bgNext, DOM.navSearchWrapper, DOM.searchbox, DOM.divider].forEach(el => {
130        if (el) el.style.transition = 'none';
131    });
132
133    // 初始化导航容器宽度 - 移除overflow设置
134    resetElement(DOM.navSearchWrapper, `
135        width: ${initialWidth}px;
136    `);
137    // 初始化 bg-next 元素
138    resetElement(DOM.bgNext, `
139        display: block;
140        opacity: ${isEntering ? "0" : "1"};
141        transform: translateX(${isEntering ? "20px" : "0"});
142        pointer-events: auto;
143        position: relative;
144        z-index: 1;
145        transition: none;
146    `);
147
148    if (!DOM.searchbox && DOM.divider) {
149        if (isEntering && !isFirstLoad) {
150            resetElement(DOM.divider, `
151                display: block;
152                opacity: 0;
153                transform: translateX(${isEntering ? "20px" : "0"});
154                transition: none;
155            `);
156        }
157    }
158
159    if (isEntering && !isFirstLoad) {
160        setInitialPositions(bgNextWidth);
161    }
162
163    // 强制重绘所有元素
164    requestAnimationFrame(() => {
165        [DOM.bgNext, DOM.navSearchWrapper, DOM.searchbox, DOM.divider].forEach(el => {
166            if (el) void el.offsetWidth;
167        });
168    });
169};
170
171// 设置初始位置
172const setInitialPositions = (bgNextWidth) => {
173    if (DOM.searchbox) {
174        DOM.searchbox.style.cssText = `
175            transform: translateX(${bgNextWidth}px);
176            transition: none;
177        `;
178    }
179    if (DOM.divider) {
180        if (!DOM.searchbox) {
181            DOM.divider.style.cssText = `
182                display: block;
183                opacity: 0;
184                transform: translateX(${bgNextWidth}px);
185                transition: none;
186            `;
187        } else {
188            DOM.divider.style.cssText = `
189                transform: translateX(${bgNextWidth}px);
190                transition: none;
191            `;
192        }
193    }
194};
195
196// 优化的动画执行函数
197const animateElements = (isEntering, bgNextWidth, initialWidth) => {
198    // 使用 transform 代替 width 动画以提高性能
199    const animate = () => {
200        setTransitions();
201        
202        // 优化动画性能
203        DOM.navSearchWrapper.style.willChange = 'transform, width';
204        DOM.bgNext.style.willChange = 'transform, opacity';
205        DOM.bgNext.style.pointerEvents = 'auto';
206        DOM.bgNext.style.zIndex = '1';
207
208        if (DOM.searchbox) DOM.searchbox.style.willChange = 'transform';
209        if (DOM.divider) DOM.divider.style.willChange = 'transform, opacity';
210
211        // 确保在动画开始前overflow为hidden
212        if (!window._searchWrapperState || !window._searchWrapperState.state) {
213            DOM.navSearchWrapper.style.overflow = "hidden";
214        }
215
216        const elements = [
217            [DOM.bgNext, {
218                opacity: isEntering ? "1" : "0",
219                transform: `translateX(${isEntering ? "0" : "20px"})`,
220                width: `${DOM.bgNext.offsetWidth}px`,
221                height: `${DOM.bgNext.offsetWidth}px`,
222            }],
223            [DOM.navSearchWrapper, {
224                width: `${initialWidth + (isEntering ? bgNextWidth : -bgNextWidth)}px`
225            }]
226        ];
227
228        if (!isEntering) {
229            if (DOM.searchbox) {
230                elements.push([DOM.searchbox, {
231                    transform: `translateX(${bgNextWidth}px)` // 直接使用bgNextWidth
232                }]);
233            }
234            if (DOM.divider) {
235                elements.push([DOM.divider, {
236                    opacity: DOM.searchbox ? "1" : "0",
237                    transform: `translateX(${bgNextWidth}px)` // 直接使用bgNextWidth
238                }]);
239            }
240        } else {
241            if (DOM.searchbox) {
242                elements.push([DOM.searchbox, {
243                    transform: "translateX(0)"
244                }]);
245            }
246            if (DOM.divider) {
247                elements.push([DOM.divider, {
248                    opacity: "1",
249                    transform: "translateX(0)"
250                }]);
251            }
252        }
253
254        // 移除之前的transitionend事件,改为仅在动画完成后处理
255        setTimeout(() => {
256            DOM.navSearchWrapper.style.willChange = '';
257            DOM.bgNext.style.willChange = '';
258            DOM.bgNext.style.pointerEvents = 'auto'; // 确保动画结束后仍可点击
259            if (DOM.searchbox) DOM.searchbox.style.willChange = '';
260            if (DOM.divider) DOM.divider.style.willChange = '';
261            
262            // 仅在非文章标题切换状态时重置overflow
263            if (!window._searchWrapperState || !window._searchWrapperState.state) {
264                requestAnimationFrame(() => {
265                    DOM.navSearchWrapper.style.overflow = "unset";
266                });
267            }
268        }, ANIMATION.durationMs);
269
270        elements.forEach(([element, styles]) => {
271            if (element) {
272                Object.assign(element.style, styles);
273            }
274        });
275    };
276
277    // 使用 RAF 确保动画流畅
278    requestAnimationFrame(animate);
279};
280
281// 页面过渡处理
282const handlePageTransition = (isHomePage, state) => {
283    // 添加清理逻辑
284    const cleanupAnimations = () => {
285        if (window._searchWrapperState) {
286            window._searchWrapperState.state = false;
287            if (window._searchWrapperState.hideTimeout) {
288                clearTimeout(window._searchWrapperState.hideTimeout);
289            }
290            if (window._searchWrapperState.scrollRAF) {
291                cancelAnimationFrame(window._searchWrapperState.scrollRAF);
292            }
293        }
294        
295        // 重置导航样式
296        DOM.navSearchWrapper.style.overflow = "unset";
297        DOM.navSearchWrapper.style.width = "auto";
298        delete DOM.navSearchWrapper.dataset.scrollswap;
299        DOM.navSearchWrapper.style.setProperty("--dw", "0");
300
301        if (isHomePage) {
302            // 确保在主页时 bg-next 可点击
303            DOM.bgNext.style.pointerEvents = 'auto';
304            DOM.bgNext.style.zIndex = '1';
305        }
306    };
307
308    if (isHomePage === state.lastPageWasHome) {
309        cleanupAnimations();
310        DOM.bgNext.style.display = isHomePage ? "block" : "none";
311        DOM.bgNext.style.transform = isHomePage ? "none" : "translateX(20px)";
312        DOM.bgNext.style.opacity = isHomePage ? "1" : "0";
313        return;
314    }
315
316    // 使用 Performance API 优化动画时机
317    if (window.performance && window.performance.now) {
318        const startTime = performance.now();
319        
320        // 添加防抖,避免快速切换导致的动画问题
321        if (state.transitionTimer) {
322            cancelAnimationFrame(state.transitionTimer);
323        }
324
325        state.transitionTimer = requestAnimationFrame(async () => {
326            // 确保在非主页初始化后首次进入主页时重新初始化
327            if (isHomePage && state.initializedOutsideHome && !InitController.initialized) {
328                await InitController.init();
329            }
330            
331            if (!InitController.initialized) {
332                await InitController.init();
333            }
334            
335            const getMeasuredWidth = (element) => {
336                return new Promise(resolve => {
337                    const clone = element.cloneNode(true);
338                    clone.style.cssText = `
339                        all: initial !important;
340                        position: fixed !important;
341                        visibility: hidden !important;
342                        display: block !important;
343                        max-width: none !important;
344                        width: auto !important;
345                        height: auto !important;
346                        margin: 0 !important;
347                        padding: ${getComputedStyle(element).padding} !important;
348                        font: ${getComputedStyle(element).font} !important;
349                    `;
350                    
351                    requestAnimationFrame(() => {
352                        document.body.appendChild(clone);
353                        requestAnimationFrame(() => {
354                            const width = clone.getBoundingClientRect().width;
355                            document.body.removeChild(clone);
356                            resolve(Math.ceil(width));
357                        });
358                    });
359                });
360            };
361
362            try {
363                const isWebKit = BrowserDetect.isWebKit();
364                const originalDisplay = DOM.bgNext.style.display;
365                
366                // 临时隐藏所有可能影响宽度的元素
367                DOM.bgNext.style.display = 'none';
368                if (DOM.searchbox) DOM.searchbox.style.visibility = 'hidden';
369                if (DOM.divider) DOM.divider.style.visibility = 'hidden';
370                
371                let wrapperWidth = Math.ceil(DOM.navSearchWrapper.getBoundingClientRect().width);
372                
373                // 恢复元素显示
374                DOM.bgNext.style.display = originalDisplay;
375                if (DOM.searchbox) DOM.searchbox.style.visibility = '';
376                if (DOM.divider) DOM.divider.style.visibility = '';
377                
378                const bgWidth = await WidthCalculator.measure(DOM.bgNext, {
379                    useCache: true,
380                    additionalStyles: 'margin: 0 !important; padding: inherit !important;'
381                });
382                
383                const gap = parseFloat(getComputedStyle(DOM.navSearchWrapper).gap) || 0;
384                const finalBgWidth = Math.ceil(bgWidth + gap);
385                
386                // 只在非主页的WebKit环境下补充宽度
387                if (isWebKit && !isHomePage) {
388                    wrapperWidth = Math.ceil(wrapperWidth + finalBgWidth);
389                }
390                
391                animateTransition(isHomePage, state, finalBgWidth, wrapperWidth);
392            } catch (error) {
393                console.error('过渡失败:', error);
394                cleanupAnimations();
395            }
396        });
397    }
398};
399
400// 执行过渡动画
401const animateTransition = (isEntering, state, bgNextWidth, initialWidth) => {
402
403    // 增加容错检测
404    const currentWidth = DOM.navSearchWrapper.offsetWidth;
405    if(Math.abs(initialWidth - currentWidth) > 50){
406        console.warn('Width deviation detected, recalculating...');
407        initialWidth = currentWidth;
408    }
409
410    if (state.isTransitioning) return;
411
412    StateManager.update({
413        isTransitioning: true,
414    });
415
416    // 使用存储的测量宽度进行验证和修正
417    if (state.measuredWidths) {
418        const currentInitialWidth = Math.ceil(DOM.navSearchWrapper.getBoundingClientRect().width);
419        // 修复:如果是第一次执行退出动画且当前是主页,初始宽度需要减去一个 bgNextWidth
420        if (!state.firstExitDone && !isEntering && location.pathname === "/") {
421            initialWidth -= bgNextWidth;
422            state.firstExitDone = true;
423            StateManager.setState(state);
424        }
425        // 检查宽度差异
426        if (Math.abs(currentInitialWidth - state.measuredWidths.initial) > 5) {
427            console.debug('Width mismatch detected, recalculating...');
428            initialWidth = currentInitialWidth;
429        }
430    }
431
432    initElementStates(isEntering, bgNextWidth, initialWidth);
433
434    [DOM.bgNext, DOM.navSearchWrapper, DOM.searchbox, DOM.divider].forEach((el) => {
435        if (el) void el.offsetWidth;
436    });
437
438    requestAnimationFrame(() => {
439        setTransitions();
440        animateElements(isEntering, bgNextWidth, initialWidth);
441
442        // 修改动画完成后的处理
443        setTimeout(() => {
444            if (!isEntering) {
445                DOM.bgNext.style.display = "none";
446                DOM.navSearchWrapper.style.width = "auto";
447                // 确保在non-home页面时正确设置overflow
448                if (!window._searchWrapperState || !window._searchWrapperState.state) {
449                    DOM.navSearchWrapper.style.overflow = "unset";
450                }
451                if (!DOM.searchbox && DOM.divider) {
452                    DOM.divider.style.display = "none";
453                }
454                [DOM.searchbox, DOM.divider].forEach((el) => {
455                    if (el) {
456                        el.style.transition = "none";
457                        el.style.transform = "";
458                    }
459                });
460            }
461            StateManager.update({
462                isTransitioning: false,
463            });
464        }, ANIMATION.durationMs);
465    });
466};
467
468// 显示或隐藏bgNext元素 - 优化后的版本
469const showBgNext = async () => {
470    const isHomePage =
471        location.pathname === "/" || location.pathname === "/index.php";
472    const state = StateManager.getState();
473
474    if (!state.initialized) {
475        const widths = await InitController.init();
476        state.initialized = true;
477        state.measuredWidths = widths;
478        StateManager.setState(state);
479    }
480
481    if (state.isTransitioning) {
482        // 即使在过渡状态,也要确保在主页时 bg-next 可点击
483        if (isHomePage) {
484            DOM.bgNext.style.pointerEvents = 'auto';
485            DOM.bgNext.style.zIndex = '1';
486        }
487        return;
488    }
489
490    // 处理首次加载非主页的情况
491    if (!state.initialized && !isHomePage) {
492        state.initializedOutsideHome = true;
493        state.initialized = true;
494        StateManager.setState(state);
495        DOM.bgNext.style.display = 'none';
496        return;
497    }
498
499    // 处理从非主页首次回到主页的情况
500    if (isHomePage && state.initializedOutsideHome && state.firstHomeTransition) {
501        try {
502            const widths = await InitController.init();
503            if (!widths) return;
504
505            state.firstHomeTransition = false;
506            state.measuredWidths = widths;
507            StateManager.setState(state);
508
509            // 设置初始状态
510            initElementStates(true, widths.bgNextWidth, widths.wrapperWidth, false);
511            
512            // 使用 RAF 链确保动画正确执行
513            requestAnimationFrame(() => {
514                DOM.bgNext.style.opacity = '0';
515                DOM.bgNext.style.transform = 'translateX(20px)';
516                DOM.bgNext.style.display = 'block';
517                requestAnimationFrame(() => {
518                    setTransitions();
519                    animateElements(true, widths.bgNextWidth, widths.wrapperWidth);
520                });
521            });
522            return;
523        } catch (error) {
524            console.error('主页初始化失败:', error);
525            state.firstHomeTransition = false;
526            StateManager.setState(state);
527            return;
528        }
529    }
530
531    // 处理常规的首页首次加载
532    if (state.firstLoad && !state.initialized && isHomePage) {
533        try {
534            const widths = await InitController.init();
535            if (!widths) return;
536            
537            // 确保DOM已准备就绪
538            await new Promise(resolve => {
539                if (document.readyState === 'complete') {
540                    resolve();
541                } else {
542                    window.addEventListener('load', resolve, { once: true });
543                }
544            });
545            
546            state.initialized = true;
547            state.measuredWidths = widths;
548            StateManager.setState(state);
549            
550            // 使用RAF链执行初始动画
551            requestAnimationFrame(() => {
552                initElementStates(true, widths.bgNextWidth, widths.wrapperWidth, true);
553                state.firstLoad = false;
554                StateManager.setState(state);
555                
556                requestAnimationFrame(() => {
557                    animateElements(true, widths.bgNextWidth, widths.wrapperWidth);
558                });
559            });
560        } catch (error) {
561            console.error('初始化失败:', error);
562            state.firstLoad = false;
563            StateManager.setState(state);
564        }
565        return;
566    }
567
568    handlePageTransition(isHomePage, state);
569};
570
571// 初始化文章标题行为
572const initArticleTitleBehavior = () => {
573    DOM.navSearchWrapper.style.overflow = "unset";
574
575    if (window._searchWrapperState) {
576        const navTitle = DOM.navSearchWrapper.querySelector(".nav-article-title");
577        if (navTitle) {
578            navTitle.remove();
579        }
580        delete DOM.navSearchWrapper.dataset.scrollswap;
581        DOM.navSearchWrapper.style.setProperty("--dw", "0");
582        window._searchWrapperState = null;
583    }
584
585    if (!_iro.land_at_home) {
586        const searchWrapperState = {
587            state: false,
588            navElement: null,
589            navTitle: null,
590            entryTitle: null,
591            titlePadding: 20,
592            scrollTimeout: null,
593            hideTimeout: null,
594            headerElement: null,
595
596            init() {
597                this.navTitle = DOM.navSearchWrapper.querySelector(".nav-article-title");
598                this.entryTitle = document.querySelector(".entry-title");
599                this.navElement = DOM.navSearchWrapper.querySelector("nav");
600                this.header = document.querySelector("header");
601
602                if (!this.navTitle) {
603                    this.navTitle = document.createElement("div");
604                    this.navTitle.classList.add("nav-article-title");
605                    this.navTitle.style.opacity = "0";
606                    DOM.navSearchWrapper.firstElementChild.insertAdjacentElement(
607                        "afterend",
608                        this.navTitle
609                    );
610
611                    this.header.addEventListener("mouseenter", () => {
612                        if (this.hideTimeout) {
613                            clearTimeout(this.hideTimeout);
614                            this.hideTimeout = null;
615                        }
616                        if (this.entryTitle && this.entryTitle.getBoundingClientRect().top < 0) {
617                            this.hide();
618                        }
619                    });
620
621                    this.header.addEventListener("mouseleave", () => {
622                        if (this.hideTimeout) {
623                            clearTimeout(this.hideTimeout);
624                        }
625                        if (this.entryTitle && this.entryTitle.getBoundingClientRect().top < 0) {
626                            this.hideTimeout = setTimeout(() => {
627                                this.show();
628                                this.hideTimeout = null;
629                            }, 3000);
630                        }
631                    });
632
633                    this.navElement.addEventListener("transitionend", (event) => {
634                        if (event.target !== this.navElement && event.target !== this.header) return;
635                        this.navTitle.style.opacity = window.getComputedStyle(this.navElement).transform == "none" ? "0" : "1";
636                        if (document.querySelector(".entry-title")) {
637                            DOM.navSearchWrapper.style.overflow = window.getComputedStyle(this.navElement).transform === "none" ? "unset" : "hidden";
638                        }
639                    });
640
641                    this.navElement.addEventListener("transitionstart", (event) => {
642                        if (event.target !== this.navElement && event.target !== this.header) return;
643                        if (document.querySelector(".entry-title")) {
644                            DOM.navSearchWrapper.style.overflow = "hidden";
645                        }
646                        this.navTitle.style.opacity = "1";
647                    });
648                }
649                this.updateTitle();
650            },
651
652            updateTitle() {
653                if (this.entryTitle) {
654                    this.navTitle.textContent = this.entryTitle.textContent;
655                    this.navTitle.style.display = "block";
656                } else {
657                    this.navTitle.style.display = "none";
658                }
659            },
660
661            show() {
662                if (this.state || !this.entryTitle) return;
663                const navSearchWrapper = DOM.navSearchWrapper;
664                navSearchWrapper.dataset.scrollswap = "true";
665
666                requestAnimationFrame(() => {
667                    // 创建临时导航容器
668                    const tempNav = document.createElement('div');
669                    tempNav.style.cssText = `
670                        position: absolute;
671                        visibility: hidden;
672                        white-space: nowrap;
673                        display: flex;
674                        align-items: center;
675                        padding: ${window.getComputedStyle(this.navElement).padding};
676                        margin: ${window.getComputedStyle(this.navElement).margin};
677                        gap: ${window.getComputedStyle(this.navElement).gap};
678                    `;
679                    document.body.appendChild(tempNav);
680
681                    // 克隆导航项并保持完整样式
682                    const menuItems = Array.from(this.navElement.children);
683                    menuItems.forEach(item => {
684                        const clone = item.cloneNode(true);
685                        const computedStyle = window.getComputedStyle(item);
686                        clone.style.cssText = Array.from(computedStyle).reduce((str, prop) => {
687                            return `${str}${prop}:${computedStyle.getPropertyValue(prop)};`;
688                        }, '');
689                        tempNav.appendChild(clone);
690                    });
691                    
692                    const actualNavWidth = Math.ceil(tempNav.getBoundingClientRect().width);
693                    
694                    // 创建临时标题容器
695                    const tempTitle = document.createElement('div');
696                    const titleStyle = window.getComputedStyle(this.navTitle);
697                    tempTitle.style.cssText = `
698                        position: absolute;
699                        visibility: hidden;
700                        white-space: nowrap;
701                        font-size: ${titleStyle.fontSize};
702                        font-family: ${titleStyle.fontFamily};
703                        font-weight: ${titleStyle.fontWeight};
704                        letter-spacing: ${titleStyle.letterSpacing};
705                        padding: ${titleStyle.padding};
706                        margin: ${titleStyle.margin};
707                    `;
708                    tempTitle.textContent = this.navTitle.textContent;
709                    document.body.appendChild(tempTitle);
710                    
711                    const actualTitleWidth = Math.ceil(tempTitle.getBoundingClientRect().width);
712                    
713                    // 直接使用宽度差值,不再添加补偿空间
714                    const deltaWidth = actualTitleWidth - actualNavWidth;
715                    
716                    navSearchWrapper.style.setProperty("--dw", `${deltaWidth}px`);
717
718                    // 清理临时元素
719                    document.body.removeChild(tempNav);
720                    document.body.removeChild(tempTitle);
721                });
722                this.state = true;
723            },
724
725            hide() {
726                if (!this.state) return;
727                const navSearchWrapper = DOM.navSearchWrapper;
728                delete navSearchWrapper.dataset.scrollswap;
729                navSearchWrapper.style.setProperty("--dw", "0");
730                if (document.querySelector(".entry-title")) {
731                    navSearchWrapper.style.overflow = "unset";
732                }
733                this.state = false;
734            },
735
736            handleScroll() {
737                // 使用 RAF 优化滚动性能
738                if (!this.scrollRAF) {
739                    this.scrollRAF = requestAnimationFrame(() => {
740                        if (this.entryTitle) {
741                            const rect = this.entryTitle.getBoundingClientRect();
742                            if (rect.top < 0) {
743                                this.show();
744                            } else {
745                                this.hide();
746                            }
747                        }
748                        this.scrollRAF = null;
749                    });
750                }
751            },
752        };
753
754        searchWrapperState.init();
755
756        const scrollHandler = () => searchWrapperState.handleScroll();
757        window.addEventListener("scroll", scrollHandler, { passive: true });
758        window.addEventListener("resize", () => searchWrapperState.show(), {
759            passive: true,
760        });
761
762        scrollHandler();
763
764        window._searchWrapperState = searchWrapperState;
765    } else {
766        requestAnimationFrame(() => {
767            DOM.navSearchWrapper.style.overflow = "unset";
768        });
769    }
770
771    // 增加延迟检测
772    setTimeout(() => {
773        if(window._searchWrapperState){
774            window._searchWrapperState.handleScroll();
775            // 强制重算宽度
776            window._searchWrapperState.show(); 
777        }
778    }, 300); // 等待浏览器滚动恢复
779};
780
781// 初始化所有动画
782const initAnimations = () => {
783    StateManager.init();
784    showBgNext();
785    initArticleTitleBehavior();
786};
787
788// 优化的事件监听器
789const addEventListeners = () => {
790    const events = [
791        ["pjax:send", () => {
792            WidthCalculator.clearCache();
793            // 清理之前的动画状态
794            if (window._searchWrapperState) {
795                if (window._searchWrapperState.hideTimeout) {
796                    clearTimeout(window._searchWrapperState.hideTimeout);
797                }
798                if (window._searchWrapperState.scrollRAF) {
799                    cancelAnimationFrame(window._searchWrapperState.scrollRAF);
800                }
801                window._searchWrapperState.state = false;
802            }
803            
804            StateManager.update({
805                lastPageWasHome: location.pathname === "/" || location.pathname === "/index.php",
806                isTransitioning: false // 强制重置过渡状态
807            });
808        }],
809        ["pjax:complete", () => {
810            requestAnimationFrame(() => {
811                // 确保所有状态重置
812                DOM.navSearchWrapper.style.overflow = "unset";
813                DOM.navSearchWrapper.style.width = "auto";
814                delete DOM.navSearchWrapper.dataset.scrollswap;
815                DOM.navSearchWrapper.style.setProperty("--dw", "0");
816                
817                showBgNext();
818                if (window._searchWrapperState) {
819                    window._searchWrapperState.init();
820                    window._searchWrapperState.handleScroll();
821                } else {
822                    initArticleTitleBehavior();
823                }
824            });
825        }],
826        ["DOMContentLoaded", initAnimations],
827        ['popstate', () => {
828            const isHomePage = location.pathname === "/" || location.pathname === "/index.php";
829            if (isHomePage) {
830                StateManager.clear();
831                StateManager.init();
832                showBgNext();
833            }
834        }]
835    ];
836
837    events.forEach(([event, handler]) => {
838        document.addEventListener(event, handler);
839    });
840};
841
842// 初始化时调用事件监听器设置
843addEventListeners();
844
845// 新增宽度测量工具
846const WidthCalculator = {
847    cache: new Map(),
848    
849    async measure(element, options = {}) {
850        if (!element) return 0;
851        
852        const cacheKey = element.id || element.className;
853        if (options.useCache && this.cache.has(cacheKey)) {
854            return this.cache.get(cacheKey);
855        }
856
857        const clone = element.cloneNode(true);
858        clone.style.cssText = `
859            position: fixed !important;
860            visibility: hidden !important;
861            display: block !important;
862            transform: none !important;
863            transition: none !important;
864            animation: none !important;
865            opacity: 0 !important;
866            pointer-events: none !important;
867            z-index: -9999 !important;
868            ${options.additionalStyles || ''}
869        `;
870        
871        document.body.appendChild(clone);
872        
873        // 等待几帧以确保样式应用完成
874        await new Promise(resolve => {
875            let frames = 3;
876            function waitFrames() {
877                if (--frames <= 0) {
878                    resolve();
879                } else {
880                    requestAnimationFrame(waitFrames);
881                }
882            }
883            requestAnimationFrame(waitFrames);
884        });
885
886        const rect = clone.getBoundingClientRect();
887        const width = Math.ceil(rect.width);
888        
889        document.body.removeChild(clone);
890        
891        if (options.useCache) {
892            this.cache.set(cacheKey, width);
893        }
894        
895        return width;
896    },
897
898    clearCache() {
899        this.cache.clear();
900    }
901};
902
903// 新增初始化控制器
904const InitController = {
905    initialized: false,
906    initializing: false,
907    queue: [],
908    
909    async init() {
910        if (this.initialized || this.initializing) return;
911        this.initializing = true;
912        
913        // 确保字体加载完成
914        await document.fonts.ready;
915        // 增加延迟确保布局稳定
916        await new Promise(resolve => setTimeout(resolve, 200));
917        
918        // 强制重新布局并测量
919        const measure = async () => {
920            // 保存原始状态
921            const originalStyles = {
922                wrapper: DOM.navSearchWrapper.style.cssText,
923                bgNext: DOM.bgNext.style.cssText,
924                searchbox: DOM.searchbox ? DOM.searchbox.style.cssText : '',
925                divider: DOM.divider ? DOM.divider.style.cssText : ''
926            };
927            
928            // 重置所有样式以获取准确测量
929            DOM.navSearchWrapper.style.cssText = `
930                overflow: hidden !important;
931                width: auto !important;
932                transition: none !important;
933                transform: none !important;
934            `;
935            
936            DOM.bgNext.style.cssText = `
937                display: none !important;
938                transition: none !important;
939                transform: none !important;
940            `;
941            
942            if (DOM.searchbox) DOM.searchbox.style.visibility = 'hidden';
943            if (DOM.divider) DOM.divider.style.visibility = 'hidden';
944            
945            // 强制重排
946            void DOM.navSearchWrapper.offsetWidth;
947            
948            // 获取基础宽度
949            let wrapperWidth = Math.ceil(DOM.navSearchWrapper.getBoundingClientRect().width);
950            
951            // 测量 bg-next
952            DOM.bgNext.style.cssText = `
953                position: relative !important;
954                visibility: visible !important;
955                display: block !important;
956                opacity: 0 !important;
957                transform: none !important;
958                transition: none !important;
959            `;
960            
961            void DOM.bgNext.offsetWidth;
962            
963            const bgWidth = Math.ceil(DOM.bgNext.getBoundingClientRect().width);
964            const gap = parseFloat(getComputedStyle(DOM.navSearchWrapper).gap) || 0;
965            const finalBgWidth = Math.ceil(bgWidth + gap);
966            
967            // 恢复原始样式
968            DOM.navSearchWrapper.style.cssText = originalStyles.wrapper;
969            DOM.bgNext.style.cssText = originalStyles.bgNext;
970            if (DOM.searchbox) DOM.searchbox.style.cssText = originalStyles.searchbox;
971            if (DOM.divider) DOM.divider.style.cssText = originalStyles.divider;
972            
973            return {
974                bgNextWidth: finalBgWidth,
975                wrapperWidth: wrapperWidth
976            };
977        };
978        
979        const measurements = await measure();
980        
981        // 二次确认测量
982        await new Promise(resolve => requestAnimationFrame(resolve));
983        const secondMeasure = await measure();
984        
985        // 如果两次测量差异过大,使用第二次的结果
986        const finalMeasurements = {
987            bgNextWidth: Math.abs(measurements.bgNextWidth - secondMeasure.bgNextWidth) > 2 
988                ? secondMeasure.bgNextWidth 
989                : measurements.bgNextWidth,
990            wrapperWidth: Math.abs(measurements.wrapperWidth - secondMeasure.wrapperWidth) > 2
991                ? secondMeasure.wrapperWidth
992                : measurements.wrapperWidth
993        };
994        
995        this.initialized = true;
996        this.initializing = false;
997        
998        return finalMeasurements;
999    }
1000};
1001
1002// 添加浏览器检测工具
1003const BrowserDetect = {
1004    isWebKit: () => {
1005        return 'WebkitAppearance' in document.documentElement.style;
1006    }
1007}
1008};//iro_nav function
1009
1010//以上仅新版导航栏,以下是通用部分
1011document.addEventListener("DOMContentLoaded", () => {
1012
1013    //防止子菜单量子叠加
1014    const menuItems = document.querySelectorAll('nav .menu > li');
1015    let activeSubMenu = null;
1016
1017    menuItems.forEach(item => {
1018        const subMenu = item.querySelector('.sub-menu');
1019
1020        if (!subMenu) return;
1021
1022        //鼠标移入时激活子菜单
1023        item.addEventListener('mouseenter', () => {
1024            if (activeSubMenu && activeSubMenu !== subMenu) {
1025                //有且仅有一个激活
1026                activeSubMenu.classList.remove('active');
1027            }
1028
1029            //更新并激活当前子菜单
1030            subMenu.classList.add('active');
1031            activeSubMenu = subMenu;
1032        });
1033    });
1034    //放叠加结束
1035
1036    //子菜单对齐
1037    const subMenus = document.querySelectorAll("nav .menu > li .sub-menu");
1038
1039    subMenus.forEach(subMenu => {
1040        const MainMenu = subMenu.parentElement;
1041
1042        // 获取渲染后的宽度
1043        const MainMenuWidth = MainMenu.getBoundingClientRect().width;
1044        const subMenuWidth = subMenu.getBoundingClientRect().width;
1045
1046        // 偏移计算,确保子菜单居中
1047        const offsetX = (subMenuWidth - MainMenuWidth) / 2;
1048
1049        // 设置初始样式
1050        const BasicSubMenuStyle = `translateY(-10px) translateX(${offsetX}px)`;
1051        subMenu.style.transform = BasicSubMenuStyle;
1052
1053        // 设置偏移量
1054        MainMenu.addEventListener("mouseenter", () => {
1055            subMenu.style.transform = `translateY(0) translateX(${offsetX}px)`;
1056        });
1057        MainMenu.addEventListener("mouseleave", () => {
1058            subMenu.style.transform = BasicSubMenuStyle;
1059        });
1060    });
1061    //子菜单对齐结束
1062
1063    //以下是窄屏/移动端通用部分
1064    //移动端菜单开关
1065    //通用部分
1066    let moNavButton = document.querySelector(".mo-nav-button");
1067    let moTocButton = document.querySelector(".mo-toc-button");
1068    let moNavMenu   = document.querySelector(".mobile-nav");
1069    let moTocMenu   = document.querySelector(".mo_toc_panel");
1070    let moHeader    = document.querySelector(".site-header");
1071    let navTransitionHandler = null;
1072    let panelTransitionHandler = null;
1073
1074    let panelOrderAnime = null;
1075
1076    moUserMenu = document.querySelector(".mo-user-menu");
1077    if (!moUserMenu) {
1078        TocButtonStat ();
1079
1080        function TocButtonStat () {
1081            document.addEventListener("tocEvent",function(event){ // page/index.js 目录生成事件
1082                
1083                let haveToc = document.querySelector("#main-container .toc-container .toc");
1084                let haveContent = event.detail;
1085
1086                if (haveToc && haveContent) { // 下方用户栏和目录二选一,此处是没有用户栏还没有目录的情况
1087                    moTocButton.style.transform = 'translateY(0)';
1088                } else {
1089                    moTocButton.style.transform = 'translateY(-100%)';
1090                }
1091
1092            })
1093        }
1094
1095        document.addEventListener('pjax:complete', function() {
1096            TocButtonStat ();
1097        });
1098    }
1099
1100    //动画监听
1101    function isAnyPanelOpen() {
1102        return moNavMenu.classList.contains("open") || moTocMenu.classList.contains("open");
1103    }
1104
1105    function isHeaderHover() {
1106        const bgColor = getComputedStyle(moHeader).backgroundColor;
1107        return bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)"; //不透明即视为Hover
1108    }
1109
1110    function openMenu(panel, button) {
1111
1112        if (isHeaderHover()) { //hover会产生背景动画,干扰相关功能,但是无需判断动画先后,直接呼出即可
1113            panel.classList.add("open");
1114            button.classList.add("open");
1115            moHeader.classList.add("bg");
1116            return;
1117        }
1118
1119        if (navTransitionHandler) {
1120            moHeader.removeEventListener("transitionend", navTransitionHandler);
1121            navTransitionHandler = null;
1122            panel.classList.add("open");
1123            button.classList.add("open");
1124            moHeader.classList.add("bg");
1125            return;
1126        }
1127
1128        // 先给导航栏添加背景
1129        if (!moHeader.classList.contains("bg")) {
1130            moHeader.classList.add("bg");
1131            button.classList.add("open");
1132            navTransitionHandler = function(e) {
1133                if (e.propertyName === "background-color") {
1134                    panel.classList.add("open");
1135                    moHeader.removeEventListener("transitionend", navTransitionHandler);
1136                    navTransitionHandler = null;
1137                }
1138            };
1139            moHeader.addEventListener("transitionend", navTransitionHandler);
1140        } else {
1141            // 已有背景
1142            panel.classList.add("open");
1143            button.classList.add("open");
1144        }
1145    }
1146
1147    function closeMenu(panel, button,CloseDirect = false) {
1148
1149        if (CloseDirect){ //直接关闭,hover不需要考虑背景动画顺序
1150            panel.classList.remove("open");
1151            button.classList.remove("open");
1152            moHeader.classList.remove("bg");
1153            return;
1154        } 
1155
1156        if (panelTransitionHandler) {
1157            panel.removeEventListener("transitionend", panelTransitionHandler);
1158            panelTransitionHandler = null;
1159            if (!isAnyPanelOpen()) {
1160                moHeader.classList.remove("bg");
1161            }
1162            return;
1163        }
1164
1165        panel.classList.remove("open");
1166        button.classList.remove("open");
1167    
1168        panelTransitionHandler = function(e) {
1169            if (e.propertyName === "max-height") {
1170                // 所有面板都关闭后才撤销导航栏背景
1171                if (!isAnyPanelOpen()) {
1172                    moHeader.classList.remove("bg");
1173                }
1174                panel.removeEventListener("transitionend", panelTransitionHandler);
1175                panelTransitionHandler = null;
1176            }
1177        };
1178        panel.addEventListener("transitionend", panelTransitionHandler);
1179    }
1180
1181    function closeThenOpen(oldPanel, newPanel) {
1182        let oldButton, newButton;
1183        // 根据面板判断对应按钮
1184        if (oldPanel === moNavMenu) {
1185            oldButton = moNavButton;
1186        } else if (oldPanel === moTocMenu) {
1187            oldButton = moTocButton;
1188        }
1189        
1190        if (newPanel === moNavMenu) {
1191            newButton = moNavButton;
1192        } else if (newPanel === moTocMenu) {
1193            newButton = moTocButton;
1194        }
1195
1196        closeMenu(oldPanel,oldButton);
1197        banAllButton(); //动画期间禁止交互
1198        panelOrderAnime = function(e) {
1199            if (e.propertyName === "max-height") {
1200                oldPanel.removeEventListener("transitionend", panelOrderAnime);
1201                openMenu(newPanel, newButton);
1202                activeAllButton();
1203            }
1204        }
1205        oldPanel.addEventListener("transitionend", panelOrderAnime);
1206    }
1207
1208    function banAllButton () {
1209        moNavButton.style.pointerEvents = "none";
1210        moTocButton.style.pointerEvents = "none";
1211    }
1212    function activeAllButton() {
1213        moNavButton.style.pointerEvents = "auto";
1214        moTocButton.style.pointerEvents = "auto";
1215    }
1216
1217    // 面板
1218    moNavButton.addEventListener("click", function (event) {
1219        event.stopPropagation();
1220
1221        if (moTocMenu.classList.contains("open")) {
1222            closeThenOpen(moTocMenu,moNavMenu);
1223            return;
1224        }
1225        if (moNavMenu.classList.contains("open")) {
1226            closeMenu(moNavMenu, moNavButton,isHeaderHover());
1227        } else {
1228            openMenu(moNavMenu, moNavButton);
1229        }
1230    });
1231
1232    moTocButton.addEventListener("click", function (event) {
1233        event.stopPropagation();
1234
1235        if (moNavMenu.classList.contains("open")) {
1236            closeThenOpen(moNavMenu,moTocMenu);
1237            generateMoToc();
1238            return;
1239        }
1240        if (moTocMenu.classList.contains("open")) {
1241            closeMenu(moTocMenu, moTocButton,isHeaderHover());
1242        } else {
1243            openMenu(moTocMenu, moTocButton);
1244            generateMoToc();
1245        }
1246    });
1247
1248    let moUserOption = document.querySelector(".mo_toc_panel .mo-user-options");
1249    function generateMoToc() {
1250        //复制菜单
1251        let mainToc = document.querySelector("#main-container .toc-container .toc");
1252        let headToc = document.querySelector(".site-header .mo_toc_panel .mo_toc");
1253
1254        if (mainToc && headToc) { //都存在
1255            if (mainToc.hasChildNodes()) { //不为空
1256                tocContent = mainToc.cloneNode(true); //复制
1257                moUserOption.style.display = "none"; //隐藏用户栏并显示目录
1258                headToc.style.display = ""; 
1259
1260                tocContent.querySelectorAll("li").forEach(li => { //遍历层级
1261                    let subol = li.querySelector("ol")
1262                    if (subol) {
1263                        //含子目录
1264                        li.classList.add("have-child");
1265
1266                        subol.classList.remove('is-collapsible');
1267                        subol.classList.remove('is-collapsed');
1268                    }
1269                });
1270
1271                tocContent.className = "mo-toc-content"; 
1272                tocContent.removeAttribute("style");
1273                headToc.innerHTML = ""; //清空目标容器,防止重复
1274                headToc.appendChild(tocContent);
1275                let activeli = headToc.querySelector(".is-active-li") || headToc.querySelector("li"); //滚动至tocbot的高亮进度
1276                if (activeli) {
1277                    activeli.scrollIntoView({ block: "center" });
1278                }
1279            } else { //目录为空
1280                if (headToc) {
1281                    headToc.style.display = "none"; //隐藏目录
1282                }
1283                moUserOption.style.display = ""; //展示用户栏
1284            }
1285        } else { //没有目录
1286            if (headToc) {
1287                headToc.style.display = "none";
1288            }
1289            moUserOption.style.display = "";
1290        }
1291    }
1292
1293    //二级菜单
1294    document.querySelectorAll(".open_submenu").forEach(function (toggle) {
1295        toggle.addEventListener("click", function (event) {
1296            event.stopPropagation();
1297
1298            let parentLi = this.closest("li");
1299            let currentSubMenu = parentLi.querySelector(".sub-menu");
1300
1301            //互斥
1302            document.querySelectorAll(".sub-menu.open").forEach(otherSubMenu => {
1303                if (otherSubMenu !== currentSubMenu) {
1304                    otherSubMenu.classList.remove("open");
1305                    let otherToggle = otherSubMenu.closest("li").querySelector(".open_submenu");
1306                    if (otherToggle) {
1307                        otherToggle.classList.remove("open");
1308                    }
1309                }
1310            });
1311
1312            if (currentSubMenu) {
1313                currentSubMenu.classList.toggle("open");
1314                this.classList.toggle("open");
1315            }
1316        });
1317    });
1318
1319    // 点击选项关闭
1320    document.querySelectorAll(".mobile-nav a, .mo_toc_panel a").forEach(link => {
1321        link.addEventListener("click", () => {
1322                closeMenu(moNavMenu, moNavButton);
1323                closeMenu(moTocMenu, moTocButton);
1324        });
1325    });
1326
1327    // 点击空白处关闭
1328    document.addEventListener("click", function (event) {
1329        let navButton = document.querySelector(".mo-nav-button");
1330        let tocButton = document.querySelector(".mo-toc-button");
1331
1332        if (
1333            moNavMenu.classList.contains("open") &&
1334            !moNavMenu.contains(event.target) &&
1335            !navButton.contains(event.target)
1336        ) {
1337            closeMenu(moNavMenu, navButton,false);
1338
1339            moNavMenu.removeEventListener("transitionend", panelOrderAnime);
1340            moTocMenu.removeEventListener("transitionend", panelOrderAnime);
1341            activeAllButton();
1342        }
1343
1344        if (
1345            moTocMenu.classList.contains("open") &&
1346            !moTocMenu.contains(event.target) &&
1347            !tocButton.contains(event.target)
1348        ) {
1349            closeMenu(moTocMenu, tocButton,false);
1350
1351            moNavMenu.removeEventListener("transitionend", panelOrderAnime);
1352            moTocMenu.removeEventListener("transitionend", panelOrderAnime);
1353            activeAllButton();
1354        }
1355
1356        if (
1357            !moNavMenu.contains(event.target) &&
1358            !moTocMenu.contains(event.target) &&
1359            !navButton.contains(event.target) &&
1360            !tocButton.contains(event.target)
1361        ) {
1362            moNavMenu.classList.remove("open");
1363            moTocMenu.classList.remove("open");
1364            navButton.classList.remove("open");
1365            tocButton.classList.remove("open");
1366            moHeader.removeEventListener("transitionend", navTransitionHandler);
1367            moNavMenu.removeEventListener("transitionend", panelTransitionHandler);
1368            moTocMenu.removeEventListener("transitionend", panelTransitionHandler);
1369            navTransitionHandler = null;
1370            panelTransitionHandler = null;
1371
1372            moNavMenu.removeEventListener("transitionend", panelOrderAnime);
1373            moTocMenu.removeEventListener("transitionend", panelOrderAnime);
1374            activeAllButton();
1375        }
1376
1377        // 关闭所有展开的二级菜单
1378        document.querySelectorAll(".sub-menu.open").forEach(function (subMenu) {
1379            if (!subMenu.contains(event.target)) {
1380                subMenu.classList.remove("open");
1381                let submenuToggle = subMenu.closest("li").querySelector(".open_submenu");
1382                if (submenuToggle) {
1383                    submenuToggle.classList.remove("open");
1384                }
1385            }
1386        });
1387    });
1388
1389    //自动收起搜索界面
1390    let moSearchInput = document.querySelector(".mo-menu-search .search-input");
1391    if (moSearchInput) {
1392        function moSearchClose () {
1393            moSearchInput.blur();
1394            closeMenu(moNavMenu, moNavButton);
1395        }
1396        moSearchInput.addEventListener("focus", function() {
1397            document.addEventListener('pjax:complete', moSearchClose);
1398        });
1399        moSearchInput.addEventListener("blur", function() {
1400            document.removeEventListener("pjax:complete", moSearchClose);
1401        });
1402    }
1403
1404    //下面是自动收起、展开导航栏部分
1405    let lastScrollTop = 0;
1406    // 阈值
1407    const scrollThreshold = document.documentElement.scrollHeight * 0.01;
1408    
1409    // 滚动恢复检测
1410    let isRestoringScroll = false;
1411    window.addEventListener('scroll', function restoreScrollCheck() {
1412        if (!isRestoringScroll && (window.pageYOffset > 0)) {
1413            isRestoringScroll = true;
1414            setTimeout(() => {
1415                if (window._searchWrapperState) {
1416                    window._searchWrapperState.handleScroll();
1417                    window._searchWrapperState.show(); // 强制重算宽度
1418                }
1419            }, 100);
1420            window.removeEventListener('scroll', restoreScrollCheck);
1421        }
1422    }, { passive: true });
1423
1424    window.addEventListener("scroll", function () {
1425
1426        if (window.innerWidth < 860) {
1427
1428            let moScrollTop = window.scrollY || document.documentElement.scrollTop;
1429
1430            if (moScrollTop > scrollThreshold) {
1431                if (moScrollTop > lastScrollTop) {
1432                    // 向下滚动
1433                    if (navTransitionHandler) {
1434                        moHeader.removeEventListener("transitionend", navTransitionHandler);
1435                        navTransitionHandler = null;
1436                    }
1437
1438                    if (panelTransitionHandler) {
1439                        moNavMenu.removeEventListener("transitionend", panelTransitionHandler);
1440                        moTocMenu.removeEventListener("transitionend", panelTransitionHandler);
1441                        panelTransitionHandler = null;
1442                    }
1443
1444                    moHeader.classList.add("mo-hide");
1445                    moHeader.classList.remove("bg");
1446                    moNavMenu.classList.remove("open");
1447                    moNavButton.classList.remove("open");
1448                    moTocMenu.classList.remove("open");
1449                    moTocButton.classList.remove("open");
1450
1451                    moNavMenu.removeEventListener("transitionend", panelOrderAnime); //移除动画监听并重新激活按钮
1452                    moTocMenu.removeEventListener("transitionend", panelOrderAnime);
1453                    activeAllButton();
1454
1455                    // 同时关闭所有展开的二级菜单
1456                    document.querySelectorAll(".mobile-nav .sub-menu.open").forEach(function (subMenu) {
1457                        subMenu.classList.remove("open");
1458                    });
1459                    document.querySelectorAll(".mobile-nav .open_submenu.open").forEach(function (toggle) {
1460                        toggle.classList.remove("open");
1461                    });
1462                } else {
1463                    // 向上滚动
1464                    moHeader.classList.remove("mo-hide");
1465                    moHeader.classList.add("bg");
1466                }
1467            } else {
1468                // 滚动距离小于阈值
1469                moHeader.classList.remove("mo-hide");
1470            }
1471            if (moScrollTop <= 0 && !isAnyPanelOpen()) {
1472                moHeader.classList.remove("bg");
1473            }
1474            
1475            lastScrollTop = moScrollTop;
1476        }
1477    });
1478    moHeader.classList.remove("bg");
1479});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.