added autocomplete
This commit is contained in:
		
							parent
							
								
									71a61304b0
								
							
						
					
					
						commit
						edc42ea35d
					
				
							
								
								
									
										225
									
								
								api/v1/ac.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										225
									
								
								api/v1/ac.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,225 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | new autocomplete(); | ||||||
|  | 
 | ||||||
|  | class autocomplete{ | ||||||
|  | 	 | ||||||
|  | 	public function __construct(){ | ||||||
|  | 		 | ||||||
|  | 		header("Content-Type: application/json"); | ||||||
|  | 		 | ||||||
|  | 		$this->scrapers = [ | ||||||
|  | 			"brave" => "https://search.brave.com/api/suggest?q={searchTerms}", | ||||||
|  | 			"ddg" => "https://duckduckgo.com/ac/?q={searchTerms}&type=list", | ||||||
|  | 			"yandex" => "https://suggest.yandex.com/suggest-ff.cgi?part={searchTerms}&uil=en&v=3&sn=5&lr=21276&yu=4861394161661655015", | ||||||
|  | 			"google" => "https://www.google.com/complete/search?client=mobile-gws-lite&q={searchTerms}", | ||||||
|  | 			"qwant" => "https://api.qwant.com/v3/suggest/?q={searchTerms}&client=opensearch", | ||||||
|  | 			"yep" => "https://api.yep.com/ac/?query={searchTerms}", | ||||||
|  | 			"marginalia" => "https://search.marginalia.nu/suggest/?partial={searchTerms}", | ||||||
|  | 			"yt" => "https://suggestqueries-clients6.youtube.com/complete/search?client=youtube&q={searchTerms}", | ||||||
|  | 			"sc" => "https://api-v2.soundcloud.com/search/queries?q={searchTerms}&client_id=iMxZgT5mfGstBj8GWJbYMvpzelS8ne0E&limit=10&offset=0&linked_partitioning=1&app_version=1693487844&app_locale=en" | ||||||
|  | 		]; | ||||||
|  | 		 | ||||||
|  | 		/* | ||||||
|  | 			Sanitize input | ||||||
|  | 		*/ | ||||||
|  | 		if(!isset($_GET["s"])){ | ||||||
|  | 			 | ||||||
|  | 			$this->do404("Missing search(s) parameter"); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if(is_string($_GET["s"]) === false){ | ||||||
|  | 			 | ||||||
|  | 			$this->do404("Invalid search(s) parameter"); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if(strlen($_GET["s"]) > 500){ | ||||||
|  | 			 | ||||||
|  | 			$this->do404("Search(s) exceeds the 500 char length"); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if( | ||||||
|  | 			isset($_GET["scraper"]) && | ||||||
|  | 			is_string($_GET["scraper"]) === false | ||||||
|  | 		){ | ||||||
|  | 			 | ||||||
|  | 			$_GET["scraper"] = "brave"; // default option
 | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		/* | ||||||
|  | 			Get $scraper | ||||||
|  | 		*/ | ||||||
|  | 		if(!isset($_GET["scraper"])){ | ||||||
|  | 			 | ||||||
|  | 			if(isset($_COOKIE["scraper_ac"])){ | ||||||
|  | 				 | ||||||
|  | 				$scraper = $_COOKIE["scraper_ac"]; | ||||||
|  | 			}else{ | ||||||
|  | 				 | ||||||
|  | 				$scraper = "brave"; // default option
 | ||||||
|  | 			} | ||||||
|  | 		}else{ | ||||||
|  | 			 | ||||||
|  | 			$scraper = $_GET["scraper"]; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		if($scraper == "disabled"){ | ||||||
|  | 			 | ||||||
|  | 			// this shouldnt happen, but let's handle it anyways
 | ||||||
|  | 			$this->doempty(); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		// make sure it exists
 | ||||||
|  | 		if(!isset($this->scrapers[$scraper])){ | ||||||
|  | 			 | ||||||
|  | 			$scraper = "brave"; // default option
 | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		// return results
 | ||||||
|  | 		 | ||||||
|  | 		switch($scraper){ | ||||||
|  | 			 | ||||||
|  | 			case "google": | ||||||
|  | 			case "yt": | ||||||
|  | 				// handle google cause they want to be a special snowflake :(
 | ||||||
|  | 				$js = $this->get($this->scrapers[$scraper], $_GET["s"]); | ||||||
|  | 				 | ||||||
|  | 				preg_match( | ||||||
|  | 					'/\((\[.*\])\)/', | ||||||
|  | 					$js, | ||||||
|  | 					$js | ||||||
|  | 				); | ||||||
|  | 				 | ||||||
|  | 				if(!isset($js[1])){ | ||||||
|  | 					 | ||||||
|  | 					$this->doempty(); | ||||||
|  | 				} | ||||||
|  | 				 | ||||||
|  | 				$js = json_decode($js[1]); | ||||||
|  | 				$json = []; | ||||||
|  | 				 | ||||||
|  | 				foreach($js[1] as $item){ | ||||||
|  | 					 | ||||||
|  | 					$json[] = strip_tags($item[0]); | ||||||
|  | 				} | ||||||
|  | 				 | ||||||
|  | 				echo json_encode( | ||||||
|  | 					[ | ||||||
|  | 						$_GET["s"], | ||||||
|  | 						$json | ||||||
|  | 					] | ||||||
|  | 				); | ||||||
|  | 				break; | ||||||
|  | 			 | ||||||
|  | 			case "sc": | ||||||
|  | 				// soundcloud
 | ||||||
|  | 				$js = $this->get($this->scrapers[$scraper], $_GET["s"]); | ||||||
|  | 				 | ||||||
|  | 				$js = json_decode($js, true); | ||||||
|  | 				 | ||||||
|  | 				if(!isset($js["collection"])){ | ||||||
|  | 					 | ||||||
|  | 					$this->doempty(); | ||||||
|  | 				} | ||||||
|  | 				 | ||||||
|  | 				$json = []; | ||||||
|  | 				foreach($js["collection"] as $item){ | ||||||
|  | 					 | ||||||
|  | 					$json[] = $item["query"]; | ||||||
|  | 				} | ||||||
|  | 				 | ||||||
|  | 				echo json_encode( | ||||||
|  | 					[ | ||||||
|  | 						$_GET["s"], | ||||||
|  | 						$json | ||||||
|  | 					] | ||||||
|  | 				); | ||||||
|  | 				break; | ||||||
|  | 			 | ||||||
|  | 			case "marginalia": | ||||||
|  | 				$json = $this->get($this->scrapers[$scraper], $_GET["s"]); | ||||||
|  | 				 | ||||||
|  | 				$json = json_decode($json, true); | ||||||
|  | 				if($json === null){ | ||||||
|  | 					 | ||||||
|  | 					 | ||||||
|  | 					$this->doempty(); | ||||||
|  | 				} | ||||||
|  | 				 | ||||||
|  | 				echo json_encode( | ||||||
|  | 					[ | ||||||
|  | 						$_GET["s"], | ||||||
|  | 						$json | ||||||
|  | 					] | ||||||
|  | 				); | ||||||
|  | 				break; | ||||||
|  | 			 | ||||||
|  | 			default: | ||||||
|  | 				// if it respects the openSearch protocol
 | ||||||
|  | 				$json = json_decode($this->get($this->scrapers[$scraper], $_GET["s"]), true); | ||||||
|  | 				 | ||||||
|  | 				echo json_encode( | ||||||
|  | 					[ | ||||||
|  | 						$_GET["s"], | ||||||
|  | 						$json[1] // ensure it contains valid key 0
 | ||||||
|  | 					] | ||||||
|  | 				); | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private function get($url, $query){ | ||||||
|  | 		 | ||||||
|  | 		$curlproc = curl_init(); | ||||||
|  | 		 | ||||||
|  | 		$url = str_replace("{searchTerms}", urlencode($query), $url); | ||||||
|  | 		 | ||||||
|  | 		curl_setopt($curlproc, CURLOPT_URL, $url); | ||||||
|  | 		 | ||||||
|  | 		curl_setopt($curlproc, CURLOPT_ENCODING, ""); // default encoding
 | ||||||
|  | 		curl_setopt($curlproc, CURLOPT_HTTPHEADER, | ||||||
|  | 			["User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/116.0", | ||||||
|  | 			"Accept: application/json, text/javascript, */*; q=0.01", | ||||||
|  | 			"Accept-Language: en-US,en;q=0.5", | ||||||
|  | 			"Accept-Encoding: gzip", | ||||||
|  | 			"DNT: 1", | ||||||
|  | 			"Connection: keep-alive", | ||||||
|  | 			"Sec-Fetch-Dest: empty", | ||||||
|  | 			"Sec-Fetch-Mode: cors", | ||||||
|  | 			"Sec-Fetch-Site: same-site"] | ||||||
|  | 		); | ||||||
|  | 		 | ||||||
|  | 		curl_setopt($curlproc, CURLOPT_RETURNTRANSFER, true); | ||||||
|  | 		curl_setopt($curlproc, CURLOPT_SSL_VERIFYHOST, 2); | ||||||
|  | 		curl_setopt($curlproc, CURLOPT_SSL_VERIFYPEER, true); | ||||||
|  | 		curl_setopt($curlproc, CURLOPT_CONNECTTIMEOUT, 30); | ||||||
|  | 		curl_setopt($curlproc, CURLOPT_TIMEOUT, 30); | ||||||
|  | 		 | ||||||
|  | 		$data = curl_exec($curlproc); | ||||||
|  | 		 | ||||||
|  | 		if(curl_errno($curlproc)){ | ||||||
|  | 			 | ||||||
|  | 			throw new Exception(curl_error($curlproc)); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		curl_close($curlproc); | ||||||
|  | 		return $data; | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private function do404($error){ | ||||||
|  | 		 | ||||||
|  | 		echo json_encode(["error" => $error]); | ||||||
|  | 		die(); | ||||||
|  | 	} | ||||||
|  | 	 | ||||||
|  | 	private function doempty(){ | ||||||
|  | 		 | ||||||
|  | 		echo json_encode( | ||||||
|  | 			[ | ||||||
|  | 				$_GET["s"], | ||||||
|  | 				[] | ||||||
|  | 			] | ||||||
|  | 		); | ||||||
|  | 		die(); | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -574,8 +574,6 @@ class brave{ | |||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 					 | 					 | ||||||
| 					echo "test"; |  | ||||||
| 					 |  | ||||||
| 					if($rating !== null){ | 					if($rating !== null){ | ||||||
| 						 | 						 | ||||||
| 						$table["Rating"] = $rating; | 						$table["Rating"] = $rating; | ||||||
|  | |||||||
| @ -1616,21 +1616,23 @@ class google{ | |||||||
| 					$imgvl | 					$imgvl | ||||||
| 				); | 				); | ||||||
| 				 | 				 | ||||||
| 				$imgvl = $imgvl[1]; | 				if(isset($imgvl[1])){ | ||||||
| 				 | 					$imgvl = $imgvl[1]; | ||||||
| 				$params["async"] = "_id:islrg_c,_fmt:html"; | 					 | ||||||
| 				$params["asearch"] = "ichunklite"; | 					$params["async"] = "_id:islrg_c,_fmt:html"; | ||||||
| 				$params["ved"] = $ved; | 					$params["asearch"] = "ichunklite"; | ||||||
| 				$params["vet"] = "1" . $ved . "..i"; | 					$params["ved"] = $ved; | ||||||
| 				$params["start"] = 100; | 					$params["vet"] = "1" . $ved . "..i"; | ||||||
| 				$params["ijn"] = 1; | 					$params["start"] = 100; | ||||||
| 				$params["imgvl"] = $imgvl; | 					$params["ijn"] = 1; | ||||||
| 				 | 					$params["imgvl"] = $imgvl; | ||||||
| 				$out["npt"] = | 					 | ||||||
| 					$this->nextpage->store( | 					$out["npt"] = | ||||||
| 						json_encode($params), | 						$this->nextpage->store( | ||||||
| 						"images" | 							json_encode($params), | ||||||
| 					); | 							"images" | ||||||
|  | 						); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		 | 		 | ||||||
|  | |||||||
| @ -288,7 +288,7 @@ class sc{ | |||||||
| 					 | 					 | ||||||
| 					if(count($description) != 0){ | 					if(count($description) != 0){ | ||||||
| 						 | 						 | ||||||
| 						$description = $count . " songs. " . implode(", ", $description); | 						$description = trim($count . " songs. " . implode(", ", $description)); | ||||||
| 					} | 					} | ||||||
| 					 | 					 | ||||||
| 					if( | 					if( | ||||||
| @ -320,7 +320,7 @@ class sc{ | |||||||
| 					 | 					 | ||||||
| 					$out["playlist"][] = [ | 					$out["playlist"][] = [ | ||||||
| 						"title" => $item["title"], | 						"title" => $item["title"], | ||||||
| 						"description" => $description, | 						"description" => $this->limitstrlen($description), | ||||||
| 						"author" => [ | 						"author" => [ | ||||||
| 							"name" => $item["user"]["username"], | 							"name" => $item["user"]["username"], | ||||||
| 							"url" => $item["user"]["permalink_url"], | 							"url" => $item["user"]["permalink_url"], | ||||||
| @ -385,13 +385,14 @@ class sc{ | |||||||
| 				"\n", | 				"\n", | ||||||
| 				wordwrap( | 				wordwrap( | ||||||
| 					str_replace( | 					str_replace( | ||||||
| 						"\n", | 						["\n\r", "\r\n", "\n", "\r"], | ||||||
| 						" ", | 						" ", | ||||||
| 						$text | 						$text | ||||||
| 					), | 					), | ||||||
| 					300, | 					300, | ||||||
| 					"\n" | 					"\n" | ||||||
| 				) | 				), | ||||||
|  | 				2 | ||||||
| 			)[0]; | 			)[0]; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										91
									
								
								settings.php
									
									
									
									
									
								
							
							
						
						
									
										91
									
								
								settings.php
									
									
									
									
									
								
							| @ -58,6 +58,56 @@ $settings = [ | |||||||
| 	[ | 	[ | ||||||
| 		"name" => "Scrapers to use", | 		"name" => "Scrapers to use", | ||||||
| 		"settings" => [ | 		"settings" => [ | ||||||
|  | 			[ | ||||||
|  | 				"description" => "Autocomplete<br><i>Picking <div class=\"code-inline\">Auto</div> changes the source dynamically depending of the page's scraper<br>Picking <div class=\"code-inline\">Disabled</div> disables this feature</i>", | ||||||
|  | 				"parameter" => "scraper_ac", | ||||||
|  | 				"options" => [ | ||||||
|  | 					[ | ||||||
|  | 						"value" => "disabled", | ||||||
|  | 						"text" => "Disabled" | ||||||
|  | 					], | ||||||
|  | 					[ | ||||||
|  | 						"value" => "auto", | ||||||
|  | 						"text" => "Auto" | ||||||
|  | 					], | ||||||
|  | 					[ | ||||||
|  | 						"value" => "brave", | ||||||
|  | 						"text" => "Brave" | ||||||
|  | 					], | ||||||
|  | 					[ | ||||||
|  | 						"value" => "ddg", | ||||||
|  | 						"text" => "DuckDuckGo" | ||||||
|  | 					], | ||||||
|  | 					[ | ||||||
|  | 						"value" => "yandex", | ||||||
|  | 						"text" => "Yandex" | ||||||
|  | 					], | ||||||
|  | 					[ | ||||||
|  | 						"value" => "google", | ||||||
|  | 						"text" => "Google" | ||||||
|  | 					], | ||||||
|  | 					[ | ||||||
|  | 						"value" => "qwant", | ||||||
|  | 						"text" => "Qwant" | ||||||
|  | 					], | ||||||
|  | 					[ | ||||||
|  | 						"value" => "yep", | ||||||
|  | 						"text" => "Yep" | ||||||
|  | 					], | ||||||
|  | 					[ | ||||||
|  | 						"value" => "marginalia", | ||||||
|  | 						"text" => "Marginalia" | ||||||
|  | 					], | ||||||
|  | 					[ | ||||||
|  | 						"value" => "yt", | ||||||
|  | 						"text" => "YouTube" | ||||||
|  | 					], | ||||||
|  | 					[ | ||||||
|  | 						"value" => "sc", | ||||||
|  | 						"text" => "SoundCloud" | ||||||
|  | 					] | ||||||
|  | 				] | ||||||
|  | 			], | ||||||
| 			[ | 			[ | ||||||
| 				"description" => "Web", | 				"description" => "Web", | ||||||
| 				"parameter" => "scraper_web", | 				"parameter" => "scraper_web", | ||||||
| @ -183,8 +233,13 @@ $settings = [ | |||||||
| if($_POST){ | if($_POST){ | ||||||
| 
 | 
 | ||||||
| 	$loop = &$_POST; | 	$loop = &$_POST; | ||||||
| }else{ | }elseif(count($_GET) !== 0){ | ||||||
| 	 | 	 | ||||||
|  | 	// redirect user to front page
 | ||||||
|  | 	$loop = &$_GET; | ||||||
|  | 	header("Location: /"); | ||||||
|  | 	 | ||||||
|  | }else{ | ||||||
| 	// refresh cookie dates
 | 	// refresh cookie dates
 | ||||||
| 	$loop = &$_COOKIE; | 	$loop = &$_COOKIE; | ||||||
| } | } | ||||||
| @ -245,7 +300,7 @@ echo | |||||||
| 		'<head>' . | 		'<head>' . | ||||||
| 			'<meta http-equiv="Content-Type" content="text/html;charset=utf-8">' . | 			'<meta http-equiv="Content-Type" content="text/html;charset=utf-8">' . | ||||||
| 			'<title>Settings</title>' . | 			'<title>Settings</title>' . | ||||||
| 			'<link rel="stylesheet" href="/static/style.css?v3">' . | 			'<link rel="stylesheet" href="/static/style.css?v4">' . | ||||||
| 			'<meta name="viewport" content="width=device-width,initial-scale=1">' . | 			'<meta name="viewport" content="width=device-width,initial-scale=1">' . | ||||||
| 			'<meta name="robots" content="index,follow">' . | 			'<meta name="robots" content="index,follow">' . | ||||||
| 			'<link rel="icon" type="image/x-icon" href="/favicon.ico">' . | 			'<link rel="icon" type="image/x-icon" href="/favicon.ico">' . | ||||||
| @ -260,14 +315,14 @@ $left = | |||||||
| 		'By clicking <div class="code-inline">Update settings!</div>, a plaintext <div class="code-inline">key=value</div> cookie will be stored on your browser. When selecting a default setting, the parameter is removed from your cookies.'; | 		'By clicking <div class="code-inline">Update settings!</div>, a plaintext <div class="code-inline">key=value</div> cookie will be stored on your browser. When selecting a default setting, the parameter is removed from your cookies.'; | ||||||
| 
 | 
 | ||||||
| $c = count($_COOKIE); | $c = count($_COOKIE); | ||||||
|  | $code = ""; | ||||||
|  | 
 | ||||||
| if($c !== 0){ | if($c !== 0){ | ||||||
| 	 | 	 | ||||||
| 	$left .= | 	$left .= | ||||||
| 		'<br><br>Your current cookie looks like this:' . | 		'<br><br>Your current cookie looks like this:' . | ||||||
| 		'<div class="code">'; | 		'<div class="code">'; | ||||||
| 	 | 	 | ||||||
| 	$code = ""; |  | ||||||
| 	 |  | ||||||
| 	$ca = 0; | 	$ca = 0; | ||||||
| 	foreach($_COOKIE as $key => $value){ | 	foreach($_COOKIE as $key => $value){ | ||||||
| 		 | 		 | ||||||
| @ -326,17 +381,23 @@ $left .= | |||||||
| 	'</div>' . | 	'</div>' . | ||||||
| 	'<div class="settings-submit">' . | 	'<div class="settings-submit">' . | ||||||
| 		'<input type="submit" value="Update settings!">' . | 		'<input type="submit" value="Update settings!">' . | ||||||
| 		'<a href="../">< Return to main page</a>' . | 		'<a href="../">< Return to front page</a>' . | ||||||
| 	'</div>' . | 	'</div>' . | ||||||
| 	'</form>'; | 	'</form>'; | ||||||
| 
 | 
 | ||||||
| echo | if(count($_GET) === 0){ | ||||||
| 	$frontend->load( | 
 | ||||||
| 		"search.html", | 	echo | ||||||
| 		[ | 		$frontend->load( | ||||||
| 			"class" => "", | 			"search.html", | ||||||
| 			"right-left" => "", | 			[ | ||||||
| 			"right-right" => "", | 				"class" => "", | ||||||
| 			"left" => $left | 				"right-left" =>			 | ||||||
| 		] | 					'<div class="infobox"><h2>Preference link</h2>Follow this link to auto-apply all cookies. Useful if your browser clears out cookies after a browsing session. Following this link will redirect you to the front page, unless no settings are set.<br><br>' . | ||||||
| 	); | 					'<a href="settings' . rtrim("?" . str_replace("; ", "&", $code), "?") . '">Bookmark me!</a>' . | ||||||
|  | 					'</div>', | ||||||
|  | 				"right-right" => "", | ||||||
|  | 				"left" => $left | ||||||
|  | 			] | ||||||
|  | 		); | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										266
									
								
								static/client.js
									
									
									
									
									
								
							
							
						
						
									
										266
									
								
								static/client.js
									
									
									
									
									
								
							| @ -660,15 +660,16 @@ function changeimage(event){ | |||||||
| 	centerpopup(); | 	centerpopup(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* |  | ||||||
| 	Shortcuts |  | ||||||
| */ |  | ||||||
| var searchbox_wrapper = document.getElementsByClassName("searchbox"); | var searchbox_wrapper = document.getElementsByClassName("searchbox"); | ||||||
| 
 | 
 | ||||||
| if(searchbox_wrapper.length !== 0){ | if(searchbox_wrapper.length !== 0){ | ||||||
|  | 		 | ||||||
| 	searchbox_wrapper = searchbox_wrapper[0]; | 	searchbox_wrapper = searchbox_wrapper[0]; | ||||||
| 	var searchbox = searchbox_wrapper.getElementsByTagName("input")[1]; | 	var searchbox = searchbox_wrapper.getElementsByTagName("input")[1]; | ||||||
| 
 | 	 | ||||||
|  | 	/* | ||||||
|  | 		Textarea shortcuts | ||||||
|  | 	*/ | ||||||
| 	document.addEventListener("keydown", function(key){ | 	document.addEventListener("keydown", function(key){ | ||||||
| 		 | 		 | ||||||
| 		switch(key.keyCode){ | 		switch(key.keyCode){ | ||||||
| @ -695,4 +696,261 @@ if(searchbox_wrapper.length !== 0){ | |||||||
| 				break; | 				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) + ac_req_appendix); | ||||||
|  | 				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(); | ||||||
|  | 					}, 300); | ||||||
|  | 			}); | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		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(); | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -149,31 +149,27 @@ h3,h4,h5,h6{ | |||||||
| 	left:-1px; | 	left:-1px; | ||||||
| 	right:-1px; | 	right:-1px; | ||||||
| 	background:var(--282828); | 	background:var(--282828); | ||||||
| 	border:1px solid var(--504945); | 	border:1px solid var(--928374); | ||||||
| 	border-top:none; | 	border-top:none; | ||||||
| 	border-radius:0 0 2px 2px; | 	border-radius:0 0 2px 2px; | ||||||
| 	z-index:10; | 	z-index:10; | ||||||
|  | 	overflow:hidden; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .autocomplete .entry{ | .autocomplete .entry{ | ||||||
| 	overflow:hidden; | 	overflow:hidden; | ||||||
| 	padding:4px 10px; | 	padding:4px 10px; | ||||||
| 	cursor:pointer; | 	cursor:pointer; | ||||||
|  | 	outline:none; | ||||||
|  | 	user-select:none; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .autocomplete .entry:hover{ | .autocomplete .entry:hover{ | ||||||
| 	background:var(--3c3836); | 	background:var(--3c3836); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .autocomplete .title{ | .autocomplete .entry:focus{ | ||||||
| 	float:left; | 	background:var(--3c3836); | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .autocomplete .subtext{ |  | ||||||
| 	float:right; |  | ||||||
| 	font-size:14px; |  | ||||||
| 	color:var(--928374); |  | ||||||
| 	margin-left:7px; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* Tabs */ | /* Tabs */ | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
| 	<head> | 	<head> | ||||||
| 		<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> | 		<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> | ||||||
| 		<title>{%title%}</title> | 		<title>{%title%}</title> | ||||||
| 		<link rel="stylesheet" href="/static/style.css?v3"> | 		<link rel="stylesheet" href="/static/style.css?v4"> | ||||||
| 		<meta name="viewport" content="width=device-width,initial-scale=1"> | 		<meta name="viewport" content="width=device-width,initial-scale=1"> | ||||||
| 		<meta name="robots" content="{%index%}index,{%index%}follow"> | 		<meta name="robots" content="{%index%}index,{%index%}follow"> | ||||||
| 		<link rel="icon" type="image/x-icon" href="/favicon.ico"> | 		<link rel="icon" type="image/x-icon" href="/favicon.ico"> | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
| 		<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> | 		<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> | ||||||
| 		<title>4get</title> | 		<title>4get</title> | ||||||
| 		<meta name="viewport" content="width=device-width,initial-scale=1"> | 		<meta name="viewport" content="width=device-width,initial-scale=1"> | ||||||
| 		<link rel="stylesheet" href="/static/style.css?v3"> | 		<link rel="stylesheet" href="/static/style.css?v4"> | ||||||
| 		<meta name="robots" content="index,follow"> | 		<meta name="robots" content="index,follow"> | ||||||
| 		<link rel="icon" type="image/x-icon" href="/favicon.ico"> | 		<link rel="icon" type="image/x-icon" href="/favicon.ico"> | ||||||
| 		<meta name="description" content="4get.ca: They live in our walls!"> | 		<meta name="description" content="4get.ca: They live in our walls!"> | ||||||
| @ -33,6 +33,6 @@ | |||||||
| 				Report a problem: <a href="https://lolcat.ca">lolcat.ca</a> | 				Report a problem: <a href="https://lolcat.ca">lolcat.ca</a> | ||||||
| 			</div> | 			</div> | ||||||
| 		</div> | 		</div> | ||||||
| 		<script src="/static/client.js?v3"></script> | 		<script src="/static/client.js?v4"></script> | ||||||
| 	</body> | 	</body> | ||||||
| </html> | </html> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 lolcat
						lolcat