2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* =================================================================================
|
|
|
|
|
* common.js - 블로그 공통 스크립트 (최종 수정본)
|
|
|
|
|
* - Quill 에디터 초기화 및 제어 (편집/읽기 모드)
|
|
|
|
|
* - 게시물 데이터 관리 (baseData) 및 서버 통신 (save, post)
|
|
|
|
|
* - UI 제어 (팝업, 컨트롤 박스 동적 설정)
|
|
|
|
|
* - 페이지 이동 및 로그인/로그아웃, 유틸리티 함수
|
|
|
|
|
* =================================================================================
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// 전역 변수: Quill 에디터 인스턴스와 게시물 기본 데이터를 저장합니다.
|
|
|
|
|
var quill = null;
|
|
|
|
|
var currentLat = 0.0;
|
|
|
|
|
var currentLon = 0.0;
|
|
|
|
|
var baseData = {
|
|
|
|
|
'id': "",
|
|
|
|
|
'title': "",
|
|
|
|
|
'content': "",
|
|
|
|
|
'category': "none",
|
|
|
|
|
'tags': "",
|
|
|
|
|
'firstPostLat': 0.0,
|
|
|
|
|
'firstPostLon': 0.0,
|
|
|
|
|
'modifyLat': 0.0,
|
|
|
|
|
'modifyLon': 0.0,
|
|
|
|
|
'originId': "",
|
|
|
|
|
'writeTime': 0,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// jQuery를 사용하여 문서가 완전히 로드된 후에 함수를 실행합니다.
|
|
|
|
|
$(document).ready(function() {
|
|
|
|
|
// 뷰어/에디터 페이지가 아닐 수 있으므로, #editor 요소가 있을 때만 initEditor를 호출하도록 방어 코드를 추가하는 것이 좋습니다.
|
|
|
|
|
// 현재는 각 페이지에서 직접 호출하므로 이 코드는 참고용입니다.
|
|
|
|
|
|
|
|
|
|
// 사이드바의 인기글/최신글 목록을 가져옵니다.
|
2025-08-05 11:24:23 +09:00
|
|
|
if (document.querySelector(".rank_of_view")) {
|
2025-09-05 18:02:27 +09:00
|
|
|
fetchRankOfViews();
|
2025-08-05 11:24:23 +09:00
|
|
|
}
|
|
|
|
|
if (document.querySelector(".recent_posts")) {
|
2025-09-05 18:02:27 +09:00
|
|
|
fetchRecentPosts();
|
2025-08-05 11:24:23 +09:00
|
|
|
}
|
2025-08-08 17:11:34 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
// 팝업 닫기 버튼 이벤트
|
|
|
|
|
$('.btn_layerClose').on('click', function(e) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
closePopup();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 로그인 폼 제출 이벤트
|
|
|
|
|
$('#loginFormElement').on('submit', function(e) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
submitLoginForm();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 로그인 팝업 열기 버튼 이벤트
|
|
|
|
|
$('.open-login-popup').on('click', function() {
|
|
|
|
|
openPopup(this);
|
|
|
|
|
});
|
2025-03-21 17:15:55 +09:00
|
|
|
});
|
2025-08-05 18:01:15 +09:00
|
|
|
|
2024-10-07 16:14:03 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* [핵심] Quill 에디터를 초기화하는 메인 함수입니다.
|
|
|
|
|
* useEditor 파라미터 값에 따라 '편집 모드'와 '읽기 모드'를 동적으로 전환합니다.
|
|
|
|
|
* @param {boolean} useEditor - true: 편집기 활성화, false: 읽기 전용 뷰어 활성화
|
|
|
|
|
*/
|
|
|
|
|
function initEditor(useEditor = false) {
|
|
|
|
|
console.log("### initEditor 함수 실행됨! 편집 모드:", useEditor, "###"); // 이 줄을 추가!
|
|
|
|
|
|
|
|
|
|
const editorContainer = document.querySelector('#editor');
|
|
|
|
|
if (!editorContainer) return;
|
|
|
|
|
|
|
|
|
|
if (typeof serverData !== 'undefined') {
|
|
|
|
|
baseData.id = serverData.id;
|
|
|
|
|
baseData.title = decodeURIComponent(serverData.title || '');
|
|
|
|
|
baseData.content = decodeURIComponent(serverData.content || '');
|
|
|
|
|
baseData.category = serverData.category;
|
|
|
|
|
baseData.tags = serverData.tags;
|
|
|
|
|
baseData.firstPostLat = serverData.firstPostLat;
|
|
|
|
|
baseData.firstPostLon = serverData.firstPostLon;
|
|
|
|
|
baseData.writeTime = serverData.writeTime;
|
|
|
|
|
baseData.originId = serverData.originId;
|
2024-10-07 16:14:03 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
getLocation();
|
2024-10-07 16:14:03 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
try {
|
|
|
|
|
var Font = Quill.import('formats/font');
|
|
|
|
|
Font.whitelist = ['sans-serif', 'serif', 'monospace', 'arial', 'georgia', 'comic-sans-ms', 'courier-new', 'roboto', 'playfair-display'];
|
|
|
|
|
Quill.register(Font, true);
|
|
|
|
|
Quill.register({ 'modules/table-better': QuillTableBetter }, true);
|
|
|
|
|
|
|
|
|
|
const quillOptions = {
|
|
|
|
|
theme: 'snow',
|
|
|
|
|
modules: useEditor ? {
|
|
|
|
|
toolbar: {
|
|
|
|
|
container: [
|
|
|
|
|
[{ font: Font.whitelist }], [{ 'size': ['small', false, 'large', 'huge'] }],
|
|
|
|
|
['bold', 'italic', 'underline', 'strike'], [{ 'color': [] }, { 'background': [] }],
|
|
|
|
|
[{ 'header': 1 }, { 'header': 2 }, 'blockquote', 'code-block'],
|
|
|
|
|
[{ 'script': 'sub'}, { 'script': 'super' }], [{ 'list': 'ordered'}, { 'list': 'bullet' }],
|
|
|
|
|
[{ 'indent': '-1'}, { 'indent': '+1' }], ['link', 'image', 'video'],
|
|
|
|
|
['table-better'], [{ 'direction': 'rtl' }], [{ 'align': [] }], ['clean']
|
|
|
|
|
],
|
|
|
|
|
handlers: { image: function() { selectLocalImage(); }, video: function() { selectLocalVideo(); } }
|
|
|
|
|
},
|
|
|
|
|
'table-better': { language: 'en_US', toolbarTable: true },
|
|
|
|
|
keyboard: { bindings: QuillTableBetter.keyboardBindings }
|
|
|
|
|
} : {
|
|
|
|
|
toolbar: false
|
|
|
|
|
},
|
|
|
|
|
readOnly: !useEditor
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
quill = new Quill(editorContainer, quillOptions);
|
|
|
|
|
|
|
|
|
|
if (baseData.content) {
|
|
|
|
|
loadContent(baseData.content);
|
2024-10-07 16:14:03 +09:00
|
|
|
}
|
2024-12-02 18:32:14 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
if (!useEditor) {
|
|
|
|
|
editorContainer.classList.add('readonly-mode');
|
|
|
|
|
} else {
|
|
|
|
|
editorContainer.classList.remove('readonly-mode');
|
|
|
|
|
const titleField = document.querySelector("#title_field");
|
|
|
|
|
if (titleField) {
|
|
|
|
|
titleField.value = baseData.title;
|
2024-12-02 18:32:14 +09:00
|
|
|
}
|
|
|
|
|
}
|
2025-09-05 18:02:27 +09:00
|
|
|
} catch (e) {
|
|
|
|
|
console.error("Quill initialization failed:", e);
|
2024-12-02 18:32:14 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
setupControlBox(useEditor ? 'edit' : 'view');
|
2024-10-07 16:14:03 +09:00
|
|
|
}
|
2024-10-23 17:06:27 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
function selectLocalImage() {
|
|
|
|
|
// 이미지 URL 입력 받기
|
|
|
|
|
const url = prompt("이미지 URL을 입력하거나 빈칸으로 두시면 파일 업로드를 합니다.");
|
|
|
|
|
|
|
|
|
|
if (url) {
|
|
|
|
|
// URL이 입력된 경우 이미지 삽입
|
|
|
|
|
const range = quill.getSelection(true);
|
|
|
|
|
quill.insertEmbed(range.index, 'image', url);
|
|
|
|
|
quill.setSelection(range.index + 1);
|
2024-10-23 17:06:27 +09:00
|
|
|
} else {
|
2025-09-05 18:02:27 +09:00
|
|
|
// URL이 없거나 취소한 경우 파일 업로드 처리
|
|
|
|
|
const input = document.createElement('input');
|
|
|
|
|
input.setAttribute('type', 'file');
|
|
|
|
|
input.setAttribute('accept', 'image/*');
|
|
|
|
|
input.click();
|
|
|
|
|
|
|
|
|
|
input.onchange = async () => {
|
|
|
|
|
const file = input.files[0];
|
|
|
|
|
if (file) {
|
|
|
|
|
const file = input.files[0];
|
|
|
|
|
console.log("on selectLocalImage File", file);
|
|
|
|
|
if (!file || !file.type.startsWith('image/')) {
|
|
|
|
|
console.warn('이미지 파일만 업로드 가능합니다.');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
uploadImage(file);
|
|
|
|
|
}
|
|
|
|
|
};
|
2024-10-23 17:06:27 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
function uploadImage(blob) {
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
formData.append('file', blob);
|
|
|
|
|
let uploadUrl = getMainPath() + "/blog/post/imageUpload.bjx";
|
|
|
|
|
let imageUrl = getMainPath() + '/blog/post/images/';
|
|
|
|
|
$.ajax({
|
|
|
|
|
type: 'POST',
|
|
|
|
|
enctype: 'multipart/form-data',
|
|
|
|
|
url: uploadUrl,
|
|
|
|
|
data: formData,
|
|
|
|
|
dataType: 'json',
|
|
|
|
|
processData: false,
|
|
|
|
|
contentType: false,
|
|
|
|
|
cache: false,
|
|
|
|
|
timeout: 600000,
|
|
|
|
|
success: function (data) {
|
|
|
|
|
console.log(data);
|
|
|
|
|
imageUrl += data.fileName;
|
|
|
|
|
insertToEditor(imageUrl);
|
|
|
|
|
},
|
|
|
|
|
error: function (e) {
|
|
|
|
|
console.error(e);
|
|
|
|
|
// callback('image_load_fail');
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-12-04 18:01:50 +09:00
|
|
|
}
|
2024-10-25 18:28:25 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
function selectLocalVideo() {
|
|
|
|
|
const input = document.createElement('input');
|
|
|
|
|
input.setAttribute('type', 'file');
|
|
|
|
|
input.setAttribute('accept', 'video/*');
|
|
|
|
|
input.click();
|
|
|
|
|
|
|
|
|
|
input.onchange = () => {
|
|
|
|
|
const file = input.files[0];
|
|
|
|
|
if (!file || !file.type.startsWith('video/')) {
|
|
|
|
|
alert('동영상 파일만 업로드할 수 있습니다.');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
uploadVideo(file);
|
|
|
|
|
};
|
2024-12-04 18:01:50 +09:00
|
|
|
}
|
2025-09-05 18:02:27 +09:00
|
|
|
function uploadVideo(file) {
|
|
|
|
|
const formData = new FormData();
|
|
|
|
|
formData.append('video', file);
|
2024-12-02 18:32:14 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
fetch('/api/upload/video', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: formData
|
|
|
|
|
})
|
|
|
|
|
.then(res => res.json())
|
|
|
|
|
.then(result => {
|
|
|
|
|
if (result.url) {
|
|
|
|
|
const range = quill.getSelection(true);
|
|
|
|
|
quill.insertEmbed(range.index, 'video', result.url);
|
|
|
|
|
quill.setSelection(range.index + 1);
|
|
|
|
|
} else {
|
|
|
|
|
console.error('동영상 업로드 실패', result);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(err => {
|
|
|
|
|
console.error('업로드 중 오류', err);
|
|
|
|
|
});
|
2025-09-01 17:23:40 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-02 17:32:02 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
function insertToEditor(url) {
|
|
|
|
|
const range = quill.getSelection(true);
|
|
|
|
|
quill.insertEmbed(range.index, 'image', url);
|
|
|
|
|
quill.setSelection(range.index + 1);
|
2025-09-02 17:32:02 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 에디터 모드('edit' 또는 'view')에 따라 컨트롤 박스를 설정합니다.
|
|
|
|
|
*/
|
|
|
|
|
function setupControlBox(mode) {
|
|
|
|
|
const categoryBox = document.querySelector('.controlbox-category');
|
|
|
|
|
const hashtagBox = document.querySelector('.controlbox-hashtag');
|
|
|
|
|
|
|
|
|
|
if (!categoryBox || !hashtagBox) return;
|
|
|
|
|
|
|
|
|
|
if (mode === 'edit') {
|
|
|
|
|
categoryBox.setAttribute('onclick', 'openPopup(this)');
|
|
|
|
|
hashtagBox.setAttribute('onclick', 'openPopup(this)');
|
|
|
|
|
categoryBox.innerText = '카테고리 설정';
|
|
|
|
|
hashtagBox.innerText = '해시태그 편집';
|
|
|
|
|
fetchCategoriesAndHashtags();
|
|
|
|
|
} else {
|
|
|
|
|
categoryBox.removeAttribute('onclick');
|
|
|
|
|
hashtagBox.removeAttribute('onclick');
|
|
|
|
|
categoryBox.classList.remove('btn-example');
|
|
|
|
|
hashtagBox.classList.remove('btn-example');
|
|
|
|
|
|
|
|
|
|
categoryBox.innerHTML = `<span class="tag-title">카테고리: </span><span class="tag-item">${baseData.category || '지정되지 않음'}</span>`;
|
|
|
|
|
|
|
|
|
|
hashtagBox.innerHTML = '<span class="tag-title">태그: </span>';
|
|
|
|
|
if (baseData.tags && baseData.tags.length > 0) {
|
|
|
|
|
baseData.tags.split(',').forEach(tag => {
|
|
|
|
|
hashtagBox.innerHTML += `<span class="tag-item">#${tag.trim()}</span>`;
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
hashtagBox.innerHTML += '<span>없음</span>';
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-12-04 18:01:50 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 백엔드 API를 호출하여 카테고리와 해시태그 목록을 가져와 팝업을 채웁니다.
|
|
|
|
|
*/
|
|
|
|
|
function fetchCategoriesAndHashtags() {
|
|
|
|
|
fetch(`${getMainPath()}/blog/categories.bjx`).then(res => res.json()).then(data => {
|
|
|
|
|
if (data.resultCode === 0 && data.tags) {
|
|
|
|
|
const list = document.querySelector('#category-list');
|
|
|
|
|
if(list) {
|
|
|
|
|
list.innerHTML = '';
|
|
|
|
|
data.tags.forEach(tag => {
|
|
|
|
|
const el = document.createElement('span');
|
|
|
|
|
el.className = 'tag-item';
|
|
|
|
|
el.innerText = tag;
|
|
|
|
|
list.appendChild(el);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}).catch(err => console.error('Error fetching categories:', err));
|
|
|
|
|
|
|
|
|
|
fetch(`${getMainPath()}/blog/hashtags.bjx`).then(res => res.json()).then(data => {
|
|
|
|
|
if (data.resultCode === 0 && data.tags) {
|
|
|
|
|
const list = document.querySelector('#hashtag-list');
|
|
|
|
|
if(list) {
|
|
|
|
|
list.innerHTML = '';
|
|
|
|
|
data.tags.forEach(tag => {
|
|
|
|
|
const el = document.createElement('span');
|
|
|
|
|
el.className = 'tag-item';
|
|
|
|
|
el.innerText = `#${tag}`;
|
|
|
|
|
list.appendChild(el);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}).catch(err => console.error('Error fetching hashtags:', err));
|
|
|
|
|
}
|
2025-03-10 17:55:48 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 컨텐츠를 Quill 에디터에 로드합니다.
|
|
|
|
|
*/
|
|
|
|
|
function loadContent(content) {
|
|
|
|
|
try {
|
|
|
|
|
const delta = JSON.parse(content);
|
|
|
|
|
if (delta && Array.isArray(delta.ops)) {
|
|
|
|
|
quill.setContents(delta);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} catch (e) { /* HTML 문자열일 경우 아래에서 처리 */ }
|
|
|
|
|
quill.clipboard.dangerouslyPasteHTML(content);
|
|
|
|
|
}
|
2025-08-04 16:35:49 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 게시물 수정 페이지로 이동합니다.
|
|
|
|
|
*/
|
|
|
|
|
function loadEditor() {
|
|
|
|
|
if (baseData.id) {
|
|
|
|
|
location.href = `${getMainPath()}/blog/edit/${baseData.id}`;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-04 16:35:49 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 작성된 게시물을 서버에 저장합니다.
|
|
|
|
|
*/
|
|
|
|
|
function save() {
|
|
|
|
|
const titleField = document.getElementById('title_field');
|
|
|
|
|
if (titleField) {
|
|
|
|
|
baseData.title = encodeURIComponent(titleField.value);
|
|
|
|
|
}
|
2025-08-04 16:35:49 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
baseData.content = encodeURIComponent(JSON.stringify(quill.getContents()));
|
|
|
|
|
baseData.modifyLat = currentLat;
|
|
|
|
|
baseData.modifyLon = currentLon;
|
2025-08-04 16:35:49 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
const uploadUrl = `${getMainPath()}/blog/post.bjx`;
|
|
|
|
|
if (confirm("해당 내용으로 저장하시겠습니까?")) {
|
|
|
|
|
post(uploadUrl, serverData.enc, JSON.stringify(baseData), serverData.keyword, function(resultData) {
|
|
|
|
|
alert("저장되었습니다.");
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-12-02 18:32:14 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 사용자의 현재 위치(위도, 경도)를 가져옵니다.
|
|
|
|
|
*/
|
|
|
|
|
function getLocation() {
|
|
|
|
|
if (navigator.geolocation) {
|
|
|
|
|
navigator.geolocation.getCurrentPosition(pos => {
|
|
|
|
|
currentLat = pos.coords.latitude;
|
|
|
|
|
currentLon = pos.coords.longitude;
|
|
|
|
|
if (baseData.firstPostLat === 0.0) baseData.firstPostLat = currentLat;
|
|
|
|
|
if (baseData.firstPostLon === 0.0) baseData.firstPostLon = currentLon;
|
|
|
|
|
baseData.modifyLat = currentLat;
|
|
|
|
|
baseData.modifyLon = currentLon;
|
|
|
|
|
const locationField = document.getElementById('location_field');
|
|
|
|
|
if (locationField) {
|
|
|
|
|
locationField.textContent = `Lat: ${currentLat.toFixed(4)}, Lon: ${currentLon.toFixed(4)}`;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-08-05 11:24:23 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 팝업 레이어를 엽니다.
|
|
|
|
|
*/
|
|
|
|
|
function openPopup(element) {
|
|
|
|
|
const targetId = element.getAttribute('to');
|
|
|
|
|
const popup = document.querySelector(targetId);
|
|
|
|
|
const overlay = document.querySelector('.dim_layer');
|
|
|
|
|
if (popup && overlay) {
|
|
|
|
|
overlay.style.display = 'block';
|
|
|
|
|
popup.style.display = 'block';
|
|
|
|
|
}
|
2024-10-25 18:28:25 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 팝업 레이어를 닫습니다.
|
|
|
|
|
*/
|
|
|
|
|
function closePopup() {
|
|
|
|
|
const overlay = document.querySelector('.dim_layer');
|
|
|
|
|
if(overlay) overlay.style.display = 'none';
|
|
|
|
|
document.querySelectorAll('.pop_layer').forEach(p => p.style.display = 'none');
|
2024-12-05 18:15:20 +09:00
|
|
|
}
|
2024-10-25 18:28:25 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 게시물 상세 보기 페이지로 이동합니다.
|
|
|
|
|
*/
|
|
|
|
|
function goToViewer(element) {
|
|
|
|
|
if (element && element.id) {
|
|
|
|
|
location.href = `${getMainPath()}/blog/viewer/${element.id}`;
|
|
|
|
|
}
|
2025-08-05 11:24:23 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
// =================================================================================
|
|
|
|
|
// [복구] 이하 누락되었던 함수들
|
|
|
|
|
// =================================================================================
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 인기글 목록을 가져와 UI에 표시합니다.
|
|
|
|
|
*/
|
|
|
|
|
function fetchRankOfViews() {
|
|
|
|
|
fetch(`${getMainPath()}/blog/rankOfViews.bjx`).then(res => res.json()).then(data => {
|
|
|
|
|
const ul = document.querySelector('.rank_of_view');
|
|
|
|
|
if (ul && data.posts) {
|
|
|
|
|
ul.innerHTML = '';
|
|
|
|
|
data.posts.forEach(item => {
|
|
|
|
|
const date = new Date(item.writeTime);
|
|
|
|
|
const formattedDate = `${date.getFullYear()}/${String(date.getMonth() + 1).padStart(2, '0')}/${String(date.getDate()).padStart(2, '0')}`;
|
|
|
|
|
ul.innerHTML += `<li><a href="${getMainPath()}/blog/viewer/${item.id}">${item.title}<br>[${formattedDate}]</a></li>`;
|
|
|
|
|
});
|
2024-12-05 18:15:20 +09:00
|
|
|
}
|
2025-09-05 18:02:27 +09:00
|
|
|
}).catch(error => console.error('Failed to fetch rank of views:', error));
|
2024-10-25 18:28:25 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 최신글 목록을 가져와 UI에 표시합니다.
|
|
|
|
|
*/
|
|
|
|
|
function fetchRecentPosts() {
|
|
|
|
|
fetch(`${getMainPath()}/blog/recentOfPost.bjx`).then(res => res.json()).then(data => {
|
|
|
|
|
const ul = document.querySelector('.recent_posts');
|
|
|
|
|
if (ul && data.posts) {
|
|
|
|
|
ul.innerHTML = '';
|
|
|
|
|
data.posts.forEach(item => {
|
|
|
|
|
const date = new Date(item.writeTime);
|
|
|
|
|
const formattedDate = `${date.getFullYear()}/${String(date.getMonth() + 1).padStart(2, '0')}/${String(date.getDate()).padStart(2, '0')}`;
|
|
|
|
|
ul.innerHTML += `<li><a href="${getMainPath()}/blog/viewer/${item.id}">${item.title}<br>[${formattedDate}]</a></li>`;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}).catch(error => console.error('Failed to fetch recent posts:', error));
|
2024-10-23 17:06:27 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 로그인 폼 데이터를 서버에 전송합니다.
|
|
|
|
|
*/
|
|
|
|
|
function submitLoginForm() {
|
|
|
|
|
const data = {
|
|
|
|
|
'user_id': $('#loginId').val(),
|
|
|
|
|
'user_pw': $('#loginPassword').val(),
|
|
|
|
|
'rememberMe': $('#rememberMe').is(':checked'),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
postLogin(`${getMainPath()}/user/login.bjx`, serverData.enc, JSON.stringify(data), serverData.keyword, function(response) {
|
|
|
|
|
if (response.isOk) {
|
|
|
|
|
location.reload();
|
|
|
|
|
} else {
|
|
|
|
|
alert(`로그인 실패: ${response.resultMsg}`);
|
|
|
|
|
}
|
2024-10-23 17:06:27 +09:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
// --- 페이지 이동(Navigation) 함수들 ---
|
|
|
|
|
function gotoHome() { document.location.replace(`${getMainPath()}/home.bs`); }
|
|
|
|
|
function gotoWrite() { document.location.replace(`${getMainPath()}/blog/edit`); } // 수정된 URL
|
|
|
|
|
function gotoModify() { document.location.replace(`${getMainPath()}/blog/posts`); } // 수정된 URL
|
|
|
|
|
function gotoLogin() { document.location.replace(`${getMainPath()}/login.bs`); }
|
|
|
|
|
function gotoJoin() { document.location.replace(`${getMainPath()}/user/join.bs`); }
|
2024-10-23 17:06:27 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
/**
|
|
|
|
|
* 로그아웃을 처리합니다.
|
|
|
|
|
*/
|
|
|
|
|
function logout() {
|
|
|
|
|
const form = document.createElement('form');
|
|
|
|
|
form.method = 'POST';
|
|
|
|
|
form.action = `${getMainPath()}/user/logout.bs`;
|
2024-10-23 17:06:27 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
// Spring Security CSRF 토큰 추가
|
|
|
|
|
const csrfToken = document.querySelector('meta[name="_csrf"]').getAttribute('content');
|
|
|
|
|
const csrfParam = document.querySelector('meta[name="_csrf_parameter"]').getAttribute('content');
|
|
|
|
|
if (csrfToken && csrfParam) {
|
|
|
|
|
const csrfInput = document.createElement('input');
|
|
|
|
|
csrfInput.type = 'hidden';
|
|
|
|
|
csrfInput.name = csrfParam;
|
|
|
|
|
csrfInput.value = csrfToken;
|
|
|
|
|
form.appendChild(csrfInput);
|
|
|
|
|
}
|
2024-10-23 17:06:27 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
document.body.appendChild(form);
|
|
|
|
|
form.submit();
|
2025-03-21 17:15:55 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
// =================================================================================
|
|
|
|
|
// 서버 통신 및 암호화 관련 유틸리티 함수들 (기존 코드 유지)
|
|
|
|
|
// =================================================================================
|
2025-03-21 17:15:55 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
function getMainPath() {
|
|
|
|
|
return location.protocol + "//" + location.hostname + (location.port ? ':' + location.port : '');
|
2025-03-21 17:15:55 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
function post(target, type, data, key, callBackResult) {
|
|
|
|
|
const httpRequest = new XMLHttpRequest();
|
|
|
|
|
httpRequest.onreadystatechange = () => {
|
|
|
|
|
if (httpRequest.readyState === XMLHttpRequest.DONE) {
|
|
|
|
|
if (httpRequest.status === 200) callBackResult(httpRequest.response);
|
|
|
|
|
else alert('Request Error!');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
httpRequest.open('POST', target, true);
|
|
|
|
|
httpRequest.setRequestHeader("Content-Type", "text/plain");
|
|
|
|
|
httpRequest.send(btoa(JSON.stringify({
|
|
|
|
|
'data': unformat(type, data, key), 'key': key, 'type': type,
|
|
|
|
|
})));
|
2025-03-21 17:15:55 +09:00
|
|
|
}
|
|
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
function postLogin(target, type, data, key, callBackResult) {
|
|
|
|
|
const httpRequest = new XMLHttpRequest();
|
|
|
|
|
httpRequest.onreadystatechange = () => {
|
|
|
|
|
if (httpRequest.readyState === XMLHttpRequest.DONE) {
|
|
|
|
|
if (httpRequest.status === 200) {
|
|
|
|
|
try {
|
|
|
|
|
callBackResult(JSON.parse(httpRequest.response));
|
|
|
|
|
} catch (e) { console.error("Login response parse error:", e); }
|
|
|
|
|
} else { alert('Request Error!'); }
|
2025-03-21 17:15:55 +09:00
|
|
|
}
|
2025-09-05 18:02:27 +09:00
|
|
|
};
|
|
|
|
|
httpRequest.withCredentials = true;
|
|
|
|
|
httpRequest.open('POST', target, true);
|
|
|
|
|
httpRequest.setRequestHeader("Content-Type", "text/plain");
|
|
|
|
|
httpRequest.send(btoa(JSON.stringify({
|
|
|
|
|
'data': unformat(type, data, key), 'key': key, 'type': type,
|
|
|
|
|
})));
|
2025-08-05 16:14:01 +09:00
|
|
|
}
|
2025-03-21 17:15:55 +09:00
|
|
|
|
2025-09-05 18:02:27 +09:00
|
|
|
function unformat(type, data, key) {
|
|
|
|
|
var even = [], odd = [];
|
|
|
|
|
data.split("").forEach((v, idx) => (idx % 2 === 0 ? even.push(v) : odd.push(v)));
|
|
|
|
|
const dividerStr = ["%7C%2A-%2A%7C", key, "%7C%2A-%2A%7C"].join("");
|
|
|
|
|
switch (type) {
|
|
|
|
|
case "T0":
|
|
|
|
|
return [odd.join(""), dividerStr, even.join("")].join("");
|
|
|
|
|
case "T1":
|
|
|
|
|
return [odd.reverse().join(""), dividerStr, even.join("")].join("");
|
|
|
|
|
case "T2":
|
|
|
|
|
return [odd.join(""), dividerStr, even.reverse().join("")].join("");
|
|
|
|
|
default:
|
|
|
|
|
return [odd.reverse().join(""), dividerStr, even.reverse().join("")].join("");
|
2025-08-05 16:14:01 +09:00
|
|
|
}
|
2024-10-23 17:06:27 +09:00
|
|
|
}
|