2976 lines
123 KiB
JavaScript
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();
|
|
} |