1/* 2 | Snicker The first native FlatFile Comment Plugin 4 Bludit 3 | @file ./system/themes/default/snicker.js 4 | @author SamBrishes <[email protected]> 5 | @version 0.1.2 [0.1.0] - Alpha 6 | 7 | @website https://github.com/pytesNET/snicker 8 | @license X11 / MIT License 9 | @copyright Copyright © 2019 SamBrishes, pytesNET <[email protected]> 10 */ 11;(function(root){ 12 "use strict"; 13 var w = root, d = root.document; 14 15 /* 16 | AJAX HELPER 17 */ 18 function ajax(url, type, data, callback, self){ 19 var xhttp = new XMLHttpRequest(); 20 xhttp.onreadystatechange = function(){ 21 if(this.readyState == 4){ 22 callback.call((self? self: this), this.responseText, this); 23 } 24 }; 25 xhttp.open(type, url, true); 26 xhttp.setRequestHeader("Cache-Control", "no-cache"); 27 xhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest"); 28 if(type == "POST"){ 29 if(!(data instanceof FormData)){ 30 xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 31 } 32 xhttp.send(data); 33 } else { 34 xhttp.send(); 35 } 36 } 37 38 /* 39 | ALERT HELPER 40 */ 41 function showAlert(type, message, parent){ 42 var alert = d.createElement("DIV"); 43 alert.className = "comment-alert alert-" + type; 44 alert.innerText = message; 45 46 // Remove Existing Status 47 var alerts = parent.querySelectorAll(".comment-alert"); 48 if(alerts.length > 0){ 49 Array.prototype.forEach.call(alerts, function(item){ 50 item.parentElement.removeChild(item); 51 }); 52 } 53 54 // Add Status 55 if(parent.children.length > 0){ 56 parent.insertBefore(alert, parent.children[0]); 57 } else { 58 parent.appendChild(alert); 59 } 60 } 61 62 // Ready? 63 d.addEventListener("DOMContentLoaded", function(){ 64 "use strict"; 65 66 // Main Elements 67 var form = d.querySelector("form.comment-form"), 68 list = d.querySelector(".snicker-comments-list"), 69 captcha = d.querySelector("a[data-captcha='reload']"); 70 71 /* 72 | HANDLE COMMENT FORM 73 */ 74 if(form){ 75 form.addEventListener("submit", function(event){ 76 event.preventDefault(); 77 if(typeof(FormData) !== "function" || !SNICKER_AJAX){ 78 return true; 79 } 80 var data = new FormData(this), self = this; 81 82 // Check Button 83 var btn = this.querySelector("[name='snicker']"); 84 if(btn.disabled){ 85 return true; 86 } 87 event.preventDefault(); 88 89 // AJAX Call 90 btn.disabled = true; 91 btn.classList.add("loading"); 92 data.append("snicker", btn.value); 93 ajax(SNICKER_PATH, "POST", data, function(json){ 94 var data = JSON.parse(json); 95 96 // Add Comment 97 if(list && data.status == "success" && "comment" in data){ 98 if(list.querySelector(".comment")){ 99 var parentComment = form.querySelector('[name="comment[parent_uid]"]'); 100 if(parentComment != null){ 101 var parentCommentId = parentComment.value; 102 list.querySelector("#comment-"+parentCommentId).insertAdjacentHTML("afterend", data.comment); 103 }else { 104 list.querySelector(".comment").insertAdjacentHTML("beforebegin", data.comment); 105 } 106 } else { 107 list.insertAdjacentHTML("afterbegin", data.comment); 108 } 109 list.querySelector(".comment").classList.add("new-comment"); 110 list.querySelector(".comment").scrollIntoView({ behavior: "smooth", block: "center" }); 111 112 // Empty Form 113 var field = self.querySelectorAll("#comment-title,#comment-text"); 114 for(var i = 0, l = field.length; i < l; i++){ 115 if(field[i].tagName == "SELECT"){ 116 field[i].options[0].selected = true; 117 } else if(field[i].getAttribute("type") === "checkbox"){ 118 field[i].checked = false;
119 } else { 120 field[i].value = ""; 121 } 122 } 123 124 showAlert("success", data.success, form.querySelector(".comment-article")); 125 } else if(data.status === "error"){ 126 showAlert("error", data.error, form.querySelector(".comment-article")); 127 128 if(captcha && "captcha" in data){ 129 d.querySelector("input[name='comment[captcha]']").value = ""; 130 captcha.querySelector("img").src = data.captcha; 131 } 132 } 133 134 // Re-Enable Button 135 btn.disabled = false; 136 btn.classList.remove("loading"); 137 }); 138 }); 139 } 140 141 /* 142 | HANDLE CAPTCHA RELOAD 143 */ 144 if(captcha){ 145 captcha.addEventListener("click", function(event){ 146 if(!SNICKER_AJAX){ 147 return false; 148 } 149 event.preventDefault(); 150 captcha.classList.add("reload"); 151 152 var data = "action=snicker&snicker=captcha&tokenCSRF="; 153 var token = d.querySelector("input[name='tokenCSRF']").value; 154 ajax(SNICKER_PATH, "POST", data + token, function(json){ 155 var data = JSON.parse(json); 156 if(data.status !== "success"){ 157 window.location.replace(captcha.getAttribute("href")); 158 } 159 captcha.querySelector("img").src = data.captcha; 160 captcha.classList.remove("reload"); 161 }); 162 }) 163 } 164 165 /* 166 | HANDLE COMMENT REPLY 167 */ 168 if(list){ 169 list.addEventListener("click", function(event){ 170 if(event.target.tagName != "A" || !form){ 171 return true; 172 } 173 174 // Check Link 175 var href = event.target.getAttribute("href"); 176 if(href.indexOf("snicker=reply") < 0){ 177 return true; 178 } 179 180 // Handle Reply 181 event.preventDefault(); 182 var comment = (function getComment(element){ 183 var parent = element.parentElement; 184 return (parent.classList.contains("comment"))? parent: getComment(parent); 185 })(event.target); 186 187 // Create Elements 188 var reply = d.createElement("DIV"); 189 reply.className = "comment-reply"; 190 reply.innerHTML = '<a href="' + window.location.href + '" class="reply-cancel"></a>' 191 + '<div class="reply-title">' + comment.querySelector(".author-username").innerText + ' hat kommentiert:</div>' 192 + '<div class="reply-content">' + comment.querySelector(".comment-comment").innerHTML + '</div>'; 193 var parent = d.createElement("INPUT"); 194 parent.type = "hidden"; 195 parent.name = "comment[parent_uid]"; 196 parent.value = comment.id.replace("comment-", ""); 197 198 // Append Cancel 199 reply.querySelector(".reply-cancel").addEventListener("click", function(event){ 200 event.preventDefault(); 201 202 // Remove Elements 203 reply.parentElement.removeChild(reply); 204 parent.parentElement.removeChild(parent); 205 206 // Switch Button Text 207 // var old = form.querySelector("button").innerText; 208 // form.querySelector("button").value = "comment"; 209 // form.querySelector("button").innerText = form.querySelector("button").getAttribute("data-string"); 210 // form.querySelector("button").setAttribute("data-string", old); 211 }); 212 213 // Inject Elements 214 var art = form.querySelector(".comment-article"); 215 if(art.querySelector(".comment-reply")){ 216 art.replaceChild(reply, art.querySelector(".comment-reply")); 217 } else { 218 art.insertBefore(reply, form.querySelector("textarea").parentElement); 219 } 220 221 var foo = form.querySelector(".comment-footer"); 222 if(foo.querySelector("input[name='comment[parent_uid]']")){ 223 foo.replaceChild(parent, foo.querySelector("input[name='comment[parent_uid]']")); 224 } else { 225 foo.appendChild(parent); 226 } 227 form.querySelector("#comment-text").focus(); 228 window.scrollTo(0, form.offsetTop); 229 // Switch Button Text 230 // var old = form.querySelector("button").innerText; 231 // form.querySelector("button").value = "reply"; 232 // form.querySelector("button").innerText = form.querySelector("button").getAttribute("data-string"); 233 // form.querySelector("button").setAttribute("data-string", old); 234 }); 235 } 236 237 /* 238 | HANDLE COMMENT RATING 239 */ 240 if(list){ 241 list.addEventListener("click", function(event){ 242 if(event.target.tagName != "A" || !SNICKER_AJAX){ 243 return true; 244 } 245 if(event.target.classList.contains("disabled")){ 246 return true; 247 } 248 249 // Check Link 250 var href = event.target.getAttribute("href"); 251 if(href.indexOf("&type=like") < 0 && href.indexOf("&type=dislike") < 0){ 252 return true; 253 } 254 255 // Event Handler 256 event.preventDefault(); 257 event.target.classList.add("disabled"); 258 var comment = (function getComment(element){ 259 var parent = element.parentElement; 260 return (parent.classList.contains("comment"))? parent: getComment(parent); 261 })(event.target), self = event.target; 262 href = href.split("?"); 263 264 // AJAX REQUEST 265 ajax(SNICKER_PATH, "POST", href[1], function(json){ 266 var data = JSON.parse(json); 267 268 if(data.status === "success" && "rating" in data){ 269 var like = comment.querySelector("[data-snicker='like']"); 270 if(like){ 271 like.innerText = String(data.rating[0]); 272 } 273 like.parentElement.classList[(like.parentElement == self? "add": "remove")]("active"); 274 275 var dislike = comment.querySelector("[data-snicker='dislike']"); 276 if(dislike){ 277 dislike.innerText = String(data.rating[1]); 278 } 279 dislike.parentElement.classList[(dislike.parentElement == self? "add": "remove")]("active"); 280 } 281 282 // Re-Enable Button 283 self.classList.remove("disabled"); 284 }); 285 }); 286 } 287 }); 288})(window);
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.