pub / serci

Search the web with !keywords
git clone https://src.jayvii.de/pub/serci.git
Home | Log | Files | Exports | Refs | README | RSS

commit 05914a9a77ae363925c4db6eb44ded266b1c437e
Author: JayVii <jayvii[AT]posteo[DOT]de>
Date:   Sat,  6 Jul 2024 13:41:55 +0200

feat: initial version of traserci

Diffstat:
Aassets/css/simple.css | 711+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aassets/css/simple.min.css | 2++
Aassets/img/favicon.ico | 0
Aassets/img/favicon.png | 0
Aconfig/config.php | 221+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aindex.php | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aopensearch.xml | 8++++++++
7 files changed, 1090 insertions(+), 0 deletions(-)

diff --git a/assets/css/simple.css b/assets/css/simple.css @@ -0,0 +1,711 @@ +/* Global variables. */ +:root, +::backdrop { + /* Set sans-serif & mono fonts */ + --sans-font: -apple-system, BlinkMacSystemFont, "Avenir Next", Avenir, + "Nimbus Sans L", Roboto, "Noto Sans", "Segoe UI", Arial, Helvetica, + "Helvetica Neue", sans-serif; + --mono-font: Consolas, Menlo, Monaco, "Andale Mono", "Ubuntu Mono", monospace; + --standard-border-radius: 5px; + + /* Default (light) theme */ + --bg: #fff; + --accent-bg: #f5f7ff; + --text: #212121; + --text-light: #585858; + --border: #898EA4; + --accent: #0d47a1; + --accent-hover: #1266e2; + --accent-text: var(--bg); + --code: #d81b60; + --preformatted: #444; + --marked: #ffdd33; + --disabled: #efefef; +} + +/* Dark theme */ +@media (prefers-color-scheme: dark) { + :root, + ::backdrop { + color-scheme: dark; + --bg: #212121; + --accent-bg: #2b2b2b; + --text: #dcdcdc; + --text-light: #ababab; + --accent: #ffb300; + --accent-hover: #ffe099; + --accent-text: var(--bg); + --code: #f06292; + --preformatted: #ccc; + --disabled: #111; + } + /* Add a bit of transparency so light media isn't so glaring in dark mode */ + img, + video { + opacity: 0.8; + } +} + +/* Reset box-sizing */ +*, *::before, *::after { + box-sizing: border-box; +} + +/* Reset default appearance */ +textarea, +select, +input, +progress { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; +} + +html { + /* Set the font globally */ + font-family: var(--sans-font); + scroll-behavior: smooth; +} + +/* Make the body a nice central block */ +body { + color: var(--text); + background-color: var(--bg); + font-size: 1.15rem; + line-height: 1.5; + display: grid; + grid-template-columns: 1fr min(45rem, 90%) 1fr; + margin: 0; +} +body > * { + grid-column: 2; +} + +/* Make the header bg full width, but the content inline with body */ +body > header { + background-color: var(--accent-bg); + border-bottom: 1px solid var(--border); + text-align: center; + padding: 0 0.5rem 2rem 0.5rem; + grid-column: 1 / -1; +} + +body > header > *:only-child { + margin-block-start: 2rem; +} + +body > header h1 { + max-width: 1200px; + margin: 1rem auto; +} + +body > header p { + max-width: 40rem; + margin: 1rem auto; +} + +/* Add a little padding to ensure spacing is correct between content and header > nav */ +main { + padding-top: 1.5rem; +} + +body > footer { + margin-top: 4rem; + padding: 2rem 1rem 1.5rem 1rem; + color: var(--text-light); + font-size: 0.9rem; + text-align: center; + border-top: 1px solid var(--border); +} + +/* Format headers */ +h1 { + font-size: 3rem; +} + +h2 { + font-size: 2.6rem; + margin-top: 3rem; +} + +h3 { + font-size: 2rem; + margin-top: 3rem; +} + +h4 { + font-size: 1.44rem; +} + +h5 { + font-size: 1.15rem; +} + +h6 { + font-size: 0.96rem; +} + +p { + margin: 1.5rem 0; +} + +/* Prevent long strings from overflowing container */ +p, h1, h2, h3, h4, h5, h6 { + overflow-wrap: break-word; +} + +/* Fix line height when title wraps */ +h1, +h2, +h3 { + line-height: 1.1; +} + +/* Reduce header size on mobile */ +@media only screen and (max-width: 720px) { + h1 { + font-size: 2.5rem; + } + + h2 { + font-size: 2.1rem; + } + + h3 { + font-size: 1.75rem; + } + + h4 { + font-size: 1.25rem; + } +} + +/* Format links & buttons */ +a, +a:visited { + color: var(--accent); +} + +a:hover { + text-decoration: none; +} + +button, +.button, +a.button, /* extra specificity to override a */ +input[type="submit"], +input[type="reset"], +input[type="button"], +label[type="button"] { + border: 1px solid var(--accent); + background-color: var(--accent); + color: var(--accent-text); + padding: 0.5rem 0.9rem; + text-decoration: none; + line-height: normal; +} + +.button[aria-disabled="true"], +input:disabled, +textarea:disabled, +select:disabled, +button[disabled] { + cursor: not-allowed; + background-color: var(--disabled); + border-color: var(--disabled); + color: var(--text-light); +} + +input[type="range"] { + padding: 0; +} + +/* Set the cursor to '?' on an abbreviation and style the abbreviation to show that there is more information underneath */ +abbr[title] { + cursor: help; + text-decoration-line: underline; + text-decoration-style: dotted; +} + +button:enabled:hover, +.button:not([aria-disabled="true"]):hover, +input[type="submit"]:enabled:hover, +input[type="reset"]:enabled:hover, +input[type="button"]:enabled:hover, +label[type="button"]:hover { + background-color: var(--accent-hover); + border-color: var(--accent-hover); + cursor: pointer; +} + +.button:focus-visible, +button:focus-visible:where(:enabled), +input:enabled:focus-visible:where( + [type="submit"], + [type="reset"], + [type="button"] +) { + outline: 2px solid var(--accent); + outline-offset: 1px; +} + +/* Format navigation */ +header > nav { + font-size: 1rem; + line-height: 2; + padding: 1rem 0 0 0; +} + +/* Use flexbox to allow items to wrap, as needed */ +header > nav ul, +header > nav ol { + align-content: space-around; + align-items: center; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + list-style-type: none; + margin: 0; + padding: 0; +} + +/* List items are inline elements, make them behave more like blocks */ +header > nav ul li, +header > nav ol li { + display: inline-block; +} + +header > nav a, +header > nav a:visited { + margin: 0 0.5rem 1rem 0.5rem; + border: 1px solid var(--border); + border-radius: var(--standard-border-radius); + color: var(--text); + display: inline-block; + padding: 0.1rem 1rem; + text-decoration: none; +} + +header > nav a:hover, +header > nav a.current, +header > nav a[aria-current="page"] { + border-color: var(--accent); + color: var(--accent); + cursor: pointer; +} + +/* Reduce nav side on mobile */ +@media only screen and (max-width: 720px) { + header > nav a { + border: none; + padding: 0; + text-decoration: underline; + line-height: 1; + } +} + +/* Consolidate box styling */ +aside, details, pre, progress { + background-color: var(--accent-bg); + border: 1px solid var(--border); + border-radius: var(--standard-border-radius); + margin-bottom: 1rem; +} + +aside { + font-size: 1rem; + width: 30%; + padding: 0 15px; + margin-inline-start: 15px; + float: right; +} +*[dir="rtl"] aside { + float: left; +} + +/* Make aside full-width on mobile */ +@media only screen and (max-width: 720px) { + aside { + width: 100%; + float: none; + margin-inline-start: 0; + } +} + +article, fieldset, dialog { + border: 1px solid var(--border); + padding: 1rem; + border-radius: var(--standard-border-radius); + margin-bottom: 1rem; +} + +article h2:first-child, +section h2:first-child, +article h3:first-child, +section h3:first-child { + margin-top: 1rem; +} + +section { + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); + padding: 2rem 1rem; + margin: 3rem 0; +} + +/* Don't double separators when chaining sections */ +section + section, +section:first-child { + border-top: 0; + padding-top: 0; +} + +section + section { + margin-top: 0; +} + +section:last-child { + border-bottom: 0; + padding-bottom: 0; +} + +details { + padding: 0.7rem 1rem; +} + +summary { + cursor: pointer; + font-weight: bold; + padding: 0.7rem 1rem; + margin: -0.7rem -1rem; + word-break: break-all; +} + +details[open] > summary + * { + margin-top: 0; +} + +details[open] > summary { + margin-bottom: 0.5rem; +} + +details[open] > :last-child { + margin-bottom: 0; +} + +/* Format tables */ +table { + border-collapse: collapse; + margin: 1.5rem 0; +} + +figure > table { + width: max-content; + margin: 0; +} + +td, +th { + border: 1px solid var(--border); + text-align: start; + padding: 0.5rem; +} + +th { + background-color: var(--accent-bg); + font-weight: bold; +} + +tr:nth-child(even) { + /* Set every other cell slightly darker. Improves readability. */ + background-color: var(--accent-bg); +} + +table caption { + font-weight: bold; + margin-bottom: 0.5rem; +} + +/* Format forms */ +textarea, +select, +input, +button, +.button { + font-size: inherit; + font-family: inherit; + padding: 0.5rem; + margin-bottom: 0.5rem; + border-radius: var(--standard-border-radius); + box-shadow: none; + max-width: 100%; + display: inline-block; +} +textarea, +select, +input { + color: var(--text); + background-color: var(--bg); + border: 1px solid var(--border); +} +label { + display: block; +} +textarea:not([cols]) { + width: 100%; +} + +/* Add arrow to drop-down */ +select:not([multiple]) { + background-image: linear-gradient(45deg, transparent 49%, var(--text) 51%), + linear-gradient(135deg, var(--text) 51%, transparent 49%); + background-position: calc(100% - 15px), calc(100% - 10px); + background-size: 5px 5px, 5px 5px; + background-repeat: no-repeat; + padding-inline-end: 25px; +} +*[dir="rtl"] select:not([multiple]) { + background-position: 10px, 15px; +} + +/* checkbox and radio button style */ +input[type="checkbox"], +input[type="radio"] { + vertical-align: middle; + position: relative; + width: min-content; +} + +input[type="checkbox"] + label, +input[type="radio"] + label { + display: inline-block; +} + +input[type="radio"] { + border-radius: 100%; +} + +input[type="checkbox"]:checked, +input[type="radio"]:checked { + background-color: var(--accent); +} + +input[type="checkbox"]:checked::after { + /* Creates a rectangle with colored right and bottom borders which is rotated to look like a check mark */ + content: " "; + width: 0.18em; + height: 0.32em; + border-radius: 0; + position: absolute; + top: 0.05em; + left: 0.17em; + background-color: transparent; + border-right: solid var(--bg) 0.08em; + border-bottom: solid var(--bg) 0.08em; + font-size: 1.8em; + transform: rotate(45deg); +} +input[type="radio"]:checked::after { + /* creates a colored circle for the checked radio button */ + content: " "; + width: 0.25em; + height: 0.25em; + border-radius: 100%; + position: absolute; + top: 0.125em; + background-color: var(--bg); + left: 0.125em; + font-size: 32px; +} + +/* Makes input fields wider on smaller screens */ +@media only screen and (max-width: 720px) { + textarea, + select, + input { + width: 100%; + } +} + +/* Set a height for color input */ +input[type="color"] { + height: 2.5rem; + padding: 0.2rem; +} + +/* do not show border around file selector button */ +input[type="file"] { + border: 0; +} + +/* Misc body elements */ +hr { + border: none; + height: 1px; + background: var(--border); + margin: 1rem auto; +} + +mark { + padding: 2px 5px; + border-radius: var(--standard-border-radius); + background-color: var(--marked); + color: black; +} + +mark a { + color: #0d47a1; +} + +img, +video { + max-width: 100%; + height: auto; + border-radius: var(--standard-border-radius); +} + +figure { + margin: 0; + display: block; + overflow-x: auto; +} + +figure > img, +figure > picture > img { + display: block; + margin-inline: auto; +} + +figcaption { + text-align: center; + font-size: 0.9rem; + color: var(--text-light); + margin-block: 1rem; +} + +blockquote { + margin-inline-start: 2rem; + margin-inline-end: 0; + margin-block: 2rem; + padding: 0.4rem 0.8rem; + border-inline-start: 0.35rem solid var(--accent); + color: var(--text-light); + font-style: italic; +} + +cite { + font-size: 0.9rem; + color: var(--text-light); + font-style: normal; +} + +dt { + color: var(--text-light); +} + +/* Use mono font for code elements */ +code, +pre, +pre span, +kbd, +samp { + font-family: var(--mono-font); + color: var(--code); +} + +kbd { + color: var(--preformatted); + border: 1px solid var(--preformatted); + border-bottom: 3px solid var(--preformatted); + border-radius: var(--standard-border-radius); + padding: 0.1rem 0.4rem; +} + +pre { + padding: 1rem 1.4rem; + max-width: 100%; + overflow: auto; + color: var(--preformatted); +} + +/* Fix embedded code within pre */ +pre code { + color: var(--preformatted); + background: none; + margin: 0; + padding: 0; +} + +/* Progress bars */ +/* Declarations are repeated because you */ +/* cannot combine vendor-specific selectors */ +progress { + width: 100%; +} + +progress:indeterminate { + background-color: var(--accent-bg); +} + +progress::-webkit-progress-bar { + border-radius: var(--standard-border-radius); + background-color: var(--accent-bg); +} + +progress::-webkit-progress-value { + border-radius: var(--standard-border-radius); + background-color: var(--accent); +} + +progress::-moz-progress-bar { + border-radius: var(--standard-border-radius); + background-color: var(--accent); + transition-property: width; + transition-duration: 0.3s; +} + +progress:indeterminate::-moz-progress-bar { + background-color: var(--accent-bg); +} + +dialog { + max-width: 40rem; + margin: auto; +} + +dialog::backdrop { + background-color: var(--bg); + opacity: 0.8; +} + +@media only screen and (max-width: 720px) { + dialog { + max-width: 100%; + margin: auto 1em; + } +} + +/* Superscript & Subscript */ +/* Prevent scripts from affecting line-height. */ +sup, sub { + vertical-align: baseline; + position: relative; +} + +sup { + top: -0.4em; +} + +sub { + top: 0.3em; +} + +/* Classes for notices */ +.notice { + background: var(--accent-bg); + border: 2px solid var(--border); + border-radius: var(--standard-border-radius); + padding: 1.5rem; + margin: 2rem 0; +} diff --git a/assets/css/simple.min.css b/assets/css/simple.min.css @@ -0,0 +1 @@ +:root,::backdrop{--sans-font:-apple-system,BlinkMacSystemFont,"Avenir Next",Avenir,"Nimbus Sans L",Roboto,"Noto Sans","Segoe UI",Arial,Helvetica,"Helvetica Neue",sans-serif;--mono-font:Consolas,Menlo,Monaco,"Andale Mono","Ubuntu Mono",monospace;--standard-border-radius:5px;--bg:#fff;--accent-bg:#f5f7ff;--text:#212121;--text-light:#585858;--border:#898ea4;--accent:#0d47a1;--accent-hover:#1266e2;--accent-text:var(--bg);--code:#d81b60;--preformatted:#444;--marked:#fd3;--disabled:#efefef}@media (prefers-color-scheme:dark){:root,::backdrop{color-scheme:dark;--bg:#212121;--accent-bg:#2b2b2b;--text:#dcdcdc;--text-light:#ababab;--accent:#ffb300;--accent-hover:#ffe099;--accent-text:var(--bg);--code:#f06292;--preformatted:#ccc;--disabled:#111}img,video{opacity:.8}}*,:before,:after{box-sizing:border-box}textarea,select,input,progress{-webkit-appearance:none;-moz-appearance:none;appearance:none}html{font-family:var(--sans-font);scroll-behavior:smooth}body{color:var(--text);background-color:var(--bg);grid-template-columns:1fr min(45rem,90%) 1fr;margin:0;font-size:1.15rem;line-height:1.5;display:grid}body>*{grid-column:2}body>header{background-color:var(--accent-bg);border-bottom:1px solid var(--border);text-align:center;grid-column:1/-1;padding:0 .5rem 2rem}body>header>:only-child{margin-block-start:2rem}body>header h1{max-width:1200px;margin:1rem auto}body>header p{max-width:40rem;margin:1rem auto}main{padding-top:1.5rem}body>footer{color:var(--text-light);text-align:center;border-top:1px solid var(--border);margin-top:4rem;padding:2rem 1rem 1.5rem;font-size:.9rem}h1{font-size:3rem}h2{margin-top:3rem;font-size:2.6rem}h3{margin-top:3rem;font-size:2rem}h4{font-size:1.44rem}h5{font-size:1.15rem}h6{font-size:.96rem}p{margin:1.5rem 0}p,h1,h2,h3,h4,h5,h6{overflow-wrap:break-word}h1,h2,h3{line-height:1.1}@media only screen and (width<=720px){h1{font-size:2.5rem}h2{font-size:2.1rem}h3{font-size:1.75rem}h4{font-size:1.25rem}}a,a:visited{color:var(--accent)}a:hover{text-decoration:none}button,.button,a.button,input[type=submit],input[type=reset],input[type=button],label[type=button]{border:1px solid var(--accent);background-color:var(--accent);color:var(--accent-text);padding:.5rem .9rem;line-height:normal;text-decoration:none}.button[aria-disabled=true],input:disabled,textarea:disabled,select:disabled,button[disabled]{cursor:not-allowed;background-color:var(--disabled);border-color:var(--disabled);color:var(--text-light)}input[type=range]{padding:0}abbr[title]{cursor:help;text-decoration-line:underline;text-decoration-style:dotted}button:enabled:hover,.button:not([aria-disabled=true]):hover,input[type=submit]:enabled:hover,input[type=reset]:enabled:hover,input[type=button]:enabled:hover,label[type=button]:hover{background-color:var(--accent-hover);border-color:var(--accent-hover);cursor:pointer}.button:focus-visible,button:focus-visible:where(:enabled),input:enabled:focus-visible:where([type=submit],[type=reset],[type=button]){outline:2px solid var(--accent);outline-offset:1px}header>nav{padding:1rem 0 0;font-size:1rem;line-height:2}header>nav ul,header>nav ol{flex-flow:wrap;place-content:space-around center;align-items:center;margin:0;padding:0;list-style-type:none;display:flex}header>nav ul li,header>nav ol li{display:inline-block}header>nav a,header>nav a:visited{border:1px solid var(--border);border-radius:var(--standard-border-radius);color:var(--text);margin:0 .5rem 1rem;padding:.1rem 1rem;text-decoration:none;display:inline-block}header>nav a:hover,header>nav a.current,header>nav a[aria-current=page]{border-color:var(--accent);color:var(--accent);cursor:pointer}@media only screen and (width<=720px){header>nav a{border:none;padding:0;line-height:1;text-decoration:underline}}aside,details,pre,progress{background-color:var(--accent-bg);border:1px solid var(--border);border-radius:var(--standard-border-radius);margin-bottom:1rem}aside{float:right;width:30%;margin-inline-start:15px;padding:0 15px;font-size:1rem}[dir=rtl] aside{float:left}@media only screen and (width<=720px){aside{float:none;width:100%;margin-inline-start:0}}article,fieldset,dialog{border:1px solid var(--border);border-radius:var(--standard-border-radius);margin-bottom:1rem;padding:1rem}article h2:first-child,section h2:first-child,article h3:first-child,section h3:first-child{margin-top:1rem}section{border-top:1px solid var(--border);border-bottom:1px solid var(--border);margin:3rem 0;padding:2rem 1rem}section+section,section:first-child{border-top:0;padding-top:0}section+section{margin-top:0}section:last-child{border-bottom:0;padding-bottom:0}details{padding:.7rem 1rem}summary{cursor:pointer;word-break:break-all;margin:-.7rem -1rem;padding:.7rem 1rem;font-weight:700}details[open]>summary+*{margin-top:0}details[open]>summary{margin-bottom:.5rem}details[open]>:last-child{margin-bottom:0}table{border-collapse:collapse;margin:1.5rem 0}figure>table{width:max-content;margin:0}td,th{border:1px solid var(--border);text-align:start;padding:.5rem}th{background-color:var(--accent-bg);font-weight:700}tr:nth-child(2n){background-color:var(--accent-bg)}table caption{margin-bottom:.5rem;font-weight:700}textarea,select,input,button,.button{font-size:inherit;border-radius:var(--standard-border-radius);box-shadow:none;max-width:100%;margin-bottom:.5rem;padding:.5rem;font-family:inherit;display:inline-block}textarea,select,input{color:var(--text);background-color:var(--bg);border:1px solid var(--border)}label{display:block}textarea:not([cols]){width:100%}select:not([multiple]){background-image:linear-gradient(45deg,transparent 49%,var(--text)51%),linear-gradient(135deg,var(--text)51%,transparent 49%);background-position:calc(100% - 15px),calc(100% - 10px);background-repeat:no-repeat;background-size:5px 5px,5px 5px;padding-inline-end:25px}[dir=rtl] select:not([multiple]){background-position:10px,15px}input[type=checkbox],input[type=radio]{vertical-align:middle;width:min-content;position:relative}input[type=checkbox]+label,input[type=radio]+label{display:inline-block}input[type=radio]{border-radius:100%}input[type=checkbox]:checked,input[type=radio]:checked{background-color:var(--accent)}input[type=checkbox]:checked:after{content:" ";border-right:solid var(--bg).08em;border-bottom:solid var(--bg).08em;background-color:#0000;border-radius:0;width:.18em;height:.32em;font-size:1.8em;position:absolute;top:.05em;left:.17em;transform:rotate(45deg)}input[type=radio]:checked:after{content:" ";background-color:var(--bg);border-radius:100%;width:.25em;height:.25em;font-size:32px;position:absolute;top:.125em;left:.125em}@media only screen and (width<=720px){textarea,select,input{width:100%}}input[type=color]{height:2.5rem;padding:.2rem}input[type=file]{border:0}hr{background:var(--border);border:none;height:1px;margin:1rem auto}mark{border-radius:var(--standard-border-radius);background-color:var(--marked);color:#000;padding:2px 5px}mark a{color:#0d47a1}img,video{border-radius:var(--standard-border-radius);max-width:100%;height:auto}figure{margin:0;display:block;overflow-x:auto}figure>img,figure>picture>img{margin-inline:auto;display:block}figcaption{text-align:center;color:var(--text-light);margin-block:1rem;font-size:.9rem}blockquote{border-inline-start:.35rem solid var(--accent);color:var(--text-light);margin-block:2rem;margin-inline:2rem 0;padding:.4rem .8rem;font-style:italic}cite{color:var(--text-light);font-size:.9rem;font-style:normal}dt{color:var(--text-light)}code,pre,pre span,kbd,samp{font-family:var(--mono-font);color:var(--code)}kbd{color:var(--preformatted);border:1px solid var(--preformatted);border-bottom:3px solid var(--preformatted);border-radius:var(--standard-border-radius);padding:.1rem .4rem}pre{color:var(--preformatted);max-width:100%;padding:1rem 1.4rem;overflow:auto}pre code{color:var(--preformatted);background:0 0;margin:0;padding:0}progress{width:100%}progress:indeterminate{background-color:var(--accent-bg)}progress::-webkit-progress-bar{border-radius:var(--standard-border-radius);background-color:var(--accent-bg)}progress::-webkit-progress-value{border-radius:var(--standard-border-radius);background-color:var(--accent)}progress::-moz-progress-bar{border-radius:var(--standard-border-radius);background-color:var(--accent);transition-property:width;transition-duration:.3s}progress:indeterminate::-moz-progress-bar{background-color:var(--accent-bg)}dialog{max-width:40rem;margin:auto}dialog::backdrop{background-color:var(--bg);opacity:.8}@media only screen and (width<=720px){dialog{max-width:100%;margin:auto 1em}}sup,sub{vertical-align:baseline;position:relative}sup{top:-.4em}sub{top:.3em}.notice{background:var(--accent-bg);border:2px solid var(--border);border-radius:var(--standard-border-radius);margin:2rem 0;padding:1.5rem} +\ No newline at end of file diff --git a/assets/img/favicon.ico b/assets/img/favicon.ico Binary files differ. diff --git a/assets/img/favicon.png b/assets/img/favicon.png Binary files differ. diff --git a/config/config.php b/config/config.php @@ -0,0 +1,221 @@ +<?php + +/* Define Search Engines +* Insert "%s" for the search-query part +* Bangs are defined here as well. They may ONLY contain [A-Za-z0-9_\-\.]. +*/ + +$searches = array( + /* MetaGer */ + "metager_web" => array( + "name" => "MetaGer Web", + "website" => "https://metager.de/?focus=web", + "query" => "https://metager.de/meta/meta.ger3?focus=web&eingabe=%s", + "bangs" => array("metager_web", "metager", "meta", "mg") + ), + "metager_img" => array( + "name" => "MetaGer Images", + "website" => "https://metager.de/focus=bilder", + "query" => "https://metager.de/meta/meta.ger3?focus=bilder&eingabe=%s", + "bangs" => array("metager_img") + ), + "metager_shop" => array( + "name" => "MetaGer Products", + "website" => "https://metager.de/focus=produkte", + "query" => "https://metager.de/meta/meta.ger3?focus=produkte&eingabe=%s", + "bangs" => array("metager_shop") + ), + "metager_news" => array( + "name" => "MetaGer Nachrichten", + "website" => "https://metager.de/?fokus=nachrichten", + "query" => "https://metager.de/meta/meta.ger3?focus=nachrichten&eingabe=%s", + "bangs" => array("metager_news") + ), + "metager_sci" => array( + "name" => "MetaGer Nachrichten", + "website" => "https://metager.de/?fokus=science", + "query" => "https://metager.de/meta/meta.ger3?focus=science&eingabe=%s", + "bangs" => array("metager_sci") + ), + "metager_map" => array( + "name" => "MetaGer Maps", + "website" => "https://metager.de/?fokus=maps", + "query" => "https://maps.metager.de/en/%s/guess", + "bangs" => array("metager_map") + ), + /* Qwant */ + "qwant_web" => array( + "name" => "Qwant Web", + "website" => "https://qwant.com/?t=web", + "query" => "https://www.qwant.com/?t=web&q=%s", + "bangs" => array("qwant_web", "qwant", "qw") + ), + "qwant_img" => array( + "name" => "Qwant Images", + "website" => "https://qwant.com/?t=images", + "query" => "https://www.qwant.com/?t=images&q=%s", + "bangs" => array("qwant_img", "img") + ), + "qwant_video" => array( + "name" => "Qwant Videos", + "website" => "https://qwant.com/?t=videos", + "query" => "https://www.qwant.com/?t=videos&q=%s", + "bangs" => array("qwant_video") + ), + "qwant_shop" => array( + "name" => "Qwant Shopping", + "website" => "https://qwant.com/?t=shopping", + "query" => "https://www.qwant.com/?t=shopping&q=%s", + "bangs" => array("qwant_shop") + ), + "qwant_news" => array( + "name" => "Qwant News", + "website" => "https://qwant.com/?t=news", + "query" => "https://www.qwant.com/?t=news&q=%s", + "bangs" => array("qwant_news") + ), + /* Mojeek */ + "mojeek_web" => array( + "name" => "Mojeek Web", + "website" => "https://mojeek.com", + "query" => "https://www.mojeek.com/search?q=%s", + "bangs" => array("mojeek_web", "mojeek", "mj") + ), + "mojeek_img" => array( + "name" => "Mojeek Images", + "website" => "https://mojeek.com/images", + "query" => "https://www.mojeek.com/search?fmt=images&q=%s", + "bangs" => array("mojeek_img") + + ), + "mojeek_news" => array( + "name" => "Mojeek News", + "website" => "https://mojeek.com/news", + "query" => "https://www.mojeek.com/search?fmt=news&q=%s", + "bangs" => array("mojeek_news") + ), + "mojeek_summary" => array( + "name" => "Mojeek Summary", + "website" => "https://mojeek.com/?fmt=summary", + "query" => "https://www.mojeek.com/search?fmt=summary&q=%s", + "bangs" => array("mojeek_summary", "summary") + ), + /* Translations */ + "leo" => array( + "name" => "LEO (en-de)", + "website" => "https://leo.org", + "query" => "https://dict.leo.org/englisch-deutsch/%s", + "bangs" => array("leo", "translate", "tl") + ), + "dictcc" => array( + "name" => "dict.cc", + "website" => "https://dict.cc", + "query" => "https://www.dict.cc/?s=%s", + "bangs" => array("dictcc", "dict") + ), + "deepl" => array( + "name" => "DeepL (en-de)", + "website" => "https://deepl.com", + "query" => "https://www.deepl.com/translator#de/en/%s", + "bangs" => array("deepl") + ), + /* Videos */ + "youtube" => array( + "name" => "YouTube", + "website" => "https://www.youtube.com", + "query" => "https://www.youtube.com/results?search_query=%s", + "bangs" => array("youtube", "yt") + ), + /* Maps */ + "openstreetmap" => array( + "name" => "OpenStreetMap", + "website" => "https://www.openstreetmap.org", + "query" => "https://www.openstreetmap.org/search?query=%s", + "bangs" => array("openstreetmap", "osm", "map", "maps") + ), + /* Science */ + "base" => array( + "name" => "BASE", + "website" => "https://www.base-search.net", + "query" => "https://www.base-search.net/Search/Results?lookfor=%s", + "bangs" => array("base", "basesearch", "science") + ), + /* Wikis */ + "wikipedia" => array( + "name" => "Wikipedia", + "website" => "https://wikipedia.org", + "query" => "https://wikipedia.org/wiki/%s", + "bangs" => array("wikipedia", "wiki", "wp") + ), + "archwiki" => array( + "name" => "Arch Wiki", + "website" => "https://wiki.archlinux.org", + "query" => "https://wiki.archlinux.org/index.php?search=%s", + "bangs" => array("archwiki", "arch", "aw") + ), + "debianwiki" => array( + "name" => "Debian Wiki", + "website" => "https://wiki.debian.org", + "query" => "https://wiki.debian.org/?action=fullsearch&value=%s", + "bangs" => array("debianwiki", "debian", "dw") + ), + /* Stackoverflow */ + "stackoverflow" => array( + "name" => "Stackoverflow", + "website" => "https://stackoverflow.com", + "query" => "https://stackoverflow.com/search?q=%s", + "bangs" => array("stackoverflow", "so", "code") + ), + /* Code Repositories */ + "github" => array( + "name" => "GitHub", + "website" => "https://github.com", + "query" => "https://github.com/search?q=%s", + "bangs" => array("github", "gh") + ), + "codeberg" => array( + "name" => "Codeberg", + "website" => "https://codeberg.org", + "query" => "https://codeberg.org/explore/repos?only_show_relevant=true&q=%s", + "bangs" => array("codeberg", "cb") + ), + "sourcehut" => array( + "name" => "Sourcehut", + "website" => "https://sr.ht", + "query" => "https://sr.ht/projects?search=%s", + "bangs" => array("sourcehut", "srht", "sir") + ), + /* Software Repositories */ + "dockerhub" => array( + "name" => "dockerhub", + "website" => "https://hub.docker.com", + "query" => "https://hub.docker.com/search?q=%s", + "bangs" => array("dockerhub", "docker", "dh") + ), + "flathub" => array( + "name" => "Flathub", + "website" => "https://flathub.org", + "query" => "https://flathub.org/apps/search?q=%s", + "bangs" => array("flathub", "flatpak", "fh", "fp") + ), + "fdroid" => array( + "name" => "F-Droid", + "website" => "https://f-droid.org", + "query" => "https://search.f-droid.org/?q=%s", + "bangs" => array("fdroid", "f-droid", "fd", "android") + ), + /* Misc */ + "symbl" => array( + "name" => "SYMBL", + "website" => "https://symbl.cc", + "query" => "https://symbl.cc/search/?q=%s", + "bangs" => array("symbl", "symbol", "unicode") + ) +); + +/* Default Search Engine +* Search Engine to use when no (known) bang is given +*/ +$default_search = "metager_web"; + +?> diff --git a/index.php b/index.php @@ -0,0 +1,148 @@ +<!doctype html> +<html> + +<!-- Process input (if given) --> +<?php + +/* Load Configuration */ +include("config/config.php"); + +/* Fetch and sanitize search query via GET request */ +$_GET = filter_input_array(INPUT_GET, FILTER_SANITIZE_FULL_SPECIAL_CHARS); +$query = rawurldecode($_GET["query"]); + +if (strlen($query) > 0) { + + /* Find bang (only first one is considered) */ + $bang = preg_replace( + '/^.*?\!([A-Za-z0-9_\-\.]+).*$/', + '${1}', + $query + ); + + /* Based on given bang, choose a search engine */ + $search = null; + foreach (array_keys($searches) as $key) { + if (array_search($bang, $searches[$key]["bangs"]) != "") { + /* If bang has been found, assign search engine and end the loop */ + $search = $key; + break; + } + } + + /* Fallback search engine, if bang wasn't found */ + if (is_null($search)) { + $search = $default_search; + } + + /* Strip bang from search term */ + $search_term = preg_replace( + '/\![A-Za-z0-9_\-\.]+/', + '', + $query + ); + + /* Remove leading or trailing spaces */ + $search_term = preg_replace( + '/\ /', + '', + $search_term + ); + + /* Construct search query */ + $target = str_replace( + '%s', + rawurlencode($search_term), + $searches[$search]["query"] + ); + + /* Redirect to target search engine: 307 - Temporary Redirect */ + header("Location: " . $target, true, 307); + die(); +} + +?> + + + <!-- Head --> + <head> + <meta charset=utf-8> + <meta name=viewport content="width=device-width,initial-scale=1"> + <link rel=icon href=/assets/img/favicon.png type=image/png> + <link rel=icon href=/assets/img/favicon.ico type=x-image/ico> + <link rel=stylesheet href=/assets/css/simple.min.css media=all> + <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="traserĉi - search with !bangs"> + <title>traserĉi - search with !bangs</title> + <style> + #searchbar { + width: 100%; + } + </style> + </head> + + <!-- Body --> + <body> + + <!-- Search Bar --> + <h1>Search with !bangs</h1> + <form action="/" method="get"> + <input + id="searchbar" + name="query" + type="text" + autofocus + placeholder="&#128270; Search the web" + alt="Search the web" + onkeydown="if(event.keyCode===13)return this.form.submit(),!1" + > + </form> + + <!-- FAQs --> + <h3>FAQs</h3> + <ul> + <li><a href="https://translate.google.com/?sl=eo&text=traser%C4%89i&op=translate" target="_blank"> + What does <em>traserĉi</em> mean? + </a></li> + <li><a href="https://duckduckgo.com/bangs" target="_blank"> + What are bangs? + </a></li> + <li><a href="https://src.jayvii.de/pub/traserci" target="_blank"> + Help with development! + </a></li> + </ul> + + <!-- Search Engine Table --> + <h3>Search Engines</h3> + <table> + <tr> + <th>Engine</th> + <th>Bangs</th> + <tr> + +<?php +/* Print one table row for each configured search engine */ +$engine_table = ""; +foreach (array_keys($searches) as $key) { + $engine_table .= "<tr><td>" . + "<a href=\"" . $searches[$key]["website"] . "\" " . + "target=\"_blank\">" . + $searches[$key]["name"]; + + if ($key == $default_search) { + $engine_table .= " (default)"; + } + + $engine_table .= "</a>" . + "</td><td>" . + "<code>!" . implode("</code>, <code>!", $searches[$key]["bangs"]) . + "</code>" . + "</td></tr>"; +} +echo $engine_table; +?> + + </table> + </body> + +</html> + diff --git a/opensearch.xml b/opensearch.xml @@ -0,0 +1,8 @@ +<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/"> + <ShortName>traserĉi</ShortName> + <Description>Search with !bangs</Description> + <Url type="text/html" method="get" template="%%URL%%/?query={searchTerms}"/> + <Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAKBgUACgYFBQwHB0wIBQWTCAUEnAgFBJwIBQScCAUEnAgFBJwIBQScCAUEnAgFBJwIBQWcBgQDnAAAAIMAAAA01nt2AK9lYE2fW1fWkFNP9o9ST/iPUk/4j1JP+I9ST/iPUk/4j1JP+I9ST/iOUk74jVFN+IhOS/g8IiHnAAAAg7FmYQXDcWyuxnJt/8dzbv/Hc27/x3Nu/8dzbv/Hc27/x3Nu/8dzbv/Hcm3/t2pl/5tZVf+1Z2P/ilBM+AcEBJy3aWUHw3FstcRxbP/EcWz/xHFs/8RxbP/EcWz/xHFs/8RxbP/EcWz/tmtm/3tdW/9ZREP/nltX/41RTvgIBQWct2llB8NxbLXEcWz/xHFs/8RxbP/DcGv/wG5p/75taP/Bb2r/uWtn/4RiYP9hYGD/f11b/7lrZv+OUk74CAUEnLdpZQfDcWy1xHFs/8RxbP++bWj/q2Rh/5RbWf+IUU7/kVJP/4ReXP9kYWD/hmJg/7hrZ//Hcm3/j1JP+AgFBJy3aWUHw3FstcRxbP+/b2r/sXx0/7CTff+wmoD/mot+/4Nzcf9ybGv/hV9c/7psZ//EcWz/x3Nu/49ST/gIBQSct2llB8NxbLXDcGv/uoN8/8epf//Ur3D/17J1/97Fmf/Lv6r/gnBv/5RUUP/Bb2r/xHFs/8dzbv+PUk/4CAUEnLdpZQfEcGu1wnRw/7WWhP/PrW//061s/9Stbf/Vr3D/2LiC/5iIff+MUk//v25p/8RxbP/Hc27/j1JP+AgFBJy3aWUHxHBrtcF4dP+tloP/2cGT/9a4f//RrWz/061s/9WvcP+nkn7/mlxZ/8Fvav/EcWz/x3Nu/49ST/gIBQSct2llB8Nwa7XFd3L/rZWM/9TGrf/r3sP/2b6L/9Ota//SsHX/qYd5/7JmYv/EcWz/xHFs/8dzbv+PUk/4CAUEnLdpZQfDcWy1xXFs/76Jhv+rn5T/08q4/9W9kv/HqG//v6GD/7Rzb//Bb2r/xHFs/8RxbP/Hc27/j1JP+AgFBJy4amYHxHFstcRxbP/Fc27/vYWC/6iLhP+qj4L/s46D/7x6df/Cb2r/xHFs/8RxbP/EcWz/x3Nu/5BTT/YIBQWTwG9qBcRxbLDEcWz/xHFs/8Rwa//Ec2//w3Vx/8Nybf/EcGv/xHFs/8RxbP/EcWz/xHFs/8Zybf+eW1fVCwYGTMRxbADEcWxNxHFsscRxbLXDcWy1w3FstcRwa7XEcWy1w3FstcNxbLXDcWy1w3FstcNxbLXDcWyvr2VgTQAAAAXEcWwAxXFsAcBvaga4amYHt2llB7dpZQe3aWUHt2llB7dpZQe3aWUHt2llB7dpZQe3aWUHtmlkBbBlYQCvZWAAgAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAEAAA==</Image> <InputEncoding>UTF-8</InputEncoding> + <moz:SearchForm>%%URL%%</moz:SearchForm> +</OpenSearchDescription> +