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  })()