diff --git a/code/controllers/subsystem/changelog.dm b/code/controllers/subsystem/changelog.dm index 488be1d32c0..713436a1d82 100644 --- a/code/controllers/subsystem/changelog.dm +++ b/code/controllers/subsystem/changelog.dm @@ -15,9 +15,8 @@ SUBSYSTEM_DEF(changelog) flags = SS_NO_FIRE var/current_cl_timestamp = "0" // Timestamp is seconds since UNIX epoch (1st January 1970). ITs also a string because BYOND doesnt like big numbers. var/ss_ready = FALSE // Is the SS ready? We dont want to run procs if we have not generated yet - var/list/startup_clients_button = list() // Clients who connected before initialization who need their button color updating - var/list/startup_clients_open = list() // Clients who connected before initialization who need the CL opening - var/changelogHTML = "" // HTML that the changelog will use to display + var/list/client/startup_clients_open = list() // Clients who connected before initialization who need the CL opening + var/list/changelog_data = list() // Parsed changelog data /datum/controller/subsystem/changelog/Initialize() // This entire subsystem relies on SQL being here. @@ -32,20 +31,21 @@ SUBSYSTEM_DEF(changelog) while(latest_cl_date.NextRow()) current_cl_timestamp = latest_cl_date.item[1] + qdel(latest_cl_date) - if(!GenerateChangelogHTML()) // if this failed to generate + if(!GenerateChangelogData()) // if this failed to generate to_chat(world, "WARNING: Changelog failed to generate. Please inform a coder/server dev") return ..() ss_ready = TRUE - // Now we can alert anyone who wanted to check the changelog - for(var/x in startup_clients_button) - var/client/C = x + + // Update buttons for those who logged in + for(var/client/C as anything in GLOB.clients) UpdatePlayerChangelogButton(C) // Now we can alert anyone who wanted to check the changelog - for(var/client/C in startup_clients_open) + for(var/client/C as anything in startup_clients_open) OpenChangelog(C) return ..() @@ -59,49 +59,40 @@ SUBSYSTEM_DEF(changelog) winset(C, "rpane.changelog", "background-color=#40628a;font-color=#ffffff;font-style=none") else winset(C, "rpane.changelog", "background-color=none;font-style=none") + + C.prefs.lastchangelog = current_cl_timestamp var/datum/db_query/updatePlayerCLTime = SSdbcore.NewQuery( - "UPDATE player SET lastchangelog=:lastchangelog WHERE ckey=:ckey", - list( + "UPDATE player SET lastchangelog=:lastchangelog WHERE ckey=:ckey", list( "lastchangelog" = current_cl_timestamp, "ckey" = C.ckey ) ) + // We dont do anything with this query so we dont care about errors too much updatePlayerCLTime.warn_execute() qdel(updatePlayerCLTime) /datum/controller/subsystem/changelog/proc/UpdatePlayerChangelogButton(client/C) - // If SQL aint even enabled, just set the button to default style - if(!SSdbcore.IsConnected()) + // If SQL aint even enabled, or we aint ready just set the button to default style + if(!SSdbcore.IsConnected() || !ss_ready) if(C.prefs.toggles & PREFTOGGLE_UI_DARKMODE) winset(C, "rpane.changelog", "background-color=#40628a;text-color=#FFFFFF") else winset(C, "rpane.changelog", "background-color=none;text-color=#000000") return - // If SQL is enabled but we aint ready, queue them up, and use the default style - if(!ss_ready) - startup_clients_button |= C + // If we are ready, process the button style + if(C.prefs.lastchangelog != current_cl_timestamp) + winset(C, "rpane.changelog", "background-color=#bb7700;text-color=#FFFFFF;font-style=bold") + to_chat(C, "Changelog has changed since your last visit.") + else if(C.prefs.toggles & PREFTOGGLE_UI_DARKMODE) winset(C, "rpane.changelog", "background-color=#40628a;text-color=#FFFFFF") else winset(C, "rpane.changelog", "background-color=none;text-color=#000000") - return - - // Sanity check to ensure clients still exist (If a client DCs mid startup this would runtime) - if(C && C.prefs) - // If we are ready, process the button style - if(C.prefs.lastchangelog != current_cl_timestamp) - winset(C, "rpane.changelog", "background-color=#bb7700;text-color=#FFFFFF;font-style=bold") - to_chat(C, "Changelog has changed since your last visit.") - else - if(C.prefs.toggles & PREFTOGGLE_UI_DARKMODE) - winset(C, "rpane.changelog", "background-color=#40628a;text-color=#FFFFFF") - else - winset(C, "rpane.changelog", "background-color=none;text-color=#000000") /datum/controller/subsystem/changelog/proc/OpenChangelog(client/C) @@ -119,9 +110,7 @@ SUBSYSTEM_DEF(changelog) UpdatePlayerChangelogDate(C) UpdatePlayerChangelogButton(C) - var/datum/browser/cl_popup = new(C.mob, "changelog", "Changelog", 700, 800) - cl_popup.set_content(changelogHTML) - cl_popup.open() + ui_interact(C.mob) /client/verb/changes() set name = "Changelog" @@ -130,51 +119,13 @@ SUBSYSTEM_DEF(changelog) // Just invoke the actual CL thing SSchangelog.OpenChangelog(src) -// Helper to turn CL types into a fontawesome icon instead of an image -// The colors are #28a745 for green, #fd7e14 for orange, and #dc3545 for red. -// These colours are from bootstrap and look good with black and white -/datum/controller/subsystem/changelog/proc/Text2Icon(text) - switch(text) - if("FIX") - return "" // Fixes are white because while they are good, they have no negative coutnerpart - if("WIP") - return "" // WIP stuff is orange because new code is good but its not done yet - if("TWEAK") - return "" // Tweaks are white because they could be good or bad, and theres no specific add or remove - if("SOUNDADD") - return "" // Sound additions are green because its something new - if("SOUNDDEL") - return "" // Sound removals are red because something has been removed - if("CODEADD") - return "" // Code additions are green because its something new - if("CODEDEL") - return "" // Code removals are red becuase someting has been removed - if("IMAGEADD") - return "" // Image additions are green because something has been added - if("IMAGEDEL") - return "" // Image removals are red because something has been removed - if("SPELLCHECK") - return "" // Spellcheck is white because theres no dedicated negative to it, so theres no red for it to collate with - if("EXPERIMENT") - return "" // Experimental stuff is orange because while its a new feature, its unstable - else // Just incase the DB somehow breaks - return "" // Same here - // This proc is the star of the show -/datum/controller/subsystem/changelog/proc/GenerateChangelogHTML() +/datum/controller/subsystem/changelog/proc/GenerateChangelogData() + // This value will be returned if the proc crashes . = FALSE - // Modify the code below to modify the header of the changelog - var/changelog_header = {" - - ParadiseSS13 Changelog - -
-

Paradise Station Changelog

-

Forum - Wiki - GitHub

-
- "} var/list/prs_to_process = list() + // Grab all from last 30 days var/datum/db_query/pr_list_query = SSdbcore.NewQuery("SELECT DISTINCT pr_number FROM changelog WHERE date_merged BETWEEN NOW() - INTERVAL 30 DAY AND NOW() ORDER BY date_merged DESC") if(!pr_list_query.warn_execute()) @@ -183,10 +134,8 @@ SUBSYSTEM_DEF(changelog) while(pr_list_query.NextRow()) prs_to_process += text2num(pr_list_query.item[1]) - qdel(pr_list_query) - // Load in the header - changelogHTML += changelog_header + qdel(pr_list_query) // We put all these queries into a list so we can batch-execute them to avoid excess delays // We index these based on PR numbers. MAKE SURE YOU USE STRING INDICIES IN THIS IF YOU EVER TWEAK IT -aa @@ -196,7 +145,7 @@ SUBSYSTEM_DEF(changelog) // Create some queries for each PR for(var/pr_number in prs_to_process) var/datum/db_query/pr_meta = SSdbcore.NewQuery( - "SELECT author, DATE_FORMAT(date_merged, '%Y-%m-%d at %T') AS date FROM changelog WHERE pr_number = :prnum LIMIT 1", + "SELECT author, DATE_FORMAT(date_merged, '%Y-%m-%d at %T') AS date, CAST(UNIX_TIMESTAMP(date_merged) AS CHAR) AS ts FROM changelog WHERE pr_number = :prnum LIMIT 1", list("prnum" = pr_number) ) @@ -221,26 +170,27 @@ SUBSYSTEM_DEF(changelog) SSdbcore.MassExecute(entry_queries, TRUE, FALSE, TRUE) for(var/pr_number in prs_to_process) - // Initial declarations - var/pr_block = "" // HTML for the changelog section - var/author = "" // Author of the PR - var/merge_date = "" // Timestamp of when the PR was merged + var/list/this_pr = list() + this_pr["num"] = pr_number // Assemble metadata while(meta_queries["[pr_number]"].NextRow()) - author = meta_queries["[pr_number]"].item[1] - merge_date = meta_queries["[pr_number]"].item[2] + this_pr["author"] = meta_queries["[pr_number]"].item[1] + this_pr["merge_date"] = meta_queries["[pr_number]"].item[2] + this_pr["merge_ts"] = meta_queries["[pr_number]"].item[3] - // Now for each actual entry - pr_block += "
" - pr_block += "

#[pr_number] by [author] (Merged on [merge_date])" + var/list/cl_entries = list() while(entry_queries["[pr_number]"].NextRow()) - pr_block += "

[Text2Icon(entry_queries["[pr_number]"].item[1])] [entry_queries["[pr_number]"].item[2]]

" + var/list/this_entry = list() + this_entry["etype"] = entry_queries["[pr_number]"].item[1] + this_entry["etext"] = entry_queries["[pr_number]"].item[2] + cl_entries += list(this_entry) // Double list required or it merges them - pr_block += "

" + this_pr["entries"] = cl_entries + + changelog_data += list(this_pr) - changelogHTML += pr_block // Cleanup queries QDEL_LIST_ASSOC_VAL(meta_queries) @@ -249,28 +199,40 @@ SUBSYSTEM_DEF(changelog) // Make sure we return TRUE so we know it worked return TRUE +/datum/controller/subsystem/changelog/ui_static_data(mob/user) + var/list/data = list() + data["cl_data"] = changelog_data + data["last_cl"] = user.client.prefs.lastchangelog_2 + + return data + +/datum/controller/subsystem/changelog/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state) + ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "ChangelogView", name, 750, 800, master_ui, state) + ui.set_autoupdate(FALSE) + ui.open() + // Topic handler so that PRs and forums and stuff open in another window -/datum/controller/subsystem/changelog/Topic(href, href_list) - // Handler to open pages in your browser instead of inside the CL window - // Yes usr.client is gross here but src is the subsystem - // Takes the page to open as an argument - if(href_list["openPage"]) - switch(href_list["openPage"]) - if("forum") - usr.client.forum() - if("wiki") - usr.client.wiki() - if("github") - usr.client.github() - // Takes a PR number as argument - if(href_list["openPR"]) - if(GLOB.configuration.url.github_url) - if(alert("This will open PR #[href_list["openPR"]] in your browser. Are you sure?",,"Yes","No")=="No") +/datum/controller/subsystem/changelog/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + if(..()) + return + + . = TRUE + + switch(action) + // Takes a PR number as argument + if("open_pr") + var/pr_num = params["pr_number"] + if(GLOB.configuration.url.github_url) + if(alert("This will open PR #[pr_num] in your browser. Are you sure?", "Open PR", "Yes", "No") == "No") + return + + // If the github URL in the config has a trailing slash, it doesnt matter here, thankfully github accepts having a double slash: https://github.com/org/repo//pull/1 + var/url = "[GLOB.configuration.url.github_url]/pull/[pr_num]" + usr << link(url) return - // If the github URL in the config has a trailing slash, it doesnt matter here, thankfully github accepts having a double slash: https://github.com/org/repo//pull/1 - var/url = "[GLOB.configuration.url.github_url]/pull/[href_list["openPR"]]" - usr << link(url) - else + to_chat(usr, "The GitHub URL is not set in the server configuration. PRs cannot be opened from changelog view. Please inform the server host.") diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 1f80287d3bd..117d3feb044 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -429,6 +429,7 @@ /client/Destroy() announce_leave() // Do not put this below SSdebugview.stop_processing(src) + SSchangelog.startup_clients_open -= src if(holder) holder.owner = null GLOB.admins -= src diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index d83fae77808..df07c138759 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -61,6 +61,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts //game-preferences var/lastchangelog = "1" //Saved changlog timestamp (unix epoch) to detect if there was a change. Dont set this to 0 unless you want the last changelog date to be 4x longer than the expected lifespan of the universe. + var/lastchangelog_2 = "1" // Clone of the above var for viewing changes since last connection. This is never overriden. Yes it needs to exist. var/exp var/ooccolor = "#b82e00" var/list/be_special = list() //Special role selection diff --git a/code/modules/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm index 113db6f1acc..c22e539650f 100644 --- a/code/modules/client/preference/preferences_mysql.dm +++ b/code/modules/client/preference/preferences_mysql.dm @@ -27,6 +27,8 @@ colourblind_mode = query.item[21] keybindings = init_keybindings(raw = query.item[22]) + lastchangelog_2 = lastchangelog // Clone please + //Sanitize ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor)) UI_style = sanitize_inlist(UI_style, list("White", "Midnight", "Plasmafire", "Retro", "Slimecore", "Operative"), initial(UI_style)) diff --git a/tgui/packages/tgui/interfaces/ChangelogView.js b/tgui/packages/tgui/interfaces/ChangelogView.js new file mode 100644 index 00000000000..9d55b2e9a15 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChangelogView.js @@ -0,0 +1,65 @@ +import { useBackend, useLocalState } from '../backend'; +import { Button, Section, Box, Icon } from '../components'; +import { Window } from '../layouts'; + +export const ChangelogView = (props, context) => { + const { act, data } = useBackend(context); + const [onlyRecent, showOnlyRecent] = useLocalState(context, "onlyRecent", 0); + const { + cl_data, + last_cl, + } = data; + + const iconMap = { + "FIX": (), + "WIP": (), + "TWEAK": (), + "SOUNDADD": (), + "SOUNDDEL": (), + "CODEADD": (), + "CODEDEL": (), + "IMAGEADD": (), + "IMAGEDEL": (), + "SPELLCHECK": (), + "EXPERIMENT": (), + } + + const cl2icon = (cl) => { + if (cl in iconMap) { + return iconMap[cl]; + } + + // Sane default if not in list + return ; + } + + return ( + + +
showOnlyRecent(!onlyRecent)} + /> + }> + {cl_data.map(e => ( + (!onlyRecent && (e.merge_ts <= last_cl) || ( +
act("open_pr", { pr_number: e.num })} /> + }> + {e.entries.map(ent => ( + + {cl2icon(ent.etype)} {ent.etext} + + ))} +
+ )) + ))} +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/public/tgui.bundle.js b/tgui/packages/tgui/public/tgui.bundle.js index e5c687a7f49..ed4c045cbf6 100644 --- a/tgui/packages/tgui/public/tgui.bundle.js +++ b/tgui/packages/tgui/public/tgui.bundle.js @@ -1,5 +1,5 @@ -!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=230)}([function(e,t,n){"use strict";t.__esModule=!0;var o=n(232);Object.keys(o).forEach((function(e){"default"!==e&&"__esModule"!==e&&(e in t&&t[e]===o[e]||(t[e]=o[e]))}))},function(e,t,n){"use strict";t.__esModule=!0,t.useSharedState=t.useLocalState=t.useBackend=t.deleteLocalState=t.backendUpdate=t.backendSetSharedState=t.backendReducer=t.backendDeleteSharedState=void 0;var o=n(31),r=n(45);t.backendUpdate=function(e){return{type:"backend/update",payload:e}};var a=function(e,t){return{type:"backend/setSharedState",payload:{key:e,nextState:t}}};t.backendSetSharedState=a;var c=function(e){return{type:"backend/deleteSharedState",payload:e}};t.backendDeleteSharedState=c;t.backendReducer=function(e,t){var n=t.type,o=t.payload;if("backend/update"===n){var a=Object.assign({},e.config,o.config),c=Object.assign({},e.data,o.static_data,o.data),i=Object.assign({},e.shared);if(o.shared)for(var l=0,d=Object.keys(o.shared);l1?n-1:0),r=1;rn?n:e};t.clamp01=function(e){return e<0?0:e>1?1:e};t.scale=function(e,t,n){return(e-t)/(n-t)};t.round=function(e,t){return!e||isNaN(e)?e:(t|=0,a=(e*=n=Math.pow(10,t))>0|-(e<0),r=Math.abs(e%1)>=.4999999999854481,o=Math.floor(e),r&&(e=o+(a>0)),(r?e:Math.round(e))/n);var n,o,r,a};t.toFixed=function(e,t){return void 0===t&&(t=0),Number(e).toFixed(Math.max(t,0))};var o=function(e,t){return t&&e>=t[0]&&e<=t[1]};t.inRange=o;t.keyOfMatchingRange=function(e,t){for(var n=0,r=Object.keys(t);n0&&(t.style=l),t};t.computeBoxProps=N;var b=function(e){var t=e.textColor||e.color,n=e.backgroundColor;return(0,o.classes)([u(t)&&"color-"+t,u(n)&&"color-bg-"+n])};t.computeBoxClassName=b;var V=function(e){var t=e.as,n=void 0===t?"div":t,o=e.className,c=e.children,l=function(e,t){if(null==e)return{};var n,o,r={},a=Object.keys(e);for(o=0;o=0||(r[n]=e[n]);return r}(e,i);if("function"==typeof c)return c(N(e));var d="string"==typeof o?o+" "+b(l):b(l),u=N(l);return(0,r.createVNode)(a.VNodeFlags.HtmlElement,n,d,c,a.ChildFlags.UnknownChildren,u)};t.Box=V,V.defaultHooks=o.pureComponentHooks},function(e,t,n){"use strict";var o=n(39);e.exports=function(e){return o(e.length)}},function(e,t,n){"use strict";var o=n(5),r=n(12),a=n(47),c=n(129),i=n(127);e.exports=function(e,t,n,l){var d=!!l&&!!l.unsafe,u=!!l&&!!l.enumerable,s=!!l&&!!l.noTargetGet,m=l&&l.name!==undefined?l.name:t;return r(n)&&c(n,m,l),e===o?(u?e[t]=n:i(t,n),e):(d?!s&&e[t]&&(u=!0):delete e[t],u?e[t]=n:a(e,t,n),e)}},function(e,t,n){"use strict";function o(e,t){var n="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(n)return(n=n.call(e)).next.bind(n);if(Array.isArray(e)||(n=function(e,t){if(!e)return;if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(e,t)}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var o=0;return function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n",apos:"'"};return e.replace(/
/gi,"\n").replace(/<\/?[a-z0-9-_]+[^>]*>/gi,"").replace(/&(nbsp|amp|quot|lt|gt|apos);/g,(function(e,n){return t[n]})).replace(/&#?([0-9]+);/gi,(function(e,t){var n=parseInt(t,10);return String.fromCharCode(n)})).replace(/&#x?([0-9a-f]+);/gi,(function(e,t){var n=parseInt(t,16);return String.fromCharCode(n)}))};t.buildQueryString=function(e){return Object.keys(e).map((function(t){return encodeURIComponent(t)+"="+encodeURIComponent(e[t])})).join("&")}},function(e,t,n){"use strict";var o=n(58),r=n(7),a=n(84),c=n(18),i=n(22),l=n(90),d=r([].push),u=function(e){var t=1==e,n=2==e,r=3==e,u=4==e,s=6==e,m=7==e,p=5==e||s;return function(f,h,C,N){for(var b,V,g=c(f),v=a(g),y=o(h,C),x=i(v),_=0,k=N||l,L=t?k(f,x):n||m?k(f,0):undefined;x>_;_++)if((p||_ in v)&&(V=y(b=v[_],_,g),e))if(t)L[_]=V;else if(V)switch(e){case 3:return!0;case 5:return b;case 6:return _;case 2:d(L,b)}else switch(e){case 4:return!1;case 7:d(L,b)}return s?-1:r||u?u:L}};e.exports={forEach:u(0),map:u(1),filter:u(2),some:u(3),every:u(4),find:u(5),findIndex:u(6),filterReject:u(7)}},function(e,t,n){"use strict";var o=n(8),r=n(14),a=n(100),c=n(62),i=n(29),l=n(54),d=n(17),u=n(171),s=Object.getOwnPropertyDescriptor;t.f=o?s:function(e,t){if(e=i(e),t=l(t),u)try{return s(e,t)}catch(n){}if(d(e,t))return c(!r(a.f,e,t),e[t])}},function(e,t,n){"use strict";var o=n(5),r=n(12),a=n(70),c=o.TypeError;e.exports=function(e){if(r(e))return e;throw c(a(e)+" is not a function")}},function(e,t,n){"use strict";var o=n(5),r=n(12),a=function(e){return r(e)?e:undefined};e.exports=function(e,t){return arguments.length<2?a(o[e]):o[e]&&o[e][t]}},function(e,t,n){"use strict";var o=n(84),r=n(30);e.exports=function(e){return o(r(e))}},function(e,t,n){"use strict";var o=n(5).TypeError;e.exports=function(e){if(e==undefined)throw o("Can't call method on "+e);return e}},function(e,t,n){"use strict";function o(e,t,n,o,r,a,c){try{var i=e[a](c),l=i.value}catch(d){return void n(d)}i.done?t(l):Promise.resolve(l).then(o,r)}t.__esModule=!0,t.winset=t.winget=t.runCommand=t.callByondAsync=t.callByond=t.IS_IE8=void 0;var r=window.Byond,a=function(){var e=navigator.userAgent.match(/Trident\/(\d+).+?;/i);if(!e)return null;var t=e[1];return t?parseInt(t,10):null}(),c=null!==a&&a<=6;t.IS_IE8=c;var i=function(e,t){void 0===t&&(t={}),r.call(e,t)};t.callByond=i;var l=function(e,t){void 0===t&&(t={}),window.__callbacks__=window.__callbacks__||[];var n=window.__callbacks__.length,o=new Promise((function(e){window.__callbacks__.push(e)}));return r.call(e,Object.assign({},t,{callback:"__callbacks__["+n+"]"})),o};t.callByondAsync=l;t.runCommand=function(e){return i("winset",{command:e})};var d=function(){var e,t=(e=regeneratorRuntime.mark((function n(e,t){var o;return regeneratorRuntime.wrap((function(n){for(;;)switch(n.prev=n.next){case 0:return n.next=2,l("winget",{id:e,property:t});case 2:return o=n.sent,n.abrupt("return",o[t]);case 4:case"end":return n.stop()}}),n)})),function(){var t=this,n=arguments;return new Promise((function(r,a){var c=e.apply(t,n);function i(e){o(c,r,a,i,l,"next",e)}function l(e){o(c,r,a,i,l,"throw",e)}i(undefined)}))});return function(e,n){return t.apply(this,arguments)}}();t.winget=d;t.winset=function(e,t,n){var o;return i("winset",((o={})[e+"."+t]=n,o))}},function(e,t,n){"use strict";t.__esModule=!0,t.zipWith=t.zip=t.uniqBy=t.toKeyedArray=t.toArray=t.sortBy=t.reduce=t.map=t.filter=void 0;t.toArray=function(e){if(Array.isArray(e))return e;if("object"==typeof e){var t=Object.prototype.hasOwnProperty,n=[];for(var o in e)t.call(e,o)&&n.push(e[o]);return n}return[]};t.toKeyedArray=function(e,t){return void 0===t&&(t="key"),o((function(e,n){var o;return Object.assign(((o={})[t]=n,o),e)}))(e)};t.filter=function(e){return function(t){if(null===t&&t===undefined)return t;if(Array.isArray(t)){for(var n=[],o=0;oi)return 1}return 0};t.sortBy=function(){for(var e=arguments.length,t=new Array(e),n=0;n"+l+""}},function(e,t,n){"use strict";var o=n(6);e.exports=function(e){return o((function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3}))}},function(e,t,n){"use strict";var o,r,a,c=n(173),i=n(5),l=n(7),d=n(11),u=n(47),s=n(17),m=n(126),p=n(102),f=n(86),h=i.TypeError,C=i.WeakMap;if(c||m.state){var N=m.state||(m.state=new C),b=l(N.get),V=l(N.has),g=l(N.set);o=function(e,t){if(V(N,e))throw new h("Object already initialized");return t.facade=e,g(N,e,t),t},r=function(e){return b(N,e)||{}},a=function(e){return V(N,e)}}else{var v=p("state");f[v]=!0,o=function(e,t){if(s(e,v))throw new h("Object already initialized");return t.facade=e,u(e,v,t),t},r=function(e){return s(e,v)?e[v]:{}},a=function(e){return s(e,v)}}e.exports={set:o,get:r,has:a,enforce:function(e){return a(e)?r(e):o(e,{})},getterFor:function(e){return function(t){var n;if(!d(t)||(n=r(t)).type!==e)throw h("Incompatible receiver, "+e+" required");return n}}}},function(e,t,n){"use strict";var o=Math.ceil,r=Math.floor;e.exports=function(e){var t=+e;return t!=t||0===t?0:(t>0?r:o)(t)}},function(e,t,n){"use strict";var o=n(38),r=Math.min;e.exports=function(e){return e>0?r(o(e),9007199254740991):0}},function(e,t,n){"use strict";t.__esModule=!0,t.modalRegisterBodyOverride=t.modalOpen=t.modalClose=t.modalAnswer=t.ComplexModal=void 0;var o=n(0),r=n(1),a=n(2),c={};t.modalOpen=function(e,t,n){var o=(0,r.useBackend)(e),a=o.act,c=o.data,i=Object.assign(c.modal?c.modal.args:{},n||{});a("modal_open",{id:t,arguments:JSON.stringify(i)})};t.modalRegisterBodyOverride=function(e,t){c[e]=t};var i=function(e,t,n,o){var a=(0,r.useBackend)(e),c=a.act,i=a.data;if(i.modal){var l=Object.assign(i.modal.args||{},o||{});c("modal_answer",{id:t,answer:n,arguments:JSON.stringify(l)})}};t.modalAnswer=i;var l=function(e,t){(0,(0,r.useBackend)(e).act)("modal_close",{id:t})};t.modalClose=l;t.ComplexModal=function(e,t){var n=(0,r.useBackend)(t).data;if(n.modal){var d,u,s=n.modal,m=s.id,p=s.text,f=s.type,h=(0,o.createComponentVNode)(2,a.Button,{icon:"arrow-left",content:"Cancel",color:"grey",onClick:function(){return l(t)}}),C="auto";if(c[m])u=c[m](n.modal,t);else if("input"===f){var N=n.modal.value;d=function(e){return i(t,m,N)},u=(0,o.createComponentVNode)(2,a.Input,{value:n.modal.value,placeholder:"ENTER to submit",width:"100%",my:"0.5rem",autofocus:!0,onChange:function(e,t){N=t}}),h=(0,o.createComponentVNode)(2,a.Box,{mt:"0.5rem",children:[(0,o.createComponentVNode)(2,a.Button,{icon:"arrow-left",content:"Cancel",color:"grey",onClick:function(){return l(t)}}),(0,o.createComponentVNode)(2,a.Button,{icon:"check",content:"Confirm",color:"good",float:"right",m:"0",onClick:function(){return i(t,m,N)}}),(0,o.createComponentVNode)(2,a.Box,{clear:"both"})]})}else if("choice"===f){var b="object"==typeof n.modal.choices?Object.values(n.modal.choices):n.modal.choices;u=(0,o.createComponentVNode)(2,a.Dropdown,{options:b,selected:n.modal.value,width:"100%",my:"0.5rem",onSelected:function(e){return i(t,m,e)}}),C="initial"}else"bento"===f?u=(0,o.createComponentVNode)(2,a.Flex,{spacingPrecise:"1",wrap:"wrap",my:"0.5rem",maxHeight:"1%",children:n.modal.choices.map((function(e,r){return(0,o.createComponentVNode)(2,a.Flex.Item,{flex:"1 1 auto",children:(0,o.createComponentVNode)(2,a.Button,{selected:r+1===parseInt(n.modal.value,10),onClick:function(){return i(t,m,r+1)},children:(0,o.createVNode)(1,"img",null,null,1,{src:e})})},r)}))}):"boolean"===f&&(h=(0,o.createComponentVNode)(2,a.Box,{mt:"0.5rem",children:[(0,o.createComponentVNode)(2,a.Button,{icon:"times",content:n.modal.no_text,color:"bad",float:"left",mb:"0",onClick:function(){return i(t,m,0)}}),(0,o.createComponentVNode)(2,a.Button,{icon:"check",content:n.modal.yes_text,color:"good",float:"right",m:"0",onClick:function(){return i(t,m,1)}}),(0,o.createComponentVNode)(2,a.Box,{clear:"both"})]}));return(0,o.createComponentVNode)(2,a.Modal,{maxWidth:e.maxWidth||window.innerWidth/2+"px",maxHeight:e.maxHeight||window.innerHeight/2+"px",onEnter:d,mx:"auto",overflowY:C,children:[(0,o.createComponentVNode)(2,a.Box,{display:"inline",children:p}),u,h]})}}},function(e,t,n){"use strict";var o=n(5),r=n(17),a=n(12),c=n(18),i=n(102),l=n(139),d=i("IE_PROTO"),u=o.Object,s=u.prototype;e.exports=l?u.getPrototypeOf:function(e){var t=c(e);if(r(t,d))return t[d];var n=t.constructor;return a(n)&&t instanceof n?n.prototype:t instanceof u?s:null}},function(e,t,n){"use strict";var o=n(7),r=o({}.toString),a=o("".slice);e.exports=function(e){return a(r(e),8,-1)}},function(e,t,n){"use strict";var o=n(7);e.exports=o({}.isPrototypeOf)},function(e,t,n){"use strict";var o=n(83),r=Function.prototype,a=r.apply,c=r.call;e.exports="object"==typeof Reflect&&Reflect.apply||(o?c.bind(a):function(){return c.apply(a,arguments)})},function(e,t,n){"use strict";t.__esModule=!0,t.timeAgo=t.getGasLabel=t.getGasColor=t.UI_UPDATE=t.UI_INTERACTIVE=t.UI_DISABLED=t.UI_CLOSE=t.RADIO_CHANNELS=t.CSS_COLORS=t.COLORS=void 0;t.UI_INTERACTIVE=2;t.UI_UPDATE=1;t.UI_DISABLED=0;t.UI_CLOSE=-1;t.COLORS={department:{command:"#526aff",security:"#CF0000",medical:"#009190",science:"#993399",engineering:"#A66300",supply:"#9F8545",service:"#80A000",centcom:"#78789B",other:"#C38312"},damageType:{oxy:"#3498db",toxin:"#2ecc71",burn:"#e67e22",brute:"#e74c3c"}};t.CSS_COLORS=["black","white","red","orange","yellow","olive","green","teal","blue","violet","purple","pink","brown","grey","good","average","bad","label"];t.RADIO_CHANNELS=[{name:"Syndicate",freq:1213,color:"#a52a2a"},{name:"SyndTeam",freq:1244,color:"#a52a2a"},{name:"Red Team",freq:1215,color:"#ff4444"},{name:"Blue Team",freq:1217,color:"#3434fd"},{name:"Response Team",freq:1345,color:"#2681a5"},{name:"Special Ops",freq:1341,color:"#2681a5"},{name:"Supply",freq:1347,color:"#b88646"},{name:"Service",freq:1349,color:"#6ca729"},{name:"Science",freq:1351,color:"#c68cfa"},{name:"Command",freq:1353,color:"#5177ff"},{name:"Procedure",freq:1339,color:"#F70285"},{name:"Medical",freq:1355,color:"#57b8f0"},{name:"Medical(I)",freq:1485,color:"#57b8f0"},{name:"Engineering",freq:1357,color:"#f37746"},{name:"Security",freq:1359,color:"#dd3535"},{name:"Security(I)",freq:1475,color:"#dd3535"},{name:"AI Private",freq:1343,color:"#d65d95"},{name:"Common",freq:1459,color:"#1ecc43"}];var o=[{id:"o2",name:"Oxygen",label:"O\u2082",color:"blue"},{id:"n2",name:"Nitrogen",label:"N\u2082",color:"red"},{id:"co2",name:"Carbon Dioxide",label:"CO\u2082",color:"grey"},{id:"plasma",name:"Plasma",label:"Plasma",color:"pink"},{id:"water_vapor",name:"Water Vapor",label:"H\u2082O",color:"grey"},{id:"nob",name:"Hyper-noblium",label:"Hyper-nob",color:"teal"},{id:"n2o",name:"Nitrous Oxide",label:"N\u2082O",color:"red"},{id:"no2",name:"Nitryl",label:"NO\u2082",color:"brown"},{id:"tritium",name:"Tritium",label:"Tritium",color:"green"},{id:"bz",name:"BZ",label:"BZ",color:"purple"},{id:"stim",name:"Stimulum",label:"Stimulum",color:"purple"},{id:"pluox",name:"Pluoxium",label:"Pluoxium",color:"blue"},{id:"miasma",name:"Miasma",label:"Miasma",color:"olive"},{id:"hydrogen",name:"Hydrogen",label:"H\u2082",color:"white"},{id:"ab",name:"Agent B",label:"Agent B",color:"purple"}];t.getGasLabel=function(e,t){var n=String(e).toLowerCase(),r=o.find((function(e){return e.id===n||e.name.toLowerCase()===n}));return r&&r.label||t||e};t.getGasColor=function(e){var t=String(e).toLowerCase(),n=o.find((function(e){return e.id===t||e.name.toLowerCase()===t}));return n&&n.color};t.timeAgo=function(e,t){if(e>t)return"in the future";var n=(t/=10)-(e/=10);if(n>3600){var o=Math.round(n/3600);return o+" hour"+(1===o?"":"s")+" ago"}if(n>60){var r=Math.round(n/60);return r+" minute"+(1===r?"":"s")+" ago"}var a=Math.round(n);return a+" second"+(1===a?"":"s")+" ago"}},function(e,t,n){"use strict";var o=n(28);e.exports=o("navigator","userAgent")||""},function(e,t,n){"use strict";var o=n(8),r=n(16),a=n(62);e.exports=o?function(e,t,n){return r.f(e,t,a(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){"use strict";var o=n(38),r=Math.max,a=Math.min;e.exports=function(e,t){var n=o(e);return n<0?r(n+t,0):a(n,t)}},function(e,t,n){"use strict";var o,r=n(9),a=n(133),c=n(131),i=n(86),l=n(176),d=n(128),u=n(102),s=u("IE_PROTO"),m=function(){},p=function(e){return"