/** * DocumentLoader 모듈 * 문서/노트 로딩 및 네비게이션 관리 */ class DocumentLoader { constructor(api) { this.api = api; // 캐싱된 API 사용 (사용 가능한 경우) this.cachedApi = window.cachedApi || api; console.log('📄 DocumentLoader 초기화 완료 (캐싱 API 적용)'); } /** * 노트 로드 */ async loadNote(documentId) { try { console.log('📝 노트 로드 시작:', documentId); // 백엔드에서 노트 정보 가져오기 const noteDocument = await this.api.get(`/note-documents/${documentId}`); // 노트 제목 설정 document.title = `${noteDocument.title} - Document Server`; // 노트 내용을 HTML로 설정 const noteContentElement = document.getElementById('note-content'); if (noteContentElement && noteDocument.content) { noteContentElement.innerHTML = noteDocument.content; } else { // 폴백: document-content 사용 const contentElement = document.getElementById('document-content'); if (contentElement && noteDocument.content) { contentElement.innerHTML = noteDocument.content; } } console.log('📝 노트 로드 완료:', noteDocument.title); return noteDocument; } catch (error) { console.error('노트 로드 실패:', error); throw new Error('노트를 불러올 수 없습니다'); } } /** * 문서 로드 (실제 API 연동) */ async loadDocument(documentId) { try { // 백엔드에서 문서 정보 가져오기 (캐싱 적용) const docData = await this.cachedApi.get(`/documents/${documentId}`, { content_type: 'document' }, { category: 'document' }); // 페이지 제목 업데이트 document.title = `${docData.title} - Document Server`; // HTML 경로가 있는 경우에만 HTML 로드 if (docData.html_path) { // HTML 파일 경로 구성 (백엔드 서버를 통해 접근) const htmlPath = docData.html_path; const fileName = htmlPath.split('/').pop(); const response = await fetch(`http://localhost:24102/uploads/documents/${fileName}`); if (!response.ok) { throw new Error('문서 파일을 불러올 수 없습니다'); } const htmlContent = await response.text(); document.getElementById('document-content').innerHTML = htmlContent; // 문서 내 스크립트 오류 방지를 위한 전역 함수들 정의 this.setupDocumentScriptHandlers(); } else if (docData.pdf_path) { // HTML이 없고 PDF만 있는 경우 PDF 뷰어로 자동 전환 console.log('🔄 HTML이 없는 문서 - PDF 뷰어로 자동 전환'); const pdfUrl = `/api/documents/${documentId}/pdf?_token=${this.api.getToken()}`; // HTML 컨테이너 숨기기 const htmlContainer = document.getElementById('document-content'); if (htmlContainer) { htmlContainer.style.display = 'none'; } // PDF iframe 표시 const iframe = document.getElementById('pdf-iframe'); if (iframe) { iframe.src = pdfUrl; iframe.style.display = 'block'; iframe.style.width = '100%'; iframe.style.height = '100vh'; } } console.log('✅ 문서 로드 완료:', docData.title, docData.pdf_path ? '(PDF)' : '(HTML)'); return docData; } catch (error) { console.error('Document load error:', error); // HTML 로드 실패 시 PDF로 자동 전환 시도 if (docData && docData.pdf_path && !docData.html_path) { console.log('🔄 HTML이 없는 문서 - PDF 뷰어로 자동 전환'); // PDF 뷰어로 리다이렉트 const currentUrl = new URL(window.location); const pdfUrl = `/api/documents/${documentId}/pdf?_token=${this.api.getToken()}`; // iframe에서 PDF 직접 로드 const iframe = document.getElementById('pdf-iframe'); if (iframe) { iframe.src = pdfUrl; iframe.style.display = 'block'; } // HTML 컨테이너 숨기기 const htmlContainer = document.getElementById('document-content'); if (htmlContainer) { htmlContainer.style.display = 'none'; } return docData; } // 백엔드 연결 실패시 목업 데이터로 폴백 console.warn('Using fallback mock data'); const mockDocument = { id: documentId, title: 'Document Server 테스트 문서', description: '하이라이트와 메모 기능을 테스트하기 위한 샘플 문서입니다.', uploader_name: '관리자' }; // 기본 HTML 내용 표시 document.getElementById('document-content').innerHTML = `
이 문서는 Document Server의 하이라이트 및 메모 기능을 테스트하기 위한 샘플입니다.
텍스트를 선택하면 하이라이트를 추가할 수 있습니다.
이것은 하이라이트 테스트를 위한 긴 단락입니다. 이 텍스트를 선택하여 하이라이트를 만들어보세요. 하이라이트를 만든 후에는 메모를 추가할 수 있습니다. 메모는 나중에 검색하고 편집할 수 있습니다.
또 다른 단락입니다. 여러 개의 하이라이트를 만들어서 메모 기능을 테스트해보세요. 각 하이라이트는 고유한 색상을 가질 수 있으며, 연결된 메모를 통해 중요한 정보를 기록할 수 있습니다.
`; // 폴백 모드에서도 스크립트 핸들러 설정 this.setupDocumentScriptHandlers(); return mockDocument; } } /** * 네비게이션 정보 로드 */ async loadNavigation(documentId) { try { // CachedAPI의 getDocumentNavigation 메서드 사용 const navigation = await this.api.getDocumentNavigation(documentId); console.log('📍 네비게이션 정보 로드됨:', navigation); return navigation; } catch (error) { console.error('❌ 네비게이션 정보 로드 실패:', error); return null; } } /** * URL 파라미터에서 특정 텍스트 하이라이트 확인 */ checkForTextHighlight() { const urlParams = new URLSearchParams(window.location.search); const highlightText = urlParams.get('highlight_text'); const startOffset = parseInt(urlParams.get('start_offset')); const endOffset = parseInt(urlParams.get('end_offset')); if (highlightText && !isNaN(startOffset) && !isNaN(endOffset)) { console.log('🎯 URL에서 하이라이트 요청:', { highlightText, startOffset, endOffset }); // 임시 하이라이트 적용 및 스크롤 setTimeout(() => { this.highlightAndScrollToText({ targetText: highlightText, startOffset: startOffset, endOffset: endOffset }); }, 500); // DOM 로딩 완료 후 실행 } } /** * 문서 내 스크립트 핸들러 설정 */ setupDocumentScriptHandlers() { // 업로드된 HTML 문서에서 사용할 수 있는 전역 함수들 정의 // 언어 토글 함수 (많은 문서에서 사용) window.toggleLanguage = function() { const koreanContent = document.getElementById('korean-content'); const englishContent = document.getElementById('english-content'); if (koreanContent && englishContent) { if (koreanContent.style.display === 'none') { koreanContent.style.display = 'block'; englishContent.style.display = 'none'; } else { koreanContent.style.display = 'none'; englishContent.style.display = 'block'; } } else { // 다른 언어 토글 방식들 const elements = document.querySelectorAll('[data-lang]'); elements.forEach(el => { if (el.dataset.lang === 'ko') { el.style.display = el.style.display === 'none' ? 'block' : 'none'; } else if (el.dataset.lang === 'en') { el.style.display = el.style.display === 'none' ? 'block' : 'none'; } }); } }; // 문서 인쇄 함수 window.printDocument = function() { // 현재 페이지의 헤더/푸터 숨기고 문서 내용만 인쇄 const originalTitle = document.title; const printContent = document.getElementById('document-content'); if (printContent) { const printWindow = window.open('', '_blank'); printWindow.document.write(`