1(function ($, drupalSettings) { 2 function fromNow( 3 date, 4 nowDate = Date.now(), 5 rft = new Intl.RelativeTimeFormat(drupalSettings.path.currentLanguage || 'vi', {numeric: "auto"}) 6 ) { 7 const SECOND = 1000; 8 const MINUTE = 60 * SECOND; 9 const HOUR = 60 * MINUTE; 10 const DAY = 24 * HOUR; 11 const WEEK = 7 * DAY; 12 const MONTH = 30 * DAY; 13 const YEAR = 365 * DAY; 14 const intervals = [ 15 {ge: YEAR, divisor: YEAR, unit: "year"}, 16 {ge: MONTH, divisor: MONTH, unit: "month"}, 17 {ge: WEEK, divisor: WEEK, unit: "week"}, 18 {ge: DAY, divisor: DAY, unit: "day"}, 19 {ge: HOUR, divisor: HOUR, unit: "hour"}, 20 {ge: MINUTE, divisor: MINUTE, unit: "minute"}, 21 {ge: 30 * SECOND, divisor: SECOND, unit: "seconds"}, 22 {ge: 0, divisor: 1, text: "just now"} 23 ]; 24 const now = typeof nowDate === "object" ? nowDate.getTime() : new Date(nowDate).getTime(); 25 const diff = now - (typeof date === "object" ? date : new Date(date)).getTime(); 26 const diffAbs = Math.abs(diff); 27 for (const interval of intervals) { 28 if (diffAbs >= interval.ge) { 29 const x = Math.round(Math.abs(diff) / interval.divisor); 30 const isFuture = diff < 0; 31 return interval.unit ? rft.format(isFuture ? x : -x, interval.unit) : interval.text; 32 } 33 } 34 } 35 36 $(document).ready(function () { 37 var fieldComment = $("#vov-social-field-comment"); 38 var vovAccountUrl = window.vovAccountLoginUrl; 39 let nid = drupalSettings.statistics.data.nid; 40 let lid = null; 41 if(!nid) return; 42 function listComment(loadMore = false) { 43 let url = `${vovAccountUrl}/get-all-comment?nid=${nid}`; 44 if (loadMore && lid) { 45 url += `&lid=${lid}`; 46 } 47 fetch(url) 48 .then(response => response.json()) 49 .then(result => { 50 if (loadMore) { 51 fieldComment.append(initHtml(result.data)); 52 } else { 53 fieldComment.html(initHtml(result.data)); 54 } 55 if (result.data.length > 0) { 56 lid = result.data[result.data.length - 1].cid; 57 $("#load-more-comments").show(); 58 } else { 59 $("#load-more-comments").hide(); 60 } 61 }) 62 .catch(error => console.error("Lá»i khi tải bình luáºn!", error)); 63 } 64 65 function listChildComment(cid) { 66 fetch(`${vovAccountUrl}/get-by-pid?pid=${cid}`) 67 .then(response => response.json()) 68 .then(result => { 69 if (result.data && result.data.length > 0) { 70 $(`#comment-children-${cid}`).html(initHtml(result.data)); 71 } 72 }) 73 .catch(error => console.error("Lá»i khi tải bình luáºn con!", error)); 74 } 75 76 function resetForm(form) { 77 form[0].reset(); 78 form.find("input[name='username']").hide(); 79 } 80 81 function initHtml(data) { 82 let result = ""; 83 data.forEach(element => { 84 let avaUrl = element.user_avatar || `https://ui-avatars.com/api/?length=1&name=${element.username}`; 85 result += ` 86 <div class="comment-item__custom"> 87 <div class="comment-item" id="vov-social-comment-${element.cid}"> 88 <div class="comment-item__text">${element.content}</div> 89 <div class="comment-item__control"> 90 <div class="row"> 91 <div class="col-md-6"> 92 <div class="comment-item__author"> 93 <div class="comment-item__avatar"> 94 <img src="${avaUrl}" width="24" height="24"> 95 </div>
96 <span class="comment-item__name">${element.username}</span> 97 <span class="comment-item__time">- ${fromNow(element.created_at)}</span> 98 </div> 99 </div> 100 <div class="col-md-6 text-end"> 101 <label class="text-vov-red ms-2 pointer reply-comment" data-cid="${element.cid}">Phản há»i</label> 102 </div> 103 </div> 104 </div> 105 <div id="comment-children-${element.cid}" class="comment-children"></div> 106 <div id="reply-form-${element.cid}" class="reply-form" style="display: none;"> 107 <form class="box-comment mt-3"> 108 <div class="mt-2 comment-form"> 109 <div class="d-flex flex-column gap-2 w-100"> 110 <textarea id="commentText-${element.cid}" name="content" class="comment-box w-100 form-item__textarea" rows="1" placeholder="Thêm phản há»i..." required></textarea> 111 <input id="userName-${element.cid}" name="username" style="display: none" class="w-100 border px-3 py-2 rounded-2 mb-2" placeholder="Há» và tên..." /> 112 </div> 113 </div> 114 <div class="comment-form"> 115 <button type="submit" class="btn-vov-red btn m-auto">Gá»i</button> 116 </div> 117 </form> 118 </div> 119 </div> 120 </div> 121 `; 122 listChildComment(element.cid); 123 }); 124 return result; 125 } 126 127 $("body").on("click", ".reply-comment", function () { 128 let cid = $(this).data("cid"); 129 $(`#reply-form-${cid}`).toggle(); 130 }); 131 132 $("body").on("input", "textarea[name='content']", function () { 133 let parent = $(this).closest(".box-comment"); 134 let userNameInput = parent.find("input[name='username']"); 135 if ($(this).val().trim().length > 0) { 136 userNameInput.show(); 137 } else { 138 userNameInput.hide(); 139 } 140 }); 141 142 $("body").on("submit", ".box-comment", function (event) { 143 event.preventDefault(); 144 let form = $(this); 145 let commentText = form.find("textarea[name='content']").val().trim(); 146 let userName = form.find("input[name='username']").val().trim(); 147 let currentComment = form.closest(".comment-item__custom"); 148 let parentComment = currentComment.parent().closest(".comment-item__custom"); 149 let parentId = parentComment.length ? parentComment.find(".reply-comment").first().data("cid") : currentComment.find(".reply-comment").first().data("cid"); 150 151 152 if (!commentText) { 153 alert("Bình luáºn không thá» Äá» trá»ng."); 154 return; 155 } 156 if (!userName) { 157 alert("Tên không thá» Äá» trá»ng."); 158 return; 159 } 160 if (typeof grecaptcha === "undefined") { 161 console.error("Google reCAPTCHA chưa ÄÆ°á»£c tải!"); 162 } else { 163 grecaptcha.ready(function () { 164 grecaptcha.execute('6LeA1IAmAAAAAFKDrtl_4OAKuuAuF0be9j953jLT', {action: 'submit'}).then(function (token) { 165 $.ajax({ 166 type: "post", 167 url: vovAccountUrl + "/api/create-comment-new?lang=" + drupalSettings.path.currentLanguage, 168 data: parentId ? form.serialize()+"&nid="+ nid + "&pid="+ parentId+ "&gresponse=" + token : form.serialize()+"&nid="+ nid + "&gresponse=" + token, 169 success: function () { 170 document.getElementById("modal-comment").style.display = "flex"; 171 let count = 3; 172 const interval = setInterval(() => { 173 document.getElementById("time-count-down").textContent = count + "s"; 174 if (count === 0) { 175 document.getElementById("modal-comment").style.display = "none"; 176 clearInterval(interval); 177 } 178 count--; 179 }, 1000); 180 listComment(); 181 resetForm(form); 182 }, 183 error: function (xhr) { 184 let res = xhr.responseJSON; 185 if (!res) { 186 try { res = JSON.parse(xhr.responseText); } catch (e) { res = null; } 187 } 188 let details = []; 189 if (res && res.errors && Array.isArray(res.errors.content)) { 190 details = res.errors.content; 191 } 192 alert(details.length > 0 ? details.join("\n") : "Dữ liá»u không hợp lá»."); 193 }, 194 }); 195 }); 196 }); 197 } 198 }); 199 200 $("body").on("click", "#load-more-comments", function () { 201 listComment(true); 202 }); 203 $("body").on("click", "#close-modal-comment", function () { 204 document.getElementById('modal-comment').style.display = 'none' 205 }); 206 if (fieldComment.length > 0) { 207 listComment(); 208 } 209 }); 210})(jQuery, drupalSettings);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.