ℹ️ NOTE 重點資訊與備註
💡 TIP 實用技巧與建議
💬 IMPORTANT 關鍵要求與步驟
⚠️ WARNING 警示與注意事項
🛑 CAUTION 危險與潛在風險
\n\n'; var blob = new Blob([singleHtml], { type: 'text/html;charset=utf-8' }); var a = document.createElement('a'); a.href = URL.createObjectURL(blob); var cleanName = (bookData.title || 'book').replace(new RegExp('[^a-zA-Z0-9\\u4e00-\\u9fa5_-]', 'g'), '_'); a.download = cleanName + '.html'; a.click(); URL.revokeObjectURL(a.href); if (typeof showToast === 'function') { showToast(isZh ? '🌐 已成功匯出單一離線 HTML 電子書!' : '🌐 Standalone HTML eBook exported!'); } } async function exportPdfPrint() { if (exportMenu) exportMenu.classList.remove('show'); if (typeof showToast === 'function') { showToast(isZh ? '⏳ 正在準備列印排版...' : '⏳ Preparing Print Layout...'); } var printContainer = document.getElementById('book-print-container'); if (!printContainer) { printContainer = document.createElement('div'); printContainer.id = 'book-print-container'; document.body.appendChild(printContainer); } var htmlParts = [ '
' + '

' + bookData.title + '

' + '

' + (isZh ? 'David888 Wiki 電子書手冊' : 'David888 Wiki Book Edition') + '

' + '
' ]; for (var i = 0; i < bookData.chapters.length; i++) { var ch = bookData.chapters[i]; var chUrl = (ch.url || '').replace(new RegExp('\\/book\\/?(\\?.*)?$'), '$1').replace(new RegExp('\\/book\\/?$'), ''); var chHtml = ''; try { var embedUrl = chUrl + (chUrl.includes('?') ? '&' : '?') + 'embed=1'; var resp = await fetch(embedUrl); if (resp.ok) { var htmlText = await resp.text(); var parser = new DOMParser(); var doc = parser.parseFromString(htmlText, 'text/html'); var bodyEl = doc.querySelector('.markdown-body') || doc.body; chHtml = bodyEl ? bodyEl.innerHTML : htmlText; } } catch(e) {} if (!chHtml) chHtml = '

' + ch.title + '

'; htmlParts.push('

' + ch.title + '

' + chHtml + '
'); } printContainer.innerHTML = htmlParts.join(''); window.print(); } function loadChapter(index) { var iframe = document.getElementById('book-content-iframe'); var crumb = document.getElementById('book-crumb-current'); var openTabBtn = document.getElementById('book-open-tab-btn'); var prevBtn = document.getElementById('book-prev-btn'); var nextBtn = document.getElementById('book-next-btn'); if (index === -1) { currentChapterIndex = -1; container.querySelectorAll('.book-toc-item').forEach(function(el) { el.classList.remove('active'); }); if (crumb) crumb.textContent = isZh ? '目錄總覽' : 'Table of Contents'; var homeUrl = (APP_STATE.sharePath || window.location.pathname.replace(new RegExp('\\/book\\/?$'), '') || '/'); if (openTabBtn) openTabBtn.href = homeUrl; if (iframe) iframe.src = homeUrl + (homeUrl.includes('?') ? '&' : '?') + 'embed=1'; if (prevBtn) prevBtn.disabled = true; if (nextBtn) nextBtn.disabled = bookData.chapters.length === 0; if (sidebar) sidebar.classList.remove('open'); return; } if (index < 0 || index >= bookData.chapters.length) return; currentChapterIndex = index; try { localStorage.setItem(readingProgressKey, index); } catch(e) {} var chapter = bookData.chapters[index]; container.querySelectorAll('.book-toc-item').forEach(function(el) { el.classList.toggle('active', parseInt(el.getAttribute('data-chapter-index'), 10) === index); }); if (crumb) crumb.textContent = chapter.title; if (openTabBtn) openTabBtn.href = chapter.url; if (prevBtn) prevBtn.disabled = index === 0; if (nextBtn) nextBtn.disabled = index >= bookData.chapters.length - 1; if (sidebar) sidebar.classList.remove('open'); if (iframe) { var targetUrl = (chapter.url || '').replace(new RegExp('\\/book\\/?(\\?.*)?$'), '$1').replace(new RegExp('\\/book\\/?$'), ''); if (!targetUrl) targetUrl = '/'; if (!chapter.isExternal && !targetUrl.includes('embed=1')) { targetUrl += (targetUrl.includes('?') ? '&' : '?') + 'embed=1'; } iframe.src = targetUrl; } } container.addEventListener('click', function(e) { var exitBtn = e.target.closest('#book-exit-btn'); if (exitBtn) { e.preventDefault(); e.stopPropagation(); document.body.classList.remove('book-mode-active'); if (container) container.remove(); window.location.href = exitUrl; return; } var homeLink = e.target.closest('#book-toc-home-link, #book-crumb-parent'); if (homeLink) { e.preventDefault(); loadChapter(-1); return; } var offlineAction = e.target.closest('#book-offline-cache-btn'); if (offlineAction) { e.preventDefault(); cacheEntireBookOffline(); return; } var mdBtn = e.target.closest('#book-export-md-btn'); if (mdBtn) { e.preventDefault(); exportCombinedMarkdown(); return; } var htmlBtn = e.target.closest('#book-export-html-btn'); if (htmlBtn) { e.preventDefault(); exportStandaloneHtml(); return; } var pdfBtn = e.target.closest('#book-export-pdf-btn'); if (pdfBtn) { e.preventDefault(); exportPdfPrint(); return; } var link = e.target.closest('.book-toc-link'); if (link) { var chIdx = parseInt(link.getAttribute('data-chapter-index'), 10); if (!isNaN(chIdx)) { e.preventDefault(); loadChapter(chIdx); return; } } var prevBtn = e.target.closest('#book-prev-btn'); if (prevBtn) { e.preventDefault(); if (currentChapterIndex > 0) loadChapter(currentChapterIndex - 1); else if (currentChapterIndex === 0) loadChapter(-1); return; } var nextBtn = e.target.closest('#book-next-btn'); if (nextBtn) { e.preventDefault(); if (currentChapterIndex < bookData.chapters.length - 1) loadChapter(currentChapterIndex + 1); return; } }); document.addEventListener('keydown', function(e) { if (!document.body.classList.contains('book-mode-active')) return; if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return; if (e.key === '[') { if (currentChapterIndex > 0) loadChapter(currentChapterIndex - 1); else if (currentChapterIndex === 0) loadChapter(-1); } else if (e.key === ']') { if (currentChapterIndex < bookData.chapters.length - 1) loadChapter(currentChapterIndex + 1); } }); // Mobile touch swipe gestures for flip chapter var touchStartX = 0; var touchStartY = 0; container.addEventListener('touchstart', function(e) { if (e.touches.length === 1) { touchStartX = e.touches[0].clientX; touchStartY = e.touches[0].clientY; } }, { passive: true }); container.addEventListener('touchend', function(e) { if (e.changedTouches.length === 1) { var deltaX = e.changedTouches[0].clientX - touchStartX; var deltaY = e.changedTouches[0].clientY - touchStartY; if (Math.abs(deltaX) > 70 && Math.abs(deltaY) < 50) { if (deltaX < 0 && currentChapterIndex < bookData.chapters.length - 1) { loadChapter(currentChapterIndex + 1); } else if (deltaX > 0 && currentChapterIndex > 0) { loadChapter(currentChapterIndex - 1); } } } }, { passive: true }); var initialIdx = 0; try { var savedIdx = parseInt(localStorage.getItem(readingProgressKey), 10); if (!isNaN(savedIdx) && savedIdx >= 0 && savedIdx < bookData.chapters.length) { initialIdx = savedIdx; } } catch(e) {} loadChapter(initialIdx); }; function maybeAutoStart() { if (window.self !== window.top || (typeof APP_STATE !== 'undefined' && (APP_STATE.isEmbed || APP_STATE.embed))) { return; } if (APP_STATE.autoBook) { window.initBookMode(); return; } if (!APP_STATE.autoPresent || _autoStarted) return; _autoStarted = true; window.initPresentation(); } // Bind present and exit buttons via event delegation document.addEventListener('click', function(e) { var exitBtn = e.target.closest('#ptb-exit, #presentation-close-btn, .presentation-tb-exit'); if (exitBtn) { e.preventDefault(); e.stopPropagation(); window.exitPresentation(); return; } var btn = e.target.closest('#present-btn, .publication-present-btn'); if (btn) { e.preventDefault(); window.initPresentation(); } var bookBtn = e.target.closest('#editor-book-btn, .publication-book-btn'); if (bookBtn) { e.preventDefault(); window.initBookMode(); } }); maybeAutoStart(); document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && document.getElementById('presentation-container') && document.getElementById('presentation-container').classList.contains('active')) { window.exitPresentation(); } }); })();