$(function() { Post.init(); }); const Post = { listURL: "", editURL: "", deleteURL: "", callback: "", code: "", bid: null, pid: null, uid: null, init: function() { const form = ( document.getElementById("fPostView") || document.getElementById("fPostWrite") || document.getElementById("fPostEdit") ); this.code = form.elements["code"].value; this.bid = form.elements["bid"].value; this.pid = form.elements["pid"].value; if(form.elements.hasOwnProperty("uid")) { this.uid = form.elements["uid"].value; } if(form.elements.hasOwnProperty("list_url")) { this.listURL = form.elements["list_url"].value; } if(form.elements.hasOwnProperty("edit_url")) { this.editURL = form.elements["edit_url"].value; } if(form.elements.hasOwnProperty("delete_url")) { this.deleteURL = form.elements["delete_url"].value; } this.callback = (this.listURL + "/" + String(this.pid)); let captcha = document.querySelector('a[href="//captcha.org/captcha.html?laravel"]'); if(captcha) { captcha.remove(); } Post.setInitEditor(); }, // 첨부파일 입력란 아래에 파일 크기를 표시 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: function(e) { return (document.getElementById("category") && !$("#isSpeaker").is("checked") && !$("#isNotice").is("checked") && e.dataset.categoryRequired === "1"); }, number: true}, // 게시판 분류 username: {required: "#username:enabled", minlength: 2, maxlength: 10}, password: {required: "#password:enabled", minlength: 3, maxlength: 10}, 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: "분류 값은 숫자여야 합니다."}, username: {required: "이름을 입력하세요.", minlength: "이름을 두 글자 이상 입력하세요.", maxlength: "이름은 10자까지 입력 가능합니다."}, password: {required: "비밀번호를 입력하세요.", minlength: "비밀번호는 3자 이상 입력하세요.", maxlength: "비밀번호는 10자까지 입력 가능합니다."}, subject: {required: "제목을 입력하세요."}, captcha: {required: "자동등록방지 문자를 입력하세요.", captchaKey: "자동등록방지 문자가 잘못되었습니다."}, }, submitHandler: function(form) { let subjectMinLength = Number(form.elements["subject_min_length"].value); let subjectMaxLength = Number(form.elements["subject_max_length"].value); let contentMinLength = Number(form.elements["content_min_length"].value); let contentMaxLength = Number(form.elements["content_max_length"].value); let uploadFileMaxSize = Number(form.elements["upload_file_max_size"].value); let inSubjectLength = Number(form.elements["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) { const isActive = e.classList.contains("active"); $.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()) { return xhr.abort(); } const message = (isActive ? "즐겨찾기 해제 하시겠습니까?" : "즐겨찾기 등록 하시겠습니까?"); if (!confirm(message)) { return false; } }, success: function (res) { if (res.success) { if (!isActive) { alert("즐겨찾기 등록되었습니다."); e.classList.add("active"); } else { alert("즐겨찾기 해제되었습니다."); e.classList.remove("active"); } } else { alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오."); } }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); } }); e.blur(); }, // 신고 blame: function(e) { e.blur(); if(!loginCheck()) { return false; } // 신고 접수 유효성 $("#fPostBlame").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", Post.bid); formData.append("pid", Post.pid); $.ajax({ url: (Post.callback + "/blame"), type: "post", cache: false, async: 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() { $("#postBlameModal").modal('hide'); } }); } } }); }, // 수정 edit: function(e) { e.blur(); let href = this.editURL; if(!this.uid) { // 권한 확인 const passwd = prompt("게시글 비밀번호 확인이 필요합니다.\n비밀번호를 입력해주세요."); if(!passwd) { return false; } href += "?passwd=" + encodeURIComponent(passwd.trim()); }else{ if (!loginCheck()) { return false; } } location.href = href; }, // 삭제 delete: function(e) { e.blur(); $.ajax({ url: this.deleteURL, type: "delete", data: {bid: this.bid, pid: this.pid, _method: "delete"}, dataType: "json", beforeSend: function (xhr) { if (!Post.uid) { // 권한 확인 const passwd = prompt("게시글 비밀번호 확인이 필요합니다.\n비밀번호를 입력해주세요."); if (!passwd) { return false; } this.url += ("?passwd=" + encodeURIComponent(passwd.trim())); } else { if (!loginCheck()) { return xhr.abort(); } } if (!confirm("게시글을 삭제하시겠습니까?")) { return false; } }, success: function (res) { if (res.success) { alert("게시글이 삭제되었습니다."); location.replace(Post.listURL); } else { alert(res.message); } }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); } }); }, // 인쇄 print: function() { let $container = $("#boardView").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 = "0"; printHtml.style.height = "initial"; printHtml.style.width = "100%"; printHtml.style.minWidth = "auto"; printHtml.style.maxWidth = "initial"; printHtml.parentNode.style.padding = "0"; printHtml.parentNode.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; $.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, !Number(e.dataset.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; $.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, !Number(e.dataset.active)); } else { alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오."); } }, error: function (xhr, status, err) { procErrorEvent(xhr, status, err); } }); }, // 다운로드 download: function (e) { let action = (this.callback + "/download"); if(!this.uid) { // 권한 확인 const passwd = prompt("게시글 비밀번호 확인이 필요합니다.\n비밀번호를 입력해주세요."); if(!passwd) { return false; } action += "?passwd=" + encodeURIComponent(passwd.trim()); }else{ if (!loginCheck()) { return false; } } 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 = action; form.target = "fr_an_post"; inputHidden_1.type = "hidden"; inputHidden_1.name = "bid"; inputHidden_1.value = this.bid; inputHidden_2.type = "hidden"; inputHidden_2.name = "pid"; inputHidden_2.value = this.pid; inputHidden_3.type = "hidden"; inputHidden_3.name = "fid"; inputHidden_3.value = e.dataset.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) { $.ajax({ url: (this.callback + "/link"), type: "post", cache: false, async: false, data: {bid: this.bid, pid: this.pid, lid: e.dataset.lid}, dataType: "json", 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; }, // SNS 북 공유하기 sns: function(e) {console.log(1); let linkURL = window.location.href; switch(e.dataset.target) { case "1": popupCenter( 'http://www.facebook.com/sharer.php?u=' + encodeURIComponent(linkURL), "Facebook share", 400, 500); break; case "2": // 트위터 공유하기 let sendText = "Twitter share"; popupCenter("https://twitter.com/intent/tweet?text=" + sendText + "&url=" + linkURL, "Twitter share", 550, 600); break; case "3": // 레딧 공유하기 popupCenter("https://www.reddit.com/submit?url=" + encodeURIComponent(linkURL), "Reddit share", 700, 800); break; } }, /* * 게시글 에디터 옵션 변경 */ setInitEditor: function () { if(tinyMCE.activeEditor) { let tinyMCEExtendSetting = { file_browser_url: (BASE_URL + "/board/" + this.code + "/uploader"), }; tinymce.init(Object.assign(tinyMCE.activeEditor.settings, tinyMCEExtendSetting)); } }, }; // 게시글 등록/수정 실행 $(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", "a#btnPostPrint", function () { return Post.print(this); }); // 게시글 주소 복사 $(document).on("click", "a#btnPostCopy", function() { return Post.copy(this); }); // 게시글 SNS 공유 $(document).on("click", "li.btn-post-sns", function() { Post.sns(this); }); // 게시글 즐겨찾기 $(document).on("click", "#btnPostBookmark", function() { Post.bookmark(this); }); // 게시글 신고 $(document).on("click", "#btnPostBlame", function() { Post.blame(this); }); // 게시글 수정 $(document).on("click", "#btnPostEdit", function() { Post.edit(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["type"].selectedIndex = 0; form.elements["reason"].value = ""; if(!IS_USER) { return false; } }); // 공지, 전체공지일 경우 문의 유형은 무시된다. $(document).on("change", "#isSpeaker, #isNotice", function () { document.getElementById("categoryBox").style.display = (this.checked ? "none" : "table-row"); });