$(function() { Comment.init(); Comment.list(); // 댓글입력 창 높이 자동조정 $.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", modify: "update", reply: "reply"}, mode: "", // write, modify, reply code: "", bid: 0, pid: 0, cid: 0, total: 0, page: 0, perPage: 0, minLength: 0, maxLength: 0, listForm: null, writeForm: null, saveHtml: null, saveBefore: null, useEditor: false, // 댓글 기본설정 init: function() { this.listForm = document.getElementById(this.listID); // 댓글 목록 if(this.listForm !== null) { this.code = document.getElementById("code").value; this.bid = document.getElementById("bid").value; this.pid = document.getElementById("pid").value; this.callback = (BASE_URL + "/board/" + String(this.code) + "/" + String(this.pid) + "/comment"); } this.writeForm = document.getElementById(this.writeID); // 댓글 입력창 if(IS_USER && this.writeForm !== null) { 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; // 댓글 입력 창 저장 this.reset(form); } }, // 초기화 reset: function(form) { this.mode = "write"; this.cid = 0; this.saveBefore = null; if (typeof form !== "undefined") { form.elements["mode"].value = "write"; form.elements["cid"].value = ""; form.elements["length"].value = "0"; form.elements["content"].value = ""; if (typeof form.elements["is_secret"] !== "undefined") { 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; } } }, // 댓글 영역 강조 focusCommentBox: function(cid) { if (typeof cid != "undefined" && cid) { $('#comment_' + cid).effect('highlight', { 'color': '#FFFFC6' }, 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 (!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 형식으로 뎃글목록 출력 let total = this.children.total.value; let page = this.children.page.value; let perPage = this.children.per_page.value; if (total) { // 댓글 수 표시 document.getElementById("txtCommentRows").innerText = total; document.getElementById("txtPostCommentCnt").innerText = total; } if (message) { // 알림이 있을 경우 alert alert(message); } Comment.page = page; Comment.perPage = perPage; Comment.total = total; Comment.focusCommentBox(cid); Comment.scrollReplyNow(cid); }); return false; }, // 댓글 등록/수정 submit: function(e) { e.blur(); e.disabled = true; $(e.form).validate({ onkeyup: false, onclick: false, onfocusout: false, rules: { mode: {required: true, contains: ["write", "modify", "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: Comment.maxLength }, is_secret: {number: true} }, messages: { 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 mode = form.elements["mode"].value; let content = form.elements["content"].value; let spamKeyword = Comment.filterSpamKeyword(content); // 금지 단어 검사 if (spamKeyword) { alert("내용에 금지어 ('" + spamKeyword + "')가 포함 되어있습니다."); form.elements["content"].focus(); return false; } // 댓글 등록 let formData = $(form).serializeArray(); formData.push({name: "bid", value: Comment.bid}); formData.push({name: "pid", value: Comment.pid}); // 버튼 비활성화 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, data: formData, dataType: 'json', beforeSend: function (xhr) { if (!loginCheck()) { xhr.abort(); } showLoading(); }, success: function (res) { if (res.success) { let page = (mode === "write" ? Math.ceil((Number(Comment.total) + 1) / Comment.perPage) : Comment.page); Comment.list(page, res.commentID); }else{ alert(res.message); } Comment.reset(form); }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); }, complete: function() { hideLoading(); } }); } }); $(e.form).submit(); }, // 수정, 답글 write: function(e, mode) { if(!loginCheck()) { return false; } if(typeof e === "undefined") { return false; } if(typeof mode == "undefined" || mode == null || !mode) { return false; } if($.inArray(mode, ["write", "modify", "reply"]) < 0) { return false; } let listForm = e.form; // 댓글 영역 let writeForm = null; let cid = (listForm.elements["cid"].value || 0); let mediaFormID = ("#fCommentWrite_" + cid); let $saveBefore = $(this.saveBefore); // 이전 답글 let $target = $(mediaFormID); // 현재 답글 // 답글 if(mode === "reply") { if(this.cid === cid && this.saveBefore === mediaFormID && this.mode === mode) { $saveBefore.toggle(); // 기존 댓글 창 보기/숨김 }else{ $saveBefore.html(""); $target.show(); writeForm = $target.html(this.saveHtml).get(0).firstElementChild; writeForm.elements["mode"].value = mode; writeForm.elements["cid"].value = cid; } } // 수정 if(mode === "modify") { $saveBefore.html(""); $target.show(); writeForm = $target.html(this.saveHtml).get(0).firstElementChild; writeForm.elements["content"].value = listForm.content.value; // 내용 대입 if(typeof writeForm.elements["is_secret"] !== "undefined") { writeForm.elements["is_secret"].checked = Boolean(listForm.is_secret.value === '1'); // 비밀글 체크 } writeForm.elements["mode"].value = mode; writeForm.elements["cid"].value = cid; // 입력창 높이 조절 this.resizeTextarea(writeForm.elements["content"]); } // textarea ID 할당 if(writeForm) { writeForm.elements["content"].setAttribute("id", writeForm.elements["content"].name + "_" + cid); // 1:1 문의 게시판은 에디터를 사용 if(this.useEditor) { tinyMCE.init(tinyMCE.activeEditor.settings); tinyMCE.EditorManager.execCommand('mceAddEditor', true, writeForm.elements["content"]); } // 글자 수 확인 this.checkByte(writeForm.elements["content"]); } this.mode = mode; this.cid = cid; this.saveBefore = mediaFormID; // 현재 영역 }, // 댓글 삭제 delete: function(e) { e.blur(); if (confirm("정말 삭제 하시겠습니까?")) { let bid = e.form.bid.value; let pid = e.form.pid.value; let cid = e.form.cid.value; $.ajax({ url: (this.callback + "/delete"), type: 'delete', cache: false, async: false, data: {bid: bid, pid: pid, cid: cid, _method: "delete"}, dataType: 'json', beforeSend: function(xhr) { if(!loginCheck()) { xhr.abort(); } }, 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; } let bid = e.form.bid.value; let pid = e.form.pid.value; let cid = e.form.cid.value; // 신고 접수 유효성 $("#fCommentBlame").validate({ onkeyup: false, onclick: false, onfocusout: false, rules: { blame_type: {required: true, contains: ['1', '2', '3', '4', '5', '6', '7', '8', '9']}, blame_reason: {required: true, normalizer: function (value) {return $.trim(value);}, maxlength: 1000} }, messages: { blame_type: {required: "신고 유형을 선택해주세요.", contains: "잘못된 요청입니다."}, blame_reason: {required: "신고 내용을 입력해주세요.", maxlength: "신고 내용은 {0}자까지 입력 가능합니다."} }, showErrors: function (errorMap, errorList) { if (this.numberOfInvalids()) { alert(errorList[0].message); errorList[0].element.focus(); } }, submitHandler: function(form) { if (confirm("신고를 접수하시겠습니까?")) { $.ajax({ url: (this.callback + "/blame"), type: 'post', cache: false, async: false, data: {bid: bid, pid: pid, cid: cid, type: form.blame_type.value, reason: form.blame_reason.value}, dataType: 'json', beforeSend: function (xhr) { if(!loginCheck()) { xhr.abort(); } }, success: function (res) { form.blame_type.value = ""; form.blame_reason.value = ""; if (res.success) { alert("감사합니다. 신고가 접수되었습니다. 이용 규칙을 위반한 것으로 확인되면 댓글이 삭제됩니다."); }else{ alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오."); } $('div.modal').modal('hide'); }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); } }); } } }); }, // 추천 like: function (e) { let bid = e.form.bid.value; let pid = e.form.pid.value; let cid = e.form.cid.value; let type = e.value; let active = Number(e.dataset.active); $.ajax({ url: (this.callback + "/like"), type: 'post', cache: false, async: false, data: {bid: bid, pid: pid, cid: cid, type: type}, dataType: 'json', beforeSend: function (xhr) { if (!loginCheck()) { xhr.abort(); } }, success: function (res) { if (res.success) { Comment.toggleLike(cid, 1, !active); } else { alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오."); } }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); } }); }, // 비추천 dislike: function (e) { let bid = e.form.bid.value; let pid = e.form.pid.value; let cid = e.form.cid.value; let type = e.value; let active = Number(e.dataset.active); $.ajax({ url: (this.callback + "/dislike"), type: 'post', cache: false, async: false, data: {bid: bid, pid: pid, cid: cid, type: type}, dataType: 'json', beforeSend: function (xhr) { if (!loginCheck()) { xhr.abort(); } }, success: function (res) { if (res.success) { Comment.toggleLike(cid, 2, !active); } else { alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오."); } }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); } }); }, // 댓글 입력 취소 cancel: function ($this) { if ($this.form.mode.value === "write") { $this.form.elements["mode"].value = "write"; $this.form.elements["cid"].value = ""; $this.form.elements["length"].value = "0"; $this.form.elements["content"].value = ""; if (typeof $this.form.elements["is_secret"] !== "undefined") { $this.form.elements["is_secret"].checked = false; } let $length = $this.form.getElementsByClassName("comment-length"); if ($length.length > 0) { $length[0].innerText = "0"; } } else { $this.form.parentNode.style.display = "none"; $this.form.parentNode.innerHTML = ""; } this.reset($this.form); }, /* * 댓글 에디터 옵션 변경 * 1:1 게시판에서 댓글을 작성하면 실행 */ setInitEditor: function () { let tinyMCEExtendSetting = { min_height: 210, max_height: 310, 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; }); } }; 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-modify", function () { Comment.write(this, "modify"); }); // 댓글 취소 $(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["blame_type"].selectedIndex = 0; form.elements["blame_reason"].value = ""; });