From 04bc9fbd135c2dc76ff3ad75bb6d1c0e5b6eecee Mon Sep 17 00:00:00 2001 From: harry Date: Wed, 26 Mar 2025 21:41:18 +0000 Subject: [PATCH] 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> --- .github/workflows/generate_web_assets.yml | 23 ++ aurorastation.dme | 1 + code/ZAS/Variable Settings.dm | 2 +- code/__DEFINES/html.dm | 5 + code/controllers/configuration.dm | 7 + .../controllers/subsystems/processing/tgui.dm | 18 ++ code/datums/mind.dm | 2 +- code/game/gamemodes/technomancer/catalog.dm | 10 +- code/game/machinery/computer/cloning.dm | 2 +- code/game/machinery/suit_storage_unit.dm | 2 +- code/game/objects/items/weapons/manuals.dm | 2 +- .../objects/items/weapons/storage/secure.dm | 2 +- .../objects/items/weapons/teleportation.dm | 2 +- code/modules/admin/DB ban/ban_mirroring.dm | 4 +- code/modules/admin/NewBan.dm | 2 +- code/modules/admin/admin.dm | 12 +- code/modules/admin/admin_verbs.dm | 1 + .../admin/secrets/admin_secrets/admin_logs.dm | 2 +- .../secrets/admin_secrets/bombing_list.dm | 2 +- .../admin/secrets/admin_secrets/list_dna.dm | 2 +- .../admin_secrets/list_fingerprints.dm | 2 +- .../secrets/admin_secrets/show_law_changes.dm | 2 +- .../secrets/admin_secrets/show_signalers.dm | 2 +- code/modules/admin/stickyban.dm | 2 +- code/modules/admin/topic.dm | 6 +- code/modules/admin/verbs/debug.dm | 22 +- code/modules/admin/verbs/diagnostics.dm | 4 +- code/modules/admin/verbs/mapping.dm | 2 +- code/modules/admin/verbs/randomverbs.dm | 2 +- .../awaymissions/bluespaceartillery.dm | 2 +- code/modules/battlemonsters/items/deck.dm | 2 +- code/modules/client/client_procs.dm | 2 +- .../preference_setup/general/03_body.dm | 2 +- .../preference_setup/general/06_flavor.dm | 4 +- .../preference_setup/occupation/occupation.dm | 2 +- code/modules/economy/EFTPOS.dm | 2 +- code/modules/holodeck/HolodeckControl.dm | 6 +- code/modules/hydroponics/trays/tray_tools.dm | 2 +- code/modules/library/lib_items.dm | 4 +- code/modules/library/lib_machines.dm | 17 +- code/modules/mining/mint.dm | 2 +- .../ghost/storyteller/storyteller_verbs.dm | 2 +- code/modules/mob/abstract/new_player/poll.dm | 10 +- code/modules/mob/language/language.dm | 4 +- code/modules/mob/living/carbon/human/emote.dm | 4 +- code/modules/mob/living/silicon/ai/ai.dm | 2 +- .../living/silicon/robot/drone/drone_items.dm | 5 +- .../modules/mob/living/silicon/robot/robot.dm | 4 +- code/modules/mob/living/silicon/silicon.dm | 2 +- .../mob/living/simple_animal/parrot.dm | 2 +- code/modules/paperwork/clipboard.dm | 4 +- code/modules/paperwork/folders.dm | 4 +- .../projectiles/guns/projectile/dartgun.dm | 2 +- code/modules/reagents/Chemistry-Logging.dm | 2 +- code/modules/recycling/sortingmachinery.dm | 2 +- code/modules/research/server.dm | 2 +- .../artifact/artifact_replicator.dm | 2 +- .../xenoarchaeology/tools/tools_locater.dm | 2 +- .../security levels/keycard authentication.dm | 2 +- html/changelogs/20652-516-compat.yml | 6 + html/statbrowser.css | 14 ++ html/statbrowser.js | 2 + tgui/packages/common/storage.js | 191 ++++++++++++---- tgui/packages/tgui-panel/audio/player.js | 117 ---------- tgui/packages/tgui-panel/audio/player.ts | 101 +++++++++ tgui/packages/tgui-panel/chat/middleware.js | 40 ++-- tgui/packages/tgui-panel/chat/renderer.js | 7 + .../tgui-panel/settings/SettingsPanel.js | 13 +- tgui/packages/tgui/components/Input.js | 1 + tgui/packages/tgui/layouts/Layout.js | 2 + tgui/packages/tgui/styles/layouts/Layout.scss | 16 ++ tgui/public/iframe.html | 213 ++++++++++++++++++ tgui/public/tgui.html | 115 ++++++---- 73 files changed, 777 insertions(+), 312 deletions(-) create mode 100644 .github/workflows/generate_web_assets.yml create mode 100644 code/__DEFINES/html.dm create mode 100644 html/changelogs/20652-516-compat.yml delete mode 100644 tgui/packages/tgui-panel/audio/player.js create mode 100644 tgui/packages/tgui-panel/audio/player.ts create mode 100644 tgui/public/iframe.html diff --git a/.github/workflows/generate_web_assets.yml b/.github/workflows/generate_web_assets.yml new file mode 100644 index 00000000000..bbb8d31c42a --- /dev/null +++ b/.github/workflows/generate_web_assets.yml @@ -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 diff --git a/aurorastation.dme b/aurorastation.dme index 874d116dfe0..58aa86c0943 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -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" diff --git a/code/ZAS/Variable Settings.dm b/code/ZAS/Variable Settings.dm index 91bf1e1c1bc..395e52b8d88 100644 --- a/code/ZAS/Variable Settings.dm +++ b/code/ZAS/Variable Settings.dm @@ -113,7 +113,7 @@ GLOBAL_DATUM_INIT(vsc, /vs_control, new()) if("[ch]_NAME" in vars) vw_name = vars["[ch]_NAME"] dat += "[vw_name] = [vw] \[Change\]
" dat += "[vw_desc]

" - user << browse(dat,"window=settings") + user << browse(HTML_SKELETON(dat), "window=settings") /vs_control/Topic(href,href_list) if("changevar" in href_list) diff --git a/code/__DEFINES/html.dm b/code/__DEFINES/html.dm new file mode 100644 index 00000000000..91af96a95c7 --- /dev/null +++ b/code/__DEFINES/html.dm @@ -0,0 +1,5 @@ +#define HTML_SKELETON_INTERNAL(head, body) \ +"[head][body]" + +#define HTML_SKELETON_TITLE(title, body) HTML_SKELETON_INTERNAL("[title]", body) +#define HTML_SKELETON(body) HTML_SKELETON_INTERNAL("", body) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 8aa0d89aff3..9f6c40eb39b 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -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") diff --git a/code/controllers/subsystems/processing/tgui.dm b/code/controllers/subsystems/processing/tgui.dm index 6d298f446c6..144691ac791 100644 --- a/code/controllers/subsystems/processing/tgui.dm +++ b/code/controllers/subsystems/processing/tgui.dm @@ -34,6 +34,24 @@ PROCESSING_SUBSYSTEM_DEF(tgui) basehtml = replacetextEx(basehtml, "", polyfill) basehtml = replacetextEx(basehtml, "", "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() diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 7545dbe35a8..68002870f95 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -187,7 +187,7 @@ out += "None." out += "
\[add\]" out += "Ambitions: [ambitions ? ambitions : "None"] \[edit\]
" - 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 diff --git a/code/game/gamemodes/technomancer/catalog.dm b/code/game/gamemodes/technomancer/catalog.dm index f3b1ed92390..fa36cb01cd0 100644 --- a/code/game/gamemodes/technomancer/catalog.dm +++ b/code/game/gamemodes/technomancer/catalog.dm @@ -152,7 +152,7 @@ GLOBAL_LIST_INIT(all_technomancer_assistance, (typesof(/datum/technomancer/assis dat += "Purchase ([spell.cost])

" else dat += "Cannot afford!

" - 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 += "Purchase ([E.cost])

" else dat += "Cannot afford!

" - 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 += "Purchase ([C.cost])

" else dat += "Cannot afford!

" - 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 += "Purchase ([A.cost])

" else dat += "Cannot afford!

" - 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.
" dat += "A meter is equal to one 'tile'.
" - user << browse(dat, "window=radio") + user << browse(HTML_SKELETON(dat), "window=radio") onclose(user, "radio") // Proc: Topic() diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index dc3406b9876..862bd69161e 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -217,7 +217,7 @@ dat += "No" - user << browse(dat, "window=cloning") + user << browse(HTML_SKELETON(dat), "window=cloning") onclose(user, "cloning") return diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 2b59349c4cf..288428b821b 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -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 diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm index 876d1273e59..4ddfb0a8c39 100644 --- a/code/game/objects/items/weapons/manuals.dm +++ b/code/game/objects/items/weapons/manuals.dm @@ -1495,7 +1495,7 @@ /obj/item/book/manual/tcaf_pamphlet/attack_self(var/mob/user as mob) if(src.dat) - user << browse("Penned by [author].
" + "[dat]", "window=book") + user << browse(HTML_SKELETON("Penned by [author].
" + "[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") diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm index cce402709f0..46e7d147215 100644 --- a/code/game/objects/items/weapons/storage/secure.dm +++ b/code/game/objects/items/weapons/storage/secure.dm @@ -92,7 +92,7 @@ ABSTRACT_TYPE(/obj/item/storage/secure) message = "*****" dat += "
\n>[message]
\n1-2-3
\n4-5-6
\n7-8-9
\nR-0-E
\n" - user << browse(dat, "window=caselock;size=300x280") + user << browse(HTML_SKELETON(dat), "window=caselock;size=300x280") /obj/item/storage/secure/Topic(href, href_list) ..() diff --git a/code/game/objects/items/weapons/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm index 185fb9b51a8..ca5772e5321 100644 --- a/code/game/objects/items/weapons/teleportation.dm +++ b/code/game/objects/items/weapons/teleportation.dm @@ -55,7 +55,7 @@ Frequency: +
Refresh"} - user << browse(dat, "window=radio") + user << browse(HTML_SKELETON(dat), "window=radio") onclose(user, "radio") return diff --git a/code/modules/admin/DB ban/ban_mirroring.dm b/code/modules/admin/DB ban/ban_mirroring.dm index bc6c6ef40c4..ef8d5ec480c 100644 --- a/code/modules/admin/DB ban/ban_mirroring.dm +++ b/code/modules/admin/DB ban/ban_mirroring.dm @@ -188,7 +188,7 @@ i = (++i % 2) output += "" - 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 += "

Back" - 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) diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm index 4a666396fd8..33bc0acc17c 100644 --- a/code/modules/admin/NewBan.dm +++ b/code/modules/admin/NewBan.dm @@ -189,7 +189,7 @@ var/savefile/Banlist dat += "" dat = "
Bans: (U) = Unban , (E) = Edit Ban - ([count] Bans)
[dat]" - usr << browse(dat, "window=unbanp;size=875x400") + usr << browse(HTML_SKELETON(dat), "window=unbanp;size=875x400") //////////////////////////////////// DEBUG //////////////////////////////////// diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 430f9dc8a9d..a9c63574d8e 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -281,7 +281,7 @@ var/global/enabled_spooking = 0 if(index == page) dat += "" - 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 += "" dat += "
[ckey] - [ban[2]] - (unban)
" - 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 Choose a default ZAS setting
"} - 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 += "[item.name()]
" dat += "
" - 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 += " \[+\]
" - usr << browse(out, "window=edit_mode[src]") + usr << browse(HTML_SKELETON(out), "window=edit_mode[src]") feedback_add_details("admin_verb","SGM") diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 28a84a90003..6af1a9a78a9 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -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( diff --git a/code/modules/admin/secrets/admin_secrets/admin_logs.dm b/code/modules/admin/secrets/admin_secrets/admin_logs.dm index 24be5c701b0..a42452e7b4a 100644 --- a/code/modules/admin/secrets/admin_secrets/admin_logs.dm +++ b/code/modules/admin/secrets/admin_secrets/admin_logs.dm @@ -10,4 +10,4 @@ dat += "
  • [l]
  • " 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") diff --git a/code/modules/admin/secrets/admin_secrets/bombing_list.dm b/code/modules/admin/secrets/admin_secrets/bombing_list.dm index 54feb9ee277..cabe401e4d3 100644 --- a/code/modules/admin/secrets/admin_secrets/bombing_list.dm +++ b/code/modules/admin/secrets/admin_secrets/bombing_list.dm @@ -9,4 +9,4 @@ var/dat = "Bombing List" for(var/l in GLOB.bombers) dat += "[l]
    " - user << browse(dat, "window=bombers") + user << browse(HTML_SKELETON(dat), "window=bombers") diff --git a/code/modules/admin/secrets/admin_secrets/list_dna.dm b/code/modules/admin/secrets/admin_secrets/list_dna.dm index 7492da9c3df..518a5e0775a 100644 --- a/code/modules/admin/secrets/admin_secrets/list_dna.dm +++ b/code/modules/admin/secrets/admin_secrets/list_dna.dm @@ -11,4 +11,4 @@ if(H.dna && H.ckey) dat += "[H][H.dna.unique_enzymes][H.b_type]" dat += "" - user << browse(dat, "window=DNA;size=440x410") + user << browse(HTML_SKELETON(dat), "window=DNA;size=440x410") diff --git a/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm b/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm index 146d0ac7738..29f3800976b 100644 --- a/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm +++ b/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm @@ -16,4 +16,4 @@ else if(!H.dna) dat += "[H]H.dna = null" dat += "" - user << browse(dat, "window=fingerprints;size=440x410") + user << browse(HTML_SKELETON(dat), "window=fingerprints;size=440x410") diff --git a/code/modules/admin/secrets/admin_secrets/show_law_changes.dm b/code/modules/admin/secrets/admin_secrets/show_law_changes.dm index e870d74381a..6de35377450 100644 --- a/code/modules/admin/secrets/admin_secrets/show_law_changes.dm +++ b/code/modules/admin/secrets/admin_secrets/show_law_changes.dm @@ -12,4 +12,4 @@ var/dat = "Showing last [length(GLOB.lawchanges)] law changes.
    " for(var/sig in GLOB.lawchanges) dat += "[sig]
    " - user << browse(dat, "window=lawchanges;size=800x500") + user << browse(HTML_SKELETON(dat), "window=lawchanges;size=800x500") diff --git a/code/modules/admin/secrets/admin_secrets/show_signalers.dm b/code/modules/admin/secrets/admin_secrets/show_signalers.dm index 44acb294c2f..13f2bd6faf5 100644 --- a/code/modules/admin/secrets/admin_secrets/show_signalers.dm +++ b/code/modules/admin/secrets/admin_secrets/show_signalers.dm @@ -12,4 +12,4 @@ var/dat = "Showing last [length(GLOB.lastsignalers)] signalers.
    " for(var/sig in GLOB.lastsignalers) dat += "[sig]
    " - user << browse(dat, "window=lastsignalers;size=800x500") + user << browse(HTML_SKELETON(dat), "window=lastsignalers;size=800x500") diff --git a/code/modules/admin/stickyban.dm b/code/modules/admin/stickyban.dm index 43f30b37ab6..48056e144c0 100644 --- a/code/modules/admin/stickyban.dm +++ b/code/modules/admin/stickyban.dm @@ -369,7 +369,7 @@ [banhtml.Join("")] "} - 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)) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index fe4bec1e51c..981daf3d455 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -416,7 +416,7 @@ dat += {"Secret
    "} dat += {"Random
    "} 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 += {"[GLOB.config.mode_names[mode]]
    "} dat += {"Random (default)
    "} 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 += "Page [page] - [pageobj.name]
    " - 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]")) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index acabc41d838..71b6cdb3ba0 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -413,7 +413,7 @@ dellog += "" - 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 += "" - 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", "
    "), "window=initlog") + usr << browse(HTML_SKELETON(replacetext(SSatoms.InitLog(), "\n", "
    ")), "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")) diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 4ec12e06561..95f0b855932 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -39,7 +39,7 @@ Tile Update: [SSair.tiles_to_update.len]
    "} - 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]
    " - 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() diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 6f6798cfef9..8b4134c3d25 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -101,7 +101,7 @@ GLOBAL_VAR_INIT(intercom_range_display_status, 0) if(!window_check) output += "
  • Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]
  • " output += "" - 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() diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index f3060f9f099..fbc69d878d5 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -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.") diff --git a/code/modules/awaymissions/bluespaceartillery.dm b/code/modules/awaymissions/bluespaceartillery.dm index a1bf5c86609..92c0d374d86 100644 --- a/code/modules/awaymissions/bluespaceartillery.dm +++ b/code/modules/awaymissions/bluespaceartillery.dm @@ -19,7 +19,7 @@ dat += "Open Fire - Area
    " dat += "Open Fire - Coordinates
    " dat += "Deployment of weapon authorized by
    [SSatlas.current_map.company_name] Chief Naval Director

    Remember, friendly fire is grounds for termination of your contract and life.
    " - user << browse(dat, "window=scroll") + user << browse(HTML_SKELETON(dat), "window=scroll") onclose(user, "scroll") return diff --git a/code/modules/battlemonsters/items/deck.dm b/code/modules/battlemonsters/items/deck.dm index 5cce79a854b..eba49ab9c9a 100644 --- a/code/modules/battlemonsters/items/deck.dm +++ b/code/modules/battlemonsters/items/deck.dm @@ -178,7 +178,7 @@ browse_data = "[formatted_data]
    Draw Card

    [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(..()) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 28c460bd9e0..50d220ba991 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -756,7 +756,7 @@ GLOBAL_LIST_INIT(localhost_addresses, list( dat += "The request is [request["request_age"]] days old.
    " dat += "OPTIONS: Accept Request | Deny Request" - src << browse(dat, "window=LinkingRequests") + src << browse(HTML_SKELETON(dat), "window=LinkingRequests") return /client/proc/gather_linking_requests() diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm index 4f08a549bb6..2e1b7883e35 100644 --- a/code/modules/client/preference_setup/general/03_body.dm +++ b/code/modules/client/preference_setup/general/03_body.dm @@ -998,7 +998,7 @@ GLOBAL_LIST_INIT(valid_bloodtypes, list( dat += "" - 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) diff --git a/code/modules/client/preference_setup/general/06_flavor.dm b/code/modules/client/preference_setup/general/06_flavor.dm index f7658c1b2e1..33186b91dfd 100644 --- a/code/modules/client/preference_setup/general/06_flavor.dm +++ b/code/modules/client/preference_setup/general/06_flavor.dm @@ -244,7 +244,7 @@ HTML += "
    " HTML += "
    " HTML += "" - 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 += "
    " HTML += "
    " HTML += "" - user << browse(HTML, "window=flavour_text_robot;size=430x300") + user << browse(HTML_SKELETON(HTML), "window=flavour_text_robot;size=430x300") return diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index 97ff81b0e4c..8279494808f 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -495,7 +495,7 @@ dat += "
    [faction.get_selection_error(pref, user)]" dat += "" - 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] diff --git a/code/modules/economy/EFTPOS.dm b/code/modules/economy/EFTPOS.dm index 9fe7b5512bd..0f34b54c9e7 100644 --- a/code/modules/economy/EFTPOS.dm +++ b/code/modules/economy/EFTPOS.dm @@ -108,7 +108,7 @@ dat += "Change access code
    " dat += "Change EFTPOS ID
    " dat += "Scan card to reset access code \[------\]" - user << browse(dat,"window=eftpos") + user << browse(HTML_SKELETON(dat),"window=eftpos") else user << browse(null,"window=eftpos") diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm index 4aa1bf4636e..2939c59b9d2 100644 --- a/code/modules/holodeck/HolodeckControl.dm +++ b/code/modules/holodeck/HolodeckControl.dm @@ -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.
    ") - 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.
    ") - 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 = "Holodeck Control System
    " dat += "Holodeck is (LOCKED)
    " - user << browse(dat, "window=computer;size=400x500") + user << browse(HTML_SKELETON(dat), "window=computer;size=400x500") onclose(user, "computer") return diff --git a/code/modules/hydroponics/trays/tray_tools.dm b/code/modules/hydroponics/trays/tray_tools.dm index 4e7379bb7d0..45325b7779e 100644 --- a/code/modules/hydroponics/trays/tray_tools.dm +++ b/code/modules/hydroponics/trays/tray_tools.dm @@ -226,6 +226,6 @@ if(dat) last_data = dat dat += "

    \[print report\]" - user << browse(dat,"window=plant_analyzer") + user << browse(HTML_SKELETON(dat),"window=plant_analyzer") return diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index 2472c6647fe..347cbba68ff 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -218,7 +218,7 @@ to_chat(user, SPAN_NOTICE("The pages of [title] have been cut out!")) return if(src.dat) - user << browse("Penned by [author].
    " + "[dat]", "window=book") + user << browse(HTML_SKELETON("Penned by [author].
    " + "[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("Penned by [author].
    " + "[dat]", "window=book") + target_mob << browse(HTML_SKELETON("Penned by [author].
    " + "[dat]"), "window=book") user.setClickCooldown(DEFAULT_QUICK_COOLDOWN) //to prevent spam diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index b6ba998c743..a6e5eaf1202 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -35,7 +35,7 @@ /obj/machinery/librarypubliccomp/attack_hand(var/mob/user) usr.set_machine(src) - var/dat = "Library Visitor\n" // + var/dat = "" switch(screenstate) if(0) dat += {"

    Search Settings


    @@ -63,7 +63,7 @@ dat += "[author][title][category][id]" dat += "
    " dat += "\[Go Back\]
    " - 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 = "[SSatlas.current_map.station_name] Library Management\n" - else - dat = "[SSatlas.current_map.station_name] Library\n" + var/dat = "" switch(screenstate) if(0) // Main Menu @@ -238,7 +233,7 @@ No.
    "} //dat += "Close

    " - 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 = "Scanner Control Interface\n" // + var/dat = "" if(cache) dat += "Data stored in memory.
    " else @@ -442,7 +437,7 @@ dat += " \[Clear Memory\]

    \[Remove Book\]" else dat += "
    " - 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) diff --git a/code/modules/mining/mint.dm b/code/modules/mining/mint.dm index 6286e0fbd5c..c4e8cf3d2fe 100644 --- a/code/modules/mining/mint.dm +++ b/code/modules/mining/mint.dm @@ -107,7 +107,7 @@ dat += "

    In total this machine produced [newCoins] coins." dat += "
    Make coins" - user << browse("[dat]", "window=mint") + user << browse(HTML_SKELETON(dat), "window=mint") /obj/machinery/mineral/mint/Topic(href, href_list) if(..()) diff --git a/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm b/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm index b31d5cffc3a..7653e9fa24f 100644 --- a/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm +++ b/code/modules/mob/abstract/ghost/storyteller/storyteller_verbs.dm @@ -18,7 +18,7 @@ Create Mob
    "} - usr << browse(dat, "window=storytellerpanel;size=210x280") + usr << browse(HTML_SKELETON(dat), "window=storytellerpanel;size=210x280") /mob/abstract/ghost/storyteller/Topic(href, href_list) . = ..() diff --git a/code/modules/mob/abstract/new_player/poll.dm b/code/modules/mob/abstract/new_player/poll.dm index aec0f09446a..b8ff2fe9306 100644 --- a/code/modules/mob/abstract/new_player/poll.dm +++ b/code/modules/mob/abstract/new_player/poll.dm @@ -27,7 +27,7 @@ output += "" - 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 += "" - 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 += "" - 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 += "" - 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) diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm index f1ae1544f80..b5a1110a146 100644 --- a/code/modules/mob/language/language.dm +++ b/code/modules/mob/language/language.dm @@ -390,7 +390,7 @@ if(L.written_style) dat += "You can write in this language on papers by writing \[lang=[L.key]\]YourTextHere\[/lang\].

    " - 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\].

    " - src << browse(dat, "window=checklanguage") + src << browse(HTML_SKELETON(dat), "window=checklanguage") /mob/living/Topic(href, href_list) if(href_list["default_lang"]) diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 2f8e9974196..2c13c15cbf8 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -10,7 +10,7 @@ set desc = "Sets an extended description of your character's features." set category = "IC" - var/HTML = "" + var/HTML = "" HTML += "
    " HTML += "Update Flavour Text
    " HTML += "
    " @@ -44,4 +44,4 @@ HTML += "
    " HTML +="\[Done\]" HTML += "" - src << browse(HTML, "window=flavor_changes;size=430x300") + src << browse(HTML_SKELETON(HTML), "window=flavor_changes;size=430x300") diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 3ad62d6ce02..f9fcd68da43 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -383,7 +383,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( Recall the channel list at any time by calling Radio-Settings under AI Commands.\

    \ " - 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" diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index cf14d86c19e..6f6b93803ef 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -180,8 +180,7 @@ if(!module) module = new /obj/item/robot_module/drone(src) - var/dat = "Drone modules\n" - dat += {" + var/dat = {" Activated Modules
    Module 1: [module_state_1 ? "Activate
    " - src << browse(dat, "window=robotmod") + src << browse(HTML_SKELETON(dat), "window=robotmod") /mob/living/silicon/robot/Topic(href, href_list) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index ba6918952ad..a30de4da411 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -231,7 +231,7 @@ var/synth = (L in speech_synthesizer_langs) dat += "[L.name] ([get_language_prefix()][L.key])[synth ? default_str : null]
    Speech Synthesizer: [synth ? "YES" : "NOT SUPPORTED"]
    [L.desc]

    " - src << browse(dat, "window=checklanguage") + src << browse(HTML_SKELETON(dat), "window=checklanguage") return /mob/living/silicon/proc/toggle_sensor_mode() diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 3dc1aa6737b..3f2c9936272 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -136,7 +136,7 @@ else dat += "
    Headset: Nothing" - user << browse(dat, "window=mob[name];size=325x500") + user << browse(HTML_SKELETON(dat), "window=mob[name];size=325x500") onclose(user, "mob[real_name]") return diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm index 2b301fad411..8eade518aa0 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -73,7 +73,7 @@ return /obj/item/clipboard/attack_self(mob/user as mob) - var/dat = "Clipboard" + var/dat = "" if(haspen) dat += "Remove Pen

    " else @@ -93,7 +93,7 @@ for(var/obj/item/photo/Ph in r_contents) dat += "Remove Rename - [Ph.name]
    " - user << browse(dat, "window=clipboard") + user << browse(HTML_SKELETON_TITLE("Clipboard", dat), "window=clipboard") if(!ui_open) ui_open = TRUE onclose(user, "clipboard") diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm index 5754fd1b0fb..87e1d0c8716 100644 --- a/code/modules/paperwork/folders.dm +++ b/code/modules/paperwork/folders.dm @@ -51,7 +51,7 @@ return /obj/item/folder/attack_self(mob/user as mob) - var/dat = "[name]" + var/dat = "" for(var/obj/item/paper/P in src) dat += "[can_write ? "Write " : ""]Remove Rename - [P.name]
    " @@ -61,7 +61,7 @@ dat += "Remove Rename - [Pb.name]
    " for(var/obj/item/sample/Pf in src) dat += "Remove - [Pf.name]
    " - user << browse(dat, "window=folder") + user << browse(HTML_SKELETON_TITLE(name, dat), "window=folder") onclose(user, "folder") add_fingerprint(usr) return diff --git a/code/modules/projectiles/guns/projectile/dartgun.dm b/code/modules/projectiles/guns/projectile/dartgun.dm index 59fa550f610..fdb7923dec7 100644 --- a/code/modules/projectiles/guns/projectile/dartgun.dm +++ b/code/modules/projectiles/guns/projectile/dartgun.dm @@ -159,7 +159,7 @@ dat += SPAN_WARNING("The dart cartridge is empty!") dat += " \[Eject\]" - 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) diff --git a/code/modules/reagents/Chemistry-Logging.dm b/code/modules/reagents/Chemistry-Logging.dm index 5ed02ccf020..9b3163a7bd9 100644 --- a/code/modules/reagents/Chemistry-Logging.dm +++ b/code/modules/reagents/Chemistry-Logging.dm @@ -24,4 +24,4 @@ GLOBAL_LIST_EMPTY(chemical_reaction_logs) for(var/entry in GLOB.chemical_reaction_logs) html += "[entry]
    " - usr << browse(html, "window=chemlogs") + usr << browse(HTML_SKELETON(html), "window=chemlogs") diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index ea5b77f2770..e18c6c9f26f 100755 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -270,7 +270,7 @@ dat += "
    Current Selection: [currTag ? currTag : "None"]
    " dat += "
    Enter custom location." - 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) diff --git a/code/modules/research/server.dm b/code/modules/research/server.dm index 1157be83125..a173d3dbe77 100644 --- a/code/modules/research/server.dm +++ b/code/modules/research/server.dm @@ -348,7 +348,7 @@ for(var/obj/machinery/r_n_d/server/S in servers) dat += "[S.name] (Transfer)
    " dat += "
    Main Menu" - user << browse("R&D Server Control
    [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 diff --git a/code/modules/research/xenoarchaeology/artifact/artifact_replicator.dm b/code/modules/research/xenoarchaeology/artifact/artifact_replicator.dm index 2022286cd0e..07271c5c112 100644 --- a/code/modules/research/xenoarchaeology/artifact/artifact_replicator.dm +++ b/code/modules/research/xenoarchaeology/artifact/artifact_replicator.dm @@ -124,7 +124,7 @@ for(var/index=1, index<=construction.len, index++) dat += "\[[construction[index]]\]
    " - 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) diff --git a/code/modules/research/xenoarchaeology/tools/tools_locater.dm b/code/modules/research/xenoarchaeology/tools/tools_locater.dm index e850c8c926a..2ca51b958b8 100644 --- a/code/modules/research/xenoarchaeology/tools/tools_locater.dm +++ b/code/modules/research/xenoarchaeology/tools/tools_locater.dm @@ -72,7 +72,7 @@ "} dat += "Close
    " - 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) diff --git a/code/modules/security levels/keycard authentication.dm b/code/modules/security levels/keycard authentication.dm index 9490f81f205..dc72b5c9db7 100644 --- a/code/modules/security levels/keycard authentication.dm +++ b/code/modules/security levels/keycard authentication.dm @@ -87,7 +87,7 @@ dat += "

    Back" - user << browse(dat, "window=keycard_auth;size=500x350") + user << browse(HTML_SKELETON(dat), "window=keycard_auth;size=500x350") return diff --git a/html/changelogs/20652-516-compat.yml b/html/changelogs/20652-516-compat.yml new file mode 100644 index 00000000000..e1484155b4e --- /dev/null +++ b/html/changelogs/20652-516-compat.yml @@ -0,0 +1,6 @@ +author: harry + +delete-after: True + +changes: + - rscadd: "compatibility in 516 for browser elements, like the chat and tgui popups" diff --git a/html/statbrowser.css b/html/statbrowser.css index dc693f42f75..3dd7f78b786 100644 --- a/html/statbrowser.css +++ b/html/statbrowser.css @@ -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; diff --git a/html/statbrowser.js b/html/statbrowser.js index aeb7fd14cdc..362bbb0ba46 100644 --- a/html/statbrowser.js +++ b/html/statbrowser.js @@ -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"); } } diff --git a/tgui/packages/common/storage.js b/tgui/packages/common/storage.js index 4143dcdecb8..7898962b216 100644 --- a/tgui/packages/common/storage.js +++ b/tgui/packages/common/storage.js @@ -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} */ @@ -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() { diff --git a/tgui/packages/tgui-panel/audio/player.js b/tgui/packages/tgui-panel/audio/player.js deleted file mode 100644 index f9dfa41cef9..00000000000 --- a/tgui/packages/tgui-panel/audio/player.js +++ /dev/null @@ -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); - } -} diff --git a/tgui/packages/tgui-panel/audio/player.ts b/tgui/packages/tgui-panel/audio/player.ts new file mode 100644 index 00000000000..861611a8a9b --- /dev/null +++ b/tgui/packages/tgui-panel/audio/player.ts @@ -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); + } +} diff --git a/tgui/packages/tgui-panel/chat/middleware.js b/tgui/packages/tgui-panel/chat/middleware.js index ae5900620a7..ba0cac8772b 100644 --- a/tgui/packages/tgui-panel/chat/middleware.js +++ b/tgui/packages/tgui-panel/chat/middleware.js @@ -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, }); diff --git a/tgui/packages/tgui-panel/chat/renderer.js b/tgui/packages/tgui-panel/chat/renderer.js index 70e39f7305e..53a4c7c2dc3 100644 --- a/tgui/packages/tgui-panel/chat/renderer.js +++ b/tgui/packages/tgui-panel/chat/renderer.js @@ -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 diff --git a/tgui/packages/tgui-panel/settings/SettingsPanel.js b/tgui/packages/tgui-panel/settings/SettingsPanel.js index 2d2de41f121..cd84cea796e 100644 --- a/tgui/packages/tgui-panel/settings/SettingsPanel.js +++ b/tgui/packages/tgui-panel/settings/SettingsPanel.js @@ -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, }) - ) - } + ); + }} /> diff --git a/tgui/packages/tgui/components/Input.js b/tgui/packages/tgui/components/Input.js index 014a95aa4f0..bfc73cf187d 100644 --- a/tgui/packages/tgui/components/Input.js +++ b/tgui/packages/tgui/components/Input.js @@ -145,6 +145,7 @@ export class Input extends Component { { const { className, theme = 'scc', children, ...rest } = props; + document.documentElement.className = `theme-${theme}`; + return (

    0.05, 30%, 10%) * $scrollbar-color-multiplier +); + +// Fancy scrollbar +html, +body { + scrollbar-color: $scrollbar-face $scrollbar-base; +} .Layout, .Layout * { diff --git a/tgui/public/iframe.html b/tgui/public/iframe.html new file mode 100644 index 00000000000..1fa2c984244 --- /dev/null +++ b/tgui/public/iframe.html @@ -0,0 +1,213 @@ + + + + + + + + + + + diff --git a/tgui/public/tgui.html b/tgui/public/tgui.html index e85d29bf14d..0c08ce805e0 100644 --- a/tgui/public/tgui.html +++ b/tgui/public/tgui.html @@ -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) { + "" ); }; - -// Signal tgui that we're ready to receive updates -Byond.sendMessage('ready');