/**
* 글벗 Light - 편집 바 모듈
* editor.js
*/
// ===== 전역 변수 =====
let isEditing = false;
let activeBlock = null;
let historyStack = [];
let redoStack = [];
const MAX_HISTORY = 50;
let isApplyingFormat = false;
// ===== 편집 바 HTML 생성 =====
// ===== 편집 바 HTML 생성 =====
function createFormatBar() {
const formatBarHTML = `
A
A
`;
return formatBarHTML;
}
// ===== 로컬 폰트 불러오기 =====
async function loadLocalFonts() {
// API 지원 여부 확인
if (!('queryLocalFonts' in window)) {
toast('⚠️ 이 브라우저는 폰트 불러오기를 지원하지 않습니다 (Chrome/Edge 필요)');
return;
}
try {
toast('🔄 폰트 불러오는 중...');
// 사용자 권한 요청 & 폰트 목록 가져오기
const fonts = await window.queryLocalFonts();
const fontSelect = document.getElementById('fontFamily');
// 기존 옵션들의 값 수집 (중복 방지)
const existingFonts = new Set();
fontSelect.querySelectorAll('option').forEach(opt => {
existingFonts.add(opt.value);
});
// 중복 제거 (family 기준)
const families = [...new Set(fonts.map(f => f.family))];
// 구분선 추가
const separator = document.createElement('option');
separator.disabled = true;
separator.textContent = '──── 내 컴퓨터 ────';
fontSelect.appendChild(separator);
// 새 폰트 추가
let addedCount = 0;
families.sort().forEach(family => {
if (!existingFonts.has(family)) {
const option = document.createElement('option');
option.value = family;
option.textContent = family;
fontSelect.appendChild(option);
addedCount++;
}
});
toast(`✅ ${addedCount}개 폰트 추가됨 (총 ${families.length}개)`);
} catch (e) {
if (e.name === 'NotAllowedError') {
toast('⚠️ 폰트 접근 권한이 거부되었습니다');
} else {
console.error('폰트 로드 오류:', e);
toast('❌ 폰트 불러오기 실패: ' + e.message);
}
}
}
// ===== 삽입 핸들러 =====
function handleInsert(type) {
if (type === 'table') openTableModal();
else if (type === 'image') insertImage();
else if (type === 'hr') insertHR();
}
// ===== 표 삽입 모달 HTML 생성 =====
function createTableModal() {
const modalHTML = `
';
for (let i = 0; i < rows; i++) {
tableHTML += '
';
for (let j = 0; j < cols; j++) {
if (i === 0 && hasHeader) {
tableHTML += '
헤더
';
} else {
tableHTML += '
내용
';
}
}
tableHTML += '
';
}
tableHTML += '
';
insertAtCursor(tableHTML);
closeTableModal();
toast('▦ 표가 삽입되었습니다');
}
// ===== 이미지 삽입 =====
function insertImage() {
const doc = getIframeDoc();
if (!doc || !isEditing) return;
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/*';
input.onchange = e => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = ev => {
saveState();
const html = `그림 설명`;
insertAtCursor(html);
toast('🖼️ 이미지가 삽입되었습니다');
};
reader.readAsDataURL(file);
};
input.click();
}
// ===== 이미지 리사이즈 =====
function selectImageForResize(img) {
if (!isEditing) return;
// 기존 선택 해제
const doc = getIframeDoc();
doc.querySelectorAll('img.selected-image').forEach(i => {
i.classList.remove('selected-image');
i.style.outline = '';
});
// 새 선택
img.classList.add('selected-image');
img.style.outline = '3px solid #00c853';
// 크기 조절 핸들러
img.onmousedown = function(e) {
if (!isEditing) return;
e.preventDefault();
const startX = e.clientX;
const startWidth = img.offsetWidth;
function onMouseMove(e) {
const diff = e.clientX - startX;
const newWidth = Math.max(50, startWidth + diff);
img.style.width = newWidth + 'px';
img.style.height = 'auto';
}
function onMouseUp() {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
saveState();
toast('이미지 크기 조절됨');
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
};
}
// ===== 구분선 삽입 =====
function insertHR() {
const doc = getIframeDoc();
if (!doc || !isEditing) return;
saveState();
insertAtCursor('');
toast('― 구분선 삽입');
}
// ===== 커서 위치에 HTML 삽입 =====
function insertAtCursor(html) {
const doc = getIframeDoc();
if (!doc) return;
const selection = doc.getSelection();
if (selection && selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
range.deleteContents();
const temp = doc.createElement('div');
temp.innerHTML = html;
const frag = doc.createDocumentFragment();
while (temp.firstChild) frag.appendChild(temp.firstChild);
range.insertNode(frag);
} else if (activeBlock) {
activeBlock.insertAdjacentHTML('afterend', html);
}
}
// ===== 블록 선택/관리 =====
function setActiveBlock(el) {
clearActiveBlock();
activeBlock = el;
if (activeBlock) activeBlock.classList.add('active-block');
}
function clearActiveBlock() {
if (activeBlock) activeBlock.classList.remove('active-block');
activeBlock = null;
}
// ===== Undo/Redo =====
function saveState() {
const doc = getIframeDoc();
if (!doc) return;
if (redoStack.length > 0) redoStack.length = 0;
historyStack.push(doc.body.innerHTML);
if (historyStack.length > MAX_HISTORY) historyStack.shift();
}
function performUndo() {
const doc = getIframeDoc();
if (!doc || historyStack.length <= 1) return;
redoStack.push(doc.body.innerHTML);
historyStack.pop();
doc.body.innerHTML = historyStack[historyStack.length - 1];
bindIframeEditEvents();
toast('↩️ 실행 취소');
}
function performRedo() {
const doc = getIframeDoc();
if (!doc || redoStack.length === 0) return;
const nextState = redoStack.pop();
historyStack.push(nextState);
doc.body.innerHTML = nextState;
bindIframeEditEvents();
toast('↪️ 다시 실행');
}
// ===== 키보드 단축키 =====
function handleEditorKeydown(e) {
if (!isEditing) return;
if (e.ctrlKey || e.metaKey) {
switch (e.key.toLowerCase()) {
case 'b': e.preventDefault(); formatText('bold'); break;
case 'i': e.preventDefault(); formatText('italic'); break;
case 'u': e.preventDefault(); formatText('underline'); break;
case 'z': e.preventDefault(); e.shiftKey ? performRedo() : performUndo(); break;
case 'y': e.preventDefault(); performRedo(); break;
case '=':
case '+': e.preventDefault(); adjustLetterSpacing(0.5); break;
case '-': e.preventDefault(); adjustLetterSpacing(-0.5); break;
}
}
if (e.key === 'Tab') {
e.preventDefault();
adjustIndent(e.shiftKey ? -1 : 1);
}
}
// ===== 리사이즈 핸들 추가 함수 =====
function addResizeHandle(doc, element, type) {
// wrapper 생성
const wrapper = doc.createElement('div');
wrapper.className = 'resizable-container ' + (type === 'table' ? 'table-resize block-type' : 'figure-resize');
// 초기 크기 설정
const rect = element.getBoundingClientRect();
wrapper.style.width = element.style.width || (rect.width + 'px');
// 크기 표시 툴팁
const tooltip = doc.createElement('div');
tooltip.className = 'size-tooltip';
tooltip.textContent = Math.round(rect.width) + ' × ' + Math.round(rect.height);
// 리사이즈 핸들
const handle = doc.createElement('div');
handle.className = 'resize-handle';
handle.title = '드래그하여 크기 조절';
// DOM 구조 변경
element.parentNode.insertBefore(wrapper, element);
wrapper.appendChild(element);
wrapper.appendChild(tooltip);
wrapper.appendChild(handle);
// 표는 width 100%로 시작
if (type === 'table') {
element.style.width = '100%';
}
// 리사이즈 이벤트
let isResizing = false;
let startX, startY, startWidth, startHeight;
handle.addEventListener('mousedown', function(e) {
e.preventDefault();
e.stopPropagation();
isResizing = true;
wrapper.classList.add('resizing');
startX = e.clientX;
startY = e.clientY;
startWidth = wrapper.offsetWidth;
startHeight = wrapper.offsetHeight;
doc.addEventListener('mousemove', onMouseMove);
doc.addEventListener('mouseup', onMouseUp);
});
function onMouseMove(e) {
if (!isResizing) return;
e.preventDefault();
const deltaX = e.clientX - startX;
const deltaY = e.clientY - startY;
const aspectRatio = startWidth / startHeight;
let newWidth = Math.max(100, startWidth + deltaX);
let newHeight;
if (e.shiftKey) {
newHeight = newWidth / aspectRatio; // 비율 유지
} else {
newHeight = Math.max(50, startHeight + deltaY);
}
wrapper.style.width = newWidth + 'px';
// 이미지인 경우 width, height 둘 다 조절
if (type !== 'table') {
const img = wrapper.querySelector('img');
if (img) {
img.style.width = newWidth + 'px';
img.style.height = newHeight + 'px';
img.style.maxWidth = 'none';
img.style.maxHeight = 'none';
}
}
tooltip.textContent = Math.round(newWidth) + ' × ' + Math.round(newHeight);
}
function onMouseUp(e) {
if (!isResizing) return;
isResizing = false;
wrapper.classList.remove('resizing');
doc.removeEventListener('mousemove', onMouseMove);
doc.removeEventListener('mouseup', onMouseUp);
saveState();
toast('📐 크기 조절: ' + Math.round(wrapper.offsetWidth) + 'px');
}
}
// ===== iframe 내부에 편집용 스타일 주입 =====
function injectEditStyles(doc) {
if (doc.getElementById('editor-inject-style')) return;
const style = doc.createElement('style');
style.id = 'editor-inject-style';
style.textContent = `
/* 리사이즈 컨테이너 */
.resizable-container { position: relative; display: inline-block; max-width: 100%; }
.resizable-container.block-type { display: block; }
/* 리사이즈 핸들 */
.resize-handle {
position: absolute;
right: -2px;
bottom: -2px;
width: 18px;
height: 18px;
background: #00C853;
cursor: se-resize;
opacity: 0;
transition: opacity 0.2s;
z-index: 100;
border-radius: 3px 0 3px 0;
display: flex;
align-items: center;
justify-content: center;
}
.resize-handle::after {
content: '⤡';
color: white;
font-size: 12px;
font-weight: bold;
}
.resizable-container:hover .resize-handle { opacity: 0.8; }
.resize-handle:hover { opacity: 1 !important; transform: scale(1.1); }
.resizable-container.resizing { outline: 2px dashed #00C853 !important; }
.resizable-container.resizing .resize-handle { opacity: 1; background: #FF9800; }
/* 표 전용 - 파란색 핸들 */
.resizable-container.table-resize .resize-handle { background: #2196F3; }
.resizable-container.table-resize.resizing .resize-handle { background: #FF5722; }
/* 이미지 전용 */
.resizable-container.figure-resize img { display: block; }
/* 크기 표시 툴팁 */
.size-tooltip {
position: absolute;
top: -25px;
right: 0;
background: rgba(0,0,0,0.8);
color: white;
padding: 2px 8px;
border-radius: 3px;
font-size: 11px;
white-space: nowrap;
opacity: 0;
transition: opacity 0.2s;
pointer-events: none;
}
.resizable-container:hover .size-tooltip,
.resizable-container.resizing .size-tooltip { opacity: 1; }
/* 열 리사이즈 핸들 */
.col-resize-handle {
position: absolute;
top: 0;
width: 6px;
height: 100%;
background: transparent;
cursor: col-resize;
z-index: 50;
}
.col-resize-handle:hover { background: rgba(33, 150, 243, 0.3); }
.col-resize-handle.dragging { background: rgba(33, 150, 243, 0.5); }
/* 편집 중 하이라이트 */
[contenteditable]:focus { outline: 2px solid #00C853 !important; }
[contenteditable]:hover { outline: 1px dashed rgba(0,200,83,0.5); }
`;
doc.head.appendChild(style);
}
// ===== iframe 편집 이벤트 바인딩 =====
// ===== iframe 편집 이벤트 바인딩 =====
function bindIframeEditEvents() {
const doc = getIframeDoc();
if (!doc) return;
// 편집용 스타일 주입
injectEditStyles(doc);
// 키보드 이벤트
doc.removeEventListener('keydown', handleEditorKeydown);
doc.addEventListener('keydown', handleEditorKeydown);
// 블록 클릭 이벤트
doc.body.addEventListener('click', function(e) {
if (!isEditing) return;
let target = e.target;
while (target && target !== doc.body) {
if (['DIV', 'P', 'H1', 'H2', 'H3', 'LI', 'TD', 'TH', 'FIGCAPTION'].includes(target.tagName)) {
setActiveBlock(target);
return;
}
target = target.parentElement;
}
clearActiveBlock();
});
// ===== 표에 리사이즈 핸들 추가 =====
doc.querySelectorAll('.body-content table, .sheet table').forEach(table => {
if (table.closest('.resizable-container')) return;
addResizeHandle(doc, table, 'table');
addColumnResizeHandles(doc, table); // 열 리사이즈 추가
});
// ===== 이미지에 리사이즈 핸들 추가 =====
doc.querySelectorAll('figure img, .body-content img, .sheet img').forEach(img => {
if (img.closest('.resizable-container')) return;
addResizeHandle(doc, img, 'image');
});
}
// ===== 표 열 리사이즈 핸들 추가 =====
function addColumnResizeHandles(doc, table) {
// 테이블에 position relative 설정
table.style.position = 'relative';
// 첫 번째 행의 셀들을 기준으로 열 핸들 생성
const firstRow = table.querySelector('tr');
if (!firstRow) return;
const cells = firstRow.querySelectorAll('th, td');
cells.forEach((cell, index) => {
if (index === cells.length - 1) return; // 마지막 열은 제외
// 이미 핸들이 있으면 스킵
if (cell.querySelector('.col-resize-handle')) return;
cell.style.position = 'relative';
const handle = doc.createElement('div');
handle.className = 'col-resize-handle';
handle.style.right = '-3px';
cell.appendChild(handle);
let startX, startWidth, nextStartWidth;
let nextCell = cells[index + 1];
handle.addEventListener('mousedown', function(e) {
e.preventDefault();
e.stopPropagation();
handle.classList.add('dragging');
startX = e.clientX;
startWidth = cell.offsetWidth;
nextStartWidth = nextCell ? nextCell.offsetWidth : 0;
doc.addEventListener('mousemove', onMouseMove);
doc.addEventListener('mouseup', onMouseUp);
});
function onMouseMove(e) {
const delta = e.clientX - startX;
const newWidth = Math.max(30, startWidth + delta);
cell.style.width = newWidth + 'px';
// 다음 열도 조정 (테이블 전체 너비 유지)
if (nextCell && nextStartWidth > 30) {
const newNextWidth = Math.max(30, nextStartWidth - delta);
nextCell.style.width = newNextWidth + 'px';
}
}
function onMouseUp() {
handle.classList.remove('dragging');
doc.removeEventListener('mousemove', onMouseMove);
doc.removeEventListener('mouseup', onMouseUp);
saveState();
toast('📊 열 너비 조절됨');
}
});
}
// ===== 편집 모드 토글 =====
function toggleEditMode() {
const doc = getIframeDoc();
if (!doc) return;
isEditing = !isEditing;
const formatBar = document.getElementById('formatBar');
const editBtn = document.getElementById('editModeBtn');
if (isEditing) {
// 편집 모드 ON
doc.designMode = 'on';
if (formatBar) formatBar.classList.add('active');
if (editBtn) {
editBtn.textContent = '✏️ 편집 중';
editBtn.classList.add('active');
}
// contenteditable 설정
doc.querySelectorAll('.sheet *').forEach(el => {
if (['DIV', 'P', 'H1', 'H2', 'H3', 'SPAN', 'LI', 'TD', 'TH', 'FIGCAPTION'].includes(el.tagName)) {
el.setAttribute('contenteditable', 'true');
}
});
bindIframeEditEvents();
saveState();
toast('✏️ 편집 모드 시작');
} else {
// 편집 모드 OFF
doc.designMode = 'off';
if (formatBar) formatBar.classList.remove('active');
if (editBtn) {
editBtn.textContent = '✏️ 편집하기';
editBtn.classList.remove('active');
}
// contenteditable 제거
doc.querySelectorAll('[contenteditable]').forEach(el => {
el.removeAttribute('contenteditable');
});
clearActiveBlock();
toast('✏️ 편집 모드 종료');
}
}
// ===== 편집기 초기화 =====
function initEditor() {
// 편집 바가 없으면 생성
if (!document.getElementById('formatBar')) {
const previewContainer = document.querySelector('.main');
if (previewContainer) {
previewContainer.insertAdjacentHTML('afterbegin', createFormatBar());
}
}
// 표 모달이 없으면 생성
if (!document.getElementById('tableModal')) {
document.body.insertAdjacentHTML('beforeend', createTableModal());
}
// 토스트 컨테이너 생성
createToastContainer();
console.log('Editor initialized');
}
// ===== 지능형 정렬 =====
function smartAlign() {
const doc = getIframeDoc();
if (!doc) {
toast('⚠️ 문서가 로드되지 않았습니다');
return;
}
// ===== 현재 스크롤 위치 저장 =====
const iframe = getPreviewIframe();
const scrollY = iframe?.contentWindow?.scrollY || 0;
const sheets = Array.from(doc.querySelectorAll('.sheet'));
if (sheets.length < 2) {
toast('⚠️ 정렬할 본문 페이지가 없습니다');
return;
}
toast('지능형 정렬 실행 중...');
setTimeout(() => {
try {
// 1. 표지 유지
const coverSheet = sheets[0];
// 2. 보고서 제목 추출
let reportTitle = "보고서";
const existingTitle = sheets[1]?.querySelector('.rpt-title, .header-title');
if (existingTitle) reportTitle = existingTitle.innerText;
// 3. 콘텐츠 수집 (표지 제외)
const contentSheets = sheets.slice(1);
let allNodes = [];
contentSheets.forEach(sheet => {
const body = sheet.querySelector('.body-content');
if (body) {
Array.from(body.children).forEach(child => {
if (child.classList.contains('add-after-btn') ||
child.classList.contains('delete-block-btn') ||
child.classList.contains('empty-placeholder')) return;
if (['P', 'DIV', 'SPAN'].includes(child.tagName) &&
child.innerText.trim() === '' &&
!child.querySelector('img, table, figure')) return;
allNodes.push(child);
});
}
sheet.remove();
});
// 4. 설정값
const MAX_HEIGHT = 970;
const HEADING_RESERVE = 90;
let currentHeaderTitle = "목차";
let pageNum = 1;
// 5. 새 페이지 생성 함수
function createNewPage(headerText) {
const newSheet = doc.createElement('div');
newSheet.className = 'sheet';
newSheet.innerHTML = `
${headerText}
`;
doc.body.appendChild(newSheet);
return newSheet;
}
// 6. 페이지 재구성
let currentPage = createNewPage(currentHeaderTitle);
let currentBody = currentPage.querySelector('.body-content');
allNodes.forEach(node => {
// 강제 페이지 브레이크
if (node.classList && node.classList.contains('page-break-forced')) {
currentPage = createNewPage(currentHeaderTitle);
currentBody = currentPage.querySelector('.body-content');
currentBody.appendChild(node);
return;
}
// H1: 새 섹션 시작
if (node.tagName === 'H1') {
currentHeaderTitle = node.innerText.split('-')[0].trim();
if (currentBody.children.length > 0) {
currentPage = createNewPage(currentHeaderTitle);
currentBody = currentPage.querySelector('.body-content');
} else {
currentPage.querySelector('.page-header').innerText = currentHeaderTitle;
}
}
// H2, H3: 남은 공간 부족하면 새 페이지
if (['H2', 'H3'].includes(node.tagName)) {
const spaceLeft = MAX_HEIGHT - currentBody.scrollHeight;
if (spaceLeft < HEADING_RESERVE) {
currentPage = createNewPage(currentHeaderTitle);
currentBody = currentPage.querySelector('.body-content');
}
}
// 노드 추가
currentBody.appendChild(node);
// 전 페이지로 강제 이동 설정된 경우 스킵
if (node.classList && node.classList.contains('move-to-prev-page')) {
return;
}
// 높이 초과 시 새 페이지로 이동
if (currentBody.scrollHeight > MAX_HEIGHT) {
currentBody.removeChild(node);
currentPage = createNewPage(currentHeaderTitle);
currentBody = currentPage.querySelector('.body-content');
currentBody.appendChild(node);
}
});
// 7. 편집 모드였으면 복원
if (isEditing) {
bindIframeEditEvents();
}
// 8. generatedHTML 업데이트 (전역 변수)
if (typeof generatedHTML !== 'undefined') {
generatedHTML = '' + doc.documentElement.outerHTML;
}
// ===== 스크롤 위치 복원 =====
setTimeout(() => {
if (iframe?.contentWindow) {
iframe.contentWindow.scrollTo(0, scrollY);
}
}, 50);
toast('✅ 지능형 정렬 완료 (' + pageNum + '페이지)');
} catch (e) {
console.error('smartAlign 오류:', e);
toast('❌ 정렬 중 오류: ' + e.message);
}
}, 100);
}
// ===== 새페이지 시작 =====
function forcePageBreak() {
const doc = getIframeDoc();
if (!doc) {
toast('⚠️ 문서가 로드되지 않았습니다');
return;
}
const selection = doc.getSelection();
if (!selection || !selection.anchorNode) {
toast('⚠️ 분리할 위치를 클릭하세요');
return;
}
let targetEl = selection.anchorNode.nodeType === 1
? selection.anchorNode
: selection.anchorNode.parentElement;
while (targetEl && targetEl.parentElement) {
if (targetEl.parentElement.classList && targetEl.parentElement.classList.contains('body-content')) {
break;
}
targetEl = targetEl.parentElement;
}
if (!targetEl || !targetEl.parentElement || !targetEl.parentElement.classList.contains('body-content')) {
toast('⚠️ 본문 블록을 먼저 클릭하세요');
return;
}
saveState();
const currentBody = targetEl.parentElement;
const currentSheet = currentBody.closest('.sheet');
const sheets = Array.from(doc.querySelectorAll('.sheet'));
const currentIndex = sheets.indexOf(currentSheet);
// 클릭한 요소부터 끝까지 수집
const elementsToMove = [];
let sibling = targetEl;
while (sibling) {
elementsToMove.push(sibling);
sibling = sibling.nextElementSibling;
}
if (elementsToMove.length === 0) {
toast('⚠️ 이동할 내용이 없습니다');
return;
}
// 다음 페이지 찾기
let nextSheet = sheets[currentIndex + 1];
let nextBody;
if (!nextSheet || !nextSheet.querySelector('.body-content')) {
const oldHeader = currentSheet.querySelector('.page-header');
const oldFooter = currentSheet.querySelector('.page-footer');
nextSheet = doc.createElement('div');
nextSheet.className = 'sheet';
nextSheet.innerHTML = `
${oldHeader ? oldHeader.innerText : ''}
`;
currentSheet.after(nextSheet);
}
nextBody = nextSheet.querySelector('.body-content');
// 역순으로 맨 앞에 삽입 (순서 유지)
for (let i = elementsToMove.length - 1; i >= 0; i--) {
nextBody.insertBefore(elementsToMove[i], nextBody.firstChild);
}
// 첫 번째 요소에 페이지 브레이크 마커 추가 (나중에 지능형 정렬이 존중함)
targetEl.classList.add('page-break-forced');
// 페이지 번호만 재정렬 (smartAlign 호출 안 함!)
renumberPages(doc);
toast('✅ 다음 페이지로 이동됨');
}
// ===== 전페이지로 이동 (즉시 적용) =====
function moveToPrevPage() {
const doc = getIframeDoc();
if (!doc) {
toast('⚠️ 문서가 로드되지 않았습니다');
return;
}
const selection = doc.getSelection();
if (!selection || !selection.anchorNode) {
toast('⚠️ 이동할 블록을 클릭하세요');
return;
}
// 현재 선택된 요소에서 body-content 직계 자식 찾기
let targetEl = selection.anchorNode.nodeType === 1
? selection.anchorNode
: selection.anchorNode.parentElement;
while (targetEl && targetEl.parentElement) {
if (targetEl.parentElement.classList && targetEl.parentElement.classList.contains('body-content')) {
break;
}
targetEl = targetEl.parentElement;
}
if (!targetEl || !targetEl.parentElement || !targetEl.parentElement.classList.contains('body-content')) {
toast('⚠️ 본문 블록을 먼저 클릭하세요');
return;
}
saveState();
// 현재 sheet 찾기
const currentSheet = targetEl.closest('.sheet');
const sheets = Array.from(doc.querySelectorAll('.sheet'));
const currentIndex = sheets.indexOf(currentSheet);
// 이전 페이지 찾기 (표지 제외)
if (currentIndex <= 1) {
toast('⚠️ 이전 페이지가 없습니다');
return;
}
const prevSheet = sheets[currentIndex - 1];
const prevBody = prevSheet.querySelector('.body-content');
if (!prevBody) {
toast('⚠️ 이전 페이지에 본문 영역이 없습니다');
return;
}
// 요소를 이전 페이지 맨 아래로 이동
prevBody.appendChild(targetEl);
// 현재 페이지가 비었으면 삭제
const currentBody = currentSheet.querySelector('.body-content');
if (currentBody && currentBody.children.length === 0) {
currentSheet.remove();
}
// 페이지 번호 재정렬
renumberPages(doc);
toast('✅ 전 페이지로 이동됨');
}
// ===== 페이지 번호 재정렬 =====
function renumberPages(doc) {
const sheets = doc.querySelectorAll('.sheet');
let pageNum = 1;
sheets.forEach((sheet, idx) => {
if (idx === 0) return; // 표지는 번호 없음
const pgNum = sheet.querySelector('.pg-num');
if (pgNum) {
pgNum.innerText = `- ${pageNum++} -`;
}
});
}
// DOM 로드 시 초기화
document.addEventListener('DOMContentLoaded', initEditor);