[MIRROR] split tgui html (#10594)

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-04-06 09:39:15 -07:00
committed by GitHub
parent 5383e18a48
commit dd0ab1d10c
29 changed files with 1106 additions and 789 deletions

View File

@@ -26,6 +26,17 @@ SUBSYSTEM_DEF(tgui)
/datum/controller/subsystem/tgui/PreInit()
basehtml = file2text('tgui/public/tgui.html')
// Inject inline helper functions
var/helpers = file2text('tgui/public/helpers.min.js')
helpers = "<script type='text/javascript'>\n[helpers]\n</script>"
basehtml = replacetextEx(basehtml, "<!-- tgui:helpers -->", helpers)
// Inject inline ntos-error styles
var/ntos_error = file2text('tgui/public/ntos-error.min.css')
ntos_error = "<style type='text/css'>\n[ntos_error]\n</style>"
basehtml = replacetextEx(basehtml, "<!-- tgui:ntos-error -->", ntos_error)
basehtml = replacetextEx(basehtml, "<!-- tgui:nt-copyright -->", "Nanotrasen (c) 2284-[CURRENT_STATION_YEAR]")
/datum/controller/subsystem/tgui/Shutdown()

View File

@@ -62,7 +62,7 @@
var/atom/movable/AM = WF.resolve()
if(isnull(AM))
log_debug("DEBUG: HasProximity called without reference on [src].")
attached_device?.HasProximity(T, WEAKREF(AM), old_loc)
attached_device?.HasProximity(T, WF, old_loc)
/obj/item/transfer_valve/Moved(old_loc, direction, forced)
. = ..()

View File

@@ -661,11 +661,11 @@ var/list/global/tank_gauge_cache = list()
SIGNAL_HANDLER
if(isnull(WF))
return
var/atom/movable/AM = WF
var/atom/movable/AM = WF.resolve()
if(isnull(AM))
log_debug("DEBUG: HasProximity called without reference on [src].")
return
assembly?.HasProximity(T, WEAKREF(AM), old_loc)
assembly?.HasProximity(T, WF, old_loc)
/obj/item/tankassemblyproxy/Moved(old_loc, direction, forced)
if(isturf(old_loc))

View File

@@ -79,9 +79,9 @@
log_debug("DEBUG: HasProximity called without reference on [src].")
return
if(a_left)
a_left.HasProximity(T, AM, old_loc)
a_left.HasProximity(T, WF, old_loc)
if(a_right)
a_right.HasProximity(T, AM, old_loc)
a_right.HasProximity(T, WF, old_loc)
/obj/item/assembly_holder/Crossed(atom/movable/AM as mob|obj)
if(AM.is_incorporeal())

View File

@@ -82,10 +82,7 @@
return
if(L.devourable && L.allowmobvore && (src.vore_fullness < src.vore_capacity))
perform_the_nom(src,L,src,src.vore_selected,1)
L |= eaten_mobs
return
else
return
eaten_mobs += L
////////////////////////////PITCHER PLANT////////////////////////////////////////////////

2
tgui/.gitignore vendored
View File

@@ -16,6 +16,8 @@ package-lock.json
/public/.tmp/**/*
/public/**/*
!/public/*.html
!/public/ntos-error.min.css
!/public/helpers.min.js
/coverage
## Previously ignored locations that are kept to avoid confusing git

View File

@@ -8,7 +8,6 @@
.swcrc
/docs
/public
/packages/tgui-polyfill
/packages/tgfont/static
/packages/tgfont/dist
@@ -16,6 +15,7 @@
**/*.yml
**/*.md
## Build artifacts
/public/.tmp/**/*
/public/*.map
# Avoid running on any bundles.
/public/**/*
# Running it on tgui.html is fine, however.
!/public/tgui.html

View File

@@ -1 +1,6 @@
singleQuote: true
overrides:
- files: 'packages/tgui-setup/helpers.js'
options:
trailingComma: es5
arrowParens: always

View File

@@ -1,20 +1,38 @@
#!/usr/bin/env node
<<<<<<< HEAD
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
=======
const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
>>>>>>> cc02a1a056 (split tgui html (#17476))
const relPnpApiPath = '../../../../.pnp.cjs';
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint/bin/eslint.js
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real eslint/bin/eslint.js your application uses
module.exports = absRequire(`eslint/bin/eslint.js`);
module.exports = wrapWithUserWrapper(absRequire(`eslint/bin/eslint.js`));

View File

@@ -1,20 +1,38 @@
#!/usr/bin/env node
<<<<<<< HEAD
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
=======
const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
>>>>>>> cc02a1a056 (split tgui html (#17476))
const relPnpApiPath = '../../../../.pnp.cjs';
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real eslint your application uses
module.exports = absRequire(`eslint`);
module.exports = wrapWithUserWrapper(absRequire(`eslint`));

View File

@@ -1,20 +1,32 @@
#!/usr/bin/env node
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
const relPnpApiPath = '../../../../.pnp.cjs';
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint/use-at-your-own-risk
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real eslint/use-at-your-own-risk your application uses
module.exports = absRequire(`eslint/use-at-your-own-risk`);
module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`));

View File

@@ -1,6 +1,6 @@
{
"name": "eslint",
"version": "8.57.0-sdk",
"version": "8.57.1-sdk",
"main": "./lib/api.js",
"type": "commonjs",
"bin": {

View File

@@ -1,20 +1,38 @@
#!/usr/bin/env node
<<<<<<< HEAD
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
=======
const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
>>>>>>> cc02a1a056 (split tgui html (#17476))
const relPnpApiPath = '../../../../.pnp.cjs';
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require prettier/bin/prettier.cjs
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real prettier/bin/prettier.cjs your application uses
module.exports = absRequire(`prettier/bin/prettier.cjs`);
module.exports = wrapWithUserWrapper(absRequire(`prettier/bin/prettier.cjs`));

View File

@@ -1,20 +1,38 @@
#!/usr/bin/env node
<<<<<<< HEAD
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
=======
const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
>>>>>>> cc02a1a056 (split tgui html (#17476))
const relPnpApiPath = '../../../.pnp.cjs';
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require prettier
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real prettier your application uses
module.exports = absRequire(`prettier`);
module.exports = wrapWithUserWrapper(absRequire(`prettier`));

View File

@@ -1,6 +1,6 @@
{
"name": "prettier",
"version": "3.2.5-sdk",
"version": "3.5.3-sdk",
"main": "./index.cjs",
"type": "commonjs",
"bin": "./bin/prettier.cjs"

View File

@@ -1,20 +1,38 @@
#!/usr/bin/env node
<<<<<<< HEAD
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
=======
const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
>>>>>>> cc02a1a056 (split tgui html (#17476))
const relPnpApiPath = '../../../../.pnp.cjs';
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/bin/tsc
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real typescript/bin/tsc your application uses
module.exports = absRequire(`typescript/bin/tsc`);
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsc`));

View File

@@ -1,20 +1,38 @@
#!/usr/bin/env node
<<<<<<< HEAD
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
=======
const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
>>>>>>> cc02a1a056 (split tgui html (#17476))
const relPnpApiPath = '../../../../.pnp.cjs';
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/bin/tsserver
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real typescript/bin/tsserver your application uses
module.exports = absRequire(`typescript/bin/tsserver`);
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsserver`));

View File

@@ -1,20 +1,38 @@
#!/usr/bin/env node
<<<<<<< HEAD
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
=======
const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
>>>>>>> cc02a1a056 (split tgui html (#17476))
const relPnpApiPath = '../../../../.pnp.cjs';
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/lib/tsc.js
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real typescript/lib/tsc.js your application uses
module.exports = absRequire(`typescript/lib/tsc.js`);
module.exports = wrapWithUserWrapper(absRequire(`typescript/lib/tsc.js`));

View File

@@ -1,15 +1,48 @@
#!/usr/bin/env node
<<<<<<< HEAD
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
=======
const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
>>>>>>> cc02a1a056 (split tgui html (#17476))
const relPnpApiPath = '../../../../.pnp.cjs';
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
<<<<<<< HEAD
const moduleWrapper = (tsserver) => {
=======
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/lib/tsserver.js
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
const moduleWrapper = exports => {
return wrapWithUserWrapper(moduleWrapperFn(exports));
};
const moduleWrapperFn = tsserver => {
>>>>>>> cc02a1a056 (split tgui html (#17476))
if (!process.versions.pnp) {
return tsserver;
}
@@ -261,11 +294,11 @@ const moduleWrapper = (tsserver) => {
return tsserver;
};
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/lib/tsserver.js
require(absPnpApiPath).setup();
}
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
// Ref https://github.com/microsoft/TypeScript/pull/55326
if (major > 5 || (major === 5 && minor >= 5)) {
moduleWrapper(absRequire(`typescript`));
}
// Defer to the real typescript/lib/tsserver.js your application uses

View File

@@ -1,15 +1,48 @@
#!/usr/bin/env node
<<<<<<< HEAD
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
=======
const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
>>>>>>> cc02a1a056 (split tgui html (#17476))
const relPnpApiPath = '../../../../.pnp.cjs';
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
<<<<<<< HEAD
const moduleWrapper = (tsserver) => {
=======
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
const moduleWrapper = exports => {
return wrapWithUserWrapper(moduleWrapperFn(exports));
};
const moduleWrapperFn = tsserver => {
>>>>>>> cc02a1a056 (split tgui html (#17476))
if (!process.versions.pnp) {
return tsserver;
}
@@ -261,11 +294,11 @@ const moduleWrapper = (tsserver) => {
return tsserver;
};
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
require(absPnpApiPath).setup();
}
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
// Ref https://github.com/microsoft/TypeScript/pull/55326
if (major > 5 || (major === 5 && minor >= 5)) {
moduleWrapper(absRequire(`typescript`));
}
// Defer to the real typescript/lib/tsserverlibrary.js your application uses

View File

@@ -1,20 +1,38 @@
#!/usr/bin/env node
<<<<<<< HEAD
const { existsSync } = require(`fs`);
const { createRequire } = require(`module`);
const { resolve } = require(`path`);
=======
const {existsSync} = require(`fs`);
const {createRequire, register} = require(`module`);
const {resolve} = require(`path`);
const {pathToFileURL} = require(`url`);
>>>>>>> cc02a1a056 (split tgui html (#17476))
const relPnpApiPath = '../../../../.pnp.cjs';
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
const absRequire = createRequire(absPnpApiPath);
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript
require(absPnpApiPath).setup();
if (isPnpLoaderEnabled && register) {
register(pathToFileURL(absPnpLoaderPath));
}
}
}
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
? exports => absRequire(absUserWrapperPath)(exports)
: exports => exports;
// Defer to the real typescript your application uses
module.exports = absRequire(`typescript`);
module.exports = wrapWithUserWrapper(absRequire(`typescript`));

View File

@@ -0,0 +1,573 @@
/* eslint-disable */
(function () {
// Utility functions
let hasOwn = Object.prototype.hasOwnProperty;
let assign = function (target) {
for (let i = 1; i < arguments.length; i++) {
let source = arguments[i];
for (let key in source) {
if (hasOwn.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
let parseMetaTag = function (name) {
let content = document.getElementById(name).getAttribute('content');
if (content === '[' + name + ']') {
return null;
}
return content;
};
// BYOND API object
// ------------------------------------------------------
let Byond = (window.Byond = {});
// Expose inlined metadata
Byond.windowId = parseMetaTag('tgui:windowId');
// Backwards compatibility
window.__windowId__ = Byond.windowId;
// Trident engine version
Byond.TRIDENT = (function () {
let groups = navigator.userAgent.match(/Trident\/(\d+).+?;/i);
let majorVersion = groups && groups[1];
return majorVersion ? parseInt(majorVersion, 10) : null;
})();
// Blink engine version
Byond.BLINK = (function () {
let groups = navigator.userAgent.match(/Chrome\/(\d+)\./);
let majorVersion = groups && groups[1];
return majorVersion ? parseInt(majorVersion, 10) : null;
})();
// Basic checks to detect whether this page runs in BYOND
let 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
// && location.pathname.indexOf('/tmp') === 0
// Version constants
Byond.IS_BYOND = isByond;
// Strict mode flag
Byond.strictMode = Boolean(Number(parseMetaTag('tgui:strictMode')));
// Callbacks for asynchronous calls
Byond.__callbacks__ = [];
// Reviver for BYOND JSON
let 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
Byond.call = function (path, params) {
// Not running in BYOND, abort.
if (!isByond) {
return;
}
// Build the URL
let url = (path || '') + '?';
let i = 0;
if (params) {
for (let key in params) {
if (hasOwn.call(params, key)) {
if (i++ > 0) {
url += '&';
}
let value = params[key];
if (value === null || value === undefined) {
value = '';
}
url += encodeURIComponent(key) + '=' + encodeURIComponent(value);
}
}
}
// If we're a Chromium client, just use the fancy method
if (window.cef_to_byond) {
cef_to_byond('byond://' + url);
return;
}
// Perform a standard call via location.href
if (url.length < 2048) {
location.href = 'byond://' + url;
return;
}
// Send an HTTP request to DreamSeeker's HTTP server.
// Allows sending much bigger payloads.
let xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.send();
};
Byond.callAsync = function (path, params) {
if (!window.Promise) {
throw new Error('Async calls require API level of ES2015 or later.');
}
let index = Byond.__callbacks__.length;
let promise = new window.Promise((resolve) => {
Byond.__callbacks__.push(resolve);
});
Byond.call(
path,
assign({}, params, {
callback: 'Byond.__callbacks__[' + index + ']',
})
);
return promise;
};
Byond.topic = function (params) {
return Byond.call('', params);
};
Byond.command = function (command) {
return Byond.call('winset', {
command: command,
});
};
Byond.winget = function (id, propName) {
if (id === null) {
id = '';
}
let isArray = propName instanceof Array;
let isSpecific = propName && propName !== '*' && !isArray;
let promise = Byond.callAsync('winget', {
id: id,
property: (isArray && propName.join(',')) || propName || '*',
});
if (isSpecific) {
promise = promise.then((props) => {
return props[propName];
});
}
return promise;
};
Byond.winset = function (id, propName, propValue) {
if (id === null) {
id = '';
} else if (typeof id === 'object') {
return Byond.call('winset', id);
}
let props = {};
if (typeof propName === 'string') {
props[propName] = propValue;
} else {
assign(props, propName);
}
props.id = id;
return Byond.call('winset', props);
};
Byond.parseJson = function (json) {
try {
return JSON.parse(json, byondJsonReviver);
} catch (err) {
throw new Error('JSON parsing error: ' + (err && err.message));
}
};
let MAX_PACKET_SIZE = 1024;
Byond.sendMessage = function (type, payload) {
let message =
typeof type === 'string' ? { type: type, payload: payload } : type;
// JSON-encode the payload
if (message.payload !== null && message.payload !== undefined) {
message.payload = JSON.stringify(message.payload);
if (!Byond.TRIDENT && message.payload.length > MAX_PACKET_SIZE) {
let chunks = [];
for (
let i = 0, charsLength = message.payload.length;
i < charsLength;
i += MAX_PACKET_SIZE
) {
chunks.push(message.payload.substring(i, i + MAX_PACKET_SIZE));
}
for (let i = 0; i < chunks.length; i++) {
let to_send = chunks[i];
message = {
type: type,
packet: to_send,
packetId: i + 1,
totalPackets: chunks.length,
tgui: 1,
window_id: Byond.windowId,
};
Byond.topic(message);
}
return;
}
}
// Append an identifying header
assign(message, {
tgui: 1,
window_id: Byond.windowId,
});
Byond.topic(message);
};
// This function exists purely for debugging, do not use it in code!
Byond.injectMessage = function (type, payload) {
window.update(JSON.stringify({ type: type, payload: payload }));
};
Byond.subscribe = function (listener) {
window.update.flushQueue(listener);
window.update.listeners.push(listener);
};
Byond.subscribeTo = function (type, listener) {
let _listener = function (_type, payload) {
if (_type === type) {
listener(payload);
}
};
window.update.flushQueue(_listener);
window.update.listeners.push(_listener);
};
// Asset loaders
// ------------------------------------------------------
let RETRY_ATTEMPTS = 5;
let RETRY_WAIT_INITIAL = 500;
let RETRY_WAIT_INCREMENT = 500;
let loadedAssetByUrl = {};
let isStyleSheetLoaded = function (node, url) {
let styleSheet = node.sheet;
if (styleSheet) {
return styleSheet.rules.length > 0;
}
return false;
};
let injectNode = function (node) {
if (!document.body) {
setTimeout(() => {
injectNode(node);
});
return;
}
let refs = document.body.childNodes;
let ref = refs[refs.length - 1];
ref.parentNode.insertBefore(node, ref.nextSibling);
};
let loadAsset = function (options) {
let url = options.url;
let type = options.type;
let sync = options.sync;
let attempt = options.attempt || 0;
if (loadedAssetByUrl[url]) {
return;
}
loadedAssetByUrl[url] = options;
// Generic retry function
let retry = function () {
if (attempt >= RETRY_ATTEMPTS) {
let errorMessage =
'Error: Failed to load the asset ' +
"'" +
url +
"' after several attempts.";
if (type === 'css') {
errorMessage +=
+'\nStylesheet was either not found, ' +
"or you're trying to load an empty stylesheet " +
'that has no CSS rules in it.';
}
throw new Error(errorMessage);
}
setTimeout(
() => {
loadedAssetByUrl[url] = null;
options.attempt += 1;
loadAsset(options);
},
RETRY_WAIT_INITIAL + attempt * RETRY_WAIT_INCREMENT
);
};
// JS specific code
if (type === 'js') {
let node = document.createElement('script');
node.type = 'text/javascript';
node.crossOrigin = 'anonymous';
node.src = url;
if (sync) {
node.defer = true;
} else {
node.async = true;
}
node.onerror = function () {
node.onerror = null;
node.parentNode.removeChild(node);
node = null;
retry();
};
injectNode(node);
return;
}
// CSS specific code
if (type === 'css') {
let node = document.createElement('link');
node.type = 'text/css';
node.rel = 'stylesheet';
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';
}
let 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)) {
// Render the stylesheet
node.media = 'all';
return;
}
removeNodeAndRetry();
};
injectNode(node);
return;
}
};
Byond.loadJs = function (url, sync) {
loadAsset({ url: url, sync: sync, type: 'js' });
};
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) {
let accept = {};
accept[blob.type] = [ext];
let opts = {
suggestedName: filename,
types: [
{
description: 'SS13 file',
accept: accept,
},
],
};
try {
window
.showSaveFilePicker(opts)
.then((fileHandle) => {
fileHandle
.createWritable()
.then((writeableFileHandle) => {
writeableFileHandle
.write(blob)
.then(() => {
writeableFileHandle.close();
})
.catch((e) => {
console.error(e);
});
})
.catch((e) => {
console.error(e);
});
})
.catch((e) => {
console.error(e);
});
} catch (e) {
console.error(e);
}
}
};
// Icon cache
Byond.iconRefMap = {};
})();
// Error handling
// ------------------------------------------------------
window.onerror = function (msg, url, line, col, error) {
window.onerror.errorCount = (window.onerror.errorCount || 0) + 1;
// Proper stacktrace
let stack = error && error.stack;
// Ghetto stacktrace
if (!stack) {
stack = msg + '\n at ' + url + ':' + line;
if (col) {
stack += ':' + col;
}
}
// Augment the stack
stack = window.__augmentStack__(stack, error);
// Print error to the page
if (Byond.strictMode) {
let errorRoot = document.getElementById('FatalError');
let errorStack = document.getElementById('FatalError__stack');
if (errorRoot) {
errorRoot.className = 'FatalError FatalError--visible';
if (window.onerror.__stack__) {
window.onerror.__stack__ += '\n\n' + stack;
} else {
window.onerror.__stack__ = stack;
}
let textProp = 'textContent';
errorStack[textProp] = window.onerror.__stack__;
}
// Set window geometry
let setFatalErrorGeometry = function () {
Byond.winset(Byond.windowId, {
titlebar: true,
'is-visible': true,
'can-resize': true,
});
};
setFatalErrorGeometry();
setInterval(setFatalErrorGeometry, 1000);
}
// Send logs to the game server
if (Byond.strictMode) {
Byond.sendMessage({
type: 'log',
fatal: 1,
message: stack,
});
} else if (window.onerror.errorCount <= 1) {
stack += '\nWindow is in non-strict mode, future errors are suppressed.';
Byond.sendMessage({
type: 'log',
message: stack,
});
}
// Short-circuit further updates
if (Byond.strictMode) {
window.update = function () {};
window.update.queue = [];
}
// Prevent default action
return true;
};
// Catch unhandled promise rejections
window.onunhandledrejection = function (e) {
let msg = 'UnhandledRejection';
if (e.reason) {
msg += ': ' + (e.reason.message || e.reason.description || e.reason);
if (e.reason.stack) {
e.reason.stack = 'UnhandledRejection: ' + e.reason.stack;
}
}
window.onerror(msg, null, null, null, e.reason);
};
// Helper for augmenting stack traces on fatal errors
window.__augmentStack__ = function (stack, error) {
return stack + '\nUser Agent: ' + navigator.userAgent;
};
// Incoming message handling
// ------------------------------------------------------
// Message handler
window.update = function (rawMessage) {
// Push onto the queue (active during initialization)
if (window.update.queueActive) {
window.update.queue.push(rawMessage);
return;
}
// Parse the message
let message = Byond.parseJson(rawMessage);
// Notify listeners
let listeners = window.update.listeners;
for (let i = 0; i < listeners.length; i++) {
listeners[i](message.type, message.payload);
}
};
// Properties and variables of this specific handler
window.update.listeners = [];
window.update.queue = [];
window.update.queueActive = true;
window.update.flushQueue = function (listener) {
// Disable and clear the queue permanently on short delay
if (window.update.queueActive) {
window.update.queueActive = false;
if (window.setTimeout) {
window.setTimeout(() => {
window.update.queue = [];
}, 0);
}
}
// Process queued messages on provided listener
let queue = window.update.queue;
for (let i = 0; i < queue.length; i++) {
let message = Byond.parseJson(queue[i]);
listener(message.type, message.payload);
}
};
window.replaceHtml = function (inline_html) {
let children = document.body.childNodes;
for (let i = 0; i < children.length; i++) {
if (children[i].nodeValue == ' tgui:inline-html-start ') {
while (children[i].nodeValue != ' tgui:inline-html-end ') {
children[i].remove();
}
children[i].remove();
}
}
document.body.insertAdjacentHTML(
'afterbegin',
'<!-- tgui:inline-html-start -->' +
inline_html +
'<!-- tgui:inline-html-end -->'
);
};

View File

@@ -0,0 +1,112 @@
/* Blink polyfill, originally authored on /tg/ by @iamgoofball */
blink {
animation: 1s linear infinite condemned-blink-effect;
}
@keyframes condemned-blink-effect {
0% {
visibility: hidden;
}
50% {
visibility: hidden;
}
100% {
visibility: visible;
}
}
.FatalError {
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 12px;
font-size: 12px;
font-family: Consolas, monospace;
color: #ffffff;
background-color: #0000dd;
z-index: 1000;
overflow: hidden;
text-align: center;
}
.FatalError--visible {
display: block !important;
}
.FatalError__logo {
display: inline-block;
text-align: left;
font-size: 10px;
line-height: 12px;
position: relative;
margin: 16px;
top: 0;
left: 0;
animation:
FatalError__rainbow 2s linear infinite alternate,
FatalError__shadow 4s linear infinite alternate,
FatalError__tfmX 3s infinite alternate,
FatalError__tfmY 4s infinite alternate;
white-space: pre;
}
.FatalError__header {
margin-top: 12px;
}
.FatalError__stack {
text-align: left;
white-space: pre-wrap;
word-break: break-all;
margin-top: 24px;
margin-bottom: 24px;
}
.FatalError__footer {
margin-bottom: 24px;
}
@keyframes FatalError__rainbow {
0% {
color: #ff0;
}
50% {
color: #0ff;
}
100% {
color: #f0f;
}
}
@keyframes FatalError__shadow {
0% {
left: -2px;
text-shadow: 4px 0 #f0f;
}
50% {
left: 0px;
text-shadow: 0px 0 #0ff;
}
100% {
left: 2px;
text-shadow: -4px 0 #ff0;
}
}
@keyframes FatalError__tfmX {
0% {
left: 15px;
}
100% {
left: -15px;
}
}
@keyframes FatalError__tfmY {
100% {
top: -15px;
}
}

View File

@@ -0,0 +1,14 @@
{
"name": "tgui-setup",
"version": "0.0.1",
"description": "Minifies assets to inject into tgui.html",
"private": true,
"scripts": {
"build:helpers": "terser helpers.js -f ascii_only,comments=false -o ../../public/helpers.min.js",
"build:style": "cleancss -o ../../public/ntos-error.min.css ntos-error.css"
},
"dependencies": {
"clean-css-cli": "^5.6.3",
"terser": "^5.39.0"
}
}

View File

@@ -1,6 +1,12 @@
import { useBackend } from 'tgui/backend';
import { Window } from 'tgui/layouts';
import { Button, LabeledList, Section, Slider } from 'tgui-core/components';
import {
Button,
LabeledList,
Section,
Slider,
Stack,
} from 'tgui-core/components';
type Data = {
volume_channels: { key; val: number }[];
@@ -12,26 +18,34 @@ export const VolumePanel = (props) => {
const { volume_channels } = data;
return (
<Window width={350} height={600}>
<Window width={550} height={600}>
<Window.Content>
<Section title="Volume Levels">
<Section title="Volume Levels" fill scrollable>
<LabeledList>
{Object.keys(volume_channels).map((key) => (
<LabeledList.Item label={key} key={key}>
<Slider
width="88%"
minValue={0}
maxValue={200}
value={volume_channels[key] * 100}
onChange={(e, val) =>
act('adjust_volume', { channel: key, vol: val / 100 })
}
/>
<Button
ml={1}
icon="undo"
onClick={() => act('adjust_volume', { channel: key, vol: 1 })}
/>
<Stack>
<Stack.Item grow>
<Slider
ml="1rem"
minValue={0}
maxValue={200}
value={volume_channels[key] * 100}
onChange={(e, val) =>
act('adjust_volume', { channel: key, vol: val / 100 })
}
/>
</Stack.Item>
<Stack.Item>
<Button
ml="1rem"
icon="undo"
onClick={() =>
act('adjust_volume', { channel: key, vol: 1 })
}
/>
</Stack.Item>
</Stack>
</LabeledList.Item>
))}
</LabeledList>

1
tgui/public/helpers.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
tgui/public/ntos-error.min.css vendored Normal file
View File

@@ -0,0 +1 @@
blink{animation:1s linear infinite condemned-blink-effect}@keyframes condemned-blink-effect{0%{visibility:hidden}50%{visibility:hidden}100%{visibility:visible}}.FatalError{display:none;position:absolute;top:0;left:0;right:0;bottom:0;padding:12px;font-size:12px;font-family:Consolas,monospace;color:#fff;background-color:#00d;z-index:1000;overflow:hidden;text-align:center}.FatalError--visible{display:block!important}.FatalError__logo{display:inline-block;text-align:left;font-size:10px;line-height:12px;position:relative;margin:16px;top:0;left:0;animation:FatalError__rainbow 2s linear infinite alternate,FatalError__shadow 4s linear infinite alternate,FatalError__tfmX 3s infinite alternate,FatalError__tfmY 4s infinite alternate;white-space:pre}.FatalError__header{margin-top:12px}.FatalError__stack{text-align:left;white-space:pre-wrap;word-break:break-all;margin-top:24px;margin-bottom:24px}.FatalError__footer{margin-bottom:24px}@keyframes FatalError__rainbow{0%{color:#ff0}50%{color:#0ff}100%{color:#f0f}}@keyframes FatalError__shadow{0%{left:-2px;text-shadow:4px 0 #f0f}50%{left:0;text-shadow:0 0 #0ff}100%{left:2px;text-shadow:-4px 0 #ff0}}@keyframes FatalError__tfmX{0%{left:15px}100%{left:-15px}}@keyframes FatalError__tfmY{100%{top:-15px}}

View File

@@ -1,730 +1,63 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta charset="utf-8">
<!-- Inlined metadata -->
<meta id="tgui:windowId" content="[tgui:windowId]">
<meta id="tgui:strictMode" content="[tgui:strictMode]">
<!-- Early setup -->
<script type="text/javascript">
(function () {
// Utility functions
var hasOwn = Object.prototype.hasOwnProperty;
var assign = function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (hasOwn.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var parseMetaTag = function (name) {
var content = document.getElementById(name).getAttribute('content');
if (content === '[' + name + ']') {
return null;
}
return content;
};
// BYOND API object
// ------------------------------------------------------
var Byond = window.Byond = {};
// Expose inlined metadata
Byond.windowId = parseMetaTag('tgui:windowId');
// Backwards compatibility
window.__windowId__ = Byond.windowId;
// Trident engine version
Byond.TRIDENT = (function () {
var groups = navigator.userAgent.match(/Trident\/(\d+).+?;/i);
var majorVersion = groups && groups[1];
return majorVersion
? parseInt(majorVersion, 10)
: 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 || 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
//&& location.pathname.indexOf('/tmp') === 0
// Version constants
Byond.IS_BYOND = isByond;
// Strict mode flag
Byond.strictMode = Boolean(Number(parseMetaTag('tgui:strictMode')));
// Callbacks for asynchronous calls
Byond.__callbacks__ = [];
// Reviver for BYOND JSON
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
Byond.call = function (path, params) {
// Not running in BYOND, abort.
if (!isByond) {
return;
}
// Build the URL
var url = (path || '') + '?';
var i = 0;
if (params) {
for (var key in params) {
if (hasOwn.call(params, key)) {
if (i++ > 0) {
url += '&';
}
var value = params[key];
if (value === null || value === undefined) {
value = '';
}
url += encodeURIComponent(key)
+ '=' + encodeURIComponent(value)
}
}
}
// If we're a Chromium client, just use the fancy method
if (window.cef_to_byond) {
cef_to_byond('byond://' + url);
return;
}
// Perform a standard call via location.href
if (url.length < 2048) {
location.href = 'byond://' + url;
return;
}
// Send an HTTP request to DreamSeeker's HTTP server.
// Allows sending much bigger payloads.
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.send();
};
Byond.callAsync = function (path, params) {
if (!window.Promise) {
throw new Error('Async calls require API level of ES2015 or later.');
}
var index = Byond.__callbacks__.length;
var promise = new window.Promise(function (resolve) {
Byond.__callbacks__.push(resolve);
});
Byond.call(path, assign({}, params, {
callback: 'Byond.__callbacks__[' + index + ']',
}));
return promise;
};
Byond.topic = function (params) {
return Byond.call('', params);
};
Byond.command = function (command) {
return Byond.call('winset', {
command: command,
});
};
Byond.winget = function (id, propName) {
if (id === null) {
id = '';
}
var isArray = propName instanceof Array;
var isSpecific = propName && propName !== '*' && !isArray;
var promise = Byond.callAsync('winget', {
id: id,
property: isArray && propName.join(',') || propName || '*',
});
if (isSpecific) {
promise = promise.then(function (props) {
return props[propName];
});
}
return promise;
};
Byond.winset = function (id, propName, propValue) {
if (id === null) {
id = '';
}
else if (typeof id === 'object') {
return Byond.call('winset', id);
}
var props = {};
if (typeof propName === 'string') {
props[propName] = propValue;
}
else {
assign(props, propName);
}
props.id = id;
return Byond.call('winset', props);
};
Byond.parseJson = function (json) {
try {
return JSON.parse(json, byondJsonReviver);
}
catch (err) {
throw new Error('JSON parsing error: ' + (err && err.message));
}
};
Byond.sendMessage = function (type, payload) {
var message = typeof type === 'string'
? { type: type, payload: payload }
: type;
// JSON-encode the payload
if (message.payload !== null && message.payload !== undefined) {
message.payload = JSON.stringify(message.payload);
if(!Byond.TRIDENT && message.payload.length > 1024) {
var chunks = [];
for(var i = 0, charsLength = message.payload.length; i < charsLength; i += 1024) {
chunks.push(message.payload.substring(i, i + 1024))
}
for(var i = 0; i < chunks.length; i++) {
var to_send = chunks[i]
message = { type: type, packet: to_send, packetId: i + 1, totalPackets: chunks.length, tgui: 1, window_id: Byond.windowId };
Byond.topic(message);
}
return;
}
}
// Append an identifying header
assign(message, {
tgui: 1,
window_id: Byond.windowId,
});
Byond.topic(message);
};
// This function exists purely for debugging, do not use it in code!
Byond.injectMessage = function (type, payload) {
window.update(JSON.stringify({ type: type, payload: payload }));
};
Byond.subscribe = function (listener) {
window.update.flushQueue(listener);
window.update.listeners.push(listener);
};
Byond.subscribeTo = function (type, listener) {
var _listener = function (_type, payload) {
if (_type === type) {
listener(payload);
}
};
window.update.flushQueue(_listener);
window.update.listeners.push(_listener);
};
// Asset loaders
// ------------------------------------------------------
var RETRY_ATTEMPTS = 5;
var RETRY_WAIT_INITIAL = 500;
var RETRY_WAIT_INCREMENT = 500;
var loadedAssetByUrl = {};
var isStyleSheetLoaded = function (node, url) {
var styleSheet = node.sheet;
if (styleSheet) {
return styleSheet.rules.length > 0;
}
return false;
};
var injectNode = function (node) {
if (!document.body) {
setTimeout(function () {
injectNode(node);
});
return;
}
var refs = document.body.childNodes;
var ref = refs[refs.length - 1];
ref.parentNode.insertBefore(node, ref.nextSibling);
};
var loadAsset = function (options) {
var url = options.url;
var type = options.type;
var sync = options.sync;
var attempt = options.attempt || 0;
if (loadedAssetByUrl[url]) {
return;
}
loadedAssetByUrl[url] = options;
// Generic retry function
var retry = function () {
if (attempt >= RETRY_ATTEMPTS) {
var errorMessage = "Error: Failed to load the asset "
+ "'" + url + "' after several attempts.";
if (type === 'css') {
errorMessage += + "\nStylesheet was either not found, "
+ "or you're trying to load an empty stylesheet "
+ "that has no CSS rules in it.";
}
throw new Error(errorMessage);
}
setTimeout(function () {
loadedAssetByUrl[url] = null;
options.attempt += 1;
loadAsset(options);
}, RETRY_WAIT_INITIAL + attempt * RETRY_WAIT_INCREMENT);
};
// JS specific code
if (type === 'js') {
var node = document.createElement('script');
node.type = 'text/javascript';
node.crossOrigin = 'anonymous';
node.src = url;
if (sync) {
node.defer = true;
}
else {
node.async = true;
}
node.onerror = function () {
node.onerror = null;
node.parentNode.removeChild(node);
node = null;
retry();
};
injectNode(node);
return;
}
// CSS specific code
if (type === 'css') {
var node = document.createElement('link');
node.type = 'text/css';
node.rel = 'stylesheet';
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)) {
// Render the stylesheet
node.media = 'all';
return;
}
// Try again
removeNodeAndRetry();
};
injectNode(node);
return;
}
};
Byond.loadJs = function (url, sync) {
loadAsset({ url: url, sync: sync, type: 'js' });
};
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
// ------------------------------------------------------
window.onerror = function (msg, url, line, col, error) {
window.onerror.errorCount = (window.onerror.errorCount || 0) + 1;
// Proper stacktrace
var stack = error && error.stack;
// Ghetto stacktrace
if (!stack) {
stack = msg + '\n at ' + url + ':' + line;
if (col) {
stack += ':' + col;
}
}
// Augment the stack
stack = window.__augmentStack__(stack, error);
// Print error to the page
if (Byond.strictMode) {
var errorRoot = document.getElementById('FatalError');
var errorStack = document.getElementById('FatalError__stack');
if (errorRoot) {
errorRoot.className = 'FatalError FatalError--visible';
if (window.onerror.__stack__) {
window.onerror.__stack__ += '\n\n' + stack;
}
else {
window.onerror.__stack__ = stack;
}
var textProp = 'textContent';
errorStack[textProp] = window.onerror.__stack__;
}
// Set window geometry
var setFatalErrorGeometry = function () {
Byond.winset(Byond.windowId, {
titlebar: true,
'is-visible': true,
'can-resize': true,
});
};
setFatalErrorGeometry();
setInterval(setFatalErrorGeometry, 1000);
}
// Send logs to the game server
if (Byond.strictMode) {
Byond.sendMessage({
type: 'log',
fatal: 1,
message: stack,
});
}
else if (window.onerror.errorCount <= 1) {
stack += '\nWindow is in non-strict mode, future errors are suppressed.';
Byond.sendMessage({
type: 'log',
message: stack,
});
}
// Short-circuit further updates
if (Byond.strictMode) {
window.update = function () { };
window.update.queue = [];
}
// Prevent default action
return true;
};
// Catch unhandled promise rejections
window.onunhandledrejection = function (e) {
var msg = 'UnhandledRejection';
if (e.reason) {
msg += ': ' + (e.reason.message || e.reason.description || e.reason);
if (e.reason.stack) {
e.reason.stack = 'UnhandledRejection: ' + e.reason.stack;
}
}
window.onerror(msg, null, null, null, e.reason);
};
// Helper for augmenting stack traces on fatal errors
window.__augmentStack__ = function (stack, error) {
return stack + '\nUser Agent: ' + navigator.userAgent;
};
// Incoming message handling
// ------------------------------------------------------
// Message handler
window.update = function (rawMessage) {
// Push onto the queue (active during initialization)
if (window.update.queueActive) {
window.update.queue.push(rawMessage);
return;
}
// Parse the message
var message = Byond.parseJson(rawMessage);
// Notify listeners
var listeners = window.update.listeners;
for (var i = 0; i < listeners.length; i++) {
listeners[i](message.type, message.payload);
}
};
// Properties and variables of this specific handler
window.update.listeners = [];
window.update.queue = [];
window.update.queueActive = true;
window.update.flushQueue = function (listener) {
// Disable and clear the queue permanently on short delay
if (window.update.queueActive) {
window.update.queueActive = false;
if (window.setTimeout) {
window.setTimeout(function () {
window.update.queue = [];
}, 0);
}
}
// Process queued messages on provided listener
var queue = window.update.queue;
for (var i = 0; i < queue.length; i++) {
var message = Byond.parseJson(queue[i]);
listener(message.type, message.payload);
}
};
window.replaceHtml = function (inline_html) {
var children = document.body.childNodes;
for (var i = 0; i < children.length; i++) {
if (children[i].nodeValue == " tgui:inline-html-start ") {
while (children[i].nodeValue != " tgui:inline-html-end ") {
children[i].remove();
}
children[i].remove();
}
}
document.body.insertAdjacentHTML(
"afterbegin",
"<!-- tgui:inline-html-start -->"
+ inline_html
+ "<!-- tgui:inline-html-end -->"
);
};
</script>
<style>
/* Blink polyfill, originally authored on /tg/ by @iamgoofball */
blink {
animation: 1s linear infinite condemned-blink-effect;
}
@keyframes condemned-blink-effect {
0% {
visibility: hidden;
}
50% {
visibility: hidden;
}
100% {
visibility: visible;
}
}
.FatalError {
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 12px;
font-size: 12px;
font-family: Consolas, monospace;
color: #ffffff;
background-color: #0000dd;
z-index: 1000;
overflow: hidden;
text-align: center;
}
.FatalError--visible {
display: block !important;
}
.FatalError__logo {
display: inline-block;
text-align: left;
font-size: 10px;
line-height: 12px;
position: relative;
margin: 16px;
top: 0;
left: 0;
animation:
FatalError__rainbow 2s linear infinite alternate,
FatalError__shadow 4s linear infinite alternate,
FatalError__tfmX 3s infinite alternate,
FatalError__tfmY 4s infinite alternate;
white-space: pre;
}
.FatalError__header {
margin-top: 12px;
}
.FatalError__stack {
text-align: left;
white-space: pre-wrap;
word-break: break-all;
margin-top: 24px;
margin-bottom: 24px;
}
.FatalError__footer {
margin-bottom: 24px;
}
@keyframes FatalError__rainbow {
0% {
color: #ff0;
}
50% {
color: #0ff;
}
100% {
color: #f0f;
}
}
@keyframes FatalError__shadow {
0% {
left: -2px;
text-shadow: 4px 0 #f0f;
}
50% {
left: 0px;
text-shadow: 0px 0 #0ff;
}
100% {
left: 2px;
text-shadow: -4px 0 #ff0;
}
}
@keyframes FatalError__tfmX {
0% {
left: 15px;
}
100% {
left: -15px;
}
}
@keyframes FatalError__tfmY {
100% {
top: -15px;
}
}
</style>
<!-- tgui:inline-css -->
</head>
<body>
<!-- tgui:assets -->
<!-- tgui:inline-html-start -->
<!-- tgui:inline-html -->
<!-- tgui:inline-html-end -->
<!-- tgui:inline-js -->
<!-- Root element for tgui interfaces -->
<div id="react-root"></div>
<!-- Fatal error container -->
<div id="FatalError" class="FatalError">
<div class="FatalError__logo">
ooooo ooo . .oooooo. .oooooo..o
`888b. `8' .o8 d8P' `Y8b d8P' `Y8
8 `88b. 8 .o888oo 888 888 Y88bo.
8 `88b. 8 888 888 888 `"Y8888o.
8 `88b.8 888 888 888 `"Y88b
8 `888 888 . `88b d88' oo .d8P
o8o `8 "888" `Y8bood8P' 8""88888P'
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="utf-8" />
<!-- Inlined metadata -->
<meta id="tgui:windowId" content="[tgui:windowId]" />
<meta id="tgui:strictMode" content="[tgui:strictMode]" />
<!-- Early setup -->
<!-- tgui:helpers -->
<!-- tgui:ntos-error -->
<!-- tgui:inline-css -->
</head>
<body>
<!-- tgui:assets -->
<!-- tgui:inline-html-start -->
<!-- tgui:inline-html -->
<!-- tgui:inline-html-end -->
<!-- tgui:inline-js -->
<!-- Root element for tgui interfaces -->
<div id="react-root"></div>
<!-- Fatal error container -->
<div id="FatalError" class="FatalError">
<!-- prettier-ignore -->
<div class="FatalError__logo">
ooooo ooo . .oooooo. .oooooo..o
`888b. `8' .o8 d8P' `Y8b d8P' `Y8
8 `88b. 8 .o888oo 888 888 Y88bo.
8 `88b. 8 888 888 888 `"Y8888o.
8 `88b.8 888 888 888 `"Y88b
8 `888 888 . `88b d88' oo .d8P
o8o `8 "888" `Y8bood8P' 8""88888P'
</div>
<marquee class="FatalError__header">
A fatal exception has occurred at 002B:C562F1B7 in TGUI. The current
application will be terminated. Send the copy of the following stack
trace to an authorized Nanotrasen incident handler at
https://github.com/VOREStation/VOREStation. Thank you for your
cooperation.
</marquee>
<div id="FatalError__stack" class="FatalError__stack"></div>
<div class="FatalError__footer">
<!-- tgui:nt-copyright -->
</div>
</div>
<marquee class="FatalError__header">
A fatal exception has occurred at 002B:C562F1B7 in TGUI.
The current application will be terminated.
Send the copy of the following stack trace to an authorized
Nanotrasen incident handler at https://github.com/VOREStation/VOREStation.
Thank you for your cooperation.
</marquee>
<div id="FatalError__stack" class="FatalError__stack"></div>
<div class="FatalError__footer">
<!-- tgui:nt-copyright -->
</div>
</div>
<noscript>
<div class="NoticeBox">
<div>Javascript is required in order to use this interface.</div>
<div>Please enable Javascript and restart the game.</div>
</div>
</noscript>
<script>
// Signal tgui that we're ready to receive updates
Byond.sendMessage('ready');
</script>
</body>
<noscript>
<div class="NoticeBox">
<div>Javascript is required in order to use this interface.</div>
<div>Please enable Javascript and restart the game.</div>
</div>
</noscript>
<script>
// Signal tgui that we're ready to receive updates
Byond.sendMessage('ready');
</script>
</body>
</html>

View File

@@ -6210,7 +6210,7 @@ __metadata:
languageName: node
linkType: hard
"chokidar@npm:^3.4.1":
"chokidar@npm:^3.4.1, chokidar@npm:^3.5.2":
version: 3.6.0
resolution: "chokidar@npm:3.6.0"
dependencies:
@@ -6316,6 +6316,20 @@ __metadata:
languageName: node
linkType: hard
"clean-css-cli@npm:^5.6.3":
version: 5.6.3
resolution: "clean-css-cli@npm:5.6.3"
dependencies:
chokidar: "npm:^3.5.2"
clean-css: "npm:^5.3.3"
commander: "npm:7.x"
glob: "npm:^7.1.6"
bin:
cleancss: bin/cleancss
checksum: 10c0/678562c4b3ba4bd150cee0fd838b0cb9b645e3260dd5ff8b9e1456215e86bb6389e3a7f542194c125eaf700034e60ab14570837d7e36663bd5a283904f86f656
languageName: node
linkType: hard
"clean-css@npm:^4.2.3":
version: 4.2.4
resolution: "clean-css@npm:4.2.4"
@@ -6325,6 +6339,15 @@ __metadata:
languageName: node
linkType: hard
"clean-css@npm:^5.3.3":
version: 5.3.3
resolution: "clean-css@npm:5.3.3"
dependencies:
source-map: "npm:~0.6.0"
checksum: 10c0/381de7523e23f3762eb180e327dcc0cedafaf8cb1cd8c26b7cc1fc56e0829a92e734729c4f955394d65ed72fb62f82d8baf78af34b33b8a7d41ebad2accdd6fb
languageName: node
linkType: hard
"clean-stack@npm:^2.0.0":
version: 2.2.0
resolution: "clean-stack@npm:2.2.0"
@@ -6501,6 +6524,13 @@ __metadata:
languageName: node
linkType: hard
"commander@npm:7.x, commander@npm:^7.2.0":
version: 7.2.0
resolution: "commander@npm:7.2.0"
checksum: 10c0/8d690ff13b0356df7e0ebbe6c59b4712f754f4b724d4f473d3cc5b3fdcf978e3a5dc3078717858a2ceb50b0f84d0660a7f22a96cdc50fb877d0c9bb31593d23a
languageName: node
linkType: hard
"commander@npm:^10.0.1":
version: 10.0.1
resolution: "commander@npm:10.0.1"
@@ -6522,13 +6552,6 @@ __metadata:
languageName: node
linkType: hard
"commander@npm:^7.2.0":
version: 7.2.0
resolution: "commander@npm:7.2.0"
checksum: 10c0/8d690ff13b0356df7e0ebbe6c59b4712f754f4b724d4f473d3cc5b3fdcf978e3a5dc3078717858a2ceb50b0f84d0660a7f22a96cdc50fb877d0c9bb31593d23a
languageName: node
linkType: hard
"common-tags@npm:^1.8.0":
version: 1.8.2
resolution: "common-tags@npm:1.8.2"
@@ -19060,7 +19083,7 @@ __metadata:
languageName: node
linkType: hard
"terser@npm:^5.3.4, terser@npm:^5.31.1":
"terser@npm:^5.3.4, terser@npm:^5.31.1, terser@npm:^5.39.0":
version: 5.39.0
resolution: "terser@npm:5.39.0"
dependencies:
@@ -19171,6 +19194,15 @@ __metadata:
languageName: unknown
linkType: soft
"tgui-setup@workspace:packages/tgui-setup":
version: 0.0.0-use.local
resolution: "tgui-setup@workspace:packages/tgui-setup"
dependencies:
clean-css-cli: "npm:^5.6.3"
terser: "npm:^5.39.0"
languageName: unknown
linkType: soft
"tgui-workspace@workspace:.":
version: 0.0.0-use.local
resolution: "tgui-workspace@workspace:."