HEX
Server: Apache
System: Linux server-674799.igrow.ws 5.14.0-611.27.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Feb 4 04:40:11 EST 2026 x86_64
User: elrashedytravel (1025)
PHP: 8.1.34
Disabled: exec,passthru,shell_exec,system
Upload Files
File: /home/elrashedytravel/public_html/wp-content/themes/custom-functions-1767952375/js/toc.js
// Table of Contents Generator
class TableOfContents {
    constructor() {
        this.tocContainer = document.querySelector('#table-of-contents.toc-container');
        this.tocList = document.getElementById('toc-list');
        this.entryContent = document.querySelector('.entry-content');
        this.tocToggle = document.querySelector('.toc-toggle');
        this.tocToggleIcon = document.querySelector('.toc-toggle-icon');
        this.headings = [];
        this.isCollapsed = false;
        
        this.init();
    }
    
    init() {
        if (!this.entryContent || !this.tocContainer) return;
        
        this.generateTOC();
        this.bindEvents();
        this.setupScrollSpy();
        
        // 如果沒有標題,隱藏整個TOC側邊欄
        if (this.headings.length === 0) {
            const tocSidebar = document.querySelector('.toc-sidebar');
            if (tocSidebar) {
                tocSidebar.style.display = 'none';
            }
        }
    }
    
    generateTOC() {
        // 查找所有標題
        this.headings = Array.from(this.entryContent.querySelectorAll('h1, h2, h3, h4, h5, h6'));
        
        if (this.headings.length === 0) return;
        
        // 為每個標題添加ID
        this.headings.forEach((heading, index) => {
            if (!heading.id) {
                heading.id = `heading-${index}`;
            }
        });
        
        // 生成TOC HTML
        const tocHTML = this.buildTOCHTML();
        this.tocList.innerHTML = tocHTML;
    }
    
    buildTOCHTML() {
        if (this.headings.length === 0) return '';
        
        // 構建層級結構
        const tocStructure = this.buildTOCStructure();
        return this.renderTOCStructure(tocStructure);
    }
    
    buildTOCStructure() {
        const structure = [];
        const stack = [{ level: 0, children: structure }];
        
        this.headings.forEach((heading, index) => {
            const level = parseInt(heading.tagName.charAt(1));
            const text = heading.textContent.trim();
            const id = heading.id;
            
            const item = {
                level,
                text,
                id,
                children: []
            };
            
            // 找到正確的父級
            while (stack.length > 1 && stack[stack.length - 1].level >= level) {
                stack.pop();
            }
            
            // 添加到當前層級
            stack[stack.length - 1].children.push(item);
            stack.push(item);
        });
        
        return structure;
    }
    
    renderTOCStructure(items) {
        if (!items || items.length === 0) return '';
        
        let html = '<ol class="toc-list">';
        
        items.forEach(item => {
            html += `
                <li class="toc-item toc-level-${item.level}">
                    <a href="#${item.id}" class="toc-link" data-heading-id="${item.id}">
                        <span class="toc-number"></span>
                        <span class="toc-text">${item.text}</span>
                    </a>
            `;
            
            // 遞歸渲染子項目
            if (item.children && item.children.length > 0) {
                html += this.renderTOCStructure(item.children);
            }
            
            html += '</li>';
        });
        
        html += '</ol>';
        return html;
    }
    
    bindEvents() {
        // TOC折疊切換
        if (this.tocToggle) {
            this.tocToggle.addEventListener('click', () => {
                this.toggleTOC();
            });
        }
        
        // 點擊TOC鏈接平滑滾動
        this.tocList.addEventListener('click', (e) => {
            if (e.target.closest('.toc-link')) {
                e.preventDefault();
                const link = e.target.closest('.toc-link');
                const targetId = link.getAttribute('data-heading-id');
                this.scrollToHeading(targetId);
            }
        });
    }
    
    toggleTOC() {
        this.isCollapsed = !this.isCollapsed;
        this.tocContainer.classList.toggle('toc-collapsed', this.isCollapsed);
        
        if (this.tocToggleIcon) {
            this.tocToggleIcon.textContent = this.isCollapsed ? '+' : '−';
        }
    }
    
    scrollToHeading(headingId) {
        const target = document.getElementById(headingId);
        if (target) {
            const offset = 80; // 頁面頂部偏移量
            const targetPosition = target.getBoundingClientRect().top + window.pageYOffset - offset;
            
            window.scrollTo({
                top: targetPosition,
                behavior: 'smooth'
            });
        }
    }
    
    setupScrollSpy() {
        // 創建Intersection Observer來監控標題
        const observerOptions = {
            rootMargin: '-80px 0px -60% 0px',
            threshold: 0
        };
        
        const observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                const id = entry.target.id;
                const tocLink = this.tocList.querySelector(`[data-heading-id="${id}"]`);
                
                if (tocLink) {
                    if (entry.isIntersecting) {
                        // 移除所有active類
                        this.tocList.querySelectorAll('.toc-link').forEach(link => {
                            link.classList.remove('active');
                        });
                        // 添加當前active類
                        tocLink.classList.add('active');
                        // 滾動TOC到可見區域
                        this.scrollTOCIntoView(tocLink);
                    }
                }
            });
        }, observerOptions);
        
        // 觀察所有標題
        this.headings.forEach(heading => {
            observer.observe(heading);
        });
    }
    
    scrollTOCIntoView(tocLink) {
        const tocNav = this.tocContainer.querySelector('.toc-nav');
        if (!tocNav || !tocLink) return;
        
        const tocNavRect = tocNav.getBoundingClientRect();
        const linkRect = tocLink.getBoundingClientRect();
        
        // 檢查鏈接是否在可見區域外
        if (linkRect.top < tocNavRect.top || linkRect.bottom > tocNavRect.bottom) {
            // 計算需要滾動的距離,讓active項目在TOC中央
            const scrollTop = tocNav.scrollTop + linkRect.top - tocNavRect.top - (tocNavRect.height / 2) + (linkRect.height / 2);
            
            tocNav.scrollTo({
                top: scrollTop,
                behavior: 'smooth'
            });
        }
    }
    
}

// 當DOM加載完成後初始化TOC
document.addEventListener('DOMContentLoaded', () => {
    // 檢查是否在單篇文章或頁面
    if (document.body.classList.contains('single') || 
        document.body.classList.contains('single-post') || 
        document.body.classList.contains('page')) {
        new TableOfContents();
    }
});