$(function() { Post.init(); }); const Post = { listURL: "", callback: "", code: "", bid: 0, pid: 0, fileDownloadPoint: 0, // 파일 다운로드 차감(P) init: function() { this.code = document.getElementById("code").value; this.bid = document.getElementById("bid").value; this.pid = document.getElementById("pid").value; this.listURL = (BASE_URL + "/board/" + String(this.code)); this.callback = (this.listURL + "/" + String(this.pid)); let captcha = document.querySelector('a[href="//captcha.org/captcha.html?laravel"]'); if(captcha) { captcha.remove(); } }, // 첨부파일 입력란 아래에 파일 크기를 표시 displayFileSize: function (object) { $(object).next().children().text(formatBytes(uploadFileSize(object))); }, // 첨부한 파일의 크기 표시를 삭제 removeFileSize: function (object) { $(object).next().children().text("0"); }, // tinyMCE 게시판 에디터로 커서 이동 focusContent: function (id) { tinymce.execCommand('mceFocus', false, id); }, // 입력 내용의 문자 통계 반환 getStats: function (id) { let body = tinymce.get(id).getBody(), text = tinymce.trim(body.innerText || body.textContent); return { chars: text.length, words: text.split(/[\w\u2019\'-]+/).length }; }, // 금지 단어 확인 filterSpamKeyword: function (subject, content) { let ret = { "subject": null, "content": null }; $.ajax({ headers: {'X-CSRF-TOKEN': CSRF}, url: (BASE_URL + "/api/filterSpamKeyword"), type: 'POST', dataType: 'JSON', async: false, cache: false, data: {subject: subject, content: content}, success: function (res) { ret.subject = res.subject; ret.content = res.content; } }); return ret; }, // 등록/수정 submit: function(e) { let form = document.getElementById(e.dataset.form); $(form).validate({ onkeyup: false, onclick: false, onfocusout: false, showErrors: function (errorMap, errorList) { if (this.numberOfInvalids() && errorList.length > 0) { alert(errorList[0].message); $(errorList[0].element).focus(); } }, rules: { is_speaker: {number: true}, is_notice: {number: true}, is_secret: {number: true}, category: {required: "#category:enabled", number: true}, // 게시판 분류 subject: {required: true}, content: {"required_tinymce": true}, captcha: {required: "#captcha:enabled"}, // Captcha 사용 여부 // 게시판 Tag // submitHandler 처리 // 게시판 Link // submitHandler 처리 // 게시판 File // submitHandler 처리 }, messages: { is_speaker: {number: "전체공지 값은 숫자여야 합니다."}, is_notice: {number: "공지 값은 숫자여야 합니다."}, is_secret: {number: "비밀글 값은 숫자여야 합니다."}, category: {required: "분류를 선택하세요.", number: "분류 값은 숫자여야 합니다."}, subject: {required: "제목을 입력하세요."}, captcha: {required: "자동등록방지 문자를 입력하세요.", captchaKey: "자동등록방지 문자가 잘못되었습니다."}, }, submitHandler: function(form) { let subjectMinLength = Number(form.subject_min_length.value); let subjectMaxLength = Number(form.subject_max_length.value); let contentMinLength = Number(form.content_min_length.value); let contentMaxLength = Number(form.content_max_length.value); let uploadFileMaxSize = Number(form.upload_file_max_size.value); let inSubjectLength = Number(form.subject.value.length); let inContentLength = (tinymce.editors.length > 0 ? Number(Post.getStats("content").chars) : form.content.value.length); try { let error = function(e) {throw new Error(e);}; // 제목, 내용 길이 확인 if (subjectMinLength > 0 && inSubjectLength < subjectMinLength) { form.subject.focus(); error("제목은 최소 " + subjectMinLength + "글자 이상 입력해야 합니다."); }else if (subjectMaxLength > 0 && inSubjectLength > subjectMaxLength) { form.subject.focus(); error("제목은 최대 " + subjectMaxLength + "글자 이하 입력 가능합니다."); } if (contentMinLength > 0 && inContentLength < contentMinLength) { Post.focusContent("content"); error("내용은 최소 " + contentMinLength + "글자 입력해야 합니다."); }else if (contentMaxLength > 0 && inContentLength > contentMaxLength) { Post.focusContent("content"); error("내용은 최대 " + contentMaxLength + "글자 입력 가능합니다."); } let spamKeywords = Post.filterSpamKeyword(form.subject.value, form.content.value); if (spamKeywords.subject) { form.subject.focus(); error("제목에 금지단어 ('" + spamKeywords.subject + "')가 포함 되어있습니다."); } if (spamKeywords.content) { form.content.focus(); error("내용에 금지단어 ('" + spamKeywords.content + "')가 포함 되어있습니다."); } // Tag 개수 확인 let tags = document.getElementById("tags"); if(tags) { if(tags.value.split(',').length > 10) { alert("Tag 는 최대 10개 입력 가능합니다."); return false; } } // Link URL 형식인지 확인 let links = form.getElementsByClassName("form-control-url"); if(links.length > 0) { for(let j in links) { let link = links[j]; if(link.value && !isValidUrl(link.value)) { link.focus(); error("URL 형식이 잘못되었습니다."); break; } } } // 첨부파일 크기 확인 if(uploadFileMaxSize > 0) { let files = form.getElementsByClassName("form-control-file"); let byte = (uploadFileMaxSize * 1024 * 1024); for(let i in files) { let file = files[i]; if(file.value) { let size = uploadFileSize(file); if (size > byte) { file.value = null; Post.removeFileSize(file); error("첨부 가능한 파일의 최대 크기는 " + uploadFileMaxSize + "MB 입니다."); break; } } } } showLoading(); return true; }catch(err) { alert(err.message); return false; } } }); $(form).submit(); }, copy: function(e) { if (copyToClipboard(e.target.href)) { alert("게시글 주소가 복사 되었습니다.\n주소를 Ctrl + C 하여, 붙여 넣을 곳에 Ctrl + V 하세요."); } return false; }, // 즐겨찾기 bookmark: function(e) { $.ajax({ url: (this.callback + "/bookmark"), type: 'post', cache: false, async: false, data: {bid: this.bid, pid: this.pid}, dataType: 'json', beforeSend: function (xhr) { if (!loginCheck()) { xhr.abort(); } }, success: function (res) { if (res.success) { if (!Number(e.value)) { alert("즐겨찾기 등록되었습니다."); e.value = "1"; e.classList.add("active"); } else { alert("즐겨찾기 해제되었습니다."); e.value = "0"; e.classList.remove("active"); } } else { alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오."); } }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); } }); e.blur(); }, // 삭제 delete: function(e) { e.blur(); if (confirm("게시글을 삭제하시겠습니까?")) { $.ajax({ url: (this.callback + "/delete"), type: 'post', data: {bid: this.bid, pid: this.pid, _method: "delete"}, dataType: 'json', beforeSend: function (xhr) { if (!loginCheck()) { xhr.abort(); } }, success: function (res) { if (res.success) { alert("게시글이 삭제되었습니다."); location.replace(Post.listURL); } else { alert(res.message); } }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); } }); } }, // 신고 blame: function(e) { e.blur(); if(!loginCheck()) { return false; } // 신고 접수 유효성 $("#fPostBlame").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("신고를 접수하시겠습니까?")) { let bid = Post.bid; let pid = Post.pid; $.ajax({ url: (Post.callback + "/blame"), type: 'post', cache: false, async: false, data: {bid: bid, pid: pid, 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); } }); } } }); }, // 인쇄 print: function(e) { let $container = $("div.container").clone(); let cssText = ""; let cssLink = ""; let style = document.getElementsByTagName("style"); let link = document.getElementsByTagName("link"); for (let i in style) { if(typeof style[i].innerHTML !== "undefined") { cssText += style[i].innerHTML; } } for (let i in link) { if(typeof link[i].outerHTML !== "undefined") { cssLink += link[i].outerHTML; } } let popupWindow = popupCenter(null, "_blank", 900, 680); if (popupWindow.focus) { popupWindow.focus(); } let printHtml = '' + '' + '' + cssLink + '' + '' + '' + $container.get(0).innerHTML + '' + ''; popupWindow.document.write(printHtml); // 프린트 전 popupWindow.onbeforeprint = function () { printHtml = popupWindow.document.body; printHtml.style.backgroundColor = "#fff"; printHtml.style.padding = "20px"; printHtml.style.height = "initial"; printHtml.style.width = "100%"; printHtml.style.minWidth = "auto"; printHtml.style.maxWidth = "initial"; printHtml.parentNode.parentNode.getElementById("boardView").style.maxWidth = "initial"; printHtml.getElementsByTagName("nav")[0].remove(); printHtml.getElementsByTagName("blockquote")[0].style.border = "none"; printHtml.getElementsByClassName("foot")[0].remove(); popupWindow.document.body.innerHTML = printHtml.innerHTML; }; // 프린트 후 popupWindow.onafterprint = function () { popupWindow.document.body.innerHTML = ""; popupWindow.document.write(""); }; popupWindow.document.close(); popupWindow.focus(); setTimeout(function () { popupWindow.print(); popupWindow.close(); }, 300); return false; }, // 좋아요 like: function(e) { e.blur(); let bid = this.bid; let pid = this.pid; 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, type: type}, dataType: 'json', beforeSend: function (xhr) { if (!loginCheck()) { xhr.abort(); } }, success: function (res) { if (res.success) { Post.toggleLike(1, !active); } else { alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오."); } }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); } }); }, // 싫어요 dislike: function(e) { e.blur(); let bid = this.bid; let pid = this.pid; 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, type: type}, dataType: 'json', beforeSend: function (xhr) { if (!loginCheck()) { xhr.abort(); } }, success: function (res) { if (res.success) { Post.toggleLike(2, !active); } else { alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오."); } }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); } }); }, // 다운로드 download: function (e) { if (!loginCheck()) { return false; } let bid = this.bid; let pid = this.pid; let fid = e.dataset.fid; let form = document.createElement("form"); let inputHidden_1 = document.createElement("input"); let inputHidden_2 = document.createElement("input"); let inputHidden_3 = document.createElement("input"); let inputHidden_4 = document.createElement("input"); let iframe = document.createElement("iframe"); iframe.setAttribute("name", "fr_an_post"); iframe.setAttribute("width", "0"); iframe.setAttribute("height", "0"); iframe.setAttribute("hidden", "hidden"); iframe.src = ("data:text/html;charset=utf-8," + encodeURI("")); form.method = "post"; form.action = (this.callback + "/download"); form.target = "fr_an_post"; inputHidden_1.type = "hidden"; inputHidden_1.name = "bid"; inputHidden_1.value = bid; inputHidden_2.type = "hidden"; inputHidden_2.name = "pid"; inputHidden_2.value = pid; inputHidden_3.type = "hidden"; inputHidden_3.name = "fid"; inputHidden_3.value = fid; inputHidden_4.type = "hidden"; inputHidden_4.name = "_token"; inputHidden_4.value = CSRF; form.appendChild(inputHidden_1); form.appendChild(inputHidden_2); form.appendChild(inputHidden_3); form.appendChild(inputHidden_4); document.body.appendChild(iframe); document.body.appendChild(form); form.submit(); return false; }, // Link 이동 link: function (e) { let bid = this.bid; let pid = this.pid; let lid = e.dataset.lid; $.ajax({ url: (this.callback + "/link"), type: 'post', cache: false, async: false, data: {bid: bid, pid: pid, lid: lid}, dataType: 'json', beforeSend: function (xhr) { if (!loginCheck()) { xhr.abort(); } }, success: function (res) { let a = document.createElement('a'); a.href = res.href; a.target = "_blank"; a.click(); }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); } }); return false; }, // 좋아요/싫어요 처리 toggleLike: function(type, active) { let btnPostLike = document.getElementById("btnPostLike"); let btnPostDisLike = document.getElementById("btnPostDislike"); if(type === 1) { this.setLike(active); if(Number(btnPostDisLike.dataset.active)) { this.setDislike(false); } }else if(type === 2) { this.setDislike(active); if(Number(btnPostLike.dataset.active)) { this.setLike(false); } } }, // 좋아요 갱신 setLike: function(active) { let button = document.getElementById("btnPostLike"); 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.getElementById("txtPostLikeCnt").innerText = rows; }, // 싫어요 갱신 setDislike: function(active) { let button = document.getElementById("btnPostDislike"); 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.getElementById("txtPostDislikeCnt").innerText = rows; }, // 게시글 QR 보기 qrcode: function(e) { let $modalQRCode = $("#modalQRCode"); $modalQRCode.find(".modal-header .modal-title").html(e.dataset.title); $modalQRCode.find(".modal-body").html('


QR Code를 스캔하시면 게시글을 확인하실 수 있습니다."); $modalQRCode.modal('toggle'); return false; } }; // 게시글 등록/수정 실행 $(document).on("click", "#btnPostSubmit", function() { Post.submit(this); }); // 파일 용량 확인 $(document).on("change", "input.form-control-file", function() { Post.displayFileSize(this); }); // 전체공지, 공지 둘중 하나만 체크 $(document).on("change", "input#isSpeaker, input#isNotice", function() { let $all = $("#isSpeaker"); let $one = $("#isNotice"); if (this.id === "isNotice" && $all.is(":checked")) { $all.prop("checked", false); } else if (this.id === "isSpeaker" && $one.is(":checked")) { $one.prop("checked", false); } }); // QR-CODE 보기 $(document).on("click", "a#btnPostQRCode, a.btn-qr-code", function () { return Post.qrcode(this); }); // 게시글 인쇄 $(document).on("click", "#btnPostPrint", function () { return Post.print(this); }); // 게시글 주소 복사 $(document).on("click", "#btnPostCopy", function() { return Post.copy(this); }); // 게시글 즐겨찾기 $(document).on("click", "#btnPostBookmark", function() { Post.bookmark(this); }); // 게시글 신고 $(document).on("click", "#btnPostBlame", function() { Post.blame(this); }); // 게시글 삭제 $(document).on("click", "#btnPostDelete", function() { Post.delete(this); }); // 게시글 좋아요 $(document).on("click", "#btnPostLike", function () { Post.like(this); }); // 게시글 싫어요 $(document).on("click", "#btnPostDislike", function () { Post.dislike(this); }); // 게시글 다운로드 $(document).on("click", "a.btn-file-download", function () { return Post.download(this); }); // 게시글 Link 이동 $(document).on("click", "a.btn-site-url", function () { return Post.link(this); }); // 게시글 신고 모달 열리면 초기화 $(document).on("show.bs.modal", "#postBlameModal", function(e) { let form = e.currentTarget.getElementsByTagName("form")[0]; form.elements["blame_type"].selectedIndex = 0; form.elements["blame_reason"].value = ""; });