mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-02-08 23:39:32 +00:00
About The Pull Request Ports and improves my interview system that has been previously used in the summer ball and toolbox tournament events. Allows for a 'softer' panic bunker, wherein players who fall below the required living time limit can still join the server and be restricted to filling out a questionnaire. Upon completing the questionnaire, the player may be allowed into the server by an administrator. If the application is approved, they get a notification that they will be reconnected and upon reconnecting will have all verbs as they usually would. If the application is denied the user is put on a cooldown after which they may submit a new questionnaire. Players who are being interviewed (herein interviewees) have no verbs other than those required for the stat panel to function, as well as a verb to pull up the interview panel. Interviews do not persist through restarts, and the ability to join that is granted by an accepted interview is only valid for the duration of that round. Open interviews are listed under a new 'interviews' tab for admins, which is VERY similar to the existing tickets tab. Below is what a player who is flagged as an interviewee will see when they join the server. They can do nothing but respond to the questionnaire or leave. image This is what an administrator sees after an interview is submitted, they will also see a corresponding message within their chatbox, and an age-old BWOINK when an interview is submitted. image The interviews tab, which is similar to the tickets menu. You can open the interview manager panel to view all active (including non-submitted) interviews, queued (submitted) interviews, and closed interviews. image FAQ: What happens if someone submits an interview when no admins are on? It's treated like adminhelps are, the message gets sent to TGS to be dispatched off to configured end-points (like Discord or IRC), and the user is notified that their interview was handled this way. Can you configure the questions? Yes, in config/ there is now a interviews.txt file in which the welcome message and the individual questions can be set and modified. Can this be turned on and off during a round? Yes, it can be toggled like the panic bunker. It requires the panic bunker to be raised in order to function. Can interviewees have further questions asked to them? Yes, if you admin-pm them, which is possible using regular means or a conveniently placed button on the interview UI, they will be able to respond to the message. Technical details To use the interview system you must have the panic bunker enabled, this is an additional setting for the panic bunker. It can be set through the PANIC_BUNKER_INTERVIEW setting in config.txt, or alternatively enabled in-game as prompted during the panic bunker toggling process. It also can be toggled on its own using a verb added for this purpose, Toggle PB Interviews found under the server tab. These new actions are included in the logging for the panic bunker. I have also added a reporting stat to the world topic status keyword, which now reports if the interview system is on using the keyword interviews. As mentioned above, for server operators, configure the questions and welcome message in config/interviews.txt. Note to maintainers and those with big brains I had to add a call to init_verbs on the stat panel window being ready because seemingly a race condition exists wherein the add_verb of the 'view my interview' verb doesn't cause a refresh of the tabs (and therefore doesn't show the 'Interview' tab) when running in dream daemon but running it directly from visual studio code properly shows the tab. Adding a init_verbs call directly after adding the verb didn't seem to help. A note for downstreams If you don't use the HTML stat panel (which may not be a bad thing) then you will have to do some conversion from the HTML stat panel stuff used here to the old style stat panels. It's pretty trivial, but just be aware of that. You can see how I used to use the old stat panels in my PR from the summer ball, here, which should be helpful. Why It's Good For The Game This allows for a softer version of the panic bunker which impedes the flow of malicious players while allowing genuine players a chance to enter a round to gain enough time to not be affected by the panic bunker's restrictions. Changelog 🆑 bobbahbrown add: Added the interview system, a 'soft' panic bunker which lets players who would normally be blocked from joining be interviewed by admins to be selectively allowed to play. /🆑
1136 lines
30 KiB
HTML
1136 lines
30 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Stat Browser</title>
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
<meta http-equiv="Cache-Control" content="max-age=15; must-revalidate" />
|
|
<link id="goonStyle" rel="stylesheet" type="text/css" href="browserOutput_white.css" media="all" />
|
|
<style>
|
|
body {
|
|
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
|
font-size: 12px !important;
|
|
margin: 0 !important;
|
|
padding: 0 !important;
|
|
}
|
|
|
|
body.dark {
|
|
background-color: #131313;
|
|
color: #abc6ec;
|
|
}
|
|
|
|
#menu {
|
|
background-color: #F0F0F0;
|
|
position: fixed;
|
|
width: 100%;
|
|
}
|
|
|
|
.dark #menu {
|
|
background-color: #202020;
|
|
}
|
|
|
|
#statcontent {
|
|
padding: 7px 7px 7px 7px;
|
|
}
|
|
|
|
a {
|
|
color: black;
|
|
text-decoration: none
|
|
}
|
|
|
|
.dark a {
|
|
color: #abc6ec;
|
|
}
|
|
|
|
a:hover, .dark a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
ul {
|
|
list-style-type: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
background-color: #333;
|
|
}
|
|
|
|
li {
|
|
float: left;
|
|
}
|
|
|
|
li a {
|
|
display: block;
|
|
color: white;
|
|
text-align: center;
|
|
padding: 14px 16px;
|
|
text-decoration: none;
|
|
}
|
|
|
|
li a:hover:not(.active) {
|
|
background-color: #111;
|
|
}
|
|
|
|
.button-container {
|
|
display: inline-flex;
|
|
flex-wrap: wrap-reverse;
|
|
flex-direction: row;
|
|
align-items: flex-start;
|
|
overflow-x: hidden;
|
|
white-space: pre-wrap;
|
|
padding: 0 4px;
|
|
}
|
|
|
|
.button {
|
|
background-color: #dfdfdf;
|
|
border-color: #cecece;
|
|
border-width: 1px;
|
|
border-style: solid;
|
|
color: rgba(0, 0, 0, 0.7);
|
|
padding: 6px 4px;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
font-size: 12px;
|
|
margin: 0;
|
|
cursor: pointer;
|
|
transition-duration: 0.25s;
|
|
order: 3;
|
|
min-width: 40px;
|
|
}
|
|
|
|
.dark button {
|
|
background-color: #444444;
|
|
border-color: #343434;
|
|
color: rgba(255, 255, 255, 0.7);
|
|
}
|
|
|
|
.button:hover {
|
|
background-color: #ececec;
|
|
}
|
|
|
|
.dark button:hover {
|
|
background-color: #4d4d4d;
|
|
}
|
|
|
|
.button:active, .button.active {
|
|
background-color: #ffffff;
|
|
color: black;
|
|
border-top: 1px solid #cecece;
|
|
border-left: 1px solid #cecece;
|
|
border-right: 1px solid #cecece;
|
|
border-bottom: 1px solid #ffffff;
|
|
}
|
|
|
|
.dark .button:active, .dark .button.active {
|
|
background-color: #131313;
|
|
color: white;
|
|
border-top: 1px solid #343434;
|
|
border-left: 1px solid #343434;
|
|
border-right: 1px solid #343434;
|
|
border-bottom: 1px solid #131313;
|
|
}
|
|
|
|
.grid-container {
|
|
display: inline-flex;
|
|
flex-wrap: wrap;
|
|
justify-content: flex-start;
|
|
align-items: flex-start;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.grid-item {
|
|
color: black;
|
|
width: 150px;
|
|
font-size: 11px;
|
|
line-height: 24px;
|
|
text-align: left;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
white-space: pre-wrap;
|
|
padding-right: 12px; /* A little more than two spaces, to look good in IE8 where flex-justify does nothing */
|
|
}
|
|
|
|
.link {
|
|
display: inline;
|
|
background: none;
|
|
border: none;
|
|
padding: 7px 14px;
|
|
color: black;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
font-size: 13px;
|
|
margin: 2px 2px;
|
|
}
|
|
|
|
.dark .link {
|
|
color: #abc6ec;
|
|
}
|
|
|
|
.link:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
img {
|
|
-ms-interpolation-mode: nearest-neighbor;
|
|
image-rendering: pixelated;
|
|
}
|
|
|
|
.interview_panel_controls, .interview_panel_stats {
|
|
margin-bottom: 10px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<ul id="menu" class="button-container"></ul>
|
|
<div id="under_menu"></div>
|
|
<div id="statcontent"></div>
|
|
<script>
|
|
// Polyfills and compatibility ------------------------------------------------
|
|
var decoder = decodeURIComponent || unescape;
|
|
var addEventListenerKey = (document.addEventListener ? 'addEventListener' : 'attachEvent'); // IE8 handling for Wine users
|
|
var textContentKey = (typeof document.body.textContent != 'undefined') ? 'textContent' : 'innerText';
|
|
if(!Array.prototype.includes) {
|
|
Array.prototype.includes = function(thing) {
|
|
for(var i = 0; i < this.length; i++) {
|
|
if(this[i] == thing) return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
if (!String.prototype.trim) {
|
|
String.prototype.trim = function () {
|
|
return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
|
|
};
|
|
}
|
|
/*\
|
|
|*|
|
|
|*| Polyfill which enables the passage of arbitrary arguments to the
|
|
|*| callback functions of JavaScript timers (HTML5 standard syntax).
|
|
|*|
|
|
|*| https://developer.mozilla.org/en-US/docs/DOM/window.setInterval
|
|
|*|
|
|
|*| Syntax:
|
|
|*| var timeoutID = window.setTimeout(func, delay[, arg1, arg2, ...]);
|
|
|*| var timeoutID = window.setTimeout(code, delay);
|
|
|*| var intervalID = window.setInterval(func, delay[, arg1, arg2, ...]);
|
|
|*| var intervalID = window.setInterval(code, delay);
|
|
|*|
|
|
\*/
|
|
(function() {
|
|
setTimeout(function(arg1) {
|
|
if (arg1 === 'test') {
|
|
// feature test is passed, no need for polyfill
|
|
return;
|
|
}
|
|
var __nativeST__ = window.setTimeout;
|
|
window.setTimeout = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) {
|
|
var aArgs = Array.prototype.slice.call(arguments, 2);
|
|
return __nativeST__(vCallback instanceof Function ? function() {
|
|
vCallback.apply(null, aArgs);
|
|
} : vCallback, nDelay);
|
|
};
|
|
}, 0, 'test');
|
|
|
|
var interval = setInterval(function(arg1) {
|
|
clearInterval(interval);
|
|
if (arg1 === 'test') {
|
|
// feature test is passed, no need for polyfill
|
|
return;
|
|
}
|
|
var __nativeSI__ = window.setInterval;
|
|
window.setInterval = function(vCallback, nDelay /*, argumentToPass1, argumentToPass2, etc. */ ) {
|
|
var aArgs = Array.prototype.slice.call(arguments, 2);
|
|
return __nativeSI__(vCallback instanceof Function ? function() {
|
|
vCallback.apply(null, aArgs);
|
|
} : vCallback, nDelay);
|
|
};
|
|
}, 0, 'test');
|
|
}())
|
|
|
|
// Browser passthrough code ---------------------------------------------------
|
|
if (window.location) {
|
|
var anti_spam = []; // wow I wish I could use e.repeat but IE is dumb and doesn't have it.
|
|
document[addEventListenerKey]("keydown", function(e) {
|
|
if(e.target && (e.target.localName == "input" || e.target.localName == "textarea"))
|
|
return;
|
|
if(e.defaultPrevented)
|
|
return; // do e.preventDefault() to prevent this behavior.
|
|
if(e.which) {
|
|
if(!anti_spam[e.which]) {
|
|
anti_spam[e.which] = true;
|
|
var key = String.fromCharCode(e.which);
|
|
var x = event.x || event.clientX;
|
|
var y = event.y || event.clientY;
|
|
if(x || y ) // if either of these exist this is happening after a click
|
|
return;
|
|
window.location.href = "byond://winset?command=keyDown " + e.key;
|
|
}
|
|
}
|
|
});
|
|
document[addEventListenerKey]("keyup", function(e) {
|
|
if(e.target && (e.target.localName == "input" || e.target.localName == "textarea"))
|
|
return;
|
|
if(e.defaultPrevented)
|
|
return;
|
|
if(e.which) {
|
|
anti_spam[e.which] = false;
|
|
var key = String.fromCharCode(e.which);
|
|
var x = event.x || event.clientX;
|
|
var y = event.y || event.clientY;
|
|
if( x || y ) // if either of these exist this is happening after a click
|
|
return;
|
|
|
|
window.location.href = "byond://winset?command=keyUp " + e.key;
|
|
}
|
|
});
|
|
}
|
|
/* document.addEventListener("mousedown", function(e){
|
|
var shiftPressed=0;
|
|
var evt = e?e:window.event;
|
|
shiftPressed=evt.shiftKey;
|
|
if (shiftPressed) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}); */
|
|
|
|
// Status panel implementation ------------------------------------------------
|
|
var status_tab_parts = ["Loading..."];
|
|
var current_tab = null;
|
|
var mc_tab_parts = [["Loading...", ""]];
|
|
var href_token = null;
|
|
var spells = [];
|
|
var spell_tabs = [];
|
|
var verb_tabs = [];
|
|
var verbs = [["", ""]]; // list with a list inside
|
|
var tickets = [];
|
|
var interviewManager = {status: "", interviews: []};
|
|
var sdql2 = [];
|
|
var permanent_tabs = []; // tabs that won't be cleared by wipes
|
|
var turfcontents = [];
|
|
var turfname = "";
|
|
var imageRetryDelay = 500;
|
|
var imageRetryLimit = 50;
|
|
var menu = document.querySelector('#menu');
|
|
var under_menu = document.querySelector('#under_menu');
|
|
var statcontentdiv = document.querySelector('#statcontent');
|
|
var storedimages = [];
|
|
|
|
function createStatusTab(name) {
|
|
if (name.indexOf(".") != -1)
|
|
name = name.split(".")[0];
|
|
if(document.getElementById(name) || name.trim() == "")
|
|
return;
|
|
if(!verb_tabs.includes(name) && !permanent_tabs.includes(name))
|
|
return;
|
|
var B = document.createElement("BUTTON");
|
|
B.onclick = function() {
|
|
tab_change(name);
|
|
this.blur();
|
|
};
|
|
B.id = name;
|
|
B[textContentKey] = name;
|
|
B.className = "button";
|
|
//ORDERING ALPHABETICALLY
|
|
B.style.order = name.charCodeAt(0);
|
|
if(name == "Status" || name == "MC")
|
|
B.style.order = name == "Status" ? 1 : 2;
|
|
//END ORDERING
|
|
menu.appendChild(B);
|
|
SendTabToByond(name);
|
|
under_menu.style.height = menu.clientHeight + 'px';
|
|
}
|
|
|
|
function removeStatusTab(name) {
|
|
if(!document.getElementById(name) || permanent_tabs.includes(name))
|
|
return;
|
|
for (var i = verb_tabs.length - 1; i >= 0; --i) {
|
|
if (verb_tabs[i] == name) {
|
|
verb_tabs.splice(i, 1);
|
|
}
|
|
}
|
|
menu.removeChild(document.getElementById(name));
|
|
TakeTabFromByond(name);
|
|
under_menu.style.height = menu.clientHeight + 'px';
|
|
}
|
|
|
|
function sortVerbs() {
|
|
verbs.sort(function (a, b) {
|
|
var selector = a[0] == b[0] ? 1 : 0;
|
|
if (a[selector].toUpperCase() < b[selector].toUpperCase()) {
|
|
return 1;
|
|
}
|
|
else if (a[selector].toUpperCase() > b[selector].toUpperCase()) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
})
|
|
}
|
|
|
|
window.onresize = function () {
|
|
under_menu.style.height = menu.clientHeight + 'px';
|
|
}
|
|
|
|
function addPermanentTab(name) {
|
|
if(!permanent_tabs.includes(name))
|
|
permanent_tabs.push(name);
|
|
createStatusTab(name);
|
|
}
|
|
|
|
function removePermanentTab(name) {
|
|
for (var i = permanent_tabs.length - 1; i >= 0; --i) {
|
|
if (permanent_tabs[i] == name) {
|
|
permanent_tabs.splice(i, 1);
|
|
}
|
|
}
|
|
removeStatusTab(name);
|
|
}
|
|
|
|
function checkStatusTab() {
|
|
for(var i=0; i < menu.children.length; i++)
|
|
if(!verb_tabs.includes(menu.children[i].id) && !permanent_tabs.includes(menu.children[i].id))
|
|
menu.removeChild(menu.children[i]);
|
|
}
|
|
function remove_verb(v) {
|
|
var verb_to_remove = v; // to_remove = [verb:category, verb:name]
|
|
for(var i = verbs.length - 1; i >= 0; i--){
|
|
var part_to_remove = verbs[i];
|
|
if(part_to_remove[1] == verb_to_remove[1]){
|
|
verbs.splice(i, 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
function check_verbs() {
|
|
for(var v = verb_tabs.length - 1; v >= 0; v--){
|
|
verbs_cat_check(verb_tabs[v]);
|
|
}
|
|
}
|
|
|
|
function verbs_cat_check(cat) {
|
|
var tabCat = cat.indexOf(".") != -1 ? cat.split(".")[0] : cat;
|
|
var verbs_in_cat = 0;
|
|
var verbcat = "";
|
|
if(!verb_tabs.includes(tabCat)){
|
|
removeStatusTab(tabCat);
|
|
return;
|
|
}
|
|
for(var v = 0; v < verbs.length; v++){
|
|
var part = verbs[v];
|
|
verbcat = part[0].indexOf(".") != -1 ? part[0].split(".")[0] : part[0];
|
|
if(verbcat != tabCat || verbcat.trim() == ""){
|
|
continue;
|
|
}
|
|
else{
|
|
verbs_in_cat = 1;
|
|
break; // we only need one
|
|
}
|
|
}
|
|
if(verbs_in_cat != 1) {
|
|
removeStatusTab(tabCat);
|
|
if(current_tab == tabCat)
|
|
tab_change("Status");
|
|
}
|
|
}
|
|
|
|
function findVerbindex(name, verblist) {
|
|
for(var i = 0; i < verblist.length; i++) {
|
|
var part = verblist[i];
|
|
if(part[1] == name)
|
|
return i;
|
|
}
|
|
}
|
|
function wipe_verbs() {
|
|
verbs = [["", ""]];
|
|
verb_tabs = [];
|
|
checkStatusTab(); // remove all empty verb tabs
|
|
}
|
|
|
|
function add_verb_list(v) {
|
|
var to_add = JSON.parse(v); // list of a list with category and verb inside it
|
|
to_add.sort(); // sort what we're adding
|
|
for(var i = 0; i < to_add.length; i++) {
|
|
var part = to_add[i];
|
|
if(!part[0])
|
|
continue;
|
|
var category = part[0].indexOf(".") == -1 ? part[0] : part[0].split(".")[0];
|
|
if(findVerbindex(part[1], verbs))
|
|
continue;
|
|
if(verb_tabs.includes(category)){
|
|
verbs.push(part);
|
|
if(current_tab == category) {
|
|
draw_verbs(category); // redraw if we added a verb to the tab we're currently in
|
|
}
|
|
} else if(category) {
|
|
verb_tabs.push(category);
|
|
verbs.push(part);
|
|
createStatusTab(category);
|
|
}
|
|
}
|
|
}
|
|
|
|
function remove_verb_list(v) {
|
|
var to_remove = JSON.parse(v);
|
|
for(var i = 0; i < to_remove.length; i++) {
|
|
remove_verb(to_remove[i]);
|
|
}
|
|
check_verbs();
|
|
sortVerbs();
|
|
if(verb_tabs.includes(current_tab))
|
|
draw_verbs(current_tab);
|
|
}
|
|
|
|
// passes a 2D list of (verbcategory, verbname) creates tabs and adds verbs to respective list
|
|
// example (IC, Say)
|
|
function init_verbs(c, v) {
|
|
wipe_verbs(); // remove all verb categories so we can replace them
|
|
checkStatusTab(); // remove all status tabs
|
|
verb_tabs = JSON.parse(c);
|
|
verb_tabs.sort(); // sort it
|
|
var do_update = false;
|
|
var cat = "";
|
|
for(var i = 0; i < verb_tabs.length; i++){
|
|
cat = verb_tabs[i];
|
|
createStatusTab(cat); // create a category if the verb doesn't exist yet
|
|
}
|
|
if(verb_tabs.includes(current_tab)) {
|
|
do_update = true;
|
|
}
|
|
if(v) {
|
|
add_verb_list(v);
|
|
sortVerbs(); // sort them
|
|
if(do_update) {
|
|
draw_verbs(current_tab);
|
|
}
|
|
}
|
|
SendTabsToByond();
|
|
}
|
|
|
|
function SendTabsToByond(){
|
|
var tabstosend = [];
|
|
tabstosend = tabstosend.concat(permanent_tabs, verb_tabs);
|
|
for(var i=0; i < tabstosend.length; i++){
|
|
SendTabToByond(tabstosend[i]);
|
|
}
|
|
}
|
|
|
|
function SendTabToByond(tab) {
|
|
window.location.href = "byond://winset?command=Send-Tabs " + tab;
|
|
}
|
|
|
|
//Byond can't have this tab anymore since we're removing it
|
|
function TakeTabFromByond(tab) {
|
|
window.location.href = "byond://winset?command=Remove-Tabs " + tab;
|
|
}
|
|
|
|
function update(global_data, ping_entry, other_entries) {
|
|
status_tab_parts = [ping_entry];
|
|
var parsed = JSON.parse(global_data);
|
|
for(var i = 0; i < parsed.length; i++) if(parsed[i] != null) status_tab_parts.push(parsed[i]);
|
|
parsed = JSON.parse(other_entries);
|
|
for(var i = 0; i < parsed.length; i++) if(parsed[i] != null) status_tab_parts.push(parsed[i]);
|
|
if(current_tab == "Status")
|
|
draw_status();
|
|
else if(current_tab == "Debug Stat Panel")
|
|
draw_debug();
|
|
}
|
|
|
|
function update_mc(global_mc_data, coords_entry) {
|
|
mc_tab_parts = JSON.parse(global_mc_data);
|
|
mc_tab_parts.splice(0,0,["Location:",coords_entry]);
|
|
if(!verb_tabs.includes("MC"))
|
|
verb_tabs.push("MC");
|
|
createStatusTab("MC");
|
|
if(current_tab == "MC")
|
|
draw_mc();
|
|
}
|
|
|
|
function remove_mc() {
|
|
removeStatusTab("MC");
|
|
if(current_tab == "MC")
|
|
tab_change("Status");
|
|
}
|
|
function remove_spells() {
|
|
for(var s = 0; s < spell_tabs.length; s++){
|
|
removeStatusTab(spell_tabs[s]);
|
|
}
|
|
}
|
|
|
|
function init_spells() {
|
|
var cat = "";
|
|
for(var i = 0; i < spell_tabs.length; i++) {
|
|
cat = spell_tabs[i];
|
|
if(cat.length > 0) {
|
|
verb_tabs.push(cat);
|
|
createStatusTab(cat);
|
|
}
|
|
}
|
|
}
|
|
|
|
function check_spells() {
|
|
for(var v = 0; v < spell_tabs.length; v++)
|
|
spell_cat_check(spell_tabs[v]);
|
|
}
|
|
function spell_cat_check(cat) {
|
|
var spells_in_cat = 0;
|
|
var spellcat = "";
|
|
for(var s = 0; s < spells.length; s++){
|
|
var spell = spells[s];
|
|
spellcat = spell[0];
|
|
if(spellcat == cat){
|
|
spells_in_cat++;
|
|
}
|
|
}
|
|
if(spells_in_cat < 1) {
|
|
removeStatusTab(cat);
|
|
}
|
|
}
|
|
function update_spells(t, s) {
|
|
spell_tabs = JSON.parse(t);
|
|
var do_update = false;
|
|
if(spell_tabs.includes(current_tab)) {
|
|
do_update = true;
|
|
}
|
|
init_spells();
|
|
if(s) {
|
|
spells = JSON.parse(s);
|
|
if(do_update) {
|
|
draw_spells(current_tab);
|
|
}
|
|
} else {
|
|
remove_spells();
|
|
}
|
|
}
|
|
|
|
function tab_change(tab) {
|
|
if(tab == current_tab) return;
|
|
if(document.getElementById(current_tab))
|
|
document.getElementById(current_tab).className = "button"; // disable active on last button
|
|
current_tab = tab;
|
|
set_byond_tab(tab);
|
|
if(document.getElementById(tab))
|
|
document.getElementById(tab).className = "button active"; // make current button active
|
|
var spell_tabs_thingy = (spell_tabs.includes(tab));
|
|
var verb_tabs_thingy = (verb_tabs.includes(tab));
|
|
if(tab == "Status") {
|
|
draw_status();
|
|
} else if(tab == "MC") {
|
|
draw_mc();
|
|
} else if(spell_tabs_thingy) {
|
|
draw_spells(tab);
|
|
} else if(verb_tabs_thingy){
|
|
draw_verbs(tab);
|
|
} else if(tab == "Debug Stat Panel") {
|
|
draw_debug();
|
|
} else if(tab == "Tickets") {
|
|
draw_tickets();
|
|
} else if(tab == "Interviews") {
|
|
draw_interviews();
|
|
} else if(tab == "SDQL2") {
|
|
draw_sdql2();
|
|
}else if(tab == turfname) {
|
|
draw_listedturf();
|
|
} else {
|
|
statcontentdiv[textContentKey] = "Loading...";
|
|
}
|
|
window.location.href = "byond://winset?statbrowser.is-visible=true";
|
|
}
|
|
|
|
function set_byond_tab(tab){
|
|
window.location.href = "byond://winset?command=Set-Tab " + tab;
|
|
}
|
|
function draw_debug() {
|
|
statcontentdiv[textContentKey] = "";
|
|
var wipeverbstabs = document.createElement("div");
|
|
var link = document.createElement("a");
|
|
link.onclick = function() {wipe_verbs()};
|
|
link[textContentKey] = "Wipe All Verbs";
|
|
wipeverbstabs.appendChild(link);
|
|
document.getElementById("statcontent").appendChild(wipeverbstabs);
|
|
var text = document.createElement("div");
|
|
text[textContentKey] = "Verb Tabs:";
|
|
document.getElementById("statcontent").appendChild(text);
|
|
var table1 = document.createElement("table");
|
|
for(var i=0; i < verb_tabs.length ; i++) {
|
|
var part = verb_tabs[i];
|
|
var tr = document.createElement("tr");
|
|
var td1 = document.createElement("td");
|
|
td1[textContentKey] = part;
|
|
var a = document.createElement("a");
|
|
a.onclick = function (part) {
|
|
return function() {removeStatusTab(part)};
|
|
}(part);
|
|
a[textContentKey] = " Delete Tab " + part;
|
|
td1.appendChild(a);
|
|
tr.appendChild(td1);
|
|
table1.appendChild(tr);
|
|
}
|
|
document.getElementById("statcontent").appendChild(table1);
|
|
var header2 = document.createElement("div");
|
|
header2[textContentKey] = "Verbs:";
|
|
document.getElementById("statcontent").appendChild(header2);
|
|
var table2 = document.createElement("table");
|
|
for(var v = 0; v < verbs.length; v++) {
|
|
var part2 = verbs[v];
|
|
var trr = document.createElement("tr");
|
|
var tdd1 = document.createElement("td");
|
|
tdd1[textContentKey] = part2[0];
|
|
var tdd2 = document.createElement("td");
|
|
tdd2[textContentKey] = part2[1];
|
|
trr.appendChild(tdd1);
|
|
trr.appendChild(tdd2);
|
|
table2.appendChild(trr);
|
|
}
|
|
document.getElementById("statcontent").appendChild(table2);
|
|
var text3 = document.createElement("div");
|
|
text3[textContentKey] = "Permanent Tabs:";
|
|
document.getElementById("statcontent").appendChild(text3);
|
|
var table3 = document.createElement("table");
|
|
for(var i=0; i < permanent_tabs.length ; i++) {
|
|
var part3 = permanent_tabs[i];
|
|
var trrr = document.createElement("tr");
|
|
var tddd1 = document.createElement("td");
|
|
tddd1[textContentKey] = part3;
|
|
trrr.appendChild(tddd1);
|
|
table3.appendChild(trrr);
|
|
}
|
|
document.getElementById("statcontent").appendChild(table3);
|
|
|
|
}
|
|
function draw_status() {
|
|
if(!document.getElementById("Status")) {
|
|
createStatusTab("Status");
|
|
current_tab = "Status";
|
|
}
|
|
statcontentdiv[textContentKey] = '';
|
|
for(var i = 0; i < status_tab_parts.length; i++) {
|
|
if(status_tab_parts[i].trim() == "") {
|
|
document.getElementById("statcontent").appendChild(document.createElement("br"));
|
|
} else {
|
|
var div = document.createElement("div");
|
|
div[textContentKey] = status_tab_parts[i];
|
|
document.getElementById("statcontent").appendChild(div);
|
|
}
|
|
}
|
|
if(verb_tabs.length == 0 || !verbs)
|
|
{
|
|
window.location.href = "byond://winset?command=Fix-Stat-Panel";
|
|
}
|
|
}
|
|
|
|
function draw_mc() {
|
|
statcontentdiv[textContentKey] = "";
|
|
var table = document.createElement("table");
|
|
for(var i = 0; i < mc_tab_parts.length; i++) {
|
|
var part = mc_tab_parts[i];
|
|
var tr = document.createElement("tr");
|
|
var td1 = document.createElement("td");
|
|
td1[textContentKey] = part[0];
|
|
var td2 = document.createElement("td");
|
|
if(part[2]) {
|
|
var a = document.createElement("a");
|
|
a.href = "?_src_=vars;admin_token=" + href_token + ";Vars=" + part[2];
|
|
a[textContentKey] = part[1];
|
|
td2.appendChild(a);
|
|
} else {
|
|
td2[textContentKey] = part[1];
|
|
}
|
|
tr.appendChild(td1);
|
|
tr.appendChild(td2);
|
|
table.appendChild(tr);
|
|
}
|
|
document.getElementById("statcontent").appendChild(table);
|
|
}
|
|
function update_tickets(T){
|
|
tickets = JSON.parse(T);
|
|
if(!verb_tabs.includes("Tickets")) {
|
|
verb_tabs.push("Tickets");
|
|
addPermanentTab("Tickets");
|
|
}
|
|
if(current_tab == "Tickets")
|
|
draw_tickets();
|
|
}
|
|
function update_interviews(I){
|
|
interviewManager = JSON.parse(I);
|
|
if(!verb_tabs.includes("Interviews")) {
|
|
verb_tabs.push("Interviews");
|
|
addPermanentTab("Interviews");
|
|
}
|
|
if(current_tab == "Interviews")
|
|
draw_interviews();
|
|
}
|
|
function update_sdql2(S) {
|
|
sdql2 = JSON.parse(S);
|
|
if(sdql2.length > 0 && !verb_tabs.includes("SDQL2")) {
|
|
verb_tabs.push("SDQL2");
|
|
addPermanentTab("SDQL2");
|
|
}
|
|
if(current_tab == "SDQL2")
|
|
draw_sdql2();
|
|
}
|
|
|
|
function remove_sdql2() {
|
|
if(sdql2) {
|
|
sdql2 = [];
|
|
removePermanentTab("SDQL2");
|
|
if(current_tab == "SDQL2")
|
|
tab_change("Status");
|
|
}
|
|
checkStatusTab();
|
|
}
|
|
|
|
function remove_tickets() {
|
|
if(tickets) {
|
|
tickets = [];
|
|
removePermanentTab("Tickets");
|
|
if(current_tab == "Tickets")
|
|
tab_change("Status");
|
|
}
|
|
checkStatusTab();
|
|
}
|
|
|
|
function remove_interviews() {
|
|
if(tickets) {
|
|
tickets = [];
|
|
removePermanentTab("Interviews");
|
|
if(current_tab == "Interviews")
|
|
tab_change("Status");
|
|
}
|
|
checkStatusTab();
|
|
}
|
|
|
|
// removes MC, Tickets and MC tabs.
|
|
function remove_admin_tabs() {
|
|
href_token = null;
|
|
remove_mc();
|
|
remove_tickets();
|
|
remove_sdql2();
|
|
remove_interviews();
|
|
}
|
|
|
|
function add_admin_tabs(ht) {
|
|
href_token = ht;
|
|
addPermanentTab("MC");
|
|
addPermanentTab("Tickets");
|
|
addPermanentTab("Interviews");
|
|
}
|
|
function create_listedturf(TN) {
|
|
remove_listedturf(); // remove the last one if we had one
|
|
turfname = JSON.parse(TN);
|
|
addPermanentTab(turfname);
|
|
tab_change(turfname);
|
|
}
|
|
function update_listedturf(TC) {
|
|
turfcontents = JSON.parse(TC);
|
|
if(current_tab == turfname)
|
|
draw_listedturf();
|
|
}
|
|
function iconError() {
|
|
var that = this;
|
|
setTimeout(function() {
|
|
var current_attempts = that.id; // a bit of a hack, change this if we need to call on img id's later
|
|
if(!current_attempts)
|
|
that.id = 1;
|
|
if (current_attempts > imageRetryLimit)
|
|
return;
|
|
var src = that.src;
|
|
that.src = null;
|
|
that.src = src + '#' + current_attempts;
|
|
that.id++;
|
|
draw_listedturf();
|
|
}, imageRetryDelay);
|
|
}
|
|
function draw_listedturf() {
|
|
statcontentdiv[textContentKey] = "";
|
|
var table = document.createElement("table");
|
|
for(var i = 0; i < turfcontents.length; i++) {
|
|
var part = turfcontents[i];
|
|
if(storedimages[part[1]] == null && part[2]) {
|
|
var img = document.createElement("img");
|
|
img.src = part[2];
|
|
img.id = part[1];
|
|
storedimages[part[1]] = part[2];
|
|
img.onerror = function() {
|
|
iconError();
|
|
};
|
|
table.appendChild(img);
|
|
} else {
|
|
var img = document.createElement("img");
|
|
img.onerror = function() {
|
|
iconError();
|
|
};
|
|
img.src = storedimages[part[1]];
|
|
img.id = part[1];
|
|
table.appendChild(img);
|
|
}
|
|
var b = document.createElement("div");
|
|
var clickcatcher = "";
|
|
b.className = "link";
|
|
b.onmousedown = function (part) {
|
|
// The outer function is used to close over a fresh "part" variable,
|
|
// rather than every onmousedown getting the "part" of the last entry.
|
|
return function(e) {
|
|
e.preventDefault();
|
|
clickcatcher = "?src=" + part[1] + ";statpanel_item_click=1";
|
|
if(e.shiftKey){
|
|
clickcatcher += ";statpanel_item_shiftclick=1";
|
|
}
|
|
if(e.ctrlKey){
|
|
clickcatcher += ";statpanel_item_ctrlclick=1";
|
|
}
|
|
if(e.altKey) {
|
|
clickcatcher += ";statpanel_item_altclick=1";
|
|
}
|
|
window.location.href = clickcatcher;
|
|
}
|
|
}(part);
|
|
b[textContentKey] = part[0];
|
|
table.appendChild(b);
|
|
table.appendChild(document.createElement("br"));
|
|
}
|
|
document.getElementById("statcontent").appendChild(table);
|
|
}
|
|
|
|
function remove_listedturf() {
|
|
removePermanentTab(turfname);
|
|
checkStatusTab();
|
|
if(current_tab == turfname)
|
|
tab_change("Status");
|
|
}
|
|
function draw_sdql2(){
|
|
statcontentdiv[textContentKey] = "";
|
|
var table = document.createElement("table");
|
|
for(var i = 0; i < sdql2.length; i++) {
|
|
var part = sdql2[i];
|
|
var tr = document.createElement("tr");
|
|
var td1 = document.createElement("td");
|
|
td1[textContentKey] = part[0];
|
|
var td2 = document.createElement("td");
|
|
if(part[2]) {
|
|
var a = document.createElement("a");
|
|
a.href = "?src=" + part[2] + ";statpanel_item_click=1";
|
|
a[textContentKey] = part[1];
|
|
td2.appendChild(a);
|
|
} else {
|
|
td2[textContentKey] = part[1];
|
|
}
|
|
tr.appendChild(td1);
|
|
tr.appendChild(td2);
|
|
table.appendChild(tr);
|
|
}
|
|
document.getElementById("statcontent").appendChild(table);
|
|
}
|
|
|
|
function draw_tickets() {
|
|
statcontentdiv[textContentKey] = "";
|
|
var table = document.createElement("table");
|
|
if(!tickets)
|
|
return;
|
|
for(var i = 0; i < tickets.length; i++) {
|
|
var part = tickets[i];
|
|
var tr = document.createElement("tr");
|
|
var td1 = document.createElement("td");
|
|
td1[textContentKey] = part[0];
|
|
var td2 = document.createElement("td");
|
|
if(part[2]) {
|
|
var a = document.createElement("a");
|
|
a.href = "?_src_=holder;admin_token=" + href_token + ";ahelp=" + part[2] + ";ahelp_action=ticket;statpanel_item_click=1;action=ticket" ;
|
|
a[textContentKey] = part[1];
|
|
td2.appendChild(a);
|
|
} else if(part[3]){
|
|
var a = document.createElement("a");
|
|
a.href = "?src=" + part[3] + ";statpanel_item_click=1";
|
|
a[textContentKey] = part[1];
|
|
td2.appendChild(a);
|
|
} else {
|
|
td2[textContentKey] = part[1];
|
|
}
|
|
tr.appendChild(td1);
|
|
tr.appendChild(td2);
|
|
table.appendChild(tr);
|
|
}
|
|
document.getElementById("statcontent").appendChild(table);
|
|
}
|
|
|
|
function draw_interviews() {
|
|
statcontentdiv[textContentKey] = "";
|
|
var body = document.createElement("div");
|
|
var manDiv = document.createElement("div");
|
|
manDiv.className = "interview_panel_controls"
|
|
var manLink = document.createElement("a");
|
|
manLink[textContentKey] = "Open Interview Manager Panel";
|
|
manLink.href = "?_src_=holder;admin_token=" + href_token + ";interview_man=1;statpanel_item_click=1";
|
|
manDiv.appendChild(manLink);
|
|
body.appendChild(manDiv);
|
|
|
|
// List interview stats
|
|
var statsDiv = document.createElement("table");
|
|
statsDiv.className="interview_panel_stats";
|
|
for (var key in interviewManager.status) {
|
|
var d = document.createElement("div");
|
|
var tr = document.createElement("tr");
|
|
var stat_name = document.createElement("td");
|
|
var stat_text = document.createElement("td");
|
|
stat_name[textContentKey] = key;
|
|
stat_text[textContentKey] = interviewManager.status[key];
|
|
tr.appendChild(stat_name);
|
|
tr.appendChild(stat_text);
|
|
statsDiv.appendChild(tr);
|
|
}
|
|
body.appendChild(statsDiv);
|
|
document.getElementById("statcontent").appendChild(body);
|
|
|
|
// List interviews if any are open
|
|
var table = document.createElement("table");
|
|
table.className = "interview_panel_table";
|
|
if(!interviewManager)
|
|
return;
|
|
for(var i = 0; i < interviewManager.interviews.length; i++) {
|
|
var part = interviewManager.interviews[i];
|
|
var tr = document.createElement("tr");
|
|
var td = document.createElement("td");
|
|
var a = document.createElement("a");
|
|
a[textContentKey] = part["status"];
|
|
a.href = "?_src_=holder;admin_token=" + href_token + ";interview=" + part["ref"] + ";statpanel_item_click=1";
|
|
td.appendChild(a);
|
|
tr.appendChild(td);
|
|
table.appendChild(tr);
|
|
}
|
|
document.getElementById("statcontent").appendChild(table);
|
|
}
|
|
|
|
function draw_spells(cat) {
|
|
statcontentdiv[textContentKey] = "";
|
|
var table = document.createElement("table");
|
|
for(var i = 0; i < spells.length; i++) {
|
|
var part = spells[i];
|
|
if(part[0] != cat) continue;
|
|
var tr = document.createElement("tr");
|
|
var td1 = document.createElement("td");
|
|
td1[textContentKey] = part[1];
|
|
var td2 = document.createElement("td");
|
|
if(part[3]) {
|
|
var a = document.createElement("a");
|
|
a.href = "?src=" + part[3] + ";statpanel_item_click=1";
|
|
a[textContentKey] = part[2];
|
|
td2.appendChild(a);
|
|
} else {
|
|
td2[textContentKey] = part[2];
|
|
}
|
|
tr.appendChild(td1);
|
|
tr.appendChild(td2);
|
|
table.appendChild(tr);
|
|
}
|
|
document.getElementById("statcontent").appendChild(table);
|
|
}
|
|
|
|
function draw_verbs(cat){
|
|
statcontentdiv[textContentKey] = "";
|
|
var table = document.createElement("div");
|
|
var additions = {}; // additional sub-categories to be rendered
|
|
table.className = "grid-container";
|
|
sortVerbs();
|
|
verbs.reverse(); // sort verbs backwards before we draw
|
|
for (var i = 0; i < verbs.length; ++i) {
|
|
var part = verbs[i];
|
|
var name = part[0];
|
|
var command = part[1];
|
|
|
|
if (command && name.lastIndexOf(cat, 0) != -1 && (name.length == cat.length || name.charAt(cat.length) == ".")) {
|
|
var subCat = name.lastIndexOf(".") != -1 ? name.split(".")[1] : null;
|
|
if (subCat && !additions[subCat]) {
|
|
var newTable = document.createElement("div");
|
|
newTable.className = "grid-container";
|
|
additions[subCat] = newTable;
|
|
}
|
|
|
|
var a = document.createElement("a");
|
|
a.href = "byond://winset?command=" + command.replace(/\s/g, "-");
|
|
a[textContentKey] = command;
|
|
a.className = "grid-item";
|
|
(subCat ? additions[subCat] : table).appendChild(a);
|
|
}
|
|
}
|
|
|
|
// Append base table to view
|
|
var content = document.getElementById("statcontent");
|
|
content.appendChild(table);
|
|
|
|
// Append additional sub-categories if relevant
|
|
for (var cat in additions) {
|
|
if (additions.hasOwnProperty(cat)) {
|
|
// do addition here
|
|
var header = document.createElement("h3");
|
|
header[textContentKey] = cat;
|
|
content.appendChild(header);
|
|
content.appendChild(additions[cat]);
|
|
}
|
|
}
|
|
}
|
|
|
|
function set_theme(which) {
|
|
if (which == "light") {
|
|
document.body.className = "";
|
|
set_style_sheet("browserOutput_white");
|
|
} else if (which == "dark") {
|
|
document.body.className = "dark";
|
|
set_style_sheet("browserOutput");
|
|
}
|
|
}
|
|
|
|
function set_style_sheet(sheet) {
|
|
if(document.getElementById("goonStyle")) {
|
|
var currentSheet = document.getElementById("goonStyle");
|
|
currentSheet.parentElement.removeChild(currentSheet);
|
|
}
|
|
var head = document.getElementsByTagName('head')[0];
|
|
var sheetElement = document.createElement("link");
|
|
sheetElement.id = "goonStyle";
|
|
sheetElement.rel = "stylesheet";
|
|
sheetElement.type = "text/css";
|
|
sheetElement.href = sheet + ".css";
|
|
sheetElement.media = 'all';
|
|
head.appendChild(sheetElement);
|
|
}
|
|
|
|
document[addEventListenerKey]("click", function(e) {
|
|
window.location.href = "byond://winset?map.focus=true";
|
|
});
|
|
|
|
if(!current_tab) {
|
|
addPermanentTab("Status");
|
|
tab_change("Status");
|
|
}
|
|
|
|
window.onload = function() {
|
|
NotifyByondOnload();
|
|
};
|
|
|
|
function NotifyByondOnload() {
|
|
window.location.href = "byond://winset?command=Panel-Ready";
|
|
}
|
|
|
|
function create_debug(){
|
|
if(!document.getElementById("Debug Stat Panel")) {
|
|
addPermanentTab("Debug Stat Panel");
|
|
} else {
|
|
removePermanentTab("Debug Stat Panel");
|
|
}
|
|
}
|
|
|
|
function getCookie(cname) {
|
|
var name = cname + '=';
|
|
var ca = document.cookie.split(';');
|
|
for(var i=0; i < ca.length; i++) {
|
|
var c = ca[i];
|
|
while (c.charAt(0)==' ') c = c.substring(1);
|
|
if (c.indexOf(name) === 0) {
|
|
return decoder(c.substring(name.length,c.length));
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|