986 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			986 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
 | |
| /*
 | |
| 	Global functions
 | |
| */
 | |
| function htmlspecialchars(str){
 | |
| 
 | |
| 	var map = {
 | |
| 		'&': '&',
 | |
| 		'<': '<',
 | |
| 		'>': '>',
 | |
| 		'"': '"',
 | |
| 		"'": '''
 | |
| 	}
 | |
| 
 | |
| 	return str.replace(/[&<>"']/g, function(m){return map[m];});
 | |
| }
 | |
| 
 | |
| function htmlspecialchars_decode(str){
 | |
| 	
 | |
| 	var map = {
 | |
| 		'&': '&',
 | |
| 		'<': '<',
 | |
| 		'>': '>',
 | |
| 		'"': '"',
 | |
| 		''': "'"
 | |
| 	}
 | |
| 	
 | |
| 	return str.replace(/&|<|>|"|'/g, function(m){return map[m];});
 | |
| }
 | |
| 
 | |
| function is_click_within(elem, classname, is_id = false){
 | |
| 	
 | |
| 	while(true){
 | |
| 		
 | |
| 		if(elem === null){
 | |
| 			
 | |
| 			return false;
 | |
| 		}
 | |
| 		
 | |
| 		if(
 | |
| 			(
 | |
| 				is_id === false &&
 | |
| 				elem.className == classname
 | |
| 			) ||
 | |
| 			(
 | |
| 				is_id === true &&
 | |
| 				elem.id == classname
 | |
| 			)
 | |
| 		){
 | |
| 			
 | |
| 			return elem;
 | |
| 		}
 | |
| 		
 | |
| 		elem = elem.parentElement;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	Prevent GET parameter pollution
 | |
| */
 | |
| var form = document.getElementsByTagName("form");
 | |
| 
 | |
| if(
 | |
| 	form.length !== 0 &&
 | |
| 	window.location.pathname != "/" &&
 | |
| 	window.location.pathname != "/settings.php" &&
 | |
| 	window.location.pathname != "/settings"
 | |
| ){
 | |
| 	form = form[0];
 | |
| 	
 | |
| 	var scraper_dropdown = document.getElementsByName("scraper")[0];
 | |
| 	
 | |
| 	scraper_dropdown.addEventListener("change", function(choice){
 | |
| 		
 | |
| 		submit(form);
 | |
| 	});
 | |
| 	
 | |
| 	form.addEventListener("submit", function(e){
 | |
| 		
 | |
| 		e.preventDefault();
 | |
| 		submit(e.srcElement);
 | |
| 	});
 | |
| }
 | |
| 
 | |
| function submit(e){
 | |
| 	
 | |
| 	var GET = "";
 | |
| 	var first = true;
 | |
| 	
 | |
| 	if((s = document.getElementsByName("s")).length !== 0){
 | |
| 		
 | |
| 		GET += "?s=" + encodeURIComponent(s[0].value).replaceAll("%20", "+");
 | |
| 		first = false;
 | |
| 	}
 | |
| 	
 | |
| 	Array.from(
 | |
| 		e.getElementsByTagName("select")
 | |
| 	).concat(
 | |
| 		Array.from(
 | |
| 			e.getElementsByTagName("input")
 | |
| 		)
 | |
| 	).forEach(function(el){
 | |
| 		
 | |
| 		var firstelem = el.getElementsByTagName("option");
 | |
| 		
 | |
| 		if(
 | |
| 			(
 | |
| 				(
 | |
| 					firstelem.length === 0 ||
 | |
| 					firstelem[0].value != el.value
 | |
| 				) &&
 | |
| 				el.name != "" &&
 | |
| 				el.value != "" &&
 | |
| 				el.name != "s"
 | |
| 			) ||
 | |
| 			el.name == "scraper" ||
 | |
| 			el.name == "nsfw"
 | |
| 		){
 | |
| 			
 | |
| 			if(first){
 | |
| 				
 | |
| 				GET += "?";
 | |
| 				first = false;
 | |
| 			}else{
 | |
| 				
 | |
| 				GET += "&";
 | |
| 			}
 | |
| 			
 | |
| 			GET += encodeURIComponent(el.name).replaceAll("%20", "+") + "=" + encodeURIComponent(el.value).replaceAll("%20", "+");
 | |
| 		}
 | |
| 	});
 | |
| 	
 | |
| 	window.location.href = GET;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*
 | |
| 	Hide show more button when it's not needed on answers
 | |
| */
 | |
| var answer_div = document.getElementsByClassName("answer");
 | |
| 
 | |
| if(answer_div.length !== 0){
 | |
| 	answer_div = Array.from(answer_div);
 | |
| 	var spoiler_button_div = Array.from(document.getElementsByClassName("spoiler-button"));
 | |
| 
 | |
| 	// execute on pageload
 | |
| 	hide_show_more();
 | |
| 
 | |
| 	window.addEventListener("resize", hide_show_more);
 | |
| 
 | |
| 	function hide_show_more(){
 | |
| 		
 | |
| 		var height = window.innerWidth >= 1000 ? 600 : 200;
 | |
| 		
 | |
| 		for(i=0; i<answer_div.length; i++){
 | |
| 			
 | |
| 			if(answer_div[i].scrollHeight < height){
 | |
| 				
 | |
| 				spoiler_button_div[i].style.display = "none";
 | |
| 				
 | |
| 				document.getElementById(spoiler_button_div[i].htmlFor).checked = true;
 | |
| 			}else{
 | |
| 				
 | |
| 				spoiler_button_div[i].style.display = "block";
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| switch(document.location.pathname){
 | |
| 	
 | |
| 	case "/web":
 | |
| 	case "/web.php":
 | |
| 		var image_class = "image";
 | |
| 		break;
 | |
| 	
 | |
| 	case "/images":
 | |
| 	case "/images.php":
 | |
| 		var image_class = "thumb";
 | |
| 		break;
 | |
| 	
 | |
| 	default:
 | |
| 		var image_class = null;
 | |
| }
 | |
| 
 | |
| if(image_class !== null){
 | |
| 	
 | |
| 	/*
 | |
| 		Add popup to document
 | |
| 	*/
 | |
| 	var popup_bg = document.createElement("div");
 | |
| 	popup_bg.id = "popup-bg";
 | |
| 	document.body.appendChild(popup_bg);
 | |
| 	
 | |
| 	// enable/disable pointer events
 | |
| 	if(!document.cookie.includes("bg_noclick=yes")){
 | |
| 		
 | |
| 		popup_bg.style.pointerEvents = "none";
 | |
| 	}
 | |
| 	
 | |
| 	var popup_status = document.createElement("div");
 | |
| 	popup_status.id = "popup-status";
 | |
| 	document.body.appendChild(popup_status);
 | |
| 	
 | |
| 	var popup_body = document.createElement("div");
 | |
| 	popup_body.id = "popup";
 | |
| 	document.body.appendChild(popup_body);
 | |
| 	
 | |
| 	// import popup
 | |
| 	var popup_body = document.getElementById("popup");
 | |
| 	var popup_status = document.getElementById("popup-status");
 | |
| 	var popup_image = null; // is set later on popup click
 | |
| 	
 | |
| 	// image metadata
 | |
| 	var collection = []; // will contain width, height, image URL
 | |
| 	var collection_index = 0;
 | |
| 	
 | |
| 	// event handling helper variables
 | |
| 	var is_popup_shown = false;
 | |
| 	var mouse_down = false;
 | |
| 	var mouse_move = false;
 | |
| 	var move_x = 0;
 | |
| 	var move_y = 0;
 | |
| 	var target_is_popup = false;
 | |
| 	var mirror_x = false;
 | |
| 	var mirror_y = false;
 | |
| 	var rotation = 0;
 | |
| 	
 | |
| 	/*
 | |
| 		Image dragging (mousedown)
 | |
| 	*/
 | |
| 	document.addEventListener("mousedown", function(div){
 | |
| 		
 | |
| 		if(div.buttons !== 1){
 | |
| 			
 | |
| 			return;
 | |
| 		}
 | |
| 		
 | |
| 		mouse_down = true;
 | |
| 		mouse_move = false;
 | |
| 		
 | |
| 		if(is_click_within(div.target, "popup", true) === false){
 | |
| 			
 | |
| 			target_is_popup = false;
 | |
| 		}else{
 | |
| 
 | |
| 			target_is_popup = true;
 | |
| 			
 | |
| 			var pos = popup_body.getBoundingClientRect();
 | |
| 			move_x = div.x - pos.x;
 | |
| 			move_y = div.y - pos.y;
 | |
| 		}
 | |
| 	});
 | |
| 	
 | |
| 	/*
 | |
| 		Image dragging (mousemove)
 | |
| 	*/
 | |
| 	document.addEventListener("mousemove", function(pos){
 | |
| 		
 | |
| 		if(
 | |
| 			target_is_popup &&
 | |
| 			mouse_down
 | |
| 		){
 | |
| 			
 | |
| 			mouse_move = true;
 | |
| 			movepopup(popup_body, pos.clientX - move_x, pos.clientY - move_y);
 | |
| 		}
 | |
| 	});
 | |
| 	
 | |
| 	/*
 | |
| 		Image dragging (mouseup)
 | |
| 	*/
 | |
| 	document.addEventListener("mouseup", function(){
 | |
| 		
 | |
| 		mouse_down = false;
 | |
| 	});
 | |
| 	
 | |
| 	/*
 | |
| 		Image popup open
 | |
| 	*/
 | |
| 	document.addEventListener("click", function(click){
 | |
| 		
 | |
| 		// should our click trigger image open?
 | |
| 		if(
 | |
| 			elem = is_click_within(click.target, image_class) ||
 | |
| 			click.target.classList.contains("openimg")
 | |
| 		){
 | |
| 			
 | |
| 			event.preventDefault();
 | |
| 			is_popup_shown = true;
 | |
| 			
 | |
| 			// reset position params
 | |
| 			mirror_x = false;
 | |
| 			mirror_y = false;
 | |
| 			rotation = 0;
 | |
| 			scale = 60;
 | |
| 			collection_index = 0;
 | |
| 			
 | |
| 			// get popup data
 | |
| 			if(elem === true){
 | |
| 				// we clicked a simple image preview
 | |
| 				elem = click.target;
 | |
| 				var image_url = elem.getAttribute("src");
 | |
| 				
 | |
| 				if(image_url.startsWith("/proxy")){
 | |
| 					
 | |
| 					var match = image_url.match(/i=([^&]+)/);
 | |
| 					
 | |
| 					if(match !== null){
 | |
| 						
 | |
| 						image_url = decodeURIComponent(match[1]);
 | |
| 					}
 | |
| 				}else{
 | |
| 					
 | |
| 					image_url = htmlspecialchars_decode(image_url);
 | |
| 				}
 | |
| 				
 | |
| 				var w = Math.round(click.target.naturalWidth);
 | |
| 				var h = Math.round(click.target.naturalHeight);
 | |
| 				
 | |
| 				if(
 | |
| 					w === 0 ||
 | |
| 					h === 0
 | |
| 				){
 | |
| 					
 | |
| 					w = 100;
 | |
| 					h = 100;
 | |
| 				}
 | |
| 				
 | |
| 				collection = [
 | |
| 					{
 | |
| 						"url": image_url,
 | |
| 						"width": w,
 | |
| 						"height": h
 | |
| 					}
 | |
| 				];
 | |
| 				
 | |
| 				var title = "No description provided";
 | |
| 				
 | |
| 				if(click.target.title != ""){
 | |
| 					
 | |
| 					title = click.target.title;
 | |
| 				}else{
 | |
| 					
 | |
| 					if(click.target.alt != ""){
 | |
| 						
 | |
| 						title = click.target.alt;
 | |
| 					}
 | |
| 				}
 | |
| 			}else{
 | |
| 				
 | |
| 				if(image_class == "thumb"){
 | |
| 					// we're inside image.php
 | |
| 					
 | |
| 					elem =
 | |
| 						elem
 | |
| 						.parentElement
 | |
| 						.parentElement;
 | |
| 					
 | |
| 					var image_url = elem.getElementsByTagName("a")[1].href;
 | |
| 				}else{
 | |
| 					
 | |
| 					// we're inside web.php
 | |
| 					var image_url = elem.href;
 | |
| 				}
 | |
| 				
 | |
| 				collection =
 | |
| 					JSON.parse(
 | |
| 						elem.getAttribute("data-json")
 | |
| 					);
 | |
| 				
 | |
| 				var imagesize = elem.getElementsByTagName("img")[0];
 | |
| 				
 | |
| 				var imagesize_w = 0;
 | |
| 				var imagesize_h = 0;
 | |
| 				
 | |
| 				if(imagesize.complete){
 | |
| 					
 | |
| 					imagesize_w = imagesize.naturalWidth;
 | |
| 					imagesize_h = imagesize.naturalHeight;
 | |
| 				}
 | |
| 				
 | |
| 				if(
 | |
| 					imagesize_w === 0 ||
 | |
| 					imagesize_h === 0
 | |
| 				){
 | |
| 					
 | |
| 					imagesize_w = 100;
 | |
| 					imagesize_h = 100;
 | |
| 				}
 | |
| 				
 | |
| 				for(var i=0; i<collection.length; i++){
 | |
| 					
 | |
| 					if(collection[i].width === null){
 | |
| 						
 | |
| 						collection[i].width = imagesize_w;
 | |
| 						collection[i].height = imagesize_h;
 | |
| 					}
 | |
| 				}
 | |
| 				
 | |
| 				var title = elem.title;
 | |
| 			}
 | |
| 			
 | |
| 			// prepare HTML
 | |
| 			var html =
 | |
| 				'<div id="popup-num">(' + collection.length + ')</div>' + 
 | |
| 				'<div id="popup-dropdown">' +
 | |
| 					'<select name="viewer-res" onchange="changeimage(event)">';
 | |
| 			
 | |
| 			for(i=0; i<collection.length; i++){
 | |
| 				
 | |
| 				if(collection[i].url.startsWith("data:")){
 | |
| 					
 | |
| 					var domain = "<Base64 Data>";
 | |
| 				}else{
 | |
| 					
 | |
| 					var domain = new URL(collection[i].url).hostname;
 | |
| 				}
 | |
| 				
 | |
| 				html += '<option value="' + i + '">' + '(' + collection[i].width + 'x' + collection[i].height + ') ' + domain + '</option>';
 | |
| 			}
 | |
| 			
 | |
| 			popup_status.innerHTML =
 | |
| 				html + '</select></div>' +
 | |
| 				'<a href="' + htmlspecialchars(image_url) + '" rel="noreferrer nofollow "id="popup-title">' + htmlspecialchars(title) + '</a>';
 | |
| 			
 | |
| 			popup_body.innerHTML =
 | |
| 				'<img src="' + getproxylink(collection[0].url) + '" draggable="false" id="popup-image">';
 | |
| 			
 | |
| 			// make changes to DOM
 | |
| 			popup_body.style.display = "block";
 | |
| 			popup_bg.style.display = "block";
 | |
| 			popup_status.style.display = "table";
 | |
| 			
 | |
| 			// store for rotation functions & changeimage()
 | |
| 			popup_image = document.getElementById("popup-image");
 | |
| 			
 | |
| 			scalepopup(collection[collection_index], scale);
 | |
| 			centerpopup();
 | |
| 		}else{
 | |
| 			
 | |
| 			// click inside the image viewer
 | |
| 			// resize image
 | |
| 			if(is_click_within(click.target, "popup", true)){
 | |
| 				
 | |
| 				if(mouse_move === false){
 | |
| 					scale = 80;
 | |
| 					scalepopup(collection[collection_index], scale);
 | |
| 					centerpopup();
 | |
| 				}
 | |
| 			}else{
 | |
| 				
 | |
| 				if(is_click_within(click.target, "popup-status", true) === false){
 | |
| 					
 | |
| 					// click outside the popup while its open
 | |
| 					// close it
 | |
| 					if(is_popup_shown){
 | |
| 						
 | |
| 						hidepopup();
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	});
 | |
| 	
 | |
| 	/*
 | |
| 		Scale image viewer
 | |
| 	*/
 | |
| 	popup_body.addEventListener("wheel", function(scroll){
 | |
| 		
 | |
| 		event.preventDefault();
 | |
| 		
 | |
| 		if(
 | |
| 			scroll.altKey ||
 | |
| 			scroll.ctrlKey ||
 | |
| 			scroll.shiftKey
 | |
| 		){
 | |
| 			
 | |
| 			var increment = 7;
 | |
| 		}else{
 | |
| 			
 | |
| 			var increment = 14;
 | |
| 		}
 | |
| 		
 | |
| 		if(scroll.wheelDelta > 0){
 | |
| 			
 | |
| 			// scrolling up
 | |
| 			scale = scale + increment;
 | |
| 		}else{
 | |
| 			
 | |
| 			// scrolling down
 | |
| 			if(scale - increment > 7){
 | |
| 				scale = scale - increment;
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		// calculate relative size before scroll
 | |
| 		var pos = popup_body.getBoundingClientRect();
 | |
| 		var x = (scroll.x - pos.x) / pos.width;
 | |
| 		var y = (scroll.y - pos.y) / pos.height;
 | |
| 		
 | |
| 		scalepopup(collection[collection_index], scale);
 | |
| 		
 | |
| 		// move popup to % we found
 | |
| 		pos = popup_body.getBoundingClientRect();
 | |
| 		
 | |
| 		movepopup(
 | |
| 			popup_body,
 | |
| 			scroll.clientX - (x * pos.width),
 | |
| 			scroll.clientY - (y * pos.height)
 | |
| 		);
 | |
| 	});
 | |
| 	
 | |
| 	/*
 | |
| 		Keyboard controls
 | |
| 	*/
 | |
| 	
 | |
| 	document.addEventListener("keydown", function(key){
 | |
| 		
 | |
| 		// close popup
 | |
| 		if(
 | |
| 			is_popup_shown &&
 | |
| 			key.keyCode === 27
 | |
| 		){
 | |
| 			
 | |
| 			hidepopup();
 | |
| 			return;
 | |
| 		}
 | |
| 		
 | |
| 		if(is_popup_shown === false){
 | |
| 			
 | |
| 			return;
 | |
| 		}
 | |
| 		
 | |
| 		if(
 | |
| 			key.altKey ||
 | |
| 			key.ctrlKey ||
 | |
| 			key.shiftKey
 | |
| 		){
 | |
| 			
 | |
| 			// mirror image
 | |
| 			switch(key.keyCode){
 | |
| 				
 | |
| 				case 37:
 | |
| 					// left
 | |
| 					key.preventDefault();
 | |
| 					mirror_x = true;
 | |
| 					break;
 | |
| 				
 | |
| 				case 38:
 | |
| 					// up
 | |
| 					key.preventDefault();
 | |
| 					mirror_y = false;
 | |
| 					break;
 | |
| 				
 | |
| 				case 39:
 | |
| 					// right
 | |
| 					key.preventDefault();
 | |
| 					mirror_x = false;
 | |
| 					break;
 | |
| 				
 | |
| 				case 40:
 | |
| 					// down
 | |
| 					key.preventDefault();
 | |
| 					mirror_y = true;
 | |
| 					break;
 | |
| 			}
 | |
| 		}else{
 | |
| 			
 | |
| 			// rotate image
 | |
| 			switch(key.keyCode){
 | |
| 				
 | |
| 				case 37:
 | |
| 					// left
 | |
| 					key.preventDefault();
 | |
| 					rotation = -90;
 | |
| 					break;
 | |
| 				
 | |
| 				case 38:
 | |
| 					// up
 | |
| 					key.preventDefault();
 | |
| 					rotation = 0;
 | |
| 					break;
 | |
| 				
 | |
| 				case 39:
 | |
| 					// right
 | |
| 					key.preventDefault();
 | |
| 					rotation = 90;
 | |
| 					break;
 | |
| 				
 | |
| 				case 40:
 | |
| 					// down
 | |
| 					key.preventDefault();
 | |
| 					rotation = -180;
 | |
| 					break;
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		popup_image.style.transform =
 | |
| 			"scale(" +
 | |
| 				(mirror_x ? "-1" : "1") +
 | |
| 				", " +
 | |
| 				(mirror_y ? "-1" : "1") +
 | |
| 			") " +
 | |
| 			"rotate(" +
 | |
| 				rotation + "deg" +
 | |
| 			")";
 | |
| 	});
 | |
| }
 | |
| 
 | |
| function getproxylink(url){
 | |
| 	
 | |
| 	if(url.startsWith("data:")){
 | |
| 		
 | |
| 		return htmlspecialchars(url);
 | |
| 	}else{
 | |
| 		
 | |
| 		return '/proxy?i=' + encodeURIComponent(url);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| function hidepopup(){
 | |
| 	
 | |
| 	is_popup_shown = false;
 | |
| 	popup_status.style.display = "none";
 | |
| 	popup_body.style.display = "none";
 | |
| 	popup_bg.style.display = "none";
 | |
| }
 | |
| 
 | |
| function scalepopup(size, scale){
 | |
| 	
 | |
| 	var ratio =
 | |
| 		Math.min(
 | |
| 			(window.innerWidth * (scale / 100)) / collection[collection_index].width, (window.innerHeight * (scale / 100)) / collection[collection_index].height
 | |
| 		);
 | |
| 	
 | |
| 	popup_body.style.width = size.width * ratio + "px";
 | |
| 	popup_body.style.height = size.height * ratio + "px";
 | |
| }
 | |
| 
 | |
| function centerpopup(){
 | |
| 	
 | |
| 	var size = popup_body.getBoundingClientRect();
 | |
| 	var size = {
 | |
| 		"width": parseInt(size.width),
 | |
| 		"height": parseInt(size.height)
 | |
| 	};
 | |
| 	
 | |
| 	movepopup(
 | |
| 		popup_body,
 | |
| 		(window.innerWidth / 2) - (size.width / 2),
 | |
| 		(window.innerHeight / 2) - (size.height / 2)
 | |
| 	);
 | |
| }
 | |
| 
 | |
| function movepopup(popup_body, x, y){
 | |
| 	
 | |
| 	popup_body.style.left = x + "px";
 | |
| 	popup_body.style.top = y + "px";
 | |
| }
 | |
| 
 | |
| function changeimage(event){
 | |
| 	
 | |
| 	// reset rotation params
 | |
| 	mirror_x = false;
 | |
| 	mirror_y = false;
 | |
| 	rotation = 0;
 | |
| 	
 | |
| 	scale = 60;
 | |
| 	
 | |
| 	collection_index = parseInt(event.target.value);
 | |
| 	
 | |
| 	// we set innerHTML otherwise old image lingers a little
 | |
| 	popup_body.innerHTML =
 | |
| 		'<img src="' + getproxylink(collection[collection_index].url) + '" draggable="false" id="popup-image">';
 | |
| 	
 | |
| 	// store for rotation functions & changeimage()
 | |
| 	popup_image = document.getElementById("popup-image");
 | |
| 	
 | |
| 	scalepopup(collection[collection_index], scale);
 | |
| 	centerpopup();
 | |
| }
 | |
| 
 | |
| var searchbox_wrapper = document.getElementsByClassName("searchbox");
 | |
| 
 | |
| if(searchbox_wrapper.length !== 0){
 | |
| 		
 | |
| 	searchbox_wrapper = searchbox_wrapper[0];
 | |
| 	var searchbox = searchbox_wrapper.getElementsByTagName("input")[1];
 | |
| 	
 | |
| 	/*
 | |
| 		Textarea shortcuts
 | |
| 	*/
 | |
| 	document.addEventListener("keydown", function(key){
 | |
| 		
 | |
| 		switch(key.keyCode){
 | |
| 			
 | |
| 			case 191:
 | |
| 				// 191 = /
 | |
| 				if(document.activeElement.tagName == "INPUT"){
 | |
| 					
 | |
| 					// already focused, ignore
 | |
| 					break;
 | |
| 				}
 | |
| 				
 | |
| 				if(
 | |
| 					typeof is_popup_shown != "undefined" &&
 | |
| 					is_popup_shown
 | |
| 				){
 | |
| 					
 | |
| 					hidepopup();
 | |
| 				}
 | |
| 				
 | |
| 				window.scrollTo(0, 0);
 | |
| 				searchbox.focus();
 | |
| 				key.preventDefault();
 | |
| 				break;
 | |
| 		}
 | |
| 	});
 | |
| 	
 | |
| 	/*
 | |
| 		Autocompleter
 | |
| 	*/
 | |
| 	if( // make sure the user wants it
 | |
| 		document.cookie.includes("scraper_ac=") &&
 | |
| 		document.cookie.includes("scraper_ac=disabled") === false
 | |
| 	){
 | |
| 		
 | |
| 		var autocomplete_cache = [];
 | |
| 		var focuspos = -1;
 | |
| 		var list = [];
 | |
| 		var autocomplete_div = document.getElementsByClassName("autocomplete")[0];
 | |
| 		
 | |
| 		if(
 | |
| 			document.cookie.includes("scraper_ac=auto") &&
 | |
| 			typeof scraper_dropdown != "undefined"
 | |
| 		){
 | |
| 			
 | |
| 			var ac_req_appendix = "&scraper=" + scraper_dropdown.value;
 | |
| 		}else{
 | |
| 			
 | |
| 			var ac_req_appendix = "";
 | |
| 		}
 | |
| 		
 | |
| 		function getsearchboxtext(){
 | |
| 			
 | |
| 			var value =
 | |
| 				searchbox.value
 | |
| 				.trim()
 | |
| 				.replace(
 | |
| 					/ +/g,
 | |
| 					" "
 | |
| 				)
 | |
| 				.toLowerCase();
 | |
| 			
 | |
| 			return value;
 | |
| 		}
 | |
| 		
 | |
| 		searchbox.addEventListener("input", async function(){
 | |
| 			
 | |
| 			// ratelimit on input only
 | |
| 			// dont ratelimit if we already have res
 | |
| 			if(typeof autocomplete_cache[getsearchboxtext()] != "undefined"){
 | |
| 				
 | |
| 				await getac();
 | |
| 			}else{
 | |
| 				
 | |
| 				await getac_ratelimit();
 | |
| 			}
 | |
| 		});
 | |
| 		
 | |
| 		async function getac(){
 | |
| 			
 | |
| 			var curvalue = getsearchboxtext();
 | |
| 			
 | |
| 			if(curvalue == ""){
 | |
| 				
 | |
| 				// hide autocompleter
 | |
| 				autocomplete_div.style.display = "none";
 | |
| 				return;
 | |
| 			}
 | |
| 			
 | |
| 			if(typeof autocomplete_cache[curvalue] == "undefined"){
 | |
| 				
 | |
| 				/*
 | |
| 					Fetch autocomplete
 | |
| 				*/
 | |
| 				// make sure we dont fetch same thing twice
 | |
| 				autocomplete_cache[curvalue] = [];
 | |
| 				
 | |
| 				var res = await fetch("/api/v1/ac?s=" + (encodeURIComponent(curvalue).replaceAll("%20", "+")) + ac_req_appendix);
 | |
| 				if(!res.ok){
 | |
| 					
 | |
| 					return;
 | |
| 				}
 | |
| 				
 | |
| 				var json = await res.json();
 | |
| 				
 | |
| 				autocomplete_cache[curvalue] = json[1];
 | |
| 				
 | |
| 				if(curvalue == getsearchboxtext()){
 | |
| 					
 | |
| 					render_ac(curvalue, autocomplete_cache[curvalue]);
 | |
| 				}
 | |
| 				return;
 | |
| 			}
 | |
| 			
 | |
| 			render_ac(curvalue, autocomplete_cache[curvalue]);	
 | |
| 		}
 | |
| 		
 | |
| 		var ac_func = null;
 | |
| 		function getac_ratelimit(){
 | |
| 			
 | |
| 			return new Promise(async function(resolve, reject){
 | |
| 				
 | |
| 				if(ac_func !== null){
 | |
| 					
 | |
| 					clearTimeout(ac_func);
 | |
| 				}//else{
 | |
| 					
 | |
| 					// no ratelimits
 | |
| 					//getac();
 | |
| 				//}
 | |
| 				
 | |
| 				ac_func =
 | |
| 					setTimeout(function(){
 | |
| 						
 | |
| 						ac_func = null;
 | |
| 						getac(); // get results after 100ms of no keystroke
 | |
| 						resolve();
 | |
| 					}, 200);
 | |
| 			});
 | |
| 		}
 | |
| 		
 | |
| 		function render_ac(query, list){
 | |
| 			
 | |
| 			if(list.length === 0){
 | |
| 				
 | |
| 				autocomplete_div.style.display = "none";
 | |
| 				return;
 | |
| 			}
 | |
| 			
 | |
| 			html = "";
 | |
| 			
 | |
| 			// prepare regex
 | |
| 			var highlight = query.split(" ");
 | |
| 			var regex = [];
 | |
| 			
 | |
| 			for(var k=0; k<highlight.length; k++){
 | |
| 				
 | |
| 				// espace regex
 | |
| 				regex.push(
 | |
| 					highlight[k].replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
 | |
| 				);
 | |
| 			}
 | |
| 			
 | |
| 			regex = new RegExp(highlight.join("|"), "gi");
 | |
| 			
 | |
| 			for(var i=0; i<list.length; i++){
 | |
| 				
 | |
| 				html +=
 | |
| 					'<div tabindex="0" class="entry" onclick="handle_entry_click(this);">' +
 | |
| 						htmlspecialchars(
 | |
| 							list[i]
 | |
| 						).replace(
 | |
| 							regex,
 | |
| 							'<u>$&</u>'
 | |
| 						) +
 | |
| 					'</div>';
 | |
| 			}
 | |
| 			
 | |
| 			autocomplete_div.innerHTML = html;
 | |
| 			autocomplete_div.style.display = "block";
 | |
| 		}
 | |
| 		
 | |
| 		var should_focus = false;
 | |
| 		document.addEventListener("keydown", function(event){
 | |
| 			
 | |
| 			if(event.key == "Escape"){
 | |
| 				
 | |
| 				document.activeElement.blur();
 | |
| 				focuspos = -1;
 | |
| 				autocomplete_div.style.display = "none";
 | |
| 				return;
 | |
| 			}
 | |
| 			
 | |
| 			if(
 | |
| 				is_click_within(event.target, "searchbox") === false ||
 | |
| 				typeof autocomplete_cache[getsearchboxtext()] == "undefined"
 | |
| 			){
 | |
| 				
 | |
| 				return;
 | |
| 			}
 | |
| 			
 | |
| 			switch(event.key){
 | |
| 				
 | |
| 				case "ArrowUp":
 | |
| 					event.preventDefault();
 | |
| 					focuspos--;
 | |
| 					if(focuspos === -2){
 | |
| 						
 | |
| 						focuspos = autocomplete_cache[getsearchboxtext()].length - 1;
 | |
| 					}
 | |
| 					break;
 | |
| 				
 | |
| 				case "ArrowDown":
 | |
| 				case "Tab":
 | |
| 					event.preventDefault();
 | |
| 					
 | |
| 					focuspos++;
 | |
| 					if(focuspos >= autocomplete_cache[getsearchboxtext()].length){
 | |
| 						
 | |
| 						focuspos = -1;
 | |
| 					}
 | |
| 					break;
 | |
| 				
 | |
| 				case "Enter":
 | |
| 					should_focus = true;
 | |
| 					
 | |
| 					if(focuspos !== -1){
 | |
| 						
 | |
| 						// replace input content
 | |
| 						event.preventDefault();
 | |
| 						searchbox.value =
 | |
| 							autocomplete_div.getElementsByClassName("entry")[focuspos].innerText;
 | |
| 						break;
 | |
| 					}
 | |
| 					break;
 | |
| 				
 | |
| 				default:
 | |
| 					focuspos = -1;
 | |
| 					break;
 | |
| 			}
 | |
| 			
 | |
| 			if(focuspos === -1){
 | |
| 				
 | |
| 				searchbox.focus();
 | |
| 				return;
 | |
| 			}
 | |
| 			
 | |
| 			autocomplete_div.getElementsByClassName("entry")[focuspos].focus();
 | |
| 		});
 | |
| 		
 | |
| 		window.addEventListener("blur", function(){
 | |
| 			
 | |
| 			autocomplete_div.style.display = "none";
 | |
| 		});
 | |
| 		
 | |
| 		document.addEventListener("keyup", function(event){
 | |
| 			
 | |
| 			// handle ENTER key on entry
 | |
| 			if(should_focus){
 | |
| 				
 | |
| 				should_focus = false;
 | |
| 				searchbox.focus();
 | |
| 			}
 | |
| 		});
 | |
| 		
 | |
| 		document.addEventListener("mousedown", function(event){
 | |
| 			
 | |
| 			// hide input if click is outside
 | |
| 			if(is_click_within(event.target, "searchbox") === false){
 | |
| 				
 | |
| 				autocomplete_div.style.display = "none";
 | |
| 				return;
 | |
| 			}
 | |
| 		});
 | |
| 		
 | |
| 		function handle_entry_click(event){
 | |
| 			
 | |
| 			searchbox.value = event.innerText;
 | |
| 			focuspos = -1;
 | |
| 			searchbox.focus();
 | |
| 		}
 | |
| 		
 | |
| 		searchbox.addEventListener("focus", function(){
 | |
| 			
 | |
| 			focuspos = -1;
 | |
| 			getac();
 | |
| 		});
 | |
| 	}
 | |
| }
 | 
