mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-28 07:38:05 +01:00
Merge pull request #13703 from neersighted/nano_reduction
NanoUI Reduction
This commit is contained in:
@@ -1,312 +1,312 @@
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell
|
||||
name = "cryo cell"
|
||||
icon = 'icons/obj/cryogenics.dmi'
|
||||
icon_state = "cell-off"
|
||||
density = 1
|
||||
anchored = 1
|
||||
layer = 4
|
||||
|
||||
var/on = 0
|
||||
var/temperature_archived
|
||||
var/obj/item/weapon/reagent_containers/glass/beaker = null
|
||||
var/next_trans = 0
|
||||
var/current_heat_capacity = 50
|
||||
state_open = 0
|
||||
var/efficiency = 1
|
||||
var/autoEject = 0
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/New()
|
||||
..()
|
||||
initialize_directions = dir
|
||||
component_parts = list()
|
||||
component_parts += new /obj/item/weapon/circuitboard/cryo_tube(null)
|
||||
component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
|
||||
component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
|
||||
component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
|
||||
component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
|
||||
component_parts += new /obj/item/stack/cable_coil(null, 1)
|
||||
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/construction()
|
||||
..(dir,dir)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/RefreshParts()
|
||||
var/C
|
||||
for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts)
|
||||
C += M.rating
|
||||
current_heat_capacity = 50 * C
|
||||
efficiency = C
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/Destroy()
|
||||
var/turf/T = loc
|
||||
T.contents += contents
|
||||
|
||||
if(beaker)
|
||||
beaker.loc = get_step(loc, SOUTH) // Beaker is carefully ejected from the wreckage of the cryotube.
|
||||
beaker = null
|
||||
return ..()
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/process_atmos()
|
||||
..()
|
||||
var/datum/gas_mixture/air_contents = AIR1
|
||||
|
||||
if(air_contents)
|
||||
temperature_archived = air_contents.temperature
|
||||
heat_gas_contents()
|
||||
if(abs(temperature_archived-air_contents.temperature) > 1)
|
||||
update_parents()
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/process()
|
||||
..()
|
||||
if(occupant && occupant.health >= 100)
|
||||
on = 0
|
||||
playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
|
||||
if(autoEject)
|
||||
open_machine()
|
||||
|
||||
if(!NODE1 || !is_operational())
|
||||
return
|
||||
if(AIR1)
|
||||
if(on && occupant)
|
||||
process_occupant()
|
||||
expel_gas()
|
||||
|
||||
return 1
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/target, mob/user)
|
||||
if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !iscarbon(target))
|
||||
return
|
||||
close_machine(target)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/relaymove(mob/user)
|
||||
return
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist()
|
||||
usr << "<span class='notice'>You struggle inside the cryotube, kicking the release with your foot.</span>"
|
||||
sleep(150)
|
||||
if(!src || !usr || (!occupant && !contents.Find(usr))) // Make sure they didn't disappear.
|
||||
return
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/examine(mob/user)
|
||||
..()
|
||||
|
||||
var/list/otherstuff = contents - beaker
|
||||
if(otherstuff.len > 0)
|
||||
user << "You can just about make out some loose objects floating in the murk:"
|
||||
for(var/atom/movable/floater in otherstuff)
|
||||
user << "\icon[floater] [floater.name]"
|
||||
else
|
||||
user << "Seems empty."
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/attack_hand(mob/user)
|
||||
if(..() | !user)
|
||||
return
|
||||
interact(user)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/interact(mob/user)
|
||||
if(panel_open)
|
||||
return
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0)
|
||||
ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "cryo", name, 520, 600, state = notcontained_state)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/get_ui_data()
|
||||
// this is the data which will be sent to the ui
|
||||
var/datum/gas_mixture/air_contents = AIR1
|
||||
|
||||
var/data = list()
|
||||
data["isOperating"] = on
|
||||
data["hasOccupant"] = occupant ? 1 : 0
|
||||
data["autoEject"] = autoEject
|
||||
|
||||
var/occupantData = list()
|
||||
if (!occupant)
|
||||
occupantData["name"] = null
|
||||
occupantData["stat"] = null
|
||||
occupantData["health"] = null
|
||||
occupantData["maxHealth"] = null
|
||||
occupantData["minHealth"] = null
|
||||
occupantData["bruteLoss"] = null
|
||||
occupantData["oxyLoss"] = null
|
||||
occupantData["toxLoss"] = null
|
||||
occupantData["fireLoss"] = null
|
||||
occupantData["bodyTemperature"] = null
|
||||
else
|
||||
occupantData["name"] = occupant.name
|
||||
occupantData["stat"] = occupant.stat
|
||||
occupantData["health"] = occupant.health
|
||||
occupantData["maxHealth"] = occupant.maxHealth
|
||||
occupantData["minHealth"] = config.health_threshold_dead
|
||||
occupantData["bruteLoss"] = occupant.getBruteLoss()
|
||||
occupantData["oxyLoss"] = occupant.getOxyLoss()
|
||||
occupantData["toxLoss"] = occupant.getToxLoss()
|
||||
occupantData["fireLoss"] = occupant.getFireLoss()
|
||||
occupantData["bodyTemperature"] = occupant.bodytemperature
|
||||
data["occupant"] = occupantData
|
||||
|
||||
data["isOpen"] = state_open
|
||||
data["cellTemperature"] = round(air_contents.temperature)
|
||||
data["cellTemperatureStatus"] = "good"
|
||||
if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius)
|
||||
data["cellTemperatureStatus"] = "bad"
|
||||
else if(air_contents.temperature > 225)
|
||||
data["cellTemperatureStatus"] = "average"
|
||||
|
||||
data["isBeakerLoaded"] = beaker ? 1 : 0
|
||||
var beakerContents[0]
|
||||
if(beaker && beaker.reagents && beaker.reagents.reagent_list.len)
|
||||
for(var/datum/reagent/R in beaker.reagents.reagent_list)
|
||||
beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list...
|
||||
data["beakerContents"] = beakerContents
|
||||
return data
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("open")
|
||||
open_machine()
|
||||
if("close")
|
||||
close_machine()
|
||||
if("autoeject")
|
||||
autoEject = !autoEject
|
||||
if("on")
|
||||
if(!state_open)
|
||||
on = 1
|
||||
if("off")
|
||||
on = 0
|
||||
if("ejectbeaker")
|
||||
if(beaker)
|
||||
beaker.loc = get_step(loc, SOUTH)
|
||||
beaker = null
|
||||
update_icon()
|
||||
return 1
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/weapon/reagent_containers/glass))
|
||||
if(isrobot(user))
|
||||
return
|
||||
if(beaker)
|
||||
user << "<span class='warning'>A beaker is already loaded into [src]!</span>"
|
||||
return
|
||||
if(!user.drop_item())
|
||||
return
|
||||
|
||||
beaker = I
|
||||
I.loc = src
|
||||
user.visible_message("[user] places [I] in [src].", \
|
||||
"<span class='notice'>You place [I] in [src].</span>")
|
||||
|
||||
if(!(on || occupant || state_open))
|
||||
if(default_deconstruction_screwdriver(user, "cell-o", "cell-off", I))
|
||||
return
|
||||
|
||||
if(default_change_direction_wrench(user, I))
|
||||
return
|
||||
|
||||
if(exchange_parts(user, I))
|
||||
return
|
||||
|
||||
if(default_pry_open(I))
|
||||
return
|
||||
|
||||
default_deconstruction_crowbar(I)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/open_machine()
|
||||
if(!state_open && !panel_open)
|
||||
on = 0
|
||||
layer = 3
|
||||
if(occupant)
|
||||
occupant.bodytemperature = Clamp(occupant.bodytemperature, 261, 360)
|
||||
..()
|
||||
if(beaker)
|
||||
beaker.loc = src
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/M)
|
||||
if(state_open && !panel_open)
|
||||
layer = 4
|
||||
..(M)
|
||||
return occupant
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/update_icon()
|
||||
if(panel_open)
|
||||
icon_state = "cell-o"
|
||||
return
|
||||
if(state_open)
|
||||
icon_state = "cell-open"
|
||||
return
|
||||
if(on && is_operational())
|
||||
if(occupant)
|
||||
icon_state = "cell-occupied"
|
||||
else
|
||||
icon_state = "cell-on"
|
||||
else
|
||||
icon_state = "cell-off"
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/power_change()
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/proc/process_occupant()
|
||||
var/datum/gas_mixture/air_contents = AIR1
|
||||
if(!on)
|
||||
return
|
||||
if(air_contents.total_moles() < 10)
|
||||
return
|
||||
if(occupant)
|
||||
if(occupant.stat == 2 || occupant.health >= 100) //Why waste energy on dead or healthy people
|
||||
occupant.bodytemperature = T0C
|
||||
return
|
||||
occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature) * current_heat_capacity / (current_heat_capacity + air_contents.heat_capacity())
|
||||
occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise //TODO: fix someone else's broken promise - duncathan
|
||||
if(occupant.bodytemperature < T0C)
|
||||
occupant.sleeping = max(5 / efficiency, (1 / occupant.bodytemperature) * 2000 / efficiency)
|
||||
occupant.Paralyse(max(5 / efficiency, (1 / occupant.bodytemperature) * 3000 / efficiency))
|
||||
if(air_contents.oxygen > 2)
|
||||
if(occupant.getOxyLoss()) occupant.adjustOxyLoss(-1)
|
||||
else
|
||||
occupant.adjustOxyLoss(-1)
|
||||
// Severe damage should heal waaay slower without proper chemicals...
|
||||
if(occupant.bodytemperature < 225)
|
||||
if(occupant.getToxLoss())
|
||||
occupant.adjustToxLoss(max(-efficiency, (-20*(efficiency ** 2)) / occupant.getToxLoss()))
|
||||
var/heal_brute = occupant.getBruteLoss() ? min(efficiency, 20*(efficiency**2) / occupant.getBruteLoss()) : 0
|
||||
var/heal_fire = occupant.getFireLoss() ? min(efficiency, 20*(efficiency**2) / occupant.getFireLoss()) : 0
|
||||
occupant.heal_organ_damage(heal_brute, heal_fire)
|
||||
if(beaker && next_trans == 0)
|
||||
beaker.reagents.trans_to(occupant, 1, 10)
|
||||
beaker.reagents.reaction(occupant, VAPOR)
|
||||
next_trans++
|
||||
if(next_trans == 10)
|
||||
next_trans = 0
|
||||
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/proc/heat_gas_contents()
|
||||
var/datum/gas_mixture/air_contents = AIR1
|
||||
|
||||
if(air_contents.total_moles() < 1)
|
||||
return
|
||||
var/air_heat_capacity = air_contents.heat_capacity()
|
||||
var/combined_heat_capacity = current_heat_capacity + air_heat_capacity
|
||||
if(combined_heat_capacity > 0)
|
||||
var/combined_energy = T20C * current_heat_capacity + air_heat_capacity * air_contents.temperature
|
||||
air_contents.temperature = combined_energy/combined_heat_capacity
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/proc/expel_gas()
|
||||
var/datum/gas_mixture/air_contents = AIR1
|
||||
|
||||
if(air_contents.total_moles() < 1)
|
||||
return
|
||||
var/datum/gas_mixture/expel_gas = new
|
||||
var/remove_amount = air_contents.total_moles() / 100
|
||||
expel_gas = air_contents.remove(remove_amount)
|
||||
expel_gas.temperature = T20C //Lets expel hot gas and see if that helps people not die as they are removed
|
||||
loc.assume_air(expel_gas)
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell
|
||||
name = "cryo cell"
|
||||
icon = 'icons/obj/cryogenics.dmi'
|
||||
icon_state = "cell-off"
|
||||
density = 1
|
||||
anchored = 1
|
||||
layer = 4
|
||||
|
||||
var/on = 0
|
||||
var/temperature_archived
|
||||
var/obj/item/weapon/reagent_containers/glass/beaker = null
|
||||
var/next_trans = 0
|
||||
var/current_heat_capacity = 50
|
||||
state_open = 0
|
||||
var/efficiency = 1
|
||||
var/autoEject = 0
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/New()
|
||||
..()
|
||||
initialize_directions = dir
|
||||
component_parts = list()
|
||||
component_parts += new /obj/item/weapon/circuitboard/cryo_tube(null)
|
||||
component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
|
||||
component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
|
||||
component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
|
||||
component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
|
||||
component_parts += new /obj/item/stack/cable_coil(null, 1)
|
||||
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/construction()
|
||||
..(dir,dir)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/RefreshParts()
|
||||
var/C
|
||||
for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts)
|
||||
C += M.rating
|
||||
current_heat_capacity = 50 * C
|
||||
efficiency = C
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/Destroy()
|
||||
var/turf/T = loc
|
||||
T.contents += contents
|
||||
|
||||
if(beaker)
|
||||
beaker.loc = get_step(loc, SOUTH) // Beaker is carefully ejected from the wreckage of the cryotube.
|
||||
beaker = null
|
||||
return ..()
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/process_atmos()
|
||||
..()
|
||||
var/datum/gas_mixture/air_contents = AIR1
|
||||
|
||||
if(air_contents)
|
||||
temperature_archived = air_contents.temperature
|
||||
heat_gas_contents()
|
||||
if(abs(temperature_archived-air_contents.temperature) > 1)
|
||||
update_parents()
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/process()
|
||||
..()
|
||||
if(occupant && occupant.health >= 100)
|
||||
on = 0
|
||||
playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
|
||||
if(autoEject)
|
||||
open_machine()
|
||||
|
||||
if(!NODE1 || !is_operational())
|
||||
return
|
||||
if(AIR1)
|
||||
if(on && occupant)
|
||||
process_occupant()
|
||||
expel_gas()
|
||||
|
||||
return 1
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/MouseDrop_T(mob/target, mob/user)
|
||||
if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !iscarbon(target))
|
||||
return
|
||||
close_machine(target)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/relaymove(mob/user)
|
||||
return
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist()
|
||||
usr << "<span class='notice'>You struggle inside the cryotube, kicking the release with your foot.</span>"
|
||||
sleep(150)
|
||||
if(!src || !usr || (!occupant && !contents.Find(usr))) // Make sure they didn't disappear.
|
||||
return
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/examine(mob/user)
|
||||
..()
|
||||
|
||||
var/list/otherstuff = contents - beaker
|
||||
if(otherstuff.len > 0)
|
||||
user << "You can just about make out some loose objects floating in the murk:"
|
||||
for(var/atom/movable/floater in otherstuff)
|
||||
user << "\icon[floater] [floater.name]"
|
||||
else
|
||||
user << "Seems empty."
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/attack_hand(mob/user)
|
||||
if(..() | !user)
|
||||
return
|
||||
interact(user)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/interact(mob/user)
|
||||
if(panel_open)
|
||||
return
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 0)
|
||||
ui = SSnano.try_update_ui(user, src, ui_key, ui, force_open = force_open)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, "cryo", name, 410, 550, state = notcontained_state)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/get_ui_data()
|
||||
// this is the data which will be sent to the ui
|
||||
var/datum/gas_mixture/air_contents = AIR1
|
||||
|
||||
var/data = list()
|
||||
data["isOperating"] = on
|
||||
data["hasOccupant"] = occupant ? 1 : 0
|
||||
data["autoEject"] = autoEject
|
||||
|
||||
var/occupantData = list()
|
||||
if (!occupant)
|
||||
occupantData["name"] = null
|
||||
occupantData["stat"] = null
|
||||
occupantData["health"] = null
|
||||
occupantData["maxHealth"] = null
|
||||
occupantData["minHealth"] = null
|
||||
occupantData["bruteLoss"] = null
|
||||
occupantData["oxyLoss"] = null
|
||||
occupantData["toxLoss"] = null
|
||||
occupantData["fireLoss"] = null
|
||||
occupantData["bodyTemperature"] = null
|
||||
else
|
||||
occupantData["name"] = occupant.name
|
||||
occupantData["stat"] = occupant.stat
|
||||
occupantData["health"] = occupant.health
|
||||
occupantData["maxHealth"] = occupant.maxHealth
|
||||
occupantData["minHealth"] = config.health_threshold_dead
|
||||
occupantData["bruteLoss"] = occupant.getBruteLoss()
|
||||
occupantData["oxyLoss"] = occupant.getOxyLoss()
|
||||
occupantData["toxLoss"] = occupant.getToxLoss()
|
||||
occupantData["fireLoss"] = occupant.getFireLoss()
|
||||
occupantData["bodyTemperature"] = occupant.bodytemperature
|
||||
data["occupant"] = occupantData
|
||||
|
||||
data["isOpen"] = state_open
|
||||
data["cellTemperature"] = round(air_contents.temperature)
|
||||
data["cellTemperatureStatus"] = "good"
|
||||
if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius)
|
||||
data["cellTemperatureStatus"] = "bad"
|
||||
else if(air_contents.temperature > 225)
|
||||
data["cellTemperatureStatus"] = "average"
|
||||
|
||||
data["isBeakerLoaded"] = beaker ? 1 : 0
|
||||
var beakerContents[0]
|
||||
if(beaker && beaker.reagents && beaker.reagents.reagent_list.len)
|
||||
for(var/datum/reagent/R in beaker.reagents.reagent_list)
|
||||
beakerContents.Add(list(list("name" = R.name, "volume" = R.volume))) // list in a list because Byond merges the first list...
|
||||
data["beakerContents"] = beakerContents
|
||||
return data
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("open")
|
||||
open_machine()
|
||||
if("close")
|
||||
close_machine()
|
||||
if("autoeject")
|
||||
autoEject = !autoEject
|
||||
if("on")
|
||||
if(!state_open)
|
||||
on = 1
|
||||
if("off")
|
||||
on = 0
|
||||
if("ejectbeaker")
|
||||
if(beaker)
|
||||
beaker.loc = get_step(loc, SOUTH)
|
||||
beaker = null
|
||||
update_icon()
|
||||
return 1
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/weapon/reagent_containers/glass))
|
||||
if(isrobot(user))
|
||||
return
|
||||
if(beaker)
|
||||
user << "<span class='warning'>A beaker is already loaded into [src]!</span>"
|
||||
return
|
||||
if(!user.drop_item())
|
||||
return
|
||||
|
||||
beaker = I
|
||||
I.loc = src
|
||||
user.visible_message("[user] places [I] in [src].", \
|
||||
"<span class='notice'>You place [I] in [src].</span>")
|
||||
|
||||
if(!(on || occupant || state_open))
|
||||
if(default_deconstruction_screwdriver(user, "cell-o", "cell-off", I))
|
||||
return
|
||||
|
||||
if(default_change_direction_wrench(user, I))
|
||||
return
|
||||
|
||||
if(exchange_parts(user, I))
|
||||
return
|
||||
|
||||
if(default_pry_open(I))
|
||||
return
|
||||
|
||||
default_deconstruction_crowbar(I)
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/open_machine()
|
||||
if(!state_open && !panel_open)
|
||||
on = 0
|
||||
layer = 3
|
||||
if(occupant)
|
||||
occupant.bodytemperature = Clamp(occupant.bodytemperature, 261, 360)
|
||||
..()
|
||||
if(beaker)
|
||||
beaker.loc = src
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/M)
|
||||
if(state_open && !panel_open)
|
||||
layer = 4
|
||||
..(M)
|
||||
return occupant
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/update_icon()
|
||||
if(panel_open)
|
||||
icon_state = "cell-o"
|
||||
return
|
||||
if(state_open)
|
||||
icon_state = "cell-open"
|
||||
return
|
||||
if(on && is_operational())
|
||||
if(occupant)
|
||||
icon_state = "cell-occupied"
|
||||
else
|
||||
icon_state = "cell-on"
|
||||
else
|
||||
icon_state = "cell-off"
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/power_change()
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/proc/process_occupant()
|
||||
var/datum/gas_mixture/air_contents = AIR1
|
||||
if(!on)
|
||||
return
|
||||
if(air_contents.total_moles() < 10)
|
||||
return
|
||||
if(occupant)
|
||||
if(occupant.stat == 2 || occupant.health >= 100) //Why waste energy on dead or healthy people
|
||||
occupant.bodytemperature = T0C
|
||||
return
|
||||
occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature) * current_heat_capacity / (current_heat_capacity + air_contents.heat_capacity())
|
||||
occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise //TODO: fix someone else's broken promise - duncathan
|
||||
if(occupant.bodytemperature < T0C)
|
||||
occupant.sleeping = max(5 / efficiency, (1 / occupant.bodytemperature) * 2000 / efficiency)
|
||||
occupant.Paralyse(max(5 / efficiency, (1 / occupant.bodytemperature) * 3000 / efficiency))
|
||||
if(air_contents.oxygen > 2)
|
||||
if(occupant.getOxyLoss()) occupant.adjustOxyLoss(-1)
|
||||
else
|
||||
occupant.adjustOxyLoss(-1)
|
||||
// Severe damage should heal waaay slower without proper chemicals...
|
||||
if(occupant.bodytemperature < 225)
|
||||
if(occupant.getToxLoss())
|
||||
occupant.adjustToxLoss(max(-efficiency, (-20*(efficiency ** 2)) / occupant.getToxLoss()))
|
||||
var/heal_brute = occupant.getBruteLoss() ? min(efficiency, 20*(efficiency**2) / occupant.getBruteLoss()) : 0
|
||||
var/heal_fire = occupant.getFireLoss() ? min(efficiency, 20*(efficiency**2) / occupant.getFireLoss()) : 0
|
||||
occupant.heal_organ_damage(heal_brute, heal_fire)
|
||||
if(beaker && next_trans == 0)
|
||||
beaker.reagents.trans_to(occupant, 1, 10)
|
||||
beaker.reagents.reaction(occupant, VAPOR)
|
||||
next_trans++
|
||||
if(next_trans == 10)
|
||||
next_trans = 0
|
||||
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/proc/heat_gas_contents()
|
||||
var/datum/gas_mixture/air_contents = AIR1
|
||||
|
||||
if(air_contents.total_moles() < 1)
|
||||
return
|
||||
var/air_heat_capacity = air_contents.heat_capacity()
|
||||
var/combined_heat_capacity = current_heat_capacity + air_heat_capacity
|
||||
if(combined_heat_capacity > 0)
|
||||
var/combined_energy = T20C * current_heat_capacity + air_heat_capacity * air_contents.temperature
|
||||
air_contents.temperature = combined_energy/combined_heat_capacity
|
||||
|
||||
/obj/machinery/atmospherics/components/unary/cryo_cell/proc/expel_gas()
|
||||
var/datum/gas_mixture/air_contents = AIR1
|
||||
|
||||
if(air_contents.total_moles() < 1)
|
||||
return
|
||||
var/datum/gas_mixture/expel_gas = new
|
||||
var/remove_amount = air_contents.total_moles() / 100
|
||||
expel_gas = air_contents.remove(remove_amount)
|
||||
expel_gas.temperature = T20C //Lets expel hot gas and see if that helps people not die as they are removed
|
||||
loc.assume_air(expel_gas)
|
||||
air_update_turf()
|
||||
+1
-1
@@ -68,7 +68,7 @@
|
||||
if(current) //remove ourself from our old body's mind variable
|
||||
current.mind = null
|
||||
|
||||
SSnano.user_transferred(current, new_character)
|
||||
SSnano.on_transfer(current, new_character)
|
||||
|
||||
if(key)
|
||||
if(new_character.key != key) //if we're transfering into a body with a key associated which is not ours
|
||||
|
||||
+233
-240
@@ -1,240 +1,233 @@
|
||||
/*
|
||||
Asset cache quick users guide:
|
||||
|
||||
Make a datum at the bottom of this file with your assets for your thing.
|
||||
The simple subsystem will most like be of use for most cases.
|
||||
Then call get_asset_datum() with the type of the datum you created and store the return
|
||||
Then call .send(client) on that stored return value.
|
||||
|
||||
You can set verify to TRUE if you want send() to sleep until the client has the assets.
|
||||
*/
|
||||
|
||||
|
||||
// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping.
|
||||
// This is doubled for the first asset, then added per asset after
|
||||
#define ASSET_CACHE_SEND_TIMEOUT 7
|
||||
|
||||
//When sending mutiple assets, how many before we give the client a quaint little sending resources message
|
||||
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
|
||||
|
||||
/client
|
||||
var/list/cache = list() // List of all assets sent to this client by the asset cache.
|
||||
var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement.
|
||||
var/list/sending = list()
|
||||
var/last_asset_job = 0 // Last job done.
|
||||
|
||||
//This proc sends the asset to the client, but only if it needs it.
|
||||
//This proc blocks(sleeps) unless verify is set to false
|
||||
/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE)
|
||||
if(!istype(client))
|
||||
if(ismob(client))
|
||||
var/mob/M = client
|
||||
if(M.client)
|
||||
client = M.client
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
if(client.cache.Find(asset_name) || client.sending.Find(asset_name))
|
||||
return 0
|
||||
|
||||
client << browse_rsc(SSasset.cache[asset_name], asset_name)
|
||||
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
|
||||
if (client)
|
||||
client.cache += asset_name
|
||||
return 1
|
||||
if (!client)
|
||||
return 0
|
||||
|
||||
client.sending |= asset_name
|
||||
var/job = ++client.last_asset_job
|
||||
|
||||
client << browse({"
|
||||
<script>
|
||||
window.location.href="?asset_cache_confirm_arrival=[job]"
|
||||
</script>
|
||||
"}, "window=asset_cache_browser")
|
||||
|
||||
var/t = 0
|
||||
var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT
|
||||
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
|
||||
sleep(1) // Lock up the caller until this is received.
|
||||
t++
|
||||
|
||||
if(client)
|
||||
client.sending -= asset_name
|
||||
client.cache |= asset_name
|
||||
client.completed_asset_jobs -= job
|
||||
|
||||
return 1
|
||||
|
||||
//This proc blocks(sleeps) unless verify is set to false
|
||||
/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE)
|
||||
if(!istype(client))
|
||||
if(ismob(client))
|
||||
var/mob/M = client
|
||||
if(M.client)
|
||||
client = M.client
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
var/list/unreceived = asset_list - (client.cache + client.sending)
|
||||
if(!unreceived || !unreceived.len)
|
||||
return 0
|
||||
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
|
||||
client << "Sending Resources..."
|
||||
for(var/asset in unreceived)
|
||||
if (asset in SSasset.cache)
|
||||
client << browse_rsc(SSasset.cache[asset], asset)
|
||||
|
||||
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
|
||||
if (client)
|
||||
client.cache += unreceived
|
||||
return 1
|
||||
if (!client)
|
||||
return 0
|
||||
client.sending |= unreceived
|
||||
var/job = ++client.last_asset_job
|
||||
|
||||
client << browse({"
|
||||
<script>
|
||||
window.location.href="?asset_cache_confirm_arrival=[job]"
|
||||
</script>
|
||||
"}, "window=asset_cache_browser")
|
||||
|
||||
var/t = 0
|
||||
var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len
|
||||
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
|
||||
sleep(1) // Lock up the caller until this is received.
|
||||
t++
|
||||
|
||||
if(client)
|
||||
client.sending -= unreceived
|
||||
client.cache |= unreceived
|
||||
client.completed_asset_jobs -= job
|
||||
|
||||
return 1
|
||||
|
||||
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
|
||||
//The proc calls procs that sleep for long times.
|
||||
/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
|
||||
for(var/file in files)
|
||||
if (!client)
|
||||
break
|
||||
if (register_asset)
|
||||
register_asset(file,files[file])
|
||||
send_asset(client,file)
|
||||
sleep(-1) //queuing calls like this too quickly can cause issues in some client versions
|
||||
|
||||
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
|
||||
//if it's an icon or something be careful, you'll have to copy it before further use.
|
||||
/proc/register_asset(var/asset_name, var/asset)
|
||||
SSasset.cache[asset_name] = asset
|
||||
|
||||
//These datums are used to populate the asset cache, the proc "register()" does this.
|
||||
|
||||
//all of our asset datums, used for referring to these later
|
||||
/var/global/list/asset_datums = list()
|
||||
|
||||
//get a assetdatum or make a new one
|
||||
/proc/get_asset_datum(var/type)
|
||||
if (!(type in asset_datums))
|
||||
return new type()
|
||||
return asset_datums[type]
|
||||
|
||||
/datum/asset/New()
|
||||
asset_datums[type] = src
|
||||
|
||||
/datum/asset/proc/register()
|
||||
return
|
||||
|
||||
/datum/asset/proc/send(client)
|
||||
return
|
||||
|
||||
//If you don't need anything complicated.
|
||||
/datum/asset/simple
|
||||
var/assets = list()
|
||||
var/verify = FALSE
|
||||
|
||||
/datum/asset/simple/register()
|
||||
for(var/asset_name in assets)
|
||||
register_asset(asset_name, assets[asset_name])
|
||||
/datum/asset/simple/send(client)
|
||||
send_asset_list(client,assets,verify)
|
||||
|
||||
|
||||
//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',
|
||||
"pda_back.png" = 'icons/pda_icons/pda_back.png',
|
||||
"pda_bell.png" = 'icons/pda_icons/pda_bell.png',
|
||||
"pda_blank.png" = 'icons/pda_icons/pda_blank.png',
|
||||
"pda_boom.png" = 'icons/pda_icons/pda_boom.png',
|
||||
"pda_bucket.png" = 'icons/pda_icons/pda_bucket.png',
|
||||
"pda_medbot.png" = 'icons/pda_icons/pda_medbot.png',
|
||||
"pda_floorbot.png" = 'icons/pda_icons/pda_floorbot.png',
|
||||
"pda_cleanbot.png" = 'icons/pda_icons/pda_cleanbot.png',
|
||||
"pda_crate.png" = 'icons/pda_icons/pda_crate.png',
|
||||
"pda_cuffs.png" = 'icons/pda_icons/pda_cuffs.png',
|
||||
"pda_eject.png" = 'icons/pda_icons/pda_eject.png',
|
||||
"pda_exit.png" = 'icons/pda_icons/pda_exit.png',
|
||||
"pda_flashlight.png" = 'icons/pda_icons/pda_flashlight.png',
|
||||
"pda_honk.png" = 'icons/pda_icons/pda_honk.png',
|
||||
"pda_mail.png" = 'icons/pda_icons/pda_mail.png',
|
||||
"pda_medical.png" = 'icons/pda_icons/pda_medical.png',
|
||||
"pda_menu.png" = 'icons/pda_icons/pda_menu.png',
|
||||
"pda_mule.png" = 'icons/pda_icons/pda_mule.png',
|
||||
"pda_notes.png" = 'icons/pda_icons/pda_notes.png',
|
||||
"pda_power.png" = 'icons/pda_icons/pda_power.png',
|
||||
"pda_rdoor.png" = 'icons/pda_icons/pda_rdoor.png',
|
||||
"pda_reagent.png" = 'icons/pda_icons/pda_reagent.png',
|
||||
"pda_refresh.png" = 'icons/pda_icons/pda_refresh.png',
|
||||
"pda_scanner.png" = 'icons/pda_icons/pda_scanner.png',
|
||||
"pda_signaler.png" = 'icons/pda_icons/pda_signaler.png',
|
||||
"pda_status.png" = 'icons/pda_icons/pda_status.png'
|
||||
)
|
||||
|
||||
/datum/asset/simple/paper
|
||||
assets = list(
|
||||
"large_stamp-clown.png" = 'icons/stamp_icons/large_stamp-clown.png',
|
||||
"large_stamp-deny.png" = 'icons/stamp_icons/large_stamp-deny.png',
|
||||
"large_stamp-ok.png" = 'icons/stamp_icons/large_stamp-ok.png',
|
||||
"large_stamp-hop.png" = 'icons/stamp_icons/large_stamp-hop.png',
|
||||
"large_stamp-cmo.png" = 'icons/stamp_icons/large_stamp-cmo.png',
|
||||
"large_stamp-ce.png" = 'icons/stamp_icons/large_stamp-ce.png',
|
||||
"large_stamp-hos.png" = 'icons/stamp_icons/large_stamp-hos.png',
|
||||
"large_stamp-rd.png" = 'icons/stamp_icons/large_stamp-rd.png',
|
||||
"large_stamp-cap.png" = 'icons/stamp_icons/large_stamp-cap.png',
|
||||
"large_stamp-qm.png" = 'icons/stamp_icons/large_stamp-qm.png',
|
||||
"large_stamp-law.png" = 'icons/stamp_icons/large_stamp-law.png'
|
||||
)
|
||||
|
||||
|
||||
//Registers HTML Interface assets.
|
||||
/datum/asset/HTML_interface/register()
|
||||
for(var/path in typesof(/datum/html_interface))
|
||||
var/datum/html_interface/hi = new path()
|
||||
hi.registerResources()
|
||||
/*
|
||||
Asset cache quick users guide:
|
||||
|
||||
Make a datum at the bottom of this file with your assets for your thing.
|
||||
The simple subsystem will most like be of use for most cases.
|
||||
Then call get_asset_datum() with the type of the datum you created and store the return
|
||||
Then call .send(client) on that stored return value.
|
||||
|
||||
You can set verify to TRUE if you want send() to sleep until the client has the assets.
|
||||
*/
|
||||
|
||||
|
||||
// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping.
|
||||
// This is doubled for the first asset, then added per asset after
|
||||
#define ASSET_CACHE_SEND_TIMEOUT 7
|
||||
|
||||
//When sending mutiple assets, how many before we give the client a quaint little sending resources message
|
||||
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
|
||||
|
||||
/client
|
||||
var/list/cache = list() // List of all assets sent to this client by the asset cache.
|
||||
var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement.
|
||||
var/list/sending = list()
|
||||
var/last_asset_job = 0 // Last job done.
|
||||
|
||||
//This proc sends the asset to the client, but only if it needs it.
|
||||
//This proc blocks(sleeps) unless verify is set to false
|
||||
/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE)
|
||||
if(!istype(client))
|
||||
if(ismob(client))
|
||||
var/mob/M = client
|
||||
if(M.client)
|
||||
client = M.client
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
if(client.cache.Find(asset_name) || client.sending.Find(asset_name))
|
||||
return 0
|
||||
|
||||
client << browse_rsc(SSasset.cache[asset_name], asset_name)
|
||||
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
|
||||
if (client)
|
||||
client.cache += asset_name
|
||||
return 1
|
||||
if (!client)
|
||||
return 0
|
||||
|
||||
client.sending |= asset_name
|
||||
var/job = ++client.last_asset_job
|
||||
|
||||
client << browse({"
|
||||
<script>
|
||||
window.location.href="?asset_cache_confirm_arrival=[job]"
|
||||
</script>
|
||||
"}, "window=asset_cache_browser")
|
||||
|
||||
var/t = 0
|
||||
var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT
|
||||
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
|
||||
sleep(1) // Lock up the caller until this is received.
|
||||
t++
|
||||
|
||||
if(client)
|
||||
client.sending -= asset_name
|
||||
client.cache |= asset_name
|
||||
client.completed_asset_jobs -= job
|
||||
|
||||
return 1
|
||||
|
||||
//This proc blocks(sleeps) unless verify is set to false
|
||||
/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE)
|
||||
if(!istype(client))
|
||||
if(ismob(client))
|
||||
var/mob/M = client
|
||||
if(M.client)
|
||||
client = M.client
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
var/list/unreceived = asset_list - (client.cache + client.sending)
|
||||
if(!unreceived || !unreceived.len)
|
||||
return 0
|
||||
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
|
||||
client << "Sending Resources..."
|
||||
for(var/asset in unreceived)
|
||||
if (asset in SSasset.cache)
|
||||
client << browse_rsc(SSasset.cache[asset], asset)
|
||||
|
||||
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
|
||||
if (client)
|
||||
client.cache += unreceived
|
||||
return 1
|
||||
if (!client)
|
||||
return 0
|
||||
client.sending |= unreceived
|
||||
var/job = ++client.last_asset_job
|
||||
|
||||
client << browse({"
|
||||
<script>
|
||||
window.location.href="?asset_cache_confirm_arrival=[job]"
|
||||
</script>
|
||||
"}, "window=asset_cache_browser")
|
||||
|
||||
var/t = 0
|
||||
var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len
|
||||
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
|
||||
sleep(1) // Lock up the caller until this is received.
|
||||
t++
|
||||
|
||||
if(client)
|
||||
client.sending -= unreceived
|
||||
client.cache |= unreceived
|
||||
client.completed_asset_jobs -= job
|
||||
|
||||
return 1
|
||||
|
||||
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
|
||||
//The proc calls procs that sleep for long times.
|
||||
/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
|
||||
for(var/file in files)
|
||||
if (!client)
|
||||
break
|
||||
if (register_asset)
|
||||
register_asset(file,files[file])
|
||||
send_asset(client,file)
|
||||
sleep(-1) //queuing calls like this too quickly can cause issues in some client versions
|
||||
|
||||
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
|
||||
//if it's an icon or something be careful, you'll have to copy it before further use.
|
||||
/proc/register_asset(var/asset_name, var/asset)
|
||||
SSasset.cache[asset_name] = asset
|
||||
|
||||
//These datums are used to populate the asset cache, the proc "register()" does this.
|
||||
|
||||
//all of our asset datums, used for referring to these later
|
||||
/var/global/list/asset_datums = list()
|
||||
|
||||
//get a assetdatum or make a new one
|
||||
/proc/get_asset_datum(var/type)
|
||||
if (!(type in asset_datums))
|
||||
return new type()
|
||||
return asset_datums[type]
|
||||
|
||||
/datum/asset/New()
|
||||
asset_datums[type] = src
|
||||
|
||||
/datum/asset/proc/register()
|
||||
return
|
||||
|
||||
/datum/asset/proc/send(client)
|
||||
return
|
||||
|
||||
//If you don't need anything complicated.
|
||||
/datum/asset/simple
|
||||
var/assets = list()
|
||||
var/verify = FALSE
|
||||
|
||||
/datum/asset/simple/register()
|
||||
for(var/asset_name in assets)
|
||||
register_asset(asset_name, assets[asset_name])
|
||||
/datum/asset/simple/send(client)
|
||||
send_asset_list(client,assets,verify)
|
||||
|
||||
|
||||
//DEFINITIONS FOR ASSET DATUMS START HERE.
|
||||
|
||||
|
||||
/datum/asset/simple/nanoui
|
||||
assets = list(
|
||||
"nanoui.js" = 'nano/assets/nanoui.js',
|
||||
"nanoui.css" = 'nano/assets/nanoui.css',
|
||||
)
|
||||
|
||||
/datum/asset/simple/pda
|
||||
assets = list(
|
||||
"pda_atmos.png" = 'icons/pda_icons/pda_atmos.png',
|
||||
"pda_back.png" = 'icons/pda_icons/pda_back.png',
|
||||
"pda_bell.png" = 'icons/pda_icons/pda_bell.png',
|
||||
"pda_blank.png" = 'icons/pda_icons/pda_blank.png',
|
||||
"pda_boom.png" = 'icons/pda_icons/pda_boom.png',
|
||||
"pda_bucket.png" = 'icons/pda_icons/pda_bucket.png',
|
||||
"pda_medbot.png" = 'icons/pda_icons/pda_medbot.png',
|
||||
"pda_floorbot.png" = 'icons/pda_icons/pda_floorbot.png',
|
||||
"pda_cleanbot.png" = 'icons/pda_icons/pda_cleanbot.png',
|
||||
"pda_crate.png" = 'icons/pda_icons/pda_crate.png',
|
||||
"pda_cuffs.png" = 'icons/pda_icons/pda_cuffs.png',
|
||||
"pda_eject.png" = 'icons/pda_icons/pda_eject.png',
|
||||
"pda_exit.png" = 'icons/pda_icons/pda_exit.png',
|
||||
"pda_flashlight.png" = 'icons/pda_icons/pda_flashlight.png',
|
||||
"pda_honk.png" = 'icons/pda_icons/pda_honk.png',
|
||||
"pda_mail.png" = 'icons/pda_icons/pda_mail.png',
|
||||
"pda_medical.png" = 'icons/pda_icons/pda_medical.png',
|
||||
"pda_menu.png" = 'icons/pda_icons/pda_menu.png',
|
||||
"pda_mule.png" = 'icons/pda_icons/pda_mule.png',
|
||||
"pda_notes.png" = 'icons/pda_icons/pda_notes.png',
|
||||
"pda_power.png" = 'icons/pda_icons/pda_power.png',
|
||||
"pda_rdoor.png" = 'icons/pda_icons/pda_rdoor.png',
|
||||
"pda_reagent.png" = 'icons/pda_icons/pda_reagent.png',
|
||||
"pda_refresh.png" = 'icons/pda_icons/pda_refresh.png',
|
||||
"pda_scanner.png" = 'icons/pda_icons/pda_scanner.png',
|
||||
"pda_signaler.png" = 'icons/pda_icons/pda_signaler.png',
|
||||
"pda_status.png" = 'icons/pda_icons/pda_status.png'
|
||||
)
|
||||
|
||||
/datum/asset/simple/paper
|
||||
assets = list(
|
||||
"large_stamp-clown.png" = 'icons/stamp_icons/large_stamp-clown.png',
|
||||
"large_stamp-deny.png" = 'icons/stamp_icons/large_stamp-deny.png',
|
||||
"large_stamp-ok.png" = 'icons/stamp_icons/large_stamp-ok.png',
|
||||
"large_stamp-hop.png" = 'icons/stamp_icons/large_stamp-hop.png',
|
||||
"large_stamp-cmo.png" = 'icons/stamp_icons/large_stamp-cmo.png',
|
||||
"large_stamp-ce.png" = 'icons/stamp_icons/large_stamp-ce.png',
|
||||
"large_stamp-hos.png" = 'icons/stamp_icons/large_stamp-hos.png',
|
||||
"large_stamp-rd.png" = 'icons/stamp_icons/large_stamp-rd.png',
|
||||
"large_stamp-cap.png" = 'icons/stamp_icons/large_stamp-cap.png',
|
||||
"large_stamp-qm.png" = 'icons/stamp_icons/large_stamp-qm.png',
|
||||
"large_stamp-law.png" = 'icons/stamp_icons/large_stamp-law.png'
|
||||
)
|
||||
|
||||
|
||||
//Registers HTML Interface assets.
|
||||
/datum/asset/HTML_interface/register()
|
||||
for(var/path in typesof(/datum/html_interface))
|
||||
var/datum/html_interface/hi = new path()
|
||||
hi.registerResources()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/mob/Logout()
|
||||
SSnano.user_logout(src) // this is used to clean up (remove) this user's Nano UIs
|
||||
SSnano.on_logout(src)
|
||||
player_list -= src
|
||||
log_access("Logout: [key_name(src)]")
|
||||
if(admin_datums[src.ckey])
|
||||
|
||||
@@ -27,6 +27,20 @@
|
||||
datum/topic_state/state = default_state)
|
||||
return -1 // Sorta implemented.
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Data to be sent to the NanoUI.
|
||||
* This must be implemented for a NanoUI to work.
|
||||
*
|
||||
* required user mob The mob interacting with the NanoUI.
|
||||
*
|
||||
* return list Data to be sent to the NanoUI.
|
||||
**/
|
||||
/atom/movable/proc/get_ui_data(mob/user)
|
||||
return list() // Not implemented.
|
||||
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
@@ -41,18 +55,6 @@
|
||||
/atom/movable/proc/ui_act(action, list/params)
|
||||
return // Not implemented.
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Data to be sent to the NanoUI.
|
||||
* This must be implemented for a NanoUI to work.
|
||||
*
|
||||
* required user mob The mob interacting with the NanoUI.
|
||||
*
|
||||
* return list Data to be sent to the NanoUI.
|
||||
**/
|
||||
/atom/movable/proc/get_ui_data(mob/user)
|
||||
return list() // Not implemented.
|
||||
|
||||
/**
|
||||
* private
|
||||
@@ -67,7 +69,6 @@
|
||||
* global
|
||||
*
|
||||
* Used to track NanoUIs for a mob.
|
||||
*
|
||||
**/
|
||||
/mob/var/list/open_uis = list()
|
||||
|
||||
|
||||
+403
-405
@@ -1,405 +1,403 @@
|
||||
/**
|
||||
* NanoUI
|
||||
*
|
||||
* Contains the NanoUI datum, and its procs.
|
||||
*
|
||||
* /tg/station user interface library
|
||||
* thanks to baystation12
|
||||
*
|
||||
* modified by neersighted
|
||||
**/
|
||||
|
||||
/**
|
||||
* NanoUI datum:
|
||||
*
|
||||
* Represents a NanoUI.
|
||||
**/
|
||||
/datum/nanoui
|
||||
var/mob/user // The mob who opened/is using the NanoUI.
|
||||
var/atom/movable/src_object // The object which owns the NanoUI.
|
||||
var/title // The title of te NanoUI.
|
||||
var/ui_key // The ui_key of the NanoUI. This allows multiple UIs for one src_object.
|
||||
var/window_id // The window_id for browse() and onclose().
|
||||
var/width = 0 // The window width.
|
||||
var/height = 0 // The window height
|
||||
var/window_options = list( // Extra options to winset().
|
||||
"focus" = 0,
|
||||
"titlebar" = 1,
|
||||
"can_resize" = 1,
|
||||
"can_minimize" = 1,
|
||||
"can_maximize" = 0,
|
||||
"can_close" = 1,
|
||||
"auto_format" = 0
|
||||
)
|
||||
var/atom/ref = null // An extra ref to call when the window is closed.
|
||||
var/layout = "nanotrasen" // The layout to be used for this UI.
|
||||
var/template // The template to be used for this UI.
|
||||
var/auto_update = 1 // Update the NanoUI every MC tick.
|
||||
var/list/initial_data // The data (and datastructure) used to initialize the NanoUI
|
||||
var/status = NANO_INTERACTIVE // The status/visibility of the NanoUI.
|
||||
var/datum/topic_state/state = null // Topic state used to determine status. Topic states are in interactions/.
|
||||
var/datum/nanoui/master_ui // The parent NanoUI.
|
||||
var/list/datum/nanoui/children = list() // Children of this NanoUI.
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Create a new NanoUI.
|
||||
*
|
||||
* required user mob The mob who opened/is using the NanoUI.
|
||||
* required src_object atom/movable The object which owns the NanoUI.
|
||||
* required ui_key string The ui_key of the NanoUI.
|
||||
* required template string The template to render the NanoUI content with.
|
||||
* optional title string The title of the NanoUI.
|
||||
* optional width int The window width.
|
||||
* optional height int The window height.
|
||||
* optional ref atom An extra ref to use when the window is closed.
|
||||
* optional master_ui datum/nanoui The parent NanoUI.
|
||||
* optional state datum/topic_state The state used to determine status.
|
||||
*
|
||||
* return datum/nanoui The requested NanoUI.
|
||||
**/
|
||||
/datum/nanoui/New(mob/user, atom/movable/src_object, ui_key, template, \
|
||||
title = 0, width = 0, height = 0, \
|
||||
atom/ref = null, datum/nanoui/master_ui = null, \
|
||||
datum/topic_state/state = default_state)
|
||||
src.user = user
|
||||
src.src_object = src_object
|
||||
src.ui_key = ui_key
|
||||
src.window_id = "\ref[src_object]-[ui_key]"
|
||||
|
||||
set_template(template)
|
||||
|
||||
if (title)
|
||||
src.title = sanitize(title)
|
||||
if (width)
|
||||
src.width = width
|
||||
if (height)
|
||||
src.height = height
|
||||
|
||||
if (ref)
|
||||
src.ref = ref
|
||||
|
||||
src.master_ui = master_ui
|
||||
if(master_ui)
|
||||
master_ui.children += src
|
||||
src.state = state
|
||||
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/nanoui)
|
||||
assets.send(user)
|
||||
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Enable/disable auto-updating of the NanoUI.
|
||||
*
|
||||
* required state bool Enable/disable auto-updating.
|
||||
**/
|
||||
/datum/nanoui/proc/set_auto_update(state = 1)
|
||||
auto_update = state
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Set the data to initialize the NanoUI with.
|
||||
* The datastructure cannot be changed by subsequent updates.
|
||||
*
|
||||
* optional data list The data/datastructure to initialize the NanoUI with.
|
||||
**/
|
||||
/datum/nanoui/proc/set_initial_data(list/data)
|
||||
initial_data = data
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Get the config data/datastructure to initialize the NanoUI with.
|
||||
*
|
||||
* return list The config data.
|
||||
**/
|
||||
/datum/nanoui/proc/get_config_data()
|
||||
var/list/config_data = list(
|
||||
"title" = title,
|
||||
"status" = status,
|
||||
"ref" = "\ref[src]",
|
||||
"window" = list(
|
||||
"width" = width,
|
||||
"height" = height,
|
||||
"ref" = window_id
|
||||
),
|
||||
"user" = list(
|
||||
"name" = user.name,
|
||||
"fancy" = user.client.prefs.nanoui_fancy,
|
||||
"ref" = "\ref[user]"
|
||||
),
|
||||
"srcObject" = list(
|
||||
"name" = src_object.name,
|
||||
"ref" = "\ref[src_object]"
|
||||
),
|
||||
"templates" = list(
|
||||
"layout" = "_[layout]",
|
||||
"content" = "[template]"
|
||||
)
|
||||
)
|
||||
return config_data
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Package the data to send to the UI.
|
||||
* This is the (regular) data and config data, bundled together.
|
||||
*
|
||||
* return list The packaged data.
|
||||
**/
|
||||
/datum/nanoui/proc/get_send_data(list/data)
|
||||
var/list/send_data = list()
|
||||
|
||||
send_data["config"] = get_config_data()
|
||||
if (!isnull(data))
|
||||
send_data["data"] = data
|
||||
|
||||
return send_data
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Sets the browse() window options for this NanoUI.
|
||||
*
|
||||
* required window_options list The window options to set.
|
||||
**/
|
||||
/datum/nanoui/proc/set_window_options(list/window_options)
|
||||
src.window_options = window_options
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Set the layout for this NanoUI.
|
||||
* This loads custom layout styles and templates for this NanoUI.
|
||||
*
|
||||
* required layout string The new UI layout.
|
||||
**/
|
||||
/datum/nanoui/proc/set_layout(layout)
|
||||
src.layout = lowertext(layout)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Set the template for this NanoUI.
|
||||
*
|
||||
* required template string The new UI template.
|
||||
**/
|
||||
/datum/nanoui/proc/set_template(template)
|
||||
src.template = lowertext(template)
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Generate HTML for this NanoUI.
|
||||
*
|
||||
* return string NanoUI HTML output.
|
||||
**/
|
||||
/datum/nanoui/proc/get_html()
|
||||
// Generate <script> and <link> tags.
|
||||
var/script_html = {"
|
||||
<script type='text/javascript' src='nanoui.lib.js'></script>
|
||||
<script type='text/javascript' src='nanoui.main.js'></script>
|
||||
<script type='text/javascript' src='nanoui.templates.js'></script>
|
||||
"}
|
||||
var/stylesheet_html = {"
|
||||
<link rel="stylesheet" type='text/css' href='http://css-spinners.com/css/spinner/hexdots.css'>
|
||||
<link rel='stylesheet' type='text/css' href='nanoui.lib.css' />
|
||||
<link rel='stylesheet' type='text/css' href='nanoui.common.css' />
|
||||
<link rel='stylesheet' type='text/css' href='nanoui.[layout].css' />
|
||||
"}
|
||||
|
||||
// Generate JSON.
|
||||
var/list/send_data = get_send_data(initial_data)
|
||||
var/initial_data_json = replacetext(JSON.stringify(send_data), "'", "\\'")
|
||||
|
||||
// Generate the HTML document.
|
||||
return {"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
||||
<script type='text/javascript'>
|
||||
function receiveUpdate(dataString) {
|
||||
if (typeof NanoBus !== 'undefined') {
|
||||
NanoBus.emit('serverUpdate', dataString);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
[stylesheet_html]
|
||||
[script_html]
|
||||
</head>
|
||||
<body>
|
||||
<div id='data' data-initial='[initial_data_json]'></div>
|
||||
<div id='layout'>
|
||||
<div class='hexdots-loader' style='position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);-ms-transform:translate(-50%, -50%);'>
|
||||
Sending Resources...
|
||||
</div>
|
||||
</div>
|
||||
<noscript>
|
||||
<h1>Javascript Required</h1>
|
||||
<p>Javascript is required in order to use this NanoUI interface.</p>
|
||||
<p>Please enable Javascript in Internet Explorer, and restart your game.</p>
|
||||
</noscript>
|
||||
</body>
|
||||
</html>
|
||||
"}
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Open this NanoUI (and initialize it with data).
|
||||
*
|
||||
* optional data list The data to intialize the UI with.
|
||||
**/
|
||||
/datum/nanoui/proc/open(list/data = null)
|
||||
if(!user.client) return
|
||||
|
||||
if (!initial_data)
|
||||
if (!data) // If we don't have initial_data and data was not passed, get data from the src_object.
|
||||
data = src_object.get_ui_data(user)
|
||||
set_initial_data(data) // Otherwise use the passed data.
|
||||
|
||||
var/window_size = ""
|
||||
if (width && height) // If we have a width and height, use them.
|
||||
window_size = "size=[width]x[height];"
|
||||
update_status(push = 0) // Update the window state.
|
||||
if (status == NANO_CLOSE)
|
||||
return // Bail if we should close.
|
||||
|
||||
user << browse(get_html(), "window=[window_id];[window_size][list2params(window_options)]") // Open the window.
|
||||
winset(user, window_id, "on-close=\"nanoclose \ref[src]\"") // Instruct the client to signal NanoUI when the window is closed.
|
||||
SSnano.ui_opened(src) // Call the opened handler.
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Reinitialize the NanoUI.
|
||||
* (Possibly with a new template and/or data).
|
||||
*
|
||||
* optional template string The filename of the new template.
|
||||
* optional data list The new initial data.
|
||||
**/
|
||||
/datum/nanoui/proc/reinitialize(template, list/data)
|
||||
if(template)
|
||||
src.template = template // Set a new template.
|
||||
if(data)
|
||||
set_initial_data(data) // Replace the initial_data.
|
||||
open()
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Close the NanoUI, and all its children.
|
||||
**/
|
||||
/datum/nanoui/proc/close()
|
||||
set_auto_update(0) // Disable auto-updates.
|
||||
user << browse(null, "window=[window_id]") // Close the window.
|
||||
SSnano.ui_closed(src) // Call the closed handler.
|
||||
for(var/datum/nanoui/child in children) // Loop through and close all children.
|
||||
child.close()
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Push data to an already open NanoUI.
|
||||
*
|
||||
* required data list The data to send.
|
||||
* optional force bool If the update should be sent regardless of state.
|
||||
**/
|
||||
/datum/nanoui/proc/push_data(data, force = 0)
|
||||
update_status(push = 0) // Update the window state.
|
||||
if (status == NANO_DISABLED && !force)
|
||||
return // Cannot update UI, we have no visibility.
|
||||
|
||||
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(JSON.stringify(send_data))), "[window_id].browser:receiveUpdate")
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Handle clicks from the NanoUI.
|
||||
* Call the src_object's Topic() if status is NANO_INTERACTIVE.
|
||||
* If the src_object's Topic() returns 1, update all UIs attacked to it.
|
||||
**/
|
||||
/datum/nanoui/Topic(href, href_list)
|
||||
update_status(push = 0) // Update the window state.
|
||||
if (status != NANO_INTERACTIVE || user != usr)
|
||||
return // If UI is not interactive or usr calling Topic is not the UI user.
|
||||
|
||||
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.
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Update the NanoUI. Only updates the contents/layout if update is true,
|
||||
* otherwise only updates the status.
|
||||
*
|
||||
* optional force bool If the UI should be forced to update.
|
||||
**/
|
||||
/datum/nanoui/process(force = 0)
|
||||
if (!src_object || !user) // If the object or user died (or something else), abort.
|
||||
close()
|
||||
return
|
||||
|
||||
if (status && (force || auto_update))
|
||||
update() // Update the UI if the status and update settings allow it.
|
||||
else
|
||||
update_status(push = 1) // Otherwise only update status.
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Updates the UI by interacting with the src_object again, which will hopefully
|
||||
* call try_ui_update on it.
|
||||
*
|
||||
* optional force_open bool If force_open should be passed to ui_interact.
|
||||
**/
|
||||
/datum/nanoui/proc/update(force_open = 0)
|
||||
src_object.ui_interact(user, ui_key, src, force_open, master_ui, state)
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Set the status/visibility of the NanoUI.
|
||||
*
|
||||
* required state int The status to set (NANO_CLOSE/NANO_DISABLED/NANO_UPDATE/NANO_INTERACTIVE).
|
||||
* optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED).
|
||||
**/
|
||||
/datum/nanoui/proc/set_status(state, push = 0)
|
||||
if (state != status) // Only update if status has changed.
|
||||
if (status == NANO_DISABLED)
|
||||
status = state
|
||||
if (push)
|
||||
update()
|
||||
else
|
||||
status = state
|
||||
if (push || status == 0) // Force an update if NANO_DISABLED.
|
||||
push_data(null, 1)
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Update the status/visibility of the NanoUI for its user.
|
||||
*
|
||||
* optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED).
|
||||
**/
|
||||
/datum/nanoui/proc/update_status(push = 0)
|
||||
var/new_status = src_object.CanUseTopic(user, state)
|
||||
if(master_ui)
|
||||
new_status = min(new_status, master_ui.status)
|
||||
|
||||
set_status(new_status, push)
|
||||
if(new_status == NANO_CLOSE)
|
||||
close()
|
||||
/**
|
||||
* NanoUI
|
||||
*
|
||||
* Contains the NanoUI datum, and its procs.
|
||||
*
|
||||
* /tg/station user interface library
|
||||
* thanks to baystation12
|
||||
*
|
||||
* modified by neersighted
|
||||
**/
|
||||
|
||||
/**
|
||||
* NanoUI datum:
|
||||
*
|
||||
* Represents a NanoUI.
|
||||
**/
|
||||
/datum/nanoui
|
||||
var/mob/user // The mob who opened/is using the NanoUI.
|
||||
var/atom/movable/src_object // The object which owns the NanoUI.
|
||||
var/title // The title of te NanoUI.
|
||||
var/ui_key // The ui_key of the NanoUI. This allows multiple UIs for one src_object.
|
||||
var/window_id // The window_id for browse() and onclose().
|
||||
var/width = 0 // The window width.
|
||||
var/height = 0 // The window height
|
||||
var/window_options = list( // Extra options to winset().
|
||||
"focus" = 0,
|
||||
"titlebar" = 1,
|
||||
"can_resize" = 1,
|
||||
"can_minimize" = 1,
|
||||
"can_maximize" = 0,
|
||||
"can_close" = 1,
|
||||
"auto_format" = 0
|
||||
)
|
||||
var/atom/ref = null // An extra ref to call when the window is closed.
|
||||
var/layout = "nanotrasen" // The layout to be used for this UI.
|
||||
var/template // The template to be used for this UI.
|
||||
var/auto_update = 1 // Update the NanoUI every MC tick.
|
||||
var/list/initial_data // The data (and datastructure) used to initialize the NanoUI
|
||||
var/status = NANO_INTERACTIVE // The status/visibility of the NanoUI.
|
||||
var/datum/topic_state/state = null // Topic state used to determine status. Topic states are in interactions/.
|
||||
var/datum/nanoui/master_ui // The parent NanoUI.
|
||||
var/list/datum/nanoui/children = list() // Children of this NanoUI.
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Create a new NanoUI.
|
||||
*
|
||||
* required user mob The mob who opened/is using the NanoUI.
|
||||
* required src_object atom/movable The object which owns the NanoUI.
|
||||
* required ui_key string The ui_key of the NanoUI.
|
||||
* required template string The template to render the NanoUI content with.
|
||||
* optional title string The title of the NanoUI.
|
||||
* optional width int The window width.
|
||||
* optional height int The window height.
|
||||
* optional ref atom An extra ref to use when the window is closed.
|
||||
* optional master_ui datum/nanoui The parent NanoUI.
|
||||
* optional state datum/topic_state The state used to determine status.
|
||||
*
|
||||
* return datum/nanoui The requested NanoUI.
|
||||
**/
|
||||
/datum/nanoui/New(mob/user, atom/movable/src_object, ui_key, template, \
|
||||
title = 0, width = 0, height = 0, \
|
||||
atom/ref = null, datum/nanoui/master_ui = null, \
|
||||
datum/topic_state/state = default_state)
|
||||
src.user = user
|
||||
src.src_object = src_object
|
||||
src.ui_key = ui_key
|
||||
src.window_id = "\ref[src_object]-[ui_key]"
|
||||
|
||||
set_template(template)
|
||||
|
||||
if (title)
|
||||
src.title = sanitize(title)
|
||||
if (width)
|
||||
src.width = width
|
||||
if (height)
|
||||
src.height = height
|
||||
|
||||
if (ref)
|
||||
src.ref = ref
|
||||
|
||||
src.master_ui = master_ui
|
||||
if(master_ui)
|
||||
master_ui.children += src
|
||||
src.state = state
|
||||
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/nanoui)
|
||||
assets.send(user)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Open this NanoUI (and initialize it with data).
|
||||
*
|
||||
* optional data list The data to intialize the UI with.
|
||||
**/
|
||||
/datum/nanoui/proc/open(list/data = null)
|
||||
if(!user.client)
|
||||
return // Bail if there is no client.
|
||||
if (status == NANO_CLOSE)
|
||||
return // Bail if we should close.
|
||||
|
||||
if (!initial_data)
|
||||
if (!data) // If we don't have initial_data and data was not passed, get data from the src_object.
|
||||
data = src_object.get_ui_data(user)
|
||||
set_initial_data(data) // Otherwise use the passed data.
|
||||
|
||||
var/window_size = ""
|
||||
if (width && height) // If we have a width and height, use them.
|
||||
window_size = "size=[width]x[height];"
|
||||
update_status(push = 0) // Update the window state.
|
||||
|
||||
user << browse(get_html(), "window=[window_id];[window_size][list2params(window_options)]") // Open the window.
|
||||
winset(user, window_id, "on-close=\"nanoclose \ref[src]\"") // Instruct the client to signal NanoUI when the window is closed.
|
||||
SSnano.on_open(src)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Reinitialize the NanoUI.
|
||||
* (Possibly with a new template and/or data).
|
||||
*
|
||||
* optional template string The filename of the new template.
|
||||
* optional data list The new initial data.
|
||||
**/
|
||||
/datum/nanoui/proc/reinitialize(template, list/data)
|
||||
if(template)
|
||||
set_template(template) // Set a new template.
|
||||
if(data)
|
||||
set_initial_data(data) // Replace the initial_data.
|
||||
open()
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Close the NanoUI, and all its children.
|
||||
**/
|
||||
/datum/nanoui/proc/close()
|
||||
set_auto_update(0) // Disable auto-updates.
|
||||
user << browse(null, "window=[window_id]") // Close the window.
|
||||
SSnano.on_close(src)
|
||||
for(var/datum/nanoui/child in children) // Loop through and close all children.
|
||||
child.close()
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Sets the browse() window options for this NanoUI.
|
||||
*
|
||||
* required window_options list The window options to set.
|
||||
**/
|
||||
/datum/nanoui/proc/set_window_options(list/window_options)
|
||||
src.window_options = window_options
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Set the layout for this NanoUI.
|
||||
* This loads custom layout styles and templates for this NanoUI.
|
||||
*
|
||||
* required layout string The new UI layout.
|
||||
**/
|
||||
/datum/nanoui/proc/set_layout(layout)
|
||||
src.layout = lowertext(layout)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Set the template for this NanoUI.
|
||||
*
|
||||
* required template string The new UI template.
|
||||
**/
|
||||
/datum/nanoui/proc/set_template(template)
|
||||
src.template = lowertext(template)
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Enable/disable auto-updating of the NanoUI.
|
||||
*
|
||||
* required state bool Enable/disable auto-updating.
|
||||
**/
|
||||
/datum/nanoui/proc/set_auto_update(state = 1)
|
||||
auto_update = state
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Set the data to initialize the NanoUI with.
|
||||
* The datastructure cannot be changed by subsequent updates.
|
||||
*
|
||||
* optional data list The data/datastructure to initialize the NanoUI with.
|
||||
**/
|
||||
/datum/nanoui/proc/set_initial_data(list/data)
|
||||
initial_data = data
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Generate HTML for this NanoUI.
|
||||
*
|
||||
* return string NanoUI HTML output.
|
||||
**/
|
||||
/datum/nanoui/proc/get_html()
|
||||
// Generate <script> and <link> tags.
|
||||
var/script_html = {"
|
||||
<script type='text/javascript' src='nanoui.js'></script>
|
||||
"}
|
||||
var/stylesheet_html = {"
|
||||
<link rel='stylesheet' type='text/css' href='http://css-spinners.com/css/spinner/hexdots.css' />
|
||||
<link rel='stylesheet' type='text/css' href='nanoui.css' />
|
||||
<link rel='stylesheet' type='text/css' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css' />
|
||||
"}
|
||||
|
||||
// Generate JSON.
|
||||
var/list/send_data = get_send_data(initial_data)
|
||||
var/initial_data_json = replacetext(JSON.stringify(send_data), "'", "'")
|
||||
|
||||
// Generate the HTML document.
|
||||
return {"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
||||
<script type='text/javascript'>
|
||||
function receiveUpdate(dataString) {
|
||||
if (typeof NanoBus !== 'undefined') {
|
||||
NanoBus.emit('serverUpdate', dataString);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
[stylesheet_html]
|
||||
[script_html]
|
||||
</head>
|
||||
<body class='[layout]'>
|
||||
<div id='data' data-initial='[initial_data_json]'></div>
|
||||
<div id='layout'>
|
||||
<div class='hexdots-loader' style='position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);-ms-transform:translate(-50%, -50%);'>
|
||||
Sending Resources...
|
||||
</div>
|
||||
</div>
|
||||
<noscript>
|
||||
<h1>Javascript Required</h1>
|
||||
<p>Javascript is required in order to use this NanoUI interface.</p>
|
||||
<p>Please enable Javascript in Internet Explorer, and restart your game.</p>
|
||||
</noscript>
|
||||
</body>
|
||||
</html>
|
||||
"}
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Get the config data/datastructure to initialize the NanoUI with.
|
||||
*
|
||||
* return list The config data.
|
||||
**/
|
||||
/datum/nanoui/proc/get_config_data()
|
||||
var/list/config_data = list(
|
||||
"title" = title,
|
||||
"status" = status,
|
||||
"ref" = "\ref[src]",
|
||||
"window" = list(
|
||||
"width" = width,
|
||||
"height" = height,
|
||||
"ref" = window_id
|
||||
),
|
||||
"user" = list(
|
||||
"name" = user.name,
|
||||
"fancy" = user.client.prefs.nanoui_fancy,
|
||||
"ref" = "\ref[user]"
|
||||
),
|
||||
"srcObject" = list(
|
||||
"name" = src_object.name,
|
||||
"ref" = "\ref[src_object]"
|
||||
),
|
||||
"templates" = list(
|
||||
"layout" = "_[layout]",
|
||||
"content" = "[template]"
|
||||
)
|
||||
)
|
||||
return config_data
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Package the data to send to the UI.
|
||||
* This is the (regular) data and config data, bundled together.
|
||||
*
|
||||
* return list The packaged data.
|
||||
**/
|
||||
/datum/nanoui/proc/get_send_data(list/data)
|
||||
var/list/send_data = list()
|
||||
|
||||
send_data["config"] = get_config_data()
|
||||
if (!isnull(data))
|
||||
send_data["data"] = data
|
||||
|
||||
return send_data
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Handle clicks from the NanoUI.
|
||||
* Call the src_object's Topic() if status is NANO_INTERACTIVE.
|
||||
* If the src_object's Topic() returns 1, update all UIs attacked to it.
|
||||
**/
|
||||
/datum/nanoui/Topic(href, href_list)
|
||||
update_status(push = 0) // Update the window state.
|
||||
if (status != NANO_INTERACTIVE || user != usr)
|
||||
return // If UI is not interactive or usr calling Topic is not the UI user.
|
||||
|
||||
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.
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Update the NanoUI. Only updates the contents/layout if update is true,
|
||||
* otherwise only updates the status.
|
||||
*
|
||||
* optional force bool If the UI should be forced to update.
|
||||
**/
|
||||
/datum/nanoui/process(force = 0)
|
||||
if (!src_object || !user) // If the object or user died (or something else), abort.
|
||||
close()
|
||||
return
|
||||
|
||||
if (status && (force || auto_update))
|
||||
update() // Update the UI if the status and update settings allow it.
|
||||
else
|
||||
update_status(push = 1) // Otherwise only update status.
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Push data to an already open NanoUI.
|
||||
*
|
||||
* required data list The data to send.
|
||||
* optional force bool If the update should be sent regardless of state.
|
||||
**/
|
||||
/datum/nanoui/proc/push_data(data, force = 0)
|
||||
update_status(push = 0) // Update the window state.
|
||||
if (status == NANO_DISABLED && !force)
|
||||
return // Cannot update UI, we have no visibility.
|
||||
|
||||
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(JSON.stringify(send_data))), "[window_id].browser:receiveUpdate")
|
||||
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Updates the UI by interacting with the src_object again, which will hopefully
|
||||
* call try_ui_update on it.
|
||||
*
|
||||
* optional force_open bool If force_open should be passed to ui_interact.
|
||||
**/
|
||||
/datum/nanoui/proc/update(force_open = 0)
|
||||
src_object.ui_interact(user, ui_key, src, force_open, master_ui, state)
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Update the status/visibility of the NanoUI for its user.
|
||||
*
|
||||
* optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED).
|
||||
**/
|
||||
/datum/nanoui/proc/update_status(push = 0)
|
||||
var/new_status = src_object.nano_state(user, state)
|
||||
if(master_ui)
|
||||
new_status = min(new_status, master_ui.status)
|
||||
|
||||
set_status(new_status, push)
|
||||
if(new_status == NANO_CLOSE)
|
||||
close()
|
||||
|
||||
/**
|
||||
* private
|
||||
*
|
||||
* Set the status/visibility of the NanoUI.
|
||||
*
|
||||
* required state int The status to set (NANO_CLOSE/NANO_DISABLED/NANO_UPDATE/NANO_INTERACTIVE).
|
||||
* optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED).
|
||||
**/
|
||||
/datum/nanoui/proc/set_status(state, push = 0)
|
||||
if (state != status) // Only update if status has changed.
|
||||
if (status == NANO_DISABLED)
|
||||
status = state
|
||||
if (push)
|
||||
update()
|
||||
else
|
||||
status = state
|
||||
if (push || status == 0) // Force an update if NANO_DISABLED.
|
||||
push_data(null, 1)
|
||||
@@ -14,7 +14,7 @@
|
||||
*
|
||||
* return NANO_state The state of the UI.
|
||||
**/
|
||||
/atom/proc/CanUseTopic(mob/user, datum/topic_state/state)
|
||||
/atom/proc/nano_state(mob/user, datum/topic_state/state)
|
||||
var/src_object = nano_host()
|
||||
return state.can_use_topic(src_object, user) // Check if the state allows interaction.
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
*
|
||||
* required ui datum/nanoui The UI to be added.
|
||||
**/
|
||||
/datum/subsystem/nano/proc/ui_opened(datum/nanoui/ui)
|
||||
/datum/subsystem/nano/proc/on_open(datum/nanoui/ui)
|
||||
var/src_object_key = "\ref[ui.src_object]"
|
||||
if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
|
||||
open_uis[src_object_key] = list(ui.ui_key = list()) // Make a list for the ui_key and src_object.
|
||||
@@ -180,7 +180,7 @@
|
||||
*
|
||||
* return bool If the UI was removed or not.
|
||||
**/
|
||||
/datum/subsystem/nano/proc/ui_closed(datum/nanoui/ui)
|
||||
/datum/subsystem/nano/proc/on_close(datum/nanoui/ui)
|
||||
var/src_object_key = "\ref[ui.src_object]"
|
||||
if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
|
||||
return 0 // It wasn't open.
|
||||
@@ -203,7 +203,7 @@
|
||||
*
|
||||
* return int The number of NanoUIs closed.
|
||||
**/
|
||||
/datum/subsystem/nano/proc/user_logout(mob/user)
|
||||
/datum/subsystem/nano/proc/on_logout(mob/user)
|
||||
return close_user_uis(user)
|
||||
|
||||
/**
|
||||
@@ -216,7 +216,7 @@
|
||||
*
|
||||
* return bool If the NanoUIs were transferred.
|
||||
**/
|
||||
/datum/subsystem/nano/proc/user_transferred(mob/oldMob, mob/newMob)
|
||||
/datum/subsystem/nano/proc/on_transfer(mob/oldMob, mob/newMob)
|
||||
if (!oldMob || isnull(oldMob.open_uis) || !istype(oldMob.open_uis, /list) || open_uis.len == 0)
|
||||
return 0 // The old mob had no open NanoUIs.
|
||||
|
||||
|
||||
+121
-127
@@ -1,127 +1,121 @@
|
||||
### Settings ###
|
||||
util = require("gulp-util")
|
||||
s =
|
||||
min: util.env.min
|
||||
colorblind: util.env.colorblind
|
||||
|
||||
# Project Paths
|
||||
input =
|
||||
fonts: "**/*.{eot,woff2}"
|
||||
images: "images"
|
||||
scripts: "scripts"
|
||||
styles: "styles"
|
||||
templates: "templates"
|
||||
|
||||
output =
|
||||
dir: "assets"
|
||||
scripts:
|
||||
lib: "nanoui.lib.js"
|
||||
main: "nanoui.main.js"
|
||||
styles:
|
||||
lib: "nanoui.lib.css"
|
||||
prefix: "nanoui."
|
||||
templates: "nanoui.templates.js"
|
||||
|
||||
# doT Settings
|
||||
dotOpts =
|
||||
evaluate: /\{\{([\s\S]+?)\}\}/g,
|
||||
interpolate: /\{\{=([\s\S]+?)\}\}/g,
|
||||
encode: /\{\{!([\s\S]+?)\}\}/g,
|
||||
use: /\{\{#([\s\S]+?)\}\}/g,
|
||||
define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g,
|
||||
conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g,
|
||||
iterate: /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g,
|
||||
varname: "data, config, helper",
|
||||
strip: true,
|
||||
append: true,
|
||||
selfcontained: true
|
||||
|
||||
### Pacakages ###
|
||||
bower = require "main-bower-files"
|
||||
child_process = require "child_process"
|
||||
del = require "del"
|
||||
gulp = require "gulp"
|
||||
merge = require "merge-stream"
|
||||
|
||||
### Plugins ###
|
||||
g = require("gulp-load-plugins")({replaceString: /^gulp(-|\.)|-/g})
|
||||
p =
|
||||
autoprefixer: require "autoprefixer"
|
||||
colorblind: require "postcss-colorblind"
|
||||
fontweights: require "postcss-font-weights"
|
||||
gradient: require "postcss-filter-gradient"
|
||||
opacity: require "postcss-opacity"
|
||||
plsfilters: require "pleeease-filters"
|
||||
rgba: require "postcss-color-rgba-fallback"
|
||||
|
||||
### Helpers ###
|
||||
|
||||
glob = (path) ->
|
||||
"#{path}/*"
|
||||
|
||||
### Tasks ###
|
||||
gulp.task "default", ["fonts", "scripts", "styles", "templates"]
|
||||
|
||||
gulp.task "clean", ->
|
||||
del glob output.dir
|
||||
|
||||
gulp.task "watch", ->
|
||||
gulp.watch [glob input.images], ["reload"]
|
||||
gulp.watch [glob input.scripts], ["reload"]
|
||||
gulp.watch [glob input.styles], ["reload"]
|
||||
gulp.watch [glob input.templates], ["reload"]
|
||||
|
||||
gulp.task "reload", ["default"], ->
|
||||
child_process.exec "reload.bat", (err, stdout, stderr) ->
|
||||
return console.log err if err
|
||||
|
||||
gulp.task "fonts", ["clean"], ->
|
||||
gulp.src bower input.fonts
|
||||
.pipe gulp.dest output.dir
|
||||
|
||||
gulp.task "scripts", ["clean"], ->
|
||||
lib = gulp.src bower "**/*.js"
|
||||
.pipe g.concat(output.scripts.lib)
|
||||
.pipe g.if(s.min, g.uglify(), g.jsbeautifier())
|
||||
.pipe gulp.dest output.dir
|
||||
|
||||
main = gulp.src glob input.scripts
|
||||
.pipe g.coffee()
|
||||
.pipe g.concat(output.scripts.main)
|
||||
.pipe g.if(s.min, g.uglify(), g.jsbeautifier())
|
||||
.pipe gulp.dest output.dir
|
||||
|
||||
merge lib, main
|
||||
|
||||
gulp.task "styles", ["clean"], ->
|
||||
lib = gulp.src bower "**/*.css"
|
||||
.pipe g.replace("../fonts/", "")
|
||||
.pipe g.concat(output.styles.lib)
|
||||
.pipe g.if(s.min, g.cssnano({discardComments: {removeAll: true}}), g.csscomb())
|
||||
.pipe gulp.dest output.dir
|
||||
|
||||
main = gulp.src glob input.styles
|
||||
.pipe g.filter(["*.less", "!_*.less"])
|
||||
.pipe g.less({paths: [input.images]})
|
||||
.pipe g.postcss([
|
||||
p.autoprefixer({browsers: ["last 2 versions", "ie >= 8"]}),
|
||||
p.plsfilters({oldIE: true}),
|
||||
p.rgba({oldie: true}),
|
||||
p.opacity,
|
||||
p.gradient,
|
||||
p.fontweights
|
||||
])
|
||||
.pipe g.if(s.colorblind, g.postcss([p.colorblind]))
|
||||
.pipe g.if(s.min, g.cssnano({discardComments: {removeAll: true}}), g.csscomb())
|
||||
.pipe g.rename({prefix: output.styles.prefix})
|
||||
.pipe gulp.dest output.dir
|
||||
|
||||
merge lib, main
|
||||
|
||||
gulp.task "templates", ["clean"], ->
|
||||
gulp.src glob input.templates
|
||||
.pipe g.dotprecompiler({dictionary: "TMPL", templateSettings: dotOpts})
|
||||
.pipe g.concat(output.templates)
|
||||
.pipe g.header("window.TMPL = {};\n")
|
||||
.pipe g.if(s.min, g.uglify(), g.jsbeautifier())
|
||||
.pipe gulp.dest output.dir
|
||||
### Settings ###
|
||||
util = require("gulp-util")
|
||||
f =
|
||||
colorblind: util.env.colorblind || util.env.c
|
||||
min: util.env.min || util.env.m
|
||||
sourcemaps: util.env.sourcemaps || util.env.s
|
||||
|
||||
# Project Paths
|
||||
input =
|
||||
images: "images"
|
||||
scripts: "scripts"
|
||||
styles: "styles"
|
||||
templates: "templates"
|
||||
|
||||
output =
|
||||
dir: "assets"
|
||||
js: "nanoui.js"
|
||||
css: "nanoui.css"
|
||||
|
||||
# doT Settings
|
||||
dotOpts =
|
||||
evaluate: /\{\{([\s\S]+?)\}\}/g,
|
||||
interpolate: /\{\{=([\s\S]+?)\}\}/g,
|
||||
encode: /\{\{!([\s\S]+?)\}\}/g,
|
||||
use: /\{\{#([\s\S]+?)\}\}/g,
|
||||
define: /\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g,
|
||||
conditional: /\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g,
|
||||
iterate: /\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g,
|
||||
varname: "data, config, helper",
|
||||
strip: true,
|
||||
append: true,
|
||||
selfcontained: true
|
||||
|
||||
### Pacakages ###
|
||||
bower = require "main-bower-files"
|
||||
child_process = require "child_process"
|
||||
del = require "del"
|
||||
gulp = require "gulp"
|
||||
merge = require "merge2"
|
||||
|
||||
### Plugins ###
|
||||
g = require("gulp-load-plugins")({replaceString: /^gulp(-|\.)|-/g})
|
||||
p =
|
||||
autoprefixer: require "autoprefixer"
|
||||
colorblind: require "postcss-colorblind"
|
||||
fontweights: require "postcss-font-weights"
|
||||
gradient: require "postcss-filter-gradient"
|
||||
opacity: require "postcss-opacity"
|
||||
plsfilters: require "pleeease-filters"
|
||||
rgba: require "postcss-color-rgba-fallback"
|
||||
|
||||
### Helpers ###
|
||||
|
||||
glob = (path) ->
|
||||
"#{path}/*"
|
||||
|
||||
### Tasks ###
|
||||
gulp.task "default", ["js", "css"]
|
||||
|
||||
|
||||
gulp.task "clean", ->
|
||||
del glob output.dir
|
||||
|
||||
|
||||
gulp.task "watch", ->
|
||||
gulp.watch [glob input.images], ["reload"]
|
||||
gulp.watch [glob input.scripts], ["reload"]
|
||||
gulp.watch [glob input.styles], ["reload"]
|
||||
gulp.watch [glob input.templates], ["reload"]
|
||||
|
||||
|
||||
gulp.task "reload", ["default"], ->
|
||||
child_process.exec "reload.bat", (err, stdout, stderr) ->
|
||||
return console.log err if err
|
||||
|
||||
|
||||
gulp.task "js", ["clean"], ->
|
||||
lib = gulp.src bower "**/*.js"
|
||||
.pipe g.if(f.sourcemaps, g.sourcemaps.init())
|
||||
|
||||
main = gulp.src glob input.scripts
|
||||
.pipe g.if(f.sourcemaps, g.sourcemaps.init())
|
||||
.pipe g.coffee()
|
||||
|
||||
templates = gulp.src glob input.templates
|
||||
.pipe g.dotprecompiler({dictionary: "TMPL", templateSettings: dotOpts})
|
||||
.pipe g.concat("templates")
|
||||
.pipe g.header("window.TMPL = {};\n")
|
||||
|
||||
combined = merge lib, main, templates
|
||||
combined
|
||||
.pipe g.concat(output.js)
|
||||
.pipe g.if(f.min, g.uglify(), g.jsbeautifier())
|
||||
.pipe g.if(f.sourcemaps, g.sourcemaps.write())
|
||||
.pipe gulp.dest output.dir
|
||||
|
||||
|
||||
gulp.task "css", ["clean"], ->
|
||||
lib = gulp.src bower "**/*.css"
|
||||
.pipe g.if(f.sourcemaps, g.sourcemaps.init())
|
||||
|
||||
main = gulp.src glob input.styles
|
||||
.pipe g.filter(["*.less", "!_*.less"])
|
||||
.pipe g.if(f.sourcemaps, g.sourcemaps.init())
|
||||
.pipe g.less({paths: [input.images]})
|
||||
.pipe g.postcss([
|
||||
p.autoprefixer({browsers: ["last 2 versions", "ie >= 8"]}),
|
||||
p.plsfilters({oldIE: true}),
|
||||
p.rgba({oldie: true}),
|
||||
p.opacity,
|
||||
p.gradient,
|
||||
p.fontweights
|
||||
])
|
||||
.pipe g.if(f.colorblind, g.postcss([p.colorblind]))
|
||||
|
||||
combined = merge lib, main
|
||||
combined
|
||||
.pipe g.if(f.min, g.cssnano({discardComments: {removeAll: true}}), g.csscomb())
|
||||
.pipe g.concat(output.css)
|
||||
.pipe g.if(f.sourcemaps, g.sourcemaps.write())
|
||||
.pipe gulp.dest output.dir
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
body{background:linear-gradient(180deg,#2a2a2a 0,#202020);filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#FF2A2A2A',EndColorStr='#FF202020')}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
body{background:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1' viewBox='0 0 425 200' opacity='.25'%3E%3Cpath d='M178.004.04H106.8a6.76 6.026 0 0 0-6.76 6.024v187.872a6.76 6.026 0 0 0 6.76 6.025h53.107a6.76 6.026 0 0 0 6.762-6.024V92.392l72.215 104.7a6.76 6.026 0 0 0 5.76 2.87H318.2a6.76 6.026 0 0 0 6.76-6.026V6.064A6.76 6.026 0 0 0 318.2.04h-54.717a6.76 6.026 0 0 0-6.76 6.024v102.62L183.762 2.91a6.76 6.026 0 0 0-5.76-2.87zM4.845 22.11A13.412 12.502 0 0 1 13.478.04h66.118a5.365 5 0 0 1 5.365 5v79.88zM420.155 177.89a13.412 12.502 0 0 1-8.633 22.07h-66.118a5.365 5 0 0 1-5.365-5v-79.88z'/%3E%3C/svg%3E") no-repeat fixed center/70% 70%,linear-gradient(180deg,#2a2a2a 0,#202020) no-repeat fixed center/100% 100%;filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#FF2A2A2A',EndColorStr='#FF000000')}
|
||||
File diff suppressed because one or more lines are too long
@@ -5,7 +5,6 @@
|
||||
"DOM4": "dom4#~1.5.2",
|
||||
"eddy-js": "~0.7.0",
|
||||
"es5-shim": "~4.3.1",
|
||||
"font-awesome": "~4.5.0",
|
||||
"IE8": "ie8#~2.9.10",
|
||||
"html5shiv": "~3.7.3",
|
||||
"json3": "~3.3.2",
|
||||
@@ -28,12 +27,6 @@
|
||||
"DOM4": "*"
|
||||
}
|
||||
},
|
||||
"font-awesome": {
|
||||
"main": [
|
||||
"css/font-awesome.css",
|
||||
"fonts/*"
|
||||
]
|
||||
},
|
||||
"jsurl": {
|
||||
"dependencies": {
|
||||
"es5-shim": "*"
|
||||
|
||||
+3
-1
@@ -3,6 +3,7 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"autoprefixer": "^6.1.2",
|
||||
"coffee-script": "^1.10.0",
|
||||
"del": "^2.2.0",
|
||||
"gulp": "^3.9.0",
|
||||
"gulp-coffee": "^2.3.1",
|
||||
@@ -19,11 +20,12 @@
|
||||
"gulp-postcss": "^6.0.1",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-replace": "^0.5.4",
|
||||
"gulp-sourcemaps": "^1.6.0",
|
||||
"gulp-uglify": "^1.5.1",
|
||||
"gulp-util": "^3.0.7",
|
||||
"hexa-color-regex": "^1.0.0",
|
||||
"main-bower-files": "^2.9.0",
|
||||
"merge-stream": "^1.0.0",
|
||||
"merge2": "^0.3.6",
|
||||
"pleeease-filters": "^2.0.0",
|
||||
"postcss-color-rgba-fallback": "git+https://github.com/postcss/postcss-color-rgba-fallback",
|
||||
"postcss-colorblind": "^0.4.0",
|
||||
|
||||
+86
-88
@@ -1,88 +1,86 @@
|
||||
class @NanoUI
|
||||
constructor: (@bus, @fragment = document) ->
|
||||
@bus.on "serverUpdate", @serverUpdate
|
||||
@bus.on "update", @update
|
||||
@bus.on "render", @render
|
||||
@bus.on "memes", @render
|
||||
|
||||
@initialized = false
|
||||
|
||||
@data = {}
|
||||
@initialData = JSON.parse @fragment.query("#data").data "initial"
|
||||
|
||||
unless @initialData? or
|
||||
not ("data" of @initialData or "config" of @initalData)
|
||||
@error "Initial data did not load correctly."
|
||||
|
||||
serverUpdate: (dataString) =>
|
||||
try
|
||||
data = JSON.parse(dataString)
|
||||
catch error
|
||||
@error error
|
||||
|
||||
@bus.emit "update", data
|
||||
return
|
||||
|
||||
update: (data) =>
|
||||
unless data.data?
|
||||
if @data.data?
|
||||
data.data = @data.data
|
||||
else
|
||||
data.data = {}
|
||||
|
||||
@data = data
|
||||
|
||||
@bus.emit "render", @data if @initialized
|
||||
@bus.emit "updated"
|
||||
return
|
||||
|
||||
render: (data) =>
|
||||
data = @initialData unless @initialized
|
||||
|
||||
try
|
||||
if not @initialized
|
||||
layout = @fragment.query("#layout")
|
||||
layout.innerHTML = TMPL[data.config.templates.layout]\
|
||||
(data.data, data.config, helpers)
|
||||
|
||||
content = @fragment.query("#content")
|
||||
content.innerHTML = TMPL[data.config.templates.content]\
|
||||
(data.data, data.config, helpers)
|
||||
|
||||
catch error
|
||||
@error error
|
||||
return
|
||||
|
||||
@bus.emit "rendered", data
|
||||
if not @initialized
|
||||
@initialized = true
|
||||
@data = @initialData
|
||||
@bus.emit "initialized", data
|
||||
|
||||
act: (action, params = {}) =>
|
||||
params.src = @data.config.ref
|
||||
params.nano = action
|
||||
location.href = util.href null, params
|
||||
|
||||
error: (error) ->
|
||||
error = "#{error.fileName}:#{error.lineNumber} #{error.message}" if error instanceof Error
|
||||
params =
|
||||
nano_error: error
|
||||
location.href = util.href null, params
|
||||
|
||||
log: (message) ->
|
||||
params =
|
||||
nano_log: message
|
||||
location.href = util.href null, params
|
||||
|
||||
close: =>
|
||||
params =
|
||||
command: "nanoclose #{@data.config.ref}"
|
||||
@winset "is-visible", "false"
|
||||
location.href = util.href "winset", params
|
||||
|
||||
winset: (key, value, window) =>
|
||||
window = @data.config.window.ref unless window?
|
||||
params =
|
||||
"#{window}.#{key}": value
|
||||
location.href = util.href "winset", params
|
||||
class @NanoUI
|
||||
constructor: (@bus, @fragment = document) ->
|
||||
@bus.on "serverUpdate", @serverUpdate
|
||||
@bus.on "update", @update
|
||||
@bus.on "render", @render
|
||||
@bus.on "memes", @render
|
||||
|
||||
@initialized = false
|
||||
|
||||
@data = {}
|
||||
@initialData = JSON.parse @fragment.query("#data").data "initial"
|
||||
|
||||
unless @initialData? or
|
||||
not ("data" of @initialData or "config" of @initalData)
|
||||
@error "Initial data did not load correctly."
|
||||
|
||||
serverUpdate: (dataString) =>
|
||||
try
|
||||
data = JSON.parse(dataString)
|
||||
catch error
|
||||
@error error
|
||||
|
||||
@bus.emit "update", data
|
||||
return
|
||||
|
||||
update: (data) =>
|
||||
unless data.data?
|
||||
if @data.data?
|
||||
data.data = @data.data
|
||||
else
|
||||
data.data = {}
|
||||
|
||||
@data = data
|
||||
|
||||
@bus.emit "render", @data if @initialized
|
||||
@bus.emit "updated"
|
||||
return
|
||||
|
||||
render: (data) =>
|
||||
data = @initialData unless @initialized
|
||||
|
||||
try
|
||||
if not @initialized
|
||||
layout = @fragment.query("#layout")
|
||||
layout.innerHTML = TMPL[data.config.templates.layout](data.data, data.config, helpers)
|
||||
|
||||
content = @fragment.query("#content")
|
||||
content.innerHTML = TMPL[data.config.templates.content](data.data, data.config, helpers)
|
||||
|
||||
catch error
|
||||
@error error
|
||||
return
|
||||
|
||||
@bus.emit "rendered", data
|
||||
if not @initialized
|
||||
@initialized = true
|
||||
@data = @initialData
|
||||
@bus.emit "initialized", data
|
||||
|
||||
act: (action, params = {}) =>
|
||||
params.src = @data.config.ref
|
||||
params.nano = action
|
||||
location.href = util.href null, params
|
||||
|
||||
error: (error) ->
|
||||
error = "#{error.fileName}:#{error.lineNumber} #{error.message}" if error instanceof Error
|
||||
params =
|
||||
nano_error: error
|
||||
location.href = util.href null, params
|
||||
|
||||
log: (message) ->
|
||||
params =
|
||||
nano_log: message
|
||||
location.href = util.href null, params
|
||||
|
||||
close: =>
|
||||
params =
|
||||
command: "nanoclose #{@data.config.ref}"
|
||||
@winset "is-visible", "false"
|
||||
location.href = util.href "winset", params
|
||||
|
||||
winset: (key, value, window) =>
|
||||
window = @data.config.window.ref unless window?
|
||||
params =
|
||||
"#{window}.#{key}": value
|
||||
location.href = util.href "winset", params
|
||||
|
||||
@@ -30,8 +30,8 @@ class @Window
|
||||
nanoui.winset "titlebar", 0
|
||||
nanoui.winset "can-resize", 0
|
||||
fancy = @fragment.queryAll ".fancy"
|
||||
fancy.forEach (element) ->
|
||||
element.style.display = 'inherit'
|
||||
fancy.forEach (chrome) ->
|
||||
chrome.classList.remove "fancy"
|
||||
|
||||
calcOffset: =>
|
||||
@xOriginal = window.screenLeft
|
||||
@@ -83,8 +83,8 @@ class @Window
|
||||
@xResize = event.screenX unless @xResize?
|
||||
@yResize = event.screenY unless @yResize?
|
||||
|
||||
x = Math.max(150, (event.screenX - @xResize) + window.innerWidth)
|
||||
y = Math.max(150, (event.screenY - @yResize) + window.innerHeight)
|
||||
x = Math.max(250, (event.screenX - @xResize) + window.innerWidth)
|
||||
y = Math.max(250, (event.screenY - @yResize) + window.innerHeight)
|
||||
@setSize x, y
|
||||
|
||||
@xResize = event.screenX
|
||||
|
||||
@@ -1,86 +1,81 @@
|
||||
@import "_config";
|
||||
@import "_document";
|
||||
@import "_util";
|
||||
@import "_interface";
|
||||
@import "_link";
|
||||
@import "_bar";
|
||||
|
||||
|
||||
.fancy {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
div.loading {
|
||||
.noticeStripes;
|
||||
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
div.content {
|
||||
margin: 32px 4px 0 4px;
|
||||
&.titlebared {
|
||||
padding-top: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
header.titlebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
|
||||
background-color: @titlebar;
|
||||
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.statusicon {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 12px;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 46px;
|
||||
|
||||
color: @title;
|
||||
font-size: 16px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.titlebutton() {
|
||||
color: @title;
|
||||
|
||||
&:hover {
|
||||
color: lighten(@title, @hover);
|
||||
}
|
||||
}
|
||||
.minimize {
|
||||
.titlebutton;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 46px;
|
||||
}
|
||||
.close {
|
||||
.titlebutton;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
div.resize {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
border-style: solid;
|
||||
border-width: 0 0 30px 30px;
|
||||
border-color: transparent transparent @resize transparent;
|
||||
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
@import "_config";
|
||||
@import "_util";
|
||||
|
||||
|
||||
div.loading {
|
||||
.noticeStripes;
|
||||
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
div.content {
|
||||
margin: 32px 4px 0 4px;
|
||||
&.titlebared {
|
||||
padding-top: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
header.titlebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
|
||||
background-color: @titlebar;
|
||||
box-shadow: 0 3px 3px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.statusicon {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 12px;
|
||||
}
|
||||
|
||||
.title {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 46px;
|
||||
|
||||
color: @title;
|
||||
font-size: 16px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.titlebutton() {
|
||||
color: @title;
|
||||
|
||||
&:hover {
|
||||
color: lighten(@title, @hover);
|
||||
}
|
||||
}
|
||||
.minimize {
|
||||
.titlebutton;
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 46px;
|
||||
}
|
||||
.close {
|
||||
.titlebutton;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
div.resize {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
border-style: solid;
|
||||
border-width: 0 0 30px 30px;
|
||||
border-color: transparent transparent @resize transparent;
|
||||
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
|
||||
.fancy {
|
||||
.hidden;
|
||||
}
|
||||
+39
-36
@@ -1,36 +1,39 @@
|
||||
@import "_config";
|
||||
@import "_util";
|
||||
|
||||
|
||||
html {
|
||||
min-height: 100%;
|
||||
|
||||
cursor: default; // Reset the cursor.
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100%;
|
||||
|
||||
font-family: @font;
|
||||
font-size: @fontsize;
|
||||
color: @text;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: @fontsize + 6px;
|
||||
margin: 0;
|
||||
padding: 6px 0;
|
||||
}
|
||||
h2 {
|
||||
&:extend(h1);
|
||||
font-size: @fontsize + 4px;
|
||||
}
|
||||
h3 {
|
||||
&:extend(h1);
|
||||
font-size: @fontsize + 2px;
|
||||
}
|
||||
|
||||
hr {
|
||||
background-color: @rule;
|
||||
border: none;
|
||||
}
|
||||
@import "_config";
|
||||
@import "_util";
|
||||
|
||||
|
||||
html {
|
||||
min-height: 100%;
|
||||
|
||||
cursor: default; // Reset the cursor.
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100%;
|
||||
height: 100%;
|
||||
|
||||
background-color: @background-end; // Fallback for old browsers.
|
||||
|
||||
font-family: @font;
|
||||
font-size: @fontsize;
|
||||
color: @text;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: @fontsize + 6px;
|
||||
margin: 0;
|
||||
padding: 6px 0;
|
||||
}
|
||||
h2 {
|
||||
&:extend(h1);
|
||||
font-size: @fontsize + 4px;
|
||||
}
|
||||
h3 {
|
||||
&:extend(h1);
|
||||
font-size: @fontsize + 2px;
|
||||
}
|
||||
|
||||
hr {
|
||||
background-color: @rule;
|
||||
border: none;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
@import "_config";
|
||||
|
||||
|
||||
body {
|
||||
background: linear-gradient(to bottom,
|
||||
@background-start 0%,
|
||||
@background-end 100%);
|
||||
}
|
||||
@import "_config";
|
||||
|
||||
|
||||
body.generic {
|
||||
background: linear-gradient(to bottom,
|
||||
@background-start 0%,
|
||||
@background-end 100%);
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
@import "_config";
|
||||
|
||||
|
||||
body {
|
||||
background: data-uri('nanotrasen.svg') no-repeat fixed center/70% 70%,
|
||||
linear-gradient(to bottom,
|
||||
@background-start 0%,
|
||||
@background-end 100%) no-repeat fixed center/100% 100%;
|
||||
}
|
||||
@import "_config";
|
||||
|
||||
|
||||
body.nanotrasen {
|
||||
background: data-uri('nanotrasen.svg') no-repeat fixed center/70% 70%,
|
||||
linear-gradient(to bottom,
|
||||
@background-start 0%,
|
||||
@background-end 100%) no-repeat fixed center/100% 100%;
|
||||
}
|
||||
+57
-57
@@ -1,57 +1,57 @@
|
||||
@import "_config";
|
||||
|
||||
|
||||
// Colors
|
||||
.color(@color) { // Explort a given color as a class.
|
||||
&.@{color} {
|
||||
color: @@color;
|
||||
}
|
||||
}
|
||||
|
||||
.color(normal);
|
||||
.color(good);
|
||||
.color(average);
|
||||
.color(bad);
|
||||
.color(label);
|
||||
.color(highlight);
|
||||
.color(dark);
|
||||
|
||||
// Text Styles
|
||||
.bold { font-weight: bold; }
|
||||
.italic { font-style: italic; }
|
||||
.fontReset {
|
||||
color: @text;
|
||||
font-size: @fontsize;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
// Fancy "Notice Me" Stripes
|
||||
.noticeStripes {
|
||||
background-color: @notice1; // Fallback for old browers.
|
||||
background-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
@notice1,
|
||||
@notice1 10px,
|
||||
@notice2 10px,
|
||||
@notice2 20px
|
||||
);
|
||||
}
|
||||
|
||||
// General Helpers
|
||||
.hidden { display: none; }
|
||||
|
||||
.clearBoth { clear: both; }
|
||||
.clearLeft { clear: left; }
|
||||
.clearRight { clear: right; }
|
||||
|
||||
.floatNone { float: none; }
|
||||
.floatLeft { float: left; }
|
||||
.floatRight { float: right; }
|
||||
|
||||
.alignTop { vertical-align: top; }
|
||||
.alignBottom { vertical-align: bottom; }
|
||||
.alignLeft { text-align: left; }
|
||||
.alignCenter { text-align: center; }
|
||||
.alignRight { text-align: right; }
|
||||
@import "_config";
|
||||
|
||||
|
||||
// Colors
|
||||
.color(@color) { // Explort a given color as a class.
|
||||
&.@{color} {
|
||||
color: @@color;
|
||||
}
|
||||
}
|
||||
|
||||
.color(normal);
|
||||
.color(good);
|
||||
.color(average);
|
||||
.color(bad);
|
||||
.color(label);
|
||||
.color(highlight);
|
||||
.color(dark);
|
||||
|
||||
// Text Styles
|
||||
.bold { font-weight: bold; }
|
||||
.italic { font-style: italic; }
|
||||
.fontReset {
|
||||
color: @text;
|
||||
font-size: @fontsize;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
// Fancy "Notice Me" Stripes
|
||||
.noticeStripes {
|
||||
background-color: @notice1; // Fallback for old browers.
|
||||
background-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
@notice1,
|
||||
@notice1 10px,
|
||||
@notice2 10px,
|
||||
@notice2 20px
|
||||
);
|
||||
}
|
||||
|
||||
// General Helpers
|
||||
.hidden { display: none !important; }
|
||||
|
||||
.clearBoth { clear: both; }
|
||||
.clearLeft { clear: left; }
|
||||
.clearRight { clear: right; }
|
||||
|
||||
.floatNone { float: none; }
|
||||
.floatLeft { float: left; }
|
||||
.floatRight { float: right; }
|
||||
|
||||
.alignTop { vertical-align: top; }
|
||||
.alignBottom { vertical-align: bottom; }
|
||||
.alignLeft { text-align: left; }
|
||||
.alignCenter { text-align: center; }
|
||||
.alignRight { text-align: right; }
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
@import "_config";
|
||||
|
||||
@import "_document";
|
||||
@import "_util";
|
||||
|
||||
@import "_chrome";
|
||||
@import "_section";
|
||||
@import "_link";
|
||||
@import "_bar";
|
||||
|
||||
@import "_generic";
|
||||
@import "_nanotrasen";
|
||||
@@ -1,14 +1,14 @@
|
||||
<div id='container' class='container'>
|
||||
<header id='titlebar' class='titlebar' unselectable='on'>
|
||||
<i class='statusicon fa fa-eye fa-2x' unselectable='on'></i>
|
||||
<span class='title' unselectable='on'>{{=config.title}}</span>
|
||||
<i class='minimize fancy fa fa-minus fa-2x' unselectable='on'></i>
|
||||
<i class='close fancy fa fa-close fa-2x' unselectable='on'></i>
|
||||
</header>
|
||||
|
||||
<div id='content' class='content titlebared resizeable' unselectable='on'>
|
||||
<div class='loading'>Initiating...</div>
|
||||
</div>
|
||||
|
||||
<div id='resize' class='resize fancy' unselectable='on'></div>
|
||||
</div>
|
||||
<div id='container' class='container'>
|
||||
<header id='titlebar' class='titlebar' unselectable='on'>
|
||||
<i class='statusicon fa fa-eye fa-2x' unselectable='on'></i>
|
||||
<span class='title' unselectable='on'>{{=config.title}}</span>
|
||||
<i class='minimize fa fa-minus fa-2x fancy' unselectable='on'></i>
|
||||
<i class='close fa fa-close fa-2x fancy' unselectable='on'></i>
|
||||
</header>
|
||||
|
||||
<div id='content' class='content titlebared resizeable' unselectable='on'>
|
||||
<div class='loading'>Initiating...</div>
|
||||
</div>
|
||||
|
||||
<div id='resize' class='resize fancy' unselectable='on'></div>
|
||||
</div>
|
||||
|
||||
+233
-233
@@ -1,233 +1,233 @@
|
||||
<article class="notice">
|
||||
{{? data.siliconUser}}
|
||||
<section>
|
||||
<span class="label">Interface Lock:</span>
|
||||
<div class="content">
|
||||
{{=helper.link('Engaged', 'lock', 'toggleaccess', null, data.locked ? 'selected' : null)}}
|
||||
{{=helper.link('Disengaged', 'unlock', 'toggleaccess', null, data.locked ? null : 'selected')}}
|
||||
</div>
|
||||
</section>
|
||||
{{??}}
|
||||
{{? data.locked}}
|
||||
<span>Swipe an ID card to unlock this interface.</span>
|
||||
{{??}}
|
||||
<span>Swipe an ID card to lock this interface.</span>
|
||||
{{?}}
|
||||
{{?}}
|
||||
</article>
|
||||
<article class="display">
|
||||
<header><h2>Air Status</h2></header>
|
||||
{{? data.environment_data}}
|
||||
{{~ data.environment_data:info:i}}
|
||||
<section>
|
||||
<span class="label">{{=info.name}}:</span>
|
||||
<div class="content">
|
||||
{{? info.danger_level == 2}}
|
||||
<span class="bad">
|
||||
{{?? info.danger_level == 1}}
|
||||
<span class="average">
|
||||
{{??}}
|
||||
<span class="good">
|
||||
{{?}}
|
||||
{{=helper.fixed(info.value, 2)}}{{=info.unit}}</span>
|
||||
</div>
|
||||
</section>
|
||||
{{~}}
|
||||
<section>
|
||||
<span class="label">Local Status:</span>
|
||||
<div class="content">
|
||||
{{? data.danger_level == 2}}
|
||||
<span class="bad bold">Danger (Internals Required)</span>
|
||||
{{?? data.danger_level == 1}}
|
||||
<span class="average bold">Caution</span>
|
||||
{{??}}
|
||||
<span class="good">Optimal</span>
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Area Status:</span>
|
||||
<div class="content">
|
||||
{{? data.atmos_alarm}}
|
||||
<span class="bad bold">Atmosphere Alarm</span>
|
||||
{{?? data.fire_alarm}}
|
||||
<span class="bad bold">Fire Alarm</span>
|
||||
{{??}}
|
||||
<span class="good">Nominal</span>
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
{{??}}
|
||||
<section>
|
||||
<span class="bad bold">Warning: Cannot obtain air sample for analysis.</span>
|
||||
</section>
|
||||
{{?}}
|
||||
{{? data.dangerous}}
|
||||
<hr />
|
||||
<section>
|
||||
<span class="bad bold">Warning: Safety measures offline. Device may exhibit abnormal behavior.</span>
|
||||
</section>
|
||||
{{?}}
|
||||
</article>
|
||||
{{? (!data.locked || data.siliconUser)}}
|
||||
{{? data.screen != 1}}
|
||||
{{=helper.link('Back', 'arrow-left', 'screen', {'screen': 1})}}
|
||||
{{?}}
|
||||
{{? data.screen == 1}}
|
||||
<article class="display">
|
||||
<header><h2>Air Controls</h2></header>
|
||||
<section class="button">
|
||||
{{? !data.atmos_alarm}}
|
||||
{{=helper.link('Area Atmospheric Alarm', 'hand-stop-o', 'alarm')}}
|
||||
{{??}}
|
||||
{{=helper.link('Area Atmospheric Alarm', 'close', 'reset', null, null, 'caution')}}
|
||||
{{?}}
|
||||
</section>
|
||||
<section class="button">
|
||||
{{? data.mode != 3}}
|
||||
{{=helper.link('Panic Siphon', 'exclamation', 'mode', {'mode': 3})}}
|
||||
{{??}}
|
||||
{{=helper.link('Panic Siphon', 'close', 'mode', {'mode': 1}, null, 'danger')}}
|
||||
{{?}}
|
||||
</section>
|
||||
<section class="button">
|
||||
{{=helper.link('Vent Controls', 'sign-out', 'screen', {'screen': 2})}}
|
||||
</section>
|
||||
<section class="button">
|
||||
{{=helper.link('Scrubber Controls', 'filter', 'screen', {'screen': 3})}}
|
||||
</section>
|
||||
<section class="button">
|
||||
{{=helper.link('Set Environmental Mode', 'cog', 'screen', {'screen': 4})}}
|
||||
</section>
|
||||
<section class="button">
|
||||
{{=helper.link('Set Alarm Threshold', 'bar-chart', 'screen', {'screen': 5})}}
|
||||
</section>
|
||||
</article>
|
||||
{{?? data.screen == 2}}
|
||||
{{~ data.vents:vent:i}}
|
||||
<article class="display">
|
||||
<header><h3>{{=vent.long_name}}</h3></header>
|
||||
<section>
|
||||
<span class="label">Power:</span>
|
||||
<div class="content">
|
||||
{{? vent.power}}
|
||||
{{=helper.link('On', 'power-off', 'adjust', {'id_tag': vent.id_tag, 'command': 'power', 'val': 0}, null, null)}}
|
||||
{{??}}
|
||||
{{=helper.link('Off', 'close', 'adjust', {'id_tag': vent.id_tag, 'command': 'power', 'val': 1}, null, 'danger')}}
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Mode:</span>
|
||||
<div class="content">
|
||||
{{? vent.direction == "release"}}
|
||||
<span class="good">Pressurizing</span>
|
||||
{{??}}
|
||||
<span class="bad">Siphoning</span>
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Pressure Checks:</span>
|
||||
<div class="content">
|
||||
{{=helper.link('Internal', 'sign-in', 'adjust', {'id_tag': vent.id_tag, 'command': 'incheck', 'val': vent.checks}, null, vent.incheck ? 'selected' : null)}}
|
||||
{{=helper.link('External', 'sign-out', 'adjust', {'id_tag': vent.id_tag, 'command': 'excheck', 'val': vent.checks}, null, vent.excheck ? 'selected' : null)}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Set Pressure:</span>
|
||||
<div class="content">
|
||||
{{=helper.link(helper.fixed(vent.external), 'pencil', 'adjust', {'id_tag': vent.id_tag, 'command': 'set_external_pressure'})}}
|
||||
{{=helper.link('Reset', 'refresh', 'adjust', {'id_tag': vent.id_tag, 'command': 'reset_external_pressure'}, vent.extdefault ? 'disabled' : null)}}
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
{{~}}
|
||||
{{? !data.vents.length}}
|
||||
<span class="bad">No vents connected.</span>
|
||||
{{?}}
|
||||
{{?? data.screen == 3}}
|
||||
{{~ data.scrubbers:scrubber:i}}
|
||||
<article class="display">
|
||||
<header><h3>{{=scrubber.long_name}}</h3></header>
|
||||
<section>
|
||||
<span class="label">Power:</span>
|
||||
<div class="content">
|
||||
{{? scrubber.power}}
|
||||
{{=helper.link('On', 'power-off', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'power', 'val': 0}, null, null)}}
|
||||
{{??}}
|
||||
{{=helper.link('Off', 'close', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'power', 'val': 1}, null, 'danger')}}
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Mode:</span>
|
||||
<div class="content">
|
||||
{{? scrubber.scrubbing}}
|
||||
{{=helper.link('Scrubbing', 'filter', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'scrubbing', 'val': 0}, null, null)}}
|
||||
{{??}}
|
||||
{{=helper.link('Siphoning', 'sign-in', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'scrubbing', 'val': 1}, null, 'danger')}}
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Range:</span>
|
||||
<div class="content">
|
||||
{{? scrubber.widenet}}
|
||||
{{=helper.link('Extended', 'expand', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'widenet', 'val': 0}, null, 'caution')}}
|
||||
{{??}}
|
||||
{{=helper.link('Normal', 'compress', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'widenet', 'val': 1}, null, null)}}
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Filters:</span>
|
||||
<div class="content">
|
||||
{{=helper.link("CO2", scrubber.filter_co2 ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "co2_scrub", 'val': scrubber.filter_co2 ? 0 : 1}, null, scrubber.filter_co2 ? 'selected' : null)}}
|
||||
{{=helper.link("N2O", scrubber.filter_n2o ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "n2o_scrub", 'val': scrubber.filter_n2o ? 0 : 1}, null, scrubber.filter_n2o ? 'selected' : null)}}
|
||||
{{=helper.link("Plasma", scrubber.filter_toxins ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "tox_scrub", 'val': scrubber.filter_toxins ? 0 : 1}, null, scrubber.filter_toxins ? 'selected' : null)}}
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
{{~}}
|
||||
{{? !data.scrubbers.length}}
|
||||
<span class="bad">No scrubbers connected.</span>
|
||||
{{?}}
|
||||
{{?? data.screen == 4}}
|
||||
<article class="display">
|
||||
<header><h2>Environmental Modes</h2></header>
|
||||
{{~ data.modes:mode:i}}
|
||||
<section class="button">
|
||||
{{=helper.link(mode.name, mode.selected ? 'check-square-o' : 'square-o', 'mode', {'mode': mode.mode}, null, mode.selected ? (mode.danger ? 'danger' : 'selected') : null)}}
|
||||
</section>
|
||||
{{~}}
|
||||
</display>
|
||||
{{?? data.screen == 5}}
|
||||
<article class="display">
|
||||
<header><h2>Alarm Thresholds</h2></header>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><span class="bad">min2</span></th>
|
||||
<th><span class="average">min1</span></th>
|
||||
<th><span class="average">max1</span></th>
|
||||
<th><span class="bad">max2</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{~ data.thresholds:threshold:i}}
|
||||
<tr>
|
||||
<th>{{=threshold.name}}</th>
|
||||
{{~ threshold.settings:setting:j}}
|
||||
<td>
|
||||
{{=helper.link(setting.selected >= 0 ? helper.round(setting.selected*100)/100 : "Off", null, 'adjust', {'command': 'set_threshold', 'env': setting.env, 'var': setting.val})}}
|
||||
</td>
|
||||
{{~}}
|
||||
</tr>
|
||||
{{~}}
|
||||
</tbody>
|
||||
<table>
|
||||
</article>
|
||||
{{?}}
|
||||
{{?}}
|
||||
<article class="notice">
|
||||
{{? data.siliconUser}}
|
||||
<section>
|
||||
<span class="label">Interface Lock:</span>
|
||||
<div class="content">
|
||||
{{=helper.link('Engaged', 'lock', 'toggleaccess', null, data.locked ? 'selected' : null)}}
|
||||
{{=helper.link('Disengaged', 'unlock', 'toggleaccess', null, data.locked ? null : 'selected')}}
|
||||
</div>
|
||||
</section>
|
||||
{{??}}
|
||||
{{? data.locked}}
|
||||
<span>Swipe an ID card to unlock this interface.</span>
|
||||
{{??}}
|
||||
<span>Swipe an ID card to lock this interface.</span>
|
||||
{{?}}
|
||||
{{?}}
|
||||
</article>
|
||||
<article class="display">
|
||||
<header><h2>Air Status</h2></header>
|
||||
{{? data.environment_data}}
|
||||
{{~ data.environment_data:info:i}}
|
||||
<section>
|
||||
<span class="label">{{=info.name}}:</span>
|
||||
<div class="content">
|
||||
{{? info.danger_level == 2}}
|
||||
<span class="bad">
|
||||
{{?? info.danger_level == 1}}
|
||||
<span class="average">
|
||||
{{??}}
|
||||
<span class="good">
|
||||
{{?}}
|
||||
{{=helper.fixed(info.value, 2)}}{{=info.unit}}</span>
|
||||
</div>
|
||||
</section>
|
||||
{{~}}
|
||||
<section>
|
||||
<span class="label">Local Status:</span>
|
||||
<div class="content">
|
||||
{{? data.danger_level == 2}}
|
||||
<span class="bad bold">Danger (Internals Required)</span>
|
||||
{{?? data.danger_level == 1}}
|
||||
<span class="average bold">Caution</span>
|
||||
{{??}}
|
||||
<span class="good">Optimal</span>
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Area Status:</span>
|
||||
<div class="content">
|
||||
{{? data.atmos_alarm}}
|
||||
<span class="bad bold">Atmosphere Alarm</span>
|
||||
{{?? data.fire_alarm}}
|
||||
<span class="bad bold">Fire Alarm</span>
|
||||
{{??}}
|
||||
<span class="good">Nominal</span>
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
{{??}}
|
||||
<section>
|
||||
<span class="bad bold">Warning: Cannot obtain air sample for analysis.</span>
|
||||
</section>
|
||||
{{?}}
|
||||
{{? data.dangerous}}
|
||||
<hr />
|
||||
<section>
|
||||
<span class="bad bold">Warning: Safety measures offline. Device may exhibit abnormal behavior.</span>
|
||||
</section>
|
||||
{{?}}
|
||||
</article>
|
||||
{{? (!data.locked || data.siliconUser)}}
|
||||
{{? data.screen != 1}}
|
||||
{{=helper.link('Back', 'arrow-left', 'screen', {'screen': 1})}}
|
||||
{{?}}
|
||||
{{? data.screen == 1}}
|
||||
<article class="display">
|
||||
<header><h2>Air Controls</h2></header>
|
||||
<section class="button">
|
||||
{{? !data.atmos_alarm}}
|
||||
{{=helper.link('Area Atmospheric Alarm', 'hand-stop-o', 'alarm')}}
|
||||
{{??}}
|
||||
{{=helper.link('Area Atmospheric Alarm', 'close', 'reset', null, null, 'caution')}}
|
||||
{{?}}
|
||||
</section>
|
||||
<section class="button">
|
||||
{{? data.mode != 3}}
|
||||
{{=helper.link('Panic Siphon', 'exclamation', 'mode', {'mode': 3})}}
|
||||
{{??}}
|
||||
{{=helper.link('Panic Siphon', 'close', 'mode', {'mode': 1}, null, 'danger')}}
|
||||
{{?}}
|
||||
</section>
|
||||
<section class="button">
|
||||
{{=helper.link('Vent Controls', 'sign-out', 'screen', {'screen': 2})}}
|
||||
</section>
|
||||
<section class="button">
|
||||
{{=helper.link('Scrubber Controls', 'filter', 'screen', {'screen': 3})}}
|
||||
</section>
|
||||
<section class="button">
|
||||
{{=helper.link('Set Environmental Mode', 'cog', 'screen', {'screen': 4})}}
|
||||
</section>
|
||||
<section class="button">
|
||||
{{=helper.link('Set Alarm Threshold', 'bar-chart', 'screen', {'screen': 5})}}
|
||||
</section>
|
||||
</article>
|
||||
{{?? data.screen == 2}}
|
||||
{{~ data.vents:vent:i}}
|
||||
<article class="display">
|
||||
<header><h3>{{=vent.long_name}}</h3></header>
|
||||
<section>
|
||||
<span class="label">Power:</span>
|
||||
<div class="content">
|
||||
{{? vent.power}}
|
||||
{{=helper.link('On', 'power-off', 'adjust', {'id_tag': vent.id_tag, 'command': 'power', 'val': 0}, null, null)}}
|
||||
{{??}}
|
||||
{{=helper.link('Off', 'close', 'adjust', {'id_tag': vent.id_tag, 'command': 'power', 'val': 1}, null, 'danger')}}
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Mode:</span>
|
||||
<div class="content">
|
||||
{{? vent.direction == "release"}}
|
||||
<span class="good">Pressurizing</span>
|
||||
{{??}}
|
||||
<span class="bad">Siphoning</span>
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Pressure Checks:</span>
|
||||
<div class="content">
|
||||
{{=helper.link('Internal', 'sign-in', 'adjust', {'id_tag': vent.id_tag, 'command': 'incheck', 'val': vent.checks}, null, vent.incheck ? 'selected' : null)}}
|
||||
{{=helper.link('External', 'sign-out', 'adjust', {'id_tag': vent.id_tag, 'command': 'excheck', 'val': vent.checks}, null, vent.excheck ? 'selected' : null)}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Set Pressure:</span>
|
||||
<div class="content">
|
||||
{{=helper.link(helper.fixed(vent.external), 'pencil', 'adjust', {'id_tag': vent.id_tag, 'command': 'set_external_pressure'})}}
|
||||
{{=helper.link('Reset', 'refresh', 'adjust', {'id_tag': vent.id_tag, 'command': 'reset_external_pressure'}, vent.extdefault ? 'disabled' : null)}}
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
{{~}}
|
||||
{{? !data.vents.length}}
|
||||
<span class="bad">No vents connected.</span>
|
||||
{{?}}
|
||||
{{?? data.screen == 3}}
|
||||
{{~ data.scrubbers:scrubber:i}}
|
||||
<article class="display">
|
||||
<header><h3>{{=scrubber.long_name}}</h3></header>
|
||||
<section>
|
||||
<span class="label">Power:</span>
|
||||
<div class="content">
|
||||
{{? scrubber.power}}
|
||||
{{=helper.link('On', 'power-off', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'power', 'val': 0}, null, null)}}
|
||||
{{??}}
|
||||
{{=helper.link('Off', 'close', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'power', 'val': 1}, null, 'danger')}}
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Mode:</span>
|
||||
<div class="content">
|
||||
{{? scrubber.scrubbing}}
|
||||
{{=helper.link('Scrubbing', 'filter', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'scrubbing', 'val': 0}, null, null)}}
|
||||
{{??}}
|
||||
{{=helper.link('Siphoning', 'sign-in', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'scrubbing', 'val': 1}, null, 'danger')}}
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Range:</span>
|
||||
<div class="content">
|
||||
{{? scrubber.widenet}}
|
||||
{{=helper.link('Extended', 'expand', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'widenet', 'val': 0}, null, 'caution')}}
|
||||
{{??}}
|
||||
{{=helper.link('Normal', 'compress', 'adjust', {'id_tag': scrubber.id_tag, 'command': 'widenet', 'val': 1}, null, null)}}
|
||||
{{?}}
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<span class="label">Filters:</span>
|
||||
<div class="content">
|
||||
{{=helper.link("CO2", scrubber.filter_co2 ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "co2_scrub", 'val': scrubber.filter_co2 ? 0 : 1}, null, scrubber.filter_co2 ? 'selected' : null)}}
|
||||
{{=helper.link("N2O", scrubber.filter_n2o ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "n2o_scrub", 'val': scrubber.filter_n2o ? 0 : 1}, null, scrubber.filter_n2o ? 'selected' : null)}}
|
||||
{{=helper.link("Plasma", scrubber.filter_toxins ? 'check-square-o' : 'square-o', 'adjust', {'id_tag': scrubber.id_tag, 'command': "tox_scrub", 'val': scrubber.filter_toxins ? 0 : 1}, null, scrubber.filter_toxins ? 'selected' : null)}}
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
{{~}}
|
||||
{{? !data.scrubbers.length}}
|
||||
<span class="bad">No scrubbers connected.</span>
|
||||
{{?}}
|
||||
{{?? data.screen == 4}}
|
||||
<article class="display">
|
||||
<header><h2>Environmental Modes</h2></header>
|
||||
{{~ data.modes:mode:i}}
|
||||
<section class="button">
|
||||
{{=helper.link(mode.name, mode.selected ? 'check-square-o' : 'square-o', 'mode', {'mode': mode.mode}, null, mode.selected ? (mode.danger ? 'danger' : 'selected') : null)}}
|
||||
</section>
|
||||
{{~}}
|
||||
</article>
|
||||
{{?? data.screen == 5}}
|
||||
<article class="display">
|
||||
<header><h2>Alarm Thresholds</h2></header>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><span class="bad">min2</span></th>
|
||||
<th><span class="average">min1</span></th>
|
||||
<th><span class="average">max1</span></th>
|
||||
<th><span class="bad">max2</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{~ data.thresholds:threshold:i}}
|
||||
<tr>
|
||||
<th>{{=threshold.name}}</th>
|
||||
{{~ threshold.settings:setting:j}}
|
||||
<td>
|
||||
{{=helper.link(setting.selected >= 0 ? helper.round(setting.selected*100)/100 : "Off", null, 'adjust', {'command': 'set_threshold', 'env': setting.env, 'var': setting.val})}}
|
||||
</td>
|
||||
{{~}}
|
||||
</tr>
|
||||
{{~}}
|
||||
</tbody>
|
||||
<table>
|
||||
</article>
|
||||
{{?}}
|
||||
{{?}}
|
||||
|
||||
Reference in New Issue
Block a user