aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMinteck <contact@minteck.org>2021-12-12 11:19:38 +0100
committerMinteck <contact@minteck.org>2021-12-12 11:19:38 +0100
commitdc8f78256944885108f1d52b822c8677bf9d964c (patch)
tree38ea776d4fe8d9d11c28448246afa8af4886351a
downloadwolfeye-php-dc8f78256944885108f1d52b822c8677bf9d964c.tar.gz
wolfeye-php-dc8f78256944885108f1d52b822c8677bf9d964c.tar.bz2
wolfeye-php-dc8f78256944885108f1d52b822c8677bf9d964c.zip
Initial commit
-rw-r--r--.gitignore4
-rw-r--r--.gitlab-ci.yml15
-rw-r--r--.idea/.gitignore8
-rw-r--r--.idea/MinteckSearch.iml8
-rw-r--r--.idea/deployment.xml14
-rw-r--r--.idea/modules.xml8
-rw-r--r--.idea/php.xml4
-rw-r--r--.idea/sshConfigs.xml8
-rw-r--r--.idea/vcs.xml6
-rw-r--r--.idea/webServers.xml14
-rw-r--r--TODO3
-rw-r--r--assets/default.pngbin0 -> 1478 bytes
-rw-r--r--assets/instant.js50
-rw-r--r--assets/main.css200
-rw-r--r--assets/relevant.svg1
-rw-r--r--assets/search.svg1
-rw-r--r--assets/verified.svg1
-rw-r--r--icon/index.php46
-rw-r--r--index.php16
-rw-r--r--logo.old.svg217
-rw-r--r--logo.svg490
-rw-r--r--out/index.php42
-rw-r--r--private/instants/kartik34
-rw-r--r--private/instants/minteck34
-rw-r--r--ratelimited/instant.php82
-rw-r--r--search/index.php270
26 files changed, 1576 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..668dbbd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+private/ratelimiting/*
+private/cache/*
+private/db.json
+private/dictionary.json \ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..4402674
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,15 @@
+# Generic Minteck GitLab CI/CD Checks
+# PHP applications
+image: php:zts-buster
+
+cache:
+ paths:
+ - vendor/
+
+before_script:
+ - apt-get update
+ - apt-get install -y git
+
+test:
+ script:
+ - bash -c '[[ $(find . -name \*.php -exec php -l {} \; | grep -v "No syntax errors") ]] && exit 2 || exit 0'
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/MinteckSearch.iml b/.idea/MinteckSearch.iml
new file mode 100644
index 0000000..c956989
--- /dev/null
+++ b/.idea/MinteckSearch.iml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+ <component name="NewModuleRootManager">
+ <content url="file://$MODULE_DIR$" />
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module> \ No newline at end of file
diff --git a/.idea/deployment.xml b/.idea/deployment.xml
new file mode 100644
index 0000000..22d9ade
--- /dev/null
+++ b/.idea/deployment.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="PublishConfigData" serverName="Familine" filePermissions="511" folderPermissions="511" remoteFilesAllowedToDisappearOnAutoupload="false">
+ <serverData>
+ <paths name="Familine">
+ <serverdata>
+ <mappings>
+ <mapping deploy="/mnt/wolfeye" local="$PROJECT_DIR$" web="/" />
+ </mappings>
+ </serverdata>
+ </paths>
+ </serverData>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..b0cb0c4
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/.idea/MinteckSearch.iml" filepath="$PROJECT_DIR$/.idea/MinteckSearch.iml" />
+ </modules>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/php.xml b/.idea/php.xml
new file mode 100644
index 0000000..7341688
--- /dev/null
+++ b/.idea/php.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="PhpProjectSharedConfiguration" php_language_level="7.4" />
+</project> \ No newline at end of file
diff --git a/.idea/sshConfigs.xml b/.idea/sshConfigs.xml
new file mode 100644
index 0000000..66ef29d
--- /dev/null
+++ b/.idea/sshConfigs.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="SshConfigs">
+ <configs>
+ <sshConfig authType="OPEN_SSH" host="minteck.org" id="319e7a2c-5091-4381-a0bc-0aad15d58f41" port="22465" nameFormat="DESCRIPTIVE" username="root" useOpenSSHConfig="true" />
+ </configs>
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
+ </component>
+</project> \ No newline at end of file
diff --git a/.idea/webServers.xml b/.idea/webServers.xml
new file mode 100644
index 0000000..e9b027b
--- /dev/null
+++ b/.idea/webServers.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="WebServers">
+ <option name="servers">
+ <webServer id="4ddeb4db-477c-482b-8bc1-be8e25957b40" name="Familine">
+ <fileTransfer accessType="SFTP" host="minteck.org" port="22465" sshConfigId="319e7a2c-5091-4381-a0bc-0aad15d58f41" sshConfig="root@minteck.org:22465 agent" authAgent="true">
+ <advancedOptions>
+ <advancedOptions dataProtectionLevel="Private" keepAliveTimeout="0" passiveMode="true" shareSSLContext="true" />
+ </advancedOptions>
+ </fileTransfer>
+ </webServer>
+ </option>
+ </component>
+</project> \ No newline at end of file
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..bd9ed29
--- /dev/null
+++ b/TODO
@@ -0,0 +1,3 @@
+Integration with Stack Overflow for development questions
+Integration with Wolfram Alpha for general questions
+Integration with DeepL for translation \ No newline at end of file
diff --git a/assets/default.png b/assets/default.png
new file mode 100644
index 0000000..b018b02
--- /dev/null
+++ b/assets/default.png
Binary files differ
diff --git a/assets/instant.js b/assets/instant.js
new file mode 100644
index 0000000..439daab
--- /dev/null
+++ b/assets/instant.js
@@ -0,0 +1,50 @@
+(async () => {
+ instant = JSON.parse(
+ (await (
+ await (
+ await window.fetch("/ratelimited/instant.php?q=" + query)
+ ).blob()
+ ).text()
+ ));
+
+ document.getElementById("details").innerHTML = "<small id='details-source'>from <a id='details-source-link' href='https://duckduckgo.com' target='_blank'>DuckDuckGo</a>:</small><p>" + instant.Abstract + "</p><p><i>— <a href='" + instant.AbstractURL + "' id='details-data-link' target='_blank'>" + instant.AbstractSource + "</a></i></p>";
+ if (instant.Abstract.trim() !== "") document.getElementById("details").style.display = "block";
+ if (instant.Abstract.trim() !== "") document.getElementById("results").classList.add("with-details");
+ if (instant.Abstract.trim() !== "") try {document.getElementsByClassName("dym-section")[0].outerHTML = ""; } catch (e) {}
+
+ if (!instant._WolfEye_External) {
+ try { document.getElementById("details-source").innerHTML = "&nbsp;" } catch (e) {}
+ try { document.getElementById("details-data-link").target = "" } catch (e) {}
+ }
+
+ if (instant.Results[0]) {
+ if (!document.getElementsByClassName("result-official")[0]) {
+ document.getElementById("results").innerHTML = '<div class="result result-official"><a href="https://example.com"><div class="result-name" style="font-size: 18px;"><img alt="" class="result-icon" src="about:blank"> <b>Example</b> <img class="result-relevant" src="/assets/relevant.svg" alt=""></div><div class="result-url"><small>https://example.com/</small></div></a></div>' + document.getElementById("results").innerHTML;
+ }
+
+ document.getElementsByClassName("result-official")[0].classList.add("result-verified");
+ document.getElementsByClassName("result-official")[0].children[0].href = instant.Results[0].FirstURL;
+ document.getElementsByClassName("result-official")[0].children[0].children[0].children[2].src = "/assets/verified.svg";
+ document.getElementsByClassName("result-official")[0].children[0].children[0].children[2].title = "Verified Website";
+ document.getElementsByClassName("result-official")[0].children[0].children[0].children[0].src = "https://duckduckgo.com" + instant.Results[0].Icon.URL;
+ document.getElementsByClassName("result-official")[0].children[0].children[0].children[1].innerText = instant.Heading;
+ document.getElementsByClassName("result-official")[0].children[0].children[1].children[0].innerText = instant.Results[0].FirstURL;
+ }
+
+ if (query === "wolfeye" || query === "wolf eye") {
+ document.getElementById("details").innerHTML = "<h3>Hold tight! Let's go home...</h3><p>You are already using WolfEye; but you seem lost. Use the search bar at the top of this page to search for something on the Web.</p>"
+ document.getElementById("details").style.display = "block";
+
+ if (!document.getElementsByClassName("result-official")[0]) {
+ document.getElementById("results").innerHTML = '<div class="result result-official"><a href="https://example.com"><div class="result-name" style="font-size: 18px;"><img alt="" class="result-icon" src="about:blank"> <b>Example</b> <img class="result-relevant" src="/assets/relevant.svg" alt=""></div><div class="result-url"><small>https://example.com/</small></div></a></div>' + document.getElementById("results").innerHTML;
+ }
+
+ document.getElementsByClassName("result-official")[0].classList.add("result-verified");
+ document.getElementsByClassName("result-official")[0].children[0].href = "https://wolfeye.minteck.org";
+ document.getElementsByClassName("result-official")[0].children[0].children[0].children[2].src = "/assets/verified.svg";
+ document.getElementsByClassName("result-official")[0].children[0].children[0].children[2].title = "Verified Website";
+ document.getElementsByClassName("result-official")[0].children[0].children[0].children[0].src = "/logo.svg";
+ document.getElementsByClassName("result-official")[0].children[0].children[0].children[1].innerText = "WolfEye";
+ document.getElementsByClassName("result-official")[0].children[0].children[1].children[0].innerText = "https://wolfeye.minteck.org";
+ }
+})() \ No newline at end of file
diff --git a/assets/main.css b/assets/main.css
new file mode 100644
index 0000000..107521f
--- /dev/null
+++ b/assets/main.css
@@ -0,0 +1,200 @@
+body, html {
+ background: #222;
+ color: white;
+ margin: 0;
+ font-family: arial,sans-serif;
+}
+
+.result-url, .result-name {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.result {
+ color: white;
+ text-decoration: none;
+ padding: 8px;
+ margin-bottom: 5px;
+ border-radius: 5px;
+ border: 2px solid transparent;
+}
+
+.result:hover {
+ background: #333;
+}
+
+.result:active, .result:focus {
+ background: #444;
+}
+
+.result a {
+ text-decoration: inherit;
+ color: inherit;
+}
+
+.result.result-official {
+ border-color: rgba(255, 255, 0, 0.3);
+}
+
+.result.result-verified {
+ border-color: rgba(0, 255, 21, 0.3);
+}
+
+.result-relevant {
+ vertical-align: middle;
+}
+
+#results-intro-logo, #results-intro-bar {
+ display: inline-block;
+ margin: 0;
+}
+
+#results-intro {
+ background: #111;
+ padding: 0 50px;
+ margin-bottom: 20px;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ border-bottom: 1px solid #252525;
+}
+
+#results-intro-bar-input {
+ background: #171717;
+ color: white;
+ border: 1px solid #222;
+ padding: 10px;
+ outline: none;
+ height: 8px;
+ border-radius: 5px;
+ width: calc(100vw - 230px);
+ margin: 8px 10px;
+}
+
+#results-intro-bar-input:hover {
+ border-color: #333;
+}
+
+#results-intro-bar-input:active, #results-intro-bar-input:focus {
+ border-color: #444;
+}
+
+#intro-results-separator {
+ margin-top: 43px;
+}
+
+@media (max-width: 1200px) {
+ #results, #details, #results-debug {
+ padding: 50px 200px !important;
+ }
+}
+
+@media (max-width: 1000px) {
+ #results, #details, #results-debug {
+ padding: 50px 150px !important;
+ }
+}
+
+@media (max-width: 700px) {
+ #results, #details, #results-debug {
+ padding: 50px 100px !important;
+ }
+}
+
+@media (max-width: 500px) {
+ #results, #details, #results-debug {
+ padding: 50px 50px !important;
+ }
+}
+
+@media (max-width: 450px) {
+ #results, #details, #results-debug {
+ padding: 50px 10px !important;
+ }
+}
+
+#results, #details, #results-debug {
+ padding: 50px 250px;
+}
+
+.dym-link {
+ color: lightgray;
+}
+
+.dym-section {
+ padding: 10px;
+ background: #333;
+ border-bottom: 1px solid #353535;
+}
+
+.result-icon {
+ width: 24px;
+ height: 24px;
+ vertical-align: middle;
+}
+
+#results-intro-logo-img {
+ width: 32px;
+ height: 32px;
+ vertical-align: middle;
+}
+
+#details {
+ padding-top: 10px !important;
+ padding-bottom: 10px !important;
+ display: none;
+}
+
+#results-intro-bar-submit {
+ background-color: transparent;
+ color: transparent;
+ border: transparent;
+ cursor: pointer;
+ background-image: url("/assets/search.svg");
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+ width: 24px;
+ height: 24px;
+ padding: 4px;
+}
+
+@media (max-width: 50px) {
+ #results-intro {
+ padding: 0 50px;
+ }
+ #results-intro-bar-input {
+ width: calc(100vw - 152px);
+ margin: 8px 5px;
+ }
+}
+
+#results-debug {
+ margin: 0;
+ padding-top: 10px !important;
+ padding-bottom: 0 !important;
+}
+
+#results.with-details {
+ padding-top: 10px !important;
+}
+
+#results-debug-inner-details {
+ opacity: 0;
+ font-family: monospace;
+}
+
+#results-debug-inner-details:hover {
+ opacity: 1;
+}
+
+#details-source {
+ display: inline-block;
+ opacity: .5;
+}
+
+#details-source-link, #details-data-link {
+ color: white;
+} \ No newline at end of file
diff --git a/assets/relevant.svg b/assets/relevant.svg
new file mode 100644
index 0000000..dc6a14c
--- /dev/null
+++ b/assets/relevant.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="rgba(255, 255, 0, 0.3)"><g><rect fill="none" height="24" width="24"/><path d="M14.43,10l-1.47-4.84c-0.29-0.95-1.63-0.95-1.91,0L9.57,10H5.12c-0.97,0-1.37,1.25-0.58,1.81l3.64,2.6l-1.43,4.61 c-0.29,0.93,0.79,1.68,1.56,1.09L12,17.31l3.69,2.81c0.77,0.59,1.85-0.16,1.56-1.09l-1.43-4.61l3.64-2.6 c0.79-0.57,0.39-1.81-0.58-1.81H14.43z"/></g></svg> \ No newline at end of file
diff --git a/assets/search.svg b/assets/search.svg
new file mode 100644
index 0000000..06a261d
--- /dev/null
+++ b/assets/search.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#ffffff"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M15.5 14h-.79l-.28-.27c1.2-1.4 1.82-3.31 1.48-5.34-.47-2.78-2.79-5-5.59-5.34-4.23-.52-7.79 3.04-7.27 7.27.34 2.8 2.56 5.12 5.34 5.59 2.03.34 3.94-.28 5.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg> \ No newline at end of file
diff --git a/assets/verified.svg b/assets/verified.svg
new file mode 100644
index 0000000..ada4600
--- /dev/null
+++ b/assets/verified.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="rgba(0, 255, 21, 0.3)"><g><rect fill="none" height="24" width="24"/><rect fill="none" height="24" width="24"/></g><g><path d="M23,12l-2.44-2.79l0.34-3.69l-3.61-0.82L15.4,1.5L12,2.96L8.6,1.5L6.71,4.69L3.1,5.5L3.44,9.2L1,12l2.44,2.79l-0.34,3.7 l3.61,0.82L8.6,22.5l3.4-1.47l3.4,1.46l1.89-3.19l3.61-0.82l-0.34-3.69L23,12z M9.38,16.01L7,13.61c-0.39-0.39-0.39-1.02,0-1.41 l0.07-0.07c0.39-0.39,1.03-0.39,1.42,0l1.61,1.62l5.15-5.16c0.39-0.39,1.03-0.39,1.42,0l0.07,0.07c0.39,0.39,0.39,1.02,0,1.41 l-5.92,5.94C10.41,16.4,9.78,16.4,9.38,16.01z"/></g></svg> \ No newline at end of file
diff --git a/icon/index.php b/icon/index.php
new file mode 100644
index 0000000..97bf068
--- /dev/null
+++ b/icon/index.php
@@ -0,0 +1,46 @@
+<?php
+
+if (!isset($_GET['q']) || !isset($_GET['i'])) {
+ header("Location: /");
+ die();
+}
+
+$_GET['q'] = strtolower(substr($_GET['q'], 0, 200));
+$_GET['q'] = preg_replace("/[^A-Za-z0-9 ]/", '', preg_replace("/[\.]/", ' ', $_GET['q']));
+
+$data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/private/db.json"), true);
+
+if ($_GET['i'] !== "_off" && isset($data["entries"][$_GET['i']])) {
+ $icdom = explode("/", $data["entries"][$_GET['i']]["url"])[2];
+ if (substr($icdom, 0, 4) === "www.") {
+ $icdom = substr($icdom, 4);
+ }
+} elseif ($_GET['i'] === "_off") {
+ $domains = [];
+
+ foreach ($data["entries"] as $id => $entry) {
+ $domain = explode("/", $entry["url"])[2];
+ $sim = similar_text($domain, $_GET['q'], $perc);
+ if (substr($domain, 0, 4) === "www.") {
+ $domainstr = substr($domain, 4);
+ } else {
+ $domainstr = $domain;
+ }
+ $domains[] = [
+ "id" => $id,
+ "url" => "https://" . $domain . "/",
+ "domain" => $domainstr,
+ "relevance" => $perc / 50 - 1
+ ];
+ }
+
+ usort($domains, function($a, $b) {
+ return $a['relevance'] <=> $b['relevance'];
+ });
+
+ $domains = array_reverse($domains);
+ $icdom = $domains[0]['domain'];
+}
+
+header("Location: https://external-content.duckduckgo.com/ip3/" . $icdom . ".ico");
+die(); \ No newline at end of file
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..4bc8815
--- /dev/null
+++ b/index.php
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>WolfEye</title>
+</head>
+<body>
+ <h1>WolfEye</h1>
+ <form action="/search">
+ <input type="text" name="q" placeholder="Search the Web...">
+ <input type="submit" value="Search!">
+ <input type="submit" value="I'm feeling lucky!" disabled>
+ <link rel="icon" href="/logo.svg">
+ </form>
+</body>
+</html>
diff --git a/logo.old.svg b/logo.old.svg
new file mode 100644
index 0000000..4f397a4
--- /dev/null
+++ b/logo.old.svg
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="48"
+ height="48"
+ version="1.1"
+ id="svg61"
+ sodipodi:docname="logo.svg"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview63"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ showgrid="false"
+ inkscape:zoom="13"
+ inkscape:cx="23.961538"
+ inkscape:cy="24"
+ inkscape:window-width="1600"
+ inkscape:window-height="843"
+ inkscape:window-x="0"
+ inkscape:window-y="28"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="g37" />
+ <defs
+ id="defs29">
+ <linearGradient
+ id="a"
+ y1="37.11"
+ x1="26.425"
+ y2="24"
+ x2="44"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ stop-color="#2980b9"
+ id="stop2"
+ style="stop-color:#2a7fff;stop-opacity:1" />
+ <stop
+ offset="1"
+ stop-color="#1d99f3"
+ id="stop4"
+ style="stop-color:#2a7fff;stop-opacity:1" />
+ </linearGradient>
+ <linearGradient
+ xlink:href="#a"
+ id="b"
+ y1="39.24"
+ x1="25.869"
+ y2="10.624"
+ x2="24.539"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ id="c"
+ y1="9.386"
+ x1="8.84"
+ y2="39.34"
+ x2="37.922"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ stop-color="#faf8f8"
+ id="stop8" />
+ <stop
+ offset="1"
+ stop-color="#bfd2d2"
+ id="stop10" />
+ </linearGradient>
+ <linearGradient
+ id="d"
+ y1="16.621"
+ x1="27.04"
+ y2="17.991"
+ x2="24.839"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ stop-color="#fcfcfc"
+ id="stop13" />
+ <stop
+ offset="1"
+ stop-color="#c7c7c7"
+ id="stop15" />
+ </linearGradient>
+ <linearGradient
+ id="e"
+ y1="27.6"
+ x1="31.537"
+ y2="31"
+ x2="35.973"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ id="stop18" />
+ <stop
+ offset="1"
+ stop-opacity="0"
+ id="stop20" />
+ </linearGradient>
+ <linearGradient
+ xlink:href="#e"
+ id="f"
+ y1="19"
+ x1="26"
+ y2="30"
+ x2="37"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(-10,5)" />
+ <linearGradient
+ id="g"
+ y1="23.644"
+ x1="3.783"
+ y2="22.495"
+ x2="19.574"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ stop-color="#2980b9"
+ id="stop24" />
+ <stop
+ offset="1"
+ stop-color="#34495e"
+ id="stop26" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#e"
+ id="linearGradient1776"
+ gradientUnits="userSpaceOnUse"
+ x1="31.537"
+ y1="27.6"
+ x2="35.973"
+ y2="31"
+ gradientTransform="matrix(-1,0,0,1,48.051543,1.5392455)" />
+ </defs>
+ <g
+ stroke-linejoin="round"
+ color="#000000"
+ stroke-width="3"
+ id="g37">
+ <path
+ fill="url(#a)"
+ d="M 24,4 C 12.95,4 4,12.954 4,24 4,35.05 12.954,44 24,44 35.05,44 44,35.046 44,24 44,12.95 35.046,4 24,4"
+ id="path31"
+ style="fill:#2c5aa0" />
+ <circle
+ fill="url(#c)"
+ cx="24.5"
+ cy="23.5"
+ r="18.5"
+ stroke-linecap="round"
+ id="circle33"
+ style="fill:#d7e3f4" />
+ <path
+ opacity="0.2"
+ fill="url(#e)"
+ d="m 8.0415429,14.963246 26.8670001,24.652 a 18.5,18.5 0 0 1 -11.357422,3.923828 18.5,18.5 0 0 1 -18.5000001,-18.5 18.5,18.5 0 0 1 2.990234,-10.07617"
+ id="path35"
+ style="fill:url(#linearGradient1776)" />
+ </g>
+ <g
+ fill-rule="evenodd"
+ id="g57"
+ transform="matrix(-1,0,0,1,47.562,0)">
+ <path
+ fill="url(#b)"
+ d="m 39.955,11.264 c -0.181,-0.015 -0.374,-0.023 -0.59,0.01 -1.724,0.265 -5.645,1.724 -6.883,2.033 -1.237,0.309 -1.817,0.586 -3.801,0.398 -2.262,-0.214 -3.278,-0.228 -3.873,-0.211 -0.891,-0.079 -1.34,-0.275 -1.34,-0.275 l 0.078,0.877 c -0.592,0.452 -1.785,1.11 -4.234,1.775 -1.591,0.177 -2.917,-0.309 -3.713,0.133 -0.795,0.442 -3.314,1.546 -4.596,1.855 -1.282,0.309 -2.231,0.889 -3,1.141 -1.221,0.398 2.42,6.233 4,7 0,0 -0.547,0.568 -1,1.043 -1.099,0.751 -1.965,0.801 -1.965,0.801 l 0.055,0.602 c -1.276,0.678 -2.54,0.995 -2.961,1.055 -0.107,0.015 -0.524,0.287 -1.129,0.715 2.615,8 10.13,13.785 19,13.785 1.802,0 3.539,-0.26 5.199,-0.707 0.167,-1.087 0.393,-2.627 0.533,-4.084 0.434,-0.638 1.018,-0.646 1.018,-0.646 0,0 -0.75,-2.125 -0.676,-4.684 0.093,-0.607 0.251,-1.179 0.451,-1.723 h 0.756 c 0,0 -0.095,-1.044 0.332,-2.105 1.405,-2.136 3.322,-3.563 4.141,-4.191 0.772,-0.593 4.331,-1.503 5.082,-1.68 0.751,-0.177 1.458,-1.371 1.502,-1.725 C 42.385,22.102 42.12,21.44 42.12,21.44 l -1.988,-0.441 -2.406,-0.271 c 0,0 3.303,-4.894 3.898,-5.461 0.631,-0.601 0.938,-1.34 0.938,-2.047 0,-0.707 -0.52,-1.658 -0.52,-1.658 0,0 -0.597,-0.303 -1.572,-0.266 -0.163,0.006 -0.331,-0.016 -0.512,-0.031"
+ id="path39"
+ style="fill:url(#b)" />
+ <path
+ opacity="0.2"
+ fill="url(#f)"
+ d="m 12,26 c 0,0 -0.547,0.568 -1,1.043 -1.099,0.751 -1.965,0.801 -1.965,0.801 L 9.09,28.446 C 8.302,28.865 7.544,29.132 6.961,29.3 L 6.113,29.502 5,30.22 c 2.588,7.941 9.919,13.705 18.84,13.777 l 3.164,-8.996 -8,-8 c 0,0 -1.309,0.429 -3.631,0.028 -2.321,-0.401 -3.369,-1.028 -3.369,-1.028"
+ id="path41"
+ style="fill:url(#f)" />
+ <path
+ fill="#2c3e50"
+ d="m 42.25,12 c 0,0 -0.313,-0.281 -0.656,-0.313 -0.344,-0.031 -1.063,0.094 -1.281,0.063 -0.219,-0.031 -0.969,-0.469 -0.969,-0.469 0,0 1,-0.563 1.344,-0.625 0.344,-0.063 0.938,-0.156 0.938,-0.156 0,0 0.25,0.313 0.438,0.75 0.188,0.438 0.188,0.75 0.188,0.75"
+ id="path43" />
+ <path
+ fill="#fcfcfc"
+ d="m 40.987,21.23 c -0.675,0.09 -0.805,0.015 -0.805,0.015 0,0 0.156,-0.707 0.325,-0.767 0.169,-0.06 0.662,0.707 0.48,0.752"
+ id="path45" />
+ <path
+ fill="#2c3e50"
+ d="m 41.906,14.969 c 0,0 -2.219,1.063 -3.281,2.719 -1.063,1.656 -1.813,3.344 -1.813,3.344 0,0 0.75,0.156 1.438,0.406 0.688,0.25 1.938,0.313 2.469,0.219 0.531,-0.094 0.688,-0.344 0.156,-0.563 -0.531,-0.219 -1.313,-0.375 -1.344,-0.844 -0.031,-0.469 0.969,-3.188 1.25,-3.625 0.281,-0.438 1.125,-1.656 1.125,-1.656"
+ id="path47" />
+ <path
+ fill="#34495e"
+ d="m 22.438,20.375 c 0,0 2.749,-0.414 3.796,-1.04 3.414,-2.04 3.298,-3.179 3.298,-3.179 0,0 -3.611,-1.445 -6.108,1.534 -1.045,1.247 -0.986,2.685 -0.986,2.685"
+ id="path49" />
+ <path
+ fill="url(#d)"
+ d="m 23.467,19.14 c 0,0 2.74,-0.552 3.757,-1.304 1.016,-0.751 1.436,-1.149 1.304,-1.414 -0.133,-0.265 -1.127,-0.663 -2.364,-0.354 -1.237,0.309 -2.121,1.193 -2.364,1.746 -0.243,0.552 -0.486,1.149 -0.331,1.326"
+ id="path51"
+ style="fill:url(#d)" />
+ <path
+ fill="#fcfcfc"
+ d="m 38.956,21.676 c 1.01,0.116 1.2,0.019 1.2,0.019 0,0 -0.232,-0.91 -0.484,-0.987 -0.252,-0.077 -0.987,0.91 -0.716,0.968"
+ id="path53" />
+ <path
+ fill="url(#g)"
+ d="m 12.298,24.795 c 1.01,0.774 3.56,1.23 4.811,1.048 C 20.06,25.412 20.212,25.633 19.5,23.75 18.986,22.391 16,21 14,23 l -1,-1 c 0.593,-0.854 -0.5,-2.844 -1,-3 -0.5,-0.156 -2.859,0.486 -3.078,0.705 -0.219,0.219 2.369,4.317 3.376,5.091"
+ id="path55"
+ style="fill:url(#g)" />
+ </g>
+ <path
+ fill="#256592"
+ stroke-linejoin="round"
+ color="#000000"
+ stroke-width="3"
+ d="m4.01 23.5c-.004.167-.012.332-.012.5 0 11.05 8.954 20 20 20 11.05 0 20-8.954 20-20 0-.168-.008-.333-.012-.5-.266 10.814-9.11 19.5-19.988 19.5-10.878 0-19.722-8.686-19.988-19.5"
+ id="path59"
+ style="fill:#3771c8" />
+</svg>
diff --git a/logo.svg b/logo.svg
new file mode 100644
index 0000000..c631fcf
--- /dev/null
+++ b/logo.svg
@@ -0,0 +1,490 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ id="svg5"
+ version="1.1"
+ viewBox="0 0 186.72737 186.72737"
+ height="186.72737mm"
+ width="186.72737mm"
+ sodipodi:docname="wolfeye.svg"
+ inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview
+ id="namedview7"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:document-units="mm"
+ showgrid="false"
+ inkscape:snap-global="true"
+ inkscape:snap-midpoints="true"
+ inkscape:object-paths="false"
+ inkscape:zoom="0.66449955"
+ inkscape:cx="262.60364"
+ inkscape:cy="270.88054"
+ inkscape:window-width="1600"
+ inkscape:window-height="843"
+ inkscape:window-x="0"
+ inkscape:window-y="28"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer1"
+ inkscape:snap-smooth-nodes="false"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-center="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:snap-nodes="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+ <defs
+ id="defs2">
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient41767">
+ <stop
+ style="stop-color:#fdfdfd;stop-opacity:1"
+ offset="0"
+ id="stop41763" />
+ <stop
+ style="stop-color:#fefefe;stop-opacity:0.99537921"
+ offset="0.57969123"
+ id="stop42511" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0"
+ offset="1"
+ id="stop41765" />
+ </linearGradient>
+ <inkscape:path-effect
+ effect="skeletal"
+ id="path-effect28702"
+ is_visible="true"
+ lpeversion="1"
+ pattern="M 0,4.9921382 C 0,2.2364779 2.2364779,0 4.9921382,0 c 2.7556604,0 4.9921383,2.2364779 4.9921383,4.9921382 0,2.7556604 -2.2364779,4.9921383 -4.9921383,4.9921383 C 2.2364779,9.9842765 0,7.7477986 0,4.9921382 Z"
+ copytype="single_stretched"
+ prop_scale="0.15"
+ scale_y_rel="false"
+ spacing="0"
+ normal_offset="0"
+ tang_offset="0"
+ prop_units="false"
+ vertical_pattern="false"
+ hide_knot="false"
+ fuse_tolerance="0" />
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient1156">
+ <stop
+ style="stop-color:#000000;stop-opacity:0.01006936"
+ offset="0"
+ id="stop1152" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.61836153"
+ offset="0.39606667"
+ id="stop1388" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1"
+ offset="1"
+ id="stop1154" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1156"
+ id="radialGradient1288-3"
+ cx="368.5625"
+ cy="465.37695"
+ fx="368.5625"
+ fy="465.37695"
+ r="59.570309"
+ gradientTransform="matrix(0.36532143,0.1220609,-0.1220609,0.36532143,20.107527,-94.521206)"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient41767"
+ id="radialGradient41771"
+ cx="918.8197"
+ cy="182.40836"
+ fx="918.8197"
+ fy="182.40836"
+ r="27.379515"
+ gradientTransform="matrix(2.0398076,0,0,2.0398079,-1794.9432,-189.12648)"
+ gradientUnits="userSpaceOnUse" />
+ </defs>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(19.987232,-89.044647)">
+ <path
+ style="fill:#000000;stroke:none;stroke-width:0.6;stroke-linejoin:round;stop-color:#000000"
+ d="m 76.550151,119.94169 c -12.25555,0.1456 -24.358803,2.65775 -36.066053,6.14998 -12.88531,3.72092 -26.24278,6.08585 -39.68453014,6.03752 -4.34856996,0.0126 -8.70941996,-0.12945 -13.04434986,0.29883 -1.15353,0.73748 -1.15752,2.51167 -0.32721,3.49423 0.75318,0.8448 1.23349,1.87768 1.84199,2.82328 5.8095299,9.5645 12.0888099,18.90403 16.6947599,29.13753 4.2925301,9.68272 6.6849801,20.09718 11.2006201,29.68949 1.78031,3.72279 3.96359,7.23789 6.25123,10.66677 3.10158,4.56683 6.44327,8.97121 9.98928,13.20194 2.18204,2.58705 4.52462,5.06461 7.24052,7.10002 5.47928,4.21758 11.90487,7.08425 18.56126,8.86874 9.873463,2.86822 19.946393,5.3394 30.243373,5.9611 5.29244,0.34311 10.610189,0.15491 15.879539,-0.42256 8.91284,-0.82683 17.84679,-1.95498 26.81578,-1.53821 8.36682,0.24126 16.67241,1.44807 25.03803,1.70277 1.05754,-0.0196 2.27594,-0.66406 2.40717,-1.81011 0.22407,-1.47691 -0.61661,-2.80785 -1.02433,-4.17333 -2.56092,-7.25134 -5.04894,-14.53047 -7.32971,-21.87524 -1.58874,-5.25936 -2.86377,-10.62148 -3.61155,-16.06792 -1.39074,-8.8794 -2.93329,-17.79562 -5.93355,-26.29653 -1.63402,-4.91495 -3.28683,-9.93451 -6.30162,-14.20643 -3.9892,-5.57941 -8.84651,-10.46515 -13.83,-15.15103 -7.58076,-7.02992 -15.85324,-13.33107 -24.737509,-18.62558 -4.95129,-2.81338 -10.56414,-4.38274 -16.22702,-4.83784 -1.34559,-0.1081 -2.69646,-0.14697 -4.04612,-0.12742 z"
+ id="path43327"
+ sodipodi:nodetypes="ccccccccccccccccccccccscccscsc"
+ inkscape:transform-center-x="7.0706594"
+ inkscape:transform-center-y="-4.7694391"
+ inkscape:export-filename="/home/gus/path43327.png"
+ inkscape:export-xdpi="80"
+ inkscape:export-ydpi="80" />
+ <g
+ inkscape:label="WireframeSphere"
+ transform="matrix(2.3659185,0,0,2.3659185,79.272161,182.9516)"
+ id="g39892"
+ style="stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <g
+ inkscape:label="Lines of Longitude"
+ id="g39874"
+ style="stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="9.6288042"
+ sodipodi:ry="26.458334"
+ sodipodi:start="1.5707963"
+ sodipodi:end="4.712389"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(14.6967)"
+ id="path39862"
+ d="m 0,26.458334 a 9.6288042,26.458334 0 0 1 -8.338789,-13.229167 9.6288042,26.458334 0 0 1 0,-26.458334 A 9.6288042,26.458334 0 0 1 0,-26.458334" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="17.349705"
+ sodipodi:ry="26.458334"
+ sodipodi:start="1.5707963"
+ sodipodi:end="4.712389"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(34.3313)"
+ id="path39864"
+ d="m 0,26.458334 a 17.349705,26.458334 0 0 1 -15.025285,-13.229167 17.349705,26.458334 0 0 1 0,-26.458334 A 17.349705,26.458334 0 0 1 0,-26.458334" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="21.63448"
+ sodipodi:ry="26.458334"
+ sodipodi:start="1.5707963"
+ sodipodi:end="4.712389"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(67.2628)"
+ id="path39866"
+ d="m 0,26.458334 a 21.63448,26.458334 0 0 1 -18.736009,-13.229167 21.63448,26.458334 0 0 1 0,-26.458334 A 21.63448,26.458334 0 0 1 0,-26.458334" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="21.63448"
+ sodipodi:ry="26.458334"
+ sodipodi:start="4.712389"
+ sodipodi:end="1.5707963"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(-67.2628)"
+ id="path39868"
+ d="M 0,-26.458334 A 21.63448,26.458334 0 0 1 21.63448,0 21.63448,26.458334 0 0 1 0,26.458334" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="17.349705"
+ sodipodi:ry="26.458334"
+ sodipodi:start="4.712389"
+ sodipodi:end="1.5707963"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(-34.3313)"
+ id="path39870"
+ d="M 0,-26.458334 A 17.349705,26.458334 0 0 1 17.349705,0 17.349705,26.458334 0 0 1 0,26.458334" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="9.6288042"
+ sodipodi:ry="26.458334"
+ sodipodi:start="4.712389"
+ sodipodi:end="1.5707963"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(-14.6967)"
+ id="path39872"
+ d="M 0,-26.458334 A 9.6288042,26.458334 0 0 1 9.6288042,0 9.6288042,26.458334 0 0 1 0,26.458334" />
+ </g>
+ <g
+ inkscape:label="Lines of Latitude"
+ id="g39886"
+ style="stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="13.83513"
+ sodipodi:rx="20.685959"
+ sodipodi:ry="11.26738"
+ sodipodi:start="0.54437703"
+ sodipodi:end="2.5972156"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ id="path39876"
+ d="M 17.695805,19.67034 A 20.685959,11.26738 0 0 1 0,25.102509 20.685959,11.26738 0 0 1 -17.695805,19.67034" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="4.9377007"
+ sodipodi:rx="25.794968"
+ sodipodi:ry="14.049946"
+ sodipodi:start="0.14877123"
+ sodipodi:end="2.9928214"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ id="path39878"
+ d="M 25.510035,7.0202265 A 25.794968,14.049946 0 0 1 0,18.987647 25.794968,14.049946 0 0 1 -25.510035,7.0202269" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="-4.9377007"
+ sodipodi:rx="25.794968"
+ sodipodi:ry="14.049946"
+ sodipodi:start="6.1344141"
+ sodipodi:end="3.2903639"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ id="path39880"
+ d="M 25.510035,-7.0202262 A 25.794968,14.049946 0 0 1 13.988979,6.8667226 25.794968,14.049946 0 0 1 -13.98898,6.8667223 25.794968,14.049946 0 0 1 -25.510035,-7.0202268" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="-13.83513"
+ sodipodi:rx="20.685959"
+ sodipodi:ry="11.26738"
+ sodipodi:start="5.7388083"
+ sodipodi:end="3.6859697"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ id="path39882"
+ d="m 17.695805,-19.67034 a 20.685959,11.26738 0 0 1 -4.289694,14.4161551 20.685959,11.26738 0 0 1 -26.812223,-3e-7 20.685959,11.26738 0 0 1 -4.289693,-14.4161548" />
+ <ellipse
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="ellipse39884"
+ cx="0"
+ cy="-19.992342"
+ rx="11.47984"
+ ry="6.2533693" />
+ </g>
+ <circle
+ style="fill:none;stroke:#000000;stroke-width:3.04322;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="circle39888"
+ cx="0"
+ cy="0"
+ r="26.458334" />
+ <path
+ style="font-variation-settings:normal;opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:3.04322;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+ d="m 0,-26.458336 v 52.91667"
+ id="path39890" />
+ </g>
+ <g
+ inkscape:label="WireframeSphere"
+ transform="matrix(2.3659185,0,0,2.3659185,79.272161,182.9516)"
+ id="g38571"
+ style="stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <g
+ inkscape:label="Lines of Longitude"
+ id="g38555"
+ style="stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="9.6288042"
+ sodipodi:ry="26.458334"
+ sodipodi:start="1.5707963"
+ sodipodi:end="4.712389"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(14.6967)"
+ id="path38543"
+ d="m 0,26.458334 a 9.6288042,26.458334 0 0 1 -8.338789,-13.229167 9.6288042,26.458334 0 0 1 0,-26.458334 A 9.6288042,26.458334 0 0 1 0,-26.458334" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="17.349705"
+ sodipodi:ry="26.458334"
+ sodipodi:start="1.5707963"
+ sodipodi:end="4.712389"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(34.3313)"
+ id="path38545"
+ d="m 0,26.458334 a 17.349705,26.458334 0 0 1 -15.025285,-13.229167 17.349705,26.458334 0 0 1 0,-26.458334 A 17.349705,26.458334 0 0 1 0,-26.458334" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="21.63448"
+ sodipodi:ry="26.458334"
+ sodipodi:start="1.5707963"
+ sodipodi:end="4.712389"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(67.2628)"
+ id="path38547"
+ d="m 0,26.458334 a 21.63448,26.458334 0 0 1 -18.736009,-13.229167 21.63448,26.458334 0 0 1 0,-26.458334 A 21.63448,26.458334 0 0 1 0,-26.458334" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="21.63448"
+ sodipodi:ry="26.458334"
+ sodipodi:start="4.712389"
+ sodipodi:end="1.5707963"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(-67.2628)"
+ id="path38549"
+ d="M 0,-26.458334 A 21.63448,26.458334 0 0 1 21.63448,0 21.63448,26.458334 0 0 1 0,26.458334" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="17.349705"
+ sodipodi:ry="26.458334"
+ sodipodi:start="4.712389"
+ sodipodi:end="1.5707963"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(-34.3313)"
+ id="path38551"
+ d="M 0,-26.458334 A 17.349705,26.458334 0 0 1 17.349705,0 17.349705,26.458334 0 0 1 0,26.458334" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="0"
+ sodipodi:rx="9.6288042"
+ sodipodi:ry="26.458334"
+ sodipodi:start="4.712389"
+ sodipodi:end="1.5707963"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ transform="rotate(-14.6967)"
+ id="path38553"
+ d="M 0,-26.458334 A 9.6288042,26.458334 0 0 1 9.6288042,0 9.6288042,26.458334 0 0 1 0,26.458334" />
+ </g>
+ <g
+ inkscape:label="Lines of Latitude"
+ id="g38567"
+ style="stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1">
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="13.83513"
+ sodipodi:rx="20.685959"
+ sodipodi:ry="11.26738"
+ sodipodi:start="0.54437703"
+ sodipodi:end="2.5972156"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ id="path38557"
+ d="M 17.695805,19.67034 A 20.685959,11.26738 0 0 1 0,25.102509 20.685959,11.26738 0 0 1 -17.695805,19.67034" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="4.9377007"
+ sodipodi:rx="25.794968"
+ sodipodi:ry="14.049946"
+ sodipodi:start="0.14877123"
+ sodipodi:end="2.9928214"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ id="path38559"
+ d="M 25.510035,7.0202265 A 25.794968,14.049946 0 0 1 0,18.987647 25.794968,14.049946 0 0 1 -25.510035,7.0202269" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="-4.9377007"
+ sodipodi:rx="25.794968"
+ sodipodi:ry="14.049946"
+ sodipodi:start="6.1344141"
+ sodipodi:end="3.2903639"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ id="path38561"
+ d="M 25.510035,-7.0202262 A 25.794968,14.049946 0 0 1 13.988979,6.8667226 25.794968,14.049946 0 0 1 -13.98898,6.8667223 25.794968,14.049946 0 0 1 -25.510035,-7.0202268" />
+ <path
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ sodipodi:cx="0"
+ sodipodi:cy="-13.83513"
+ sodipodi:rx="20.685959"
+ sodipodi:ry="11.26738"
+ sodipodi:start="5.7388083"
+ sodipodi:end="3.6859697"
+ sodipodi:open="true"
+ sodipodi:type="arc"
+ sodipodi:arc-type="arc"
+ id="path38563"
+ d="m 17.695805,-19.67034 a 20.685959,11.26738 0 0 1 -4.289694,14.4161551 20.685959,11.26738 0 0 1 -26.812223,-3e-7 20.685959,11.26738 0 0 1 -4.289693,-14.4161548" />
+ <ellipse
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path38565"
+ cx="0"
+ cy="-19.992342"
+ rx="11.47984"
+ ry="6.2533693" />
+ </g>
+ <circle
+ style="fill:none;stroke:#ffffff;stroke-width:0.718538;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path38569"
+ cx="0"
+ cy="0"
+ r="26.458334" />
+ <path
+ style="font-variation-settings:normal;opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.718538;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000;stop-opacity:1"
+ d="m 0,-26.458336 v 52.91667"
+ id="path38920" />
+ </g>
+ <path
+ style="fill:url(#radialGradient41771);fill-opacity:1;stroke:none;stroke-width:0.6;stroke-linejoin:round;stop-color:#000000"
+ d="m 93.182621,125.87502 c 31.523039,7.68177 50.849539,39.46432 43.166099,70.98697 -7.68172,31.52307 -39.464289,50.84961 -70.986959,43.16619 -31.523073,-7.68175 -50.849583,-39.46432 -43.166133,-70.98698 7.68174,-31.52309 39.464313,-50.84963 70.986993,-43.16618 z"
+ id="path41731"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ id="path111477"
+ style="opacity:1;fill-opacity:0.99702;stroke-width:7.2;stroke-linejoin:round;stop-color:#000000;stop-opacity:1"
+ d="M 106.70102,182.95159 A 27.428835,27.428835 0 0 1 79.272191,210.38043 27.428835,27.428835 0 0 1 51.843352,182.95159 27.428835,27.428835 0 0 1 79.272191,155.52276 27.428835,27.428835 0 0 1 106.70102,182.95159 Z" />
+ <path
+ id="rect111785"
+ style="display:inline;opacity:1;fill:#ffffff;fill-opacity:0.995379;stroke-width:7.2;stroke-linejoin:round;stop-color:#000000;stop-opacity:1"
+ d="m 75.209181,164.37573 c 2.41771,-0.77663 5.73024,4.61075 4.73541,5.96762 -3.18223,4.34033 -13.8433,13.49237 -15.07434,12.22724 l -5.474483,-5.62611 c -2.02361,-2.07969 9.402763,-10.50938 15.813413,-12.56875 z"
+ sodipodi:nodetypes="sssss" />
+ </g>
+</svg>
diff --git a/out/index.php b/out/index.php
new file mode 100644
index 0000000..cefe36b
--- /dev/null
+++ b/out/index.php
@@ -0,0 +1,42 @@
+<?php
+
+if (!isset($_GET['q']) || !isset($_GET['u']) || !isset($_GET['i'])) {
+ header("Location: /");
+ die();
+}
+
+$_GET['q'] = strtolower(substr($_GET['q'], 0, 200));
+$_GET['q'] = preg_replace("/[^A-Za-z0-9 ]/", '', preg_replace("/[\.]/", ' ', $_GET['q']));
+
+$data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/private/db.json"), true);
+
+if ($_GET['i'] !== "_off" && isset($data["entries"][$_GET['i']])) {
+ header("Location: " . $data["entries"][$_GET['i']]["url"]);
+ die();
+} elseif ($_GET['i'] === "_off") {
+ $domains = [];
+
+ foreach ($data["entries"] as $id => $entry) {
+ $domain = explode("/", $entry["url"])[2];
+ $sim = similar_text($domain, $_GET['q'], $perc);
+ if (substr($domain, 0, 4) === "www.") {
+ $domainstr = substr($domain, 4);
+ } else {
+ $domainstr = $domain;
+ }
+ $domains[] = [
+ "id" => $id,
+ "url" => "https://" . $domain . "/",
+ "domain" => $domainstr,
+ "relevance" => $perc / 50 - 1
+ ];
+ }
+
+ usort($domains, function($a, $b) {
+ return $a['relevance'] <=> $b['relevance'];
+ });
+
+ $domains = array_reverse($domains);
+ header("Location: " . $domains[0]['url']);
+ die();
+} \ No newline at end of file
diff --git a/private/instants/kartik b/private/instants/kartik
new file mode 100644
index 0000000..5f52c0f
--- /dev/null
+++ b/private/instants/kartik
@@ -0,0 +1,34 @@
+{
+ "Abstract" : "Kartik is an online car racing game developed by Minteck in early 2021. The game is made of its own engine based on GitHub's Electron; which use have caused major problems during the development; Minteck said on her blog that she doesn't even know how to properly use Electron. The game releases on June 12th 2021, and with bug fixing updates until October 1st 2021. Kartik was originally supposed to be released under a proprietary license, but was made open source later in the development as per the JetBrains License Agreement.",
+ "AbstractSource" : "Minteck",
+ "AbstractText" : "",
+ "AbstractURL" : "/search?q=minteck",
+ "Answer" : "",
+ "AnswerType" : "",
+ "Definition" : "",
+ "DefinitionSource" : "",
+ "DefinitionURL" : "",
+ "Entity" : "infobox",
+ "Heading" : "Kartik",
+ "Image" : "/i/kartik.minteck.org.ico",
+ "ImageHeight" : 0,
+ "ImageIsLogo" : 1,
+ "ImageWidth" : 0,
+ "Infobox" : {},
+ "Redirect" : "",
+ "RelatedTopics" : [],
+ "Results" : [
+ {
+ "FirstURL" : "https://kartik.minteck.org/home/",
+ "Icon" : {
+ "Height" : 0,
+ "URL" : "/i/kartik.minteck.org.ico",
+ "Width" : 0
+ },
+ "Result" : null,
+ "Text" : "Official site"
+ }
+ ],
+ "Type" : "A",
+ "meta" : {}
+}
diff --git a/private/instants/minteck b/private/instants/minteck
new file mode 100644
index 0000000..bdf23d1
--- /dev/null
+++ b/private/instants/minteck
@@ -0,0 +1,34 @@
+{
+ "Abstract" : "Mia (also known as Minteck) is a French Web developer; developing her own software since 2018 and contributing to open source projects since 2019. She is mainly known for games like Kartik or Foxperson, and for the content management system Neutron. Since 2021, all software she makes is free libre and open source as per her JetBrains License Agreement.",
+ "AbstractSource" : "Minteck",
+ "AbstractText" : "",
+ "AbstractURL" : "/search?q=minteck",
+ "Answer" : "",
+ "AnswerType" : "",
+ "Definition" : "",
+ "DefinitionSource" : "",
+ "DefinitionURL" : "",
+ "Entity" : "infobox",
+ "Heading" : "Minteck",
+ "Image" : "/i/staging.minteck.org.ico",
+ "ImageHeight" : 0,
+ "ImageIsLogo" : 1,
+ "ImageWidth" : 0,
+ "Infobox" : {},
+ "Redirect" : "",
+ "RelatedTopics" : [],
+ "Results" : [
+ {
+ "FirstURL" : "https://minteck.org/",
+ "Icon" : {
+ "Height" : 0,
+ "URL" : "/i/staging.minteck.org.ico",
+ "Width" : 0
+ },
+ "Result" : null,
+ "Text" : "Official site"
+ }
+ ],
+ "Type" : "A",
+ "meta" : {}
+}
diff --git a/ratelimited/instant.php b/ratelimited/instant.php
new file mode 100644
index 0000000..bfbb56f
--- /dev/null
+++ b/ratelimited/instant.php
@@ -0,0 +1,82 @@
+<?php
+
+if (isset($_GET['q'])) {
+ $query = strtolower(substr($_GET['q'], 0, 200));
+ $query = preg_replace("/[^A-Za-z0-9 ]/", '', preg_replace("/[\.]/", ' ', $query));
+} else {
+ header("Location: /");
+ die();
+}
+
+$addr = sha1($_SERVER['REMOTE_ADDR']);
+$cacheOnly = false;
+$rl = [
+ "local" => -1,
+ "global" => -1,
+ "blocked" => false
+];
+
+if (!file_exists($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/" . $addr)) {
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/" . $addr, "1|" . date('YmdHi'));
+} else {
+ $requests = (int)explode("|", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/" . $addr))[0];
+ $date = explode("|", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/" . $addr))[1];
+ if ($date === date('YmdHi')) {
+ $requests++;
+ if ($requests > 3) {
+ $cacheOnly = true;
+ $rl["blocked"] = true;
+ }
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/" . $addr, $requests . "|" . date('YmdHi'));
+ } else {
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/" . $addr, "1|" . date('YmdHi'));
+ }
+
+ $rl["local"] = $requests;
+}
+
+if (!file_exists($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/_global")) {
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/_global", "1|" . date('YmdHi'));
+} else {
+ $requests = (int)explode("|", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/_global"))[0];
+ $date = explode("|", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/_global"))[1];
+ if ($date === date('YmdHi')) {
+ $requests++;
+ if ($requests >= 20) {
+ $cacheOnly = true;
+ $rl["blocked"] = true;
+ }
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/_global", $requests . "|" . date('YmdHi'));
+ } else {
+ file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/private/ratelimiting/_global", "1|" . date('YmdHi'));
+ }
+
+ $rl["global"] = $requests;
+}
+
+header("Content-Type: application/json");
+
+if (file_exists($_SERVER["DOCUMENT_ROOT"] . "/private/instants/" . str_replace(" ", "_", $query))) {
+ $ds = "local";
+ $data = file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/private/instants/" . str_replace(" ", "_", $query));
+} else if (file_exists($_SERVER["DOCUMENT_ROOT"] . "/private/cache/instant_" . str_replace(" ", "_", $query))) {
+ $ds = "cache";
+ $data = file_get_contents($_SERVER["DOCUMENT_ROOT"] . "/private/cache/instant_" . str_replace(" ", "_", $query));
+} else {
+ if ($cacheOnly) {
+ die();
+ } else {
+ $ds = "online";
+ $data = file_get_contents("https://api.duckduckgo.com/?q=" . $query . "&format=json&pretty=1&t=Minteck-WolfEye_https://minteck.org&skip_disambig=1&no_redirect=1&no_html=1");
+ file_put_contents($_SERVER["DOCUMENT_ROOT"] . "/private/cache/instant_" . str_replace(" ", "_", $query), $data);
+ }
+}
+
+$parsed = json_decode($data, true);
+$parsed = array_reverse($parsed);
+$parsed["_WolfEye_DataSource"] = $ds;
+$parsed["_WolfEye_RateLimiting"] = $rl;
+$parsed["_WolfEye_External"] = $ds !== "local";
+$parsed = array_reverse($parsed);
+
+die(json_encode($parsed, JSON_PRETTY_PRINT)); \ No newline at end of file
diff --git a/search/index.php b/search/index.php
new file mode 100644
index 0000000..709a239
--- /dev/null
+++ b/search/index.php
@@ -0,0 +1,270 @@
+<?php
+
+$start = microtime(true);
+$start_io = microtime(true);
+$data = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/private/db.json"), true);
+$dict = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/private/dictionary.json"), true);
+
+if (isset($_GET['q'])) {
+ $query = strtolower(substr($_GET['q'], 0, 200));
+ $query = preg_replace("/[^A-Za-z0-9 ]/", '', preg_replace("/[\.]/", ' ', $query));
+} else {
+ header("Location: /");
+ die();
+}
+
+if (trim($query) === "") {
+ header("Location: /");
+ die();
+}
+$elapsed_io = microtime(true) - $start_io;
+
+?>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title><?= ucfirst($query) ?> on WolfEye</title>
+ <link rel="stylesheet" href="/assets/main.css">
+ <link rel="icon" href="/logo.svg">
+</head>
+<body>
+ <div id="results-intro">
+ <a href="/" id="results-intro-logo"><img id="results-intro-logo-img" src="/logo.svg"></a>
+ <form id="results-intro-bar" action="/search">
+ <input id="results-intro-bar-input" autocomplete="off" spellcheck="false" type="text" name="q" value="<?= $query ?>" placeholder="Search the Web...">
+ <input type="submit" value="Search" id="results-intro-bar-submit">
+ </form>
+ </div>
+
+ <div id="intro-results-separator"></div><?php
+
+ // Autocorrect
+ $start_correct = microtime(true);
+ $fixed = false;
+ $fixes = [];
+ $fixesd = [];
+
+ foreach (explode(" ", $query) as $item) {
+ $shortest = -1;
+ foreach ($dict as $word) {
+
+ $lev = levenshtein($item, $word);
+
+ if ($lev == 0) {
+ $closest = $word;
+ $shortest = 0;
+
+ break;
+ }
+
+ if ($lev <= $shortest || $shortest < 0) {
+ $closest = $word;
+ $shortest = $lev;
+ }
+ }
+
+ if ($shortest !== 0 && $shortest < 15 && $item !== "wolfeye" && $item !== "eye") {
+ $fixed = true;
+ $fixes[] = str_replace($item, $closest, $query);
+ $fixesd[] = str_replace($item, "<b>$closest</b>", $query);
+ }
+
+ if ($query === "google") {
+ $fixed = true;
+ $fixes[] = str_replace($item, "duckduckgo", $query);
+ $fixesd[] = str_replace($item, "<b>duckduckgo</b>", $query);
+ }
+
+ if ($query === "microsoft") {
+ $fixed = true;
+ $fixes[] = str_replace($item, "redhat", $query);
+ $fixesd[] = str_replace($item, "<b>redhat</b>", $query);
+ }
+
+ if ($query === "windows") {
+ $fixed = true;
+ $fixes[] = str_replace($item, "linux", $query);
+ $fixesd[] = str_replace($item, "<b>linux</b>", $query);
+ }
+
+ if ($query === "apple") {
+ $fixed = true;
+ $fixes[] = str_replace($item, "raspberry", $query);
+ $fixesd[] = str_replace($item, "<b>raspberry</b>", $query);
+ }
+
+ if ($query === "france") {
+ $fixed = true;
+ $fixes[] = str_replace($item, "hell", $query);
+ $fixesd[] = str_replace($item, "<b>hell</b>", $query);
+ }
+ }
+
+ if ($fixed) {
+ echo("<div class='dym-section'>");
+ if (count($fixes) > 1) {
+ echo("Showing results for \"$query\", did you mean:<ul>");
+ foreach ($fixes as $i => $fix) {
+ echo("<li>\"<a class='dym-link' href='/search?q=$fix'>$fixesd[$i]</a>\"?</li>");
+ }
+ echo("</ul>");
+ } else {
+ echo("Showing results for \"$query\", did you mean \"<a class='dym-link' href='/search?q=$fixes[0]'>$fixesd[0]</a>\"?");
+ }
+ echo("</div>");
+ }
+ $elapsed_correct = microtime(true) - $start_correct;
+
+ // Actual Search
+ $start_search = microtime(true);
+ $matches = [];
+ foreach ($data["entries"] as $id => $item) {
+ $avg = -1;
+ $aavg = [];
+ $iparsed = strtolower(substr($item["title"], 0, 200));
+ $iparsed = preg_replace("/[^A-Za-z0-9 ]/", '', preg_replace("/[\.]/", ' ', $iparsed));
+ $qwords = explode(" ", $query);
+
+ if (trim($iparsed) !== "") {
+ foreach ($qwords as $qword) {
+ $sim = similar_text($iparsed, $qword, $perc);
+ if ($perc > 0) {
+ $aavg[] = $perc;
+ }
+ }
+
+ if (count($aavg) > 0) {
+ $aavg = array_filter($aavg);
+ $avg = (array_sum($aavg) / count($aavg)) / 50 - 1;
+ $matches[] = [
+ "id" => $id,
+ "relevance" => $avg
+ ];
+ }
+ }
+
+ $avg = -1;
+ $aavg = [];
+ $iparsed = strtolower(substr($item["url"], 0, 200));
+ $iparsed = preg_replace("/[^A-Za-z0-9 ]/", '', preg_replace("/[\.]/", ' ', $iparsed));
+ $qwords = explode(" ", $query);
+
+ if (trim($iparsed) !== "") {
+ foreach ($qwords as $qword) {
+ $sim = similar_text($iparsed, $qword, $perc);
+ if ($perc > 0) {
+ $aavg[] = $perc;
+ }
+ }
+
+ if (count($aavg) > 0) {
+ $aavg = array_filter($aavg);
+ $avg = (array_sum($aavg) / count($aavg)) / 50 - 1;
+ $matches[] = [
+ "id" => $id,
+ "relevance" => $avg
+ ];
+ }
+ }
+ }
+
+ usort($matches, function($a, $b) {
+ return $a['relevance'] <=> $b['relevance'];
+ });
+
+ $matches = array_reverse($matches);
+
+ $matches2 = [];
+ $prel = [];
+
+ foreach($matches as $match) {
+ if (!isset($prel[(string)$match["relevance"]])) {
+ $prel[(string)$match["relevance"]] = [$match];
+ } else {
+ $prel[(string)$match["relevance"]][] = $match;
+ }
+ }
+
+ $elapsed_search = microtime(true) - $start_search;
+ $start_parse = microtime(true);
+ $entriesdom = [];
+ foreach($prel as $item => $items) {
+ $shortest = -1;
+
+ foreach ($items as $word) {
+ $lev = levenshtein($query, $data["entries"][$word["id"]]["title"]);
+
+ if ($lev == 0) {
+ $closest = $word["id"];
+ $shortest = 0;
+ break;
+ }
+
+ if ($lev <= $shortest || $shortest < 0) {
+ $closest = $word["id"];
+ $shortest = $lev;
+ }
+ }
+
+ $entriesdom[] = [
+ "link" => "/out?q=" . $query . "&u=" . uniqid() . "&i=" . $closest,
+ "title" => $data["entries"][$closest]["title"],
+ "url" => $data["entries"][$closest]["url"],
+ "icon" => "/icon?q=" . $query . "&i=" . $closest,
+ ];
+ }
+ $elapsed_parse = microtime(true) - $start_parse;
+ ?>
+ <p id="results-debug"><small id="results-debug-inner"><?= count($entriesdom) ?> results</small></p>
+
+ <div id="details"></div>
+ <script>query = "<?= $query ?>";</script>
+ <script src="/assets/instant.js"></script>
+
+ <div id="results">
+ <?php $start_official = microtime(true); $domains = []; foreach ($data["entries"] as $id => $entry): ?>
+ <?php $domain = explode("/", $entry["url"])[2];
+ $sim = similar_text($domain, $query, $perc);
+ if (substr($domain, 0, 4) === "www.") {
+ $domainstr = substr($domain, 4);
+ } else {
+ $domainstr = $domain;
+ }
+ $domains[] = [
+ "id" => $id,
+ "url" => "https://" . $domain . "/",
+ "domain" => $domainstr,
+ "relevance" => $perc / 50 - 1
+ ];
+ ?>
+ <?php endforeach;
+
+ usort($domains, function($a, $b) {
+ return $a['relevance'] <=> $b['relevance'];
+ });
+
+ $domains = array_reverse($domains); if ($domains[0]["relevance"] > 0): ?>
+ <div class="result result-official">
+ <a href="<?= $domains[0]["url"] ?>">
+ <div class="result-name" style="font-size: 18px;"><img alt="" class="result-icon" src="/icon/?q=<?= $query ?>&u=<?= uniqid() ?>&i=_off"> <b><?= ucfirst($domains[0]["domain"]) ?></b> <img class="result-relevant" src="/assets/relevant.svg" title="Official Website" alt=""></div>
+ <div class="result-url"><small><?= $domains[0]["url"] ?></small></div>
+ </a>
+ </div>
+ <?php endif; $elapsed_official = microtime(true) - $start_official; ?>
+ <?php $index = 0; foreach ($entriesdom as $entry): if ($index < 100): ?>
+ <div class="result">
+ <a href="<?= $entry["link"] ?>">
+ <div class="result-name" style="font-size: 18px;"><img alt="" class="result-icon" src="/icon/?q=<?= $entry["icon"] ?>"> <b><?= $entry["title"] ?></b></div>
+ <div class="result-url"><small><?= $entry["url"] ?></small></div>
+ </a>
+ </div>
+ <?php $index++; endif; endforeach; ?>
+ </div>
+
+ <?php $time_elapsed_secs = microtime(true) - $start; ?>
+ <script>
+ document.getElementById("results-debug-inner").innerHTML = document.getElementById("results-debug-inner").innerHTML + " in <?= round($time_elapsed_secs * 1000, 3) ?> ms<span id='results-debug-inner-details'> (io: <?= round($elapsed_io * 1000, 3) ?>; correct: <?= round($elapsed_correct * 1000, 3) ?>; search: <?= round($elapsed_search * 1000, 3) ?>; parse: <?= round($elapsed_parse * 1000, 3) ?>; official: <?= round($elapsed_official * 1000, 3) ?>)</span>";
+ </script>
+</body>
+</html>