- 메인 컨테이너 padding-top을 pt-4에서 pt-20으로 증가 - 드롭다운 z-index를 z-[60]으로 설정하여 헤더보다 높은 우선순위 부여 - 스토리 선택 드롭다운이 정상적으로 작동하도록 수정 - 디버깅용 코드 정리
262 lines
11 KiB
JavaScript
262 lines
11 KiB
JavaScript
/**
|
|
* 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`;
|
|
|
|
// PDF 문서가 아닌 경우에만 HTML 로드
|
|
if (!docData.pdf_path && 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();
|
|
}
|
|
|
|
console.log('✅ 문서 로드 완료:', docData.title, docData.pdf_path ? '(PDF)' : '(HTML)');
|
|
return docData;
|
|
|
|
} catch (error) {
|
|
console.error('Document load error:', error);
|
|
|
|
// 백엔드 연결 실패시 목업 데이터로 폴백
|
|
console.warn('Using fallback mock data');
|
|
const mockDocument = {
|
|
id: documentId,
|
|
title: 'Document Server 테스트 문서',
|
|
description: '하이라이트와 메모 기능을 테스트하기 위한 샘플 문서입니다.',
|
|
uploader_name: '관리자'
|
|
};
|
|
|
|
// 기본 HTML 내용 표시
|
|
document.getElementById('document-content').innerHTML = `
|
|
<h1>테스트 문서</h1>
|
|
<p>이 문서는 Document Server의 하이라이트 및 메모 기능을 테스트하기 위한 샘플입니다.</p>
|
|
<p>텍스트를 선택하면 하이라이트를 추가할 수 있습니다.</p>
|
|
<h2>주요 기능</h2>
|
|
<ul>
|
|
<li>텍스트 선택 후 하이라이트 생성</li>
|
|
<li>하이라이트에 메모 추가</li>
|
|
<li>메모 검색 및 관리</li>
|
|
<li>책갈피 기능</li>
|
|
</ul>
|
|
<h2>테스트 단락</h2>
|
|
<p>이것은 하이라이트 테스트를 위한 긴 단락입니다. 이 텍스트를 선택하여 하이라이트를 만들어보세요.
|
|
하이라이트를 만든 후에는 메모를 추가할 수 있습니다. 메모는 나중에 검색하고 편집할 수 있습니다.</p>
|
|
<p>또 다른 단락입니다. 여러 개의 하이라이트를 만들어서 메모 기능을 테스트해보세요.
|
|
각 하이라이트는 고유한 색상을 가질 수 있으며, 연결된 메모를 통해 중요한 정보를 기록할 수 있습니다.</p>
|
|
`;
|
|
|
|
// 폴백 모드에서도 스크립트 핸들러 설정
|
|
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(`
|
|
<html>
|
|
<head>
|
|
<title>${originalTitle}</title>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
@media print { body { margin: 0; } }
|
|
</style>
|
|
</head>
|
|
<body>${printContent.innerHTML}</body>
|
|
</html>
|
|
`);
|
|
printWindow.document.close();
|
|
printWindow.print();
|
|
} else {
|
|
window.print();
|
|
}
|
|
};
|
|
|
|
// 링크 처리 함수 (문서 내 링크가 새 탭에서 열리지 않도록)
|
|
document.addEventListener('click', function(e) {
|
|
const link = e.target.closest('a');
|
|
if (link && link.href && !link.href.startsWith('#')) {
|
|
// 외부 링크는 새 탭에서 열기
|
|
if (!link.href.includes(window.location.hostname)) {
|
|
e.preventDefault();
|
|
window.open(link.href, '_blank');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 텍스트 하이라이트 및 스크롤 (임시 하이라이트)
|
|
* 이 함수는 나중에 HighlightManager로 이동될 예정
|
|
*/
|
|
highlightAndScrollToText({ targetText, startOffset, endOffset }) {
|
|
// 임시 구현 - ViewerCore의 highlightAndScrollToText 호출
|
|
if (window.documentViewerInstance && window.documentViewerInstance.highlightAndScrollToText) {
|
|
window.documentViewerInstance.highlightAndScrollToText(targetText, startOffset, endOffset);
|
|
} else {
|
|
// 폴백: 간단한 스크롤만
|
|
console.log('🎯 텍스트 하이라이트 요청 (폴백):', { targetText, startOffset, endOffset });
|
|
|
|
const documentContent = document.getElementById('document-content');
|
|
if (!documentContent) return;
|
|
|
|
const textContent = documentContent.textContent;
|
|
const targetIndex = textContent.indexOf(targetText);
|
|
|
|
if (targetIndex !== -1) {
|
|
const scrollRatio = targetIndex / textContent.length;
|
|
const scrollPosition = documentContent.scrollHeight * scrollRatio;
|
|
|
|
window.scrollTo({
|
|
top: scrollPosition,
|
|
behavior: 'smooth'
|
|
});
|
|
|
|
console.log('✅ 텍스트로 스크롤 완료 (폴백)');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 전역으로 내보내기
|
|
window.DocumentLoader = DocumentLoader;
|