post.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. $(function() {
  2. Post.init();
  3. });
  4. const Post =
  5. {
  6. listURL: "",
  7. callback: "",
  8. code: "",
  9. bid: 0,
  10. pid: 0,
  11. fileDownloadPoint: 0, // 파일 다운로드 차감(P)
  12. init: function() {
  13. this.code = document.getElementById("code").value;
  14. this.bid = document.getElementById("bid").value;
  15. this.pid = document.getElementById("pid").value;
  16. this.listURL = (BASE_URL + "/board/" + String(this.code));
  17. this.callback = (this.listURL + "/" + String(this.pid));
  18. let captcha = document.querySelector('a[href="//captcha.org/captcha.html?laravel"]');
  19. if(captcha) {
  20. captcha.remove();
  21. }
  22. },
  23. // 첨부파일 입력란 아래에 파일 크기를 표시
  24. displayFileSize: function (object) {
  25. $(object).next().children().text(formatBytes(uploadFileSize(object)));
  26. },
  27. // 첨부한 파일의 크기 표시를 삭제
  28. removeFileSize: function (object) {
  29. $(object).next().children().text("0");
  30. },
  31. // tinyMCE 게시판 에디터로 커서 이동
  32. focusContent: function (id) {
  33. tinymce.execCommand('mceFocus', false, id);
  34. },
  35. // 입력 내용의 문자 통계 반환
  36. getStats: function (id) {
  37. let body = tinymce.get(id).getBody(), text = tinymce.trim(body.innerText || body.textContent);
  38. return {
  39. chars: text.length,
  40. words: text.split(/[\w\u2019\'-]+/).length
  41. };
  42. },
  43. // 금지 단어 확인
  44. filterSpamKeyword: function (subject, content) {
  45. let ret = {
  46. "subject": null,
  47. "content": null
  48. };
  49. $.ajax({
  50. headers: {'X-CSRF-TOKEN': CSRF},
  51. url: (BASE_URL + "/api/filterSpamKeyword"),
  52. type: 'POST',
  53. dataType: 'JSON',
  54. async: false,
  55. cache: false,
  56. data: {subject: subject, content: content},
  57. success: function (res) {
  58. ret.subject = res.subject;
  59. ret.content = res.content;
  60. }
  61. });
  62. return ret;
  63. },
  64. // 등록/수정
  65. submit: function(e)
  66. {
  67. let form = document.getElementById(e.dataset.form);
  68. $(form).validate({
  69. onkeyup: false,
  70. onclick: false,
  71. onfocusout: false,
  72. showErrors: function (errorMap, errorList) {
  73. if (this.numberOfInvalids() && errorList.length > 0) {
  74. alert(errorList[0].message);
  75. $(errorList[0].element).focus();
  76. }
  77. },
  78. rules: {
  79. is_speaker: {number: true},
  80. is_notice: {number: true},
  81. is_secret: {number: true},
  82. category: {required: "#category:enabled", number: true}, // 게시판 분류
  83. subject: {required: true},
  84. content: {"required_tinymce": true},
  85. captcha: {required: "#captcha:enabled"}, // Captcha 사용 여부
  86. // 게시판 Tag
  87. // submitHandler 처리
  88. // 게시판 Link
  89. // submitHandler 처리
  90. // 게시판 File
  91. // submitHandler 처리
  92. },
  93. messages: {
  94. is_speaker: {number: "전체공지 값은 숫자여야 합니다."},
  95. is_notice: {number: "공지 값은 숫자여야 합니다."},
  96. is_secret: {number: "비밀글 값은 숫자여야 합니다."},
  97. category: {required: "분류를 선택하세요.", number: "분류 값은 숫자여야 합니다."},
  98. subject: {required: "제목을 입력하세요."},
  99. captcha: {required: "자동등록방지 문자를 입력하세요.", captchaKey: "자동등록방지 문자가 잘못되었습니다."},
  100. },
  101. submitHandler: function(form) {
  102. let subjectMinLength = Number(form.subject_min_length.value);
  103. let subjectMaxLength = Number(form.subject_max_length.value);
  104. let contentMinLength = Number(form.content_min_length.value);
  105. let contentMaxLength = Number(form.content_max_length.value);
  106. let uploadFileMaxSize = Number(form.upload_file_max_size.value);
  107. let inSubjectLength = Number(form.subject.value.length);
  108. let inContentLength = (tinymce.editors.length > 0 ? Number(Post.getStats("content").chars) : form.content.value.length);
  109. try
  110. {
  111. let error = function(e) {throw new Error(e);};
  112. // 제목, 내용 길이 확인
  113. if (subjectMinLength > 0 && inSubjectLength < subjectMinLength) {
  114. form.subject.focus();
  115. error("제목은 최소 " + subjectMinLength + "글자 이상 입력해야 합니다.");
  116. }else if (subjectMaxLength > 0 && inSubjectLength > subjectMaxLength) {
  117. form.subject.focus();
  118. error("제목은 최대 " + subjectMaxLength + "글자 이하 입력 가능합니다.");
  119. }
  120. if (contentMinLength > 0 && inContentLength < contentMinLength) {
  121. Post.focusContent("content");
  122. error("내용은 최소 " + contentMinLength + "글자 입력해야 합니다.");
  123. }else if (contentMaxLength > 0 && inContentLength > contentMaxLength) {
  124. Post.focusContent("content");
  125. error("내용은 최대 " + contentMaxLength + "글자 입력 가능합니다.");
  126. }
  127. let spamKeywords = Post.filterSpamKeyword(form.subject.value, form.content.value);
  128. if (spamKeywords.subject) {
  129. form.subject.focus();
  130. error("제목에 금지단어 ('" + spamKeywords.subject + "')가 포함 되어있습니다.");
  131. }
  132. if (spamKeywords.content) {
  133. form.content.focus();
  134. error("내용에 금지단어 ('" + spamKeywords.content + "')가 포함 되어있습니다.");
  135. }
  136. // Tag 개수 확인
  137. let tags = document.getElementById("tags");
  138. if(tags) {
  139. if(tags.value.split(',').length > 10) {
  140. alert("Tag 는 최대 10개 입력 가능합니다.");
  141. return false;
  142. }
  143. }
  144. // Link URL 형식인지 확인
  145. let links = form.getElementsByClassName("form-control-url");
  146. if(links.length > 0) {
  147. for(let j in links) {
  148. let link = links[j];
  149. if(link.value && !isValidUrl(link.value)) {
  150. link.focus();
  151. error("URL 형식이 잘못되었습니다.");
  152. break;
  153. }
  154. }
  155. }
  156. // 첨부파일 크기 확인
  157. if(uploadFileMaxSize > 0) {
  158. let files = form.getElementsByClassName("form-control-file");
  159. let byte = (uploadFileMaxSize * 1024 * 1024);
  160. for(let i in files) {
  161. let file = files[i];
  162. if(file.value) {
  163. let size = uploadFileSize(file);
  164. if (size > byte) {
  165. file.value = null;
  166. Post.removeFileSize(file);
  167. error("첨부 가능한 파일의 최대 크기는 " + uploadFileMaxSize + "MB 입니다.");
  168. break;
  169. }
  170. }
  171. }
  172. }
  173. showLoading();
  174. return true;
  175. }catch(err) {
  176. alert(err.message);
  177. return false;
  178. }
  179. }
  180. });
  181. $(form).submit();
  182. },
  183. copy: function(e) {
  184. if (copyToClipboard(e.target.href)) {
  185. alert("게시글 주소가 복사 되었습니다.\n주소를 Ctrl + C 하여, 붙여 넣을 곳에 Ctrl + V 하세요.");
  186. }
  187. return false;
  188. },
  189. // 즐겨찾기
  190. bookmark: function(e)
  191. {
  192. $.ajax({
  193. url: (this.callback + "/bookmark"),
  194. type: 'post',
  195. cache: false,
  196. async: false,
  197. data: {bid: this.bid, pid: this.pid},
  198. dataType: 'json',
  199. beforeSend: function (xhr) {
  200. if (!loginCheck()) {
  201. xhr.abort();
  202. }
  203. },
  204. success: function (res) {
  205. if (res.success) {
  206. if (!Number(e.value)) {
  207. alert("즐겨찾기 등록되었습니다.");
  208. e.value = "1";
  209. e.classList.add("active");
  210. } else {
  211. alert("즐겨찾기 해제되었습니다.");
  212. e.value = "0";
  213. e.classList.remove("active");
  214. }
  215. } else {
  216. alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오.");
  217. }
  218. },
  219. error: function (xhr, status, err) {
  220. procErrorEvent(xhr, status, err);
  221. }
  222. });
  223. e.blur();
  224. },
  225. // 삭제
  226. delete: function(e)
  227. {
  228. e.blur();
  229. if (confirm("게시글을 삭제하시겠습니까?")) {
  230. $.ajax({
  231. url: (this.callback + "/delete"),
  232. type: 'post',
  233. data: {bid: this.bid, pid: this.pid, _method: "delete"},
  234. dataType: 'json',
  235. beforeSend: function (xhr) {
  236. if (!loginCheck()) {
  237. xhr.abort();
  238. }
  239. },
  240. success: function (res) {
  241. if (res.success) {
  242. alert("게시글이 삭제되었습니다.");
  243. location.replace(Post.listURL);
  244. } else {
  245. alert(res.message);
  246. }
  247. },
  248. error: function (xhr, status, err) {
  249. procErrorEvent(xhr, status, err);
  250. }
  251. });
  252. }
  253. },
  254. // 신고
  255. blame: function(e)
  256. {
  257. e.blur();
  258. if(!loginCheck()) {
  259. return false;
  260. }
  261. // 신고 접수 유효성
  262. $("#fPostBlame").validate({
  263. onkeyup: false,
  264. onclick: false,
  265. onfocusout: false,
  266. rules: {
  267. blame_type: {required: true, contains: ['1', '2', '3', '4', '5', '6', '7', '8', '9']},
  268. blame_reason: {required: true, normalizer: function (value) {return $.trim(value);}, maxlength: 1000}
  269. },
  270. messages: {
  271. blame_type: {required: "신고 유형을 선택해주세요.", contains: "잘못된 요청입니다."},
  272. blame_reason: {required: "신고 내용을 입력해주세요.", maxlength: "신고 내용은 {0}자까지 입력 가능합니다."}
  273. },
  274. showErrors: function (errorMap, errorList) {
  275. if (this.numberOfInvalids()) {
  276. alert(errorList[0].message);
  277. errorList[0].element.focus();
  278. }
  279. },
  280. submitHandler: function (form) {
  281. if (confirm("신고를 접수하시겠습니까?")) {
  282. let bid = Post.bid;
  283. let pid = Post.pid;
  284. $.ajax({
  285. url: (Post.callback + "/blame"),
  286. type: 'post',
  287. cache: false,
  288. async: false,
  289. data: {bid: bid, pid: pid, type: form.blame_type.value, reason: form.blame_reason.value},
  290. dataType: 'json',
  291. beforeSend: function (xhr) {
  292. if (!loginCheck()) {
  293. xhr.abort();
  294. }
  295. },
  296. success: function (res) {
  297. form.blame_type.value = "";
  298. form.blame_reason.value = "";
  299. if (res.success) {
  300. alert("감사합니다. 신고가 접수되었습니다. 이용 규칙을 위반한 것으로 확인되면 게시글이 삭제됩니다.");
  301. } else {
  302. alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오.");
  303. }
  304. $('div.modal').modal('hide');
  305. },
  306. error: function (xhr, status, err) {
  307. procErrorEvent(xhr, status, err);
  308. }
  309. });
  310. }
  311. }
  312. });
  313. },
  314. // 인쇄
  315. print: function(e)
  316. {
  317. let $container = $("div.container").clone();
  318. let cssText = "";
  319. let cssLink = "";
  320. let style = document.getElementsByTagName("style");
  321. let link = document.getElementsByTagName("link");
  322. for (let i in style) {
  323. if(typeof style[i].innerHTML !== "undefined") {
  324. cssText += style[i].innerHTML;
  325. }
  326. }
  327. for (let i in link) {
  328. if(typeof link[i].outerHTML !== "undefined") {
  329. cssLink += link[i].outerHTML;
  330. }
  331. }
  332. let popupWindow = popupCenter(null, "_blank", 900, 680);
  333. if (popupWindow.focus) {
  334. popupWindow.focus();
  335. }
  336. let printHtml = '<!DOCTYPE html>' +
  337. '<html lang="ko">' +
  338. '<head>' +
  339. cssLink +
  340. '<style>' + cssText + '</style>' +
  341. '</head>' +
  342. '<body>' + $container.get(0).innerHTML + '</body>' +
  343. '</html>';
  344. popupWindow.document.write(printHtml);
  345. // 프린트 전
  346. popupWindow.onbeforeprint = function () {
  347. printHtml = popupWindow.document.body;
  348. printHtml.style.backgroundColor = "#fff";
  349. printHtml.style.padding = "20px";
  350. printHtml.style.height = "initial";
  351. printHtml.style.width = "100%";
  352. printHtml.style.minWidth = "auto";
  353. printHtml.style.maxWidth = "initial";
  354. printHtml.parentNode.parentNode.getElementById("boardView").style.maxWidth = "initial";
  355. printHtml.getElementsByTagName("nav")[0].remove();
  356. printHtml.getElementsByTagName("blockquote")[0].style.border = "none";
  357. printHtml.getElementsByClassName("foot")[0].remove();
  358. popupWindow.document.body.innerHTML = printHtml.innerHTML;
  359. };
  360. // 프린트 후
  361. popupWindow.onafterprint = function () {
  362. popupWindow.document.body.innerHTML = "";
  363. popupWindow.document.write("");
  364. };
  365. popupWindow.document.close();
  366. popupWindow.focus();
  367. setTimeout(function () {
  368. popupWindow.print();
  369. popupWindow.close();
  370. }, 300);
  371. return false;
  372. },
  373. // 좋아요
  374. like: function(e)
  375. {
  376. e.blur();
  377. let bid = this.bid;
  378. let pid = this.pid;
  379. let type = e.value;
  380. let active = Number(e.dataset.active);
  381. $.ajax({
  382. url: (this.callback + "/like"),
  383. type: 'post',
  384. cache: false,
  385. async: false,
  386. data: {bid: bid, pid: pid, type: type},
  387. dataType: 'json',
  388. beforeSend: function (xhr) {
  389. if (!loginCheck()) {
  390. xhr.abort();
  391. }
  392. },
  393. success: function (res) {
  394. if (res.success) {
  395. Post.toggleLike(1, !active);
  396. } else {
  397. alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오.");
  398. }
  399. },
  400. error: function (xhr, status, err) {
  401. procErrorEvent(xhr, status, err);
  402. }
  403. });
  404. },
  405. // 싫어요
  406. dislike: function(e)
  407. {
  408. e.blur();
  409. let bid = this.bid;
  410. let pid = this.pid;
  411. let type = e.value;
  412. let active = Number(e.dataset.active);
  413. $.ajax({
  414. url: (this.callback + "/dislike"),
  415. type: 'post',
  416. cache: false,
  417. async: false,
  418. data: {bid: bid, pid: pid, type: type},
  419. dataType: 'json',
  420. beforeSend: function (xhr) {
  421. if (!loginCheck()) {
  422. xhr.abort();
  423. }
  424. },
  425. success: function (res) {
  426. if (res.success) {
  427. Post.toggleLike(2, !active);
  428. } else {
  429. alert(res.message || "처리 중 오류가 발생하였습니다. 관리자에게 문의하십시오.");
  430. }
  431. },
  432. error: function (xhr, status, err) {
  433. procErrorEvent(xhr, status, err);
  434. }
  435. });
  436. },
  437. // 다운로드
  438. download: function (e)
  439. {
  440. if (!loginCheck()) {
  441. return false;
  442. }
  443. let bid = this.bid;
  444. let pid = this.pid;
  445. let fid = e.dataset.fid;
  446. let form = document.createElement("form");
  447. let inputHidden_1 = document.createElement("input");
  448. let inputHidden_2 = document.createElement("input");
  449. let inputHidden_3 = document.createElement("input");
  450. let inputHidden_4 = document.createElement("input");
  451. let iframe = document.createElement("iframe");
  452. iframe.setAttribute("name", "fr_an_post");
  453. iframe.setAttribute("width", "0");
  454. iframe.setAttribute("height", "0");
  455. iframe.setAttribute("hidden", "hidden");
  456. iframe.src = ("data:text/html;charset=utf-8," + encodeURI(""));
  457. form.method = "post";
  458. form.action = (this.callback + "/download");
  459. form.target = "fr_an_post";
  460. inputHidden_1.type = "hidden";
  461. inputHidden_1.name = "bid";
  462. inputHidden_1.value = bid;
  463. inputHidden_2.type = "hidden";
  464. inputHidden_2.name = "pid";
  465. inputHidden_2.value = pid;
  466. inputHidden_3.type = "hidden";
  467. inputHidden_3.name = "fid";
  468. inputHidden_3.value = fid;
  469. inputHidden_4.type = "hidden";
  470. inputHidden_4.name = "_token";
  471. inputHidden_4.value = CSRF;
  472. form.appendChild(inputHidden_1);
  473. form.appendChild(inputHidden_2);
  474. form.appendChild(inputHidden_3);
  475. form.appendChild(inputHidden_4);
  476. document.body.appendChild(iframe);
  477. document.body.appendChild(form);
  478. form.submit();
  479. return false;
  480. },
  481. // Link 이동
  482. link: function (e)
  483. {
  484. let bid = this.bid;
  485. let pid = this.pid;
  486. let lid = e.dataset.lid;
  487. $.ajax({
  488. url: (this.callback + "/link"),
  489. type: 'post',
  490. cache: false,
  491. async: false,
  492. data: {bid: bid, pid: pid, lid: lid},
  493. dataType: 'json',
  494. beforeSend: function (xhr) {
  495. if (!loginCheck()) {
  496. xhr.abort();
  497. }
  498. },
  499. success: function (res) {
  500. let a = document.createElement('a');
  501. a.href = res.href;
  502. a.target = "_blank";
  503. a.click();
  504. },
  505. error: function (xhr, status, err) {
  506. procErrorEvent(xhr, status, err);
  507. }
  508. });
  509. return false;
  510. },
  511. // 좋아요/싫어요 처리
  512. toggleLike: function(type, active) {
  513. let btnPostLike = document.getElementById("btnPostLike");
  514. let btnPostDisLike = document.getElementById("btnPostDislike");
  515. if(type === 1) {
  516. this.setLike(active);
  517. if(Number(btnPostDisLike.dataset.active)) {
  518. this.setDislike(false);
  519. }
  520. }else if(type === 2) {
  521. this.setDislike(active);
  522. if(Number(btnPostLike.dataset.active)) {
  523. this.setLike(false);
  524. }
  525. }
  526. },
  527. // 좋아요 갱신
  528. setLike: function(active) {
  529. let button = document.getElementById("btnPostLike");
  530. let rows = Number(button.dataset.rows);
  531. if (active) {
  532. rows = (rows + 1);
  533. button.innerHTML = '<i class="fas fa-thumbs-up"></i><span> ' + String(rows) + '</span>';
  534. button.dataset.active = "1";
  535. }else{
  536. rows = (rows > 0 ? rows - 1 : rows);
  537. button.innerHTML = '<i class="far fa-thumbs-up"></i><span> ' + String(rows) + '</span>';
  538. button.dataset.active = "0";
  539. }
  540. button.dataset.rows = rows;
  541. document.getElementById("txtPostLikeCnt").innerText = rows;
  542. },
  543. // 싫어요 갱신
  544. setDislike: function(active) {
  545. let button = document.getElementById("btnPostDislike");
  546. let rows = Number(button.dataset.rows);
  547. if (active) {
  548. rows = (rows + 1);
  549. button.innerHTML = '<i class="fas fa-thumbs-down"></i><span> ' + String(rows) + '</span>';
  550. button.dataset.active = "1";
  551. }else{
  552. rows = (rows > 0 ? rows - 1 : rows);
  553. button.innerHTML = '<i class="far fa-thumbs-down"></i><span> ' + String(rows) + '</span>';
  554. button.dataset.active = "0";
  555. }
  556. button.dataset.rows = rows;
  557. document.getElementById("txtPostDislikeCnt").innerText = rows;
  558. },
  559. // 게시글 QR 보기
  560. qrcode: function(e) {
  561. let $modalQRCode = $("#modalQRCode");
  562. $modalQRCode.find(".modal-header .modal-title").html(e.dataset.title);
  563. $modalQRCode.find(".modal-body").html('<br/><img src="https://chart.googleapis.com/chart?cht=qr&chld=H|2&chs=100&chl="' + encodeURIComponent(this.href) + "/><br/><br/>QR Code를 스캔하시면 게시글을 확인하실 수 있습니다.");
  564. $modalQRCode.modal('toggle');
  565. return false;
  566. }
  567. };
  568. // 게시글 등록/수정 실행
  569. $(document).on("click", "#btnPostSubmit", function() {
  570. Post.submit(this);
  571. });
  572. // 파일 용량 확인
  573. $(document).on("change", "input.form-control-file", function() {
  574. Post.displayFileSize(this);
  575. });
  576. // 전체공지, 공지 둘중 하나만 체크
  577. $(document).on("change", "input#isSpeaker, input#isNotice", function() {
  578. let $all = $("#isSpeaker");
  579. let $one = $("#isNotice");
  580. if (this.id === "isNotice" && $all.is(":checked")) {
  581. $all.prop("checked", false);
  582. } else if (this.id === "isSpeaker" && $one.is(":checked")) {
  583. $one.prop("checked", false);
  584. }
  585. });
  586. // QR-CODE 보기
  587. $(document).on("click", "a#btnPostQRCode, a.btn-qr-code", function () {
  588. return Post.qrcode(this);
  589. });
  590. // 게시글 인쇄
  591. $(document).on("click", "#btnPostPrint", function () {
  592. return Post.print(this);
  593. });
  594. // 게시글 주소 복사
  595. $(document).on("click", "#btnPostCopy", function() {
  596. return Post.copy(this);
  597. });
  598. // 게시글 즐겨찾기
  599. $(document).on("click", "#btnPostBookmark", function() {
  600. Post.bookmark(this);
  601. });
  602. // 게시글 신고
  603. $(document).on("click", "#btnPostBlame", function() {
  604. Post.blame(this);
  605. });
  606. // 게시글 삭제
  607. $(document).on("click", "#btnPostDelete", function() {
  608. Post.delete(this);
  609. });
  610. // 게시글 좋아요
  611. $(document).on("click", "#btnPostLike", function () {
  612. Post.like(this);
  613. });
  614. // 게시글 싫어요
  615. $(document).on("click", "#btnPostDislike", function () {
  616. Post.dislike(this);
  617. });
  618. // 게시글 다운로드
  619. $(document).on("click", "a.btn-file-download", function () {
  620. return Post.download(this);
  621. });
  622. // 게시글 Link 이동
  623. $(document).on("click", "a.btn-site-url", function () {
  624. return Post.link(this);
  625. });
  626. // 게시글 신고 모달 열리면 초기화
  627. $(document).on("show.bs.modal", "#postBlameModal", function(e) {
  628. let form = e.currentTarget.getElementsByTagName("form")[0];
  629. form.elements["blame_type"].selectedIndex = 0;
  630. form.elements["blame_reason"].value = "";
  631. });