847 lines
34 KiB
JavaScript
847 lines
34 KiB
JavaScript
//형태는 ?path=fullPath(ex.http://gsim.hanmac..../1.jpg)&data=eadNadfl
|
|
//data는 json({key:value})을 stringify한 후에 btoa로 base64로 변경해서 달기.
|
|
//ex)
|
|
//const jsonData = JSON.stringify({ key1: "value1", key2: "value2" });
|
|
//const encodedData = btoa(jsonData);
|
|
//?path=${fullPath}&data=${encodedData}
|
|
//넘어간 data는 모두 표출됨, 숨길 data는 key 앞에 $붙여주기(type, password는 그냥써도 무조건 표출X)
|
|
|
|
//받는 형태는 pdf, img(png, jpg, jpeg, panorama), mp4, gsim(gsimViewer로 redirect), ifc, 미지원 6가지
|
|
const searchParam = new URLSearchParams(window.location.search);
|
|
//serchParam 비워주기 - debug off
|
|
// window.history.replaceState(null, null, '/')
|
|
|
|
const fullPath = searchParam.get('path');
|
|
let ext = fullPath.toLowerCase().split('.').pop();//가장 마지막.이후 확장자
|
|
|
|
const dataA = searchParam.get('data');
|
|
// title filename으로 변경
|
|
document.title = fullPath.split('/').pop();
|
|
let data = (dataA)?JSON.parse((dataA)?decodeURIComponent(escape(atob(dataA))):'{}'):undefined;
|
|
//data로 ext 넘어오면 무조건 변경
|
|
ext = data.$ext;
|
|
|
|
|
|
// 🚩3d모델뷰어 썸네일 생성을 위해 아래의 변수 추가
|
|
const thumbnail_key = decodeURIComponent(searchParam.get('thumbnail_key'));
|
|
const path_name = searchParam.get('path_name');
|
|
const resourcePath = decodeURIComponent(searchParam.get('resourcePath'));
|
|
const dataId = searchParam.get('dataId');
|
|
|
|
if(data && Object.keys(data).length>0 && (data.$type == 'text'|| data.type == 'text')){
|
|
//type text로 넘어왔을때 무조건 text뷰어로 연결
|
|
_openText(fullPath);
|
|
}else{
|
|
switch(ext){
|
|
case 'pdf':
|
|
_openPdf(fullPath,data);
|
|
break;
|
|
case 'xls':
|
|
case 'xlsx':
|
|
case 'xlsm':
|
|
_openExcel(fullPath, data);
|
|
break;
|
|
case 'docx':
|
|
_openDocx(fullPath, data);
|
|
break;
|
|
case 'hwp':
|
|
case 'hwpx':
|
|
_openHwp(fullPath, data);
|
|
break;
|
|
case 'mp4':
|
|
case 'mov':
|
|
case 'webm':
|
|
_openVideo(fullPath);
|
|
break;
|
|
case 'png':
|
|
case 'jpg':
|
|
case 'jpeg':
|
|
case 'webp':
|
|
case 'gif':
|
|
if(data && Object.keys(data).length>0 && (data.$type == 'panorama'||data.type == 'panorama')){
|
|
// data 있으면서 type이 panorama인 경우
|
|
_openPano(fullPath);
|
|
}else{
|
|
_openImage(fullPath);
|
|
}
|
|
break;
|
|
case 'gsim':
|
|
// gsim viewer 주소로 변경 필요
|
|
window.location.href = `/libs/gsimViewer/gsimViewer.html?${searchParam.toString()}`;
|
|
break;
|
|
case 'ifc':
|
|
_openIfc(fullPath, thumbnail_key, resourcePath, dataId, path_name);
|
|
break;
|
|
case 'txt':
|
|
case 'log':
|
|
case 'md':
|
|
_openText(fullPath);
|
|
break;
|
|
case 'url':
|
|
_openUrl(fullPath);
|
|
break;
|
|
case 'zip':
|
|
_openZip(fullPath);
|
|
break;
|
|
case 'glb':
|
|
case 'gltf':
|
|
case 'obj':
|
|
case 'stl':
|
|
case 'fbx':
|
|
case '3dm':
|
|
_open3d(fullPath, thumbnail_key, resourcePath, dataId, path_name);
|
|
break;
|
|
case 'html':
|
|
_openHtml(fullPath);
|
|
break;
|
|
}
|
|
if(data && Object.keys(data).length>0){
|
|
let keys = Object.keys(data).filter(key=>key.indexOf('$') != 0);
|
|
if(keys.length > 0){
|
|
_drawMeta(data);
|
|
}
|
|
}
|
|
}
|
|
|
|
function _openText(path){
|
|
// ext를 매개변수로 받아도 md파일이 txt로 넘어오기때문에 경로에서 직접 ext 추출
|
|
let ext = path.split('.').pop();
|
|
ext = ext.split('%')[0];
|
|
fetch(path).then(res => res.text()).then(data=>{
|
|
let pre;
|
|
// md 파일 확장자 일때 파싱 후 HTML 삽입
|
|
if(ext === 'md'){
|
|
pre = document.createElement('div');
|
|
pre.classList.add('markdown-wrap');
|
|
// mermaid 시퀀스 다이어 그램 변환
|
|
const renderer = new marked.Renderer();
|
|
const originalCode = renderer.code.bind(renderer);
|
|
renderer.code = (code) => {
|
|
if(code.lang === 'mermaid') return `<pre class="mermaid">${code.text}</pre>`
|
|
return originalCode(code);
|
|
}
|
|
marked.setOptions({ gfm: true, breaks: true, renderer});
|
|
const div = document.createElement('div');
|
|
div.classList.add('markdown-body');
|
|
div.innerHTML = marked.parse(data);
|
|
// mermaid 초기화 및 생성
|
|
const mermaidEls = div.querySelectorAll('.mermaid');
|
|
if(mermaidEls.length > 0){
|
|
mermaid.initialize({ startOnLoad: false });
|
|
mermaid.init(undefined, mermaidEls);
|
|
}
|
|
pre.appendChild(div);
|
|
} else {
|
|
pre = document.createElement('pre');
|
|
pre.style.width = '100%';
|
|
pre.style.height = '100%';
|
|
pre.style.objectFit = 'contain';
|
|
pre.textContent = data;
|
|
}
|
|
document.getElementById('popup_viewer').appendChild(pre);
|
|
if(ext === 'md')hljs.highlightAll();
|
|
}).catch(err=>console.error('파일 로드 실패', err));
|
|
}
|
|
|
|
function _openZip(path){
|
|
fetch(path).then(async data=>{
|
|
let zblob = await data.blob();
|
|
|
|
const zip = new JSZip();
|
|
await zip.loadAsync(zblob);
|
|
let folderText = ``;
|
|
let fileText = ``;
|
|
|
|
zip.forEach((relativePath, zipEntry) => {
|
|
let slashIdx = relativePath.indexOf('/');
|
|
if(slashIdx == -1 || slashIdx == relativePath.length -1){
|
|
if(zipEntry.dir){
|
|
folderText += `(폴더) ${zipEntry.name.split('/')[0]} \n`;
|
|
}else{
|
|
fileText += `(파일) ${zipEntry.name} \n`
|
|
}
|
|
}
|
|
});
|
|
|
|
const pre = document.createElement('pre');
|
|
pre.style.width = '100%';
|
|
pre.style.height = '100%';
|
|
pre.style.objectFit = 'contain';
|
|
pre.textContent = `${folderText}${(folderText == ``)?'':'\n'}${fileText}`;
|
|
document.getElementById('popup_viewer').appendChild(pre);
|
|
})
|
|
}
|
|
|
|
function _openUrl(path){
|
|
fetch(path).then(res => res.text()).then(data=>{
|
|
let url = data.split('URL=')[1];
|
|
let iframe = document.createElement('iframe');
|
|
iframe.src = url;
|
|
iframe.style.width = '100%'; // 컨테이너에 맞게 너비 설정
|
|
iframe.style.height = '100%'; // 컨테이너에 맞게 높이 설정
|
|
iframe.style.border = 'none'; // 테두리 제거 (선택 사항)
|
|
|
|
document.getElementById('popup_viewer').appendChild(iframe);
|
|
}).catch(err=>console.error('파일 로드 실패', err));
|
|
}
|
|
|
|
function _openHtml(path){
|
|
fetch(path).then(res => res.text()).then(data=>{
|
|
let iframe = document.createElement('iframe');
|
|
iframe.srcdoc = data;
|
|
iframe.style.width = '100%'; // 컨테이너에 맞게 너비 설정
|
|
iframe.style.height = '100%'; // 컨테이너에 맞게 높이 설정
|
|
iframe.style.border = 'none'; // 테두리 제거 (선택 사항)
|
|
|
|
document.getElementById('popup_viewer').appendChild(iframe);
|
|
}).catch(err=>console.error('파일 로드 실패', err));
|
|
}
|
|
|
|
// function _openPdf(path,data){
|
|
// let pdf_options = {
|
|
// url: path,
|
|
// initialPage: 1,
|
|
// };
|
|
// if(data && Object.keys(data).length > 0 && (data.$password || data.password)){
|
|
// pdf_options.password = (data.$password)?data.$password:data.password;
|
|
// }
|
|
|
|
// let iframe = document.createElement('iframe');
|
|
// iframe.src = `/libs/pdfViewer/web/viewer.html`;
|
|
// document.getElementById('popup_viewer').appendChild(iframe);
|
|
// iframe.addEventListener('load', () => {
|
|
// // pdf 실행 시 무조건 1페이지부터 보이도록 기존 pdf 히스토리 삭제
|
|
// try {
|
|
// let appWin = iframe.contentWindow;
|
|
// // PDF.js의 기본 히스토리 키
|
|
// appWin.localStorage.removeItem('pdfjs.history');
|
|
// // 또는 여러 키 삭제
|
|
// Object.keys(appWin.localStorage).forEach(k => {
|
|
// if (k.startsWith('pdfjs.history') || k.startsWith('pdfjs.preferences')) {
|
|
// appWin.localStorage.removeItem(k);
|
|
// }
|
|
// });
|
|
// } catch (e) { /* ignore */ }
|
|
|
|
// let app = document.querySelector('#popup_viewer iframe').contentWindow.PDFViewerApplication;
|
|
// app.pdfCursorTools._handTool.activate();
|
|
|
|
// app.open(pdf_options);
|
|
|
|
// iframe.width = '100%';
|
|
// iframe.height = '100%';
|
|
// });
|
|
// }
|
|
|
|
function _openPdf(path, data) {
|
|
let pdf_options = {
|
|
url: path,
|
|
initialPage: 1,
|
|
};
|
|
|
|
if (data && Object.keys(data).length > 0 && (data.$password || data.password)) {
|
|
pdf_options.password = (data.$password) ? data.$password : data.password;
|
|
}
|
|
|
|
let iframe = document.createElement('iframe');
|
|
iframe.src = `/libs/pdfViewer/web/viewer.html`;
|
|
document.getElementById('popup_viewer').appendChild(iframe);
|
|
|
|
iframe.addEventListener('load', () => {
|
|
try {
|
|
let appWin = iframe.contentWindow;
|
|
|
|
// 히스토리 삭제
|
|
appWin.localStorage.removeItem('pdfjs.history');
|
|
Object.keys(appWin.localStorage).forEach(k => {
|
|
if (k.startsWith('pdfjs.history') || k.startsWith('pdfjs.preferences')) {
|
|
appWin.localStorage.removeItem(k);
|
|
}
|
|
});
|
|
|
|
let app = appWin.PDFViewerApplication;
|
|
let isRendering = false;
|
|
let lastScale = 1;
|
|
|
|
app.initializedPromise.then(() => {
|
|
const PDFPageView = appWin.PDFPageView;
|
|
|
|
// draw 함수를 완전히 오버라이드
|
|
const originalDraw = PDFPageView.prototype.draw;
|
|
PDFPageView.prototype.draw = function() {
|
|
// 고해상도 설정
|
|
this.outputScale = {
|
|
sx: (window.devicePixelRatio || 1) * 2,
|
|
sy: (window.devicePixelRatio || 1) * 2
|
|
};
|
|
return originalDraw.call(this);
|
|
};
|
|
|
|
// reset 함수도 오버라이드하여 캔버스 완전 삭제
|
|
const originalReset = PDFPageView.prototype.reset;
|
|
PDFPageView.prototype.reset = function() {
|
|
// 캔버스 완전히 제거
|
|
if (this.canvas) {
|
|
if (this.canvas.parentNode) {
|
|
this.canvas.parentNode.removeChild(this.canvas);
|
|
}
|
|
this.canvas.width = 0;
|
|
this.canvas.height = 0;
|
|
this.canvas = null;
|
|
}
|
|
|
|
// 렌더 태스크 취소
|
|
if (this.renderTask) {
|
|
this.renderTask.cancel();
|
|
this.renderTask = null;
|
|
}
|
|
|
|
return originalReset.call(this);
|
|
};
|
|
|
|
if (app.pdfViewer) {
|
|
app.pdfViewer.textLayerMode = 1;
|
|
app.pdfViewer.useOnlyCssZoom = false;
|
|
}
|
|
|
|
// 확대 시 완전 재렌더링
|
|
app.eventBus.on('scalechanging', function(evt) {
|
|
if (isRendering) return;
|
|
|
|
const newScale = evt.scale;
|
|
const scaleDiff = Math.abs(newScale - lastScale);
|
|
|
|
// 스케일 변화가 있을 때만 재렌더링
|
|
if (scaleDiff > 0.01) {
|
|
isRendering = true;
|
|
|
|
setTimeout(() => {
|
|
if (app.pdfViewer && app.pdfViewer._pages) {
|
|
app.pdfViewer._pages.forEach(pageView => {
|
|
// 1. 렌더 태스크 강제 취소
|
|
if (pageView.renderTask) {
|
|
pageView.renderTask.cancel();
|
|
pageView.renderTask = null;
|
|
}
|
|
|
|
// 2. 캔버스 완전 제거
|
|
if (pageView.canvas) {
|
|
const parent = pageView.canvas.parentNode;
|
|
if (parent) {
|
|
parent.removeChild(pageView.canvas);
|
|
}
|
|
pageView.canvas.width = 0;
|
|
pageView.canvas.height = 0;
|
|
pageView.canvas = null;
|
|
}
|
|
|
|
// 3. viewport 업데이트
|
|
if (pageView.pdfPage) {
|
|
pageView.viewport = pageView.pdfPage.getViewport({
|
|
scale: newScale,
|
|
rotation: pageView.rotation || 0
|
|
});
|
|
}
|
|
|
|
// 4. 페이지 완전 리셋
|
|
pageView.reset();
|
|
|
|
// 5. 새로 그리기
|
|
pageView.draw();
|
|
});
|
|
}
|
|
|
|
lastScale = newScale;
|
|
isRendering = false;
|
|
}, 200); // 딜레이 증가
|
|
}
|
|
});
|
|
|
|
// 스크롤/페이지 변경 시에도 재렌더링
|
|
app.eventBus.on('updateviewarea', function(evt) {
|
|
if (isRendering) return;
|
|
|
|
// 현재 보이는 페이지만 재렌더링
|
|
const visiblePages = app.pdfViewer._getVisiblePages();
|
|
if (visiblePages && visiblePages.views) {
|
|
visiblePages.views.forEach(view => {
|
|
if (view && view.div && view.div.classList.contains('pdfPage')) {
|
|
const pageView = app.pdfViewer._pages[view.id - 1];
|
|
if (pageView && !pageView.renderTask) {
|
|
pageView.draw();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
app.pdfCursorTools._handTool.activate();
|
|
app.open(pdf_options);
|
|
|
|
} catch (e) {
|
|
console.error('PDF 설정 오류:', e);
|
|
}
|
|
|
|
iframe.width = '100%';
|
|
iframe.height = '100%';
|
|
});
|
|
}
|
|
|
|
function _openVideo(path){
|
|
const popupVideo = document.createElement('video');
|
|
popupVideo.autoplay = true;
|
|
popupVideo.muted = true;
|
|
popupVideo.playsInline = true;
|
|
popupVideo.controls = true;
|
|
popupVideo.crossOrigin = 'anonymous';
|
|
|
|
const sourceElement = document.createElement('source');
|
|
sourceElement.src = path;
|
|
|
|
popupVideo.style.width = '100%';
|
|
popupVideo.style.height = '100%';
|
|
popupVideo.style.objectFit = 'contain';
|
|
|
|
document.getElementById('popup_viewer').appendChild(popupVideo);
|
|
popupVideo.appendChild(sourceElement);
|
|
}
|
|
|
|
function _openPano(path){
|
|
let panorama = pannellum.viewer('popup_viewer',{
|
|
"type": "equirectangular",
|
|
"panorama": path,
|
|
"autoLoad": true,
|
|
"compass":false,
|
|
})
|
|
}
|
|
|
|
function _openImage(path){
|
|
const img = document.createElement('img');
|
|
img.src = path;
|
|
document.getElementById('popup_viewer').appendChild(img);
|
|
img.style.width = '100%';
|
|
img.style.height = '100%';
|
|
img.style.objectFit = 'contain';
|
|
|
|
img.addEventListener('click',()=>{
|
|
document.getElementById('large-img-container').style.display = 'block';
|
|
centerImage();
|
|
})
|
|
|
|
const container = document.getElementById('large-img-container');
|
|
const image = document.getElementById('large-image');
|
|
const zoomInfo = document.getElementById('large-img-zoomInfo');
|
|
image.src = path;
|
|
|
|
let isDragging = false;
|
|
let startX, startY;
|
|
let translateX = 0;
|
|
let translateY = 0;
|
|
let scale = 1;
|
|
|
|
// 드래그 시작
|
|
function dragStart(e) {
|
|
if (e.target !== image) return;
|
|
|
|
isDragging = true;
|
|
startX = e.clientX - translateX;
|
|
startY = e.clientY - translateY;
|
|
|
|
// 드래그 중일 때 transition 제거
|
|
image.style.transition = 'none';
|
|
}
|
|
|
|
// 드래그 중
|
|
function drag(e) {
|
|
if (!isDragging) return;
|
|
|
|
e.preventDefault();
|
|
|
|
// 현재 마우스 위치에서 시작 위치를 빼서 이동 거리 계산
|
|
translateX = e.clientX - startX;
|
|
translateY = e.clientY - startY;
|
|
|
|
updateImageTransform();
|
|
}
|
|
|
|
// 드래그 종료
|
|
function dragEnd() {
|
|
if (!isDragging) return;
|
|
|
|
isDragging = false;
|
|
// 드래그 종료 시 부드러운 transition 효과 복원
|
|
image.style.transition = 'transform 0.1s ease-out';
|
|
}
|
|
|
|
// 확대/축소
|
|
function zoom(e) {
|
|
e.preventDefault();
|
|
|
|
// 현재 이미지의 실제 위치와 크기
|
|
const imageRect = image.getBoundingClientRect();
|
|
|
|
// 마우스 포인터의 화면상 좌표
|
|
const mouseX = e.clientX;
|
|
const mouseY = e.clientY;
|
|
|
|
// 마우스 포인터의 이미지상 상대 좌표
|
|
const mouseImageX = mouseX - imageRect.left;
|
|
const mouseImageY = mouseY - imageRect.top;
|
|
|
|
// 이전 스케일 저장
|
|
const prevScale = scale;
|
|
|
|
// 새로운 스케일 계산
|
|
const delta = Math.max(-1, Math.min(1, e.wheelDelta || -e.detail));
|
|
const scaleFactor = 0.1;
|
|
|
|
if (delta > 0) {
|
|
scale = Math.min(4, scale + scaleFactor);
|
|
} else {
|
|
scale = Math.max(0.1, scale - scaleFactor);
|
|
}
|
|
|
|
// 스케일 변화율
|
|
const scaleRatio = scale / prevScale;
|
|
|
|
// 새로운 translate 값 계산
|
|
// 마우스 포인터 위치는 고정되어야 하므로, 스케일 변화에 따른 위치 조정
|
|
translateX = mouseX - (((mouseX - translateX) * scaleRatio));
|
|
translateY = mouseY - (((mouseY - translateY) * scaleRatio));
|
|
|
|
updateImageTransform();
|
|
updateZoomInfo();
|
|
}
|
|
|
|
// 이미지 transform 업데이트
|
|
function updateImageTransform() {
|
|
// transform의 원점을 이미지의 중심으로 설정
|
|
const originX = image.offsetWidth / 2;
|
|
const originY = image.offsetHeight / 2;
|
|
image.style.transformOrigin = `${originX}px ${originY}px`;
|
|
image.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
|
|
}
|
|
|
|
// 줌 정보 업데이트
|
|
function updateZoomInfo() {
|
|
zoomInfo.textContent = `${Math.round(scale * 100)}%`;
|
|
}
|
|
|
|
// 이미지 초기 위치 설정
|
|
function centerImage() {
|
|
translateX = (container.offsetWidth - image.offsetWidth) / 2;
|
|
translateY = (container.offsetHeight - image.offsetHeight) / 2;
|
|
updateImageTransform();
|
|
}
|
|
|
|
// 이벤트 리스너 등록
|
|
container.addEventListener('mousedown', dragStart);
|
|
window.addEventListener('mousemove', drag);
|
|
window.addEventListener('mouseup', dragEnd);
|
|
container.addEventListener('wheel', zoom);
|
|
container.addEventListener('contextmenu',(e)=>{
|
|
e.preventDefault();
|
|
container.style.display = 'none';
|
|
})
|
|
|
|
// 더블클릭으로 원래 크기로 복원
|
|
container.addEventListener('dblclick', () => {
|
|
scale = 1;
|
|
updateZoomInfo();
|
|
});
|
|
}
|
|
|
|
function _openIfc(path, thumbnail_key, resourcePath, dataId, path_name){
|
|
const iframe = document.createElement('iframe');
|
|
iframe.onload = () => {
|
|
iframe.contentWindow.postMessage({ path, thumbnail_key, resourcePath, dataId, path_name }, '*'); // path 값을 iframe에 전달
|
|
};
|
|
iframe.src = `/libs/ifcViewer/index.html`;
|
|
iframe.width = '100%';
|
|
iframe.height = '100%';
|
|
document.getElementById('popup_viewer').appendChild(iframe);
|
|
}
|
|
|
|
function _open3d(path, thumbnail_key, resourcePath, dataId, path_name){
|
|
const iframe = document.createElement('iframe');
|
|
iframe.onload = () => {
|
|
iframe.contentWindow.postMessage({ path, thumbnail_key, resourcePath, dataId, path_name }, '*'); // path 값을 iframe에 전달
|
|
};
|
|
iframe.src = `/libs/3dViewer/index.html`;
|
|
iframe.width = '100%';
|
|
iframe.height = '100%';
|
|
document.getElementById('popup_viewer').appendChild(iframe);
|
|
}
|
|
|
|
function _drawMeta(data){
|
|
let container = document.getElementById('meta-data');
|
|
container.style.display = 'block';
|
|
Object.keys(data).forEach(item=>{
|
|
if(!item.startsWith('$') && item != 'type' && item != 'password'){
|
|
let line = `<span class="key">${item}</span> : <span>${data[item]}</span><br>`;
|
|
container.innerHTML += line;
|
|
}
|
|
})
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
// 오픈소스 문서 직접 뷰잉 및 PDF 폴백 함수 정의
|
|
// -----------------------------------------------------------------
|
|
|
|
function initFallbackPdfButton(dataId, path_name, resourcePath) {
|
|
const btn = document.getElementById('fallback-pdf-btn');
|
|
if (!btn) return;
|
|
|
|
// 이전 등록된 리스너 제거를 위해 복사 대체
|
|
const newBtn = btn.cloneNode(true);
|
|
btn.parentNode.replaceChild(newBtn, btn);
|
|
|
|
newBtn.style.display = 'block';
|
|
newBtn.textContent = 'PDF로 보기';
|
|
newBtn.style.pointerEvents = 'auto';
|
|
|
|
newBtn.addEventListener('click', async () => {
|
|
newBtn.textContent = '로딩 중...';
|
|
newBtn.style.pointerEvents = 'none';
|
|
|
|
try {
|
|
// 1. 파일 메타데이터 (popup_key) 조회
|
|
let dataInfoRes = await fetch(`${path_name}/getDataInfo`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ params: { dataIdArr: [dataId], isRemoved: false, debug: "popup fallback" } })
|
|
});
|
|
let dataInfo = await dataInfoRes.json();
|
|
let result = dataInfo.result;
|
|
if (Array.isArray(result)) result = result[0];
|
|
let popupKey = result ? result.popup_key : null;
|
|
|
|
// 2. 만약 PDF 변환본이 아직 없다면 백엔드 변환 요청
|
|
if (!popupKey) {
|
|
newBtn.textContent = 'PDF 변환 요청 중...';
|
|
await fetch(`${path_name}/convertPdf`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
params: {
|
|
dataId: dataId,
|
|
resourcePath: resourcePath,
|
|
userInfoString: JSON.stringify({ user_id: 'SYSTEM', user_nm: 'Viewer User' }),
|
|
objectKey: result.object_key,
|
|
storageType: result.storage_type
|
|
}
|
|
})
|
|
});
|
|
alert('서버 측 PDF 변환이 시작되었습니다. 잠시 후 다시 클릭해 주세요.');
|
|
newBtn.textContent = 'PDF로 보기';
|
|
newBtn.style.pointerEvents = 'auto';
|
|
return;
|
|
}
|
|
|
|
// 3. PDF용 Presigned URL 생성
|
|
let downloadUrlRes = await fetch(`${path_name}/generateDownloadUrl`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ objectKey: popupKey, resourcePath: resourcePath })
|
|
});
|
|
let downloadUrlData = await downloadUrlRes.json();
|
|
if (downloadUrlData.message === 'generateDownloadUrl_success') {
|
|
let pdfUrl = downloadUrlData.url;
|
|
|
|
// 화면 초기화 및 PDF 뷰어 로드
|
|
document.getElementById('popup_viewer').innerHTML = '';
|
|
newBtn.style.display = 'none';
|
|
_openPdf(pdfUrl, {});
|
|
} else {
|
|
alert('PDF 미리보기 주소 획득에 실패했습니다.');
|
|
newBtn.textContent = 'PDF로 보기';
|
|
newBtn.style.pointerEvents = 'auto';
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
alert('PDF 변환 로드 과정 중 오류가 발생했습니다.');
|
|
newBtn.textContent = 'PDF로 보기';
|
|
newBtn.style.pointerEvents = 'auto';
|
|
}
|
|
});
|
|
}
|
|
|
|
function _openExcel(path, data) {
|
|
const viewer = document.getElementById('popup_viewer');
|
|
viewer.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:100%;font-size:1.2rem;color:#666;background:#fff;">엑셀 데이터를 불러오는 중...</div>';
|
|
|
|
fetch(path)
|
|
.then(res => {
|
|
if (!res.ok) throw new Error(`HTTP ${res.status} ${res.statusText}`);
|
|
return res.arrayBuffer();
|
|
})
|
|
.then(arrayBuffer => {
|
|
viewer.innerHTML = '';
|
|
|
|
LuckyExcel.transformExcelToLucky(arrayBuffer, function(exportJson, luckysheetfile) {
|
|
if(exportJson.sheets == null || exportJson.sheets.length == 0) {
|
|
viewer.innerHTML = '<div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;color:#d9534f;font-size:1.1rem;background:#fff;">엑셀 데이터를 파싱하지 못했습니다. (xls 확장자는 지원하지 않습니다.)</div>';
|
|
if (dataId && path_name) {
|
|
initFallbackPdfButton(dataId, path_name, resourcePath);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (window.luckysheet) {
|
|
window.luckysheet.destroy();
|
|
}
|
|
|
|
viewer.style.position = 'relative';
|
|
const container = document.createElement('div');
|
|
container.id = 'luckysheet_inner';
|
|
container.style.margin = '0px';
|
|
container.style.padding = '0px';
|
|
container.style.position = 'absolute';
|
|
container.style.width = '100%';
|
|
container.style.height = '100%';
|
|
container.style.left = '0px';
|
|
container.style.top = '0px';
|
|
viewer.appendChild(container);
|
|
|
|
try {
|
|
window.luckysheet.create({
|
|
container: 'luckysheet_inner',
|
|
data: exportJson.sheets,
|
|
title: exportJson.info.name || document.title,
|
|
lang: 'en',
|
|
showinfobar: false,
|
|
myFolderUrl: 'javascript:void(0)'
|
|
});
|
|
} catch (createErr) {
|
|
console.error("Luckysheet create error: ", createErr);
|
|
viewer.innerHTML = `<div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;color:#d9534f;background:#fff;">
|
|
<div>엑셀 시트 생성 중 오류가 발생했습니다.</div>
|
|
<div style="font-size:0.9rem;color:#999;margin-top:8px;">에러: ${createErr.message}</div>
|
|
</div>`;
|
|
if (dataId && path_name) {
|
|
initFallbackPdfButton(dataId, path_name, resourcePath);
|
|
}
|
|
}
|
|
}, function(err) {
|
|
console.error("Luckysheet transform error: ", err);
|
|
viewer.innerHTML = `<div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;color:#d9534f;background:#fff;">
|
|
<div>엑셀 파일을 읽는 중 오류가 발생했습니다.</div>
|
|
<div style="font-size:0.9rem;color:#999;margin-top:8px;">상세: ${err.message || err}</div>
|
|
</div>`;
|
|
if (dataId && path_name) {
|
|
initFallbackPdfButton(dataId, path_name, resourcePath);
|
|
}
|
|
});
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
viewer.innerHTML = `<div style="display:flex;flex-direction:column;justify-content:center;align-items:center;height:100%;color:#d9534f;background:#fff;">
|
|
<div>엑셀 파일을 불러오는데 실패했습니다.</div>
|
|
<div style="font-size:0.9rem;color:#999;margin-top:8px;">에러: ${err.message}</div>
|
|
</div>`;
|
|
if (dataId && path_name) {
|
|
initFallbackPdfButton(dataId, path_name, resourcePath);
|
|
}
|
|
});
|
|
}
|
|
|
|
function _openDocx(path, data) {
|
|
const viewer = document.getElementById('popup_viewer');
|
|
viewer.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:100%;font-size:1.2rem;color:#666;background:#fff;">워드 문서를 불러오는 중...</div>';
|
|
|
|
if (dataId && path_name) {
|
|
initFallbackPdfButton(dataId, path_name, resourcePath);
|
|
}
|
|
|
|
fetch(path)
|
|
.then(res => {
|
|
if (!res.ok) throw new Error('Word fetch failed');
|
|
return res.arrayBuffer();
|
|
})
|
|
.then(arrayBuffer => {
|
|
viewer.innerHTML = '';
|
|
|
|
const container = document.createElement('div');
|
|
container.style.width = '100%';
|
|
container.style.height = '100%';
|
|
container.style.overflow = 'auto';
|
|
container.style.padding = '20px';
|
|
container.style.boxSizing = 'border-box';
|
|
container.style.background = '#f5f5f5';
|
|
|
|
const docxInner = document.createElement('div');
|
|
docxInner.style.background = '#ffffff';
|
|
docxInner.style.margin = '0 auto';
|
|
docxInner.style.maxWidth = '800px';
|
|
docxInner.style.boxShadow = '0 4px 10px rgba(0,0,0,0.1)';
|
|
docxInner.style.padding = '40px';
|
|
|
|
container.appendChild(docxInner);
|
|
viewer.appendChild(container);
|
|
|
|
docx.renderAsync(arrayBuffer, docxInner)
|
|
.then(() => console.log("docx rendered"))
|
|
.catch(err => {
|
|
console.error(err);
|
|
docxInner.innerHTML = '<div style="color:#d9534f;text-align:center;">워드 문서 파싱 중 오류가 발생했습니다. 상단의 "PDF로 보기" 버튼을 이용해 주세요.</div>';
|
|
});
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
viewer.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:100%;color:#d9534f;background:#fff;">워드 문서를 불러오는데 실패했습니다.</div>';
|
|
});
|
|
}
|
|
|
|
function _openHwp(path, data) {
|
|
const viewer = document.getElementById('popup_viewer');
|
|
viewer.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:100%;font-size:1.2rem;color:#666;background:#fff;">한글 문서를 불러오는 중...</div>';
|
|
|
|
if (dataId && path_name) {
|
|
initFallbackPdfButton(dataId, path_name, resourcePath);
|
|
}
|
|
|
|
fetch(path)
|
|
.then(res => {
|
|
if (!res.ok) throw new Error('HWP fetch failed');
|
|
return res.blob();
|
|
})
|
|
.then(blob => {
|
|
viewer.innerHTML = '';
|
|
|
|
const container = document.createElement('div');
|
|
container.style.width = '100%';
|
|
container.style.height = '100%';
|
|
container.style.overflow = 'auto';
|
|
container.style.padding = '20px';
|
|
container.style.boxSizing = 'border-box';
|
|
container.style.background = '#f5f5f5';
|
|
|
|
const hwpInner = document.createElement('div');
|
|
hwpInner.style.background = '#ffffff';
|
|
hwpInner.style.margin = '0 auto';
|
|
hwpInner.style.maxWidth = '800px';
|
|
hwpInner.style.boxShadow = '0 4px 10px rgba(0,0,0,0.1)';
|
|
hwpInner.style.padding = '40px';
|
|
hwpInner.style.minHeight = '100%';
|
|
|
|
container.appendChild(hwpInner);
|
|
viewer.appendChild(container);
|
|
|
|
const reader = new FileReader();
|
|
reader.onload = (e) => {
|
|
const bstr = e.target.result;
|
|
try {
|
|
new hwp.Viewer(hwpInner, bstr);
|
|
} catch (err) {
|
|
console.error("hwp.js error: ", err);
|
|
hwpInner.innerHTML = '<div style="color:#d9534f;text-align:center;">한글 문서 파싱 중 오류가 발생했습니다. 상단의 "PDF로 보기" 버튼을 이용해 주세요.</div>';
|
|
}
|
|
};
|
|
reader.readAsBinaryString(blob);
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
viewer.innerHTML = '<div style="display:flex;justify-content:center;align-items:center;height:100%;color:#d9534f;background:#fff;">한글 문서를 불러오는데 실패했습니다.</div>';
|
|
});
|
|
} |