$(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 + '' + '' + '' + '