${title}
${$("
$(document).ready(function () { 'use strict'; // ========================= // Config & Selectors // ========================= const API_BASE_URL = `${typeof hostURL !== 'undefined' ? hostURL : ''}/ajax/api.php`; const SELECTOR = { viewList: '#viewList', viewWrite: '#viewWrite', viewDetail: '#viewDetail', postList: '#postList', writeForm: '#writeForm', categorySelect: '#categorySelect', boardPagination: '#boardPagination', boardCategory: '#board_catagory' }; const urlParams = new URLSearchParams(window.location.search); const modeParam = urlParams.get('mode'); const idParam = urlParams.get('id'); const currentPageFileName = window.location.pathname.split('/').pop(); if (currentPageFileName.includes('manager')) { var current_page_manager = currentPageFileName; var current_page = currentPageFileName.replace('_manager', ''); } else { var current_page = currentPageFileName; var current_page_manager = currentPageFileName.replace('.html', '_manager.html'); } // 페이지 관련 변수 let currentDetailRaw = null; const PAGE_SIZE = 20; let currentPostData = []; let currentPage = 1; let totalPageCount = 1; let totalPostCount = 0; let currentSearchType = null; let currentSearchKeyword = null; let currentCategory = null; var rowListCount = 20; var rowImageCount = 4; // 파일 업로드 관련 상태 let dt = new DataTransfer(); let existingFiles = []; let deletedFileIds = []; // DOM 캐싱 const $dropZone = $('#file-upload-box'); const $fileInput = $('#fileInput'); const $fileListContainer = $('#fileListContainer'); const $fileItemsList = $('#fileItemsList'); const $fileCount = $('#fileCount'); const $fileTotalSize = $('#fileTotalSize'); // ========================= // Init & Event Binding // ========================= initPage(); bindEvents(); // ========================= // Core & Page Init // ========================= function initPage() { if ($(SELECTOR.viewList).length) { loadPosts(); } if ($(SELECTOR.viewWrite).length || $(SELECTOR.viewDetail).length) { if (modeParam === 'detail' && idParam) { showPostDetail(idParam); } else if (modeParam === 'edit' && idParam) { loadCategoryOptions().then(() => loadPostForEdit(idParam)); } else { loadCategoryOptions().then(() => { resetWriteForm(); showWriteView(); }); } } } // ========================= // Core & Event Init // ========================= function bindEvents() { $('#btnGoWrite').on('click', () => { location.href = `${current_page_manager}?mode=write`; }); $('#btnSearch').on('click', onClickSearch); $('#searchForm').on('submit', onSubmitSearch); // 체크박스 & 선택 삭제 $('#checkAll').on('change', onChangeCheckAll); $(document).on('change', '.row-check', onChangeRowCheck); $('#btnDeleteSelected').on('click', onClickDeleteSelected); // 분류 추가/설정 모달 $('#categoryForm').on('submit', onSubmitCategoryForm); $('#boardSetupModal').on('show.bs.modal', loadboardSetup); $('#btnSaveBoardSetup').on('click', onClickSaveBoardSetup); // 이벤트 위임 $(document) .on('click', '#postList .title-cell', onClickTitleCell) .on('click', '#board_catagory .category-pill-btn', onClickCategory) .on('click', '#boardPagination .page-link', onClickPageBtn) .on('click', '#btnBackToList', () => { location.href = `${current_page}`; }) .on('click', '#btnDeleteCurrentPost', onClickDeleteCurrent) .on('click', '#btnEditCurrentPost', onClickEditCurrent); // 작성 폼 관련 $('#btnCancelWrite, #btnCancelWriteBottom').on('click', onClickCancelWrite); $(SELECTOR.writeForm).on('submit', onSubmitWriteForm); // 드래그 앤 드롭 & 파일 인풋 $fileInput.on('change', function () { if (this.files?.length) appendFiles(this.files); }); $dropZone .on('dragover dragenter', (e) => { e.preventDefault(); e.stopPropagation(); $dropZone.addClass('is-dragover'); }) .on('dragleave dragend drop', (e) => { e.preventDefault(); e.stopPropagation(); $dropZone.removeClass('is-dragover'); }) .on('drop', (e) => { const droppedFiles = e.originalEvent.dataTransfer.files; if (droppedFiles?.length) appendFiles(droppedFiles); }); $fileItemsList .on('click', '.btn-remove-file', onRemoveNewFile) .on('click', '.btn-remove-existing-file', onRemoveExistingFile); $('#btnClearAllFiles').on('click', onClearAllFiles); } function getBoardName() { return $('#board_name').val() || 'post_notice'; } // ========================= // Util Functions // ========================= function escapeHtml(str) { if (!str) return ''; return String(str) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function formatBytes(bytes) { if (!bytes || bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } function resolveFileUrl(rawPath) { if (!rawPath) return ''; if (/^(https?:)?\/\//i.test(rawPath)) return rawPath; const base = (typeof hostURL !== 'undefined' ? hostURL : '').replace(/\/+$/, ''); return `${base}/${String(rawPath).replace(/^\/+/, '')}`; } function extractFirstContentImageUrl(content) { if (!content) return ''; try { const doc = new DOMParser().parseFromString(content, 'text/html'); const img = doc.querySelector('img'); const src = img ? (img.getAttribute('src') || '') : ''; return src ? resolveFileUrl(src) : ''; } catch (e) { return ''; } } function getGalleryThumbnailUrl(item) { if (item.first_image_attachment?.file_path) { return resolveFileUrl(item.first_image_attachment.file_path); } return extractFirstContentImageUrl(item.content); } // ========================= // File Management // ========================= function updateFileListUI() { const newFiles = dt.files; const totalCount = existingFiles.length + newFiles.length; if (totalCount === 0) { $fileItemsList.empty(); $fileListContainer.addClass('d-none'); $fileInput[0].files = dt.files; return; } $fileListContainer.removeClass('d-none'); $fileCount.text(totalCount); let totalBytes = 0; let htmlBuffer = ''; // 1. 기존 파일 렌더링 existingFiles.forEach((file, index) => { const size = Number(file.file_size || file.size || 0); totalBytes += size; const fileName = file.origin_name || file.original_name || file.name || '첨부파일'; htmlBuffer += `
${$("