github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/public/member.js (about) 1 // TODO: Push ImageFileExts to the client from the server in some sort of gen.js? 2 var imageExts = ["png","jpg","jpe","jpeg","jif","jfi","jfif","svg","bmp","gif","tiff","tif","webp"]; 3 4 (() => { 5 function copyToClipboard(str) { 6 const el=document.createElement('textarea'); 7 el.value=str; 8 el.setAttribute('readonly',''); 9 el.style.position='absolute'; 10 el.style.left='-9999px'; 11 document.body.appendChild(el); 12 el.select(); 13 document.execCommand('copy'); 14 document.body.removeChild(el); 15 } 16 17 function uploadFileHandler(fileList, maxFiles=5, step1 = () => {}, step2 = () => {}) { 18 let files = []; 19 for(var i=0; i<fileList.length && i<5; i++) files[i] = fileList[i]; 20 21 let totalSize = 0; 22 for(let i=0; i<files.length; i++) { 23 log("file "+i,files[i]); 24 totalSize += files[i]["size"]; 25 } 26 if(totalSize > me.Site.MaxReqSize) throw("You can't upload this much at once, max: "+me.Site.MaxReqSize); 27 28 for(let i=0; i<files.length; i++) { 29 let fname = files[i]["name"]; 30 let f = e => { 31 step1(e,fname) 32 33 let reader = new FileReader(); 34 reader.onload = e2 => { 35 crypto.subtle.digest('SHA-256',e2.target.result) 36 .then(hash => { 37 const hashArray = Array.from(new Uint8Array(hash)) 38 return hashArray.map(b => ('00' + b.toString(16)).slice(-2)).join('') 39 }).then(hash => step2(e,hash,fname)); 40 } 41 reader.readAsArrayBuffer(files[i]); 42 }; 43 44 let ext = getExt(fname); 45 // TODO: Push ImageFileExts to the client from the server in some sort of gen.js? 46 let isImage = imageExts.includes(ext); 47 if(isImage) { 48 let reader = new FileReader(); 49 reader.onload = f; 50 reader.readAsDataURL(files[i]); 51 } else f(null); 52 } 53 } 54 55 // Attachment Manager 56 function uploadAttachHandler2() { 57 let post = this.closest(".post_item"); 58 let fileDock = this.closest(".attach_edit_bay"); 59 try { 60 uploadFileHandler(this.files, 5, () => {}, 61 (e,hash,fname) => { 62 log("hash",hash); 63 let formData = new FormData(); 64 formData.append("s",me.User.S); 65 for(let i=0; i<this.files.length; i++) formData.append("upload_files",this.files[i]); 66 bindAttachManager(); 67 68 let req = new XMLHttpRequest(); 69 req.addEventListener("load", () => { 70 let data = JSON.parse(req.responseText); 71 //log("rdata",data); 72 let fileItem = document.createElement("div"); 73 let ext = getExt(fname); 74 // TODO: Push ImageFileExts to the client from the server in some sort of gen.js? 75 let isImage = imageExts.includes(ext); 76 let c = ""; 77 if(isImage) c = " attach_image_holder" 78 fileItem.className = "attach_item attach_item_item"+c; 79 fileItem.innerHTML = Tmpl_topic_c_attach_item({ 80 ID: data.elems[hash+"."+ext], 81 ImgSrc: isImage ? e.target.result : "", 82 Path: hash+"."+ext, 83 FullPath: "//" + window.location.host + "/attachs/" + hash + "." + ext, 84 }); 85 fileDock.insertBefore(fileItem,fileDock.querySelector(".attach_item_buttons")); 86 87 post.classList.add("has_attachs"); 88 bindAttachItems(); 89 }); 90 req.open("POST","//"+window.location.host+"/"+fileDock.getAttribute("type")+"/attach/add/submit/"+fileDock.getAttribute("id")); 91 req.send(formData); 92 }); 93 } catch(e) { 94 // TODO: Use a notice instead 95 log("e",e); 96 alert(e); 97 } 98 } 99 100 // Quick Topic / Quick Reply 101 function uploadAttachHandler() { 102 try { 103 uploadFileHandler(this.files,5,(e,fname) => { 104 // TODO: Use client templates here 105 let fileDock = document.getElementById("upload_file_dock"); 106 let fileItem = document.createElement("label"); 107 log("fileItem",fileItem); 108 109 let ext = getExt(fname); 110 // TODO: Push ImageFileExts to the client from the server in some sort of gen.js? 111 let isImage = imageExts.includes(ext); 112 fileItem.innerText = "."+ext; 113 fileItem.className = "formbutton uploadItem"; 114 // TODO: Check if this is actually an image 115 if(isImage) fileItem.style.backgroundImage = "url("+e.target.result+")"; 116 117 fileDock.appendChild(fileItem); 118 },(e,hash,fname) => { 119 log("hash",hash); 120 let ext = getExt(fname) 121 let con = document.getElementById("input_content") 122 log("con.value",con.value); 123 124 let attachItem; 125 if(con.value=="") attachItem = "//"+window.location.host+"/attachs/"+hash+"."+ext; 126 else attachItem = "\r\n//"+window.location.host+"/attachs/"+hash+"."+ext; 127 con.value = con.value + attachItem; 128 log("con.value",con.value); 129 130 // For custom / third party text editors 131 attachItemCallback(attachItem); 132 }); 133 } catch(e) { 134 // TODO: Use a notice instead 135 log("e",e); 136 alert(e); 137 } 138 } 139 140 function bindAttachManager() { 141 let uploadFiles = document.getElementsByClassName("upload_files_post"); 142 if(uploadFiles==null) return; 143 for(let i=0; i<uploadFiles.length; i++) { 144 let uploader = uploadFiles[i]; 145 uploader.value = ""; 146 uploader.removeEventListener("change", uploadAttachHandler2, false); 147 uploader.addEventListener("change", uploadAttachHandler2, false); 148 } 149 } 150 151 //addInitHook("before_init_bind_page", () => { 152 //log("in member.js before_init_bind_page") 153 addInitHook("end_bind_topic", () => { 154 log("in member.js end_bind_topic") 155 156 let changeListener = (files,h) => { 157 if(files!=null) { 158 files.removeEventListener("change", h, false); 159 files.addEventListener("change", h, false); 160 } 161 }; 162 let uploadFiles = document.getElementById("upload_files"); 163 changeListener(uploadFiles,uploadAttachHandler); 164 let uploadFilesOp = document.getElementById("upload_files_op"); 165 changeListener(uploadFilesOp,uploadAttachHandler2); 166 bindAttachManager(); 167 168 function bindAttachItems() { 169 $(".attach_item_select").unbind("click"); 170 $(".attach_item_copy").unbind("click"); 171 $(".attach_item_select").click(function(){ 172 let hold = $(this).closest(".attach_item"); 173 if(hold.hasClass("attach_item_selected")) hold.removeClass("attach_item_selected"); 174 else hold.addClass("attach_item_selected"); 175 }); 176 $(".attach_item_copy").click(function(){ 177 let hold = $(this).closest(".attach_item"); 178 let pathNode = hold.find(".attach_item_path"); 179 copyToClipboard(pathNode.attr("fullPath")); 180 }); 181 } 182 bindAttachItems(); 183 184 $(".attach_item_delete").unbind("click"); 185 $(".attach_item_delete").click(function(){ 186 let formData = new URLSearchParams(); 187 formData.append("s",me.User.S); 188 189 let post = this.closest(".post_item"); 190 let aidList = ""; 191 let elems = post.getElementsByClassName("attach_item_selected"); 192 if(elems==null) return; 193 194 for(let i = 0; i < elems.length; i++) { 195 let pathNode = elems[i].querySelector(".attach_item_path"); 196 log("pathNode",pathNode); 197 aidList += pathNode.getAttribute("aid")+","; 198 elems[i].remove(); 199 } 200 if(aidList.length > 0) aidList = aidList.slice(0, -1); 201 log("aidList",aidList) 202 formData.append("aids",aidList); 203 204 let ec = 0; 205 let e = post.getElementsByClassName("attach_item_item"); 206 if(e!=null) ec = e.length; 207 if(ec==0) post.classList.remove("has_attachs"); 208 209 let req = new XMLHttpRequest(); 210 let fileDock = this.closest(".attach_edit_bay"); 211 req.open("POST","//"+window.location.host+"/"+fileDock.getAttribute("type")+"/attach/remove/submit/"+fileDock.getAttribute("id"),true); 212 req.send(formData); 213 214 bindAttachItems(); 215 bindAttachManager(); 216 }); 217 218 function addPollInput() { 219 log("clicked on pollinputinput"); 220 let dataPollInput = $(this).parent().attr("data-pollinput"); 221 log("dataPollInput",dataPollInput); 222 if(dataPollInput==undefined) return; 223 if(dataPollInput!=(pollInputIndex-1)) return; 224 $(".poll_content_row .formitem").append(Tmpl_topic_c_poll_input({ 225 Index: pollInputIndex, 226 Place: phraseBox["topic"]["topic.reply_add_poll_option"].replace("%d",pollInputIndex), 227 })); 228 pollInputIndex++; 229 log("new pollInputIndex",pollInputIndex); 230 $(".pollinputinput").off("click"); 231 $(".pollinputinput").click(addPollInput); 232 } 233 234 let pollInputIndex = 1; 235 $("#add_poll_button").unbind("click"); 236 $("#add_poll_button").click(ev => { 237 ev.preventDefault(); 238 $(".poll_content_row").removeClass("auto_hide"); 239 $("#has_poll_input").val("1"); 240 $(".pollinputinput").click(addPollInput); 241 }); 242 }); 243 //}); 244 function modCancel() { 245 log("enter modCancel"); 246 if(!$(".mod_floater").hasClass("auto_hide")) $(".mod_floater").addClass("auto_hide") 247 $(".moderate_link").unbind("click"); 248 $(".moderate_link").removeClass("moderate_open"); 249 $(".pre_opt").addClass("auto_hide"); 250 $(".mod_floater_submit").unbind("click"); 251 $("#topicsItemList,#forumItemList").removeClass("topics_moderate"); 252 $(".topic_selected").removeClass("topic_selected"); 253 // ! Be careful not to trample bindings elsewhere 254 $(".topic_row").unbind("click"); 255 $("#mod_topic_mover").addClass("auto_hide"); 256 } 257 function modCancelBind() { 258 log("enter modCancelBind") 259 $(".moderate_link").unbind("click"); 260 $(".moderate_open").click(ev => { 261 modCancel(); 262 $(".moderate_open").unbind("click"); 263 modLinkBind(); 264 }); 265 } 266 function modLinkBind() { 267 log("enter modLinkBind"); 268 $(".moderate_link").click(ev => { 269 log("enter .moderate_link"); 270 ev.preventDefault(); 271 $(".pre_opt").removeClass("auto_hide"); 272 $(".moderate_link").addClass("moderate_open"); 273 selectedTopics=[]; 274 modCancelBind(); 275 $("#topicsItemList,#forumItemList").addClass("topics_moderate"); 276 $(".topic_row").each(function(){ 277 $(this).click(function(){ 278 if(!this.classList.contains("can_mod")) return; 279 let tid = parseInt($(this).attr("data-tid"),10); 280 let sel = this.classList.contains("topic_selected"); 281 if(sel) { 282 for(var i=0; i<selectedTopics.length; i++){ 283 if(selectedTopics[i]===tid) selectedTopics.splice(i, 1); 284 } 285 } else selectedTopics.push(tid); 286 if(selectedTopics.length==1) { 287 var msg = phraseBox["topic_list"]["topic_list.what_to_do_single"]; 288 } else { 289 var msg = "What do you want to do with these "+selectedTopics.length+" topics?"; 290 } 291 $(".mod_floater_head span").html(msg); 292 if(!sel) { 293 $(this).addClass("topic_selected"); 294 $(".mod_floater").removeClass("auto_hide"); 295 } else { 296 $(this).removeClass("topic_selected"); 297 } 298 if(selectedTopics.length==0 && !$(".mod_floater").hasClass("auto_hide")) $(".mod_floater").addClass("auto_hide"); 299 }); 300 }); 301 302 let bulkActionSender = (action,selectedTopics,fragBit) => { 303 $.ajax({ 304 url: "/topic/"+action+"/submit/"+fragBit+"?s="+me.User.S, 305 type: "POST", 306 data: JSON.stringify(selectedTopics), 307 contentType: "application/json", 308 error: ajaxError, 309 success: () => window.location.reload() 310 }); 311 }; 312 // TODO: Should we unbind this here to avoid binding multiple listeners to this accidentally? 313 $(".mod_floater_submit").click(function(ev){ 314 ev.preventDefault(); 315 let selectNode = this.form.querySelector(".mod_floater_options"); 316 let optionNode = selectNode.options[selectNode.selectedIndex]; 317 let action = optionNode.getAttribute("value"); 318 319 // Handle these specially 320 switch(action) { 321 case "move": 322 log("move action"); 323 let modTopicMover = $("#mod_topic_mover"); 324 $("#mod_topic_mover").removeClass("auto_hide"); 325 $("#mod_topic_mover .pane_row").unbind("click"); 326 $("#mod_topic_mover .pane_row").click(function(){ 327 modTopicMover.find(".pane_row").removeClass("pane_selected"); 328 let fid = this.getAttribute("data-fid"); 329 if(fid==null) return; 330 this.classList.add("pane_selected"); 331 log("fid",fid); 332 forumToMoveTo = fid; 333 334 $("#mover_submit").unbind("click"); 335 $("#mover_submit").click(ev => { 336 ev.preventDefault(); 337 bulkActionSender("move",selectedTopics,forumToMoveTo); 338 }); 339 }); 340 return; 341 } 342 343 bulkActionSender(action,selectedTopics,""); 344 }); 345 }); 346 } 347 //addInitHook("after_init_bind_page", () => { 348 //addInitHook("before_init_bind_page", () => { 349 //log("in member.js before_init_bind_page 2") 350 addInitHook("end_bind_page", () => { 351 log("in member.js end_bind_page") 352 modCancel(); 353 modLinkBind(); 354 }); 355 addInitHook("after_init_bind_page", () => addHook("end_unbind_page", () => modCancel())) 356 //}); 357 })()