$(function() {
// 댓글입력 창 높이 자동조정
$.each($('textarea[data-autoresize]'), function () {
$(this).on('keyup input', function () {
Comment.resizeTextarea(this);
}).removeAttr('data-autoresize');
});
});
const Comment =
{
callback: "",
listID: "commentList",
writeID: "commentWrite",
method: {write: "store", edit: "update", reply: "reply"},
mode: "",
code: "",
bid: 0,
pid: 0,
cid: 0,
uid: 0,
total: 0,
page: 0,
perPage: 0,
minLength: 0,
maxLength: 0,
listForm: null,
writeForm: null,
saveHtml: null,
saveBefore: null,
useEditor: false,
queryString: null,
tinymce: null,
// 댓글 기본설정
init: function(config)
{
if(!config.hasOwnProperty("code") && !config.hasOwnProperty("bid") && !config.hasOwnProperty("pid")) {
return false;
}
this.code = config.code;
this.bid = config.bid;
this.pid = config.pid;
this.callback = (BASE_URL + "/board/" + String(this.code) + "/" + String(this.pid) + "/comment");
this.listForm = document.getElementById(this.listID);
this.writeForm = document.getElementById(this.writeID);
// 댓글 입력창
let form = this.writeForm.getElementsByTagName("form")[0];
this.minLength = Number(form.elements["min_length"].value);
this.maxLength = Number(form.elements["max_length"].value);
this.saveHtml = this.writeForm.innerHTML; // 댓글 입력 창 저장
},
// 초기화
reset: function(form)
{
this.mode = "write";
this.cid = 0;
this.saveBefore = null;
if (typeof form !== "undefined") {
if(form.elements["mode"].value !== "write") {
form.style.display = "none";
}
form.elements["mode"].value = "write";
form.elements["bid"].value = "";
form.elements["pid"].value = "";
form.elements["cid"].value = "";
form.elements["uid"].value = "";
form.elements["length"].value = "0";
form.elements["content"].value = "";
if (form.elements.hasOwnProperty("username")) {
form.elements["username"].value = "";
}
if (form.elements.hasOwnProperty("password")) {
form.elements["password"].value = "";
}
if (form.elements.hasOwnProperty("is_secret")) {
form.elements["is_secret"].checked = false;
}
let length = form.getElementsByClassName("comment-length");
if (length.length > 0) {
length[0].innerText = "0";
}
form.elements["btn_comment_submit"].disabled = false;
form.elements["btn_comment_cancel"].disabled = false;
this.resizeTextarea(form.elements["content"]);
}
if (this.useEditor) {
tinyMCE.activeEditor.setContent("");
}
},
// 댓글 목록 새로고침
refresh: function() {
this.list(this.page);
},
// textarea 높이 조절
resizeTextarea: function(e)
{
$(e).css("height", "auto").css("height", e.scrollHeight + (e.offsetHeight - e.clientHeight));
},
// 댓글 영역으로 스크롤 이동
scrollReplyNow: function(cid)
{
if (typeof cid != "undefined" && cid) {
let offset = $("#comment_" + cid).offset();
if (typeof offset != "undefined") {
window.document.body.scrollTop = offset.top;
document.getElementById("comment_" + cid).scrollIntoView();
}
}
},
// 댓글 영역 강조
focusCommentBox: function(cid)
{
if (typeof cid != "undefined" && cid) {
$("#comment_" + cid).effect("highlight", {
'color': '#4f4f4f'
}, 1500);
}
},
// 금지 단어 확인
filterSpamKeyword: function (content)
{
let ret = "";
$.ajax({
headers: {"X-CSRF-TOKEN": CSRF},
url: (BASE_URL + "/api/filterSpamKeyword"),
type: "post",
dataType: "json",
async: false,
cache: false,
data: {subject: "", content: content},
success: function (res) {
ret = res.content;
},
error: function (xhr, status, err) {
procErrorEvent(xhr, status, err);
}
});
return ret;
},
// 글자수 확인
checkByte: function(e)
{
let form = e.form;
if (typeof form !== "undefined") {
let hiddenInputLength = form.elements["length"];
let spanInputLength = form.getElementsByClassName("comment-length")[0];
let textarea = form.elements["content"];
let length = textarea.value.length;
let tinymce = form.getElementsByTagName("iframe");
if (this.useEditor) {
let editor = tinymce[0].contentDocument.body;
length = editor.innerText.trim().length;
if (this.maxLength > 0 && length > this.maxLength) {
editor.innerHTML = editor.innerHTML.substr(0, this.maxLength);
length = editor.innerHTML.length;
}
} else {
if (this.maxLength > 0 && length > this.maxLength) {
textarea.value = textarea.value.substr(0, this.maxLength);
length = textarea.value.length;
}
}
// 글자 수 저장
hiddenInputLength.value = length;
// 글자 수 출력
if (typeof spanInputLength !== "undefined") {
spanInputLength.innerText = length;
}
}
},
// 목록
list: function(page, cid, message, sort) {
if(!this.code && !this.bid && !this.pid) {
return false;
}
if (!page || page < 1 || page === Number.POSITIVE_INFINITY) {
page = 1;
}
let listURL = (this.callback + "?page=" + page);
if (sort) {
listURL += "&sort=" + sort;
}
$('#' + this.listID).load(listURL, function (response, status, xhr) { // HTML 형식으로 뎃글목록 출력
if(status !== "success") {
return;
}
let total = this.children.total.value;
let page = this.children.page.value;
let perPage = this.children.per_page.value;
document.getElementById("txtCommentRows").innerText = total;
document.getElementById("txtPostCommentCnt").innerText = total;
if (message) { // 알림이 있을 경우 alert
alert(message);
}
Comment.total = total;
Comment.page = page;
Comment.perPage = perPage;
Comment.focusCommentBox(cid);
Comment.scrollReplyNow(cid);
});
},
// 댓글 등록/수정
submit: function(e)
{
e.blur();
e.disabled = true;
$(e.form).validate({
onkeyup: false,
onclick: false,
onfocusout: false,
rules: {
username: {required: "#username:enabled", minlength: 2, maxlength: 10},
password: {required: "#password:enabled", minlength: 3, maxlength: 10},
mode: {required: true, contains: ["write", "edit", "reply"]},
content: {
required: true,
normalizer: function (value) {
if(Comment.useEditor) {
return tinyMCE.activeEditor.getContent({format: "text"}).trim();
}else{
return value.trim();
}
},
minlength: Comment.minLength,
maxlength: function() {
return (Comment.maxLength > 0 ? Comment.maxLength : 500)
}
},
is_secret: {number: true}
},
messages: {
username: {required: "이름을 입력하세요.", minlength: "이름은 두 글자 이상 입력하세요.", maxlength: "이름은 10자까지 입력 가능합니다."},
password: {required: "비밀번호를 입력하세요.", minlength: "비밀번호는 3자 이상 입력하세요.", maxlength: "비밀번호는 10자까지 입력 가능합니다."},
mode: {required: "댓글 처리중 오류가 발생하였습니다.", contains: "잘못된 요청입니다."},
content: {required: "댓글을 입력해주세요."},
is_secret: {number: true}
},
showErrors: function (errorMap, errorList) {
if (this.numberOfInvalids()) {
setTimeout(function() {
alert(errorList[0].message);
errorList[0].element.focus();
if(Comment.useEditor) {
tinyMCE.activeEditor.getBody().scrollIntoView();
}else{
window.scrollTo({top: errorList[0].element.offsetTop});
}
}, 100);
e.disabled = false;
}
},
submitHandler: function (form)
{
let formData = new FormData(form);
let spamKeyword = Comment.filterSpamKeyword(formData.get("content")); // 금지 단어 검사
if (spamKeyword) {
alert("내용에 금지어 ('" + spamKeyword + "')가 포함 되어있습니다.");
form.elements["content"].focus();
return false;
}
let page;
let mode = formData.get("mode");
if(mode === "write") {
formData.append("bid", Comment.bid);
formData.append("pid", Comment.pid);
page = Math.ceil((Number(Comment.total) + 1) / Comment.perPage);
}else{
page = Comment.page;
}
if(mode === "edit") {
formData.append("_method", "PUT");
}
// 버튼 비활성화
form.elements["btn_comment_submit"].disabled = true;
form.elements["btn_comment_cancel"].disabled = true;
$.ajax({
headers: {'X-CSRF-TOKEN': CSRF},
url: (Comment.callback + "/" + Comment.method[mode]),
type: "post",
cache: false,
async: true,
contentType: false,
processData: false,
data: formData,
dataType: "json",
beforeSend: function () {
if (formData.get("uid")) { // 권한 확인
if (!loginCheck()) {
return xhr.abort();
}
}
showLoading();
},
success: function (res) {
if (res.success) {
Comment.list(page, res.cid);
}else{
alert(res.message);
}
},
error: function (xhr, status, err) {
procErrorEvent(xhr, status, err);
},
complete: function() {
Comment.reset(form);
hideLoading();
}
});
}
});
$(e.form).submit();
},
// 수정, 답글
write: function(e, mode)
{
e.blur();
if(typeof e === "undefined") {
return false;
}
if(typeof mode === "undefined" || mode == null || !mode) {
return false;
}
if(!mode in this.method) {
return false;
}
let formData = new FormData(e.form); // 댓글 영역
let mediaFormID = ("#fCommentWrite_" + formData.get("cid")); // 현재 답글 번호
let $saveBefore = $(this.saveBefore); // 이전 댓글 창
let $target = $(mediaFormID); // 현재 댓글 창
if(this.cid === formData.get("cid") && this.saveBefore === mediaFormID && this.mode === mode) {
$saveBefore.toggle();
}else{
$saveBefore.html("");
$target.show();
let writeForm = $target.html(this.saveHtml).get(0).firstElementChild;
// 답글
if(mode === "reply") {
if(writeForm.elements.hasOwnProperty("username")) {
writeForm.elements["username"].value = "";
}
if(writeForm.elements.hasOwnProperty("is_secret")) {
writeForm.elements["is_secret"].checked = false;
}
writeForm.elements["content"].value = "";
}
// 수정
if(mode === "edit") {
if(writeForm.elements.hasOwnProperty("username")) {
writeForm.elements["username"].value = formData.get("username");
}
if(writeForm.elements.hasOwnProperty("is_secret")) {
writeForm.elements["is_secret"].checked = Boolean(formData.get("is_secret").checked);
}
writeForm.elements["content"].value = formData.get("content");
// 입력창 높이 조절
this.resizeTextarea(writeForm.elements["content"]);
}
writeForm.elements["mode"].value = mode;
writeForm.elements["bid"].value = formData.get("bid");
writeForm.elements["pid"].value = formData.get("pid");
writeForm.elements["cid"].value = formData.get("cid");
writeForm.elements["uid"].value = formData.get("uid");
// textarea ID 할당
if(writeForm) {
writeForm.elements["content"].setAttribute("id", formData.get("content") + "_" + formData.get("cid"));
// 1:1 문의 게시판은 에디터를 사용
if(this.useEditor) {
tinyMCE.EditorManager.execCommand('mceAddEditor', true, writeForm.elements["content"]);
}
// 글자 수 확인
this.checkByte(formData.get("content"));
}
}
this.mode = mode;
this.cid = formData.get("cid");
this.saveBefore = mediaFormID; // 현재 영역
},
// 댓글 삭제
delete: function(e)
{
e.blur();
let formData = new FormData(e.form);
$.ajax({
url: (this.callback + "/delete"),
type: "delete",
cache: false,
async: false,
data: {bid: formData.get("bid"), pid: formData.get("pid"), cid: formData.get("cid"), _method: "DELETE"},
dataType: "json",
beforeSend: function(xhr) {
if (!formData.get("uid")) { // 권한 확인
const password = prompt("댓글 비밀번호 확인이 필요합니다.\n비밀번호를 입력해주세요.");
if (!password) {
return false;
}
this.url += ("?passwd=" + encodeURIComponent(password.trim()));
} else {
if (!loginCheck()) {
return xhr.abort();
}
}
if (!confirm("댓글을 삭제하시겠습니까?")) {
return false;
}
},
success: function (res) {
if (res.success) {
alert("댓글이 삭제되었습니다.");
Comment.refresh();
}else{
alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오.");
}
Comment.reset();
},
error: function (xhr, status, err) {
procErrorEvent(xhr, status, err);
}
});
return false;
},
// 댓글 신고
blame: function(e)
{
e.blur();
if(!loginCheck()) {
return false;
}
// 신고 접수 유효성 적용
$("#fCommentBlame").validate({
onkeyup: false,
onclick: false,
onfocusout: false,
rules: {
type: {required: true, contains: ['1', '2', '3', '4', '5', '6', '7', '8', '9']},
reason: {
required: true, normalizer: function (value) {
return $.trim(value);
}, maxlength: 1000
}
},
messages: {
type: {required: "신고 유형을 선택해주세요.", contains: "잘못된 요청입니다."},
reason: {required: "신고 내용을 입력해주세요.", maxlength: "신고 내용은 {0}자까지 입력 가능합니다."}
},
showErrors: function (errorMap, errorList) {
if (this.numberOfInvalids()) {
alert(errorList[0].message);
errorList[0].element.focus();
}
},
submitHandler: function (form) {
if (confirm("신고를 접수하시겠습니까?")) {
let formData = new FormData(form);
formData.append("bid", e.form.elements["bid"].value);
formData.append("pid", e.form.elements["pid"].value);
formData.append("cid", e.form.elements["cid"].value);
$.ajax({
url: (Comment.callback + "/blame"),
type: "post",
cache: false,
contentType: false,
processData: false,
data: formData,
dataType: "json",
beforeSend: function (xhr) {
if (!loginCheck()) {
xhr.abort();
}
},
success: function (res) {
if (res.success) {
alert("감사합니다. 신고가 접수되었습니다. 이용 규칙을 위반한 것으로 확인되면 댓글이 삭제됩니다.");
} else {
alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오.");
}
},
error: function (xhr, status, err) {
procErrorEvent(xhr, status, err);
},
complete : function() {
$("#commentBlameModal").modal('hide');
}
});
}
}
});
},
// 추천
like: function (e) {
let formData = new FormData(e.form);
formData.append("type", e.value);
$.ajax({
url: (this.callback + "/like"),
type: "post",
cache: false,
contentType: false,
processData: false,
data: formData,
dataType: "json",
beforeSend: function (xhr) {
if (!loginCheck()) {
xhr.abort();
}
},
success: function (res) {
if (res.success) {
Comment.toggleLike(formData.get("cid"), 1, !Number(e.dataset.active));
} else {
alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오.");
}
},
error: function (xhr, status, err) {
procErrorEvent(xhr, status, err);
}
});
},
// 비추천
dislike: function (e) {
let formData = new FormData(e.form);
formData.append("type", e.value);
$.ajax({
url: (this.callback + "/dislike"),
type: "post",
cache: false,
contentType: false,
processData: false,
data: formData,
dataType: "json",
beforeSend: function (xhr) {
if (!loginCheck()) {
xhr.abort();
}
},
success: function (res) {
if (res.success) {
Comment.toggleLike(formData.get("cid"), 2, !Number(e.dataset.active));
} else {
alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오.");
}
},
error: function (xhr, status, err) {
procErrorEvent(xhr, status, err);
}
});
},
// 댓글 입력 취소
cancel: function (e) {
if (e.form.mode.value === "write") {
e.form.elements["mode"].value = "write";
e.form.elements["cid"].value = "";
e.form.elements["length"].value = "0";
e.form.elements["content"].value = "";
if (typeof e.form.elements["is_secret"] !== "undefined") {
e.form.elements["is_secret"].checked = false;
}
let length = e.form.getElementsByClassName("comment-length");
if (length.length > 0) {
length[0].innerText = "0";
}
} else {
e.form.parentNode.style.display = "none";
e.form.parentNode.innerHTML = "";
}
this.reset(e.form);
},
/*
* 댓글 에디터 옵션 변경
* 1:1 게시판에서 댓글을 작성하면 실행
*/
setInitEditor: function () {
let tinyMCEExtendSetting = {
min_height: 210,
max_height: 310,
file_browser_url: (BASE_URL + "/board/" + this.code + "/uploader"),
init_instance_callback: function (editor) {
editor.on('SetContent', function (e) {
let iframeRows = e.target.contentDocument.body.getElementsByTagName("iframe");
if (iframeRows > 0) {
$(e.content).filter("iframe").attr("src", ""); // iframe src 주소
}
});
editor.on('keyup', function () {
Comment.checkByte(editor.targetElm);
});
editor.on('keypress', function () {
Comment.checkByte(editor.targetElm);
// document.getElementById(e.target.dataset.id).value = e.currentTarget.textContent;
});
}
};
this.tinymce = tinymce.init(Object.assign(tinyMCE.activeEditor.settings, tinyMCEExtendSetting));
this.useEditor = true;
},
// 좋아요/싫어요 처리
toggleLike: function (cid, type, active) {
let btnCommentLike = document.getElementById("comment_" + cid).getElementsByClassName("btn-comment-like")[0];
let btnCommentDisLike = document.getElementById("comment_" + cid).getElementsByClassName("btn-comment-dislike")[0];
if (type === 1) {
this.setLike(cid, active);
if (Number(btnCommentDisLike.dataset.active)) {
this.setDislike(cid, false);
}
} else if (type === 2) {
this.setDislike(cid, active);
if (Number(btnCommentLike.dataset.active)) {
this.setLike(cid, false);
}
}
},
// 좋아요 갱신
setLike: function (cid, active) {
let button = document.getElementById("comment_" + cid).getElementsByClassName("btn-comment-like")[0];
let rows = Number(button.dataset.rows);
if (active) {
rows = (rows + 1);
button.innerHTML = ' ' + String(rows) + '';
button.dataset.active = "1";
} else {
rows = (rows > 0 ? rows - 1 : rows);
button.innerHTML = ' ' + String(rows) + '';
button.dataset.active = "0";
}
button.dataset.rows = rows;
},
// 싫어요 갱신
setDislike: function (cid, active) {
let button = document.getElementById("comment_" + cid).getElementsByClassName("btn-comment-dislike")[0];
let rows = Number(button.dataset.rows);
if (active) {
rows = (rows + 1);
button.innerHTML = ' ' + String(rows) + '';
button.dataset.active = "1";
} else {
rows = (rows > 0 ? rows - 1 : rows);
button.innerHTML = ' ' + String(rows) + '';
button.dataset.active = "0";
}
button.dataset.rows = rows;
}
};
// 댓글 입력하면 창 크기 조절
$(document).on("keyup input", "textarea[data-autoresize]", function() {
Comment.resizeTextarea(this);
});
// 댓글 글자수 확인
$(document).on('keydown keyup', "textarea.comment-content", function (e) {
Comment.checkByte(this);
});
// 댓글 등록
$(document).on("click", "button.btn-comment-submit", function () {
Comment.submit(this);
});
// 댓글 답글
$(document).on("click", "button.btn-comment-reply", function() {
Comment.write(this, "reply");
});
// 댓글 수정
$(document).on("click", "button.btn-comment-edit", function () {
Comment.write(this, "edit");
});
// 댓글 취소
$(document).on("click", "button.btn-comment-cancel", function () {
Comment.cancel(this);
});
// 댓글 삭제
$(document).on("click", "button.btn-comment-delete", function () {
Comment.delete(this);
});
// 댓글 신고
$(document).on("click", "button.btn-comment-blame", function() {
Comment.blame(this);
});
// 댓글 추천하기
$(document).on("click", "button.btn-comment-like", function () {
Comment.like(this);
});
// 댓글 비추천하기
$(document).on("click", "button.btn-comment-dislike", function () {
Comment.dislike(this);
});
// 댓글 페이징 처리
$(document).on("click", "#commentList > nav > ul > li > a", function () {
let page = Number(params('page', this.href));
return Comment.list(page);
});
// 댓글 정렬
$(document).on("change", "select#commentSort", function () {
Comment.list(null, null, null, this.value);
});
// 댓글 신고 모달 열리면 초기화
$(document).on("show.bs.modal", "#commentBlameModal", function(e) {
let form = e.currentTarget.getElementsByTagName("form")[0];
form.elements["type"].selectedIndex = 0;
form.elements["reason"].value = "";
if(!IS_USER) {
return false;
}
});