window.loadingReplies = window.loadingReplies || {}; window.latestNewComment = null; window.toggleReplies = function(cid) { var container = $("#replies_" + cid); if (!container.length) { container = $("#reply_list_" + cid); } if (!container.length) return; if (container.data("animating")) return; if (container.is(":animated")) return; if (!container.is(":visible")) { container.data("animating", true); container.removeClass("d-none").slideDown(function() { container.data("animating", false); }); if (container.children().length === 0) { if (!window.loadingReplies[cid]) { window.loadingReplies[cid] = true; window.loadRepliesAndOpen(cid, 1, function(){ window.loadingReplies[cid] = false; }); } } } else { container.data("animating", true); container.slideUp(function(){ container.addClass("d-none"); container.data("animating", false); }); } }; (function($){ $(document).ready(function(){ $(".reply-count-link").removeAttr("onclick"); var currentEditId = null; var originalOuterHTML = ""; window.cancelParentEdit = function(){ if (!currentEditId) return; var c = $("#comment_" + currentEditId); c.replaceWith(originalOuterHTML); currentEditId = null; }; window.resetParentCommentForm = function($form){ if (!$form.length) return; $form[0].reset(); }; $(document).off("click", ".comment-edit-btn").on("click", ".comment-edit-btn", function(e){ e.preventDefault(); var c = $(this).closest(".comment-item"); if(c.hasClass("child-comment")) return; var cid = c.data("comment-id"); if(currentEditId && currentEditId !== cid){ $("#comment_" + currentEditId).replaceWith(originalOuterHTML); currentEditId = null; } c.removeClass("py-2").addClass("p-2"); originalOuterHTML = $("#comment_" + cid).prop("outerHTML"); currentEditId = cid; var oh = c.find(".comment-content").html() || ""; // jQuery trick: decode HTML to plain text var ot = $("
").html(oh).text(); var t = c.data("commenter-name") || ""; if(t && ot.indexOf("@" + t) === 0) { ot = ot.substring(("@" + t).length).trim(); } var sv = parseFloat(c.data("star") || 0); var isSec = !!c.data("secret"); c.empty(); var f = ''; f += '
'; f += ''; f += ''; f += ''; f += ''; f += ''; f += ''; f += '
'; f += '프로필'; f += '
'; f += '
'; f += '
'; f += ''; f += '
'; f += ''; f += ''; f += ''; f += ''; f += ''; f += '
'; f += ''; f += '
'; f += '
'; if(typeof IS_LOGGED_IN !== "undefined" && IS_LOGGED_IN){ f += '
'; } f += ''; f += ''; f += '
'; c.append(f); var fm = c.find(".inline-edit-form"); if(sv > 0){ fm.find("#wr_star").val(sv); var st = fm.find(".starpoint_box .fa-star"); st.each(function(i){ if((i+1) <= sv) $(this).addClass("active"); }); fm.find("#star-rating-block, .user-info, .reply-buttons, .btn-cancel, .btn-reply").removeClass("d-none"); fm.find(".btn-reply").prop("disabled", false).text("수정"); } else { fm.find("#star-rating-block, .user-info, .reply-buttons, .btn-cancel, .btn-reply").removeClass("d-none"); fm.find(".btn-reply").prop("disabled", false).text("수정"); } if(isSec){ fm.find(".secret-check").removeClass("d-none"); fm.find("#wr_secret_inline").prop("checked", true); } else { fm.find(".secret-check").removeClass("d-none"); } fm.find(".starpoint_box .fa-star").on("click", function(){ var v = $(this).data("value"); fm.find("#wr_star").val(v); fm.find(".fa-star").removeClass("active").each(function(i){ if((i+1) <= v) $(this).addClass("active"); }); }); fm.find(".btn-cancel").on("click", function(e){ e.preventDefault(); $("#comment_" + cid).replaceWith(originalOuterHTML); currentEditId = null; }); fm.on("input", "input[name='wr_content']", function(){ var v = $(this).val().trim(); fm.find(".btn-reply").prop("disabled", v.length < 1); }); fm.on("submit", function(e){ e.preventDefault(); $.ajax({ url: fm.attr("action"), type: "POST", data: fm.serialize(), dataType: "json", success: function(r){ if(r.success){ var cid = fm.find("input[name='comment_id']").val(); $("#comment_" + cid).replaceWith(r.comment_html); if(typeof updateRatingSummary === "function") updateRatingSummary(); currentEditId = null; } } }); }); }); $(document).off("click", ".comment-like-btn:not(.child-like)").on("click", ".comment-like-btn:not(.child-like)", function(e){ e.preventDefault(); var cid = $(this).data("id"); if(!cid)return; var b = $(this); var l = b.find("span"); var db = b.closest("ul,.bo_vc_act").find(".comment-dislike-btn:not(.child-like)").find("span"); $.ajax({ url: (typeof AJAX_COMMENT_ACTION_URL !== "undefined") ? AJAX_COMMENT_ACTION_URL : "/comment/comment_action.php", type: "POST", dataType: "json", data: { action: "vote", bo_table: (typeof BO_TABLE !== "undefined") ? BO_TABLE : "", comment_id: cid, vote_type: "good" }, success: function(res){ if(res.success){ var ng = res.good_count || 0, nog = res.nogood_count || 0; l.text(ng); db.text(nog); b.find("i").removeClass("hand_up hand_up-2").addClass(res.new_vote_type==="good" ? "hand_up-2" : "hand_up"); b.closest("ul,.bo_vc_act").find(".comment-dislike-btn:not(.child-like) i") .removeClass("hand_down hand_down-2") .addClass(res.new_vote_type==="nogood" ? "hand_down-2" : "hand_down"); } } }); }); $(document).off("click", ".comment-dislike-btn:not(.child-like)").on("click", ".comment-dislike-btn:not(.child-like)", function(e){ e.preventDefault(); var cid = $(this).data("id"); if(!cid)return; var b = $(this); var lb = b.closest("ul,.bo_vc_act").find(".comment-like-btn:not(.child-like)").find("span"); var db = b.find("span"); $.ajax({ url: (typeof AJAX_COMMENT_ACTION_URL !== "undefined") ? AJAX_COMMENT_ACTION_URL : "/comment/comment_action.php", type: "POST", dataType: "json", data: { action: "vote", bo_table: (typeof BO_TABLE !== "undefined") ? BO_TABLE : "", comment_id: cid, vote_type: "nogood" }, success: function(res){ if(res.success){ var ng = res.good_count || 0, nog = res.nogood_count || 0; lb.text(ng); db.text(nog); b.closest("ul,.bo_vc_act").find(".comment-like-btn:not(.child-like) i") .removeClass("hand_up hand_up-2") .addClass(res.new_vote_type==="good" ? "hand_up-2" : "hand_up"); b.find("i").removeClass("hand_down hand_down-2") .addClass(res.new_vote_type==="nogood" ? "hand_down-2" : "hand_down"); } } }); }); $(document).off("click", ".comment-pin-btn").on("click", ".comment-pin-btn", function(e){ e.preventDefault(); if(!window.IS_ADMIN && !(window.POST_AUTHOR_ID === window.CURRENT_USER_ID)){ alert("권한이 없습니다."); return; } var cid = $(this).data("id"); var p = $(this).data("pin"); var act = (p == 1) ? "unpin" : "pin"; var btn = $(this); $.ajax({ url: (typeof AJAX_COMMENT_ACTION_URL !== "undefined") ? AJAX_COMMENT_ACTION_URL : "/comment/comment_action.php", type: "POST", dataType: "json", data: { token: (typeof CSRF_TOKEN !== "undefined") ? CSRF_TOKEN : "", action: act, bo_table: (typeof BO_TABLE !== "undefined") ? BO_TABLE : "", comment_id: cid }, success: function(r){ if(r.success){ window.loadComments((typeof WR_ID !== "undefined" ? WR_ID : 0), ""); } else { alert(r.message); } }, error: function(){ alert("서버 통신 오류"); } }); }); $(document).off("click", ".reply-btn").on("click", ".reply-btn", function(e){ e.preventDefault(); if(!$(e.target).closest(".more-replies").length){ e.stopPropagation(); } var btn = $(this); var commentItem = btn.closest(".comment-item"); if(window.currentOpenFormType==="parent" && typeof resetParentCommentForm==="function"){ resetParentCommentForm($("#parent-comment-form")); window.currentOpenFormType = null; } if(window.currentOpenFormType==="reply"){ var ex = $(".reply-form-container"); if(ex.length) ex.remove(); window.currentOpenFormType = null; } currentEditId = null; window.currentOpenFormType = "reply"; if(commentItem.find(".reply-form-container").length) return; var replyContainer = $('
'); btn.closest(".d-flex").after(replyContainer); var commenterName = commentItem.data("commenter-name") || ""; var rf = ''; rf += '
'; rf += ''; rf += ''; rf += ''; rf += ''; rf += ''; rf += ''; rf += '
'; rf += '프로필'; rf += '
'; rf += ''; rf += '
'; rf += '
'; rf += '
'; rf += ''; rf += ''; rf += '
'; replyContainer.html(rf); var replyForm = replyContainer.find("form"); replyForm.find("input[name='wr_content']").on("input", function(){ var v = $(this).val().trim(); replyForm.find(".btn-reply").prop("disabled", (v.length < 1)); }); replyForm.find(".btn-cancel").on("click", function(e){ e.preventDefault(); replyContainer.remove(); window.currentOpenFormType = null; }); replyForm.on("submit", function(e) { e.preventDefault(); if ($(this).data("submitted")) return; $(this).data("submitted", true); var bs = $(this).find("button[type='submit']"); bs.prop("disabled", true); var fd = $(this).serialize(); $.ajax({ url: $(this).attr("action"), type: "POST", data: fd, dataType: "json", success: function(r) { if (r.success) { if (r.total_comments !== undefined) { $("#total-comments").text(r.total_comments); } else { var x = parseInt($("#total-comments").text(), 10) || 0; $("#total-comments").text(x + 1); } var pid = commentItem.data("comment-id"); replyForm.closest(".reply-form-container").remove(); window.currentOpenFormType = null; window.loadRepliesAndOpen(pid, 1, function() { var nr = $("#new_replies_" + pid); if (!nr.length) { nr = $(''); $("#replies_" + pid).prepend(nr); } var newElem = $(r.reply_html); var newId = newElem.attr("id"); if ($("#" + newId).length) { $("#" + newId).remove(); } nr.prepend(newElem); var replyCountLink = commentItem.find(".reply-count-link"); if (replyCountLink.length) { var currentText = replyCountLink.text(); var match = currentText.match(/\d+/); var count = match ? parseInt(match[0], 10) + 1 : 1; replyCountLink.html(' 답글 ' + count + '개'); if (count > 0) { commentItem.find(".replybt").removeClass("d-none"); } else { commentItem.find(".replybt").addClass("d-none"); } } else { commentItem.find(".replybt").remove(); commentItem.append('
답글 1개
'); } $("html, body").animate({ scrollTop: commentItem.offset().top }, 0); $("#replies_" + pid).removeClass("d-none").show(); }); } }, complete: function() { replyForm.data("submitted", false); bs.prop("disabled", false); } }); }); }); $(document).off("submit", "#parent-comment-form").on("submit", "#parent-comment-form", function(e){ e.preventDefault(); var f = $(this); if(f.data("submitted")) return; f.data("submitted", true); var b = f.find("button[type='submit']"); b.prop("disabled", true); var d = f.serialize(); $.ajax({ url: f.attr("action"), type: "POST", data: d, dataType: "json", success: function(r){ if(r.success){ var nch = r.comment_html; var nco = $(nch); var cl = $("#comment-list"); var pd = cl.find("li.comment-item[data-pinned='1']"); if(pd.length){ pd.last().after(nco); } else { var firstComment = cl.find("li.comment-item").first(); if(firstComment.length){ firstComment.before(nco); } else { cl.append(nco); } } nco.find(".comment-date").each(function(){ var rawText = $(this).text(); var match = rawText.match(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/); if(match){ var relative = timeAgo(match[1]); $(this).html(' ' + relative); } }); f[0].reset(); if(typeof updateRatingSummary === "function") updateRatingSummary(); window.currentOpenFormType = null; } }, complete: function(){ f.data("submitted", false); b.prop("disabled", false); } }); }); $(window).off("scroll.commentInfinite").on("scroll.commentInfinite", function(){ if(window.isLoading || !window.hasMore) return; var cnt = $("#comment-list li.comment-item").length; if(cnt < 10) return; var st = $(this).scrollTop(), wh = $(this).height(), dh = $(document).height(); if(st + wh >= dh - 200) loadMoreParentComments(); }); function loadMoreParentComments(){ if(window.isLoading) return; window.isLoading = true; if(!$("#infinite-scroll-spinner").length){ $("body").append('
'); } $("#infinite-scroll-spinner").show(); var np = window.currentPage + 1; $.ajax({ url: (typeof AJAX_COMMENT_LIST_URL !== "undefined") ? AJAX_COMMENT_LIST_URL : "/comment/ajax_comment_list.php", type: "GET", data: { bo_table: (typeof BO_TABLE !== "undefined") ? BO_TABLE : "", wr_id: (typeof WR_ID !== "undefined") ? WR_ID : 0, sort: "", page: np }, dataType: "html", success: function(r){ var tmp = $("
").html(r); var nl = tmp.find("#comment-list>li.comment-item").filter(function(){ return !$("#" + $(this).attr("id")).length; }); if(nl.length){ $("#comment-list").append(nl); if(nl.length < 10) window.hasMore = false; } else { window.hasMore = false; } var z = tmp.find("#total-comments").text(); if(z && parseInt(z) > 0) $("#total-comments").text(z); window.currentPage = np; }, complete: function(){ window.isLoading = false; $("#infinite-scroll-spinner").hide(); } }); } window.loadComments = function(wrId, sortType){ if(currentEditId){ $("#comment_" + currentEditId).replaceWith(originalOuterHTML); currentEditId = null; } wrId = (wrId !== undefined) ? wrId : ((typeof WR_ID !== "undefined") ? WR_ID : 0); sortType = ""; $.ajax({ url: (typeof COMMENT_LIST_URL !== "undefined") ? COMMENT_LIST_URL : "/comment/comment_list.php", type: "GET", data: { bo_table: (typeof BO_TABLE !== "undefined") ? BO_TABLE : "", wr_id: wrId, sort: sortType }, dataType: "html", success: function(d){ var r = $(d).filter("#comment-list"); if(r.length) $("#comment-list").html(r.html()); else $("#comment-list").empty(); var c = $(d).find("#total-comments").text(); if(c) $("#total-comments").text(c); var tc = parseInt(c, 10) || 0, lc = $("#comment-list li.comment-item").length; window.hasMore = (tc > lc); window.currentPage = 1; } }); }; $(document).on("show.bs.dropdown", ".comment-dropdown", function(){ var dd = $(this); var c = dd.closest(".comment-item"); var cm = c.data("commenter-id"); if(!(window.IS_ADMIN || window.POST_AUTHOR_ID === window.CURRENT_USER_ID)){ dd.find(".action-pin").remove(); } if(cm === window.CURRENT_USER_ID){ dd.find(".action-report").remove(); } }); if (!window.replyToggleBound) { $(document).on("click", ".reply-count-link", function(e){ e.preventDefault(); e.stopPropagation(); var parentId = $(this).data("parent-id"); if(!parentId){ var pe = $(this).closest("[id^='comment_']"); if(pe.length){ parentId = pe.attr("id").replace("comment_",""); } } if(parentId && typeof window.toggleReplies === "function"){ window.toggleReplies(parentId); } }); window.replyToggleBound = true; } $(document).on("childReplyAdded", function(e, cid){ var parentReplyLink = $("#comment_" + cid).find(".reply-count-link"); if(parentReplyLink.length){ var txt = parentReplyLink.text(); var m = txt.match(/답글\s*(\d+)/); if(m){ var cnt = parseInt(m[1],10) + 1; parentReplyLink.html(' 답글 ' + cnt + '개'); } else { parentReplyLink.html(' 답글 1개'); } } }); // ============================================================ // 5) AJAX로 자식 댓글을 불러오는 loadRepliesAndOpen (중복 제거 포함) // ============================================================ window.loadRepliesAndOpen = function(cid, page, cb, newId, newHtml){ var c = $("#comment_" + cid), r = $("#replies_" + cid); if(!c.length){ if(typeof cb === "function") cb(); return; } if(!r.length){ r = $('
'); c.append(r); } var rl = $("#reply_list_" + cid); if(!rl.length){ rl = $(''); r.append(rl); } if(page === 1 && rl.is(":empty")){ rl.html("
"); } else { rl.find("#temp-spinner").remove(); rl.append("
"); } var requestUrl; if(page > 1){ requestUrl = (typeof AJAX_COMMENT_REPLIES_URL !== "undefined") ? AJAX_COMMENT_REPLIES_URL : "/comment/ajax_comment_replies.php"; } else { requestUrl = (typeof COMMENT_REPLY_URL !== "undefined") ? COMMENT_REPLY_URL : "/comment/comment_reply.php"; } $.ajax({ url: requestUrl, type: "GET", data: { bo_table: (typeof BO_TABLE !== "undefined") ? BO_TABLE : "", comment_id: cid, page: page, limit: 10 }, dataType: "html", success: function(res){ $("[data-comment-id='" + cid + "'].more-replies-container").remove(); $("[data-comment-id='" + cid + "'].more-replies").remove(); var $res = $(res); var $replyList = $res.filter("#reply_list_" + cid); if(!$replyList.length){ $replyList = $res.find("#reply_list_" + cid); } var newList = $replyList.html() || ""; rl.find("#temp-spinner").remove(); if(page === 1){ rl.html(newList); } else { rl.append(newList); } // #new_replies_ 컨테이너: 대댓글 작성은 여기서만 처리 (업데이트는 이 컨테이너에서) var nr = $("#new_replies_" + cid); if(!nr.length){ nr = $(''); r.prepend(nr); } if(newId !== undefined && newHtml){ var $newElem = $(newHtml); var isSecret = $newElem.data("secret"); // `data-secret`이 비밀글 여부를 나타낸다고 가정 if (isSecret) { $newElem.find('.comment-content').html('비밀글입니다.'); } var $existing = nr.find("li.child-comment[data-comment-id='" + newId + "']"); if($existing.length){ $existing.first().replaceWith($newElem); $existing.not(":first").remove(); } else { nr.prepend($newElem); } } var seen = {}; $("#new_replies_" + cid + ", #reply_list_" + cid).find("li.child-comment").each(function(){ var commentId = $(this).data("comment-id"); if(seen[commentId]){ $(this).remove(); } else { seen[commentId] = true; } }); // ★ 중복 제거 로직 끝 ★ var moreWrapper = $res.find(".more-replies-container"); if(moreWrapper.length){ var existingContainer = r.find(".more-replies-container[data-comment-id='" + cid + "']"); if(existingContainer.length){ existingContainer.remove(); } r.append(moreWrapper); } else { var moreLink = $res.find(".more-replies"); if(moreLink.length){ var existingLink = r.find(".more-replies[data-comment-id='" + cid + "']"); if(existingLink.length){ existingLink.remove(); } r.append(moreLink); } } if(typeof cb === "function"){ cb(); } }, error: function(){ if(typeof cb === "function") cb(); } }); }; }); })(jQuery);