diff options
Diffstat (limited to 'static')
-rw-r--r-- | static/client.js | 32 | ||||
-rw-r--r-- | static/serverping.js | 495 | ||||
-rw-r--r-- | static/style.css | 140 | ||||
-rw-r--r-- | static/themes/Cream.css | 31 |
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><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 = 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><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"; +} 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; +} |