diff --git a/code/__defines/text.dm b/code/__defines/text.dm index 6885d859cb..a638832cb9 100644 --- a/code/__defines/text.dm +++ b/code/__defines/text.dm @@ -13,3 +13,5 @@ #define STRIP_HTML_SIMPLE(text, limit) (GLOB.angular_brackets.Replace(copytext(text, 1, limit), "")) #define MAX_MESSAGE_CHUNKS 130 + +#define MAX_TGUI_INPUT (MAX_MESSAGE_CHUNKS * 1024) diff --git a/code/modules/tgui_input/alert.dm b/code/modules/tgui_input/alert.dm index 80a80228ac..80ef7bd98d 100644 --- a/code/modules/tgui_input/alert.dm +++ b/code/modules/tgui_input/alert.dm @@ -10,7 +10,7 @@ * * timeout - The timeout of the alert, after which the modal will close and qdel itself. Set to zero for no timeout. * * autofocus - The bool that controls if this alert should grab window focus. */ -/proc/tgui_alert(mob/user, message = "", title, list/buttons = list("Ok"), timeout = 0, autofocus = TRUE, strict_byond = FALSE) +/proc/tgui_alert(mob/user, message = "", title, list/buttons = list("Ok"), timeout = 0, autofocus = TRUE, strict_byond = FALSE, ui_state = GLOB.tgui_always_state) if (istext(buttons)) stack_trace("tgui_alert() received text for buttons instead of list") return @@ -24,7 +24,11 @@ var/client/client = user user = client.mob else - return + return null + + if(isnull(user.client)) + return null + // A gentle nudge - you should not be using TGUI alert for anything other than a simple message. //if(length(buttons) > 3) // log_tgui(user, "Error: TGUI Alert initiated with too many buttons. Use a list.", "TguiAlert") @@ -37,7 +41,7 @@ if(length(buttons) == 3) return alert(user, message, title, buttons[1], buttons[2], buttons[3]) - var/datum/tgui_alert/alert = new(user, message, title, buttons, timeout, autofocus) + var/datum/tgui_alert/alert = new(user, message, title, buttons, timeout, autofocus, ui_state) alert.tgui_interact(user) alert.wait() if (alert) @@ -67,12 +71,15 @@ var/autofocus /// Boolean field describing if the tgui_modal was closed by the user. var/closed + /// The TGUI UI state that will be returned in ui_state(). Default: always_state + var/datum/tgui_state/state -/datum/tgui_alert/New(mob/user, message, title, list/buttons, timeout, autofocus) +/datum/tgui_alert/New(mob/user, message, title, list/buttons, timeout, autofocus, ui_state) src.autofocus = autofocus src.buttons = buttons.Copy() src.message = message src.title = title + src.state = ui_state if (timeout) src.timeout = timeout start_time = world.time @@ -80,6 +87,7 @@ /datum/tgui_alert/Destroy(force, ...) SStgui.close_uis(src) + state = null QDEL_NULL(buttons) . = ..() @@ -102,7 +110,7 @@ closed = TRUE /datum/tgui_alert/tgui_state(mob/user) - return GLOB.tgui_always_state + return state /datum/tgui_alert/tgui_static_data(mob/user) var/list/data = list() @@ -151,7 +159,7 @@ * * callback - The callback to be invoked when a choice is made. * * timeout - The timeout of the alert, after which the modal will close and qdel itself. Disabled by default, can be set to seconds otherwise. */ -/proc/tgui_alert_async(mob/user, message = "", title, list/buttons = list("Ok"), datum/callback/callback, timeout = 0, autofocus = TRUE) +/proc/tgui_alert_async(mob/user, message = "", title, list/buttons = list("Ok"), datum/callback/callback, timeout = 0, autofocus = TRUE, ui_state = GLOB.tgui_always_state) if (istext(buttons)) stack_trace("tgui_alert() received text for buttons instead of list") return @@ -165,8 +173,12 @@ var/client/client = user user = client.mob else - return - var/datum/tgui_alert/async/alert = new(user, message, title, buttons, callback, timeout, autofocus) + return null + + if(isnull(user.client)) + return null + + var/datum/tgui_alert/async/alert = new(user, message, title, buttons, callback, timeout, autofocus, ui_state) alert.tgui_interact(user) /** @@ -178,8 +190,8 @@ /// The callback to be invoked by the tgui_modal upon having a choice made. var/datum/callback/callback -/datum/tgui_alert/async/New(mob/user, message, title, list/buttons, callback, timeout, autofocus) - ..(user, message, title, buttons, timeout, autofocus) +/datum/tgui_alert/async/New(mob/user, message, title, list/buttons, callback, timeout, autofocus, ui_state) + ..(user, message, title, buttons, timeout, autofocus, ui_state) src.callback = callback /datum/tgui_alert/async/Destroy(force, ...) diff --git a/code/modules/tgui_input/color.dm b/code/modules/tgui_input/color.dm index 23c1122a92..2dc2846898 100644 --- a/code/modules/tgui_input/color.dm +++ b/code/modules/tgui_input/color.dm @@ -8,7 +8,7 @@ * * timeout - The timeout of the picker, after which the modal will close and qdel itself. Set to zero for no timeout. * * autofocus - The bool that controls if this picker should grab window focus. */ -/proc/tgui_color_picker(mob/user, message, title, default = "#000000", timeout = 0, autofocus = TRUE) +/proc/tgui_color_picker(mob/user, message, title, default = "#000000", timeout = 0, autofocus = TRUE, ui_state = GLOB.tgui_always_state) if (!user) user = usr if (!istype(user)) @@ -16,11 +16,15 @@ var/client/client = user user = client.mob else - return + return null + + if(isnull(user.client)) + return null + // Client does NOT have tgui_input on: Returns regular input if(!user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_mode)) return input(user, message, title, default) as color|null - var/datum/tgui_color_picker/picker = new(user, message, title, default, timeout, autofocus) + var/datum/tgui_color_picker/picker = new(user, message, title, default, timeout, autofocus, ui_state) picker.tgui_interact(user) picker.wait() if (picker) @@ -51,12 +55,15 @@ var/closed /// The user's presets var/preset_colors + /// The TGUI UI state that will be returned in ui_state(). Default: always_state + var/datum/tgui_state/state -/datum/tgui_color_picker/New(mob/user, message, title, default, timeout, autofocus) +/datum/tgui_color_picker/New(mob/user, message, title, default, timeout, autofocus, ui_state) src.autofocus = autofocus src.title = title src.default = default src.message = message + src.state = ui_state if (timeout) src.timeout = timeout start_time = world.time @@ -66,6 +73,7 @@ /datum/tgui_color_picker/Destroy(force) SStgui.close_uis(src) + state = null . = ..() /** @@ -90,7 +98,7 @@ closed = TRUE /datum/tgui_color_picker/tgui_state(mob/user) - return GLOB.tgui_always_state + return state /datum/tgui_color_picker/tgui_static_data(mob/user) . = list() diff --git a/code/modules/tgui_input/text.dm b/code/modules/tgui_input/text.dm index b1b02e2052..a31f7d2891 100644 --- a/code/modules/tgui_input/text.dm +++ b/code/modules/tgui_input/text.dm @@ -15,7 +15,7 @@ * * encode - Toggling this determines if input is filtered via html_encode. Setting this to FALSE gives raw input. * * timeout - The timeout of the textbox, after which the modal will close and qdel itself. Set to zero for no timeout. */ -/proc/tgui_input_text(mob/user, message = "", title = "Text Input", default, max_length = 130000, multiline = FALSE, encode = FALSE, timeout = 0, prevent_enter = FALSE) // 130k limit due to chunking limit... if we need longer that needs fixing +/proc/tgui_input_text(mob/user, message = "", title = "Text Input", default, max_length = MAX_TGUI_INPUT, multiline = FALSE, encode = FALSE, timeout = 0, prevent_enter = FALSE, ui_state = GLOB.tgui_always_state) // 130k limit due to chunking limit... if we need longer that needs fixing if (!user) user = usr if (!istype(user)) @@ -23,7 +23,7 @@ var/client/client = user user = client.mob else - return + return null if(isnull(user.client)) return null @@ -41,7 +41,7 @@ else return input(user, message, title, default) as text|null - var/datum/tgui_input_text/text_input = new(user, message, title, default, max_length, multiline, encode, timeout) + var/datum/tgui_input_text/text_input = new(user, message, title, default, max_length, multiline, encode, timeout, ui_state) text_input.tgui_interact(user) text_input.wait() if (text_input) @@ -75,14 +75,17 @@ var/timeout /// The title of the TGUI window var/title + /// The TGUI UI state that will be returned in ui_state(). Default: always_state + var/datum/tgui_state/state -/datum/tgui_input_text/New(mob/user, message, title, default, max_length, multiline, encode, timeout) +/datum/tgui_input_text/New(mob/user, message, title, default, max_length, multiline, encode, timeout, ui_state) src.default = default src.encode = encode src.max_length = max_length src.message = message src.multiline = multiline src.title = title + src.state = ui_state if (timeout) src.timeout = timeout start_time = world.time @@ -90,6 +93,7 @@ /datum/tgui_input_text/Destroy(force) SStgui.close_uis(src) + state = null return ..() /** @@ -111,7 +115,7 @@ closed = TRUE /datum/tgui_input_text/tgui_state(mob/user) - return GLOB.tgui_always_state + return state /datum/tgui_input_text/tgui_static_data(mob/user) var/list/data = list() diff --git a/tgui/public/tgui.html b/tgui/public/tgui.html index 2f1138b602..ec87b9968f 100644 --- a/tgui/public/tgui.html +++ b/tgui/public/tgui.html @@ -340,6 +340,18 @@ 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,9 +360,7 @@ return; } // Try again - node.parentNode.removeChild(node); - node = null; - retry(); + removeNodeAndRetry(); }; injectNode(node); return;