From da4842dddf28775f01864b60769ffa511757993e Mon Sep 17 00:00:00 2001 From: Bjorn Neergaard Date: Tue, 15 Dec 2015 22:37:52 -0600 Subject: [PATCH] Make NanoUI resistant to Topic spoofs Move Topic() into a NanoUI-specific ui_act proc Update to @YotaXP's latest JSON code. Return focus to the mapwindow if a key is pressed in a NanoUI. --- .../components/binary_devices/passive_gate.dm | 7 +- .../components/binary_devices/pump.dm | 7 +- .../components/binary_devices/volume_pump.dm | 7 +- .../components/components_base.dm | 4 - .../components/trinary_devices/filter.dm | 9 +- .../components/trinary_devices/mixer.dm | 11 +- .../components/unary_devices/cryo.dm | 5 +- code/__DEFINES/json.dm | 1 - code/game/machinery/alarm.dm | 28 +-- code/game/machinery/atmoalter/canister.dm | 6 +- .../machinery/doors/airlock_electronics.dm | 6 +- code/game/machinery/machinery.dm | 7 + code/game/machinery/spaceheater.dm | 12 +- .../game/objects/items/weapons/tanks/tanks.dm | 6 +- code/modules/client/asset_cache.dm | 25 +-- code/modules/json/json.dm | 203 +++++++++++++++++- code/modules/nano/external.dm | 14 ++ code/modules/nano/nanoui.dm | 9 +- code/modules/power/apc.dm | 42 +--- code/modules/power/smes.dm | 8 +- code/modules/power/solar.dm | 14 +- .../chemistry/machinery/chem_dispenser.dm | 14 +- .../chemistry/machinery/chem_heater.dm | 5 +- nano/assets/nanoui.main.js | 2 +- nano/scripts/handlers.coffee | 34 --- nano/scripts/main.coffee | 1 - nano/scripts/nanoui.coffee | 2 +- nano/scripts/window.coffee | 32 +++ tgstation.dme | 1 - 29 files changed, 354 insertions(+), 168 deletions(-) delete mode 100644 code/__DEFINES/json.dm delete mode 100644 nano/scripts/handlers.coffee diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm index 4cb3fd560a1..60268e0c7ea 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -149,22 +149,21 @@ Passive gate is similar to the regular pump except: return interact(user) -/obj/machinery/atmospherics/components/binary/passive_gate/Topic(href, href_list) +/obj/machinery/atmospherics/components/binary/passive_gate/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("power") on = !on investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") if("pressure") - switch(href_list["set"]) + switch(params["set"]) if ("max") target_pressure = MAX_OUTPUT_PRESSURE if ("custom") target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa)", target_pressure))) investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - add_fingerprint(usr) update_icon() return 1 diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index 18a309d3521..650f06b3a68 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -157,22 +157,21 @@ Thus, the two variables affect pump operation are set in New(): return interact(user) -/obj/machinery/atmospherics/components/binary/pump/Topic(href,href_list) +/obj/machinery/atmospherics/components/binary/pump/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("power") on = !on investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") if("pressure") - switch(href_list["set"]) + switch(params["set"]) if ("max") target_pressure = MAX_OUTPUT_PRESSURE if ("custom") target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa)", target_pressure))) investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - add_fingerprint(usr) update_icon() return 1 diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index 331d0125def..7b28aa206ab 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -155,22 +155,21 @@ Thus, the two variables affect pump operation are set in New(): return interact(user) -/obj/machinery/atmospherics/components/binary/volume_pump/Topic(href,href_list) +/obj/machinery/atmospherics/components/binary/volume_pump/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("power") on = !on investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") if("transfer") - switch(href_list["set"]) + switch(params) if ("max") transfer_rate = MAX_TRANSFER_RATE if ("custom") transfer_rate = max(0, min(MAX_TRANSFER_RATE, safe_input("Pressure control", "Enter new transfer rate (0-[MAX_TRANSFER_RATE] L/s)", transfer_rate))) investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos") - add_fingerprint(usr) update_icon() return 1 diff --git a/code/ATMOSPHERICS/components/components_base.dm b/code/ATMOSPHERICS/components/components_base.dm index 4429f181721..47791c124d7 100644 --- a/code/ATMOSPHERICS/components/components_base.dm +++ b/code/ATMOSPHERICS/components/components_base.dm @@ -137,10 +137,6 @@ Pipenet stuff; housekeeping T.assume_air(to_release) air_update_turf(1) -/* -I think this is NanoUI? -*/ - /obj/machinery/atmospherics/components/proc/safe_input(var/title, var/text, var/default_set) var/new_value = input(usr,text,title,default_set) as num if(usr.canUseTopic(src)) diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index e1dcb450680..54cd1780810 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -182,23 +182,23 @@ Filter types: data["filter_type"] = filter_type return data -/obj/machinery/atmospherics/components/trinary/filter/Topic(href, href_list) +/obj/machinery/atmospherics/components/trinary/filter/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("power") on=!on investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") if("pressure") - switch(href_list["set"]) + switch(params["set"]) if("max") target_pressure = MAX_OUTPUT_PRESSURE if("custom") target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", target_pressure))) investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") if("filter") - src.filter_type = text2num(href_list["mode"]) + src.filter_type = text2num(params["mode"]) var/filtering_name = "nothing" switch(filter_type) if(FILTER_PLASMA) @@ -212,6 +212,5 @@ Filter types: if(FILTER_NITROUSOXIDE) filtering_name = "nitrous oxide" investigate_log("was set to filter [filtering_name] by [key_name(usr)]", "atmos") - add_fingerprint(usr) update_icon() return 1 diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm index 5f267b01965..cf2e0335afa 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm @@ -147,31 +147,30 @@ data["node2_concentration"] = round(node2_concentration*100) return data -/obj/machinery/atmospherics/components/trinary/mixer/Topic(href,href_list) +/obj/machinery/atmospherics/components/trinary/mixer/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("power") on = !on investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") if("pressure") - switch(href_list["set"]) + switch(params["set"]) if("max") target_pressure = MAX_OUTPUT_PRESSURE if("custom") target_pressure = max(0, min(MAX_OUTPUT_PRESSURE, safe_input("Pressure control", "Enter new output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", target_pressure))) investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") if("node1") - var/value = text2num(href_list["concentration"]) + var/value = text2num(params["concentration"]) src.node1_concentration = max(0, min(1, src.node1_concentration + value)) src.node2_concentration = max(0, min(1, src.node2_concentration - value)) investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos") if("node2") - var/value = text2num(href_list["concentration"]) + var/value = text2num(params["concentration"]) src.node2_concentration = max(0, min(1, src.node2_concentration + value)) src.node1_concentration = max(0, min(1, src.node1_concentration - value)) investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos") - add_fingerprint(usr) update_icon() return 1 \ No newline at end of file diff --git a/code/ATMOSPHERICS/components/unary_devices/cryo.dm b/code/ATMOSPHERICS/components/unary_devices/cryo.dm index 705666321b2..8931b19ff33 100644 --- a/code/ATMOSPHERICS/components/unary_devices/cryo.dm +++ b/code/ATMOSPHERICS/components/unary_devices/cryo.dm @@ -166,11 +166,11 @@ data["beakerContents"] = beakerContents return data -/obj/machinery/atmospherics/components/unary/cryo_cell/Topic(href, href_list) +/obj/machinery/atmospherics/components/unary/cryo_cell/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("open") open_machine() if("close") @@ -186,7 +186,6 @@ if(beaker) beaker.loc = get_step(loc, SOUTH) beaker = null - add_fingerprint(usr) update_icon() return 1 diff --git a/code/__DEFINES/json.dm b/code/__DEFINES/json.dm deleted file mode 100644 index 3f004ea4271..00000000000 --- a/code/__DEFINES/json.dm +++ /dev/null @@ -1 +0,0 @@ -#define writeJson(value) list2text(_jsonHelper.WriteValue(list(), (value))) \ No newline at end of file diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index a579f30063f..673dfc06d2c 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -387,7 +387,7 @@ data["thresholds"] = thresholds -/obj/machinery/alarm/Topic(href, href_list) +/obj/machinery/alarm/ui_act(action, params) if(..()) return @@ -400,17 +400,17 @@ if (usr.has_unlimited_silicon_privilege && src.aidisabled) return - switch(href_list["nano"]) + switch(action) if("toggleaccess") if(usr.has_unlimited_silicon_privilege && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) locked = !locked if("adjust") - var/device_id = href_list["id_tag"] - switch(href_list["command"]) + var/device_id = params["id_tag"] + switch(params["command"]) if("set_external_pressure") var/input_pressure = input("Enter target pressure:", "Pressure Controls") as num|null if(isnum(input_pressure)) - send_signal(device_id, list(href_list["command"] = input_pressure)) + send_signal(device_id, list(params["command"] = input_pressure)) if("reset_external_pressure") send_signal(device_id, list("set_external_pressure" = ONE_ATMOSPHERE)) if( @@ -422,14 +422,14 @@ "widenet", "scrubbing" ) - send_signal(device_id, list (href_list["command"] = text2num(href_list["val"]))) + send_signal(device_id, list (params["command"] = text2num(params["val"]))) if ("excheck") - send_signal(device_id, list ("checks" = text2num(href_list["val"])^1)) + send_signal(device_id, list ("checks" = text2num(params["val"])^1)) if ("incheck") - send_signal(device_id, list ("checks" = text2num(href_list["val"])^2)) + send_signal(device_id, list ("checks" = text2num(params["val"])^2)) if("set_threshold") - var/env = href_list["env"] - var/varname = href_list["var"] + var/env = params["env"] + var/varname = params["var"] var/datum/tlv/tlv = TLV[env] var/newval = input("Enter [varname] for [env]:", "Alarm Triggers", tlv.vars[varname]) as num|null if (isnull(newval)) @@ -446,16 +446,16 @@ newval = round(newval,0.01) tlv.vars[varname] = newval if("screen") - screen = text2num(href_list["screen"]) + screen = text2num(params["screen"]) if("mode") - mode = text2num(href_list["mode"]) + mode = text2num(params["mode"]) apply_mode() if("alarm") - if (alarm_area.atmosalert(2, src)) + if(alarm_area.atmosalert(2, src)) post_alert(2) update_icon() if("reset") - if (alarm_area.atmosalert(0, src)) + if(alarm_area.atmosalert(0, src)) post_alert(0) update_icon() return 1 diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 800002c3777..f201c663091 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -298,11 +298,11 @@ update_flag data["holdingTank"]["tankPressure"] = round(holding.air_contents.return_pressure()) return data -/obj/machinery/portable_atmospherics/canister/Topic(href, href_list) +/obj/machinery/portable_atmospherics/canister/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("relabel") if (can_label) var/list/colors = list(\ @@ -320,7 +320,7 @@ update_flag src.icon_state = colors[label] src.name = "canister: [label]" if("pressure") - switch(href_list["set"]) + switch(params["set"]) if("custom") var/custom = input(usr, "What rate do you set the regulator to? The dial reads from [CAN_MIN_RELEASE_PRESSURE] to [CAN_MAX_RELEASE_PRESSURE].") as null|num if(custom) diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index 4bfe9978f9e..59e312eaf45 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -40,18 +40,18 @@ return data -/obj/item/weapon/electronics/airlock/Topic(href, href_list) +/obj/item/weapon/electronics/airlock/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("clear") accesses = list() one_access = 0 if("one_access") one_access = !one_access if("set") - var/access = text2num(href_list["access"]) + var/access = text2num(params["access"]) if (!(access in accesses)) accesses += access else diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index fede733c2af..7f081902b51 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -209,6 +209,13 @@ Class Procs: add_fingerprint(usr) return 0 +/obj/machinery/ui_act(action, params) + ..() + if(!can_be_used_by(usr)) + return 1 + add_fingerprint(usr) + return 0 + /obj/machinery/proc/can_be_used_by(mob/user) if(!interact_offline && stat & (NOPOWER|BROKEN)) return 0 diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index 24dc29ab8bb..d5e58202f1c 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -162,28 +162,28 @@ ui = new(user, src, ui_key, "space_heater", name, 490, 340, state = physical_state) ui.open() -/obj/machinery/space_heater/Topic(href, href_list) - if(stat & BROKEN || ..()) +/obj/machinery/space_heater/ui_act(action, params) + if(..()) return - switch(href_list["nano"]) + switch(action) if("power") on = !on mode = HEATER_MODE_STANDBY usr.visible_message("[usr] switches [on ? "on" : "off"] \the [src].", "You switch [on ? "on" : "off"] \the [src].") update_icon() if("mode") - setMode = href_list["mode"] + setMode = params["mode"] if("temp") if(panel_open) var/value - if(href_list["set"] == "custom") + if(params["set"] == "custom") value = input("Please input the target temperature", name) as num|null if(isnull(value)) return value += T0C else - value = targetTemperature + text2num(href_list["set"]) + value = targetTemperature + text2num(params["set"]) var/minTemp = max(settableTemperatureMedian - settableTemperatureRange, TCMB) var/maxTemp = settableTemperatureMedian + settableTemperatureRange diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index cee0dd6a33c..5a8bb1245ff 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -157,13 +157,13 @@ data["maskConnected"] = 1 return data -/obj/item/weapon/tank/Topic(href, href_list) +/obj/item/weapon/tank/ui_act(action, params) if (..()) return - switch(href_list["nano"]) + switch(action) if("pressure") - switch(href_list["set"]) + switch(params["set"]) if("custom") var/custom = input(usr, "What rate do you set the regulator to? The dial reads from 0 to [TANK_MAX_RELEASE_PRESSURE].") as null|num if(isnum(custom)) diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index e93b8598ea8..76630a3dc1f 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -173,6 +173,19 @@ You can set verify to TRUE if you want send() to sleep until the client has the //DEFINITIONS FOR ASSET DATUMS START HERE. +/datum/asset/simple/nanoui + assets = list( + "nanoui.lib.js" = 'nano/assets/nanoui.lib.js', + "nanoui.main.js" = 'nano/assets/nanoui.main.js', + "nanoui.templates.js" = 'nano/assets/nanoui.templates.js', + "nanoui.lib.css" = 'nano/assets/nanoui.lib.css', + "nanoui.common.css" = 'nano/assets/nanoui.common.css', + "nanoui.generic.css" = 'nano/assets/nanoui.generic.css', + "nanoui.nanotrasen.css" = 'nano/assets/nanoui.nanotrasen.css', + "fontawesome-webfont.eot" = 'nano/assets/fontawesome-webfont.eot', + "fontawesome-webfont.woff2" = 'nano/assets/fontawesome-webfont.woff2' + ) + /datum/asset/simple/pda assets = list( "pda_atmos.png" = 'icons/pda_icons/pda_atmos.png', @@ -219,18 +232,6 @@ You can set verify to TRUE if you want send() to sleep until the client has the "large_stamp-law.png" = 'icons/stamp_icons/large_stamp-law.png' ) -/datum/asset/simple/nanoui - assets = list( - "nanoui.lib.js" = 'nano/assets/nanoui.lib.js', - "nanoui.main.js" = 'nano/assets/nanoui.main.js', - "nanoui.templates.js" = 'nano/assets/nanoui.templates.js', - "nanoui.lib.css" = 'nano/assets/nanoui.lib.css', - "nanoui.common.css" = 'nano/assets/nanoui.common.css', - "nanoui.generic.css" = 'nano/assets/nanoui.generic.css', - "nanoui.nanotrasen.css" = 'nano/assets/nanoui.nanotrasen.css', - "fontawesome-webfont.eot" = 'nano/assets/fontawesome-webfont.eot', - "fontawesome-webfont.woff2" = 'nano/assets/fontawesome-webfont.woff2' - ) //Registers HTML Interface assets. /datum/asset/HTML_interface/register() diff --git a/code/modules/json/json.dm b/code/modules/json/json.dm index cd411dba09e..d3b80d6792d 100644 --- a/code/modules/json/json.dm +++ b/code/modules/json/json.dm @@ -1,6 +1,14 @@ -/var/datum/jsonHelper/_jsonHelper = new // Solely as a namespace for procs. +/* Usage: + JSON.stringify(obj) - Converts lists and values into a JSON string. + JSON.parse(json) - Converts a JSON string into lists and values. +*/ + +/var/datum/jsonHelper/JSON = new // A namespace for procs. // ************************************ WRITER ************************************ +/datum/jsonHelper/proc/stringify(value) + return list2text(WriteValue(list(), value)) + /datum/jsonHelper/proc/WriteValue(list/json, value) . = json if(isnum(value)) @@ -82,4 +90,195 @@ #undef Either #undef CannotBeFlat #undef CannotBeAssoc - #undef BadList \ No newline at end of file + #undef BadList + +// ************************************ READER ************************************ +#define aBackspace 0x08 +#define aTab 0x09 +#define aLineBreak 0x0A +#define aVertTab 0x0B +#define aFormFeed 0x0C +#define aCarriageReturn 0x0D +#define aSpace 0x20 +#define aZero 0x30 +#define aNonBreakSpace 0xA0 + +#define Advance if(++readPos > jsonLen) { curAscii = 0; curChar = "" } else { curAscii = text2ascii(json, readPos); curChar = ascii2text(curAscii) } // Deal with it. +#define SkipWhitespace while(curAscii in whitespace) Advance +#define AdvanceWS Advance; SkipWhitespace + +/datum/jsonHelper/var + readPos + jsonLen + json + curAscii + curChar + static/list/whitespace = list(aTab, aLineBreak, aVertTab, aFormFeed, aCarriageReturn, aSpace, aNonBreakSpace) + +/datum/jsonHelper/proc/parse(json) + readPos = 0 + jsonLen = length(json) + src.json = json + curAscii = 0 + curChar = "" + AdvanceWS + var/value = ParseValue() + if(readPos < jsonLen) + throw EXCEPTION("Expected: End of JSON") + return value + +/datum/jsonHelper/proc/ParseValue() + if(curChar == "\"") + return ParseString() + else if(curChar == "-" || (curAscii >= aZero && curAscii <= aZero + 9)) + return ParseNumber() + else if(curChar == "{") + return ParseObject() + else if(curChar == "\[") + return ParseArray() + else if(curChar == "t") + if(copytext(json, readPos, readPos+4) == "true") + readPos += 3 + AdvanceWS + return TRUE + else + throw EXCEPTION("Expected: 'true'") + else if(curChar == "f") + if(copytext(json, readPos, readPos+5) == "false") + readPos += 4 + AdvanceWS + return FALSE + else + throw EXCEPTION("Expected: 'false'") + else if(curChar == "n") + if(copytext(json, readPos, readPos+4) == "null") + readPos += 3 + AdvanceWS + return null + else + throw EXCEPTION("Expected: 'null'") + else if(curChar == "") + throw EXCEPTION("Unexpected: End of JSON") + else + throw EXCEPTION("Unexpected: '[curChar]'") + + + +/datum/jsonHelper/proc/ParseString() + ASSERT(curChar == "\"") + Advance + var/list/chars = list() + while(readPos <= jsonLen) + if(curChar == "\"") + AdvanceWS + return list2text(chars) + else if(curChar == "\\") + Advance + switch(curChar) + if("\"", "\\", "/") + chars += ascii2text(curAscii) + if("b") + chars += ascii2text(aBackspace) + if("f") + chars += ascii2text(aFormFeed) + if("n") + chars += "\n" + if("r") + chars += ascii2text(aCarriageReturn) // Should we ignore these? + if("t") + chars += "\t" + if("u") + throw EXCEPTION("JSON \\uXXXX escape sequence not supported") + else + throw EXCEPTION("Invalid escape sequence") + Advance + else + chars += ascii2text(curAscii) + Advance + throw EXCEPTION("Unterminated string") + +/datum/jsonHelper/proc/ParseNumber() + var/firstPos = readPos + if(curChar == "-") + Advance + if(curAscii >= aZero + 1 && curAscii <= aZero + 9) + do + Advance + while(curAscii >= aZero && curAscii <= aZero + 9) + else if(curAscii == aZero) + Advance + else + throw EXCEPTION("Expected: digit") + + if(curChar == ".") + Advance + var/found = FALSE + while(curAscii >= aZero && curAscii <= aZero + 9) + found = TRUE + Advance + if(!found) + throw EXCEPTION("Expected: digit") + + if(curChar == "E" || curChar == "e") + Advance + var/found = FALSE + if(curChar == "-") + Advance + else if(curChar == "+") + Advance + while(curAscii >= aZero && curAscii <= aZero + 9) + found = TRUE + Advance + if(!found) + throw EXCEPTION("Expected: digit") + + SkipWhitespace + return text2num(copytext(json, firstPos, readPos)) + +/datum/jsonHelper/proc/ParseObject() + ASSERT(curChar == "{") + var/list/object = list() + AdvanceWS + while(curChar == "\"") + var/key = ParseString() + if(curChar != ":") + throw EXCEPTION("Expected: ':'") + AdvanceWS + object[key] = ParseValue() + if(curChar == ",") + AdvanceWS + else + break + if(curChar != "}") + throw EXCEPTION("Expected: string or '}'") + AdvanceWS + return object + +/datum/jsonHelper/proc/ParseArray() + ASSERT(curChar == "\[") + var/list/array = list() + AdvanceWS + while(curChar != "]") + array += list(ParseValue()) // Wrapped in a list in case ParseValue() returns a list. + if(curChar == ",") + AdvanceWS + else + break + if(curChar != "]") + throw EXCEPTION("Expected: ']'") + AdvanceWS + return array + +#undef aBackspace +#undef aTab +#undef aLineBreak +#undef aVertTab +#undef aFormFeed +#undef aCarriageReturn +#undef aSpace +#undef aZero +#undef aNonBreakSpace + +#undef Advance +#undef SkipWhitespace +#undef AdvanceWS \ No newline at end of file diff --git a/code/modules/nano/external.dm b/code/modules/nano/external.dm index ac469027f0c..bd72394934e 100644 --- a/code/modules/nano/external.dm +++ b/code/modules/nano/external.dm @@ -27,6 +27,20 @@ datum/topic_state/state = default_state) return -1 // Sorta implemented. + /** + * public + * + * Called on a NanoUI when the UI receieves a href. + * Think of this as Topic(). + * + * required action string The action/button that has been invoked by the user. + * required params list A list of parameters attached to the button. + * + * return bool If the UI should be updated or not. + **/ +/atom/movable/proc/ui_act(action, list/params) + return // Not implemented. + /** * public * diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index bdca1787ff3..fa23b149b46 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -214,7 +214,7 @@ // Generate JSON. var/list/send_data = get_send_data(initial_data) - var/initial_data_json = replacetext(writeJson(send_data), "'", "\\'") + var/initial_data_json = replacetext(JSON.stringify(send_data), "'", "\\'") // Generate the HTML document. return {" @@ -318,7 +318,7 @@ var/list/send_data = get_send_data(data) // Get the data to send. // Send the new data to the recieveUpdate() Javascript function. - user << output(list2params(list(writeJson(send_data))), "[window_id].browser:receiveUpdate") + user << output(list2params(list(JSON.stringify(send_data))), "[window_id].browser:receiveUpdate") /** * private @@ -332,7 +332,10 @@ if (status != NANO_INTERACTIVE || user != usr) return // If UI is not interactive or usr calling Topic is not the UI user. - var/update = src_object.Topic(href, href_list, 0, state) // Call Topic() on the src_object. + var/action = href_list["nano"] // Pull the action out. + href_list -= "nano" + + var/update = src_object.ui_act(action, href_list, state) // Call Topic() on the src_object. if (src_object && update) SSnano.update_uis(src_object) // If we have a src_object and its Topic() told us to update. diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index dd97eee36aa..afbe1928d1e 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -721,22 +721,8 @@ /obj/machinery/power/apc/proc/can_use(mob/user, loud = 0) //used by attack_hand() and Topic() - if (IsAdminGhost(user)) + if(IsAdminGhost(user)) return 1 - if (user.stat) - user << "You must be conscious to use [src]!" - return 0 - if(!user.client) - return 0 - if(!user.IsAdvancedToolUser()) - user << "You don't have the dexterity to use [src]!" - return 0 - if(user.restrained()) - user << "You must have free hands to use [src]." - return 0 - if(user.lying) - user << "You must stand to use [src]!" - return 0 if(user.has_unlimited_silicon_privilege) var/mob/living/silicon/ai/AI = user var/mob/living/silicon/robot/robot = user @@ -754,24 +740,16 @@ else if ((!in_range(src, user) || !istype(src.loc, /turf))) return 0 - - var/mob/living/carbon/human/H = user - if (istype(H)) - if(H.getBrainLoss() >= 60) - H.visible_message("[H] stares cluelessly at [src] and drools.") - return 0 - else if(prob(H.getBrainLoss())) - user << "You momentarily forget how to use [src]." - return 0 return 1 -/obj/machinery/power/apc/Topic(href, href_list) +/obj/machinery/power/apc/ui_act(action, params) if(..()) return + if(!can_use(usr, 1)) return - switch(href_list["nano"]) + switch(action) if("lock") coverlocked = !coverlocked if ("breaker") @@ -782,18 +760,18 @@ charging = 0 update_icon() if("channel") - if (href_list["eqp"]) - var/val = text2num(href_list["eqp"]) + if (params["eqp"]) + var/val = text2num(params["eqp"]) equipment = setsubsystem(val) update_icon() update() - else if (href_list["lgt"]) - var/val = text2num(href_list["lgt"]) + else if (params["lgt"]) + var/val = text2num(params["lgt"]) lighting = setsubsystem(val) update_icon() update() - else if (href_list["env"]) - var/val = text2num(href_list["env"]) + else if (params["env"]) + var/val = text2num(params["env"]) environ = setsubsystem(val) update_icon() update() diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 94edaaf9b96..17ea7713b36 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -350,11 +350,11 @@ ) return data -/obj/machinery/power/smes/Topic(href, href_list) +/obj/machinery/power/smes/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("tryinput") input_attempt = !input_attempt log_smes(usr.ckey) @@ -364,7 +364,7 @@ log_smes(usr.ckey) update_icon() if("input") - switch(href_list["set"]) + switch(params["set"]) if("custom") var/custom = input(usr, "What rate would you like this SMES to attempt to charge at? Max is [input_level_max].") as null|num if(custom) @@ -380,7 +380,7 @@ input_level = Clamp(input_level, 0, input_level_max) log_smes(usr.ckey) if("output") - switch(href_list["set"]) + switch(params["set"]) if("custom") var/custom = input(usr, "What rate would you like this SMES to attempt to output at? Max is [output_level_max].") as null|num if(custom) diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index e0f172c7748..7992fd85fdc 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -437,24 +437,24 @@ targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it nexttime += 36000/abs(trackrate) //reset the counter for the next 1° -/obj/machinery/power/solar_control/Topic(href, href_list) +/obj/machinery/power/solar_control/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("control") - if(href_list["cdir"]) - src.cdir = dd_range(0,359,(360+src.cdir+text2num(href_list["cdir"]))%360) + if(params["cdir"]) + src.cdir = dd_range(0,359,(360+src.cdir+text2num(params["cdir"]))%360) src.targetdir = src.cdir if(track == 2) //manual update, so losing auto-tracking track = 0 spawn(1) set_panels(cdir) - if(href_list["tdir"]) - src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(href_list["tdir"])) + if(params["tdir"]) + src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(params["tdir"])) if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) if("tracking") - track = text2num(href_list["mode"]) + track = text2num(params["mode"]) if(track == 2) if(connected_tracker) connected_tracker.set_angle(SSsun.angle) diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index ba2a12ed5cd..fef849aaac3 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -7,6 +7,7 @@ icon_state = "dispenser" use_power = 1 idle_power_usage = 40 + interact_offline = 1 var/energy = 100 var/max_energy = 100 var/amount = 30 @@ -122,27 +123,27 @@ data["chemicals"] = chemicals return data -/obj/machinery/chem_dispenser/Topic(href, href_list) +/obj/machinery/chem_dispenser/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("amount") - amount = round(text2num(href_list["set"]), 5) // round to nearest 5 + amount = round(text2num(params["set"]), 5) // round to nearest 5 if (amount < 0) // Since the user can actually type the commands himself, some sanity checking amount = 0 if (amount > 100) amount = 100 if("dispense") - if(beaker && dispensable_reagents.Find(href_list["reagent"])) + if(beaker && dispensable_reagents.Find(params["reagent"])) var/datum/reagents/R = beaker.reagents var/space = R.maximum_volume - R.total_volume - R.add_reagent(href_list["reagent"], min(amount, energy * 10, space)) + R.add_reagent(params["reagent"], min(amount, energy * 10, space)) energy = max(energy - min(amount, energy * 10, space) / 10, 0) if("remove") if(beaker) - var/amount = text2num(href_list["amount"]) + var/amount = text2num(params["amount"]) if(isnum(amount) && (amount > 0) && (amount in beaker.possible_transfer_amounts)) beaker.reagents.remove_all(amount) if("eject") @@ -150,7 +151,6 @@ beaker.loc = loc beaker = null overlays.Cut() - add_fingerprint(usr) return 1 /obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params) diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index 9fe19a740d9..b900f40dcac 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -77,18 +77,17 @@ return interact(user) -/obj/machinery/chem_heater/Topic(href, href_list) +/obj/machinery/chem_heater/ui_act(action, params) if(..()) return - switch(href_list["nano"]) + switch(action) if("power") on = !on if("temperature") desired_temp = Clamp(input("Please input the target temperature", name) as num, 0, 1000) if("eject") eject_beaker() - add_fingerprint(usr) return 1 /obj/machinery/chem_heater/interact(mob/user) diff --git a/nano/assets/nanoui.main.js b/nano/assets/nanoui.main.js index 195ecb81b31..87dc88ea848 100644 --- a/nano/assets/nanoui.main.js +++ b/nano/assets/nanoui.main.js @@ -1 +1 @@ -(function(){this.NANO={INTERACTIVE:2,UPDATE:1,DISABLED:0}}).call(this),function(){var t=function(t,n){return function(){return t.apply(n,arguments)}};this.Handlers=function(){function n(n,i){this.bus=n,this.fragment=null!=i?i:document,this.attachLinks=t(this.attachLinks,this),this.updateLinks=t(this.updateLinks,this),this.updateStatus=t(this.updateStatus,this),this.bus.on("rendered",this.updateStatus),this.bus.on("rendered",this.updateLinks),this.bus.on("rendered",this.attachLinks)}return n.prototype.updateStatus=function(t){var n;return n=this.fragment.queryAll(".statusicon"),n.forEach(function(n){var i;switch(n.className=n.className.replace(/good|bad|average/g,""),t.config.status){case NANO.INTERACTIVE:i="good";break;case NANO.UPDATE:i="average";break;default:i="bad"}return n.classList.add(i)})},n.prototype.updateLinks=function(t){var n;return n=this.fragment.queryAll(".link"),t.config.status!==NANO.INTERACTIVE?n.forEach(function(t){return t.className="link disabled"}):void 0},n.prototype.attachLinks=function(t){var n;return n=function(){var n,i;return n=this.data("action"),i=JSON.parse(this.data("params")),null!=n&&null!=i&&t.config.status===NANO.INTERACTIVE?nanoui.bycall(n,i):void 0},this.fragment.queryAll(".link.active").forEach(function(t){return t.on("click",n)})},n}()}.call(this),function(){this.helpers={link:function(t,n,i,e,s,r){return null==t&&(t=""),null==n&&(n=""),null==i&&(i=""),null==e&&(e={}),null==s&&(s=""),null==r&&(r=""),e=JSON.stringify(e),n&&(n="",r+=" iconed"),s?""+n+t+"":""+n+t+""},bar:function(t,n,i,e,s){var r;return null==t&&(t=0),null==n&&(n=0),null==i&&(i=100),null==e&&(e=""),null==s&&(s=""),i>n?n>t?t=n:t>i&&(t=i):t>n?t=n:i>t&&(t=i),r=Math.round((t-n)/(i-n)*100),"
"+s+"
"},round:function(t){return Math.round(t)},fixed:function(t,n){return null==n&&(n=1),Number(Math.round(t+"e"+n)+"e-"+n)},floor:function(t){return Math.floor(t)},ceil:function(t){return Math.ceil(t)}}}.call(this),function(){document.when("ready",function(t){return function(){var n;n={},t.nanoui=new t.NanoUI(n,document),t.handlers=new t.Handlers(n,document),t.nanowindow=new t.Window(n,document),n.emit("memes"),t.NanoBus=n}}(this))}.call(this),function(){var t=function(t,n){return function(){return t.apply(n,arguments)}};this.NanoUI=function(){function n(n,i){this.bus=n,this.fragment=null!=i?i:document,this.winset=t(this.winset,this),this.close=t(this.close,this),this.bycall=t(this.bycall,this),this.render=t(this.render,this),this.update=t(this.update,this),this.serverUpdate=t(this.serverUpdate,this),this.bus.on("serverUpdate",this.serverUpdate),this.bus.on("update",this.update),this.bus.on("render",this.render),this.bus.on("memes",this.render),this.initialized=!1,this.data={},this.initialData=JSON.parse(this.fragment.query("#data").data("initial")),null==this.initialData&&("data"in this.initialData||"config"in this.initalData)&&this.error("Initial data did not load correctly.")}return n.prototype.serverUpdate=function(t){var n,i,e;try{n=JSON.parse(t)}catch(e){i=e,this.error(i)}this.bus.emit("update",n)},n.prototype.update=function(t){null==t.data&&(null!=this.data.data?t.data=this.data.data:t.data={}),this.data=t,this.initialized&&this.bus.emit("render",this.data),this.bus.emit("updated")},n.prototype.render=function(t){var n,i,e,s;this.initialized||(t=this.initialData);try{this.initialized||(s=this.fragment.query("#layout"),s.innerHTML=TMPL[t.config.templates.layout](t.data,t.config,helpers)),n=this.fragment.query("#content"),n.innerHTML=TMPL[t.config.templates.content](t.data,t.config,helpers)}catch(e){return i=e,void this.error(i)}return this.bus.emit("rendered",t),this.initialized?void 0:(this.initialized=!0,this.data=this.initialData,this.bus.emit("initialized",t))},n.prototype.bycall=function(t,n){return null==n&&(n={}),n.src=this.data.config.ref,n.nano=t,location.href=util.href(null,n)},n.prototype.error=function(t){var n;return t instanceof Error&&(t=t.fileName+":"+t.lineNumber+" "+t.message),n={nano_error:t},location.href=util.href(null,n)},n.prototype.log=function(t){var n;return n={nano_log:t},location.href=util.href(null,n)},n.prototype.close=function(){var t;return t={command:"nanoclose "+this.data.config.ref},this.winset("is-visible","false"),location.href=util.href("winset",t)},n.prototype.winset=function(t,n,i){var e,s;return null==i&&(i=this.data.config.window.ref),e={},e[i+"."+t]=n,s=e,location.href=util.href("winset",s)},n}()}.call(this),function(){this.util={extend:function(t,n){return Object.keys(n).forEach(function(i){var e;return e=n[i],e&&"[object Object]"===Object.prototype.toString.call(e)?(t[i]=t[i]||{},util.extend(t[i],e)):t[i]=e}),t},href:function(t,n){return null==t&&(t=""),null==n&&(n={}),t=new Url("byond://"+t),util.extend(t.query,n),t}}}.call(this),function(){var t=function(t,n){return function(){return t.apply(n,arguments)}};this.Window=function(){function n(n,i){this.bus=n,this.fragment=null!=i?i:document,this.resize=t(this.resize,this),this.attachResize=t(this.attachResize,this),this.drag=t(this.drag,this),this.attachDrag=t(this.attachDrag,this),this.attachButtons=t(this.attachButtons,this),this.calcOffset=t(this.calcOffset,this),this.fancyChrome=t(this.fancyChrome,this),this.dragging=!1,this.resizing=!1,this.bus.once("initialized",function(t){return function(n){return setTimeout(t.focusMap,100),n.config.user.fancy?(t.fancyChrome(),t.calcOffset(),t.attachButtons(),t.attachDrag(),t.attachResize()):void 0}}(this)),this.fragment.on("keydown",this.focusMap)}return n.prototype.setPos=function(t,n){return nanoui.winset("pos",t+","+n)},n.prototype.setSize=function(t,n){return nanoui.winset("size",t+","+n)},n.prototype.focusMap=function(){return nanoui.winset("focus",1,"mapwindow.map")},n.prototype.fancyChrome=function(){var t;return nanoui.winset("titlebar",0),nanoui.winset("can-resize",0),t=this.fragment.queryAll(".fancy"),t.forEach(function(t){return t.style.display="inherit"})},n.prototype.calcOffset=function(){return this.xOriginal=window.screenLeft,this.yOriginal=window.screenTop,this.setPos(0,0),this.xOffset=window.screenLeft,this.yOffset=window.screenTop,this.setPos(this.xOriginal-this.xOffset,this.yOriginal-this.yOffset)},n.prototype.attachButtons=function(){var t,n,i,e;return t=function(){return nanoui.close()},i=function(){return nanoui.winset("is-minimized","true")},n=this.fragment.queryAll(".close"),n.forEach(function(n){return n.on("click",t)}),e=this.fragment.queryAll(".minimize"),e.forEach(function(t){return t.on("click",i)})},n.prototype.attachDrag=function(){var t;return t=this.fragment.query("#titlebar"),this.fragment.on("mousemove",this.drag),t.on("mousedown",function(t){return function(){return t.dragging=!0}}(this)),this.fragment.on("mouseup",function(t){return function(){return t.dragging=!1}}(this))},n.prototype.drag=function(t){var n,i;return null==t&&(t=window.event),this.dragging?(null==this.xDrag&&(this.xDrag=t.screenX),null==this.yDrag&&(this.yDrag=t.screenY),n=t.screenX-this.xDrag+(window.screenLeft-this.xOffset),i=t.screenY-this.yDrag+(window.screenTop-this.yOffset),this.setPos(n,i),this.xDrag=t.screenX,this.yDrag=t.screenY):void 0},n.prototype.attachResize=function(){var t;return t=this.fragment.query("#resize"),this.fragment.on("mousemove",this.resize),t.on("mousedown",function(t){return function(){return t.resizing=!0}}(this)),this.fragment.on("mouseup",function(t){return function(){return t.resizing=!1}}(this))},n.prototype.resize=function(t){var n,i;return null==t&&(t=window.event),this.resizing?(null==this.xResize&&(this.xResize=t.screenX),null==this.yResize&&(this.yResize=t.screenY),n=t.screenX-this.xResize+window.innerWidth,i=t.screenY-this.yResize+window.innerHeight,this.setSize(n,i),this.xResize=t.screenX,this.yResize=t.screenY):void 0},n}()}.call(this); \ No newline at end of file +(function(){this.NANO={INTERACTIVE:2,UPDATE:1,DISABLED:0}}).call(this),function(){this.helpers={link:function(t,n,i,e,s,r){return null==t&&(t=""),null==n&&(n=""),null==i&&(i=""),null==e&&(e={}),null==s&&(s=""),null==r&&(r=""),e=JSON.stringify(e),n&&(n="",r+=" iconed"),s?""+n+t+"":""+n+t+""},bar:function(t,n,i,e,s){var r;return null==t&&(t=0),null==n&&(n=0),null==i&&(i=100),null==e&&(e=""),null==s&&(s=""),i>n?n>t?t=n:t>i&&(t=i):t>n?t=n:i>t&&(t=i),r=Math.round((t-n)/(i-n)*100),"
"+s+"
"},round:function(t){return Math.round(t)},fixed:function(t,n){return null==n&&(n=1),Number(Math.round(t+"e"+n)+"e-"+n)},floor:function(t){return Math.floor(t)},ceil:function(t){return Math.ceil(t)}}}.call(this),function(){document.when("ready",function(t){return function(){var n;n={},t.nanoui=new t.NanoUI(n,document),t.nanowindow=new t.Window(n,document),n.emit("memes"),t.NanoBus=n}}(this))}.call(this),function(){var t=function(t,n){return function(){return t.apply(n,arguments)}};this.NanoUI=function(){function n(n,i){this.bus=n,this.fragment=null!=i?i:document,this.winset=t(this.winset,this),this.close=t(this.close,this),this.act=t(this.act,this),this.render=t(this.render,this),this.update=t(this.update,this),this.serverUpdate=t(this.serverUpdate,this),this.bus.on("serverUpdate",this.serverUpdate),this.bus.on("update",this.update),this.bus.on("render",this.render),this.bus.on("memes",this.render),this.initialized=!1,this.data={},this.initialData=JSON.parse(this.fragment.query("#data").data("initial")),null==this.initialData&&("data"in this.initialData||"config"in this.initalData)&&this.error("Initial data did not load correctly.")}return n.prototype.serverUpdate=function(t){var n,i,e;try{n=JSON.parse(t)}catch(e){i=e,this.error(i)}this.bus.emit("update",n)},n.prototype.update=function(t){null==t.data&&(null!=this.data.data?t.data=this.data.data:t.data={}),this.data=t,this.initialized&&this.bus.emit("render",this.data),this.bus.emit("updated")},n.prototype.render=function(t){var n,i,e,s;this.initialized||(t=this.initialData);try{this.initialized||(s=this.fragment.query("#layout"),s.innerHTML=TMPL[t.config.templates.layout](t.data,t.config,helpers)),n=this.fragment.query("#content"),n.innerHTML=TMPL[t.config.templates.content](t.data,t.config,helpers)}catch(e){return i=e,void this.error(i)}return this.bus.emit("rendered",t),this.initialized?void 0:(this.initialized=!0,this.data=this.initialData,this.bus.emit("initialized",t))},n.prototype.act=function(t,n){return null==n&&(n={}),n.src=this.data.config.ref,n.nano=t,location.href=util.href(null,n)},n.prototype.error=function(t){var n;return t instanceof Error&&(t=t.fileName+":"+t.lineNumber+" "+t.message),n={nano_error:t},location.href=util.href(null,n)},n.prototype.log=function(t){var n;return n={nano_log:t},location.href=util.href(null,n)},n.prototype.close=function(){var t;return t={command:"nanoclose "+this.data.config.ref},this.winset("is-visible","false"),location.href=util.href("winset",t)},n.prototype.winset=function(t,n,i){var e,s;return null==i&&(i=this.data.config.window.ref),e={},e[i+"."+t]=n,s=e,location.href=util.href("winset",s)},n}()}.call(this),function(){this.util={extend:function(t,n){return Object.keys(n).forEach(function(i){var e;return e=n[i],e&&"[object Object]"===Object.prototype.toString.call(e)?(t[i]=t[i]||{},util.extend(t[i],e)):t[i]=e}),t},href:function(t,n){return null==t&&(t=""),null==n&&(n={}),t=new Url("byond://"+t),util.extend(t.query,n),t}}}.call(this),function(){var t=function(t,n){return function(){return t.apply(n,arguments)}};this.Window=function(){function n(n,i){this.bus=n,this.fragment=null!=i?i:document,this.attachLinks=t(this.attachLinks,this),this.updateLinks=t(this.updateLinks,this),this.updateStatus=t(this.updateStatus,this),this.resize=t(this.resize,this),this.attachResize=t(this.attachResize,this),this.drag=t(this.drag,this),this.attachDrag=t(this.attachDrag,this),this.attachButtons=t(this.attachButtons,this),this.calcOffset=t(this.calcOffset,this),this.fancyChrome=t(this.fancyChrome,this),this.dragging=!1,this.resizing=!1,this.bus.once("initialized",function(t){return function(n){return setTimeout(t.focusMap,100),n.config.user.fancy?(t.fancyChrome(),t.calcOffset(),t.attachButtons(),t.attachDrag(),t.attachResize()):void 0}}(this)),this.bus.on("rendered",this.updateStatus),this.bus.on("rendered",this.updateLinks),this.bus.on("rendered",this.attachLinks),this.fragment.on("keydown",this.focusMap)}return n.prototype.setPos=function(t,n){return nanoui.winset("pos",t+","+n)},n.prototype.setSize=function(t,n){return nanoui.winset("size",t+","+n)},n.prototype.focusMap=function(){return nanoui.winset("focus",1,"mapwindow.map")},n.prototype.fancyChrome=function(){var t;return nanoui.winset("titlebar",0),nanoui.winset("can-resize",0),t=this.fragment.queryAll(".fancy"),t.forEach(function(t){return t.style.display="inherit"})},n.prototype.calcOffset=function(){return this.xOriginal=window.screenLeft,this.yOriginal=window.screenTop,this.setPos(0,0),this.xOffset=window.screenLeft,this.yOffset=window.screenTop,this.setPos(this.xOriginal-this.xOffset,this.yOriginal-this.yOffset)},n.prototype.attachButtons=function(){var t,n,i,e;return t=function(){return nanoui.close()},i=function(){return nanoui.winset("is-minimized","true")},n=this.fragment.queryAll(".close"),n.forEach(function(n){return n.on("click",t)}),e=this.fragment.queryAll(".minimize"),e.forEach(function(t){return t.on("click",i)})},n.prototype.attachDrag=function(){var t;return t=this.fragment.query("#titlebar"),this.fragment.on("mousemove",this.drag),t.on("mousedown",function(t){return function(){return t.dragging=!0}}(this)),this.fragment.on("mouseup",function(t){return function(){return t.dragging=!1}}(this))},n.prototype.drag=function(t){var n,i;return null==t&&(t=window.event),this.dragging?(null==this.xDrag&&(this.xDrag=t.screenX),null==this.yDrag&&(this.yDrag=t.screenY),n=t.screenX-this.xDrag+(window.screenLeft-this.xOffset),i=t.screenY-this.yDrag+(window.screenTop-this.yOffset),this.setPos(n,i),this.xDrag=t.screenX,this.yDrag=t.screenY):void 0},n.prototype.attachResize=function(){var t;return t=this.fragment.query("#resize"),this.fragment.on("mousemove",this.resize),t.on("mousedown",function(t){return function(){return t.resizing=!0}}(this)),this.fragment.on("mouseup",function(t){return function(){return t.resizing=!1}}(this))},n.prototype.resize=function(t){var n,i;return null==t&&(t=window.event),this.resizing?(null==this.xResize&&(this.xResize=t.screenX),null==this.yResize&&(this.yResize=t.screenY),n=t.screenX-this.xResize+window.innerWidth,i=t.screenY-this.yResize+window.innerHeight,this.setSize(n,i),this.xResize=t.screenX,this.yResize=t.screenY):void 0},n.prototype.updateStatus=function(t){var n;return n=this.fragment.queryAll(".statusicon"),n.forEach(function(n){var i;switch(n.className=n.className.replace(/good|bad|average/g,""),t.config.status){case NANO.INTERACTIVE:i="good";break;case NANO.UPDATE:i="average";break;default:i="bad"}return n.classList.add(i)})},n.prototype.updateLinks=function(t){var n;return n=this.fragment.queryAll(".link"),t.config.status!==NANO.INTERACTIVE?n.forEach(function(t){return t.className="link disabled"}):void 0},n.prototype.attachLinks=function(t){var n;return n=function(){var n,i;return n=this.data("action"),i=JSON.parse(this.data("params")),null!=n&&null!=i&&t.config.status===NANO.INTERACTIVE?nanoui.act(n,i):void 0},this.fragment.queryAll(".link.active").forEach(function(t){return t.on("click",n)})},n}()}.call(this); \ No newline at end of file diff --git a/nano/scripts/handlers.coffee b/nano/scripts/handlers.coffee deleted file mode 100644 index 65a468efc32..00000000000 --- a/nano/scripts/handlers.coffee +++ /dev/null @@ -1,34 +0,0 @@ -class @Handlers - constructor: (@bus, @fragment = document) -> - @bus.on "rendered", @updateStatus - @bus.on "rendered", @updateLinks - @bus.on "rendered", @attachLinks - - updateStatus: (data) => - statusicons = @fragment.queryAll ".statusicon" - statusicons.forEach (statusicon) -> - statusicon.className = statusicon.className.replace /good|bad|average/g, "" - switch data.config.status - when NANO.INTERACTIVE - klass = "good" - when NANO.UPDATE - klass = "average" - else - klass = "bad" - statusicon.classList.add klass - - updateLinks: (data) => - links = @fragment.queryAll ".link" - if data.config.status isnt NANO.INTERACTIVE - links.forEach (element) -> - element.className = "link disabled" - - attachLinks: (data) => - onClick = -> - action = @data "action" - params = JSON.parse @data "params" - if action? and params? and data.config.status is NANO.INTERACTIVE - nanoui.bycall action, params - - @fragment.queryAll(".link.active").forEach (link) -> - link.on "click", onClick diff --git a/nano/scripts/main.coffee b/nano/scripts/main.coffee index 7085df959d0..5a0a6d2af15 100644 --- a/nano/scripts/main.coffee +++ b/nano/scripts/main.coffee @@ -2,7 +2,6 @@ document.when "ready", => coderbus = {} @nanoui = new @NanoUI coderbus, document - @handlers = new @Handlers coderbus, document @nanowindow = new @Window coderbus, document coderbus.emit "memes" diff --git a/nano/scripts/nanoui.coffee b/nano/scripts/nanoui.coffee index 291a3f52a35..5fb4e2cd426 100644 --- a/nano/scripts/nanoui.coffee +++ b/nano/scripts/nanoui.coffee @@ -59,7 +59,7 @@ class @NanoUI @data = @initialData @bus.emit "initialized", data - bycall: (action, params = {}) => + act: (action, params = {}) => params.src = @data.config.ref params.nano = action location.href = util.href null, params diff --git a/nano/scripts/window.coffee b/nano/scripts/window.coffee index 69c46f205fc..6c173b2cac9 100644 --- a/nano/scripts/window.coffee +++ b/nano/scripts/window.coffee @@ -12,6 +12,9 @@ class @Window @attachDrag() @attachResize() + @bus.on "rendered", @updateStatus + @bus.on "rendered", @updateLinks + @bus.on "rendered", @attachLinks @fragment.on "keydown", @focusMap # If we get input, return focus. setPos: (x, y) -> @@ -86,3 +89,32 @@ class @Window @xResize = event.screenX @yResize = event.screenY + + updateStatus: (data) => + statusicons = @fragment.queryAll ".statusicon" + statusicons.forEach (statusicon) -> + statusicon.className = statusicon.className.replace /good|bad|average/g, "" + switch data.config.status + when NANO.INTERACTIVE + klass = "good" + when NANO.UPDATE + klass = "average" + else + klass = "bad" + statusicon.classList.add klass + + updateLinks: (data) => + links = @fragment.queryAll ".link" + if data.config.status isnt NANO.INTERACTIVE + links.forEach (element) -> + element.className = "link disabled" + + attachLinks: (data) => + onClick = -> + action = @data "action" + params = JSON.parse @data "params" + if action? and params? and data.config.status is NANO.INTERACTIVE + nanoui.act action, params + + @fragment.queryAll(".link.active").forEach (link) -> + link.on "click", onClick diff --git a/tgstation.dme b/tgstation.dme index 12c4e9e6010..e785a418d1d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -31,7 +31,6 @@ #include "code\__DEFINES\genetics.dm" #include "code\__DEFINES\hud.dm" #include "code\__DEFINES\is_helpers.dm" -#include "code\__DEFINES\json.dm" #include "code\__DEFINES\machines.dm" #include "code\__DEFINES\math.dm" #include "code\__DEFINES\misc.dm"