general cleanup
All checks were successful
Build and publish the docker image / build (push) Successful in 18s
Signed-off-by: ngn <ngn@ngn.tf>
BIN
src/static/404.png
Normal file
After Width: | Height: | Size: 216 B |
985
src/static/client.js
Normal file
@ -0,0 +1,985 @@
|
||||
|
||||
/*
|
||||
Global functions
|
||||
*/
|
||||
function htmlspecialchars(str){
|
||||
|
||||
var map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
}
|
||||
|
||||
return str.replace(/[&<>"']/g, function(m){return map[m];});
|
||||
}
|
||||
|
||||
function htmlspecialchars_decode(str){
|
||||
|
||||
var map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
''': "'"
|
||||
}
|
||||
|
||||
return str.replace(/&|<|>|"|'/g, function(m){return map[m];});
|
||||
}
|
||||
|
||||
function is_click_within(elem, classname, is_id = false){
|
||||
|
||||
while(true){
|
||||
|
||||
if(elem === null){
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(
|
||||
(
|
||||
is_id === false &&
|
||||
elem.className == classname
|
||||
) ||
|
||||
(
|
||||
is_id === true &&
|
||||
elem.id == classname
|
||||
)
|
||||
){
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
elem = elem.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Prevent GET parameter pollution
|
||||
*/
|
||||
var form = document.getElementsByTagName("form");
|
||||
|
||||
if(
|
||||
form.length !== 0 &&
|
||||
window.location.pathname != "/" &&
|
||||
window.location.pathname != "/settings.php" &&
|
||||
window.location.pathname != "/settings"
|
||||
){
|
||||
form = form[0];
|
||||
|
||||
var scraper_dropdown = document.getElementsByName("scraper")[0];
|
||||
|
||||
scraper_dropdown.addEventListener("change", function(choice){
|
||||
|
||||
submit(form);
|
||||
});
|
||||
|
||||
form.addEventListener("submit", function(e){
|
||||
|
||||
e.preventDefault();
|
||||
submit(e.srcElement);
|
||||
});
|
||||
}
|
||||
|
||||
function submit(e){
|
||||
|
||||
var GET = "";
|
||||
var first = true;
|
||||
|
||||
if((s = document.getElementsByName("s")).length !== 0){
|
||||
|
||||
GET += "?s=" + encodeURIComponent(s[0].value).replaceAll("%20", "+");
|
||||
first = false;
|
||||
}
|
||||
|
||||
Array.from(
|
||||
e.getElementsByTagName("select")
|
||||
).concat(
|
||||
Array.from(
|
||||
e.getElementsByTagName("input")
|
||||
)
|
||||
).forEach(function(el){
|
||||
|
||||
var firstelem = el.getElementsByTagName("option");
|
||||
|
||||
if(
|
||||
(
|
||||
(
|
||||
firstelem.length === 0 ||
|
||||
firstelem[0].value != el.value
|
||||
) &&
|
||||
el.name != "" &&
|
||||
el.value != "" &&
|
||||
el.name != "s"
|
||||
) ||
|
||||
el.name == "scraper" ||
|
||||
el.name == "nsfw"
|
||||
){
|
||||
|
||||
if(first){
|
||||
|
||||
GET += "?";
|
||||
first = false;
|
||||
}else{
|
||||
|
||||
GET += "&";
|
||||
}
|
||||
|
||||
GET += encodeURIComponent(el.name).replaceAll("%20", "+") + "=" + encodeURIComponent(el.value).replaceAll("%20", "+");
|
||||
}
|
||||
});
|
||||
|
||||
window.location.href = GET;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Hide show more button when it's not needed on answers
|
||||
*/
|
||||
var answer_div = document.getElementsByClassName("answer");
|
||||
|
||||
if(answer_div.length !== 0){
|
||||
answer_div = Array.from(answer_div);
|
||||
var spoiler_button_div = Array.from(document.getElementsByClassName("spoiler-button"));
|
||||
|
||||
// execute on pageload
|
||||
hide_show_more();
|
||||
|
||||
window.addEventListener("resize", hide_show_more);
|
||||
|
||||
function hide_show_more(){
|
||||
|
||||
var height = window.innerWidth >= 1000 ? 600 : 200;
|
||||
|
||||
for(i=0; i<answer_div.length; i++){
|
||||
|
||||
if(answer_div[i].scrollHeight < height){
|
||||
|
||||
spoiler_button_div[i].style.display = "none";
|
||||
|
||||
document.getElementById(spoiler_button_div[i].htmlFor).checked = true;
|
||||
}else{
|
||||
|
||||
spoiler_button_div[i].style.display = "block";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(document.location.pathname){
|
||||
|
||||
case "/web":
|
||||
case "/web.php":
|
||||
var image_class = "image";
|
||||
break;
|
||||
|
||||
case "/images":
|
||||
case "/images.php":
|
||||
var image_class = "thumb";
|
||||
break;
|
||||
|
||||
default:
|
||||
var image_class = null;
|
||||
}
|
||||
|
||||
if(image_class !== null){
|
||||
|
||||
/*
|
||||
Add popup to document
|
||||
*/
|
||||
var popup_bg = document.createElement("div");
|
||||
popup_bg.id = "popup-bg";
|
||||
document.body.appendChild(popup_bg);
|
||||
|
||||
// enable/disable pointer events
|
||||
if(!document.cookie.includes("bg_noclick=yes")){
|
||||
|
||||
popup_bg.style.pointerEvents = "none";
|
||||
}
|
||||
|
||||
var popup_status = document.createElement("div");
|
||||
popup_status.id = "popup-status";
|
||||
document.body.appendChild(popup_status);
|
||||
|
||||
var popup_body = document.createElement("div");
|
||||
popup_body.id = "popup";
|
||||
document.body.appendChild(popup_body);
|
||||
|
||||
// import popup
|
||||
var popup_body = document.getElementById("popup");
|
||||
var popup_status = document.getElementById("popup-status");
|
||||
var popup_image = null; // is set later on popup click
|
||||
|
||||
// image metadata
|
||||
var collection = []; // will contain width, height, image URL
|
||||
var collection_index = 0;
|
||||
|
||||
// event handling helper variables
|
||||
var is_popup_shown = false;
|
||||
var mouse_down = false;
|
||||
var mouse_move = false;
|
||||
var move_x = 0;
|
||||
var move_y = 0;
|
||||
var target_is_popup = false;
|
||||
var mirror_x = false;
|
||||
var mirror_y = false;
|
||||
var rotation = 0;
|
||||
|
||||
/*
|
||||
Image dragging (mousedown)
|
||||
*/
|
||||
document.addEventListener("mousedown", function(div){
|
||||
|
||||
if(div.buttons !== 1){
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mouse_down = true;
|
||||
mouse_move = false;
|
||||
|
||||
if(is_click_within(div.target, "popup", true) === false){
|
||||
|
||||
target_is_popup = false;
|
||||
}else{
|
||||
|
||||
target_is_popup = true;
|
||||
|
||||
var pos = popup_body.getBoundingClientRect();
|
||||
move_x = div.x - pos.x;
|
||||
move_y = div.y - pos.y;
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Image dragging (mousemove)
|
||||
*/
|
||||
document.addEventListener("mousemove", function(pos){
|
||||
|
||||
if(
|
||||
target_is_popup &&
|
||||
mouse_down
|
||||
){
|
||||
|
||||
mouse_move = true;
|
||||
movepopup(popup_body, pos.clientX - move_x, pos.clientY - move_y);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Image dragging (mouseup)
|
||||
*/
|
||||
document.addEventListener("mouseup", function(){
|
||||
|
||||
mouse_down = false;
|
||||
});
|
||||
|
||||
/*
|
||||
Image popup open
|
||||
*/
|
||||
document.addEventListener("click", function(click){
|
||||
|
||||
// should our click trigger image open?
|
||||
if(
|
||||
elem = is_click_within(click.target, image_class) ||
|
||||
click.target.classList.contains("openimg")
|
||||
){
|
||||
|
||||
event.preventDefault();
|
||||
is_popup_shown = true;
|
||||
|
||||
// reset position params
|
||||
mirror_x = false;
|
||||
mirror_y = false;
|
||||
rotation = 0;
|
||||
scale = 60;
|
||||
collection_index = 0;
|
||||
|
||||
// get popup data
|
||||
if(elem === true){
|
||||
// we clicked a simple image preview
|
||||
elem = click.target;
|
||||
var image_url = elem.getAttribute("src");
|
||||
|
||||
if(image_url.startsWith("/proxy")){
|
||||
|
||||
var match = image_url.match(/i=([^&]+)/);
|
||||
|
||||
if(match !== null){
|
||||
|
||||
image_url = decodeURIComponent(match[1]);
|
||||
}
|
||||
}else{
|
||||
|
||||
image_url = htmlspecialchars_decode(image_url);
|
||||
}
|
||||
|
||||
var w = Math.round(click.target.naturalWidth);
|
||||
var h = Math.round(click.target.naturalHeight);
|
||||
|
||||
if(
|
||||
w === 0 ||
|
||||
h === 0
|
||||
){
|
||||
|
||||
w = 100;
|
||||
h = 100;
|
||||
}
|
||||
|
||||
collection = [
|
||||
{
|
||||
"url": image_url,
|
||||
"width": w,
|
||||
"height": h
|
||||
}
|
||||
];
|
||||
|
||||
var title = "No description provided";
|
||||
|
||||
if(click.target.title != ""){
|
||||
|
||||
title = click.target.title;
|
||||
}else{
|
||||
|
||||
if(click.target.alt != ""){
|
||||
|
||||
title = click.target.alt;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
|
||||
if(image_class == "thumb"){
|
||||
// we're inside image.php
|
||||
|
||||
elem =
|
||||
elem
|
||||
.parentElement
|
||||
.parentElement;
|
||||
|
||||
var image_url = elem.getElementsByTagName("a")[1].href;
|
||||
}else{
|
||||
|
||||
// we're inside web.php
|
||||
var image_url = elem.href;
|
||||
}
|
||||
|
||||
collection =
|
||||
JSON.parse(
|
||||
elem.getAttribute("data-json")
|
||||
);
|
||||
|
||||
var imagesize = elem.getElementsByTagName("img")[0];
|
||||
|
||||
var imagesize_w = 0;
|
||||
var imagesize_h = 0;
|
||||
|
||||
if(imagesize.complete){
|
||||
|
||||
imagesize_w = imagesize.naturalWidth;
|
||||
imagesize_h = imagesize.naturalHeight;
|
||||
}
|
||||
|
||||
if(
|
||||
imagesize_w === 0 ||
|
||||
imagesize_h === 0
|
||||
){
|
||||
|
||||
imagesize_w = 100;
|
||||
imagesize_h = 100;
|
||||
}
|
||||
|
||||
for(var i=0; i<collection.length; i++){
|
||||
|
||||
if(collection[i].width === null){
|
||||
|
||||
collection[i].width = imagesize_w;
|
||||
collection[i].height = imagesize_h;
|
||||
}
|
||||
}
|
||||
|
||||
var title = elem.title;
|
||||
}
|
||||
|
||||
// prepare HTML
|
||||
var html =
|
||||
'<div id="popup-num">(' + collection.length + ')</div>' +
|
||||
'<div id="popup-dropdown">' +
|
||||
'<select name="viewer-res" onchange="changeimage(event)">';
|
||||
|
||||
for(i=0; i<collection.length; i++){
|
||||
|
||||
if(collection[i].url.startsWith("data:")){
|
||||
|
||||
var domain = "<Base64 Data>";
|
||||
}else{
|
||||
|
||||
var domain = new URL(collection[i].url).hostname;
|
||||
}
|
||||
|
||||
html += '<option value="' + i + '">' + '(' + collection[i].width + 'x' + collection[i].height + ') ' + domain + '</option>';
|
||||
}
|
||||
|
||||
popup_status.innerHTML =
|
||||
html + '</select></div>' +
|
||||
'<a href="' + htmlspecialchars(image_url) + '" rel="noreferrer nofollow "id="popup-title">' + htmlspecialchars(title) + '</a>';
|
||||
|
||||
popup_body.innerHTML =
|
||||
'<img src="' + getproxylink(collection[0].url) + '" draggable="false" id="popup-image">';
|
||||
|
||||
// make changes to DOM
|
||||
popup_body.style.display = "block";
|
||||
popup_bg.style.display = "block";
|
||||
popup_status.style.display = "table";
|
||||
|
||||
// store for rotation functions & changeimage()
|
||||
popup_image = document.getElementById("popup-image");
|
||||
|
||||
scalepopup(collection[collection_index], scale);
|
||||
centerpopup();
|
||||
}else{
|
||||
|
||||
// click inside the image viewer
|
||||
// resize image
|
||||
if(is_click_within(click.target, "popup", true)){
|
||||
|
||||
if(mouse_move === false){
|
||||
scale = 80;
|
||||
scalepopup(collection[collection_index], scale);
|
||||
centerpopup();
|
||||
}
|
||||
}else{
|
||||
|
||||
if(is_click_within(click.target, "popup-status", true) === false){
|
||||
|
||||
// click outside the popup while its open
|
||||
// close it
|
||||
if(is_popup_shown){
|
||||
|
||||
hidepopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Scale image viewer
|
||||
*/
|
||||
popup_body.addEventListener("wheel", function(scroll){
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if(
|
||||
scroll.altKey ||
|
||||
scroll.ctrlKey ||
|
||||
scroll.shiftKey
|
||||
){
|
||||
|
||||
var increment = 7;
|
||||
}else{
|
||||
|
||||
var increment = 14;
|
||||
}
|
||||
|
||||
if(scroll.wheelDelta > 0){
|
||||
|
||||
// scrolling up
|
||||
scale = scale + increment;
|
||||
}else{
|
||||
|
||||
// scrolling down
|
||||
if(scale - increment > 7){
|
||||
scale = scale - increment;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate relative size before scroll
|
||||
var pos = popup_body.getBoundingClientRect();
|
||||
var x = (scroll.x - pos.x) / pos.width;
|
||||
var y = (scroll.y - pos.y) / pos.height;
|
||||
|
||||
scalepopup(collection[collection_index], scale);
|
||||
|
||||
// move popup to % we found
|
||||
pos = popup_body.getBoundingClientRect();
|
||||
|
||||
movepopup(
|
||||
popup_body,
|
||||
scroll.clientX - (x * pos.width),
|
||||
scroll.clientY - (y * pos.height)
|
||||
);
|
||||
});
|
||||
|
||||
/*
|
||||
Keyboard controls
|
||||
*/
|
||||
|
||||
document.addEventListener("keydown", function(key){
|
||||
|
||||
// close popup
|
||||
if(
|
||||
is_popup_shown &&
|
||||
key.keyCode === 27
|
||||
){
|
||||
|
||||
hidepopup();
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_popup_shown === false){
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(
|
||||
key.altKey ||
|
||||
key.ctrlKey ||
|
||||
key.shiftKey
|
||||
){
|
||||
|
||||
// mirror image
|
||||
switch(key.keyCode){
|
||||
|
||||
case 37:
|
||||
// left
|
||||
key.preventDefault();
|
||||
mirror_x = true;
|
||||
break;
|
||||
|
||||
case 38:
|
||||
// up
|
||||
key.preventDefault();
|
||||
mirror_y = false;
|
||||
break;
|
||||
|
||||
case 39:
|
||||
// right
|
||||
key.preventDefault();
|
||||
mirror_x = false;
|
||||
break;
|
||||
|
||||
case 40:
|
||||
// down
|
||||
key.preventDefault();
|
||||
mirror_y = true;
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
|
||||
// rotate image
|
||||
switch(key.keyCode){
|
||||
|
||||
case 37:
|
||||
// left
|
||||
key.preventDefault();
|
||||
rotation = -90;
|
||||
break;
|
||||
|
||||
case 38:
|
||||
// up
|
||||
key.preventDefault();
|
||||
rotation = 0;
|
||||
break;
|
||||
|
||||
case 39:
|
||||
// right
|
||||
key.preventDefault();
|
||||
rotation = 90;
|
||||
break;
|
||||
|
||||
case 40:
|
||||
// down
|
||||
key.preventDefault();
|
||||
rotation = -180;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
popup_image.style.transform =
|
||||
"scale(" +
|
||||
(mirror_x ? "-1" : "1") +
|
||||
", " +
|
||||
(mirror_y ? "-1" : "1") +
|
||||
") " +
|
||||
"rotate(" +
|
||||
rotation + "deg" +
|
||||
")";
|
||||
});
|
||||
}
|
||||
|
||||
function getproxylink(url){
|
||||
|
||||
if(url.startsWith("data:")){
|
||||
|
||||
return htmlspecialchars(url);
|
||||
}else{
|
||||
|
||||
return '/proxy?i=' + encodeURIComponent(url);
|
||||
}
|
||||
}
|
||||
|
||||
function hidepopup(){
|
||||
|
||||
is_popup_shown = false;
|
||||
popup_status.style.display = "none";
|
||||
popup_body.style.display = "none";
|
||||
popup_bg.style.display = "none";
|
||||
}
|
||||
|
||||
function scalepopup(size, scale){
|
||||
|
||||
var ratio =
|
||||
Math.min(
|
||||
(window.innerWidth * (scale / 100)) / collection[collection_index].width, (window.innerHeight * (scale / 100)) / collection[collection_index].height
|
||||
);
|
||||
|
||||
popup_body.style.width = size.width * ratio + "px";
|
||||
popup_body.style.height = size.height * ratio + "px";
|
||||
}
|
||||
|
||||
function centerpopup(){
|
||||
|
||||
var size = popup_body.getBoundingClientRect();
|
||||
var size = {
|
||||
"width": parseInt(size.width),
|
||||
"height": parseInt(size.height)
|
||||
};
|
||||
|
||||
movepopup(
|
||||
popup_body,
|
||||
(window.innerWidth / 2) - (size.width / 2),
|
||||
(window.innerHeight / 2) - (size.height / 2)
|
||||
);
|
||||
}
|
||||
|
||||
function movepopup(popup_body, x, y){
|
||||
|
||||
popup_body.style.left = x + "px";
|
||||
popup_body.style.top = y + "px";
|
||||
}
|
||||
|
||||
function changeimage(event){
|
||||
|
||||
// reset rotation params
|
||||
mirror_x = false;
|
||||
mirror_y = false;
|
||||
rotation = 0;
|
||||
|
||||
scale = 60;
|
||||
|
||||
collection_index = parseInt(event.target.value);
|
||||
|
||||
// we set innerHTML otherwise old image lingers a little
|
||||
popup_body.innerHTML =
|
||||
'<img src="' + getproxylink(collection[collection_index].url) + '" draggable="false" id="popup-image">';
|
||||
|
||||
// store for rotation functions & changeimage()
|
||||
popup_image = document.getElementById("popup-image");
|
||||
|
||||
scalepopup(collection[collection_index], scale);
|
||||
centerpopup();
|
||||
}
|
||||
|
||||
var searchbox_wrapper = document.getElementsByClassName("searchbox");
|
||||
|
||||
if(searchbox_wrapper.length !== 0){
|
||||
|
||||
searchbox_wrapper = searchbox_wrapper[0];
|
||||
var searchbox = searchbox_wrapper.getElementsByTagName("input")[1];
|
||||
|
||||
/*
|
||||
Textarea shortcuts
|
||||
*/
|
||||
document.addEventListener("keydown", function(key){
|
||||
|
||||
switch(key.keyCode){
|
||||
|
||||
case 191:
|
||||
// 191 = /
|
||||
if(document.activeElement.tagName == "INPUT"){
|
||||
|
||||
// already focused, ignore
|
||||
break;
|
||||
}
|
||||
|
||||
if(
|
||||
typeof is_popup_shown != "undefined" &&
|
||||
is_popup_shown
|
||||
){
|
||||
|
||||
hidepopup();
|
||||
}
|
||||
|
||||
window.scrollTo(0, 0);
|
||||
searchbox.focus();
|
||||
key.preventDefault();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Autocompleter
|
||||
*/
|
||||
if( // make sure the user wants it
|
||||
document.cookie.includes("scraper_ac=") &&
|
||||
document.cookie.includes("scraper_ac=disabled") === false
|
||||
){
|
||||
|
||||
var autocomplete_cache = [];
|
||||
var focuspos = -1;
|
||||
var list = [];
|
||||
var autocomplete_div = document.getElementsByClassName("autocomplete")[0];
|
||||
|
||||
if(
|
||||
document.cookie.includes("scraper_ac=auto") &&
|
||||
typeof scraper_dropdown != "undefined"
|
||||
){
|
||||
|
||||
var ac_req_appendix = "&scraper=" + scraper_dropdown.value;
|
||||
}else{
|
||||
|
||||
var ac_req_appendix = "";
|
||||
}
|
||||
|
||||
function getsearchboxtext(){
|
||||
|
||||
var value =
|
||||
searchbox.value
|
||||
.trim()
|
||||
.replace(
|
||||
/ +/g,
|
||||
" "
|
||||
)
|
||||
.toLowerCase();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
searchbox.addEventListener("input", async function(){
|
||||
|
||||
// ratelimit on input only
|
||||
// dont ratelimit if we already have res
|
||||
if(typeof autocomplete_cache[getsearchboxtext()] != "undefined"){
|
||||
|
||||
await getac();
|
||||
}else{
|
||||
|
||||
await getac_ratelimit();
|
||||
}
|
||||
});
|
||||
|
||||
async function getac(){
|
||||
|
||||
var curvalue = getsearchboxtext();
|
||||
|
||||
if(curvalue == ""){
|
||||
|
||||
// hide autocompleter
|
||||
autocomplete_div.style.display = "none";
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof autocomplete_cache[curvalue] == "undefined"){
|
||||
|
||||
/*
|
||||
Fetch autocomplete
|
||||
*/
|
||||
// make sure we dont fetch same thing twice
|
||||
autocomplete_cache[curvalue] = [];
|
||||
|
||||
var res = await fetch("/api/v1/ac?s=" + (encodeURIComponent(curvalue).replaceAll("%20", "+")) + ac_req_appendix);
|
||||
if(!res.ok){
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var json = await res.json();
|
||||
|
||||
autocomplete_cache[curvalue] = json[1];
|
||||
|
||||
if(curvalue == getsearchboxtext()){
|
||||
|
||||
render_ac(curvalue, autocomplete_cache[curvalue]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
render_ac(curvalue, autocomplete_cache[curvalue]);
|
||||
}
|
||||
|
||||
var ac_func = null;
|
||||
function getac_ratelimit(){
|
||||
|
||||
return new Promise(async function(resolve, reject){
|
||||
|
||||
if(ac_func !== null){
|
||||
|
||||
clearTimeout(ac_func);
|
||||
}//else{
|
||||
|
||||
// no ratelimits
|
||||
//getac();
|
||||
//}
|
||||
|
||||
ac_func =
|
||||
setTimeout(function(){
|
||||
|
||||
ac_func = null;
|
||||
getac(); // get results after 100ms of no keystroke
|
||||
resolve();
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
|
||||
function render_ac(query, list){
|
||||
|
||||
if(list.length === 0){
|
||||
|
||||
autocomplete_div.style.display = "none";
|
||||
return;
|
||||
}
|
||||
|
||||
html = "";
|
||||
|
||||
// prepare regex
|
||||
var highlight = query.split(" ");
|
||||
var regex = [];
|
||||
|
||||
for(var k=0; k<highlight.length; k++){
|
||||
|
||||
// espace regex
|
||||
regex.push(
|
||||
highlight[k].replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
||||
);
|
||||
}
|
||||
|
||||
regex = new RegExp(highlight.join("|"), "gi");
|
||||
|
||||
for(var i=0; i<list.length; i++){
|
||||
|
||||
html +=
|
||||
'<div tabindex="0" class="entry" onclick="handle_entry_click(this);">' +
|
||||
htmlspecialchars(
|
||||
list[i]
|
||||
).replace(
|
||||
regex,
|
||||
'<u>$&</u>'
|
||||
) +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
autocomplete_div.innerHTML = html;
|
||||
autocomplete_div.style.display = "block";
|
||||
}
|
||||
|
||||
var should_focus = false;
|
||||
document.addEventListener("keydown", function(event){
|
||||
|
||||
if(event.key == "Escape"){
|
||||
|
||||
document.activeElement.blur();
|
||||
focuspos = -1;
|
||||
autocomplete_div.style.display = "none";
|
||||
return;
|
||||
}
|
||||
|
||||
if(
|
||||
is_click_within(event.target, "searchbox") === false ||
|
||||
typeof autocomplete_cache[getsearchboxtext()] == "undefined"
|
||||
){
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
switch(event.key){
|
||||
|
||||
case "ArrowUp":
|
||||
event.preventDefault();
|
||||
focuspos--;
|
||||
if(focuspos === -2){
|
||||
|
||||
focuspos = autocomplete_cache[getsearchboxtext()].length - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case "ArrowDown":
|
||||
case "Tab":
|
||||
event.preventDefault();
|
||||
|
||||
focuspos++;
|
||||
if(focuspos >= autocomplete_cache[getsearchboxtext()].length){
|
||||
|
||||
focuspos = -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case "Enter":
|
||||
should_focus = true;
|
||||
|
||||
if(focuspos !== -1){
|
||||
|
||||
// replace input content
|
||||
event.preventDefault();
|
||||
searchbox.value =
|
||||
autocomplete_div.getElementsByClassName("entry")[focuspos].innerText;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
focuspos = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if(focuspos === -1){
|
||||
|
||||
searchbox.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
autocomplete_div.getElementsByClassName("entry")[focuspos].focus();
|
||||
});
|
||||
|
||||
window.addEventListener("blur", function(){
|
||||
|
||||
autocomplete_div.style.display = "none";
|
||||
});
|
||||
|
||||
document.addEventListener("keyup", function(event){
|
||||
|
||||
// handle ENTER key on entry
|
||||
if(should_focus){
|
||||
|
||||
should_focus = false;
|
||||
searchbox.focus();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("mousedown", function(event){
|
||||
|
||||
// hide input if click is outside
|
||||
if(is_click_within(event.target, "searchbox") === false){
|
||||
|
||||
autocomplete_div.style.display = "none";
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
function handle_entry_click(event){
|
||||
|
||||
searchbox.value = event.innerText;
|
||||
focuspos = -1;
|
||||
searchbox.focus();
|
||||
}
|
||||
|
||||
searchbox.addEventListener("focus", function(){
|
||||
|
||||
focuspos = -1;
|
||||
getac();
|
||||
});
|
||||
}
|
||||
}
|
BIN
src/static/icon/amazon.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/static/icon/appstore.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/static/icon/call.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/static/icon/directions.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/static/icon/facebook.png
Normal file
After Width: | Height: | Size: 555 B |
BIN
src/static/icon/gamespot.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/static/icon/github.png
Normal file
After Width: | Height: | Size: 508 B |
BIN
src/static/icon/googleplay.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/static/icon/imdb.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/static/icon/instagram.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/static/icon/itunes.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/static/icon/microsoft.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/static/icon/quora.png
Normal file
After Width: | Height: | Size: 835 B |
BIN
src/static/icon/reddit.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/static/icon/rottentomatoes.png
Normal file
After Width: | Height: | Size: 468 B |
BIN
src/static/icon/sciencedirect.png
Normal file
After Width: | Height: | Size: 354 B |
BIN
src/static/icon/soundcloud.png
Normal file
After Width: | Height: | Size: 592 B |
BIN
src/static/icon/spotify.png
Normal file
After Width: | Height: | Size: 689 B |
BIN
src/static/icon/steam.png
Normal file
After Width: | Height: | Size: 622 B |
BIN
src/static/icon/twitter.png
Normal file
After Width: | Height: | Size: 698 B |
BIN
src/static/icon/w3html.png
Normal file
After Width: | Height: | Size: 1000 B |
BIN
src/static/icon/website.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/static/icon/wikipedia.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/static/icon/youtube.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/static/misc/christmas-dark-bg.png
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
src/static/misc/christmas-hat.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
src/static/misc/christmas-white-bg.png
Normal file
After Width: | Height: | Size: 107 KiB |
BIN
src/static/misc/snow.png
Normal file
After Width: | Height: | Size: 188 KiB |
473
src/static/serverping.js
Normal file
@ -0,0 +1,473 @@
|
||||
|
||||
function htmlspecialchars(str){
|
||||
|
||||
if(str === null){
|
||||
|
||||
return "<i><Empty></i>";
|
||||
}
|
||||
|
||||
var map = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
}
|
||||
|
||||
return str.replace(/[&<>"']/g, function(m){return map[m];});
|
||||
}
|
||||
|
||||
// initialize garbage
|
||||
var list = [];
|
||||
var pinged_list = [];
|
||||
var reqs = 0;
|
||||
var errors = 0;
|
||||
var sort = 6; // highest version first
|
||||
|
||||
// check for instance redirect stuff
|
||||
var redir = [];
|
||||
var target = "/web?";
|
||||
new URL(window.location.href)
|
||||
.searchParams
|
||||
.forEach(
|
||||
function(value, key){
|
||||
|
||||
if(key == "target"){
|
||||
|
||||
target = "/" + encodeURIComponent(value) + "?";
|
||||
return;
|
||||
}
|
||||
|
||||
if(key == "npt"){ return; }
|
||||
redir.push(encodeURIComponent(key) + "=" + encodeURIComponent(value))
|
||||
}
|
||||
);
|
||||
|
||||
if(redir.length !== 0){
|
||||
|
||||
redir = target + redir.join("&");
|
||||
}else{
|
||||
|
||||
redir = "";
|
||||
}
|
||||
|
||||
var quote = document.createElement("div");
|
||||
quote.className = "quote";
|
||||
quote.innerHTML = 'Pinged <b>0</b> servers (<b>0</b> failed requests)';
|
||||
var [div_servercount, div_failedreqs] =
|
||||
quote.getElementsByTagName("b");
|
||||
|
||||
var noscript = document.getElementsByTagName("noscript")[0];
|
||||
document.body.insertBefore(quote, noscript.nextSibling);
|
||||
|
||||
// create table
|
||||
var table = document.createElement("table");
|
||||
table.innerHTML =
|
||||
'<thead>' +
|
||||
'<tr>' +
|
||||
'<th class="extend">Server</th>' +
|
||||
'<th>Address</th>' +
|
||||
'<th>Bot protection</th>' +
|
||||
'<th title="Amount of legit requests processed since the last APCU cache clear (usually happens at midnight)">Real reqs (?)</th>' +
|
||||
'<th title="Amount of filtered requests processed since the last APCU cache clear (usually happens at midnight)">Bot reqs (?)</th>' +
|
||||
'<th>API</th>' +
|
||||
'<th><div class="arrow up"></div>Version</th>' +
|
||||
'</tr>' +
|
||||
'</thead>' +
|
||||
'<tbody></tbody>';
|
||||
|
||||
document.body.insertBefore(table, quote.nextSibling);
|
||||
|
||||
// handle sorting clicks
|
||||
var tbody = table.getElementsByTagName("tbody")[0];
|
||||
var th = table.getElementsByTagName("th");
|
||||
|
||||
for(var i=0; i<th.length; i++){
|
||||
|
||||
th[i].addEventListener("click", function(event){
|
||||
|
||||
if(event.target.className.includes("arrow")){
|
||||
|
||||
var div = event.target.parentElement;
|
||||
}else{
|
||||
|
||||
var div = event.target;
|
||||
}
|
||||
|
||||
var arrow = div.getElementsByClassName("arrow");
|
||||
var orientation = 0; // up
|
||||
|
||||
if(arrow.length === 0){
|
||||
|
||||
// delete arrow and add new one
|
||||
arrow = document.getElementsByClassName("arrow");
|
||||
arrow[0].remove();
|
||||
|
||||
arrow = document.createElement("div");
|
||||
arrow.className = "arrow up";
|
||||
div.insertBefore(arrow, event.target.firstChild);
|
||||
}else{
|
||||
|
||||
// switch arrow position
|
||||
if(arrow[0].className == "arrow down"){
|
||||
|
||||
arrow[0].className = "arrow up";
|
||||
}else{
|
||||
|
||||
arrow[0].className = "arrow down";
|
||||
orientation = 1;
|
||||
}
|
||||
}
|
||||
|
||||
switch(div.textContent.toLowerCase()){
|
||||
|
||||
case "server": sort = 0 + orientation; break;
|
||||
case "address": sort = 2 + orientation; break;
|
||||
case "bot protection": sort = 4 + orientation; break;
|
||||
case "real reqs (?)": sort = 6 + orientation; break;
|
||||
case "bot reqs (?)": sort = 8 + orientation; break;
|
||||
case "api": sort = 10 + orientation; break;
|
||||
case "version": sort = 12 + orientation; break;
|
||||
}
|
||||
|
||||
render_list();
|
||||
});
|
||||
}
|
||||
|
||||
function validate_url(url, allow_http = false){
|
||||
|
||||
try{
|
||||
|
||||
url = new URL(url);
|
||||
if(
|
||||
url.protocol == "https:" ||
|
||||
(
|
||||
(
|
||||
allow_http === true ||
|
||||
window.location.protocol == "http:"
|
||||
) &&
|
||||
url.protocol == "http:"
|
||||
)
|
||||
){
|
||||
|
||||
return true;
|
||||
}
|
||||
}catch(error){} // do nothing
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function number_format(int){
|
||||
|
||||
return new Intl.NumberFormat().format(int);
|
||||
}
|
||||
|
||||
// parse initial server list
|
||||
fetch_server(window.location.origin);
|
||||
|
||||
async function fetch_server(server){
|
||||
|
||||
if(!validate_url(server)){
|
||||
console.warn("Invalid server URL: " + server);
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure baseURL is origin
|
||||
server = new URL(server).origin;
|
||||
// prevent multiple fetches
|
||||
for(var i=0; i<list.length; i++){
|
||||
|
||||
if(list[i] == server){
|
||||
|
||||
// serber was already fetched
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// prevent future fetches
|
||||
list.push(server);
|
||||
|
||||
var data = null;
|
||||
|
||||
try{
|
||||
|
||||
var payload = await fetch(server + "/ami4get");
|
||||
|
||||
if(payload.status !== 200){
|
||||
|
||||
// endpoint is not available
|
||||
errors++;
|
||||
div_failedreqs.textContent = number_format(errors);
|
||||
console.warn(server + ": Invalid HTTP code " + payload.status);
|
||||
return;
|
||||
}
|
||||
|
||||
data = await payload.json();
|
||||
|
||||
}catch(error){
|
||||
|
||||
errors++;
|
||||
div_failedreqs.textContent = number_format(errors);
|
||||
console.warn(server + ": Could not fetch or decode JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
// sanitize data
|
||||
if(
|
||||
typeof data.status != "string" ||
|
||||
data.status != "ok" ||
|
||||
typeof data.server != "object" ||
|
||||
!(
|
||||
typeof data.server.name == "string" ||
|
||||
(
|
||||
typeof data.server.name == "object" &&
|
||||
data.server.name === null
|
||||
)
|
||||
) ||
|
||||
typeof data.service != "string" ||
|
||||
data.service != "4get" ||
|
||||
(
|
||||
typeof data.server.description != "string" &&
|
||||
data.server.description !== null
|
||||
) ||
|
||||
typeof data.server.bot_protection != "number" ||
|
||||
typeof data.server.real_requests != "number" ||
|
||||
typeof data.server.bot_requests != "number" ||
|
||||
typeof data.server.api_enabled != "boolean" ||
|
||||
typeof data.server.alt_addresses != "object" ||
|
||||
typeof data.server.version != "number" ||
|
||||
typeof data.instances != "object"
|
||||
){
|
||||
|
||||
errors++;
|
||||
div_failedreqs.textContent = number_format(errors);
|
||||
console.warn(server + ": Malformed JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
data.server.ip = server;
|
||||
|
||||
reqs++;
|
||||
div_servercount.textContent = number_format(reqs);
|
||||
|
||||
var total = pinged_list.push(data) - 1;
|
||||
pinged_list[total].index = total;
|
||||
|
||||
render_list();
|
||||
|
||||
// get more serbers
|
||||
for(var i=0; i<data.instances.length; i++){
|
||||
|
||||
fetch_server(data.instances[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function sorta(object, element, order){
|
||||
|
||||
return object.slice().sort(
|
||||
function(a, b){
|
||||
|
||||
if(order){
|
||||
|
||||
return a.server[element] - b.server[element];
|
||||
}
|
||||
|
||||
return b.server[element] - a.server[element];
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function textsort(object, element, order){
|
||||
|
||||
var sort = object.slice().sort(
|
||||
function(a, b){
|
||||
|
||||
return a.server[element].localeCompare(b.server[element]);
|
||||
}
|
||||
);
|
||||
|
||||
if(!order){
|
||||
return sort.reverse();
|
||||
}
|
||||
|
||||
return sort;
|
||||
}
|
||||
|
||||
function render_list(){
|
||||
|
||||
var sorted_list = [];
|
||||
|
||||
// sort
|
||||
var filter = Boolean(sort % 2);
|
||||
|
||||
switch(sort){
|
||||
|
||||
case 0:
|
||||
case 1:
|
||||
sorted_list = textsort(pinged_list, "name", filter === true ? false : true);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
sorted_list = textsort(pinged_list, "ip", filter === true ? false : true);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
case 5:
|
||||
sorted_list = sorta(pinged_list, "bot_protection", filter === true ? false : true);
|
||||
break;
|
||||
|
||||
case 6:
|
||||
case 7:
|
||||
sorted_list = sorta(pinged_list, "real_requests", filter);
|
||||
break;
|
||||
|
||||
case 8:
|
||||
case 9:
|
||||
sorted_list = sorta(pinged_list, "bot_requests", filter);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
case 11:
|
||||
sorted_list = sorta(pinged_list, "api_enabled", filter);
|
||||
break;
|
||||
|
||||
case 12:
|
||||
case 13:
|
||||
sorted_list = sorta(pinged_list, "version", filter);
|
||||
break;
|
||||
}
|
||||
|
||||
// render tabloid
|
||||
var html = "";
|
||||
|
||||
for(var k=0; k<sorted_list.length; k++){
|
||||
|
||||
html += '<tr onclick="show_server(' + sorted_list[k].index + ');">';
|
||||
|
||||
for(var i=0; i<7; i++){
|
||||
|
||||
html += '<td';
|
||||
|
||||
switch(i){
|
||||
|
||||
// server name
|
||||
case 0: html += ' class="extend">' + htmlspecialchars(sorted_list[k].server.name); break;
|
||||
case 1: html += '>' + htmlspecialchars(new URL(sorted_list[k].server.ip).host); break;
|
||||
case 2: // bot protection
|
||||
switch(sorted_list[k].server.bot_protection){
|
||||
|
||||
case 0:
|
||||
html += '><span style="color:var(--green);">Disabled</span>';
|
||||
break;
|
||||
|
||||
case 1:
|
||||
html += '><span style="color:var(--yellow);">Image captcha</span>';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
html += '><span style="color:var(--red);">Invite only</span>';
|
||||
break;
|
||||
|
||||
default:
|
||||
html += '>Unknown';
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: // real reqs
|
||||
html += '>' + number_format(sorted_list[k].server.real_requests);
|
||||
break;
|
||||
|
||||
case 4: // bot reqs
|
||||
html += '>' + number_format(sorted_list[k].server.bot_requests);
|
||||
break;
|
||||
|
||||
case 5: // api enabled
|
||||
|
||||
if(sorted_list[k].server.api_enabled){
|
||||
|
||||
html += '><span style="color:var(--green);">Yes</span>';
|
||||
}else{
|
||||
|
||||
html += '><span style="color:var(--red);">No</span>';
|
||||
}
|
||||
break;
|
||||
|
||||
// version
|
||||
case 6: html += ">v" + sorted_list[k].server.version; break;
|
||||
}
|
||||
|
||||
html += '</td>';
|
||||
}
|
||||
|
||||
html += '</tr>';
|
||||
}
|
||||
|
||||
console.log(html);
|
||||
|
||||
tbody.innerHTML = html;
|
||||
}
|
||||
|
||||
var popup_bg = document.getElementById("popup-bg");
|
||||
var popup_wrapper = document.getElementsByClassName("popup-wrapper")[0];
|
||||
var popup = popup_wrapper.getElementsByClassName("popup")[0];
|
||||
var popup_shown = false;
|
||||
|
||||
popup_bg.addEventListener("click", function(){
|
||||
|
||||
popup_wrapper.style.display = "none";
|
||||
popup_bg.style.display = "none";
|
||||
});
|
||||
|
||||
function show_server(serverid){
|
||||
|
||||
var html =
|
||||
'<h2>' + htmlspecialchars(pinged_list[serverid].server.name) + '</h2>' +
|
||||
'Description' +
|
||||
'<div class="code">' + htmlspecialchars(pinged_list[serverid].server.description) + '</div>';
|
||||
|
||||
var url_obj = new URL(pinged_list[serverid].server.ip);
|
||||
var url = htmlspecialchars(url_obj.origin);
|
||||
var domain = url_obj.hostname;
|
||||
|
||||
html +=
|
||||
'URL: <a rel="noreferer" target="_BLANK" href="' + url + redir + '">' + url + '</a> <a rel="noreferer" target="_BLANK" href="https://browserleaks.com/ip/' + encodeURIComponent(domain) + '">(IP lookup)</a>' +
|
||||
'<br><br>Alt addresses:';
|
||||
|
||||
var len = pinged_list[serverid].server.alt_addresses.length;
|
||||
|
||||
if(len === 0){
|
||||
|
||||
html += ' <i><Empty></i>';
|
||||
}else{
|
||||
|
||||
html += '<ul>';
|
||||
|
||||
for(var i=0; i<len; i++){
|
||||
|
||||
var url_obj = new URL(pinged_list[serverid].server.alt_addresses[i]);
|
||||
var url = htmlspecialchars(url_obj.origin);
|
||||
var domain = url_obj.hostname;
|
||||
|
||||
if(validate_url(pinged_list[serverid].server.alt_addresses[i], true)){
|
||||
|
||||
html += '<li><a rel="noreferer" href="' + url + redir + '" target="_BLANK">' + url + '</a> <a rel="noreferer" target="_BLANK" href="https://browserleaks.com/ip/' + encodeURIComponent(domain) + '">(IP lookup)</a></li>';
|
||||
}else{
|
||||
|
||||
console.warn(pinged_list[serverid].server.ip + ": Invalid peer URL => " + pinged_list[serverid].server.alt_addresses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
html += '</ul>';
|
||||
}
|
||||
popup.innerHTML = html;
|
||||
|
||||
popup_wrapper.style.display = "block";
|
||||
popup_bg.style.display = "block";
|
||||
}
|
||||
|
||||
function hide_server(){
|
||||
|
||||
popup_wrapper.style.display = "none";
|
||||
popup_bg.style.display = "none";
|
||||
}
|
1384
src/static/style.css
Normal file
20
src/static/themes/Catppuccin Latte.css
Normal file
@ -0,0 +1,20 @@
|
||||
:root{
|
||||
/* background */
|
||||
--1d2021: #eff1f5;
|
||||
--282828: #eff1f5;
|
||||
--3c3836: #dce0e8;
|
||||
--504945: #5c5f77;
|
||||
|
||||
/* font */
|
||||
--928374: #8c8fa1;
|
||||
--a89984: #4c4f69;
|
||||
--bdae93: #4c4f69;
|
||||
--8ec07c: #df8e1d;
|
||||
--ebdbb2: #4c4f69;
|
||||
|
||||
/* code highlighter */
|
||||
--comment: #e64553;
|
||||
--default: #eff1f5;
|
||||
--keyword: #df8e1d;
|
||||
--string: #209fb5;
|
||||
}
|
20
src/static/themes/Catppuccin Mocha.css
Normal file
@ -0,0 +1,20 @@
|
||||
:root{
|
||||
/* background */
|
||||
--1d2021: #1e1e2e;
|
||||
--282828: #313244;
|
||||
--3c3836: #45475a;
|
||||
--504945: #585b70;
|
||||
|
||||
/* font */
|
||||
--928374: #bac2de;
|
||||
--a89984: #a6adc8;
|
||||
--bdae93: #cdd6f4;
|
||||
--8ec07c: #a6e3a1;
|
||||
--ebdbb2: #f9e2af;
|
||||
|
||||
/* code highlighter */
|
||||
--comment: #f5e0dc;
|
||||
--default: #f2cdcd;
|
||||
--keyword: #fab387;
|
||||
--string: #74c7ec;
|
||||
}
|
31
src/static/themes/Cream.css
Normal file
@ -0,0 +1,31 @@
|
||||
:root{
|
||||
/* background */
|
||||
--1d2021: #bdae93;
|
||||
--282828: #a89984;
|
||||
--3c3836: #a89984;
|
||||
--504945: #504945;
|
||||
|
||||
/* font */
|
||||
--928374: #1d2021;
|
||||
--a89984: #282828;
|
||||
--bdae93: #3c3836;
|
||||
--8ec07c: #52520e;
|
||||
--ebdbb2: #1d2021;
|
||||
|
||||
/* code highlighter */
|
||||
--comment: #6a4400;
|
||||
--default: #d4be98;
|
||||
--keyword: #4a4706;
|
||||
--string: #076678;
|
||||
|
||||
/* color codes for instance list */
|
||||
--green: #636311;
|
||||
--yellow: #8a6214;
|
||||
--red: #711410;
|
||||
}
|
||||
|
||||
.autocomplete .entry:hover,
|
||||
.instances th:hover
|
||||
{
|
||||
background:#928374;
|
||||
}
|
95
src/static/themes/Dark Christmas.css
Normal file
@ -0,0 +1,95 @@
|
||||
body{
|
||||
background-image:url("/static/misc/christmas-dark-bg.png");
|
||||
}
|
||||
|
||||
.home::before{
|
||||
content:"";
|
||||
position:fixed;
|
||||
top:0;
|
||||
left:0;
|
||||
width:100%;
|
||||
height:100%;
|
||||
background-image:url("/static/misc/snow.png");
|
||||
pointer-events:none;
|
||||
z-index:-1;
|
||||
animation:snowfall 12s linear infinite;
|
||||
opacity:.6;
|
||||
}
|
||||
|
||||
.home{
|
||||
background:#1d2021;
|
||||
}
|
||||
|
||||
@keyframes snowfall{
|
||||
from{
|
||||
background-position:0% 0px;
|
||||
}
|
||||
|
||||
to{
|
||||
background-position:0% 600px;
|
||||
}
|
||||
}
|
||||
|
||||
.web .answer::after{
|
||||
display:none;
|
||||
}
|
||||
|
||||
:root{
|
||||
/* background */
|
||||
--1d2021: #1d2021;
|
||||
--282828: #282828;
|
||||
--3c3836: #3c3836;
|
||||
--504945: #504945;
|
||||
|
||||
/* font */
|
||||
--928374: #928374;
|
||||
--a89984: #bdae93;
|
||||
--bdae93: #d5c4a1;
|
||||
--8ec07c: #8ec07c;
|
||||
--ebdbb2: #ebdbb2;
|
||||
|
||||
/* code highlighter */
|
||||
--comment: #9e8e73;
|
||||
--default: #d4be98;
|
||||
--keyword: #d8a657;
|
||||
--string: #7daea7;
|
||||
|
||||
/* color codes for instance list */
|
||||
--green: #b8bb26;
|
||||
--yellow: #d8a657;
|
||||
--red: #fb4934;
|
||||
}
|
||||
|
||||
.web .wiki-head table, .about table, .web .info-table, .instances table{
|
||||
background:#1d202170;
|
||||
color:#bdae93;
|
||||
}
|
||||
|
||||
.web .wiki-head tr:nth-child(odd), .about table tr:nth-child(odd), .web .info-table tr:nth-child(even), .nextpage, .spoiler-button, .instances tbody tr:nth-child(even){
|
||||
background:#28282870;
|
||||
color:#bdae93;
|
||||
}
|
||||
|
||||
.instances tbody tr:hover{
|
||||
background:#3c383690;
|
||||
}
|
||||
|
||||
.wiki-head .description{
|
||||
overflow:initial !important;
|
||||
}
|
||||
|
||||
.wiki-head .photo{
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.wiki-head .photo::after{
|
||||
content:"";
|
||||
position:absolute;
|
||||
width:100px;
|
||||
height:100px;
|
||||
background-image:url("/static/misc/christmas-hat.png");
|
||||
background-size:contain;
|
||||
top:-61px;
|
||||
left:-31px;
|
||||
transform:rotate(310deg);
|
||||
}
|
323
src/static/themes/Gore's shitty theme.css
Normal file
@ -0,0 +1,323 @@
|
||||
:root{
|
||||
--1d2021: #1d2021;
|
||||
--282828: #282828;
|
||||
--3c3836: #3c3836;
|
||||
--504945: #504945;
|
||||
|
||||
/* font */
|
||||
--928374: #928374;
|
||||
--a89984: #c9c5bf;
|
||||
--bdae93: #bdae93;
|
||||
--8ec07c: #8ec07c;
|
||||
--ebdbb2: #ebdbb2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
body{
|
||||
padding:15px 4% 40px;
|
||||
margin:unset;
|
||||
}
|
||||
|
||||
h1,h2,h3,h4,h5,h6{
|
||||
padding:0;
|
||||
margin:0 0 7px 0;
|
||||
line-height:initial;
|
||||
color:var(--bdae93);
|
||||
}
|
||||
|
||||
h3,h4,h5,h6{
|
||||
margin-bottom:14px;
|
||||
}
|
||||
|
||||
/*
|
||||
Web styles
|
||||
*/
|
||||
|
||||
.searchbox input[type="submit"]{
|
||||
float:right;
|
||||
cursor:pointer;
|
||||
padding:0 10px;
|
||||
border-left: 1px solid var(--504945);
|
||||
background: #723c0b;
|
||||
}
|
||||
|
||||
|
||||
.searchbox input{
|
||||
all:unset;
|
||||
line-height:36px;
|
||||
box-sizing:border-box;
|
||||
height:36px;
|
||||
}
|
||||
|
||||
.searchbox:focus-within{
|
||||
border:1px solid #ee8a9c;
|
||||
}
|
||||
|
||||
.autocomplete{
|
||||
display:none;
|
||||
position:absolute;
|
||||
top:35px;
|
||||
left:-1px;
|
||||
right:-1px;
|
||||
background:var(--282828);
|
||||
border:1px solid var(--504945);
|
||||
border-top:none;
|
||||
border-radius:0 0 2px 2px;
|
||||
z-index:10;
|
||||
}
|
||||
|
||||
.autocomplete .entry{
|
||||
overflow:hidden;
|
||||
padding:4px 10px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.autocomplete .title{
|
||||
float:left;
|
||||
}
|
||||
|
||||
.autocomplete .subtext{
|
||||
float:right;
|
||||
font-size:14px;
|
||||
color:var(--928374);
|
||||
margin-left:7px;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
|
||||
.tabs{
|
||||
padding-bottom:0px;
|
||||
}
|
||||
|
||||
.tabs .tab{
|
||||
text-decoration:none;
|
||||
color:#d3d0c1;
|
||||
padding:0px 10px;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
|
||||
.tabs .tab.selected{
|
||||
border-bottom:2px solid #fc92a5;
|
||||
}
|
||||
|
||||
/* Filters */
|
||||
.filters{
|
||||
padding-bottom:12px;
|
||||
padding-top:7px;
|
||||
margin-bottom:7px;
|
||||
background-color:#232525
|
||||
}
|
||||
|
||||
.filters .filter{
|
||||
display:inline-block;
|
||||
margin-right:7px;
|
||||
}
|
||||
|
||||
.filters .filter .title{
|
||||
font-size:13px;
|
||||
}
|
||||
|
||||
.filters .filter input,
|
||||
.filters .filter select{
|
||||
all:unset;
|
||||
display:block;
|
||||
border:1px solid var(--504945);
|
||||
border-radius:2px;
|
||||
font-size:14px;
|
||||
padding:0 2px;
|
||||
width:127px;
|
||||
height:22px;
|
||||
}
|
||||
|
||||
/* infobox */
|
||||
.web .infobox{
|
||||
border:1px dashed var(--504945);
|
||||
padding:10px;
|
||||
margin-bottom:17px;
|
||||
}
|
||||
|
||||
.web .infobox .code{
|
||||
white-space:initial;
|
||||
}
|
||||
|
||||
.web .infobox ul{
|
||||
padding-left:27px;
|
||||
margin-bottom:0;
|
||||
}
|
||||
|
||||
.web .infobox a{
|
||||
color:var(--bdae93);
|
||||
}
|
||||
|
||||
.web .infobox a:hover{
|
||||
text-decoration:underline;
|
||||
}
|
||||
|
||||
/* text-result */
|
||||
|
||||
.web .url .part{
|
||||
font-size:15px;
|
||||
text-decoration:none;
|
||||
color:#90c186;
|
||||
}
|
||||
|
||||
.web .separator::before{
|
||||
content:"/";
|
||||
padding:0 4px;
|
||||
color:#5ab442;
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
|
||||
.web .hover{
|
||||
display:block;
|
||||
text-decoration:none;
|
||||
color:var(--a89984);
|
||||
overflow:hidden;
|
||||
clear:left;
|
||||
padding-top:3px;
|
||||
}
|
||||
|
||||
.web .text-result .title{
|
||||
font-size:18px;
|
||||
color:#81b5f4;
|
||||
margin-bottom:5px;
|
||||
}
|
||||
|
||||
.web .text-result a:visited .title{
|
||||
color:#aa77c1 !important;
|
||||
}
|
||||
|
||||
.theme-white .web .text-result a:visited .title{
|
||||
color:#9760b1 !important;
|
||||
}
|
||||
|
||||
|
||||
.web .text-result .greentext{
|
||||
font-size:14px;
|
||||
color:var(--bdae93);
|
||||
}
|
||||
|
||||
|
||||
/* favicon */
|
||||
|
||||
|
||||
.favicon-dropdown a{
|
||||
text-decoration:none;
|
||||
color:#d3d0c1;
|
||||
display:block;
|
||||
padding:2px 7px 2px 5px;
|
||||
font-size:13px;
|
||||
}
|
||||
|
||||
|
||||
.web .favicon img,
|
||||
.favicon-dropdown img{
|
||||
margin:3px 7px 0 0;
|
||||
height:16px;
|
||||
font-size:12px;
|
||||
line-height:16px;;
|
||||
display:block;
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
|
||||
.web .sublinks{
|
||||
padding:17px 10px;
|
||||
font-size:15px;
|
||||
color:var(--#928374);
|
||||
}
|
||||
|
||||
|
||||
.web .text-result .sublinks:last-child{
|
||||
padding-bottom:0;
|
||||
}
|
||||
|
||||
|
||||
/* Wikipedia head */
|
||||
.wiki-head{
|
||||
padding:5px;
|
||||
background-color: #322f2b
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Images tab
|
||||
*/
|
||||
|
||||
#images{
|
||||
line-height:15px;
|
||||
overflow:hidden;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
#images .image-wrapper{
|
||||
width:20%;
|
||||
float:left;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#images .image .title{
|
||||
white-space:nowrap;
|
||||
overflow:hidden;
|
||||
margin-bottom:7px;
|
||||
font-weight:bold;
|
||||
color:var(--bdae93);
|
||||
}
|
||||
|
||||
|
||||
#popup-status{
|
||||
display:none;
|
||||
position:fixed;
|
||||
top:0;
|
||||
left:0;
|
||||
width:100%;
|
||||
height:35px;
|
||||
background:var(--1d2021);
|
||||
border-bottom:1px solid var(--928374);
|
||||
}
|
||||
|
||||
/*
|
||||
Settings page
|
||||
*/
|
||||
|
||||
|
||||
.web .settings-submit a{
|
||||
margin-right:17px;
|
||||
color:#bdae93;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Responsive image
|
||||
*/
|
||||
@media only screen and (max-width: 1454px){ #images .image-wrapper{ width:25%; } }
|
||||
@media only screen and (max-width: 1161px){ #images .image-wrapper{ width:25%; } }
|
||||
@media only screen and (max-width: 750px){ #images .image-wrapper{ width:50%; } }
|
||||
@media only screen and (max-width: 450px){ #images .image-wrapper{ width:100%; } }
|
||||
|
||||
|
||||
/*
|
||||
Responsive design
|
||||
*/
|
||||
@media only screen and (max-width: 1550px){
|
||||
|
||||
|
||||
.web .left,
|
||||
.searchbox{
|
||||
width:60%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1000px){
|
||||
|
||||
}
|
||||
|
||||
.type{
|
||||
color:var(--bdae93);
|
||||
}
|
||||
}
|
94
src/static/themes/White Christmas.css
Normal file
@ -0,0 +1,94 @@
|
||||
body{
|
||||
background:#e7e7e7;
|
||||
background-image:url("/static/misc/christmas-white-bg.png");
|
||||
}
|
||||
|
||||
.home::before{
|
||||
content:"";
|
||||
position:fixed;
|
||||
top:0;
|
||||
left:0;
|
||||
width:100%;
|
||||
height:100%;
|
||||
background:#b9b5b380;
|
||||
background-image:url("/static/misc/snow.png");
|
||||
pointer-events:none;
|
||||
z-index:-1;
|
||||
animation:snowfall 12s linear infinite;
|
||||
}
|
||||
|
||||
.web .answer::after{
|
||||
display:none;
|
||||
}
|
||||
|
||||
@keyframes snowfall{
|
||||
from{
|
||||
background-position:0% 0px;
|
||||
}
|
||||
|
||||
to{
|
||||
background-position:0% 600px;
|
||||
}
|
||||
}
|
||||
|
||||
:root{
|
||||
/* background */
|
||||
--1d2021: #bdae93;
|
||||
--282828: #e7e7e7;
|
||||
--3c3836: #e7e7e7;
|
||||
--504945: #504945;
|
||||
|
||||
/* font */
|
||||
--928374: #1d2021;
|
||||
--a89984: #282828;
|
||||
--bdae93: #3c3836;
|
||||
--8ec07c: #52520e;
|
||||
--ebdbb2: #1d2021;
|
||||
|
||||
/* code highlighter */
|
||||
--comment: #6a4400;
|
||||
--default: #d4be98;
|
||||
--keyword: #4a4706;
|
||||
--string: #076678;
|
||||
|
||||
/* color codes for instance list */
|
||||
--green: #636311;
|
||||
--yellow: #8a6214;
|
||||
--red: #711410;
|
||||
}
|
||||
|
||||
.autocomplete .entry:hover{
|
||||
background:#92837480;
|
||||
}
|
||||
|
||||
.web .wiki-head table, .about table, .web .info-table, .instances table{
|
||||
background:#a8998470;
|
||||
}
|
||||
|
||||
.web .wiki-head tr:nth-child(odd), .about table tr:nth-child(odd), .web .info-table tr:nth-child(even), .nextpage, .spoiler-button, .instances tbody tr:nth-child(even){
|
||||
background:#bdae9370;
|
||||
}
|
||||
|
||||
.instances tbody tr:hover{
|
||||
background:#92837480;
|
||||
}
|
||||
|
||||
.wiki-head .description{
|
||||
overflow:initial !important;
|
||||
}
|
||||
|
||||
.wiki-head .photo{
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.wiki-head .photo::after{
|
||||
content:"";
|
||||
position:absolute;
|
||||
width:100px;
|
||||
height:100px;
|
||||
background-image:url("/static/misc/christmas-hat.png");
|
||||
background-size:contain;
|
||||
top:-61px;
|
||||
left:-31px;
|
||||
transform:rotate(310deg);
|
||||
}
|
40
src/static/themes/Wine.css
Normal file
@ -0,0 +1,40 @@
|
||||
:root
|
||||
{
|
||||
--accent : #f79e98;
|
||||
--1d2021 : #180d0c;
|
||||
--282828 : #180d0c;
|
||||
--3c3836 : #251615;
|
||||
--504945 : #251615;
|
||||
--928374 : var(--accent);
|
||||
--a89984 : #d8c5c4;
|
||||
--bdae93 : #d8c5c4;
|
||||
--8ec07c : var(--accent);
|
||||
--ebdbb2 : #d8c5c4;
|
||||
--comment: #928374;
|
||||
--default: #DCC9BC;
|
||||
--keyword: #F07342;
|
||||
--string : var(--accent);
|
||||
--green : #959A6B;
|
||||
--yellow : #E39C45;
|
||||
--red : #CF223E;
|
||||
--white : var(--a89984);
|
||||
--black : var(--1d2021);
|
||||
--hover : #b18884
|
||||
}
|
||||
|
||||
a.link, a { color: var(--accent); text-decoration: none; }
|
||||
.searchbox { width: 23%; }
|
||||
.filters filter select { color: #E39C45; }
|
||||
.web .separator::before { color: var(--white) }
|
||||
.searchbox input[type="text"]::placeholder { color: var(--white); }
|
||||
a.link:hover
|
||||
{
|
||||
color: var(--hover);
|
||||
text-shadow: 0 0 .2rem var(--hover);
|
||||
}
|
||||
.code-inline
|
||||
{ border-color: var(--default); font-family: monospace;}
|
||||
.home #center a
|
||||
{ color: var(--accent); }
|
||||
.home .subtext
|
||||
{ color: var(--white); }
|
20
src/static/themes/gentoo.css
Normal file
@ -0,0 +1,20 @@
|
||||
:root{
|
||||
/* background */
|
||||
--1d2021: #21222d;
|
||||
--282828: #393657;
|
||||
--3c3836: #27273c;
|
||||
--504945: #aa85e1;
|
||||
|
||||
/* font */
|
||||
--928374: #906be3;
|
||||
--a89984: #9794ac;
|
||||
--bdae93: #cec0f2;
|
||||
--8ec07c: #5db6e1;
|
||||
--ebdbb2: #b194f5;
|
||||
|
||||
/* code highlighter */
|
||||
--comment: #a0c0a4;
|
||||
--default: #f00;
|
||||
--keyword: #9376e4;
|
||||
--string: #ecd78f;
|
||||
}
|