summaryrefslogtreecommitdiff
path: root/static
diff options
context:
space:
mode:
authorlolcat <will@lolcat.ca>2023-11-07 08:04:56 -0500
committerlolcat <will@lolcat.ca>2023-11-07 08:04:56 -0500
commit785452873f0ee0a27fc157b482b7551560f0282d (patch)
tree4c70e240031ed3868425ca683c83ebfd378a9159 /static
parent64b090ee058953aed2246967332c7f0b6623cd8f (diff)
fix typo
Diffstat (limited to 'static')
-rw-r--r--static/client.js32
-rw-r--r--static/serverping.js495
-rw-r--r--static/style.css140
-rw-r--r--static/themes/Cream.css31
4 files changed, 665 insertions, 33 deletions
diff --git a/static/client.js b/static/client.js
index 2e691f8..5935f92 100644
--- a/static/client.js
+++ b/static/client.js
@@ -318,11 +318,23 @@ if(image_class !== null){
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": Math.round(click.target.naturalWidth),
- "height": Math.round(click.target.naturalHeight)
+ "width": w,
+ "height": h
}
];
@@ -362,10 +374,22 @@ if(image_class !== null){
var imagesize = elem.getElementsByTagName("img")[0];
+ var imagesize_w = 0;
+ var imagesize_h = 0;
+
if(imagesize.complete){
- var imagesize_w = imagesize.naturalWidth;
- var imagesize_h = imagesize.naturalHeight;
+ 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++){
diff --git a/static/serverping.js b/static/serverping.js
new file mode 100644
index 0000000..5fe285d
--- /dev/null
+++ b/static/serverping.js
@@ -0,0 +1,495 @@
+
+function htmlspecialchars(str){
+
+ if(str === null){
+
+ return "<i>&lt;Empty&gt;</i>";
+ }
+
+ var map = {
+ '&': '&amp;',
+ '<': '&lt;',
+ '>': '&gt;',
+ '"': '&quot;',
+ "'": '&#039;'
+ }
+
+ return str.replace(/[&<>"']/g, function(m){return map[m];});
+}
+
+// initialize garbage
+var list = [];
+var pinged_list = [];
+var reqs = 0;
+var errors = 0;
+var sort = 0; // lower ping 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 += encodeURIComponent(key) + "=" + encodeURIComponent(value)
+ }
+ );
+
+if(redir != ""){
+ redir = target + 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><div class="arrow up"></div>Ping</th>' +
+ '<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>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 "ping": sort = orientation; break;
+ case "server": sort = 2 + orientation; break;
+ case "address": sort = 4 + orientation; break;
+ case "bot protection": sort = 6 + orientation; break;
+ case "real reqs (?)": sort = 8 + orientation; break;
+ case "bot reqs (?)": sort = 10 + orientation; break;
+ case "api": sort = 12 + orientation; break;
+ case "version": sort = 14 + 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
+ console.info("Already checked server: " + server);
+ return;
+ }
+ }
+
+ // prevent future fetches
+ list.push(server);
+
+ var data = null;
+ var ping = new Date().getTime();
+
+ try{
+
+ data = await fetch(
+ server + "/ami4get"
+ );
+
+ if(data.status !== 200){
+
+ // endpoint is not available
+ errors++;
+ div_failedreqs.textContent = number_format(errors);
+ console.warn(server + ": Invalid HTTP code " + data.status);
+ return;
+ }
+
+ data = await data.json();
+ data.server.ping = new Date().getTime() - ping;
+
+ }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 = sorta(pinged_list, "ping", filter === true ? false : true);
+ break;
+
+ case 2:
+ case 3:
+ sorted_list = textsort(pinged_list, "name", filter === true ? false : true);
+ break;
+
+ case 4:
+ case 5:
+ sorted_list = textsort(pinged_list, "ip", filter === true ? false : true);
+ break;
+
+ case 6:
+ case 7:
+ sorted_list = sorta(pinged_list, "bot_protection", filter === true ? false : true);
+ break;
+
+ case 8:
+ case 9:
+ sorted_list = sorta(pinged_list, "real_requests", filter);
+ break;
+
+ case 10:
+ case 11:
+ sorted_list = sorta(pinged_list, "bot_requests", filter);
+ break;
+
+ case 12:
+ case 13:
+ sorted_list = sorta(pinged_list, "api_enabled", filter);
+ break;
+
+ case 14:
+ case 15:
+ 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<8; i++){
+
+ html += '<td';
+
+ switch(i){
+
+ case 0: // server ping
+ if(sorted_list[k].server.ping <= 100){
+
+ html += '><span style="color:var(--green);">' + sorted_list[k].server.ping + '</span>';
+ break;
+ }
+
+ if(sorted_list[k].server.ping <= 200){
+
+ html += '><span style="color:var(--yellow);">' + sorted_list[k].server.ping + '</span>';
+ break;
+ }
+
+ html += '><span style="color:var(--red);">' + number_format(sorted_list[k].server.ping) + '</span>';
+ break;
+
+ // server name
+ case 1: html += ' class="extend">' + htmlspecialchars(sorted_list[k].server.name); break;
+ case 2: html += '>' + htmlspecialchars(new URL(sorted_list[k].server.ip).host); break;
+ case 3: // 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 4: // real reqs
+ html += '>' + number_format(sorted_list[k].server.real_requests);
+ break;
+
+ case 5: // bot reqs
+ html += '>' + number_format(sorted_list[k].server.bot_requests);
+ break;
+
+ case 6: // 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 7: html += ">v" + sorted_list[k].server.version; break;
+ }
+
+ html += '</td>';
+ }
+
+ html += '</tr>';
+ }
+
+ 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>&lt;Empty&gt;</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";
+}
diff --git a/static/style.css b/static/style.css
index fdb4951..bb76c2e 100644
--- a/static/style.css
+++ b/static/style.css
@@ -1,7 +1,3 @@
-
-/*
- Global styles
-*/
:root{
/* background */
--1d2021: #1d2021;
@@ -21,31 +17,11 @@
--default: #d4be98;
--keyword: #d8a657;
--string: #7daea7;
-}
-
-.theme-white{
- /* 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;
-}
-
-.theme-white .autocomplete .entry:hover{
- background:#928374;
+ /* color codes for instance list */
+ --green: #b8bb26;
+ --yellow: #d8a657;
+ --red: #fb4934;
}
audio{
@@ -516,6 +492,7 @@ h3,h4,h5,h6{
.web .favicon img,
.favicon-dropdown img{
margin:3px 7px 0 0;
+ width:16px;
height:16px;
font-size:12px;
line-height:16px;
@@ -1020,6 +997,7 @@ table tr a:last-child{
cursor:grab;
user-select:none;
pointer-events:none;
+ z-index:5;
}
#popup:active{
@@ -1046,6 +1024,7 @@ table tr a:last-child{
height:35px;
background:var(--1d2021);
border-bottom:1px solid var(--928374);
+ z-index:4;
}
#popup-bg{
@@ -1057,6 +1036,7 @@ table tr a:last-child{
width:100%;
height:100%;
display:none;
+ z-index:3;
}
#popup-status select{
@@ -1167,6 +1147,108 @@ table tr a:last-child{
}
/*
+ Instances page
+*/
+.instances table{
+ white-space:nowrap;
+ margin-top:17px;
+}
+
+.instances a{
+ color:var(--bdae93);
+}
+
+.instances tbody tr:nth-child(even){
+ background:var(--282828);
+}
+
+.instances thead{
+ outline:1px solid var(--928374);
+ outline-offset:-1px;
+ background:var(--3c3836);
+ user-select:none;
+ z-index:2;
+ position:sticky;
+ top:0;
+}
+
+.instances th{
+ cursor:row-resize;
+}
+
+.instances th:hover{
+ background:var(--504945);
+}
+
+.instances tbody{
+ outline:1px solid var(--504945);
+ outline-offset:-1px;
+ position:relative;
+ top:-1px;
+}
+
+.instances tbody tr:hover{
+ background:var(--3c3836);
+ cursor:pointer;
+}
+
+.instances .arrow{
+ display:inline-block;
+ position:relative;
+ top:6px;
+ margin-right:7px;
+ width:0;
+ height:0;
+ border:6px solid transparent;
+ border-top:10px solid var(--bdae93);
+}
+
+.instances .arrow.up{
+ top:0;
+ border:6px solid transparent;
+ border-bottom:10px solid var(--bdae93);
+}
+
+.instances th, .instances td{
+ padding:4px 7px;
+ width:0;
+}
+
+.instances .extend{
+ width:unset;
+ overflow:hidden;
+ max-width:200px;
+}
+
+.instances .popup-wrapper{
+ display:none;
+ position:fixed;
+ left:50%;
+ top:50%;
+ transform:translate(-50%, -50%);
+ width:800px;
+ max-width:100%;
+ max-height:100%;
+ overflow-x:auto;
+ padding:17px;
+ box-sizing:border-box;
+ pointer-events:none;
+ z-index:3;
+}
+
+.instances .popup{
+ border:1px solid var(--928374);
+ background:var(--282828);
+ padding:7px 10px;
+ pointer-events:initial;
+}
+
+.instances ul{
+ padding-left:20px;
+}
+
+
+/*
Responsive image
*/
@media only screen and (max-width: 1454px){ #images .image-wrapper{ width:25%; } }
@@ -1221,7 +1303,7 @@ table tr a:last-child{
width:100%;
}
- table td{
+ body:not(.instances) table td{
display:block;
width:100%;
}
diff --git a/static/themes/Cream.css b/static/themes/Cream.css
new file mode 100644
index 0000000..3d6b615
--- /dev/null
+++ b/static/themes/Cream.css
@@ -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;
+}