mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-22 05:17:38 +01:00
516 compatibility for browsers (#20652)
this is a bit of https://github.com/cmss13-devs/cmss13/pull/8646 https://github.com/cmss13-devs/cmss13/pull/8875 https://github.com/cmss13-devs/cmss13/pull/8606 https://github.com/cmss13-devs/cmss13/pull/8607 https://github.com/cmss13-devs/cmss13/pull/8359 https://github.com/cmss13-devs/cmss13/pull/7860 ( and thus https://github.com/ParadiseSS13/Paradise/pull/25105 https://github.com/ParadiseSS13/Paradise/pull/25205 https://github.com/ParadiseSS13/Paradise/pull/25363 https://github.com/ParadiseSS13/Paradise/pull/26423 and https://github.com/ParadiseSS13/Paradise/pull/26433 ) https://github.com/tgstation/tgstation/pull/89766 in order to work properly - using the efficient storage method, instead of the byond json method - github pages needs to be enabled and built from the gh-pages branch. because yeah --------- Co-authored-by: harryob <55142896+harryob@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
name: Generate web assets
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
generate_static_assets:
|
||||
if: ( !contains(github.event.head_commit.message, '[ci skip]') )
|
||||
runs-on: ubuntu-latest
|
||||
concurrency: gen-assets
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Generate static assets
|
||||
run: |
|
||||
mv tgui/public/ assets
|
||||
- name: Deploy
|
||||
uses: JamesIves/github-pages-deploy-action@3.7.1
|
||||
with:
|
||||
BRANCH: gh-pages
|
||||
CLEAN: true
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SINGLE_COMMIT: true
|
||||
FOLDER: assets
|
||||
@@ -72,6 +72,7 @@
|
||||
#include "code\__DEFINES\gradient.dm"
|
||||
#include "code\__DEFINES\guns.dm"
|
||||
#include "code\__DEFINES\hallucinations.dm"
|
||||
#include "code\__DEFINES\html.dm"
|
||||
#include "code\__DEFINES\hud.dm"
|
||||
#include "code\__DEFINES\hydroponics.dm"
|
||||
#include "code\__DEFINES\icon_layering.dm"
|
||||
|
||||
@@ -113,7 +113,7 @@ GLOBAL_DATUM_INIT(vsc, /vs_control, new())
|
||||
if("[ch]_NAME" in vars) vw_name = vars["[ch]_NAME"]
|
||||
dat += "<b>[vw_name] = [vw]</b> <A href='byond://?src=[REF(src)];changevar=[ch]'>\[Change\]</A><br>"
|
||||
dat += "<i>[vw_desc]</i><br><br>"
|
||||
user << browse(dat,"window=settings")
|
||||
user << browse(HTML_SKELETON(dat), "window=settings")
|
||||
|
||||
/vs_control/Topic(href,href_list)
|
||||
if("changevar" in href_list)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#define HTML_SKELETON_INTERNAL(head, body) \
|
||||
"<!DOCTYPE html><html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><meta http-equiv='X-UA-Compatible' content='IE=edge'>[head]</head><body>[body]</body></html>"
|
||||
|
||||
#define HTML_SKELETON_TITLE(title, body) HTML_SKELETON_INTERNAL("<title>[title]</title>", body)
|
||||
#define HTML_SKELETON(body) HTML_SKELETON_INTERNAL("", body)
|
||||
@@ -477,6 +477,8 @@ GLOBAL_LIST_EMPTY(gamemode_cache)
|
||||
var/asset_cdn_webroot = ""
|
||||
var/asset_cdn_url = null
|
||||
|
||||
var/storage_cdn_iframe = "https://aurorastation.github.io/aurora.3/iframe.html"
|
||||
|
||||
GENERAL_PROTECT_DATUM(/datum/configuration)
|
||||
|
||||
/datum/configuration/New()
|
||||
@@ -1087,6 +1089,9 @@ GENERAL_PROTECT_DATUM(/datum/configuration)
|
||||
if("asset_cdn_url")
|
||||
asset_cdn_url = (value[length(value)] != "/" ? (value + "/") : value)
|
||||
|
||||
if("storage_cdn_iframe")
|
||||
storage_cdn_iframe = value
|
||||
|
||||
else
|
||||
log_config("Unknown setting in configuration: '[name]'")
|
||||
|
||||
@@ -1181,6 +1186,8 @@ GENERAL_PROTECT_DATUM(/datum/configuration)
|
||||
load_away_sites_config()
|
||||
load_exoplanets_config()
|
||||
|
||||
Master.OnConfigLoad()
|
||||
|
||||
/datum/configuration/proc/save_logging_config()
|
||||
rustg_file_write(json_encode(GLOB.config.logsettings), "config/logging.json")
|
||||
rustg_file_write(json_encode(GLOB.config.logfiles), "config/logging_files.json")
|
||||
|
||||
@@ -34,6 +34,24 @@ PROCESSING_SUBSYSTEM_DEF(tgui)
|
||||
basehtml = replacetextEx(basehtml, "<!-- tgui:inline-polyfill -->", polyfill)
|
||||
basehtml = replacetextEx(basehtml, "<!-- tgui:nt-copyright -->", "NanoTrasen © 2457-[text2num(time2text(world.realtime, "YYYY")) + 442]")
|
||||
|
||||
/datum/controller/subsystem/processing/tgui/OnConfigLoad()
|
||||
var/storage_iframe = GLOB.config.storage_cdn_iframe
|
||||
if(storage_iframe && storage_iframe != /datum/configuration::storage_cdn_iframe)
|
||||
basehtml = replacetextEx(basehtml, "tgui:storagecdn", storage_iframe)
|
||||
return
|
||||
|
||||
if(GLOB.config.asset_transport == "webroot")
|
||||
var/datum/asset_transport/webroot/webroot = SSassets.transport
|
||||
|
||||
var/datum/asset_cache_item/item = webroot.register_asset("iframe.html", file("tgui/public/iframe.html"))
|
||||
basehtml = replacetext(basehtml, "tgui:storagecdn", webroot.get_asset_url("iframe.html", item))
|
||||
return
|
||||
|
||||
if(!storage_iframe)
|
||||
return
|
||||
|
||||
basehtml = replacetextEx(basehtml, "tgui:storagecdn", storage_iframe)
|
||||
|
||||
/datum/controller/subsystem/processing/tgui/Shutdown()
|
||||
close_all_uis()
|
||||
|
||||
|
||||
+1
-1
@@ -187,7 +187,7 @@
|
||||
out += "None."
|
||||
out += "<br><a href='byond://?src=[REF(src)];obj_add=1'>\[add\]</a>"
|
||||
out += "<b>Ambitions:</b> [ambitions ? ambitions : "None"] <a href='byond://?src=[REF(src)];amb_edit=[REF(src)]'>\[edit\]</a></br>"
|
||||
usr << browse(out, "window=edit_memory[src]")
|
||||
usr << browse(HTML_SKELETON(out), "window=edit_memory[src]")
|
||||
|
||||
/datum/mind/Topic(href, href_list)
|
||||
if(!check_rights(R_ADMIN)) return
|
||||
|
||||
@@ -152,7 +152,7 @@ GLOBAL_LIST_INIT(all_technomancer_assistance, (typesof(/datum/technomancer/assis
|
||||
dat += "<a href='byond://?src=[REF(src)];spell_choice=[spell.name]'>Purchase</a> ([spell.cost])<br><br>"
|
||||
else
|
||||
dat += "<font color='red'><b>Cannot afford!</b></font><br><br>"
|
||||
user << browse(dat, "window=radio")
|
||||
user << browse(HTML_SKELETON(dat), "window=radio")
|
||||
onclose(user, "radio")
|
||||
if(1) //Equipment
|
||||
var/dat = ""
|
||||
@@ -173,7 +173,7 @@ GLOBAL_LIST_INIT(all_technomancer_assistance, (typesof(/datum/technomancer/assis
|
||||
dat += "<a href='byond://?src=[REF(src)];item_choice=[E.name]'>Purchase</a> ([E.cost])<br><br>"
|
||||
else
|
||||
dat += "<font color='red'><b>Cannot afford!</b></font><br><br>"
|
||||
user << browse(dat, "window=radio")
|
||||
user << browse(HTML_SKELETON(dat), "window=radio")
|
||||
onclose(user, "radio")
|
||||
if(2) //Consumables
|
||||
var/dat = ""
|
||||
@@ -194,7 +194,7 @@ GLOBAL_LIST_INIT(all_technomancer_assistance, (typesof(/datum/technomancer/assis
|
||||
dat += "<a href='byond://?src=[REF(src)];item_choice=[C.name]'>Purchase</a> ([C.cost])<br><br>"
|
||||
else
|
||||
dat += "<font color='red'><b>Cannot afford!</b></font><br><br>"
|
||||
user << browse(dat, "window=radio")
|
||||
user << browse(HTML_SKELETON(dat), "window=radio")
|
||||
onclose(user, "radio")
|
||||
if(3) //Assistance
|
||||
var/dat = ""
|
||||
@@ -214,7 +214,7 @@ GLOBAL_LIST_INIT(all_technomancer_assistance, (typesof(/datum/technomancer/assis
|
||||
dat += "<a href='byond://?src=[REF(src)];item_choice=[A.name]'>Purchase</a> ([A.cost])<br><br>"
|
||||
else
|
||||
dat += "<font color='red'><b>Cannot afford!</b></font><br><br>"
|
||||
user << browse(dat, "window=radio")
|
||||
user << browse(HTML_SKELETON(dat), "window=radio")
|
||||
onclose(user, "radio")
|
||||
if(4) //Info
|
||||
var/dat = ""
|
||||
@@ -281,7 +281,7 @@ GLOBAL_LIST_INIT(all_technomancer_assistance, (typesof(/datum/technomancer/assis
|
||||
dat += "When a function refers to 'allies', it means you, your apprentices, currently controlled entities (with the \
|
||||
Control function), and friendly simple-minded entities that you've summoned with the Scepter of Enhancement.<br>"
|
||||
dat += "A meter is equal to one 'tile'.<br>"
|
||||
user << browse(dat, "window=radio")
|
||||
user << browse(HTML_SKELETON(dat), "window=radio")
|
||||
onclose(user, "radio")
|
||||
|
||||
// Proc: Topic()
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
dat += "<b><a href='byond://?src=[REF(src)];menu=3'>No</a></b>"
|
||||
|
||||
|
||||
user << browse(dat, "window=cloning")
|
||||
user << browse(HTML_SKELETON(dat), "window=cloning")
|
||||
onclose(user, "cloning")
|
||||
return
|
||||
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
//onclose(user, "suit_storage_unit")
|
||||
|
||||
|
||||
user << browse(dat, "window=suit_storage_unit;size=400x500")
|
||||
user << browse(HTML_SKELETON(dat), "window=suit_storage_unit;size=400x500")
|
||||
onclose(user, "suit_storage_unit")
|
||||
return
|
||||
|
||||
|
||||
@@ -1495,7 +1495,7 @@
|
||||
|
||||
/obj/item/book/manual/tcaf_pamphlet/attack_self(var/mob/user as mob)
|
||||
if(src.dat)
|
||||
user << browse("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]", "window=book")
|
||||
user << browse(HTML_SKELETON("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]"), "window=book")
|
||||
user.visible_message("[user] opens a pamphlet titled \"[src.title]\" and begins reading intently.")
|
||||
playsound(loc, 'sound/bureaucracy/paperfold.ogg', 50, 1)
|
||||
onclose(user, "book")
|
||||
|
||||
@@ -92,7 +92,7 @@ ABSTRACT_TYPE(/obj/item/storage/secure)
|
||||
message = "*****"
|
||||
dat += "<HR>\n>[message]<BR>\n<A href='byond://?src=[REF(src)];type=1'>1</A>-<A href='byond://?src=[REF(src)];type=2'>2</A>-<A href='byond://?src=[REF(src)];type=3'>3</A><BR>\n<A href='byond://?src=[REF(src)];type=4'>4</A>-<A href='byond://?src=[REF(src)];type=5'>5</A>-<A href='byond://?src=[REF(src)];type=6'>6</A><BR>\n<A href='byond://?src=[REF(src)];type=7'>7</A>-<A href='byond://?src=[REF(src)];type=8'>8</A>-<A href='byond://?src=[REF(src)];type=9'>9</A><BR>\n<A href='byond://?src=[REF(src)];type=R'>R</A>-<A href='byond://?src=[REF(src)];type=0'>0</A>-<A href='byond://?src=[REF(src)];type=E'>E</A><BR>\n</TT>"
|
||||
|
||||
user << browse(dat, "window=caselock;size=300x280")
|
||||
user << browse(HTML_SKELETON(dat), "window=caselock;size=300x280")
|
||||
|
||||
/obj/item/storage/secure/Topic(href, href_list)
|
||||
..()
|
||||
|
||||
@@ -55,7 +55,7 @@ Frequency:
|
||||
<A href='byond://?src=[REF(src)];freq=10'>+</A><BR>
|
||||
|
||||
<A href='byond://?src=[REF(src)];refresh=1'>Refresh</A>"}
|
||||
user << browse(dat, "window=radio")
|
||||
user << browse(HTML_SKELETON(dat), "window=radio")
|
||||
onclose(user, "radio")
|
||||
return
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@
|
||||
i = (++i % 2)
|
||||
|
||||
output += "</table>"
|
||||
user << browse(output, "window=banmirrors;size=600x400")
|
||||
user << browse(HTML_SKELETON(output), "window=banmirrors;size=600x400")
|
||||
|
||||
/proc/display_mirrors_ckeys(mob/user, mirror_id)
|
||||
if (!user || !check_rights(R_MOD|R_ADMIN) || !mirror_id)
|
||||
@@ -223,7 +223,7 @@
|
||||
return
|
||||
|
||||
output += "<br><br><a href='byond://?_src_=holder;dbbanmirrors=[query.item[2]];'>Back</a>"
|
||||
user << browse(output, "window=banmirrors")
|
||||
user << browse(HTML_SKELETON(output), "window=banmirrors")
|
||||
|
||||
/proc/toggle_mirror_status(mob/user, mirror_id, inactive = FALSE)
|
||||
if (!user || !check_rights(R_MOD|R_ADMIN) || !mirror_id)
|
||||
|
||||
@@ -189,7 +189,7 @@ var/savefile/Banlist
|
||||
|
||||
dat += "</table>"
|
||||
dat = "<HR><B>Bans:</B> <FONT COLOR=blue>(U) = Unban , (E) = Edit Ban</FONT> - <FONT COLOR=green>([count] Bans)</FONT><HR><table border=1 rules=all frame=void cellspacing=0 cellpadding=3 >[dat]"
|
||||
usr << browse(dat, "window=unbanp;size=875x400")
|
||||
usr << browse(HTML_SKELETON(dat), "window=unbanp;size=875x400")
|
||||
|
||||
//////////////////////////////////// DEBUG ////////////////////////////////////
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ var/global/enabled_spooking = 0
|
||||
if(index == page)
|
||||
dat += "</b>"
|
||||
|
||||
usr << browse(dat, "window=player_notes;size=400x400")
|
||||
usr << browse(HTML_SKELETON(dat), "window=player_notes;size=400x400")
|
||||
|
||||
|
||||
/datum/admins/proc/player_has_info(var/key as text)
|
||||
@@ -598,7 +598,7 @@ var/global/enabled_spooking = 0
|
||||
dat+="Please report this on GitHub, along with what you did to make this appear."
|
||||
|
||||
|
||||
usr << browse(dat, "window=admincaster_main;size=400x600")
|
||||
usr << browse(HTML_SKELETON(dat), "window=admincaster_main;size=400x600")
|
||||
onclose(usr, "admincaster_main")
|
||||
|
||||
|
||||
@@ -618,7 +618,7 @@ var/global/enabled_spooking = 0
|
||||
dat += "<tr><td>[ckey] - [ban[2]] - (<a href='byond://?src=[REF(src)];jobban_tgt=[ckey];jobban_job=[job];'>unban</a>)</td></tr>"
|
||||
|
||||
dat += "</table>"
|
||||
usr << browse(dat, "window=ban;size=400x400")
|
||||
usr << browse(HTML_SKELETON(dat), "window=ban;size=400x400")
|
||||
|
||||
/datum/admins/proc/Game()
|
||||
if(!check_rights(0)) return
|
||||
@@ -640,7 +640,7 @@ var/global/enabled_spooking = 0
|
||||
<A href='byond://?src=[REF(src)];vsc=default'>Choose a default ZAS setting</A><br>
|
||||
"}
|
||||
|
||||
usr << browse(dat, "window=admin2;size=210x280")
|
||||
usr << browse(HTML_SKELETON(dat), "window=admin2;size=210x280")
|
||||
return
|
||||
|
||||
/datum/admins/proc/Secrets()
|
||||
@@ -658,7 +658,7 @@ var/global/enabled_spooking = 0
|
||||
continue
|
||||
dat += "<A href='byond://?src=[REF(src)];admin_secrets=[REF(item)]'>[item.name()]</A><BR>"
|
||||
dat += "<BR>"
|
||||
usr << browse(dat, "window=secrets")
|
||||
usr << browse(HTML_SKELETON(dat), "window=secrets")
|
||||
return
|
||||
|
||||
|
||||
@@ -1139,7 +1139,7 @@ var/global/enabled_spooking = 0
|
||||
out += " None."
|
||||
out += " <a href='byond://?src=[REF(SSticker.mode)];add_antag_type=1'>\[+\]</a><br/>"
|
||||
|
||||
usr << browse(out, "window=edit_mode[src]")
|
||||
usr << browse(HTML_SKELETON(out), "window=edit_mode[src]")
|
||||
feedback_add_details("admin_verb","SGM")
|
||||
|
||||
|
||||
|
||||
@@ -499,6 +499,7 @@ GLOBAL_LIST_INIT(admin_verbs_dev, list( //will need to be altered - Ryan784
|
||||
/client/proc/cmd_generate_lag,
|
||||
/client/proc/create_poll, //Allows to create polls
|
||||
/client/proc/profiler_start,
|
||||
/client/proc/allow_browser_inspect,
|
||||
))
|
||||
|
||||
GLOBAL_LIST_INIT(admin_verbs_cciaa, list(
|
||||
|
||||
@@ -10,4 +10,4 @@
|
||||
dat += "<li>[l]</li>"
|
||||
if(!GLOB.admin_log.len)
|
||||
dat += "No-one has done anything this round!"
|
||||
user << browse(dat, "window=admin_log")
|
||||
user << browse(HTML_SKELETON(dat), "window=admin_log")
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
var/dat = "<B>Bombing List</B>"
|
||||
for(var/l in GLOB.bombers)
|
||||
dat += "[l]<BR>"
|
||||
user << browse(dat, "window=bombers")
|
||||
user << browse(HTML_SKELETON(dat), "window=bombers")
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
if(H.dna && H.ckey)
|
||||
dat += "<tr><td>[H]</td><td>[H.dna.unique_enzymes]</td><td>[H.b_type]</td></tr>"
|
||||
dat += "</table>"
|
||||
user << browse(dat, "window=DNA;size=440x410")
|
||||
user << browse(HTML_SKELETON(dat), "window=DNA;size=440x410")
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
else if(!H.dna)
|
||||
dat += "<tr><td>[H]</td><td>H.dna = null</td></tr>"
|
||||
dat += "</table>"
|
||||
user << browse(dat, "window=fingerprints;size=440x410")
|
||||
user << browse(HTML_SKELETON(dat), "window=fingerprints;size=440x410")
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
var/dat = "<B>Showing last [length(GLOB.lawchanges)] law changes.</B><HR>"
|
||||
for(var/sig in GLOB.lawchanges)
|
||||
dat += "[sig]<BR>"
|
||||
user << browse(dat, "window=lawchanges;size=800x500")
|
||||
user << browse(HTML_SKELETON(dat), "window=lawchanges;size=800x500")
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
var/dat = "<B>Showing last [length(GLOB.lastsignalers)] signalers.</B><HR>"
|
||||
for(var/sig in GLOB.lastsignalers)
|
||||
dat += "[sig]<BR>"
|
||||
user << browse(dat, "window=lastsignalers;size=800x500")
|
||||
user << browse(HTML_SKELETON(dat), "window=lastsignalers;size=800x500")
|
||||
|
||||
@@ -369,7 +369,7 @@
|
||||
[banhtml.Join("")]
|
||||
</body>
|
||||
"}
|
||||
usr << browse(html,"window=stickybans;size=700x400")
|
||||
usr << browse(HTML_SKELETON(html), "window=stickybans;size=700x400")
|
||||
|
||||
/proc/sticky_banned_ckeys()
|
||||
if (establish_db_connection(GLOB.dbcon) || length(SSstickyban.dbcache))
|
||||
|
||||
@@ -416,7 +416,7 @@
|
||||
dat += {"<A href='byond://?src=[REF(src)];c_mode2=secret'>Secret</A><br>"}
|
||||
dat += {"<A href='byond://?src=[REF(src)];c_mode2=random'>Random</A><br>"}
|
||||
dat += {"Now: [GLOB.master_mode]"}
|
||||
usr << browse(dat, "window=c_mode")
|
||||
usr << browse(HTML_SKELETON(dat), "window=c_mode")
|
||||
|
||||
else if(href_list["f_secret"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
@@ -431,7 +431,7 @@
|
||||
dat += {"<A href='byond://?src=[REF(src)];f_secret2=[mode]'>[GLOB.config.mode_names[mode]]</A><br>"}
|
||||
dat += {"<A href='byond://?src=[REF(src)];f_secret2=secret'>Random (default)</A><br>"}
|
||||
dat += {"Now: [GLOB.secret_force_mode]"}
|
||||
usr << browse(dat, "window=f_secret")
|
||||
usr << browse(HTML_SKELETON(dat), "window=f_secret")
|
||||
|
||||
else if(href_list["c_mode2"])
|
||||
if(!check_rights(R_ADMIN|R_SERVER))
|
||||
@@ -981,7 +981,7 @@
|
||||
var/obj/pageobj = B.pages[page]
|
||||
data += "<A href='byond://?src=[REF(src)];AdminFaxViewPage=[page];paper_bundle=[REF(B)]'>Page [page] - [pageobj.name]</A><BR>"
|
||||
|
||||
usr << browse(data, "window=[B.name]")
|
||||
usr << browse(HTML_SKELETON(data), "window=[B.name]")
|
||||
else
|
||||
to_chat(usr, SPAN_WARNING("The faxed item is not viewable. This is probably a bug, and should be reported on the tracker: [fax.type]"))
|
||||
|
||||
|
||||
@@ -413,7 +413,7 @@
|
||||
|
||||
dellog += "</ol>"
|
||||
|
||||
usr << browse(dellog.Join(), "window=dellog")
|
||||
usr << browse(HTML_SKELETON(dellog.Join()), "window=dellog")
|
||||
|
||||
/**
|
||||
* Same as `cmd_display_del_log`, but only shows harddels
|
||||
@@ -454,14 +454,14 @@
|
||||
|
||||
dellog += "</ol>"
|
||||
|
||||
usr << browse(dellog.Join(), "window=harddellog")
|
||||
usr << browse(HTML_SKELETON(dellog.Join()), "window=harddellog")
|
||||
|
||||
/client/proc/cmd_display_init_log()
|
||||
set category = "Debug"
|
||||
set name = "Display Initialize() Log"
|
||||
set desc = "Displays a list of things that didn't handle Initialize() properly"
|
||||
|
||||
usr << browse(replacetext(SSatoms.InitLog(), "\n", "<br>"), "window=initlog")
|
||||
usr << browse(HTML_SKELETON(replacetext(SSatoms.InitLog(), "\n", "<br>")), "window=initlog")
|
||||
|
||||
/client/proc/reload_nanoui_resources()
|
||||
set category = "Debug"
|
||||
@@ -499,3 +499,19 @@
|
||||
for(var/atom/an_atom in world)
|
||||
if(world.tick_usage > (100+(tick_offenses+jitter_this_run)))
|
||||
stoplag()
|
||||
|
||||
/client/proc/allow_browser_inspect()
|
||||
set category = "Debug"
|
||||
set name = "Allow Browser Inspect"
|
||||
set desc = "Allow browser debugging via inspect"
|
||||
|
||||
if(!check_rights(R_DEBUG) || !isclient(src))
|
||||
return
|
||||
|
||||
if(byond_version < 516)
|
||||
to_chat(src, SPAN_WARNING("You can only use this on 516!"))
|
||||
return
|
||||
|
||||
to_chat(src, SPAN_INFO("You can now right click to use inspect on browsers."))
|
||||
winset(src, null, list("browser-options" = "+devtools"))
|
||||
winset(src, null, list("browser-options" = "+find"))
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
Tile Update: [SSair.tiles_to_update.len]<BR>
|
||||
"}
|
||||
|
||||
usr << browse(output,"window=airreport")
|
||||
usr << browse(HTML_SKELETON(output), "window=airreport")
|
||||
|
||||
/client/proc/fix_next_move()
|
||||
set category = "Debug"
|
||||
@@ -95,7 +95,7 @@
|
||||
else
|
||||
output += " [device]<br>"
|
||||
|
||||
usr << browse(output,"window=radioreport")
|
||||
usr << browse(HTML_SKELETON(output), "window=radioreport")
|
||||
feedback_add_details("admin_verb","RR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/reload_admins()
|
||||
|
||||
@@ -101,7 +101,7 @@ GLOBAL_VAR_INIT(intercom_range_display_status, 0)
|
||||
if(!window_check)
|
||||
output += "<li><font color='red'>Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]</color></li>"
|
||||
output += "</ul>"
|
||||
usr << browse(output,"window=airreport;size=1000x500")
|
||||
usr << browse(HTML_SKELETON(output), "window=airreport;size=1000x500")
|
||||
feedback_add_details("admin_verb","mCRP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/intercom_view()
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
to_chat(src, "Some accounts did not have proper ages set in their clients. This function requires database to be present")
|
||||
|
||||
if(msg != "")
|
||||
src << browse(msg, "window=Player_age_check")
|
||||
src << browse(HTML_SKELETON(msg), "window=Player_age_check")
|
||||
else
|
||||
to_chat(src, "No matches for that age range found.")
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
dat += "<A href='byond://?src=[REF(src)];fireArea=1'>Open Fire - Area</A><BR>"
|
||||
dat += "<A href='byond://?src=[REF(src)];fireCords=1'>Open Fire - Coordinates</A><BR>"
|
||||
dat += "Deployment of weapon authorized by <br>[SSatlas.current_map.company_name] Chief Naval Director<br><br>Remember, friendly fire is grounds for termination of your contract and life.<HR>"
|
||||
user << browse(dat, "window=scroll")
|
||||
user << browse(HTML_SKELETON(dat), "window=scroll")
|
||||
onclose(user, "scroll")
|
||||
return
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
|
||||
browse_data = "[formatted_data]<br><a href='byond://?src=[REF(src)];selection=[cardname]'>Draw Card</a><br><hr>[browse_data]"
|
||||
|
||||
user << browse(browse_data, "window=battlemonsters_hand")
|
||||
user << browse(HTML_SKELETON(browse_data), "window=battlemonsters_hand")
|
||||
|
||||
/obj/item/battle_monsters/deck/Topic(href,href_list)
|
||||
if(..())
|
||||
|
||||
@@ -756,7 +756,7 @@ GLOBAL_LIST_INIT(localhost_addresses, list(
|
||||
dat += "The request is [request["request_age"]] days old.<br>"
|
||||
dat += "OPTIONS: <a href='byond://?src=[REF(src)];linkingrequest=[request["id"]];linkingaction=accept'>Accept Request</a> | <a href='byond://?src=[REF(src)];linkingrequest=[request["id"]];linkingaction=deny'>Deny Request</a>"
|
||||
|
||||
src << browse(dat, "window=LinkingRequests")
|
||||
src << browse(HTML_SKELETON(dat), "window=LinkingRequests")
|
||||
return
|
||||
|
||||
/client/proc/gather_linking_requests()
|
||||
|
||||
@@ -998,7 +998,7 @@ GLOBAL_LIST_INIT(valid_bloodtypes, list(
|
||||
dat += "</center>"
|
||||
|
||||
|
||||
user << browse(dat.Join(), "window=species;size=700x400")
|
||||
user << browse(HTML_SKELETON(dat.Join()), "window=species;size=700x400")
|
||||
|
||||
/// This proc verifies if a sprite accessory can be put on a robolimb, checking its manufacturer.
|
||||
/datum/category_item/player_setup_item/general/body/proc/verify_robolimb_appropriate(datum/sprite_accessory/S)
|
||||
|
||||
@@ -244,7 +244,7 @@
|
||||
HTML += "<br>"
|
||||
HTML += "<hr />"
|
||||
HTML += "<tt>"
|
||||
user << browse(HTML, "window=flavor_text;size=430x300")
|
||||
user << browse(HTML_SKELETON(HTML), "window=flavor_text;size=430x300")
|
||||
return
|
||||
|
||||
/datum/category_item/player_setup_item/general/flavor/proc/SetFlavourTextRobot(mob/user)
|
||||
@@ -261,5 +261,5 @@
|
||||
HTML += "<br>"
|
||||
HTML += "<hr />"
|
||||
HTML += "<tt>"
|
||||
user << browse(HTML, "window=flavour_text_robot;size=430x300")
|
||||
user << browse(HTML_SKELETON(HTML), "window=flavour_text_robot;size=430x300")
|
||||
return
|
||||
|
||||
@@ -495,7 +495,7 @@
|
||||
dat += "<br><span class='warning'>[faction.get_selection_error(pref, user)]</span>"
|
||||
dat += "</center>"
|
||||
|
||||
user << browse(dat.Join(), "window=factionpreview;size=750x450")
|
||||
user << browse(HTML_SKELETON(dat.Join()), "window=factionpreview;size=750x450")
|
||||
|
||||
/datum/category_item/player_setup_item/occupation/proc/validate_and_set_faction(selected_faction)
|
||||
var/datum/faction/faction = SSjobs.name_factions[selected_faction]
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
dat += "<a href='byond://?src=[REF(src)];choice=change_code'>Change access code</a><br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];choice=change_id'>Change EFTPOS ID</a><br>"
|
||||
dat += "Scan card to reset access code <a href='byond://?src=[REF(src)];choice=reset'>\[------\]</a>"
|
||||
user << browse(dat,"window=eftpos")
|
||||
user << browse(HTML_SKELETON(dat),"window=eftpos")
|
||||
else
|
||||
user << browse(null,"window=eftpos")
|
||||
|
||||
|
||||
@@ -75,13 +75,13 @@ GLOBAL_LIST_EMPTY_TYPED(holodeck_controls, /obj/machinery/computer/holodeck_cont
|
||||
|
||||
if(!linkedholodeck)
|
||||
dat += SPAN_DANGER("Warning: Unable to locate holodeck.<br>")
|
||||
user << browse(dat, "window=computer;size=400x500")
|
||||
user << browse(HTML_SKELETON(dat), "window=computer;size=400x500")
|
||||
onclose(user, "computer")
|
||||
return
|
||||
|
||||
if(!SSatlas.current_map.holodeck_supported_programs.len)
|
||||
dat += SPAN_DANGER("Warning: No supported holo-programs loaded.<br>")
|
||||
user << browse(dat, "window=computer;size=400x500")
|
||||
user << browse(HTML_SKELETON(dat), "window=computer;size=400x500")
|
||||
onclose(user, "computer")
|
||||
return
|
||||
|
||||
@@ -126,7 +126,7 @@ GLOBAL_LIST_EMPTY_TYPED(holodeck_controls, /obj/machinery/computer/holodeck_cont
|
||||
dat = "<B>Holodeck Control System</B><BR>"
|
||||
dat += "Holodeck is <A href='byond://?src=[REF(src)];togglehololock=1'><font color=red>(LOCKED)</font></A><BR>"
|
||||
|
||||
user << browse(dat, "window=computer;size=400x500")
|
||||
user << browse(HTML_SKELETON(dat), "window=computer;size=400x500")
|
||||
onclose(user, "computer")
|
||||
return
|
||||
|
||||
|
||||
@@ -226,6 +226,6 @@
|
||||
if(dat)
|
||||
last_data = dat
|
||||
dat += "<br><br>\[<a href='byond://?src=[REF(src)];print=1'>print report</a>\]"
|
||||
user << browse(dat,"window=plant_analyzer")
|
||||
user << browse(HTML_SKELETON(dat),"window=plant_analyzer")
|
||||
|
||||
return
|
||||
|
||||
@@ -218,7 +218,7 @@
|
||||
to_chat(user, SPAN_NOTICE("The pages of [title] have been cut out!"))
|
||||
return
|
||||
if(src.dat)
|
||||
user << browse("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]", "window=book")
|
||||
user << browse(HTML_SKELETON("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]"), "window=book")
|
||||
user.visible_message("[user] opens a book titled \"[src.title]\" and begins reading intently.")
|
||||
playsound(loc, 'sound/bureaucracy/bookopen.ogg', 50, TRUE)
|
||||
onclose(user, "book")
|
||||
@@ -314,7 +314,7 @@
|
||||
if(target_zone == BP_EYES)
|
||||
user.visible_message(SPAN_NOTICE("You open up the book and show it to [target_mob]. "), \
|
||||
SPAN_NOTICE(" [user] opens up a book and shows it to [target_mob]. "))
|
||||
target_mob << browse("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]", "window=book")
|
||||
target_mob << browse(HTML_SKELETON("<TT><I>Penned by [author].</I></TT> <BR>" + "[dat]"), "window=book")
|
||||
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //to prevent spam
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
/obj/machinery/librarypubliccomp/attack_hand(var/mob/user)
|
||||
usr.set_machine(src)
|
||||
var/dat = "<HEAD><TITLE>Library Visitor</TITLE></HEAD><BODY>\n" // <META HTTP-EQUIV='Refresh' CONTENT='10'>
|
||||
var/dat = ""
|
||||
switch(screenstate)
|
||||
if(0)
|
||||
dat += {"<h2>Search Settings</h2><br>
|
||||
@@ -63,7 +63,7 @@
|
||||
dat += "<tr><td>[author]</td><td>[title]</td><td>[category]</td><td>[id]</td></tr>"
|
||||
dat += "</table><br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];back=1'>\[Go Back\]</a><br>"
|
||||
user << browse(dat, "window=publiclibrary")
|
||||
user << browse(HTML_SKELETON_TITLE("Library Visitor", dat), "window=publiclibrary")
|
||||
onclose(user, "publiclibrary")
|
||||
|
||||
/obj/machinery/librarypubliccomp/Topic(href, href_list)
|
||||
@@ -133,12 +133,7 @@
|
||||
|
||||
/obj/machinery/librarycomp/attack_hand(var/mob/user)
|
||||
user.set_machine(src)
|
||||
var/dat
|
||||
// Public Related Code
|
||||
if(!is_public)
|
||||
dat = "<head><title>[SSatlas.current_map.station_name] Library Management</title></head><body>\n"
|
||||
else
|
||||
dat = "<head><title>[SSatlas.current_map.station_name] Library</title></head><body>\n"
|
||||
var/dat = ""
|
||||
switch(screenstate)
|
||||
if(0)
|
||||
// Main Menu
|
||||
@@ -238,7 +233,7 @@
|
||||
<a href='byond://?src=[REF(src)];switchscreen=0'>No.</a><br>"}
|
||||
|
||||
//dat += "<a href='byond://?src=[REF(user)];mach_close=library'>Close</a><br><br>"
|
||||
user << browse(dat, "window=library")
|
||||
user << browse(HTML_SKELETON_TITLE(is_public ? "[SSatlas.current_map.station_name] Library" : "[SSatlas.current_map.station_name] Library Management", dat), "window=library")
|
||||
onclose(user, "library")
|
||||
|
||||
/obj/machinery/librarycomp/emag_act(var/remaining_charges, var/mob/user)
|
||||
@@ -432,7 +427,7 @@
|
||||
|
||||
/obj/machinery/libraryscanner/attack_hand(var/mob/user)
|
||||
usr.set_machine(src)
|
||||
var/dat = "<HEAD><TITLE>Scanner Control Interface</TITLE></HEAD><BODY>\n" // <META HTTP-EQUIV='Refresh' CONTENT='10'>
|
||||
var/dat = ""
|
||||
if(cache)
|
||||
dat += "<FONT color=#005500>Data stored in memory.</FONT><br>"
|
||||
else
|
||||
@@ -442,7 +437,7 @@
|
||||
dat += " <a href='byond://?src=[REF(src)];clear=1'>\[Clear Memory\]</a><br><br><a href='byond://?src=[REF(src)];eject=1'>\[Remove Book\]</a>"
|
||||
else
|
||||
dat += "<br>"
|
||||
user << browse(dat, "window=scanner")
|
||||
user << browse(HTML_SKELETON_TITLE("Scanner Control Interface", dat), "window=scanner")
|
||||
onclose(user, "scanner")
|
||||
|
||||
/obj/machinery/libraryscanner/Topic(href, href_list)
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
|
||||
dat += "<br><br>In total this machine produced <font color='green'><b>[newCoins]</b></font> coins."
|
||||
dat += "<br><A href='byond://?src=[REF(src)];makeCoins=[1]'>Make coins</A>"
|
||||
user << browse("[dat]", "window=mint")
|
||||
user << browse(HTML_SKELETON(dat), "window=mint")
|
||||
|
||||
/obj/machinery/mineral/mint/Topic(href, href_list)
|
||||
if(..())
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<A href='byond://?src=[REF(src)]];create_mob=1'>Create Mob</A><br>
|
||||
"}
|
||||
|
||||
usr << browse(dat, "window=storytellerpanel;size=210x280")
|
||||
usr << browse(HTML_SKELETON(dat), "window=storytellerpanel;size=210x280")
|
||||
|
||||
/mob/abstract/ghost/storyteller/Topic(href, href_list)
|
||||
. = ..()
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
output += "</table>"
|
||||
|
||||
src << browse(output,"window=playerpolllist;size=500x300")
|
||||
src << browse(HTML_SKELETON(output),"window=playerpolllist;size=500x300")
|
||||
|
||||
/mob/abstract/new_player/proc/show_poll_link(var/pollid = -1)
|
||||
if(pollid == -1) return
|
||||
@@ -129,7 +129,7 @@
|
||||
output += "</div>"
|
||||
|
||||
|
||||
src << browse(output,"window=playerpoll;size=500x250")
|
||||
src << browse(HTML_SKELETON(output),"window=playerpoll;size=500x250")
|
||||
|
||||
//Polls with a text input
|
||||
if("TEXT")
|
||||
@@ -175,7 +175,7 @@
|
||||
output += "[vote_text]"
|
||||
|
||||
|
||||
src << browse(output,"window=playerpoll;size=500x500")
|
||||
src << browse(HTML_SKELETON(output),"window=playerpoll;size=500x500")
|
||||
|
||||
//Polls with a text input
|
||||
if("NUMVAL")
|
||||
@@ -250,7 +250,7 @@
|
||||
output += "</form>"
|
||||
|
||||
|
||||
src << browse(output,"window=playerpoll;size=500x500")
|
||||
src << browse(HTML_SKELETON(output),"window=playerpoll;size=500x500")
|
||||
|
||||
if("MULTICHOICE")
|
||||
var/DBQuery/voted_query = GLOB.dbcon.NewQuery("SELECT optionid FROM ss13_poll_vote WHERE pollid = [pollid] AND ckey = '[usr.ckey]'")
|
||||
@@ -317,7 +317,7 @@
|
||||
output += "</div>"
|
||||
|
||||
|
||||
src << browse(output,"window=playerpoll;size=500x250")
|
||||
src << browse(HTML_SKELETON(output),"window=playerpoll;size=500x250")
|
||||
return
|
||||
|
||||
/mob/abstract/new_player/proc/vote_on_poll(var/pollid = -1, var/optionid = -1, var/multichoice = 0)
|
||||
|
||||
@@ -390,7 +390,7 @@
|
||||
if(L.written_style)
|
||||
dat += "You can write in this language on papers by writing \[lang=[L.key]\]YourTextHere\[/lang\].<br/><br/>"
|
||||
|
||||
src << browse(dat, "window=checklanguage")
|
||||
src << browse(HTML_SKELETON(dat), "window=checklanguage")
|
||||
return
|
||||
|
||||
/mob/living/check_languages()
|
||||
@@ -410,7 +410,7 @@
|
||||
if(L.written_style)
|
||||
dat += "You can write in this language on papers by writing \[lang=[L.key]\]YourTextHere\[/lang\].<br/><br/>"
|
||||
|
||||
src << browse(dat, "window=checklanguage")
|
||||
src << browse(HTML_SKELETON(dat), "window=checklanguage")
|
||||
|
||||
/mob/living/Topic(href, href_list)
|
||||
if(href_list["default_lang"])
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
set desc = "Sets an extended description of your character's features."
|
||||
set category = "IC"
|
||||
|
||||
var/HTML = "<body>"
|
||||
var/HTML = ""
|
||||
HTML += "<tt><center>"
|
||||
HTML += "<b>Update Flavour Text</b> <hr />"
|
||||
HTML += "<br></center>"
|
||||
@@ -44,4 +44,4 @@
|
||||
HTML += "<hr />"
|
||||
HTML +="<a href='byond://?src=[REF(src)];flavor_change=done'>\[Done\]</a>"
|
||||
HTML += "<tt>"
|
||||
src << browse(HTML, "window=flavor_changes;size=430x300")
|
||||
src << browse(HTML_SKELETON(HTML), "window=flavor_changes;size=430x300")
|
||||
|
||||
@@ -383,7 +383,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
Recall the channel list at any time by calling <code>Radio-Settings</code> under <em>AI Commands</em>.\
|
||||
</p>\
|
||||
"
|
||||
usr << browse(dat, "window=aihelp,size=520x700")
|
||||
usr << browse(HTML_SKELETON(dat), "window=aihelp,size=520x700")
|
||||
|
||||
/mob/living/silicon/ai/proc/pick_icon()
|
||||
set category = "AI Commands"
|
||||
|
||||
@@ -180,8 +180,7 @@
|
||||
if(!module)
|
||||
module = new /obj/item/robot_module/drone(src)
|
||||
|
||||
var/dat = "<HEAD><TITLE>Drone modules</TITLE></HEAD><BODY>\n"
|
||||
dat += {"
|
||||
var/dat = {"
|
||||
<B>Activated Modules</B>
|
||||
<BR>
|
||||
Module 1: [module_state_1 ? "<A href='byond://?src=[REF(src)];mod=[REF(module_state_1)]>[module_state_1]<A>" : "No Module"]<BR>
|
||||
@@ -221,4 +220,4 @@
|
||||
|
||||
dat += resources
|
||||
|
||||
src << browse(dat, "window=robotmod")
|
||||
src << browse(HTML_SKELETON_TITLE("Drone modules", dat), "window=robotmod")
|
||||
|
||||
@@ -485,7 +485,7 @@
|
||||
to_chat(src, SPAN_WARNING("WARNING: Power too low for self-diagnostic functions."))
|
||||
return
|
||||
var/dat = self_diagnosis()
|
||||
src << browse(dat, "window=robotdiagnosis")
|
||||
src << browse(HTML_SKELETON(dat), "window=robotdiagnosis")
|
||||
|
||||
/mob/living/silicon/robot/verb/toggle_component()
|
||||
set category = "Robot Commands"
|
||||
@@ -896,7 +896,7 @@
|
||||
dat += "[module.malf_AI_module]: <B>Activated</B><BR>"
|
||||
else
|
||||
dat += "[module.malf_AI_module]: <A href='byond://?src=[REF(src)];act=[REF(module.malf_AI_module)]>Activate</A><BR>"
|
||||
src << browse(dat, "window=robotmod")
|
||||
src << browse(HTML_SKELETON(dat), "window=robotmod")
|
||||
|
||||
|
||||
/mob/living/silicon/robot/Topic(href, href_list)
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
var/synth = (L in speech_synthesizer_langs)
|
||||
dat += "<b>[L.name] ([get_language_prefix()][L.key])</b>[synth ? default_str : null]<br/>Speech Synthesizer: <i>[synth ? "YES" : "NOT SUPPORTED"]</i><br/>[L.desc]<br/><br/>"
|
||||
|
||||
src << browse(dat, "window=checklanguage")
|
||||
src << browse(HTML_SKELETON(dat), "window=checklanguage")
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/toggle_sensor_mode()
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
else
|
||||
dat += "<br><b>Headset:</b> <a href='byond://?src=[REF(src)];add_inv=ears'>Nothing</a>"
|
||||
|
||||
user << browse(dat, "window=mob[name];size=325x500")
|
||||
user << browse(HTML_SKELETON(dat), "window=mob[name];size=325x500")
|
||||
onclose(user, "mob[real_name]")
|
||||
return
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
return
|
||||
|
||||
/obj/item/clipboard/attack_self(mob/user as mob)
|
||||
var/dat = "<title>Clipboard</title>"
|
||||
var/dat = ""
|
||||
if(haspen)
|
||||
dat += "<A href='byond://?src=[REF(src)];pen=1'>Remove Pen</A><BR><HR>"
|
||||
else
|
||||
@@ -93,7 +93,7 @@
|
||||
for(var/obj/item/photo/Ph in r_contents)
|
||||
dat += "<A href='byond://?src=[REF(src)];remove=[REF(Ph)]'>Remove</A> <A href='byond://?src=[REF(src)];rename=[REF(Ph)]'>Rename</A> - <A href='byond://?src=[REF(src)];look=[REF(Ph)]'>[Ph.name]</A><BR>"
|
||||
|
||||
user << browse(dat, "window=clipboard")
|
||||
user << browse(HTML_SKELETON_TITLE("Clipboard", dat), "window=clipboard")
|
||||
if(!ui_open)
|
||||
ui_open = TRUE
|
||||
onclose(user, "clipboard")
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
return
|
||||
|
||||
/obj/item/folder/attack_self(mob/user as mob)
|
||||
var/dat = "<title>[name]</title>"
|
||||
var/dat = ""
|
||||
|
||||
for(var/obj/item/paper/P in src)
|
||||
dat += "[can_write ? "<A href='byond://?src=[REF(src)];write=[REF(P)]'>Write</A> " : ""]<A href='byond://?src=[REF(src)];remove=[REF(P)]'>Remove</A> <A href='byond://?src=[REF(src)];rename=[REF(P)]'>Rename</A> - <A href='byond://?src=[REF(src)];read=[REF(P)]'>[P.name]</A><BR>"
|
||||
@@ -61,7 +61,7 @@
|
||||
dat += "<A href='byond://?src=[REF(src)];remove=[REF(Pb)]'>Remove</A> <A href='byond://?src=[REF(src)];rename=[REF(Pb)]'>Rename</A> - <A href='byond://?src=[REF(src)];browse=[REF(Pb)]'>[Pb.name]</A><BR>"
|
||||
for(var/obj/item/sample/Pf in src)
|
||||
dat += "<A href='byond://?src=[REF(src)];remove=[REF(Pf)]'>Remove</A> - [Pf.name]<BR>"
|
||||
user << browse(dat, "window=folder")
|
||||
user << browse(HTML_SKELETON_TITLE(name, dat), "window=folder")
|
||||
onclose(user, "folder")
|
||||
add_fingerprint(usr)
|
||||
return
|
||||
|
||||
@@ -159,7 +159,7 @@
|
||||
dat += SPAN_WARNING("The dart cartridge is empty!")
|
||||
dat += " \[<A href='byond://?src=[REF(src)];eject_cart=1'>Eject</A>\]"
|
||||
|
||||
user << browse(dat, "window=dartgun")
|
||||
user << browse(HTML_SKELETON(dat), "window=dartgun")
|
||||
onclose(user, "dartgun", src)
|
||||
|
||||
/obj/item/gun/projectile/dartgun/proc/check_beaker_mixing(var/obj/item/B)
|
||||
|
||||
@@ -24,4 +24,4 @@ GLOBAL_LIST_EMPTY(chemical_reaction_logs)
|
||||
for(var/entry in GLOB.chemical_reaction_logs)
|
||||
html += "[entry]<br>"
|
||||
|
||||
usr << browse(html, "window=chemlogs")
|
||||
usr << browse(HTML_SKELETON(html), "window=chemlogs")
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
|
||||
dat += "</tr></table><br>Current Selection: [currTag ? currTag : "None"]</tt>"
|
||||
dat += "<br><a href='byond://?src=[REF(src)];nextTag=CUSTOM'>Enter custom location.</a>"
|
||||
user << browse(dat, "window=destTagScreen;size=450x375")
|
||||
user << browse(HTML_SKELETON(dat), "window=destTagScreen;size=450x375")
|
||||
onclose(user, "destTagScreen")
|
||||
|
||||
/obj/item/device/destTagger/attack_self(mob/user)
|
||||
|
||||
@@ -348,7 +348,7 @@
|
||||
for(var/obj/machinery/r_n_d/server/S in servers)
|
||||
dat += "[S.name] <A href='byond://?src=[REF(src)];send_to=[S.server_id]'> (Transfer)</A><BR>"
|
||||
dat += "<HR><A href='byond://?src=[REF(src)];main=1'>Main Menu</A>"
|
||||
user << browse("<TITLE>R&D Server Control</TITLE><HR>[dat]", "window=server_control;size=575x400")
|
||||
user << browse(HTML_SKELETON_TITLE("R&D Server Control", dat), "window=server_control;size=575x400")
|
||||
onclose(user, "server_control")
|
||||
return
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
for(var/index=1, index<=construction.len, index++)
|
||||
dat += "<A href='byond://?src=[REF(src)];activate=[index]'>\[[construction[index]]\]</a><br>"
|
||||
|
||||
user << browse(dat, "window=alien_replicator")
|
||||
user << browse(HTML_SKELETON(dat), "window=alien_replicator")
|
||||
|
||||
/obj/machinery/replicator/attackby(obj/item/attacking_item, mob/user)
|
||||
user.drop_from_inventory(attacking_item, src)
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"}
|
||||
|
||||
dat += "<A href='byond://?src=[REF(src)];close=1'>Close</a><br>"
|
||||
user << browse(dat,"window=locater;size=300x150")
|
||||
user << browse(HTML_SKELETON(dat),"window=locater;size=300x150")
|
||||
onclose(user, "locater")
|
||||
|
||||
/obj/item/device/beacon_locator/Topic(href, href_list)
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
dat += "<p><A href='byond://?src=[REF(src)];reset=1'>Back</A>"
|
||||
|
||||
|
||||
user << browse(dat, "window=keycard_auth;size=500x350")
|
||||
user << browse(HTML_SKELETON(dat), "window=keycard_auth;size=500x350")
|
||||
return
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
author: harry
|
||||
|
||||
delete-after: True
|
||||
|
||||
changes:
|
||||
- rscadd: "compatibility in 516 for browser elements, like the chat and tgui popups"
|
||||
@@ -1,3 +1,12 @@
|
||||
.light:root {
|
||||
--scrollbar-base: #f2f2f2;
|
||||
--scrollbar-thumb: #a7a7a7;
|
||||
}
|
||||
|
||||
html, body {
|
||||
scrollbar-color: var(--scrollbar-base) var(--scrollbar-thumb);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||
font-size: 12px !important;
|
||||
@@ -19,6 +28,11 @@ body.dark {
|
||||
scrollbar-shadow-color: #3b3b3b;
|
||||
}
|
||||
|
||||
.dark:root {
|
||||
--scrollbar-base: #181818;
|
||||
--scrollbar-thumb: #363636;
|
||||
}
|
||||
|
||||
#menu {
|
||||
background-color: #F0F0F0;
|
||||
position: fixed;
|
||||
|
||||
@@ -707,9 +707,11 @@ function draw_verbs(cat) {
|
||||
function set_theme(which) {
|
||||
if (which == "light") {
|
||||
document.body.className = "";
|
||||
document.documentElement.className = "light";
|
||||
set_style_sheet("browserOutput_white");
|
||||
} else if (which == "dark") {
|
||||
document.body.className = "dark";
|
||||
document.documentElement.className = "dark";
|
||||
set_style_sheet("browserOutput");
|
||||
}
|
||||
}
|
||||
|
||||
+150
-41
@@ -7,8 +7,9 @@
|
||||
*/
|
||||
|
||||
export const IMPL_MEMORY = 0;
|
||||
export const IMPL_LOCAL_STORAGE = 1;
|
||||
export const IMPL_HUB_STORAGE = 1;
|
||||
export const IMPL_INDEXED_DB = 2;
|
||||
export const IMPL_IFRAME_INDEXED_DB = 3;
|
||||
|
||||
const INDEXED_DB_VERSION = 1;
|
||||
const INDEXED_DB_NAME = 'tgui';
|
||||
@@ -25,14 +26,11 @@ const testGeneric = (testFn) => () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Localstorage can sometimes throw an error, even if DOM storage is not
|
||||
// disabled in IE11 settings.
|
||||
// See: https://superuser.com/questions/1080011
|
||||
// prettier-ignore
|
||||
const testLocalStorage = testGeneric(() => (
|
||||
window.localStorage && window.localStorage.getItem
|
||||
));
|
||||
const testHubStorage = testGeneric(
|
||||
() => window.hubStorage && window.hubStorage.getItem
|
||||
);
|
||||
|
||||
// TODO: Remove with 516
|
||||
// prettier-ignore
|
||||
const testIndexedDb = testGeneric(() => (
|
||||
(window.indexedDB || window.msIndexedDB)
|
||||
@@ -45,49 +43,144 @@ class MemoryBackend {
|
||||
this.store = {};
|
||||
}
|
||||
|
||||
get(key) {
|
||||
async get(key) {
|
||||
return this.store[key];
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
async set(key, value) {
|
||||
this.store[key] = value;
|
||||
}
|
||||
|
||||
remove(key) {
|
||||
async remove(key) {
|
||||
this.store[key] = undefined;
|
||||
}
|
||||
|
||||
clear() {
|
||||
async clear() {
|
||||
this.store = {};
|
||||
}
|
||||
}
|
||||
|
||||
class LocalStorageBackend {
|
||||
class HubStorageBackend {
|
||||
constructor() {
|
||||
this.impl = IMPL_LOCAL_STORAGE;
|
||||
this.impl = IMPL_HUB_STORAGE;
|
||||
}
|
||||
|
||||
get(key) {
|
||||
const value = localStorage.getItem(key);
|
||||
async get(key) {
|
||||
const value = await window.hubStorage.getItem('aurora-' + key);
|
||||
if (typeof value === 'string') {
|
||||
return JSON.parse(value);
|
||||
}
|
||||
}
|
||||
|
||||
set(key, value) {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
async set(key, value) {
|
||||
window.hubStorage.setItem('aurora-' + key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
remove(key) {
|
||||
localStorage.removeItem(key);
|
||||
async remove(key) {
|
||||
window.hubStorage.removeItem('aurora-' + key);
|
||||
}
|
||||
|
||||
clear() {
|
||||
localStorage.clear();
|
||||
async clear() {
|
||||
window.hubStorage.clear();
|
||||
}
|
||||
}
|
||||
|
||||
class IFrameIndexedDbBackend {
|
||||
constructor() {
|
||||
this.impl = IMPL_IFRAME_INDEXED_DB;
|
||||
}
|
||||
|
||||
async ready() {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.style.display = 'none';
|
||||
iframe.src = Byond.storageCdn;
|
||||
|
||||
const completePromise = new Promise((resolve) => {
|
||||
iframe.onload = () => resolve(this);
|
||||
});
|
||||
|
||||
this.documentElement = document.body.appendChild(iframe);
|
||||
this.iframeWindow = this.documentElement.contentWindow;
|
||||
|
||||
return completePromise;
|
||||
}
|
||||
|
||||
async get(key) {
|
||||
const promise = new Promise((resolve) => {
|
||||
window.addEventListener('message', (message) => {
|
||||
if (message.data.key && message.data.key === key) {
|
||||
resolve(message.data.value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.iframeWindow.postMessage({ type: 'get', key: key }, '*');
|
||||
return promise;
|
||||
}
|
||||
|
||||
async set(key, value) {
|
||||
this.iframeWindow.postMessage({ type: 'set', key: key, value: value }, '*');
|
||||
}
|
||||
|
||||
async remove(key) {
|
||||
this.iframeWindow.postMessage({ type: 'remove', key: key }, '*');
|
||||
}
|
||||
|
||||
async clear() {
|
||||
this.iframeWindow.postMessage({ type: 'clear' }, '*');
|
||||
}
|
||||
|
||||
async ping() {
|
||||
const promise = new Promise((resolve) => {
|
||||
window.addEventListener('message', (message) => {
|
||||
if (message.data === true) {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => resolve(false), 100);
|
||||
});
|
||||
|
||||
this.iframeWindow.postMessage({ type: 'ping' }, '*');
|
||||
return promise;
|
||||
}
|
||||
|
||||
async processChatMessages(messages) {
|
||||
this.iframeWindow.postMessage(
|
||||
{ type: 'processChatMessages', messages: messages },
|
||||
'*'
|
||||
);
|
||||
}
|
||||
|
||||
async getChatMessages() {
|
||||
const promise = new Promise((resolve) => {
|
||||
window.addEventListener('message', (message) => {
|
||||
if (message.data.messages) {
|
||||
resolve(message.data.messages);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.iframeWindow.postMessage({ type: 'getChatMessages' }, '*');
|
||||
return promise;
|
||||
}
|
||||
|
||||
async setNumberStored(number) {
|
||||
this.iframeWindow.postMessage(
|
||||
{ type: 'setNumberStored', newMax: number },
|
||||
'*'
|
||||
);
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
document.body.removeChild(this.documentElement);
|
||||
this.documentElement = null;
|
||||
this.iframeWindow = null;
|
||||
}
|
||||
}
|
||||
|
||||
class IndexedDbBackend {
|
||||
// TODO: Remove with 516
|
||||
constructor() {
|
||||
this.impl = IMPL_INDEXED_DB;
|
||||
/** @type {Promise<IDBDatabase>} */
|
||||
@@ -108,7 +201,7 @@ class IndexedDbBackend {
|
||||
});
|
||||
}
|
||||
|
||||
getStore(mode) {
|
||||
async getStore(mode) {
|
||||
// prettier-ignore
|
||||
return this.dbPromise.then((db) => db
|
||||
.transaction(INDEXED_DB_STORE_NAME, mode)
|
||||
@@ -125,13 +218,6 @@ class IndexedDbBackend {
|
||||
}
|
||||
|
||||
async set(key, value) {
|
||||
// The reason we don't _save_ null is because IE 10 does
|
||||
// not support saving the `null` type in IndexedDB. How
|
||||
// ironic, given the bug below!
|
||||
// See: https://github.com/mozilla/localForage/issues/161
|
||||
if (value === null) {
|
||||
value = undefined;
|
||||
}
|
||||
// NOTE: We deliberately make this operation transactionless
|
||||
const store = await this.getStore(READ_WRITE);
|
||||
store.put(value, key);
|
||||
@@ -150,17 +236,40 @@ class IndexedDbBackend {
|
||||
}
|
||||
}
|
||||
|
||||
// Namespace for keys in storage, so we do not share storage with other servers.
|
||||
let namespace = 'AURORASTATION_';
|
||||
|
||||
/**
|
||||
* Web Storage Proxy object, which selects the best backend available
|
||||
* depending on the environment.
|
||||
* Also applies namespacing to keys.
|
||||
*/
|
||||
class StorageProxy {
|
||||
export class StorageProxy {
|
||||
constructor() {
|
||||
this.backendPromise = (async () => {
|
||||
if (!Byond.TRIDENT) {
|
||||
if (Byond.storageCdn) {
|
||||
const iframe = new IFrameIndexedDbBackend();
|
||||
await iframe.ready();
|
||||
|
||||
if ((await iframe.ping()) === true) {
|
||||
return iframe;
|
||||
}
|
||||
|
||||
iframe.destroy();
|
||||
}
|
||||
|
||||
if (!testHubStorage()) {
|
||||
Byond.winset(null, 'browser-options', '+byondstorage');
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const listener = () => {
|
||||
document.removeEventListener('byondstorageupdated', listener);
|
||||
resolve(new HubStorageBackend());
|
||||
};
|
||||
|
||||
document.addEventListener('byondstorageupdated', listener);
|
||||
});
|
||||
}
|
||||
return new HubStorageBackend();
|
||||
}
|
||||
// TODO: Remove with 516
|
||||
if (testIndexedDb()) {
|
||||
try {
|
||||
const backend = new IndexedDbBackend();
|
||||
@@ -168,26 +277,26 @@ class StorageProxy {
|
||||
return backend;
|
||||
} catch {}
|
||||
}
|
||||
if (testLocalStorage()) {
|
||||
return new LocalStorageBackend();
|
||||
}
|
||||
console.warn(
|
||||
'No supported storage backend found. Using in-memory storage.'
|
||||
);
|
||||
return new MemoryBackend();
|
||||
})();
|
||||
}
|
||||
|
||||
async get(key) {
|
||||
const backend = await this.backendPromise;
|
||||
return backend.get(namespace + key);
|
||||
return backend.get(key);
|
||||
}
|
||||
|
||||
async set(key, value) {
|
||||
const backend = await this.backendPromise;
|
||||
return backend.set(namespace + key, value);
|
||||
return backend.set(key, value);
|
||||
}
|
||||
|
||||
async remove(key) {
|
||||
const backend = await this.backendPromise;
|
||||
return backend.remove(namespace + key);
|
||||
return backend.remove(key);
|
||||
}
|
||||
|
||||
async clear() {
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { createLogger } from 'tgui/logging';
|
||||
|
||||
const logger = createLogger('AudioPlayer');
|
||||
|
||||
export class AudioPlayer {
|
||||
constructor() {
|
||||
// Doesn't support HTMLAudioElement
|
||||
if (Byond.IS_LTE_IE9) {
|
||||
return;
|
||||
}
|
||||
// Set up the HTMLAudioElement node
|
||||
this.node = document.createElement('audio');
|
||||
this.node.style.setProperty('display', 'none');
|
||||
document.body.appendChild(this.node);
|
||||
// Set up other properties
|
||||
this.playing = false;
|
||||
this.volume = 1;
|
||||
this.options = {};
|
||||
this.onPlaySubscribers = [];
|
||||
this.onStopSubscribers = [];
|
||||
// Listen for playback start events
|
||||
this.node.addEventListener('canplaythrough', () => {
|
||||
logger.log('canplaythrough');
|
||||
this.playing = true;
|
||||
this.node.playbackRate = this.options.pitch || 1;
|
||||
this.node.currentTime = this.options.start || 0;
|
||||
this.node.volume = this.volume;
|
||||
this.node.play();
|
||||
for (let subscriber of this.onPlaySubscribers) {
|
||||
subscriber();
|
||||
}
|
||||
});
|
||||
// Listen for playback stop events
|
||||
this.node.addEventListener('ended', () => {
|
||||
logger.log('ended');
|
||||
this.stop();
|
||||
});
|
||||
// Listen for playback errors
|
||||
this.node.addEventListener('error', (e) => {
|
||||
if (this.playing) {
|
||||
logger.log('playback error', e.error);
|
||||
this.stop();
|
||||
}
|
||||
});
|
||||
// Check every second to stop the playback at the right time
|
||||
this.playbackInterval = setInterval(() => {
|
||||
if (!this.playing) {
|
||||
return;
|
||||
}
|
||||
const shouldStop =
|
||||
this.options.end > 0 && this.node.currentTime >= this.options.end;
|
||||
if (shouldStop) {
|
||||
this.stop();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (!this.node) {
|
||||
return;
|
||||
}
|
||||
this.node.stop();
|
||||
document.removeChild(this.node);
|
||||
clearInterval(this.playbackInterval);
|
||||
}
|
||||
|
||||
play(url, options = {}) {
|
||||
if (!this.node) {
|
||||
return;
|
||||
}
|
||||
logger.log('playing', url, options);
|
||||
this.options = options;
|
||||
this.node.src = url;
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (!this.node) {
|
||||
return;
|
||||
}
|
||||
if (this.playing) {
|
||||
for (let subscriber of this.onStopSubscribers) {
|
||||
subscriber();
|
||||
}
|
||||
}
|
||||
logger.log('stopping');
|
||||
this.playing = false;
|
||||
this.node.src = '';
|
||||
}
|
||||
|
||||
setVolume(volume) {
|
||||
if (!this.node) {
|
||||
return;
|
||||
}
|
||||
this.volume = volume;
|
||||
this.node.volume = volume;
|
||||
}
|
||||
|
||||
onPlay(subscriber) {
|
||||
if (!this.node) {
|
||||
return;
|
||||
}
|
||||
this.onPlaySubscribers.push(subscriber);
|
||||
}
|
||||
|
||||
onStop(subscriber) {
|
||||
if (!this.node) {
|
||||
return;
|
||||
}
|
||||
this.onStopSubscribers.push(subscriber);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @file
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { createLogger } from 'tgui/logging';
|
||||
|
||||
const logger = createLogger('AudioPlayer');
|
||||
|
||||
type AudioOptions = {
|
||||
pitch?: number;
|
||||
start?: number;
|
||||
end?: number;
|
||||
};
|
||||
|
||||
export class AudioPlayer {
|
||||
element: HTMLAudioElement | null;
|
||||
options: AudioOptions;
|
||||
volume: number;
|
||||
|
||||
onPlaySubscribers: { (): void }[];
|
||||
onStopSubscribers: { (): void }[];
|
||||
|
||||
constructor() {
|
||||
this.element = null;
|
||||
|
||||
this.onPlaySubscribers = [];
|
||||
this.onStopSubscribers = [];
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.element = null;
|
||||
}
|
||||
|
||||
play(url: string, options: AudioOptions = {}) {
|
||||
if (this.element) {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
this.options = options;
|
||||
|
||||
const audio = (this.element = new Audio(url));
|
||||
audio.volume = this.volume;
|
||||
audio.playbackRate = this.options.pitch || 1;
|
||||
|
||||
logger.log('playing', url, options);
|
||||
|
||||
audio.addEventListener('ended', () => {
|
||||
logger.log('ended');
|
||||
this.stop();
|
||||
});
|
||||
|
||||
audio.addEventListener('error', (error) => {
|
||||
logger.log('playback error', error);
|
||||
});
|
||||
|
||||
if (this.options.end) {
|
||||
audio.addEventListener('timeupdate', () => {
|
||||
if (
|
||||
this.options.end &&
|
||||
this.options.end > 0 &&
|
||||
audio.currentTime >= this.options.end
|
||||
) {
|
||||
this.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
audio.play().catch((error) => logger.log('playback error', error));
|
||||
|
||||
this.onPlaySubscribers.forEach((subscriber) => subscriber());
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (!this.element) return;
|
||||
|
||||
logger.log('stopping');
|
||||
|
||||
this.element.pause();
|
||||
this.element = null;
|
||||
|
||||
this.onStopSubscribers.forEach((subscriber) => subscriber());
|
||||
}
|
||||
|
||||
setVolume(volume: number): void {
|
||||
this.volume = volume;
|
||||
|
||||
if (!this.element) return;
|
||||
|
||||
this.element.volume = volume;
|
||||
}
|
||||
|
||||
onPlay(subscriber: () => void): void {
|
||||
this.onPlaySubscribers.push(subscriber);
|
||||
}
|
||||
|
||||
onStop(subscriber: () => void): void {
|
||||
this.onStopSubscribers.push(subscriber);
|
||||
}
|
||||
}
|
||||
@@ -20,22 +20,36 @@ const FORBID_TAGS = ['a', 'iframe', 'link', 'video'];
|
||||
const saveChatToStorage = async (store) => {
|
||||
const settings = selectSettings(store.getState());
|
||||
const state = selectChat(store.getState());
|
||||
const fromIndex = Math.max(
|
||||
0,
|
||||
chatRenderer.messages.length - settings.maxMessages
|
||||
);
|
||||
const messages = chatRenderer.messages
|
||||
.slice(fromIndex)
|
||||
.map((message) => serializeMessage(message));
|
||||
|
||||
if (!window.hubStorage) {
|
||||
const indexedDbBackend = await storage.backendPromise;
|
||||
indexedDbBackend.processChatMessages(chatRenderer.storeQueue);
|
||||
} else {
|
||||
const fromIndex = Math.max(
|
||||
0,
|
||||
chatRenderer.messages.length - settings.maxMessages
|
||||
);
|
||||
const messages = chatRenderer.messages
|
||||
.slice(fromIndex)
|
||||
.map((message) => serializeMessage(message));
|
||||
|
||||
storage.set('chat-messages', messages);
|
||||
}
|
||||
|
||||
chatRenderer.storeQueue = [];
|
||||
storage.set('chat-state', state);
|
||||
storage.set('chat-messages', messages);
|
||||
};
|
||||
|
||||
const loadChatFromStorage = async (store) => {
|
||||
const [state, messages] = await Promise.all([
|
||||
storage.get('chat-state'),
|
||||
storage.get('chat-messages'),
|
||||
]);
|
||||
const state = await storage.get('chat-state');
|
||||
|
||||
let messages;
|
||||
if (!window.hubStorage) {
|
||||
messages = await (await storage.backendPromise).getChatMessages();
|
||||
} else {
|
||||
messages = await storage.get('chat-messages');
|
||||
}
|
||||
|
||||
// Discard incompatible versions
|
||||
if (state && state.version <= 4) {
|
||||
store.dispatch(loadChat());
|
||||
@@ -43,7 +57,7 @@ const loadChatFromStorage = async (store) => {
|
||||
}
|
||||
if (messages) {
|
||||
for (let message of messages) {
|
||||
if (message.html) {
|
||||
if (message?.html) {
|
||||
message.html = DOMPurify.sanitize(message.html, {
|
||||
FORBID_TAGS,
|
||||
});
|
||||
|
||||
@@ -110,6 +110,7 @@ class ChatRenderer {
|
||||
/** @type {HTMLElement} */
|
||||
this.rootNode = null;
|
||||
this.queue = [];
|
||||
this.storeQueue = [];
|
||||
this.messages = [];
|
||||
this.visibleMessages = [];
|
||||
this.page = null;
|
||||
@@ -326,6 +327,8 @@ class ChatRenderer {
|
||||
let node;
|
||||
for (let payload of batch) {
|
||||
const message = createMessage(payload);
|
||||
let historical = message.stored;
|
||||
|
||||
// Combine messages
|
||||
const combinable = this.getCombinableMessage(message);
|
||||
if (combinable) {
|
||||
@@ -426,6 +429,10 @@ class ChatRenderer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!historical) {
|
||||
this.storeQueue.push({ ...message, stored: true });
|
||||
}
|
||||
// Store the node in the message
|
||||
message.node = node;
|
||||
// Query all possible selectors to find out the message type
|
||||
|
||||
@@ -14,6 +14,7 @@ import { THEMES } from '../themes';
|
||||
import { changeSettingsTab, updateSettings, addHighlightSetting, removeHighlightSetting, updateHighlightSetting } from './actions';
|
||||
import { SETTINGS_TABS, FONTS, MAX_HIGHLIGHT_SETTINGS } from './constants';
|
||||
import { selectActiveTab, selectSettings, selectHighlightSettings, selectHighlightSettingById } from './selectors';
|
||||
import { IMPL_IFRAME_INDEXED_DB, storage } from 'common/storage';
|
||||
|
||||
export const SettingsPanel = (props, context) => {
|
||||
const activeTab = useSelector(context, selectActiveTab);
|
||||
@@ -159,13 +160,19 @@ export const SettingsGeneral = (props, context) => {
|
||||
maxValue={32000}
|
||||
value={maxMessages}
|
||||
format={(value) => toFixed(value)}
|
||||
onChange={(e, value) =>
|
||||
onChange={(e, value) => {
|
||||
storage.backendPromise.then((promise) => {
|
||||
if (promise.impl === IMPL_IFRAME_INDEXED_DB) {
|
||||
promise.setNumberStored(value);
|
||||
}
|
||||
});
|
||||
|
||||
dispatch(
|
||||
updateSettings({
|
||||
maxMessages: value,
|
||||
})
|
||||
)
|
||||
}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
|
||||
@@ -145,6 +145,7 @@ export class Input extends Component {
|
||||
<input
|
||||
ref={this.inputRef}
|
||||
className="Input__input"
|
||||
type="search"
|
||||
placeholder={placeholder}
|
||||
onInput={this.handleInput}
|
||||
onFocus={this.handleFocus}
|
||||
|
||||
@@ -10,6 +10,8 @@ import { addScrollableNode, removeScrollableNode } from '../events';
|
||||
|
||||
export const Layout = (props) => {
|
||||
const { className, theme = 'scc', children, ...rest } = props;
|
||||
document.documentElement.className = `theme-${theme}`;
|
||||
|
||||
return (
|
||||
<div className={'theme-' + theme}>
|
||||
<div
|
||||
|
||||
@@ -5,8 +5,24 @@
|
||||
|
||||
@use 'sass:color';
|
||||
@use '../base.scss';
|
||||
@use '../functions.scss' as *;
|
||||
|
||||
$scrollbar-color-multiplier: 1 !default;
|
||||
$luminance: luminance(base.$color-bg);
|
||||
$scrollbar-base: color.scale(
|
||||
base.$color-bg,
|
||||
$lightness: -25% * $scrollbar-color-multiplier
|
||||
);
|
||||
$scrollbar-face: color.scale(
|
||||
base.$color-bg,
|
||||
$lightness: if($luminance > 0.05, 30%, 10%) * $scrollbar-color-multiplier
|
||||
);
|
||||
|
||||
// Fancy scrollbar
|
||||
html,
|
||||
body {
|
||||
scrollbar-color: $scrollbar-face $scrollbar-base;
|
||||
}
|
||||
|
||||
.Layout,
|
||||
.Layout * {
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv='X-UA-Compatible' content='IE=edge' />
|
||||
<meta charset='utf-8' />
|
||||
|
||||
<script type='text/javascript'>
|
||||
const INDEXED_DB_VERSION = 2;
|
||||
const INDEXED_DB_NAME = 'tgui';
|
||||
const INDEXED_DB_STORE_NAME = 'storage';
|
||||
|
||||
const READ_ONLY = 'readonly';
|
||||
const READ_WRITE = 'readwrite';
|
||||
|
||||
let maxStoredMessages = 2500;
|
||||
let startFrom = 0;
|
||||
let writeFrom = 0;
|
||||
|
||||
const dbPromise = new Promise((resolve, reject) => {
|
||||
const indexedDB = window.indexedDB || window.msIndexedDB;
|
||||
const req = indexedDB.open(INDEXED_DB_NAME, INDEXED_DB_VERSION);
|
||||
req.onupgradeneeded = (event) => {
|
||||
try {
|
||||
|
||||
if(event.oldVersion < 1) {
|
||||
req.result.createObjectStore(INDEXED_DB_STORE_NAME);
|
||||
}
|
||||
|
||||
if(event.oldVersion < 2) {
|
||||
req.result.createObjectStore("ring");
|
||||
req.result.createObjectStore("messages", { autoIncrement: true }).transaction.oncomplete = async () => {
|
||||
const trans = req.result.transaction("messages", READ_WRITE).objectStore("messages");
|
||||
|
||||
startFrom = await promiseForRequest(trans.add(null)) - 1;
|
||||
for (let index = 0; index < maxStoredMessages - 1; index++) {
|
||||
await promiseForRequest(trans.add(null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
reject(new Error('Failed to upgrade IDB: ' + req.error));
|
||||
}
|
||||
};
|
||||
req.onsuccess = async () => {
|
||||
const storage = req.result.transaction(["ring", "messages", INDEXED_DB_STORE_NAME], "readonly")
|
||||
|
||||
writeFrom = await promiseForRequest(storage.objectStore("ring").get("value")) || 0;
|
||||
|
||||
const panelSettings = await promiseForRequest(storage.objectStore(INDEXED_DB_STORE_NAME).get("panel-settings"))
|
||||
maxStoredMessages = panelSettings?.maxMessages || 2500;
|
||||
|
||||
const messageStore = storage.objectStore("messages");
|
||||
|
||||
const messageCount = await promiseForRequest(messageStore.count());
|
||||
if(messageCount !== maxStoredMessages) {
|
||||
await alterMessageRing(req.result);
|
||||
}
|
||||
|
||||
let cursorReq = await promiseForRequest(messageStore.openCursor());
|
||||
let cursor = await(cursorReq);
|
||||
startFrom = cursor.key - 1;
|
||||
|
||||
resolve(req.result);
|
||||
};
|
||||
req.onerror = () => {
|
||||
reject(new Error('Failed to open IDB: ' + req.error));
|
||||
};
|
||||
});
|
||||
|
||||
const alterMessageRing = async (db) => {
|
||||
db = db ?? await dbPromise;
|
||||
|
||||
const existingMessages = (await getSavedMessages(db)).slice(0, maxStoredMessages);
|
||||
|
||||
const trans = db.transaction("messages", READ_WRITE).objectStore("messages");
|
||||
|
||||
await promiseForRequest(trans.clear());
|
||||
|
||||
startFrom = await promiseForRequest(trans.add(null)) - 1;
|
||||
for (let index = 0; index < maxStoredMessages - 1; index++) {
|
||||
await promiseForRequest(trans.add(null));
|
||||
}
|
||||
|
||||
writeFrom = startFrom;
|
||||
|
||||
await addMessageBatch(existingMessages, db);
|
||||
}
|
||||
|
||||
window.addEventListener('message', (messageEvent) => {
|
||||
switch (messageEvent.data.type) {
|
||||
case 'ping':
|
||||
messageEvent.source.postMessage(true, "*");
|
||||
break;
|
||||
case 'get':
|
||||
get(messageEvent.data.key).then((value) => {
|
||||
messageEvent.source.postMessage({key: messageEvent.data.key, value: value}, "*");
|
||||
});
|
||||
break;
|
||||
case 'set':
|
||||
set(messageEvent.data.key, messageEvent.data.value);
|
||||
break;
|
||||
case 'remove':
|
||||
remove(messageEvent.data.key);
|
||||
break;
|
||||
case 'clear':
|
||||
clear();
|
||||
break;
|
||||
case 'setNumberStored':
|
||||
maxStoredMessages = messageEvent.data.newMax;
|
||||
alterMessageRing();
|
||||
break;
|
||||
case 'processChatMessages':
|
||||
addMessageBatch(messageEvent.data.messages);
|
||||
break;
|
||||
case 'getChatMessages':
|
||||
getSavedMessages().then((messages) => {
|
||||
messageEvent.source.postMessage({messages: messages}, "*");
|
||||
})
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
const addMessageBatch = async (messages, db) => {
|
||||
if(!messages || !messages.length) return;
|
||||
|
||||
const database = db ?? await dbPromise;
|
||||
|
||||
const trans = database.transaction("messages", READ_WRITE).objectStore("messages");
|
||||
|
||||
console.log(writeFrom, startFrom);
|
||||
|
||||
let cursorReq = trans.openCursor(IDBKeyRange.bound(writeFrom, writeFrom + messages.length, true));
|
||||
let cursor = await promiseForRequest(cursorReq);
|
||||
|
||||
for (let index = 0; index < messages.length; index++) {
|
||||
const message = messages[index];
|
||||
|
||||
cursor.update(message);
|
||||
writeFrom = cursor.key;
|
||||
|
||||
if(cursor.key === startFrom + maxStoredMessages) {
|
||||
writeFrom = startFrom;
|
||||
|
||||
cursorReq = trans.openCursor(IDBKeyRange.bound(startFrom, startFrom + messages.length - index + 1, true));
|
||||
cursor = await promiseForRequest(cursorReq);
|
||||
} else {
|
||||
cursor.continue();
|
||||
await promiseForRequest(cursorReq);
|
||||
}
|
||||
}
|
||||
|
||||
database.transaction("ring", READ_WRITE).objectStore("ring").put(writeFrom, "value");
|
||||
}
|
||||
|
||||
const getSavedMessages = async (db) => {
|
||||
const database = db ?? await dbPromise;
|
||||
|
||||
const trans = database.transaction("messages", READ_ONLY).objectStore("messages");
|
||||
const messyMessages = await promiseForRequest(trans.getAll());
|
||||
|
||||
const upTo = messyMessages.splice(0, writeFrom);
|
||||
|
||||
if(messyMessages[0] === null) {
|
||||
return upTo;
|
||||
}
|
||||
|
||||
const messages = messyMessages.concat(upTo);
|
||||
return messages;
|
||||
}
|
||||
|
||||
const getStore = async (mode) => {
|
||||
return dbPromise.then((db) => db
|
||||
.transaction(INDEXED_DB_STORE_NAME, mode)
|
||||
.objectStore(INDEXED_DB_STORE_NAME));
|
||||
};
|
||||
|
||||
const get = async (key) => {
|
||||
const store = await getStore(READ_ONLY);
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = store.get(key);
|
||||
req.onsuccess = () => resolve(req.result);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
};
|
||||
|
||||
const set = async (key, value) => {
|
||||
const store = await getStore(READ_WRITE);
|
||||
store.put(value, key);
|
||||
};
|
||||
|
||||
const remove = async (key) => {
|
||||
const store = await getStore(READ_WRITE);
|
||||
store.delete(key);
|
||||
};
|
||||
|
||||
const clear = async () => {
|
||||
const store = await getStore(READ_WRITE);
|
||||
store.clear();
|
||||
};
|
||||
|
||||
const promiseForRequest = (request) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
request.onsuccess = () => { resolve(request.result); };
|
||||
request.onerror = () => { reject(request.error); };
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
</html>
|
||||
+70
-45
@@ -40,6 +40,8 @@
|
||||
// Expose inlined metadata
|
||||
Byond.windowId = parseMetaTag('tgui:windowId');
|
||||
|
||||
Byond.storageCdn = "tgui:storagecdn"
|
||||
|
||||
// Backwards compatibility
|
||||
window.__windowId__ = Byond.windowId;
|
||||
|
||||
@@ -52,8 +54,15 @@
|
||||
: null;
|
||||
})();
|
||||
|
||||
// Blink engine version
|
||||
Byond.BLINK = (function () {
|
||||
var groups = navigator.userAgent.match(/Chrome\/(\d+)\./);
|
||||
var majorVersion = groups && groups[1];
|
||||
return majorVersion ? parseInt(majorVersion, 10) : null;
|
||||
})();
|
||||
|
||||
// Basic checks to detect whether this page runs in BYOND
|
||||
var isByond = (Byond.TRIDENT !== null || window.cef_to_byond)
|
||||
var isByond = (Byond.TRIDENT !== null || Byond.BLINK !== null || window.cef_to_byond)
|
||||
&& location.hostname === '127.0.0.1'
|
||||
&& location.search !== '?external';
|
||||
//As of BYOND 515 the path doesn't seem to include tmp dir anymore if you're trying to open tgui in external browser and looking why it doesn't work
|
||||
@@ -61,10 +70,6 @@
|
||||
|
||||
// Version constants
|
||||
Byond.IS_BYOND = isByond;
|
||||
Byond.IS_LTE_IE8 = Byond.TRIDENT !== null && Byond.TRIDENT <= 4;
|
||||
Byond.IS_LTE_IE9 = Byond.TRIDENT !== null && Byond.TRIDENT <= 5;
|
||||
Byond.IS_LTE_IE10 = Byond.TRIDENT !== null && Byond.TRIDENT <= 6;
|
||||
Byond.IS_LTE_IE11 = Byond.TRIDENT !== null && Byond.TRIDENT <= 7;
|
||||
|
||||
// Strict mode flag
|
||||
Byond.strictMode = Boolean(Number(parseMetaTag('tgui:strictMode')));
|
||||
@@ -73,17 +78,12 @@
|
||||
Byond.__callbacks__ = [];
|
||||
|
||||
// Reviver for BYOND JSON
|
||||
// IE8: No reviver for you!
|
||||
// See: https://stackoverflow.com/questions/1288962
|
||||
var byondJsonReviver;
|
||||
if (!Byond.IS_LTE_IE8) {
|
||||
byondJsonReviver = function (key, value) {
|
||||
if (typeof value === 'object' && value !== null && value.__number__) {
|
||||
return parseFloat(value.__number__);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
var byondJsonReviver = function (key, value) {
|
||||
if (typeof value === 'object' && value !== null && value.__number__) {
|
||||
return parseFloat(value.__number__);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
// Makes a BYOND call.
|
||||
// See: https://secure.byond.com/docs/ref/skinparams.html
|
||||
@@ -244,23 +244,10 @@
|
||||
var loadedAssetByUrl = {};
|
||||
|
||||
var isStyleSheetLoaded = function (node, url) {
|
||||
// Method #1 (works on IE10+)
|
||||
var styleSheet = node.sheet;
|
||||
if (styleSheet) {
|
||||
return styleSheet.rules.length > 0;
|
||||
}
|
||||
// Method #2
|
||||
var styleSheets = document.styleSheets;
|
||||
var len = styleSheets.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
var styleSheet = styleSheets[i];
|
||||
if(styleSheet.href === undefined)
|
||||
continue;
|
||||
if (styleSheet.href.indexOf(url) !== -1) {
|
||||
return styleSheet.rules.length > 0;
|
||||
}
|
||||
}
|
||||
// All methods failed
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -308,10 +295,7 @@
|
||||
var node = document.createElement('script');
|
||||
node.type = 'text/javascript';
|
||||
node.crossOrigin = 'anonymous';
|
||||
// IE8: Prefer non-https protocols
|
||||
node.src = Byond.IS_LTE_IE9
|
||||
? url.replace('https://', 'http://')
|
||||
: url;
|
||||
node.src = url;
|
||||
if (sync) {
|
||||
node.defer = true;
|
||||
}
|
||||
@@ -332,15 +316,25 @@
|
||||
var node = document.createElement('link');
|
||||
node.type = 'text/css';
|
||||
node.rel = 'stylesheet';
|
||||
// IE8: Prefer non-https protocols
|
||||
node.href = Byond.IS_LTE_IE9
|
||||
? url.replace('https://', 'http://')
|
||||
: url;
|
||||
node.crossOrigin = 'anonymous';
|
||||
node.href = url;
|
||||
// Temporarily set media to something inapplicable
|
||||
// to ensure it'll fetch without blocking render
|
||||
if (!sync) {
|
||||
node.media = 'only x';
|
||||
}
|
||||
var removeNodeAndRetry = function () {
|
||||
node.parentNode.removeChild(node);
|
||||
node = null;
|
||||
retry();
|
||||
}
|
||||
// 516: Chromium won't call onload() if there is a 404 error
|
||||
// Legacy IE doesn't use onerror, so we retain that
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#stylesheet_load_events
|
||||
node.onerror = function () {
|
||||
node.onerror = null;
|
||||
removeNodeAndRetry();
|
||||
}
|
||||
node.onload = function () {
|
||||
node.onload = null;
|
||||
if (isStyleSheetLoaded(node, url)) {
|
||||
@@ -348,10 +342,7 @@
|
||||
node.media = 'all';
|
||||
return;
|
||||
}
|
||||
// Try again
|
||||
node.parentNode.removeChild(node);
|
||||
node = null;
|
||||
retry();
|
||||
removeNodeAndRetry();
|
||||
};
|
||||
injectNode(node);
|
||||
return;
|
||||
@@ -365,6 +356,39 @@
|
||||
Byond.loadCss = function (url, sync) {
|
||||
loadAsset({ url: url, sync: sync, type: 'css' });
|
||||
};
|
||||
|
||||
Byond.saveBlob = function (blob, filename, ext) {
|
||||
if (window.navigator.msSaveBlob) {
|
||||
window.navigator.msSaveBlob(blob, filename)
|
||||
} else if (window.showSaveFilePicker) {
|
||||
var accept = {};
|
||||
accept[blob.type] = [ext];
|
||||
|
||||
var opts = {
|
||||
suggestedName: filename,
|
||||
types: [
|
||||
{
|
||||
description: "SS13 file",
|
||||
accept: accept,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
try {
|
||||
window.showSaveFilePicker(opts).then(function (fileHandle) {
|
||||
fileHandle.createWritable().then(function (writeableFileHandle) {
|
||||
writeableFileHandle.write(blob).then(function () {
|
||||
writeableFileHandle.close()
|
||||
}).catch(function (e) { console.error(e) });
|
||||
}).catch(function (e) { console.error(e) });
|
||||
}).catch(function (e) { console.error(e) });
|
||||
} catch (e) { console.error(e) }
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Icon cache
|
||||
Byond.iconRefMap = {};
|
||||
})();
|
||||
|
||||
// Error handling
|
||||
@@ -395,7 +419,7 @@ window.onerror = function (msg, url, line, col, error) {
|
||||
else {
|
||||
window.onerror.__stack__ = stack;
|
||||
}
|
||||
var textProp = Byond.IS_LTE_IE8 ? 'innerText' : 'textContent';
|
||||
var textProp = 'textContent';
|
||||
errorStack[textProp] = window.onerror.__stack__;
|
||||
}
|
||||
// Set window geometry
|
||||
@@ -510,9 +534,6 @@ window.replaceHtml = function (inline_html) {
|
||||
+ "<!-- tgui:inline-html-end -->"
|
||||
);
|
||||
};
|
||||
|
||||
// Signal tgui that we're ready to receive updates
|
||||
Byond.sendMessage('ready');
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -657,5 +678,9 @@ Thank you for your cooperation.
|
||||
</div>
|
||||
</noscript>
|
||||
|
||||
<script>
|
||||
// Signal tgui that we're ready to receive updates
|
||||
Byond.sendMessage('ready');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user