Files
PM_test/views/main/jsm/archive/eventManager.js
2026-06-12 17:14:03 +09:00

2976 lines
123 KiB
JavaScript

import { vars } from './variable.js';
import { checkProjectInactive } from '../main.js';
import { getList, drawUserPermissionList } from './userPermission.js';
import {
getDepth,
splitBaseAndExt,
splitTopPathAndTargetName,
getDataFromTreeObject,
targetFocus,
extractPathByLength,
getMyDownloadList,
pxToRem,
openNewWindowViewer,
initFileAreaUI,
syncGroupStyle
} from './common.js';
import {
preparePageRendering,
prepareRecycleBinRendering,
processHeaderBtnOverflow,
renderList,
renderViewer,
resetViewer,
renderContextmenu,
renderSizeBar,
renderMemo,
changeHeaderBtnStyle,
changeTreeItemStyle,
changeListItemStyle,
renderCompositionData
} from './pageRenderer.js';
import {
createFolder,
renameTarget,
downloadTarget,
openRenameModal,
openEditAuthorModal,
toggleRelocateCover,
openRemoveModal,
openDeleteModal,
downloadTempFile,
uploadMainTitleImage,
updateAiButtonState,
createVideoThumbnail,
createTextThumbnail,
getPdfData,
createPdfThumbnail,
resizeImage
} from './dataManager.js'
import { toggleRecycleBinModal, toggleModal, toggleDevMenuModal } from './modalManager.js'
let archiveMain = document.querySelector('.archive-main');
let listContainer = archiveMain?.querySelector('.archive-main-center .list-container');
let listNotice = archiveMain?.querySelector('.archive-main-center .list-notice');
let viewerContainer = archiveMain?.querySelector('.archive-main-right .viewer-container');
let viewerNotice = archiveMain?.querySelector('.archive-main-right .viewer-notice');
let overviewMain = document.querySelector('.overview-main');
let officialDocMain = document.querySelector('.official-doc-main');
let mainNotice = document.querySelector('.main-notice');
//// 키보드 이벤트
// 트리영역 화살표 키 이벤트 방지
document.querySelector('.tree-container')?.addEventListener('keydown', (e) => {
if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') e.preventDefault();
})
// 리스트영역 화살표 키 이벤트 방지
document.querySelector('.list-container .list-wrap.list-body')?.addEventListener('keydown', (e) => {
// 갤러리 폴더에서 위치 수정 모드가 실행중인 경우 리턴 - 경도/위도 입력란에서 화살표 키 필요
if (vars.mapMode == 'edit') return;
if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'ArrowLeft' || e.key === 'ArrowRight') e.preventDefault();
})
// 썸네일크기 슬라이더 화살표 키 이벤트 방지
document.querySelector('.slider-input .thumbnail-size-slider')?.addEventListener('keydown', (e) => {
e.preventDefault();
})
// 실제 키보드 이벤트 코드
window.addEventListener('keydown', async (e) => {
//// 모달창 단축키
// 모달 닫기 esc
// 이름 변경 확인 enter
//// 파일 관련 단축키
// 새 폴더 Ctrl A
// 폴더 업로드 Ctrl Q
// 파일 업로드 Ctrl E
// 이름 변경 F2
// 이동 Ctrl X
// 다운로드 Ctrl D
// 삭제 Del
// if (e.target.nodeName != 'INPUT') {
// if (e.key == 'Backspace' || e.code == 'Backspace' || e.keyCode == 8) {
// e.preventDefault(); // 기본 동작 막기
// e.stopPropagation(); // 이벤트 전파 중단하기
// }
// }
let archiveModal = document.querySelector('.archive-modal');
let devMenuModal = document.querySelector('.dev-menu-modal');
let binModal = document.querySelector('.recycle-bin-modal');
let compositionModal = document.querySelector('.composition-modal');
//// 아카이브/개발자메뉴/휴지통 모달창에서 esc, enter 키 이벤트 적용
if (archiveModal?.style.display == 'flex' || devMenuModal?.style.display == 'flex' || binModal.style.display == 'flex' || compositionModal.style.display == 'flex') {
// 모달 닫기
if (e.key == 'Escape') {
if (document.querySelector('.permission-modal').style.display == 'flex') {
document.querySelector('.permission-modal').style.display = 'none';
return;
}
if (archiveModal.style.display == 'flex') {
toggleModal(false);
return;
}
if (devMenuModal?.style.display == 'flex') {
toggleDevMenuModal(false);
return;
}
if (binModal.style.display == 'flex') {
toggleRecycleBinModal(false);
toggleContextmenu(false);
return;
}
if(compositionModal.style.display == 'flex') {
compositionModal.style.display = 'none';
return;
}
}
// 모달창에서 엔터, 스페이스바 누르면 버튼 클릭 처리
if (e.key == 'Enter') {
// button 태그에 엔터키 적용하면 .click()으로 모달이 닫힌 후에도 기존 포커스된 버튼이 재활성화되며 다시 눌리는 현상 발생
// 브라우저 이벤트 흐름 상, keydown → click 순서 또는 이벤트 전파(bubbling) 문제
// e.preventDefault();
// e.stopPropagation();
if (devMenuModal?.style.display == 'flex') {
// 모달 두개 이상 겹칠 수 있도록?
// let toggleParams = {
// text: '개발자 메뉴에서는 오작동 방지를 위해 엔터키 사용이 제한됩니다.',
// type: 'alertModal'
// };
// toggleModal(true, toggleParams);
if (document.querySelector('.archive-modal .input-wrap textarea')) return;
e.preventDefault();
e.stopPropagation();
alert('개발자 메뉴에서는 오작동 방지를 위해 엔터키 사용이 제한됩니다. (textarea 작성 경우는 제외)');
return;
}
if (archiveModal.querySelector('.input-wrap').getElementsByTagName('textarea')[0]) return;
let convertBtn = document.querySelector('.archive-modal .convert-btn');
if (convertBtn) convertBtn.click();
let confirmBtn = document.querySelector('.archive-modal .confirm-btn');
if (confirmBtn && !confirmBtn.classList.contains('disabled')) confirmBtn.click();
}
}
//// 리스트/그리드 파일 목록 또는 휴지통 모달창에서 wasd/화살표, f2, delete 키 이벤트 적용
// 갤러리 폴더 지도 모드/위치 수정 모드 일 때 안내 표시 및 리턴
let mapContainer = listContainer?.querySelector('.map-container');
if (mapContainer && mapContainer.style.display == 'flex') {
let notificatitonParams = { text: '지도모드에서는 키보드 기능이 제한됩니다.' }
if (!mapContainer.classList.contains('edit-mode')) showNotification(notificatitonParams);
return;
}
//// 리스트/그리드 파일 목록이 표시되어 있고, 모달창이 전부 꺼져 있을 때
if (archiveMain.style.display == 'flex' && listContainer.style.display == 'flex'
&& (archiveModal.style.display != 'flex' && devMenuModal?.style.display != 'flex' && binModal.style.display != 'flex')) {
// 정보 영역 textarea 선택되어 있는 경우 리턴
if (document.activeElement == document.querySelector('.info-wrap textarea')) return;
// 전체 선택
if (e.ctrlKey && e.key == 'a') {
e.preventDefault(); // 기본 동작 막기
e.stopPropagation(); // 이벤트 전파 중단하기
resetViewer();
// 강조 해제
toggleContextFocusBox(false);
// 컨텍스트 메뉴 닫기
toggleContextmenu(false, e);
listContainer.querySelectorAll('.list-wrap.list-body .list-item').forEach(item => {
item.classList.add('selected');
item.classList.add('group-style');
});
vars.multiSelectListItemArr = Array.from(listContainer.querySelectorAll('.list-wrap.list-body .list-item'));
vars.lastSelectTarget = vars.multiSelectListItemArr[0];
}
// 리스트/그리드 파일이 한개 이상이고, 특수키(컨트롤, 알트, 쉬프트, 맥os 메타키)를 누르지 않은 상태일 때 상하좌우/WSAD 키로 리스트/그리드 탐색
let arrowKeyArr = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
let wasdKeyArr = ['w', 's', 'a', 'd'];
let targetKeyArr = [...arrowKeyArr, ...wasdKeyArr];
if (
listContainer.querySelector('.list-body .list-item-wrap').children.length > 0
&& !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey
&& targetKeyArr.includes(e.key)
) {
let isList = listContainer.classList.contains('list');
let isGrid = listContainer.classList.contains('grid');
let gridItemSize = -(document.querySelector('.slider-input .thumbnail-size-slider')?.value);
let listItemArr = [...listContainer.querySelector('.list-body .list-item-wrap').children];
if (viewerContainer.dataset.resourcePath == '') {
// 왼쪽 / A
if (e.key == arrowKeyArr[2] || e.key == wasdKeyArr[2]) {
if (isList) return; // 리스트 목록이면 리턴 (리스트 목록에서는 위아래로만 이동)
}
// 오른쪽 / D
if (e.key == arrowKeyArr[3] || e.key == wasdKeyArr[3]) {
if (isList) return; // 리스트 목록이면 리턴 (리스트 목록에서는 위아래로만 이동)
}
vars.lastListItem = listItemArr[0];
} else {
let currentIdx = listItemArr.indexOf(vars.lastListItem);
// 위 / W
if (e.key == arrowKeyArr[0] || e.key == wasdKeyArr[0]) {
if (currentIdx == 0) return; // 현재 선택된 아이템이 첫 번째면 리턴
if (isList) vars.lastListItem = listItemArr[(currentIdx-1 < 0) ? 0 : currentIdx-1];
if (isGrid) vars.lastListItem = listItemArr[(currentIdx-gridItemSize < 0) ? 0 : currentIdx-gridItemSize];
}
// 아래 / S
if (e.key == arrowKeyArr[1] || e.key == wasdKeyArr[1]) {
if (currentIdx == listItemArr.length-1) return; // 현재 선택된 아이템이 마지막이면 리턴
if (isList) vars.lastListItem = listItemArr[currentIdx+1];
if (isGrid) vars.lastListItem = listItemArr[(currentIdx+gridItemSize > listItemArr.length-1) ? listItemArr.length-1 : currentIdx+gridItemSize];
}
// 왼쪽 / A
if (e.key == arrowKeyArr[2] || e.key == wasdKeyArr[2]) {
if (currentIdx == 0 || isList) return; // 현재 선택된 아이템이 첫 번째이거나 리스트 목록이면 리턴 (리스트 목록에서는 위아래로만 이동)
vars.lastListItem = listItemArr[(currentIdx < 0) ? 0 : currentIdx-1];
}
// 오른쪽 / D
if (e.key == arrowKeyArr[3] || e.key == wasdKeyArr[3]) {
if (currentIdx == listItemArr.length-1 || isList) return; // 현재 선택된 아이템이 마지막이거나 리스트 목록이면 리턴 (리스트 목록에서는 위아래로만 이동)
vars.lastListItem = listItemArr[currentIdx+1];
}
}
// 기존에 선택된 다중 선택된 아이템 배열 및 모든 아이템 선택상태 초기화
vars.multiSelectListItemArr = [];
listContainer.querySelectorAll('.list-item').forEach((elem) => {
elem.classList.remove('selected');
elem.classList.add('non-selected');
});
// 키보드로 선택된 아이템 뷰어에 표시
let resourcePath = vars.lastListItem.dataset.resourcePath;
let dataId = vars.lastListItem.dataset.id;
renderViewer(resourcePath, dataId);
let res = await axios.get(`${vars.path_name}/getMemoInfo`, {
params: { userInfoString: vars.userInfoString, resourcePath, dataId }
});
let memo = res.data.result.memo;
setTimeout(() => {
renderMemo(memo, dataId);
}, 100);
// document.querySelectorAll('.archive-main .list-container .list-body .list-item').forEach((elem) => {
// elem.classList.remove('selected');
// elem.classList.add('non-selected');
// });
// clickedListItem.classList.add('selected');
// clickedListItem.classList.remove('non-selected');
// 선택 상태로 스타일 변경
changeListItemStyle(vars.lastListItem);
// 선택된 아이템으로 포커스 이동
targetFocus(vars.lastListItem);
vars.lastSelectTarget = vars.lastListItem;
await syncGroupStyle();
}
}
//// 휴지통 모달창 파일 목록이 표시되어 있을 때
if (binModal.style.display == 'flex') {
let binModalListContainer = binModal.querySelector('.list-container');
// 전체 선택
if (e.ctrlKey && e.key == 'a') {
e.preventDefault(); // 기본 동작 막기
e.stopPropagation(); // 이벤트 전파 중단하기
resetViewer();
// 강조 해제
toggleContextFocusBox(false);
// 컨텍스트 메뉴 닫기
toggleContextmenu(false, e);
binModalListContainer.querySelectorAll('.list-wrap.list-body .list-item').forEach(item => {
item.classList.add('selected');
item.classList.add('group-style');
});
vars.multiSelectListItemArr_bin = Array.from(binModalListContainer.querySelectorAll('.list-wrap.list-body .list-item'));
vars.lastSelectTarget_bin = vars.multiSelectListItemArr_bin[0];
}
// 리스트/그리드 파일이 한개 이상이고, 특수키(컨트롤, 알트, 쉬프트, 맥os 메타키)를 누르지 않은 상태일 때 상하/WS 키로 리스트 탐색
let arrowKeyArr = ['ArrowUp', 'ArrowDown'];
let wasdKeyArr = ['w', 's'];
let targetKeyArr = [...arrowKeyArr, ...wasdKeyArr];
if (
binModalListContainer.querySelector('.list-body .list-item-wrap').children.length > 0
&& !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey
&& targetKeyArr.includes(e.key)
) {
let listItemArr = [...binModalListContainer.querySelector('.list-body .list-item-wrap').children];
let currentIdx = listItemArr.indexOf(vars.lastListItem_bin);
// 위 / W
if (e.key == arrowKeyArr[0] || e.key == wasdKeyArr[0]) {
if (currentIdx == 0) return; // 현재 선택된 아이템이 첫 번째면 리턴
vars.lastListItem_bin = listItemArr[(currentIdx-1 < 0) ? 0 : currentIdx-1];
}
// 아래 / S
if (e.key == arrowKeyArr[1] || e.key == wasdKeyArr[1]) {
if (currentIdx == listItemArr.length-1) return; // 현재 선택된 아이템이 마지막이면 리턴
vars.lastListItem_bin = listItemArr[currentIdx+1];
}
// 기존에 선택된 다중 선택된 아이템 배열 및 모든 아이템 선택상태 초기화
vars.multiSelectListItemArr_bin = [];
binModalListContainer.querySelectorAll('.list-item').forEach((elem) => {
elem.classList.remove('selected');
});
// 선택 상태로 스타일 변경
changeListItemStyle(vars.lastListItem_bin);
// 선택된 아이템으로 포커스 이동
targetFocus(vars.lastListItem_bin);
vars.lastSelectTarget_bin = vars.lastListItem_bin;
await syncGroupStyle();
}
}
if (archiveModal?.style.display != 'flex' && devMenuModal?.style.display != 'flex') {
let permission = vars.permission.permission;
// 새 폴더
// if (e.ctrlKey && e.key == 'g') {
// // if (document.querySelector('.archive-modal').style.display == 'flex') return;
// e.preventDefault(); // 기본 동작 막기
// e.stopPropagation(); // 이벤트 전파 중단하기
// openCreateFolderModal();
// toggleContextmenu(false);
// }
// // 폴더 업로드
// if (e.ctrlKey && e.key == 'q') {
// e.preventDefault(); // 기본 동작 막기
// e.stopPropagation(); // 이벤트 전파 중단하기
// document.getElementById('uploadFolderInput').click();
// toggleContextmenu(false);
// }
// // 파일 업로드
// if (e.ctrlKey && e.key == 'e') {
// e.preventDefault(); // 기본 동작 막기
// e.stopPropagation(); // 이벤트 전파 중단하기
// document.getElementById('uploadFileInput').click();
// toggleContextmenu(false);
// }
// // 이동
// if (e.ctrlKey && e.key == 'b') {
// if (vars.lastSelectTarget) {
// openMoveModal();
// toggleContextmenu(false);
// }
// }
// // 다운로드
// if (e.ctrlKey && e.key == 'd') {
// e.preventDefault(); // 기본 동작 막기
// e.stopPropagation(); // 이벤트 전파 중단하기
// downloadTarget();
// toggleContextmenu(false);
// }
let isRecycleBinModal = document.querySelector('.recycle-bin-modal').style.display == 'flex';
let target;
if (!isRecycleBinModal && vars.lastSelectTarget) {
if (vars.lastSelectTarget.classList.contains('folder-btn')) target = vars.lastHeaderBtn;
if (vars.lastSelectTarget.classList.contains('tree-item')) target = vars.lastMainTreeItem;
if (vars.lastSelectTarget.classList.contains('list-item')) target = vars.lastListItem;
}
if (isRecycleBinModal && vars.lastSelectTarget_bin) {
if (vars.lastSelectTarget_bin.classList.contains('list-item')) target = vars.lastListItem_bin;
}
let lastContextTarget = (isRecycleBinModal) ? vars.lastContextTarget_bin : vars.lastContextTarget;
if (lastContextTarget) target = lastContextTarget;
// .tree-item 우클릭 시 target을 .tree-item-wrap으로 설정
// if (target && target.matches('.tree-item')) target = target.parentElement;
// 우선 리스트 아이템만 F2 단축키로 이름 변경하도록 제한
// if (target && !target.matches('.list-item')) return;
// 이름 변경
if (e.key == 'F2') {
if (!target) return;
let depth = getDepth(target.dataset.resourcePath);
if ((depth == 1 && permission < 191) || (depth >= 2 && permission < 7)) return;
if (isRecycleBinModal) return;
if (target) openRenameModal(target.dataset.resourcePath, target);
}
// 삭제
if (e.key == 'Delete') {
if (!target) return;
let memoTextarea = document.querySelector('.info-wrap .textarea');
if (document.activeElement == memoTextarea) {
return;
} else {
if (target) {
let depth = getDepth(target.dataset.resourcePath);
if ((depth == 1 && permission < 191) || (depth >= 2 && permission < 7)) return;
// 폴더는 Delete키로 삭제 못하도록 제한
if (!target.classList.contains('list-item')) return;
if (isRecycleBinModal) openDeleteModal(target.dataset.resourcePath, target);
else openRemoveModal(target.dataset.resourcePath, target);
} else {
if (isRecycleBinModal) {
let depth = getDepth(vars.multiSelectListItemArr_bin[0].dataset.resourcePath);
if ((depth == 1 && permission < 191) || (depth >= 2 && permission < 7)) return;
if (vars.multiSelectListItemArr_bin.length > 0) openDeleteModal(vars.multiSelectListItemArr_bin[0].dataset.resourcePath, vars.multiSelectListItemArr_bin[0]);
} else {
let depth = getDepth(vars.multiSelectListItemArr[0].dataset.resourcePath);
if ((depth == 1 && permission < 191) || (depth >= 2 && permission < 7)) return;
if (vars.multiSelectListItemArr.length > 0) openRemoveModal(vars.multiSelectListItemArr[0].dataset.resourcePath);
}
}
}
}
// 확대 / 축소
if (vars.lastFileAreaMode == 'grid') {
if (e.key == '+' || e.key == '=') {
let value = Number(gridSizeSlider.value);
if (value == -1) return;
if (value != -1) value = value + 1;
gridSizeSlider.value = value;
changeSliderTrackColor(gridSizeSlider, false);
changeGridItemSize(value);
}
if (e.key == '-') {
let value = Number(gridSizeSlider.value);
if (value == -10) return;
if (value != -10) value = value -1;
gridSizeSlider.value = value;
changeSliderTrackColor(gridSizeSlider, false);
changeGridItemSize(value);
}
}
}
});
// let mediaQuery;
// let orientationListenerAdded = false;
// function handleOrientationChange(e) {
// if (e.matches) {
// alert('세로 모드 전환됨');
// } else {
// alert('가로 모드 전환됨');
// }
// }
// function setupOrientationListener() {
// if (mediaQuery && orientationListenerAdded) return; // 이미 등록됨
// matchMedia = css_mediaQuery를 동적으로 사용하게 해주는 이벤트 orientation : portrait(세로모드) 감시로 true/false를 반환한다
// mediaQuery = window.matchMedia("(orientation: portrait)");
// mediaQuery.addEventListener('change', handleOrientationChange);
// orientationListenerAdded = true;
// }
// setupOrientationListener();
// window resize시 이벤트 -> 크기 조절할 때마다 연속적으로 함수가 많이 실행되는 현상 방지하기 위해 setTimeout 사용
let resizeTimer;
window.addEventListener("resize", function(e) {
clearTimeout(resizeTimer);
// 리사이즈 이벤트 종료 후 지정한 시간이 지난 후 내부 함수들 실행 (200~500이 적당)
resizeTimer = setTimeout(() => {
// 강조 해제
toggleContextFocusBox(false);
// 컨텍스트 메뉴 닫기
toggleContextmenu(false, e);
// 헤더 버튼 overflow 처리
processHeaderBtnOverflow();
// 터치 스크린이 있는지 감지
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0 ;
// 터치 스크린 (태블릿, 모바일)인 상황에서 분기 처리
if(!isTouchDevice){
// 모달 닫기
toggleModal(false);
toggleDevMenuModal(false);
if (document.querySelector('.permission-modal')?.style.display == 'flex') {
document.querySelector('.permission-modal').style.display = 'none';
}
}
// 파일 이동 화면 닫기
toggleRelocateCover(false);
// 저장공간 푸터 막대그래프 렌더링
renderSizeBar();
}, 200);
})
// document 전체에서 클릭 시 이벤트
document.addEventListener('click', (e) => {
// 강조 해제
toggleContextFocusBox(false, e.target);
// 컨텍스트 메뉴 닫기
toggleContextmenu(false, e);
if(e.target.id != 'model'){
// 위치기반 모델 임시창 닫기
document.getElementById('model-select-modal')?.remove();
}
})
// document 전체에서 휠 조작 시 이벤트
document.addEventListener('wheel', (e) => {
let headerCenterLeftWrap = document.querySelector('body > .header .center .left.wrap');
let menuTab = headerCenterLeftWrap.querySelector('.menu-tab');
if (headerCenterLeftWrap.contains(e.target)) {
if (e.deltaY > 0) menuTab.scrollLeft += 25;
else menuTab.scrollLeft -= 25;
}
// 강조 해제
toggleContextFocusBox(false);
// 컨텍스트 메뉴 닫기
toggleContextmenu(false, e);
})
// document 전체에서 우클릭 시 이벤트
document.addEventListener('contextmenu', (e) => {
// 기본 컨텍스트 메뉴 표시 방지
e.preventDefault();
e.stopPropagation();
// 카운트다운 툴팁 표시되어 있는 경우 삭제
document.querySelector('.countdown-tooltip')?.remove();
// 컨텍스트 메뉴 열려야 될 영역 예외 처리
if (e.target.closest('.folder-btn')) return;
if (e.target.closest('.recycle-bin-modal .list-container .list-body')) return;
if (e.target.closest('.archive-main .archive-main-left')) return;
if (e.target.closest('.archive-main .archive-main-center')) return;
// 컨텍스트 메뉴가 열리지 않는 영역에서 우클릭 시 강조 해제 및 컨텍스트 메뉴 닫기
toggleContextFocusBox(false);
toggleContextmenu(false);
})
// 스크롤바 조작 시 이벤트
document.querySelectorAll('.scroll-container').forEach(scrollContainer => {
scrollContainer.addEventListener('scroll', async (e) => {
// 강조 해제
toggleContextFocusBox(false);
// 컨텍스트 메뉴 닫기
toggleContextmenu(false, e);
})
})
// 헤더 좌측 타이틀 부분 (PM / OOO) 클릭 시 프로젝트 리스트 화면으로 이동
document.querySelector('body > .header .left .title').addEventListener('click', (e) => {
if (!e.isTrusted) {
console.warn("Title click event bypassed due to programmatic trigger.");
return;
}
// 데이터 불러오는 중 (로딩창)이 활성화되어 있는 동안에는 타이틀 클릭으로 인한 리다이렉트를 차단하여 루프 방지
const initProgress = document.querySelector('.init-progress');
if (initProgress && initProgress.style.display !== 'none') {
console.warn("Title click event bypassed because data is still loading.");
return;
}
// 페이지 로드 후 1.5초 이내의 타이틀 클릭 이벤트도 잔상 클릭으로 간주하여 차단
if (window.performance && window.performance.now() < 1500) {
console.warn("Title click event bypassed due to immediate click after load.");
return;
}
//////// pm-bcmf 연결용 테스트 코드 - bcmf계정인 경우 타이틀 클릭해서 프로젝트 리스트 화면으로 이동 불가능
if (JSON.parse(vars.userInfoString).user_id.includes('bcmf-')) return;
// 프로젝트에 참여하지 않은 인원이 비정상적으로 프로젝트 페이지에 접속한 경우 타이틀 클릭 불가능
if (!vars.project) return;
if (vars.storageType == 'ONPREMISE') {
// 로컬 온프레미스 환경에서는 복잡한 카테고리 경로 대신 루트(/)로 직접 리다이렉트하여 리다이렉트 루프 방지
window.location.href = '/';
}
if (vars.storageType == 'CLOUD') window.location.href = window.location.protocol + "//" + window.location.host;
})
// 헤더 중간 좌측 영역 mouseover/mouserout 시 스크롤바 on/off
let headerCenterLeftWrap = document.querySelector('body > .header .center .left.wrap');
headerCenterLeftWrap?.addEventListener('mouseover', (e) => {
document.documentElement.style.setProperty('--header-scrollbar-thumb-color', '#e9eeed');
})
headerCenterLeftWrap?.addEventListener('mouseout', (e) => {
document.documentElement.style.setProperty('--header-scrollbar-thumb-color', '#e9eeed00');
})
// 헤더 중간 좌측 스크롤 좌우 버튼 클릭 이벤트
let menuTab = headerCenterLeftWrap?.querySelector('.menu-tab');
document.querySelector('.scroll-left')?.addEventListener('click', (e) => {
menuTab.scrollBy({ left: -100, behavior: 'smooth' });
})
document.querySelector('.scroll-right')?.addEventListener('click', (e) => {
menuTab.scrollBy({ left: 100, behavior: 'smooth' });
})
// let scrollLeft = document.querySelector('.scroll-left');
// let scrollRight = document.querySelector('.scroll-left');
// if (scrollLeft) {
// document.querySelector('.scroll-left').onclick = () => {
// headerCenterLeftWrap.querySelector('.menu-tab').scrollBy({ left: -100, behavior: 'smooth' });
// }
// }
// if (scrollRight) {
// document.querySelector('.scroll-right').onclick = () => {
// headerCenterLeftWrap.querySelector('.menu-tab').scrollBy({ left: 100, behavior: 'smooth' });
// }
// }
// 헤더 우측 공문 버튼
document.querySelector('body > .header .center .right.wrap .official-doc-btn')?.addEventListener('click', async (e)=>{
document.querySelector('.archive-main').style.display = 'none';
if (overviewMain) overviewMain.style.display = 'none';
document.querySelector('.official-doc-main').style.display = 'flex';
// 공문 index.js 임포트
await import(`../officialDoc/index.js`);
if (mainNotice.style.display == 'flex') mainNotice.style.display = 'none';
//// vars에서 마지막 선택 아이템을 저장한 속성들 초기화 - 헤더 우측 공문 버튼
vars.lastMainTreeItem = undefined;
vars.lastListItem = undefined;
vars.lastListGroupTarget = undefined;
vars.lastContextTarget = undefined;
vars.lastSelectTarget = undefined;
vars.multiSelectListItemArr = [];
let pageRanderingOption = { scope: 'headerBtn_page', resourcePath: '/공문' };
await preparePageRendering(pageRanderingOption);
changeHeaderBtnStyle(e.target);
// 컨트롤 박스 숨김
toggleControlBox(false);
});
// 헤더 우측 위치기반모델 버튼
document.querySelector('body > .header .center .right.wrap .model-btn')?.addEventListener('click',async (e)=>{
///////PresignedUrl
// let PresignedUrl = undefined;
// let generateDownloadUrlParams = {
// objectKey: `gsim/model.gsim`, //=> 모든 모델 위치 버킷/gsim/model.gsim
// resourcePath: `/model.gsim`
// }
// let generateDownloadUrlRes = await axios.post(`${vars.path_name}/generateDownloadUrl`, generateDownloadUrlParams);
// if (generateDownloadUrlRes.data.message == 'generateDownloadUrl_success') {
// PresignedUrl = generateDownloadUrlRes.data.url;
// }
// ///////PresignedUrl end
// let fullPath = encodeURIComponent(PresignedUrl);
// window.open(`/popup?path=${fullPath}&data=eyIkZXh0IjoiZ3NpbSJ9`, '', `width=${screen.width}, height=${screen.height}`);
//test
if(vars.project.gsim_id.includes(',')){
let idArr = vars.project.gsim_id.split(',');
let nameArr = vars.project.gsim_nm.split(',');
let div = document.createElement('div');
div.className = 'model-select';
div.id = 'model-select-modal';
div.style.left = `${e.clientX}px`;
div.style.top = `${e.clientY}px`;
for(let i = 0; i < idArr.length; i++){
let item = document.createElement('h4');
item.className = 'model-item';
item.innerHTML = `${nameArr[i]}`;
div.append(item);
item.addEventListener('click', async (e)=>{
let uri = `https://www.gsimtech.com/${idArr[i]}?by=PM`;
window.open(uri, '_blank');
div.remove();
});
}
document.querySelector('body').append(div);
}else{
let uri = `https://www.gsimtech.com/${(vars.project.gsim_id)?vars.project.gsim_id:''}?by=PM`;
window.open(uri, '_blank');
}
});
// 헤더 우측 접속 인원 클릭 이벤트
document.querySelector('body > .header .right .connected-users')?.addEventListener('click', (e) => {
let toggleParams = {
title: '접속 인원',
type: 'connectedUsers',
};
toggleModal(true, toggleParams);
// 모달 열기
// 현재 사용자 정보 표시
})
// 접속 인원 모달 - 유저 권한 설정 버튼 클릭 이벤트
document.querySelector('.archive-modal > .wrap .modal-wrap .modal-header > .title .left-wrap .set-user-permission-btn')?.addEventListener('click', async () => {
//list 받아오기
let list = await getList();
list.changePermission = {};
//체크박스 업데이트에 사용할 객체
list.deptUserCount = {};
list.permission.forEach(p => { list.changePermission[p.user_id] = {user_id : p.user_id, lev : p.lev}})
list.all.forEach(user => {
const key = `${user.company}_${user.dept}`;
if(!list.deptUserCount[key]) list.deptUserCount[key] = 0;
list.deptUserCount[key]++;
});
vars.permissionList = list;
document.getElementById('sub-master').checked = true;
drawUserPermissionList(vars.permissionList);
let tab = document.querySelector('.permission-modal .select-list.select-list-sub-master');
[...tab.parentElement.children].forEach(element => {
if (element.classList.contains('select-list')) element.style.display = 'none';
});
tab.style.display = 'block';
vars.permissionList.changed = [];
document.querySelector('.permission-modal').style.display = 'flex';
})
// 접속 인원 모달 - 프로젝트 type, step 변경 이벤트
// document.getElementById('project-type-btn')?.addEventListener('click', (e) => {
// e.stopPropagation();
// document.querySelector('.project-type__list').classList.toggle('--project-list__open');
// })
// document.getElementById('project-step-btn')?.addEventListener('click', (e) => {
// e.stopPropagation();
// document.querySelector('.project-step__list').classList.toggle('--project-list__open');
// })
// document.getElementById('project-type-btn').closest('.modal-wrap').addEventListener('click', (e) => {
// document.querySelector('.project-type__list').classList.remove('--project-list__open');
// document.querySelector('.project-step__list').classList.remove('--project-list__open');
// })
// document.querySelectorAll('.project-type__list_item').forEach(item => {
// item.addEventListener('click', async(e) => {
// let res = await axios.get(`/gsim/setProjectType?project_id=${vars.project_id}&type=${e.target.classList[1].split('__')[1]}`);
// document.querySelector('.project-type__list').classList.remove('--project-list__open');
// let type = e.target.classList[1].split('__')[1];
// let btn = document.getElementById('project-type-btn');
// let kor = '';
// switch (type) {
// case 'construction' : kor = '시공'; break;
// case 'design' : kor = '설계'; break;
// case 'surgest' : kor = '제안'; break;
// case 'research' : kor = '연구'; break;
// case 'support' : kor = '지원'; break;
// case 'center' : kor = '센터'; break;
// }
// btn.innerHTML = `
// <h5 class="project-type__label --type__${type}">${kor}</h5>
// <i class="project-type__icon"></i>`;
// });
// })
// document.querySelectorAll('.project-step__list_item').forEach(item => {
// item.addEventListener('click', async(e) => {
// let res = await axios.get(`/gsim/setProjectStep?project_id=${vars.project_id}&step=${e.target.classList[1].split('__')[1]}`);
// document.querySelector('.project-step__list').classList.remove('--project-list__open');
// let step = e.target.classList[1].split('__')[1];
// let btn = document.getElementById('project-step-btn');
// let kor = '';
// switch (step) {
// case 'active' : kor = '진행'; break;
// case 'done' : kor = '완료'; break;
// case 'stop' : kor = '중지'; break;
// case 'wait' : kor = '대기'; break;
// }
// btn.innerHTML = `
// <h5 class="project-step__label --step__${step}">${kor}</h5>
// <i class="project-step__icon"></i>`;
// });
// })
// 접속 인원 모달 - 로그아웃 버튼 클릭 이벤트
document.querySelector('.archive-modal > .wrap .modal-wrap .modal-body > .connected-users-wrap .btn-wrap .logout-btn')?.addEventListener('click', function() {
window.location.href = '/oauth/logout';
})
// 좌측 트리 우클릭 시 강조 및 컨텍스트 메뉴 열기
// document.querySelector('.archive-main .archive-main-left .tree-container')?.addEventListener('contextmenu', (e) => {
document.querySelector('.archive-main .archive-main-left')?.addEventListener('contextmenu', (e) => {
let target, scope;
if (e.target.classList.contains('countdown')) return;
if (e.target.matches('.tree-item')) {
target = e.target.parentElement;
if (target.matches('.depth2')) scope = 'tree-item-wrap-depth2';
if (target.matches('.depth3')) scope = 'tree-item-wrap-depth3';
} else {
// target = document.querySelector('.archive-main .archive-main-left .tree-container');
target = document.querySelector('.archive-main .archive-main-left');
scope = 'tree';
}
toggleContextFocusBox(true, target, scope);
toggleContextmenu(true, e, scope);
})
// 좌측 트리 타이틀 클릭
document.querySelector('.archive-main .archive-main-left .tree-title')?.addEventListener('click', async (e) => {
let listViewerCover = document.querySelector('.list-viewer-cover');
if (listViewerCover.style.display == 'flex') {
let toggleParams = {
text: '파일 이동 화면에서는 트리 타이틀 버튼의 좌클릭이 제한되며, 우클릭으로 새 폴더 생성만 가능합니다.',
type: 'alertModal'
};
toggleModal(true, toggleParams);
return;
}
let treeTitle = e.target;
changeTreeItemStyle(treeTitle);
listContainer.querySelector('.list-body .list-item-wrap').innerHTML = '';
listContainer.style.display = 'none';
listNotice.style.display = 'flex';
resetViewer();
// 컨트롤 박스 숨김
toggleControlBox(false);
// 251114 김아름, 브라우저 경로 수정
let pageRanderingOption = {
resourcePath: vars.lastMainTreeItem.dataset.resourcePath,
pushState: true,
}
await preparePageRendering(pageRanderingOption);
// 251114 김아름, 주석처리
// // 아카이브 우측 영역 표시
// let option = {
// from: '좌측 트리 타이틀 클릭'
// }
// toggleArchiveMainRight(true, option);
toggleArchiveMainRight(false);
})
/////////////////////////// 컨트롤 박스
// 컨트롤 박스 모드 선택 버튼 클릭 이벤트
document.querySelectorAll('.control-box .file-area-mode-btn').forEach(btn => {
btn.addEventListener('click', async (e) => {
// 초기화
document.querySelector('.control-box .file-area-mode-btn.selected')?.classList.remove('selected');
// 클릭한 버튼을 선택 상태로 변경
let newSelectedBtn = e.currentTarget;
newSelectedBtn.classList.add('selected');
// 클릭한 버튼의 id를 value로 사용
let value = newSelectedBtn.id;
if (vars.lastFileAreaMode == value) {
// 이전에 선택된 모드와 동일하고 vars.mapMode가 normal이면 리턴
if (vars.mapMode == 'normal') return;
// vars.mapMode가 edit인 경우 edit모드에서 빠져나와야 하기 때문에 이전에 선택된 모드와 동일한 지도 표시 모드여도 리턴하지 않고 이어서 진행
}
// 사용자가 직접 컨트롤 박스 모드 선택 버튼을 클릭한 경우 value를 전역변수에 저장
// if (e.isTrusted) vars.lastFileAreaMode = value;
vars.lastFileAreaMode = value;
let clusterListWrap = document.querySelector('.map-container .cluster-list-wrap');
clusterListWrap.style.display = 'none';
let mapContainer = document.querySelector('.list-container .list-body .map-container');
mapContainer.classList.remove('edit-mode');
vars.mapMode = 'normal';
if (value == 'list') {
listContainer.classList.add('list');
listContainer.classList.remove('grid');
} else {
listContainer.classList.add('grid');
listContainer.classList.remove('list');
}
// 파일 영역 모드에 맞게 파일 영역 UI 초기화
initFileAreaUI(value);
let renderOption = {
resourcePath: vars.lastMainTreeItem.dataset.resourcePath,
scope : 'list'
}
await preparePageRendering(renderOption);
await syncGroupStyle();
})
})
//// 플로팅 버전 컨트롤 박스 관련 코드
// // 컨트롤 박스 드래그 관련 변수
// let isControlBoxDragging = false;
// let controlBoxOffsetX = 0;
// let controlBoxOffsetY = 0;
// let currentDragElement = null;
// // 컨트롤 박스 초기 위치 설정
// async function setControlBoxInitialPosition(controlBox) {
// // DB에서 저장된 위치 정보 가져오기 (실제 구현 필요)
// let savedPosition = await loadControlBoxPosition(); // 이 함수는 직접 구현해야 함
// if (savedPosition && savedPosition.leftPercent && savedPosition.topPercent) {
// // 저장된 퍼센티지 위치가 있는 경우
// setControlBoxPositionFromPercent(controlBox, savedPosition.leftPercent, savedPosition.topPercent);
// } else {
// // 저장된 위치가 없는 경우 - 기본 위치 (중앙 하단)
// setControlBoxPositionFromPercent(controlBox);
// }
// }
// // 퍼센티지 값으로 컨트롤 박스 위치 설정
// function setControlBoxPositionFromPercent(controlBox, leftPercent, topPercent) {
// let finalLeftPercent, finalTopPercent;
// if (!leftPercent) {
// let archiveMainLeftWidth = document.querySelector('.archive-main-left').getBoundingClientRect().width;
// let controlBoxWidth = controlBox.getBoundingClientRect().width;
// let left = (archiveMainLeftWidth - controlBoxWidth) / 2;
// finalLeftPercent = `${(left / window.innerWidth) * 100}%`;
// }
// if (!topPercent) {
// let controlBoxHeight = controlBox.getBoundingClientRect().height;
// // finalTopPercent = '95%';
// finalTopPercent = `calc(100% - 2.25rem - ${pxToRem(controlBoxHeight)}rem)`;
// }
// if (leftPercent && topPercent) {
// // 퍼센티지에서 % 제거하고 숫자로 변환
// let leftPercentValue = parseFloat(leftPercent.replace('%', ''));
// let topPercentValue = parseFloat(topPercent.replace('%', ''));
// // 요소 크기를 고려한 최대 퍼센티지 계산
// let boxRect = controlBox.getBoundingClientRect();
// let maxLeftPercent = 100 - (boxRect.width / window.innerWidth * 100);
// let maxTopPercent = 100 - (boxRect.height / window.innerHeight * 100);
// // 퍼센티지 값 경계 체크
// finalLeftPercent = `${Math.max(0, Math.min(leftPercentValue, maxLeftPercent))}%`;
// finalTopPercent = `${Math.max(0, Math.min(topPercentValue, maxTopPercent))}%`;
// }
// // 퍼센티지로 직접 적용
// controlBox.style.left = finalLeftPercent;
// controlBox.style.top = finalTopPercent;
// // controlBox.style.left = leftPercent;
// // controlBox.style.top = topPercent;
// }
// // 포인터 다운 (드래그 시작)
// function handleControlBoxPointerDown(e) {
// isControlBoxDragging = true;
// currentDragElement = e.target.closest('.control-box');
// currentDragElement.querySelector('.drag-handle').style.cursor = 'grabbing';
// // 마우스와 요소의 상대 위치 계산
// let rect = currentDragElement.getBoundingClientRect();
// controlBoxOffsetX = e.clientX - rect.left;
// controlBoxOffsetY = e.clientY - rect.top;
// // 드래그 중 선택 방지
// e.preventDefault();
// // 드래그 중 애니메이션 제거
// currentDragElement.classList.add('dragging');
// }
// // 포인터 이동 (드래그 중)
// function handleControlBoxPointerMove(e) {
// if (!isControlBoxDragging || !currentDragElement) return;
// // 새로운 위치 계산
// let newX = e.clientX - controlBoxOffsetX;
// let newY = e.clientY - controlBoxOffsetY;
// // 화면 경계 체크
// let maxX = window.innerWidth - currentDragElement.offsetWidth;
// let maxY = window.innerHeight - currentDragElement.offsetHeight;
// newX = Math.max(0, Math.min(newX, maxX));
// newY = Math.max(0, Math.min(newY, maxY));
// // 위치 적용
// // currentDragElement.style.left = newX + 'px';
// // currentDragElement.style.top = newY + 'px';
// //// 퍼센티지로 적용 추가
// // 현재 화면 해상도
// const screenWidth = window.innerWidth;
// const screenHeight = window.innerHeight;
// // 퍼센티지 계산 (소수점 2자리까지)
// const leftPercent = parseFloat((newX / screenWidth * 100).toFixed(2));
// const topPercent = parseFloat((newY / screenHeight * 100).toFixed(2));
// currentDragElement.style.left = leftPercent + '%';
// currentDragElement.style.top = topPercent + '%';
// }
// // 포인터 업 (드래그 종료)
// function handleControlBoxPointerUp(e) {
// if (!isControlBoxDragging) return;
// isControlBoxDragging = false;
// if (currentDragElement) {
// currentDragElement.querySelector('.drag-handle').style.cursor = 'grab';
// currentDragElement.classList.remove('dragging');
// // 드래그 종료 시 위치 저장 (console.log로 표시)
// saveControlBoxPosition(currentDragElement);
// currentDragElement = null;
// }
// }
// // DB에서 저장된 위치 정보 가져오기 (구현 필요)
// async function loadControlBoxPosition() {
// let position = null;
// let userId = JSON.parse(vars.userInfoString).user_id;
// let getControlBoxPositionRes = await axios.get(`${vars.path_name}/getControlBoxPosition`, { params: { userId: userId } });
// if (getControlBoxPositionRes.data.message == 'getControlBoxPosition_success' && getControlBoxPositionRes.data.result.floating_box_position) {
// position = getControlBoxPositionRes.data.result.floating_box_position;
// }
// return JSON.parse(position);
// }
// async function saveControlBoxPosition(element) {
// // 현재 화면 해상도
// let screenWidth = window.innerWidth;
// let screenHeight = window.innerHeight;
// let positionData = {
// // 퍼센티지 값
// leftPercent: element.style.left,
// topPercent: element.style.top,
// // 화면 해상도 정보
// screenWidth: screenWidth,
// screenHeight: screenHeight
// }
// let params = {
// userId: JSON.parse(vars.userInfoString).user_id,
// positionData: JSON.stringify(positionData)
// };
// await axios.post(`${vars.path_name}/setControlBoxPosition`, { params: params });
// }
///////////////////////////
// 가운데 파일 영역 우클릭 시 강조 및 컨텍스트 메뉴 열기
document.querySelector('.archive-main .archive-main-center .list-container')?.addEventListener('contextmenu', (e) => {
let target, scope;
if (e.target.matches('.list-item-data')) {
target = e.target.parentElement;
scope = 'list-item';
} else if (e.target.matches('.convert-btn')) {
target = e.target.parentElement.parentElement;
scope = 'list-item';
} else if (e.target.matches('.grid-item') || e.target.closest('.grid-item')) {
if (e.target.matches('.grid-item')) target = e.target;
else target = e.target.closest('.grid-item');
scope = 'grid-item';
} else if (e.target.matches('.map-item')) {
target = e.target;
scope = 'map-item';
} else {
target = document.querySelector('.archive-main .archive-main-center .list-container');
scope = 'list';
}
// 갤러리 폴더에서 위치 수정 모드가 실행중인 경우 리턴
if (vars.mapMode == 'edit') return;
toggleContextFocusBox(true, target, scope);
toggleContextmenu(true, e, scope);
})
// 휴지통 모달창 리스트 우클릭 시 강조 및 컨텍스트 메뉴 열기
document.querySelector('.recycle-bin-modal .list-container')?.addEventListener('contextmenu', (e) => {
if (e.target.matches('.list-item-data')) {
let target = e.target.parentElement;
let scope = 'list-item';
toggleContextFocusBox(true, target, scope);
toggleContextmenu(true, e, scope);
}
})
// // 가운데 리스트 드래그 - 지도 화면에서는 작동 안함
// // let listBody = document.querySelector('.archive-main-center .list-body');
// let listBody = document.querySelector('.list-container .list-body');
// let draggingStartTarget, draggingEndTarget;
// let draggingStart = false, isDragging = false;
// let isCtrl = false, isShift = false;
// let processedItems = new Set();
// let originalSelection = new Set();
// let multiSelectBox = document.createElement('div');
// multiSelectBox.style.position = 'absolute';
// multiSelectBox.style.border = '1px solid rgba(0, 120, 215, 1)';
// multiSelectBox.style.background = 'rgba(0, 120, 215, 0.2)';
// multiSelectBox.style.zIndex = '9999';
// multiSelectBox.style.pointerEvents = 'none';
// multiSelectBox.style.display = 'none';
// listBody?.appendChild(multiSelectBox);
// vars.multiSelectListItemArr = [];
// vars.preventNextClick = false;
// let startX = 0;
// let startY = 0;
// let prevClientX = 0;
// let prevClientY = 0;
// let autoScrollDirection = null;
// let autoScrollFrameId = null;
// const scrollSpeed = 8;
// const scrollMargin = 36;
// function autoScrollLoop() {
// if (!autoScrollDirection) return;
// // 더 작으면 느리고 부드러움
// if (autoScrollDirection === 'down') {
// listBody.scrollTop += scrollSpeed;
// } else if (autoScrollDirection === 'up') {
// listBody.scrollTop -= scrollSpeed;
// }
// autoScrollFrameId = requestAnimationFrame(autoScrollLoop);
// }
// listBody?.addEventListener('pointerdown', (e) => {
// // archive-main 화면이 아닌 경우 리턴
// if (document.querySelector('.archive-main').style.display !== 'flex') return;
// // 마우스 좌클릭이 아닌 경우 리턴
// if (e.button !== 0) return;
// // 갤러리 폴더 지도화면인 경우 리턴
// if (listBody.querySelector('.map-container').style.display === 'flex') return;
// draggingStartTarget = e.target;
// draggingStart = true;
// isDragging = false;
// isCtrl = e.ctrlKey;
// isShift = e.shiftKey;
// processedItems.clear();
// originalSelection = new Set(vars.multiSelectListItemArr);
// vars.preventNextClick = false;
// // if (isCtrl) {
// // vars.lastListItem = e.target.closest('.list-item');
// // } else {
// // listBody.querySelectorAll('.list-item').forEach(listItem => {
// // listItem.classList.remove('selected');
// // listItem.classList.remove('group-style');
// // })
// // vars.multiSelectListItemArr = [];
// // }
// const dragStartListItem = e.target.closest('.list-item');
// if (dragStartListItem && !isShift) {
// vars.lastListItem = dragStartListItem;
// }
// if (!isCtrl) {
// // Ctrl이 아닐 경우 기존 선택 모두 해제
// listBody.querySelectorAll('.list-item').forEach(listItem => {
// listItem.classList.remove('selected');
// listItem.classList.remove('group-style');
// });
// vars.multiSelectListItemArr = [];
// }
// const bodyRect = listBody.getBoundingClientRect();
// startX = e.clientX + listBody.scrollLeft - bodyRect.left;
// startY = e.clientY + listBody.scrollTop - bodyRect.top;
// prevClientX = e.clientX;
// prevClientY = e.clientY;
// Object.assign(multiSelectBox.style, {
// left: `${startX}px`,
// top: `${startY}px`,
// width: `0px`,
// height: `0px`,
// display: 'none'
// });
// });
// listBody?.addEventListener('pointermove', (e) => {
// // archive-main 화면이 아닌 경우 리턴
// if (document.querySelector('.archive-main').style.display !== 'flex') return;
// // 마우스 좌클릭이 아닌 경우 리턴
// if (e.buttons !== 1) return;
// // list-body에서 마우스 드래그가 시작된게 아닌 경우 리턴
// if (!draggingStart) return;
// if (e.clientX === prevClientX && e.clientY === prevClientY) return;
// prevClientX = e.clientX;
// prevClientY = e.clientY;
// const listBodyRect = listBody.getBoundingClientRect();
// const currX = e.clientX + listBody.scrollLeft - listBodyRect.left;
// const currY = e.clientY + listBody.scrollTop - listBodyRect.top;
// const dx = currX - startX;
// const dy = currY - startY;
// if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
// isDragging = true;
// vars.preventNextClick = true;
// const left = Math.min(startX, currX);
// const top = Math.min(startY, currY);
// const width = Math.abs(dx);
// const height = Math.abs(dy);
// Object.assign(multiSelectBox.style, {
// left: `${left}px`,
// top: `${top}px`,
// width: `${width}px`,
// height: `${height}px`,
// display: 'block'
// });
// const boxRect = multiSelectBox.getBoundingClientRect();
// document.querySelectorAll('.list-body .list-item .wrap').forEach(elem => {
// const rect = elem.getBoundingClientRect();
// const overlaps = !(
// rect.right < boxRect.left ||
// rect.left > boxRect.right ||
// rect.bottom < boxRect.top ||
// rect.top > boxRect.bottom
// );
// let listItem = elem.parentElement;
// if (isCtrl) {
// if (overlaps && !processedItems.has(listItem)) {
// processedItems.add(listItem);
// // ✅ 토글: 원래 선택되어 있었으면 해제, 아니면 선택
// if (originalSelection.has(listItem)) {
// listItem.classList.remove('selected');
// listItem.classList.remove('group-style');
// vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => item !== listItem);
// } else {
// listItem.classList.add('selected');
// listItem.classList.add('group-style');
// vars.multiSelectListItemArr.push(listItem);
// }
// }
// // ✅ 박스에서 벗어난 항목 복원
// if (!overlaps && processedItems.has(listItem)) {
// processedItems.delete(listItem);
// if (originalSelection.has(listItem)) {
// listItem.classList.add('selected');
// listItem.classList.add('group-style');
// if (!vars.multiSelectListItemArr.includes(listItem)) {
// vars.multiSelectListItemArr.push(listItem);
// }
// } else {
// listItem.classList.remove('selected');
// listItem.classList.remove('group-style');
// vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => item !== listItem);
// }
// }
// } else {
// listItem.classList.toggle('selected', overlaps);
// listItem.classList.toggle('group-style', overlaps);
// if (overlaps && !vars.multiSelectListItemArr.includes(listItem)) {
// vars.multiSelectListItemArr.push(listItem);
// }
// }
// });
// if (e.clientY > listBodyRect.bottom - scrollMargin) {
// if (autoScrollDirection !== 'down') {
// cancelAnimationFrame(autoScrollFrameId);
// autoScrollDirection = 'down';
// autoScrollLoop();
// }
// } else if (e.clientY < listBodyRect.top + scrollMargin) {
// if (autoScrollDirection !== 'up') {
// cancelAnimationFrame(autoScrollFrameId);
// autoScrollDirection = 'up';
// autoScrollLoop();
// }
// } else {
// if (autoScrollDirection !== null) {
// cancelAnimationFrame(autoScrollFrameId);
// autoScrollDirection = null;
// }
// }
// }
// });
// document.addEventListener('pointerup', async (e) => {
// // list-body에서 마우스 드래그가 시작된게 아닌 경우 리턴
// if (!draggingStart) return;
// if (vars.lastHeaderBtn) targetFocus(vars.lastHeaderBtn);
// if (vars.lastMainTreeItem) targetFocus(vars.lastMainTreeItem);
// if (isShift && isDragging) {
// const dragStartListItem = draggingStartTarget.closest('.list-item');
// if (dragStartListItem) {
// vars.lastListItem = dragStartListItem;
// }
// }
// multiSelectBox.style.display = 'none';
// draggingEndTarget = e.target;
// draggingStart = false;
// isDragging = false;
// isCtrl = false;
// isShift = false;
// // // 드래그가 끝나고 pointerup 이벤트가 발생하는 시점에 selected 클래스가 있는 listItem만 필터링
// // vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => item.classList.contains('selected'));
// // vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => !item.classList.contains('disabled'));
// // vars.multiSelectListItemArr.forEach(item => {
// // console.log(item);
// // item.classList.remove('non-selected');
// // })
// // await syncGroupStyle();
// // 드래그가 아이템 바깥 영역에서만 진행되고, 드래그에 포함된 아이템이 하나도 없을 때 뷰어 초기화
// if (((draggingStartTarget.matches('.list-body') && draggingEndTarget.matches('.list-body'))
// || (draggingStartTarget.matches('.list-item-wrap') && draggingEndTarget.matches('.list-item-wrap')))
// && vars.multiSelectListItemArr.length == 0)
// {
// // 모든 아이템에서 non-selected 클래스 삭제
// document.querySelectorAll('.grid-item').forEach(gridItem => {
// gridItem.classList.remove('non-selected')
// })
// // if (viewerContainer.style.display == 'flex') resetViewer();
// resetViewer();
// vars.lastListGroupTarget = undefined;
// vars.lastListItem = undefined;
// // vars.lastSelectTarget = undefined;
// vars.lastContextTarget = undefined;
// // userSelected 삭제
// let me = JSON.parse(vars.userInfoString);
// me.selected = undefined;
// vars.socket.emit('fileSelect',{me : me});
// } else {
// // 모든 아이템에 non-selected 클래스 추가
// document.querySelectorAll('.grid-item').forEach(gridItem => {
// gridItem.classList.add('non-selected')
// })
// }
// // 드래그 시작한 대상과 종료된 대상이 다르면 뷰어 초기화
// if (draggingStartTarget != draggingEndTarget) {
// vars.lastListItem = undefined;
// vars.lastListGroupTarget = undefined;
// if (viewerContainer.style.display == 'flex') resetViewer();
// }
// // 드래그가 끝나고 pointerup 이벤트가 발생하는 시점에 selected 클래스가 있는 listItem만 필터링
// vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => item.classList.contains('selected'));
// // vars.multiSelectListItemArr = vars.multiSelectListItemArr.filter(item => !item.classList.contains('disabled'));
// // vars.multiSelectListItemArr에 포함된 아이템에서 non-selected 클래스 삭제
// vars.multiSelectListItemArr.forEach(item => {
// item.classList.remove('non-selected');
// })
// await syncGroupStyle();
// // 드래그 시작한 대상의 부모요소와 종료된 대상의 부모요소가 다르면 뷰어 초기화
// // if (draggingStartTarget.parentElement != draggingEndTarget.parentElement) {
// // if (viewerContainer.style.display == 'flex') resetViewer();
// // }
// vars.lastSelectTarget = vars.multiSelectListItemArr[0];
// cancelAnimationFrame(autoScrollFrameId);
// autoScrollDirection = null;
// });
// 휴지통 모달창 헤더 - 정렬 클릭 이벤트
document.querySelector('.recycle-bin-modal .list-header-area')?.querySelectorAll('div').forEach(ele=>{
ele.addEventListener('click',async (e)=>{
let headers = document.querySelector('.recycle-bin-modal .list-header-area').querySelectorAll('div');
headers.forEach(h=>{
if (e.target == h) {
if (h.classList.contains('sort-asc') && !h.classList.contains('sort-desc')) {
h.classList.remove('sort-asc');
h.classList.add('sort-desc');
} else if (!h.classList.contains('sort-asc') && h.classList.contains('sort-desc')) {
h.classList.add('sort-asc');
h.classList.remove('sort-desc');
} else if (!h.classList.contains('sort-asc') && !h.classList.contains('sort-desc')) {
h.classList.add('sort-asc');
}
} else {
h.classList.remove('sort-asc', 'sort-desc');
}
})
if(vars.curSortCol_bin != e.target.classList[0]){
vars.curSortCol_bin = `${e.target.classList[0]}`;
vars.curSortOrder_bin = `desc`;
}
vars.curSortOrder_bin = (vars.curSortOrder_bin == 'asc') ? `desc` : `asc`;
await prepareRecycleBinRendering();
})
})
// 일반 폴더 헤더 - 정렬 클릭 이벤트
document.querySelector('.archive-main-center .list-header-area')?.querySelectorAll('div').forEach(ele=>{
ele.addEventListener('click',async (e)=>{
let headers = document.querySelector('.archive-main-center .list-header-area').querySelectorAll('div');
headers.forEach(h=>{
if (e.target == h) {
if (h.classList.contains('sort-asc') && !h.classList.contains('sort-desc')) {
h.classList.remove('sort-asc');
h.classList.add('sort-desc');
} else if (!h.classList.contains('sort-asc') && h.classList.contains('sort-desc')) {
h.classList.add('sort-asc');
h.classList.remove('sort-desc');
} else if (!h.classList.contains('sort-asc') && !h.classList.contains('sort-desc')) {
h.classList.add('sort-asc');
}
} else {
h.classList.remove('sort-asc', 'sort-desc');
}
})
if(vars.curSortCol != e.target.classList[0]){
vars.curSortCol = `${e.target.classList[0]}`;
vars.curSortOrder = `desc`;
}
vars.curSortOrder = (vars.curSortOrder == 'asc') ? `desc` : `asc`;
let renderOption = {
resourcePath: vars.lastMainTreeItem.dataset.resourcePath,
scope : 'list'
}
await preparePageRendering(renderOption);
})
})
// 갤러리 폴더 헤더 - 분류 라디오버튼 클릭 이벤트
document.querySelector('.control-area .category .radio-btn-wrap')?.addEventListener('click', async function(e) {
let clickedInput = e.target.querySelector('input');
// 라디오버튼이 이미 체크된 상태인 경우 리턴
if (clickedInput.checked == true) return;
clickedInput.checked = true;
let sortRadioBtns = document.querySelectorAll('.control-area .sort .radio-btn-wrap .radio-btn');
if (clickedInput.value == 'none') {
//// 분류 없음 선택
// 정렬 라디오버튼 비활성화
sortRadioBtns.forEach(sortRadioBtn => {
sortRadioBtn.classList.add('disabled');
})
document.querySelectorAll('.thumbnail-title').forEach(title => {
title.style.display = 'none';
});
} else {
//// 분류 파일명/등록자/등록일자/용량 선택
// 정렬 라디오버튼 활성화
sortRadioBtns.forEach(sortRadioBtn => {
sortRadioBtn.classList.remove('disabled');
})
// 분류, 정렬 기준으로 그리드 리스트 다시 렌더링
vars.curSortCol = clickedInput.value;
vars.curSortOrder = document.querySelector('input[type="radio"][name="sort"]:checked').value;
//// 썸네일 모드에서 vars.curSortCol이 none(라벨 숨김)으로 저장된 채 리스트 모드로 바꾼 뒤
//// 바로 파일명 정렬을 하면 무시되는 문제가 생기기 때문에 vars.curSortCol이 none인 경우 name으로 강제 저장
if (vars.curSortCol == 'none') vars.curSortCol = 'name';
let renderOption = {
resourcePath: vars.lastMainTreeItem.dataset.resourcePath,
scope : 'list'
}
await preparePageRendering(renderOption);
}
})
// 갤러리 폴더 헤더 - 정렬 라디오버튼 클릭 이벤트
document.querySelector('.control-area .sort .radio-btn-wrap')?.addEventListener('click', async function(e) {
if (e.target.classList.contains('disabled')) return;
let clickedInput = e.target.querySelector('input');
// 라디오버튼이 이미 체크된 상태인 경우 리턴
if (clickedInput.checked == true) return;
clickedInput.checked = true;
vars.curSortOrder = clickedInput.value;
vars.curSortCol = document.querySelector('input[type="radio"][name="category"]:checked').value;
//// 썸네일 모드에서 vars.curSortCol이 none(라벨 숨김)으로 저장된 채 리스트 모드로 바꾼 뒤
//// 바로 파일명 정렬을 하면 무시되는 문제가 생기기 때문에 vars.curSortCol이 none인 경우 name으로 강제 저장
if (vars.curSortCol == 'none') vars.curSortCol = 'name';
let renderOption = {
resourcePath: vars.lastMainTreeItem.dataset.resourcePath,
scope : 'list'
}
await preparePageRendering(renderOption);
})
// 갤러리 폴더 헤더 - 슬라이더 조절 이벤트
let gridSizeSlider = document.querySelector('.control-area .thumbnail .input-wrap .slider-input .thumbnail-size-slider');
gridSizeSlider?.addEventListener('input', function(e) {
let value = Number(gridSizeSlider.value);
changeSliderTrackColor(gridSizeSlider, gridSizeSlider.matches(':hover'));
changeGridItemSize(value);
})
// 갤러리 폴더 헤더 - 슬라이더 양 옆 + - 버튼 클릭 이벤트
document.querySelector('.control-area .thumbnail .input-wrap .btn.plus')?.addEventListener('click', function(e) {
let value = Number(gridSizeSlider.value);
if (value == -1) return;
if (value != -1) value = value + 1;
gridSizeSlider.value = value;
changeSliderTrackColor(gridSizeSlider, false);
changeGridItemSize(value);
})
document.querySelector('.control-area .thumbnail .input-wrap .btn.minus')?.addEventListener('click', function(e) {
let value = Number(gridSizeSlider.value);
if (value == -10) return;
if (value != -10) value = value - 1;
gridSizeSlider.value = value;
changeSliderTrackColor(gridSizeSlider, false);
changeGridItemSize(value);
})
// 갤러리 폴더 헤더 - 슬라이더 마우스호버 이벤트
gridSizeSlider?.addEventListener('mouseenter', () => changeSliderTrackColor(gridSizeSlider, true));
gridSizeSlider?.addEventListener('mouseleave', () => changeSliderTrackColor(gridSizeSlider, false));
// 갤러리 폴더 헤더 - 슬라이더 시작지점에서 커스텀thumb지점까지 색깔 채우는 함수
function changeSliderTrackColor(slider, isHover) {
let value = slider.value;
value = -(value);
let min = -(slider.min);
let max = -(slider.max);
let percent = ((value - min) / (max - min)) * 100;
let color = (isHover) ? '#1E5149' : '#a5b9b6';
slider.style.background = `linear-gradient(to right, ${color} ${percent}%, #ccc ${percent}%)`;
}
// 갤러리 폴더 헤더 - 슬라이더 조절 시 또는 양 옆 + - 버튼 클릭 시 그리드 크기 변경하는 함수
function changeGridItemSize(value) {
value = -(value);
// 썸네일 크기 클수록 썸네일 간격 넓게 적용
// if (value == 1) document.documentElement.style.setProperty('--thumbnail-gap', '1rem');
// if (value == 2) document.documentElement.style.setProperty('--thumbnail-gap', '0.75rem');
// if (value >= 3) document.documentElement.style.setProperty('--thumbnail-gap', '0.5rem');
let thumbnailSizeValue = `0 0 calc((100% - ${value-1} * var(--thumbnail-gap)) / ${value})`;
document.documentElement.style.setProperty('--thumbnail-size', thumbnailSizeValue);
document.documentElement.style.setProperty('--thumbnail-size-value', value);
}
// 갤러리 폴더 바디 - 기본지도 버튼 클릭 이벤트
document.querySelector('.map-container .base-map-btn')?.addEventListener('click', async function(e) {
document.querySelector('.map-container .base-map-btn').classList.toggle('on');
document.querySelector('.map-container .base-map').classList.toggle('on');
})
// 갤러리 폴더 바디 - 기본지도 메뉴 닫기 클릭 이벤트
document.querySelector('.map-container .base-map .close')?.addEventListener('click', async function(e) {
document.querySelector('.map-container .base-map-btn').classList.remove('on');
document.querySelector('.map-container .base-map').classList.remove('on');
})
// 갤러리 폴더 바디 - 기본지도 메뉴 라디오버튼 클릭 이벤트
document.querySelectorAll('.map-container .base-map .radio-btn-wrap .radio-btn').forEach(radioBtn => {
radioBtn.addEventListener('click', async function(e) {
let clickedInput = e.target.querySelector('input');
// 라디오버튼이 이미 체크된 상태인 경우 리턴
if (clickedInput.checked == true) return;
clickedInput.checked = true;
let value = document.querySelector('input[type="radio"][name="base-map"]:checked').value;
// 지도 생성 시 일반 지도 1개만 등록 후 지도 url 변경 방식
// let source = vars.baseLayer.getSource();
// console.log(vars[value]);
// source.setUrl(vars[value]);
// source.refresh();
// 지도 생성 시 3가지 지도 모두 등록 후 교체 방식
vars.roadLayer.setVisible(value === 'road');
vars.satelliteLayer.setVisible(value === 'satellite');
vars.hybridLayer.setVisible(value === 'hybrid');
})
})
let infoWrap = document.querySelector('.archive-main .archive-main-right .viewer-container .info-wrap');
let memo = infoWrap?.querySelector('.memo');
let apiBtn = infoWrap?.querySelector('.memo .header .wrap .api-btn');
let editBtn = infoWrap?.querySelector('.memo .header .wrap .edit-btn');
let aiBtn = infoWrap?.querySelector('.memo .header .wrap .ai-btn');
let editMessage = infoWrap?.querySelector('.memo .header .wrap .message');
let bodyWrap = infoWrap?.querySelector('.memo .body .wrap');
let bodyTextarea = infoWrap?.querySelector('.memo .body .wrap .textarea');
let bodyMessage = infoWrap?.querySelector('.memo .body .wrap .message');
// 우측 요약 textarea 입력 이벤트
bodyTextarea?.addEventListener('input', async (e) => {
if (bodyTextarea.value == '') bodyMessage.style.color = '#777';
else bodyMessage.style.color = '#ddd';
})
// 우측 요약 수정/저장 버튼 클릭 이벤트
editBtn?.addEventListener('click', async(e) => {
editBtn.classList.toggle('save');
if(editBtn.matches('.save')) {
editBtn.querySelector('.text').innerText = '저장';
bodyWrap.style.border = '1px solid #000';
bodyTextarea.disabled = false;
bodyMessage.innerText = '내용을 작성/수정한 후 저장 버튼을 눌러주세요.';
// text 입력 이벤트
bodyTextarea.addEventListener('input', () => {
const resourcePath = vars.lastListItem.dataset.resourcePath;
vars.tempMemo[resourcePath] = bodyTextarea.value;
})
}else {
editBtn.querySelector('.text').innerText = '수정';
bodyWrap.style.border = '1px solid #ddd';
bodyTextarea.disabled = true;
bodyMessage.innerText = '수정 버튼을 눌러 내용을 작성/수정할 수 있습니다.';
if (bodyTextarea.value == '' ) bodyMessage.style.color = '#777';
else bodyMessage.style.color = '#ddd';
editMessage.style.transition = '1s';
editMessage.style.opacity = '1';
let timeoutId = setTimeout(async function() {
editMessage.style.opacity = '0';
clearTimeout(timeoutId);
}, 1500);
let userInfoString = vars.userInfoString;
let resourcePath = vars.lastListItem.dataset.resourcePath;
let memo = bodyTextarea.value;
let dataId = vars.lastListItem.dataset.dataId;
let params = {
resourcePath: resourcePath,
memo: memo
};
let res = await axios.post(`${vars.path_name}/updateMemoInfo`, { userInfoString, params });
if(res.data.message == 'updateMemo_success') {
delete vars.tempMemo[resourcePath];
}
}
})
// ai 버튼 클릭 이벤트
// rpsm test 용
// aiBtn?.addEventListener('click', async event => {
// const element = apiBtn.children[0];
// const container = element.closest('.viewer-container');
// const formData = new FormData();
// // 1. 선택한 파일의 과업명 추출
// for(let i=0; i<vars.multiSelectListItemArr.length; i++) {
// const listItem = vars.multiSelectListItemArr[i];
// if(listItem.className.includes('selected')) {
// let getDataInfoParams = {
// dataIdArr: [listItem.dataset.id],
// storageType: vars.storageType,
// isRemoved: false,
// debug: 'AI 변환 진행 중'
// };
// let getDataInfoRes = await axios.post(`${vars.path_name}/getDataInfo`, {params: getDataInfoParams});
// if (getDataInfoRes.data.message == 'getDataInfo_success') {
// const result = getDataInfoRes.data.result;
// if(container.dataset.dataId == result.data_id) {
// // 진행 중 표시 & 현재 버튼만 로딩 반영
// updateAiButtonState(result.data_id, 'loading', 'gemini');
// // 파일 확장자 구분
// let fileName;
// if(result.path5) {
// fileName = result.path5;
// }else {
// fileName = result.path4;
// }
// const ext = fileName.split('.').pop().toLowerCase();
// const extArr = ['png', 'jpg', 'jpeg', 'pdf', 'doc', 'docx', 'hwp', 'hwpx'];
// let isExt;
// if (extArr.includes(ext)) isExt = ext;
// if(!result.preview_key || !isExt) {
// alert('pdf, 이미지(png, jpg, jpeg) 및 변환된 hwp, hwpx, doc, docx 파일만 AI 요약이 가능합니다.');
// updateAiButtonState(result.data_id, 'initial');
// return;
// }
// const resourcePath = listItem.getAttribute('data-resource-path');
// // 2. 프롬프트 파일
// // 과업명
// const project_name = result.ai_summary;
// // 기본 프롬프트
// const basePrompt = await (await fetch('/main/jsm/archive/ai_prompt/rpsm_prompt.txt')).text();
// // JSON 헤더 + 기본 프롬프트 결합
// const jsonBlock = JSON.stringify({
// project_name: project_name,
// as_of_kst: new Date().toLocaleString('sv-SE', {timeZone: 'Asia/Seoul'})
// });
// const fullPrompt = `${jsonBlock}\n\n${basePrompt}`;
// // 업로드용 prompt_file 생성 후 formdata에 첨부
// const promptFile = new File([fullPrompt], 'prompt.txt', {type: 'text/plain'});
// formData.append('prompt_file', promptFile);
// let params = {
// projectId: result.project_id,
// objectKey: result.preview_key,
// storageType: result.storage_type,
// userInfoString: vars.userInfoString,
// resourcePath: resourcePath,
// dataId: result.data_id,
// type: 'gemini'
// };
// await axios.post(`${vars.path_name}/summarizeAI`, formData, {params: params });
// }
// }
// }
// }
// })
// const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
// aiBtn?.addEventListener('click', async (e) => {
// 준비중 안내 모달 -> ai 기능 완료되면 제거
// let toggleParams = {
// title: '안내',
// text: 'AI 기능은 준비중입니다. 추후 업데이트 예정입니다.',
// type: 'temp_ai',
// };
// toggleModal(true, toggleParams);
// const formData = new FormData();
// // 1. 프롬프트 파일
// const promptRes = await fetch('/main/jsm/archive/ai_prompt/prompt.txt');
// const promptBlob = await promptRes.blob();
// const promptFile = new File([promptBlob], 'prompt.txt', { type: 'text/plain' });
// formData.append('file', promptFile);
// // 2. json 파일
// const jsonRes = await fetch('/main/jsm/archive/ai_prompt/prompt.json');
// const jsonBlob = await jsonRes.blob();
// const jsonFile = new File([jsonBlob], 'prompt.json', { type: 'application/json'});
// formData.append('file', jsonFile);
// // 3. 선택한 파일
// const fileObj = vars.currentTreeObject.file;
// const keys = Object.keys(fileObj);
// for(let i=0; i<vars.multiSelectListItemArr.length; i++) {
// let listItem = vars.multiSelectListItemArr[i];
// let dataId = listItem.dataset.id;
// const dataResourcePath = listItem.getAttribute('data-resource-path');
// const fileName = dataResourcePath.split('/').pop();
// // 확장자 검사
// const ext = fileName.split('.').pop().toLowerCase();
// if (ext !== 'pdf') {
// alert(" PDF 파일만 AI요약이 가능합니다.");
// return;
// }
// const matchedKey = keys.find(key => key == fileName);
// const resourcePath = fileObj[matchedKey].resourcePath;
// let getDataInfoParams = {
// userInfoString: vars.userInfoString,
// storageType: vars.storageType,
// dataIdArr: [dataId],
// isRemoved: false,
// debug: 'ai 변환 진행 중'
// }
// let getDataInfoRes = await axios.post(`${vars.path_name}/getDataInfo`, { params: getDataInfoParams });
// if(getDataInfoRes.data.message == 'getDataInfo_success') {
// let result = getDataInfoRes.data.result;
// let params = {
// projectId: result.project_id,
// objectKey: result.object_key,
// storageType: result.storage_type,
// userInfoString: vars.userInfoString,
// resourcePath: resourcePath,
// dataId: result.data_id,
// depth1: extractPathByLength(resourcePath, 1),
// depth2: extractPathByLength(resourcePath, 2),
// depth3: extractPathByLength(resourcePath, 3),
// type: 'gemini',
// }
// const aiRes = await axios.post(`${vars.path_name}/summarizeAI`, formData, { params: params });
// }
// }
// })
// 외부 API AI
// apiBtn?.addEventListener('click', async event => {
// const element = apiBtn.children[0];
// const container = element.closest('.viewer-container');
// const formData = new FormData();
// // 1. 프롬프트 파일
// const promptRes = await fetch('/main/jsm/archive/ai_prompt/prompt.txt');
// const promptBlob = await promptRes.blob();
// const promptFile = new File([promptBlob], 'prompt.txt', {type: 'text/plain'});
// formData.append('prompt_file', promptFile);
// // 2. 선택한 파일
// for (let i=0; i<vars.multiSelectListItemArr.length; i++) {
// const listItem = vars.multiSelectListItemArr[i];
// if (listItem.className.includes('selected')) {
// let getDataInfoParams = {
// userInfoString: vars.userInfoString,
// storageType: vars.storageType,
// dataIdArr: [listItem.dataset.id],
// isRemoved: false,
// debug: "'summarizeAi'에서 실행"
// }
// let getDataInfoRes = await axios.post(`${vars.path_name}/getDataInfo`, { params: getDataInfoParams});
// if (getDataInfoRes.data.message == 'getDataInfo_success') {
// const result = getDataInfoRes.data.result;
// // 파일 확장자 구분
// let fileName;
// }
// }
// }
// })
// const apiBtn = infoWrap.querySelectorAll('.memo .header .wrap .api-btn');
apiBtn?.addEventListener('click', async event => {
// element.addEventListener('click', async function(event) {
const element = apiBtn.children[0];
const container = element.closest('.viewer-container');
const formData = new FormData();
// 1. 프롬프트 파일
const promptRes = await fetch('/main/jsm/archive/ai_prompt/prompt.txt');
const promptBlob = await promptRes.blob();
const promptFile = new File([promptBlob], 'prompt.txt', { type: 'text/plain'});
formData.append('prompt_file', promptFile);
// 2. 선택한 파일
for(let i=0; i<vars.multiSelectListItemArr.length; i++) {
const listItem = vars.multiSelectListItemArr[i];
if(listItem.className.includes('selected')) {
let getDataInfoParams = {
dataIdArr: [listItem.dataset.id],
storageType: vars.storageType,
isRemoved: false,
debug: 'AI 변환 진행 중'
};
let getDataInfoRes = await axios.post(`${vars.path_name}/getDataInfo`, { params: getDataInfoParams } );
if (getDataInfoRes.data.message == 'getDataInfo_success') {
const result = getDataInfoRes.data.result;
if(container.dataset.dataId == result.data_id) {
// 진행중 표시 & 현재 버튼만 로딩 반영
updateAiButtonState(result.data_id, 'loading');
// 파일 확장자 구분
let fileName;
if(result.path5) {
fileName = result.path5;
}else {
fileName = result.path4;
}
const ext = fileName.split('.').pop().toLowerCase();
const extArr = ['png', 'jpg', 'jpeg', 'pdf', 'doc', 'docx', 'hwp', 'hwpx'];
let isExt;
if(extArr.includes(ext)) isExt = ext;
if(!result.preview_key || !isExt) {
alert('pdf, 이미지(png, jpg, jpeg) 및 변환된 hwp, hwpx, doc, docx 파일만 AI 요약이 가능합니다.');
updateAiButtonState(result.data_id, 'initial');
return;
}
const resourcePath = listItem.getAttribute('data-resource-path');
let params = {
projectId: result.project_id,
objectKey: result.preview_key,
storageType: result.storage_type,
userInfoString: vars.userInfoString,
resourcePath: resourcePath,
dataId: result.data_id,
type: 'outer'
};
await axios.post(`${vars.path_name}/summarizeAI`, formData, { params: params });
}
}
}
}
// })
})
// 우측 정보영역 토글 버튼 클릭 이벤트
infoWrap?.querySelector('.separator .toggle-btn').addEventListener('click', async (e) => {
infoWrap.classList.toggle('open');
infoWrap.classList.toggle('close');
})
// 우측 미리보기 10페이지 안내 버튼 클릭 이벤트
// document.querySelector('.archive-main .archive-main-right .viewer-container .viewer-wrap .thumb-alert')?.addEventListener('click', async (e) => {
// e.target.classList.toggle('compact');
// });
// 우측 미리보기 새 창으로 열기 버튼 클릭 이벤트
document.querySelector('.archive-main .archive-main-right .viewer-container .viewer-header .btn')?.addEventListener('click', async () => {
openNewWindowViewer();
});
// 우측 툴바 이전/다음 버튼 클릭 이벤트
// document.querySelectorAll('.archive-main .archive-main-right .viewer-container .toolbar .toolbar-container .btn').forEach(btn => {
// btn.addEventListener('click', function(e) {
// // disabled 클래스를 가지고 있으면 리턴
// if (e.target.classList.contains('disabled')) return;
// // 현재 선택된 아이템의 인덱스를 기준으로 이전 버튼 클릭하면 이전 인덱스 아이템을, 다음 버튼을 클릭하면 다음 인덱스 아이템을 vars.lastListItem에 저장
// let listItemArr = [...listContainer.querySelector('.list-body .list-item-wrap').children];
// let currentIdx = listItemArr.indexOf(vars.lastListItem);
// if (e.target.classList.contains('prev-btn')) vars.lastListItem = listItemArr[currentIdx-1];
// if (e.target.classList.contains('next-btn')) vars.lastListItem = listItemArr[currentIdx+1];
// // 모든 아이템 스타일 초기화
// document.querySelectorAll('.list-item').forEach((elem) => {
// elem.classList.remove('selected');
// elem.classList.remove('group-style');
// });
// // 버튼 클릭으로 선택된 아이템 뷰어에 표시
// let resourcePath = vars.lastListItem.dataset.resourcePath;
// let dataId = vars.lastListItem.dataset.id;
// renderViewer(resourcePath, dataId);
// // 선택 상태로 스타일 변경
// changeListItemStyle(vars.lastListItem);
// // 선택된 아이템으로 포커스 이동
// targetFocus(vars.lastListItem);
// })
// });
// 우측 메타데이터 영역 작성자 변경 버튼 클릭 이벤트
document.querySelector('.archive-main .archive-main-right .viewer-container .metadata .metadata-item-wrap .author .btn')?.addEventListener('click', ()=>{
let resourcePath = vars.lastListItem.dataset.resourcePath;
openEditAuthorModal(resourcePath, vars.lastListItem);
});
// 우측 메타데이터 영역 위치 수정 버튼 클릭 이벤트
document.querySelector('.archive-main .archive-main-right .viewer-container .metadata .metadata-item-wrap .gps-data .btn')?.addEventListener('click', ()=>{
let toggleParams = {
title: '위치 수정',
text: '확인 버튼을 누르면 위치 수정 모드로 전환됩니다.',
type: 'editPosition',
};
toggleModal(true, toggleParams);
});
// 푸터 도움말 버튼 클릭 이벤트
document.querySelector('body > .footer .left .manual-wrap .text')?.addEventListener('click', ()=>{
let toggleParams = {
title: '도움말',
type: 'manual',
};
toggleModal(true, toggleParams);
})
// 푸터 문의사항 버튼 클릭 이벤트
document.querySelector('body > .footer .left .question-wrap .text')?.addEventListener('click', ()=>{
window.open('https://docs.google.com/spreadsheets/d/18XL_xJERh50Ls1n1Y7G_nSrDo1DKvJwPRaPuuiGw2uQ/edit?gid=0#gid=0','question');
})
// 푸터 패치노트 버튼 클릭 이벤트
document.querySelector('body > .footer .left .patch-note-wrap .text')?.addEventListener('click', ()=>{
window.open('https://docs.google.com/spreadsheets/d/1MgLs-_PZkpu8jb653A56hw6NQ9jl77OAiXOM4r45Ri0/edit?gid=1522357484#gid=1522357484','patch-note');
})
// 푸터 휴지통 버튼 클릭 이벤트
document.querySelector('body > .footer .left .recycle-bin-wrap .text')?.addEventListener('click', async ()=>{
toggleRecycleBinModal(true);
})
// 푸터 저장공간 버튼 클릭 이벤트
document.querySelector('body > .footer .left .size-wrap .text')?.addEventListener('click', (e) => {
let toggleParams = {
title: '저장공간',
type: 'size',
};
toggleModal(true, toggleParams);
// 모달 열기
// 현재 사용자 정보 표시
})
// 푸터 활동로그 버튼 클릭 이벤트
document.querySelector('body > .footer .left .log-wrap .text')?.addEventListener('click', (e) => {
let toggleParams = {
title: '활동로그',
type: 'log',
};
toggleModal(true, toggleParams);
// 모달 열기
// 현재 사용자 정보 표시
})
// 개발자 계정일 때 푸터 로고 클릭 이벤트
if (vars.permission.checkPermission('dev-menu')) {
// 클릭을 위해 이미지 태그에 기본으로 설정된 pointer-events 'none'을 'all'로 변경
document.querySelector('body > .footer .right').addEventListener('click', () => {
toggleDevMenuModal(true);
})
}
// 모달창 이외 영역 클릭 시 모달창 닫기
// document.querySelector('.archive-modal')?.addEventListener('pointerdown', (e) => {
// if (e.target.className.includes('archive-modal')) toggleModal(false);
// })
// 모달창 X버튼 닫기
document.querySelector('.archive-modal .modal-header .close')?.addEventListener('click', () => {
document.querySelector('.project-type__list')?.classList.remove('--project-list__open');
document.querySelector('.project-step__list')?.classList.remove('--project-list__open');
toggleModal(false);
})
// 휴지통 모달창 X버튼 닫기
document.querySelector('.recycle-bin-modal .modal-header .close')?.addEventListener('click', () => {
toggleRecycleBinModal(false);
})
// 개발자 메뉴 모달창 이외 영역 클릭 시 모달창 닫기
// document.querySelector('.dev-menu-modal')?.addEventListener('pointerdown', (e) => {
// if (e.target.className.includes('dev-menu-modal')) toggleDevMenuModal(false);
// })
// 개발자 메뉴 모달창 X버튼 닫기
document.querySelector('.dev-menu-modal .modal-header .close')?.addEventListener('click', () => {
toggleDevMenuModal(false);
})
export function toggleContextFocusBox(state, target, scope) {
let contextFocusBox = document.querySelector('.context-focus-box');
if (state == false) {
// 이벤트 대상(target)이 컨텍스트메뉴에 포함된 dom(컨텍스트메뉴 아이템)이 아닌 경우 리턴
if (target && document.querySelector('.contextmenu.main-menu').contains(target)) return;
if (target && document.querySelector('.archive-modal').contains(target)) return;
// contextFocusBox 숨김 처리
contextFocusBox.style.display = 'none';
}
if (state == true) {
let targetWidth = target.offsetWidth;
let targetHeight = target.offsetHeight;
let top = 0;
let left = 0;
if (scope == 'tree') {
let controlBox = document.querySelector('.archive-main .archive-main-left .control-box');
if (controlBox) targetHeight = targetHeight - controlBox.offsetHeight;
}
if (scope == 'list') {
// 갤러리 폴더 지도모드일 때 컨텍스트 포커스 박스 생성 비활성화
let mapContainer = listContainer?.querySelector('.map-container');
if (mapContainer.style.display == 'flex') return;
}
// if (scope == 'headerBtn') {
// targetWidth = target.querySelector('.wrap').offsetWidth;
// targetHeight = target.querySelector('.wrap').offsetHeight;
// }
if (scope == 'tree' || scope == 'list') {
targetWidth = targetWidth-8;
targetHeight = targetHeight-8;
top = 4;
left = 4;
}
if (scope == 'list-item') {
// 파일 이동 모드인 경우 리턴
if (document.querySelector('.list-viewer-cover').style.display == 'flex') return;
// 추가 파일에 disabled 클래스가 포함되어 있으면 리턴 (파일 이동 모드)
let listItem = target.closest('.list-item');
if (listItem.classList.contains('disabled')) return;
targetWidth = target.parentElement.offsetWidth;
targetHeight = target.parentElement.offsetHeight;
}
if (scope == 'grid-item') {
// 파일 이동 모드인 경우 리턴
if (document.querySelector('.list-viewer-cover').style.display == 'flex') return;
targetWidth = targetWidth;
targetHeight = targetHeight;
top = -1;
left = -1;
}
if (scope == 'map-item') {
return;
// targetWidth = targetWidth-4;
// targetHeight = targetHeight-4;
// top = 1;
// left = 1;
}
let width = `${targetWidth}px`;
let height = `${targetHeight}px`;
contextFocusBox.style.width = width;
contextFocusBox.style.height = height;
contextFocusBox.style.top = `${top}px`;
contextFocusBox.style.left = `${left}px`;
if (document.querySelector('.context-focus-box-clone')) {
document.querySelector('.context-focus-box-clone').remove();
}
let contextFocusBoxClone = contextFocusBox.cloneNode(true);
contextFocusBoxClone.classList.add('context-focus-box-clone');
contextFocusBoxClone.style.display = 'block';
target.style.position = 'relative';
target.appendChild(contextFocusBoxClone);
}
}
export function toggleContextmenu(state, event, scope) {
let contextmenu = document.querySelector('.contextmenu');
if (state == false) {
contextmenu.style.display = 'none';
// 컨텍스트메뉴 아이템을 제외한 영역에서 toggleContextmenu false 작동 시 컨텍스트메뉴 아이템 초기화 (업로드)
if (event && event.target.closest && !event.target.closest('.contextmenu-item')) contextmenu.querySelector('.contextmenu-container').innerHTML = '';
// 이벤트 대상(event.target)이 컨텍스트메뉴에 포함된 dom(컨텍스트메뉴 아이템)이 아닌 경우 리턴
// if (event && document.querySelector('.contextmenu.main-menu')?.contains(event.target)) return;
// if (event && document.querySelector('.archive-modal')?.contains(event.target)) return;
if (event && typeof event.target.nodeType === 'number') {
if (
['.contextmenu.main-menu', '.archive-modal'].some(selector =>
document.querySelector(selector)?.contains(event.target)
)
) {
return;
// } else {
}
}
// vars.lastContextTarget/vars.lastContextTarget_bin 초기화
vars.lastContextTarget = undefined;
vars.lastContextTarget_bin = undefined;
}
if (state == true) {
let target = event.target;
if (!target.dataset.resourcePath) {
if (target.matches('.tree-notice')) {
// .tree-container > .tree-notice 우클릭 시 target을 .tree-container로 설정
target = target.parentElement;
} else if (target.matches('.tree-wrap')) {
// .tree-container > .tree-wrap 우클릭 시 target을 .tree-container로 설정
target = target.parentElement;
} else if (target.matches('.tree-item')) {
// .tree-item 우클릭 시 target을 .tree-item-wrap으로 설정
target = target.parentElement;
} else if (target.closest('.list-wrap.list-header')) {
// .list-wrap.list-header > .list-item-wrap > .list-item > .wrap > div 우클릭 시 target을 .list-wrap.list-header으로 설정
target = target.closest('.list-wrap.list-header');
} else if (target.closest('.list-wrap.list-body')) {
// .list-wrap.list-body > .list-item-wrap > .list-item > .wrap > div 우클릭 시 target을 div를 포함하고 있는 .list-item으로 설정
target = target.closest('.list-item');
if (target && target.matches('.sub-list-item')) scope = 'sub-list-item';
}
}
let isRecycleBinModal = document.querySelector('.recycle-bin-modal').style.display == 'flex';
if (isRecycleBinModal) {
vars.lastContextTarget_bin = target;
if (vars.multiSelectListItemArr_bin.includes(target)) {
if (vars.multiSelectListItemArr_bin.length > 1) {
scope = 'multi-select-list'
toggleContextFocusBox(false);
}
} else {
document.querySelectorAll('.recycle-bin-modal .list-body .list-item').forEach(elem => {
elem.classList.remove('selected');
elem.classList.remove('group-style');
})
vars.lastListGroupTarget_bin = undefined;
vars.lastListItem_bin = undefined;
vars.lastSelectTarget_bin = undefined;
//userSelected 삭제
let me = JSON.parse(vars.userInfoString);
me.selected = undefined;
vars.socket.emit('fileSelect',{me : me});
vars.multiSelectListItemArr_bin = [];
}
} else {
vars.lastContextTarget = target;
if (vars.multiSelectListItemArr.includes(target)) {
if (vars.multiSelectListItemArr.length > 1) {
scope = 'multi-select-list'
toggleContextFocusBox(false);
}
} else {
document.querySelectorAll('.archive-main .list-body .list-item').forEach(elem => {
if (viewerContainer.style.display == 'flex') resetViewer();
elem.classList.remove('selected');
elem.classList.remove('group-style');
})
vars.lastListGroupTarget = undefined;
vars.lastListItem = undefined;
vars.lastSelectTarget = undefined;
//userSelected 삭제
let me = JSON.parse(vars.userInfoString);
me.selected = undefined;
vars.socket.emit('fileSelect',{me : me});
vars.multiSelectListItemArr = [];
}
}
renderContextmenu(event, scope);
}
}
// 컨트롤 박스 드래그 on/off 토글
export function toggleControlBox(state, fileAreaMode) {
let controlBox = document.querySelector('.control-box');
if (state == false) {
controlBox.style.display = 'none';
}
//// 아카이브 좌측 영역 고정 버전 컨트롤 박스 관련 코드
if (state == true) {
controlBox.style.display = 'flex';
controlBox.querySelectorAll('.file-area-mode-btn').forEach(btn => {
btn.classList.remove('selected');
})
controlBox.querySelector(`.file-area-mode-btn.${fileAreaMode}`).classList.add('selected');
}
//// 플로팅 버전 컨트롤 박스 관련 코드
// if (state == true) {
// controlBox.style.display = 'flex';
// controlBox.querySelectorAll('.file-area-mode-btn').forEach(btn => {
// btn.classList.remove('selected');
// })
// controlBox.querySelector(`.file-area-mode-btn.${fileAreaMode}`).classList.add('selected');
// // 모든 버튼 크기 동일하게 설정
// // let firstBtnWidth = Math.round(controlBox.querySelectorAll('.file-area-mode-btn')[0].getBoundingClientRect().width);
// // controlBox.querySelectorAll('.file-area-mode-btn').forEach(btn => {
// // btn.style.minWidth = `${pxToRem(firstBtnWidth)}rem`;
// // })
// let dragHandle = controlBox.querySelector('.drag-handle');
// // 드래그 핸들에 이벤트 리스너 추가
// dragHandle.addEventListener('pointerdown', handleControlBoxPointerDown);
// // 전역 이벤트 리스너 (한 번만 추가)
// document.addEventListener('pointermove', handleControlBoxPointerMove);
// document.addEventListener('pointerup', handleControlBoxPointerUp);
// // 저장된 위치 확인 및 적용
// setControlBoxInitialPosition(controlBox);
// }
}
export function toggleArchiveMainRight(state, option) {
// let { from } = option;
// console.log('@@@@@@@@@@@@@@@@');
// console.log(from);
let archiveMainRight = document.querySelector('.archive-main-right');
if (state == false) {
if (viewerContainer.style.display == 'flex') viewerContainer.style.display = 'none';
archiveMainRight.style.display = 'none';
// archiveMainRight.style.width = '0rem';
// archiveMainRight.style.opacity = '0';
}
if (state == true) {
if (vars.viewer && vars.viewer.childElementCount != 0) viewerContainer.style.display = 'flex';
archiveMainRight.style.display = 'flex';
// archiveMainRight.style.width = '41rem';
// archiveMainRight.style.opacity = '1';
if (document.querySelector('.viewer-container .viewer-wrap .viewer').children.length > 0) viewerNotice.style.display = 'none';
if (vars.lastListItem) {
// 마지막으로 선택된 파일 아이템이 있는 경우 현재 파일 영역 모드에 따라서 뷰어 정보 영역 세팅
let value = document.querySelector('.control-box .file-area-mode-btn.selected')?.id;
if (value) {
let infoWrap = document.querySelector('.archive-main-right .info-wrap');
let toggleBtnText = infoWrap.querySelector('.toggle-btn .text');
let metadata = infoWrap.querySelector('.metadata');
// 뷰어 툴바는 그리드 모드 미리보기에서 사용하는데 그리드 모드에서 미리보기 표시 안하기로 했기 때문에 뷰어 툴바는 무조건 숨김
let viewerToolbar = infoWrap.querySelector('.toolbar');
viewerToolbar.style.display = 'none';
//// 리스트 모드와 지도 표시 모드에서 미리보기 하단 영역 다르게 표시 --- 시작
// if (value == 'list') {
// infoWrap.style.height = '15%';
// toggleBtnText.textContent = '메모 (AI 요약)';
// metadata.style.display = 'none';
// } else {
// // 메타데이터 내용 채우기!!!!!!!!!!!!!!!!!!!!!!!!!!! 애초에 리스트 아이템 클릭할 때 메타데이터 숨겨져 있어도 내용은 그냥 채우는걸로?
// infoWrap.style.height = '30%';
// toggleBtnText.textContent = '메타데이터';
// metadata.style.display = 'flex';
// }
//// 리스트 모드와 지도 표시 모드에서 미리보기 하단 영역 다르게 표시 --- 끝
// 메타데이터 수정 시 업데이트 된 메타데이터로 뷰어 재렌더링
setTimeout(async() => {
if (vars.lastListItem) {
let updateResourcePath = vars.lastListItem.dataset.resourcePath;
let updatedDataId = vars.lastListItem.dataset.id;
await renderViewer(updateResourcePath, updatedDataId, false);
}
}, 300);
//// 리스트 모드와 지도 표시 모드에서 미리보기 하단 영역 메타데이터 형태로 동일하게 표시 --- 시작
infoWrap.style.height = '30%';
toggleBtnText.textContent = '메타데이터';
metadata.style.display = 'flex';
//// 리스트 모드와 지도 표시 모드에서 미리보기 하단 영역 메타데이터 형태로 동일하게 표시 --- 끝
}
} else {
// 마지막으로 선택된 파일 아이템이 없으면 뷰어 초기화
resetViewer();
}
}
}
export function showNotification(params) {
let fadeMs = 500;
let holdMs = 1000;
let notificationWrap = document.querySelector('.list-wrap.list-body .notification-wrap');
let text = notificationWrap.querySelector('.text');
text.innerHTML = params.text;
// 1) 기존 hide 타이머 취소(연속 호출 동안 계속 표시)
if (notificationWrap._hideTimer) {
clearTimeout(notificationWrap._hideTimer);
notificationWrap._hideTimer = null;
}
// 2) 현재 애니메이션 상태 확인
let animationName = getComputedStyle(notificationWrap).animationName || 'none';
// 2-1) fadeOut 중이면 즉시 중단하고 보이도록 스냅(깜빡임 방지)
if (animationName !== 'none' && animationName.indexOf('fadeOut') !== -1) {
resetNotificationAnimation(notificationWrap);
notificationWrap.style.animation = 'fadeIn 1ms forwards';
notificationWrap._visible = true;
}
// 2-2) 처음 표시될 때만 fadeIn 실행
else if (!notificationWrap._visible) {
notificationWrap.style.animation = `fadeIn ${fadeMs}ms forwards`;
notificationWrap._visible = true;
}
// 이미 보이는/페이드인 중이면 건드리지 않음 → 깜빡임 없음
// 3) 마지막 호출 이후 holdMs 지나면 fadeOut
notificationWrap._hideTimer = setTimeout(() => {
resetNotificationAnimation(notificationWrap);
notificationWrap.style.animation = `fadeOut ${fadeMs}ms forwards`;
let onEnd = () => {
notificationWrap.removeEventListener('animationend', onEnd);
notificationWrap._visible = false;
// 애니메이션 속성 정리 → 기본 CSS(opacity:0)로 복귀
resetNotificationAnimation(notificationWrap);
};
notificationWrap.addEventListener('animationend', onEnd, { once: true });
}, holdMs);
}
function resetNotificationAnimation(el) {
el.style.animation = 'none';
void el.offsetWidth; // 강제 리플로우
el.style.animation = '';
}
// 템플릿 다운로드
document.querySelector('.list-notice-negative')?.addEventListener('click', () => {
downloadTempFile();
})
document.querySelector('.list-notice-box-toggle-menu-list.download')?.addEventListener('click', () => {
downloadTempFile();
})
// depth1 이미지 파일 업로드
setupDepth1Upload('fileInput');
setupDepth1Upload('fileInput2');
function setupDepth1Upload(inputId) {
document.getElementById(inputId)?.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const newFileName = 'MAIN_TITLE_IMAGE___' + file.name;
const renamedFile = new File([file], newFileName, {
type: file.type,
lastModified: file.lastModified
});
document.querySelector('.list-notice-box-toggle-menu').style.display = 'none';
uploadMainTitleImage([renamedFile], inputId);
});
}
document.querySelector('.list-notice-box-toggle')?.addEventListener('click', () => {
const toggleMenu = document.querySelector('.list-notice-box-toggle-menu');
updateToggleMenuPosition();
if (toggleMenu.style.display === 'none' || toggleMenu.style.display === '') {
toggleMenu.style.display = 'flex';
}else {
toggleMenu.style.display = 'none';
}
})
function updateToggleMenuPosition() {
if (checkProjectInactive()) return;
const toggleBtn = document.querySelector('.list-notice-box-toggle');
const toggleMenu = document.querySelector('.list-notice-box-toggle-menu');
const wasHidden = toggleMenu.style.display === 'none';
// 일시적으로 보여주기 (위치 계산 위해)
if (wasHidden) {
toggleMenu.style.visibility = 'hidden';
toggleMenu.style.display = 'flex';
}
if (toggleMenu.style.display !== 'none') {
const menuWidth = toggleMenu.offsetWidth;
toggleMenu.style.top = `${toggleBtn.offsetTop + toggleBtn.offsetHeight + 4}px`;
toggleMenu.style.left = `${toggleBtn.offsetLeft - menuWidth + toggleBtn.offsetWidth}px`;
}
// 다시 숨김 (필요한 경우)
if (wasHidden) {
toggleMenu.style.display = 'none';
toggleMenu.style.visibility = 'visible';
}
}
// 창 크기 변경될 때 위치 다시 계산
window.addEventListener('resize', updateToggleMenuPosition);
// main title image 삭제 이벤트
document.querySelector('.list-notice-box-toggle-menu-list.delete')?.addEventListener('click', async () =>{
let resourcePath = `${vars.lastHeaderBtn.dataset.resourcePath}`;
let param = { userInfoString: vars.userInfoString, resourcePath: resourcePath };
let res = await axios.post(`${vars.path_name}/deleteMainTitleImage`, { params: param });
if(res.data.message == 'deleteMainTitleImage_success') {
document.querySelector('.list-notice-top').style.display = 'flex';
document.querySelector('.list-notice-bottom').style.display = 'flex';
document.querySelector('.list-notice-viewer').style.display = 'none';
}
})
// download btn 이벤트
document.querySelector(`.download-btn`)?.addEventListener('click', async () => {
await getMyDownloadList();
document.querySelector('.download-modal').style.display = 'flex';
})
// download modal close 이벤트
document.querySelector(`.download-modal__head_close`)?.addEventListener('click', () => {
document.querySelector('.download-modal').style.display = 'none';
})
document.querySelector(`.download-modal__foot .--button__medium`)?.addEventListener('click', () => {
document.querySelector('.download-modal').style.display = 'none';
})
// (임시) 썸네일 생성 버튼 이벤트
const thumbBtn = document.querySelector(`.archive-main .archive-main-center .list-container .list-wrap .list-item-wrap .list-item .wrap .thumbnail .thumb-btn-wrap`);
thumbBtn.addEventListener('click', async () => {
await regenerateThumbnails();
});
async function regenerateThumbnails() {
// 1) db에서 썸네일 없는 파일 목록 불러오기
let res = await axios.get(`${vars.path_name}/getNullThumbnailDataInfo`);
if(res.data.message === 'getNullThumbnailDataInfo_success') {
let lists = res.data.result;
console.log('!!!!!!!!!!!!!');
console.log(lists);
if(!lists || lists.length === 0) {
console.log('처리할 파일이 없습니다.');
return;
}
// 2) 서버에 썸네일 presigned URL 요청
let imageExtArr = ['jpg', 'jpeg', 'png', 'webp', 'gif'];
let videoExtArr = ['mp4', 'webm', 'mov'];
let docExtArr = ['hwp', 'hwpx', 'doc', 'docx', 'xls', 'xlsx', 'xlsm', 'ppt', 'pptx', 'dwg', 'dxf', 'grm'];
let textExtArr = ['txt', 'md'];
let date = new Date();
let needsThumbnailExtArr = [...imageExtArr, ...videoExtArr, ...docExtArr, ...textExtArr,'pdf'];
let isLowerExt = true;
let coordArr = [];
let uploadSuccessFileArr = [];
for (let i=0; i<lists.length; i++) {
console.log('');
console.log(`======================== ${i+1}/${lists.length} start =========================`);
let list = lists[i];
let resourcePath = list.path1 + '/' + list.path2 + '/' + list.path3 + '/' + list.path4;
if (list.path5) resourcePath = resourcePath + '/' + list.path5;
let ext = splitBaseAndExt(resourcePath, isLowerExt).ext;
console.log(ext);
ext = ext.split('_')[0];
console.log(ext);
let needsThumbnail = needsThumbnailExtArr.includes(ext);
if (!needsThumbnail) continue;
let thumbnailPath = splitBaseAndExt(resourcePath, isLowerExt).base + 'jpeg';
// presigend 업로드 URL 요청 (썸네일)
let generateUploadUrlParams = {
resourcePath: resourcePath,
date: date,
needsThumbnail: needsThumbnail,
thumbnailPath: thumbnailPath
};
let generateUploadUrlRes = await axios.post(`${vars.path_name}/generateUploadUrl`, generateUploadUrlParams);
if (generateUploadUrlRes.data.message !== 'generateUploadUrl_success') continue;
let { thumbnailUrl, thumbnailKey } = generateUploadUrlRes.data.result;
console.log('-------- thumbnailUrl, thumbnailKey');
console.log(thumbnailUrl);
console.log(thumbnailKey);
// 3) presigned 다운로드 URL 요청 (원본 파일)
let generateDownloadUrlParams = {
objectKey: list.popup_key,
resourcePath: resourcePath,
};
let generateDownloadUrlRes = await axios.post(`${vars.path_name}/generateDownloadUrl`, generateDownloadUrlParams);
if (generateDownloadUrlRes.data.message !== 'generateDownloadUrl_success') continue;
let fileRes = await fetch(generateDownloadUrlRes.data.url);
if(fileRes.ok) {
console.log(111);
console.log(fileRes);
const blob = await fileRes.blob();
console.log(blob);
let fileName = list.path4;
if (list.path5) fileName = list.path5;
const file = new File([blob], fileName, { type: blob.type });
console.log(file);
console.log('file silze: ' + file.size);
if (file.size != 0) {
// 4) 썸네일 생성
let isImage = imageExtArr.includes(ext);
let isVideo = videoExtArr.includes(ext);
let isPdf = ext === 'pdf';
let isDoc = docExtArr.includes(ext);
let isText = textExtArr.includes(ext);
let lon = null, lat = null, height = null;
// exif 좌표 추출
if (isImage) {
try {
let parse = await exifr.parse(file);
if(parse?.latitude && parse?.longitude) {
lon = parse.longitude;
lat = parse.latitude;
height = parse.GPSAltitude || 0;
}
} catch(err) {
console.warn(`exifr 좌표 추출 실패 (${resourcePath})`);
}
}
coordArr.push({ lon, lat, height });
// 썸네일 생성 로직
let thumbnailFile;
if (thumbnailUrl && thumbnailKey) {
console.log(222);
let resizeTarget;
if (isImage) {
console.log(333 + ' isImage');
resizeTarget = file;
} else if (isVideo) {
console.log(444 + ' isVideo');
resizeTarget = await createVideoThumbnail(file, thumbnailPath);
}else if (isPdf || isDoc) {
console.log(555 + ' isPdf/isDoc');
try {
let pdfData = await getPdfData(file);
console.log('------ pdfData');
console.log(pdfData);
resizeTarget = await createPdfThumbnail(pdfData, 960, thumbnailPath);
} catch (err) {
console.log('***************** pdf문제');
console.log(err);
}
}else if (isText) {
console.log(555000 + ' isText');
resizeTarget = await createTextThumbnail(file, thumbnailPath);
}
if (resizeTarget) {
console.log(666);
// 이미지 리사이징 (최대 100KB)
let img = new Image();
img.src = URL.createObjectURL(resizeTarget);
await new Promise(r => img.onload = r);
console.log(777);
let maxSizeBytes = 100 * 1024;
if (resizeTarget.size > maxSizeBytes) {
thumbnailFile = await resizeImage(img, maxSizeBytes, thumbnailPath);
}else {
thumbnailFile = resizeTarget;
}
// 5) s3에 썸네일 업로드
await axios.put(thumbnailUrl, thumbnailFile, {
headers: {
'Content-Type': thumbnailFile.type || 'application/octet-stream'
}
});
console.log(`@@@@@@@@@@@@@@ 썸네일 s3 업로드 완료_${i+1}/${lists.length} @@@@@@@@@@@@@@`);
// 6) DB의 기존 데이터 썸네일 정보 업데이트
let updateParams = {
data_id: list.data_id,
thumbnail_key: thumbnailKey,
thumbnail_size: thumbnailFile.size,
lon,
lat,
height,
user_id: vars.userInfoString.user_id,
};
let updateRes = await axios.post(`${vars.path_name}/updateThumbnailInfo`, updateParams);
if(updateRes.data.message === 'updateThumbnailInfo_success') {
uploadSuccessFileArr.push(list.paht4);
console.log(`@@@@@@@@@@@@@@ 썸네일 정보 DB 업데이트 완료_${i+1}/${lists.length} @@@@@@@@@@@@@@`);
}else {
console.warn(`${fileName} DB 업데이트 실패`)
}
}
}
}
}
}
console.log('***************************************************************');
console.log('************************* 작업 종료 *************************');
console.log('***************************************************************');
}
}
// 구성 모달 ver
// document.querySelector('.footer .left .wrap.site-map-wrap').addEventListener('click', () => {
// // 1. 모달 표시
// const modal = document.querySelector('.composition-modal');
// modal.style.display = 'flex';
// // 2. 리스트 초기화
// const listWrap = modal.querySelector('.modal-wrap > ul');
// listWrap.innerHTML = '';
// // 3. 데이터 렌더링
// renderCompositionData();
// });
// // 구성 모달 닫기 이벤트
// document.querySelector('.composition-modal .modal-wrap .head i').addEventListener('click', () => {
// const modal = document.querySelector('.composition-modal');
// modal.style.display = 'none';
// });
// 구성 팝업 ver
// document.querySelector('.footer .left .wrap.site-map-wrap').addEventListener('click', () => {
// const popupWidth = 1400;
// const popupHeight = 800;
// const left = (screen.width - popupWidth) / 2;
// const top = (screen.height - popupHeight) / 2;
// window.open(
// `/main/composition-popup.html`,
// 'composition',
// `width=${popupWidth}, height=${popupHeight}, left=${left}, top=${top}, resizeable, scrollbars=yes`
// );
// })
// 구성 탭 ver
document.querySelector('.footer .left .wrap.site-map-wrap').addEventListener('click', () => {
// 새 탭으로 열기 (_blank)
const newTab = window.open(
`/main/composition-tab.html`,
'_blank'
);
// 탭이 로드될 때까지 대기 후 데이터 전송
if (newTab) {
const checkTabLoad = setInterval(() => {
try {
// 새 탭이 로드되었는지 확인
if (newTab.document.readyState === 'complete') {
clearInterval(checkTabLoad);
// 탭에 필요한 데이터 전송
newTab.postMessage({
type: 'INIT_DATA',
data: {
userInfoString: vars.userInfoString,
storageType: vars.storageType,
allTreeObject: vars.allTreeObject,
path_name: vars.path_name,
}
}, window.location.origin);
}
}catch(e) {
// Cross-origin 에러 무시 (탭이 완전히 로드되기 전)
}
}, 100);
// 10초 후에도 로드되지 않으면 interval 정리
setTimeout(() => clearInterval(checkTabLoad), 10000);
}
});
const messageHandler = {
'NAVIGATE_TO_PATH': (path) => {
// vars 대신
const resourcePath = `/${path}`;
// 헤더 버튼 클릭 (탭 선택)
const tabPath = resourcePath.split('/')[1]; // '모델뷰어테스트/모델/3dm' -> '모델뷰어테스트'
const headerBtn = document.querySelector(`[data-resource-path="/${tabPath}"]`);
if (headerBtn) headerBtn.click();
// 트리 아이템 클릭 (폴더 선택)
setTimeout(() => {
const treeItem = document.querySelector(`.tree-item-wrap[data-resource-path="${resourcePath}"] .tree-item`);
if (treeItem) treeItem.click();
}, 500);
}
}
window.addEventListener('message', (event) => {
if(event.origin !== window.location.origin) return;
const handler = messageHandler[event.data.type];
if (handler) handler(event.data.path);
});
window.asd = async function() {
regenerateThumbnails();
}