mirror of
https://github.com/CHOMPstation/CHOMPstation.git
synced 2026-08-23 12:06:22 +01:00
Nano UI Framework
------------------------------------------------------------ nanoui - For creating and updating browser UIs manomanager - For managing nanouis Includes the "D:\Development\SS13-TG\code\game\machinery\cryo.dm" rebuilt using Nano. Templates are stored in /nano/templates This code is in development, it's also not commented yet.
This commit is contained in:
@@ -36,6 +36,8 @@
|
||||
#define FILE_DIR "icons/turf"
|
||||
#define FILE_DIR "icons/vending_icons"
|
||||
#define FILE_DIR "icons/xenoarch_icons"
|
||||
#define FILE_DIR "nano"
|
||||
#define FILE_DIR "nano/images"
|
||||
#define FILE_DIR "sound"
|
||||
#define FILE_DIR "sound/AI"
|
||||
#define FILE_DIR "sound/ambience"
|
||||
@@ -1044,6 +1046,12 @@
|
||||
#include "code\modules\mob\new_player\preferences_setup.dm"
|
||||
#include "code\modules\mob\new_player\skill.dm"
|
||||
#include "code\modules\mob\new_player\sprite_accessories.dm"
|
||||
#include "code\modules\nano\_JSON.dm"
|
||||
#include "code\modules\nano\JSON Reader.dm"
|
||||
#include "code\modules\nano\JSON Writer.dm"
|
||||
#include "code\modules\nano\nanoexternal.dm"
|
||||
#include "code\modules\nano\nanomanager.dm"
|
||||
#include "code\modules\nano\nanoui.dm"
|
||||
#include "code\modules\organs\blood.dm"
|
||||
#include "code\modules\organs\organ.dm"
|
||||
#include "code\modules\organs\organ_external.dm"
|
||||
|
||||
@@ -24,6 +24,7 @@ datum/controller/game_controller
|
||||
var/objects_cost = 0
|
||||
var/networks_cost = 0
|
||||
var/powernets_cost = 0
|
||||
var/nano_cost = 0
|
||||
var/events_cost = 0
|
||||
var/ticker_cost = 0
|
||||
var/total_cost = 0
|
||||
@@ -196,6 +197,13 @@ datum/controller/game_controller/proc/process()
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//NANO UIS
|
||||
timer = world.timeofday
|
||||
process_nano()
|
||||
nano_cost = (world.timeofday - timer) / 10
|
||||
|
||||
sleep(breather_ticks)
|
||||
|
||||
//EVENTS
|
||||
timer = world.timeofday
|
||||
process_events()
|
||||
@@ -208,7 +216,7 @@ datum/controller/game_controller/proc/process()
|
||||
ticker_cost = (world.timeofday - timer) / 10
|
||||
|
||||
//TIMING
|
||||
total_cost = air_cost + sun_cost + mobs_cost + diseases_cost + machines_cost + objects_cost + networks_cost + powernets_cost + events_cost + ticker_cost
|
||||
total_cost = air_cost + sun_cost + mobs_cost + diseases_cost + machines_cost + objects_cost + networks_cost + powernets_cost + nano_cost + events_cost + ticker_cost
|
||||
|
||||
var/end_time = world.timeofday
|
||||
if(end_time < start_time)
|
||||
@@ -286,6 +294,16 @@ datum/controller/game_controller/proc/process_powernets()
|
||||
continue
|
||||
powernets.Cut(i,i+1)
|
||||
|
||||
datum/controller/game_controller/proc/process_nano()
|
||||
var/i = 1
|
||||
while(i<=nanomanager.processing_uis.len)
|
||||
var/datum/nanoui/ui = nanomanager.processing_uis[i]
|
||||
if(ui && ui.src_object && ui.user)
|
||||
ui.process()
|
||||
i++
|
||||
continue
|
||||
nanomanager.processing_uis.Cut(i,i+1)
|
||||
|
||||
datum/controller/game_controller/proc/process_events()
|
||||
last_thing_processed = /datum/event
|
||||
var/i = 1
|
||||
|
||||
+60
-31
@@ -1,11 +1,11 @@
|
||||
/obj/machinery/atmospherics/unary/cryo_cell
|
||||
name = "cryo cell"
|
||||
name = "Cryo Cell"
|
||||
icon = 'icons/obj/cryogenics.dmi'
|
||||
icon_state = "cell-off"
|
||||
density = 1
|
||||
anchored = 1.0
|
||||
layer = 2.8
|
||||
|
||||
|
||||
var/on = 0
|
||||
var/temperature_archived
|
||||
var/mob/living/carbon/occupant = null
|
||||
@@ -61,37 +61,66 @@
|
||||
go_out()
|
||||
return
|
||||
|
||||
/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user as mob)
|
||||
user.set_machine(src)
|
||||
var/beaker_text = ""
|
||||
var/health_text = ""
|
||||
var/temp_text = ""
|
||||
if(occupant)
|
||||
if(occupant.health <= -100)
|
||||
health_text = "<FONT color=red>Dead</FONT>"
|
||||
else if(occupant.health < 0)
|
||||
health_text = "<FONT color=red>[round(occupant.health,0.1)]</FONT>"
|
||||
else
|
||||
health_text = "[round(occupant.health,0.1)]"
|
||||
if(air_contents.temperature > T0C)
|
||||
temp_text = "<FONT color=red>[air_contents.temperature]</FONT>"
|
||||
/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user)
|
||||
ui_interact(user)
|
||||
|
||||
/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main")
|
||||
|
||||
var/data[0]
|
||||
data["isOperating"] = on
|
||||
|
||||
data["hasOccupant"] = occupant ? 1 : 0
|
||||
|
||||
var/occupantData[0]
|
||||
if (!occupant)
|
||||
occupantData["name"] = null
|
||||
occupantData["stat"] = null
|
||||
occupantData["health"] = 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"] = round(occupant.health)
|
||||
occupantData["bruteLoss"] = round(occupant.getBruteLoss())
|
||||
occupantData["oxyLoss"] = round(occupant.getOxyLoss())
|
||||
occupantData["toxLoss"] = round(occupant.getToxLoss())
|
||||
occupantData["fireLoss"] = round(occupant.getFireLoss())
|
||||
occupantData["bodyTemperature"] = round(occupant.bodytemperature)
|
||||
data["occupant"] = occupantData;
|
||||
|
||||
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)
|
||||
temp_text = "<FONT color=black>[air_contents.temperature]</FONT>"
|
||||
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
|
||||
|
||||
//user << list2json(data)
|
||||
|
||||
var/datum/nanoui/ui = nanomanager.get_open_ui(user, src, ui_key)
|
||||
if (!ui)
|
||||
ui = new(user, src, ui_key, 'nano/templates/cryo.tmpl', "Cryo Cell Control System", 520, 410)
|
||||
// When the UI is first opened this is the data it will use
|
||||
ui.set_initial_data(data)
|
||||
ui.open()
|
||||
// Auto update every Master Controller tick
|
||||
ui.set_auto_update(1)
|
||||
else
|
||||
temp_text = "<FONT color=blue>[air_contents.temperature]</FONT>"
|
||||
if(beaker)
|
||||
beaker_text = "<B>Beaker:</B> <A href='?src=\ref[src];eject=1'>Eject</A>"
|
||||
else
|
||||
beaker_text = "<B>Beaker:</B> <FONT color=red>No beaker loaded</FONT>"
|
||||
var/dat = {"<B>Cryo cell control system</B><BR>
|
||||
<B>Current cell temperature:</B> [temp_text]K<BR>
|
||||
<B>Cryo status:</B> [ on ? "<A href='?src=\ref[src];start=1'>Off</A> <B>On</B>" : "<B>Off</B> <A href='?src=\ref[src];start=1'>On</A>"]<BR>
|
||||
[beaker_text]<BR><BR>
|
||||
<B>Current occupant:</B> [occupant ? "<BR>Name: [occupant]<BR>Health: [health_text]<BR>Oxygen deprivation: [round(occupant.getOxyLoss(),0.1)]<BR>Brute damage: [round(occupant.getBruteLoss(),0.1)]<BR>Fire damage: [round(occupant.getFireLoss(),0.1)]<BR>Toxin damage: [round(occupant.getToxLoss(),0.1)]<BR>Body temperature: [occupant.bodytemperature]<BR>Heartbeat rate: [occupant.get_pulse(GETPULSE_TOOL)]" : "<FONT color=red>None</FONT>"]<BR>
|
||||
"}
|
||||
user.set_machine(src)
|
||||
user << browse(dat, "window=cryo")
|
||||
onclose(user, "cryo")
|
||||
// The UI is already open so push the new data to it
|
||||
ui.push_data(data)
|
||||
return
|
||||
//user.set_machine(src)
|
||||
|
||||
/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list)
|
||||
if ((get_dist(src, usr) <= 1) || istype(usr, /mob/living/silicon/ai))
|
||||
|
||||
@@ -165,6 +165,9 @@ var/shuttlecoming = 0
|
||||
var/join_motd = null
|
||||
var/forceblob = 0
|
||||
|
||||
// nanomanager, the manager for Nano UIs
|
||||
var/datum/nanomanager/nanomanager = new()
|
||||
|
||||
//airlockWireColorToIndex takes a number representing the wire color, e.g. the orange wire is always 1, the dark red wire is always 2, etc. It returns the index for whatever that wire does.
|
||||
//airlockIndexToWireColor does the opposite thing - it takes the index for what the wire does, for example AIRLOCK_WIRE_IDSCAN is 1, AIRLOCK_WIRE_POWER1 is 2, etc. It returns the wire color number.
|
||||
//airlockWireColorToFlag takes the wire color number and returns the flag for it (1, 2, 4, 8, 16, etc)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
////////////
|
||||
//SECURITY//
|
||||
////////////
|
||||
#define TOPIC_SPAM_DELAY 4 //4 ticks is about 3/10ths of a second
|
||||
#define TOPIC_SPAM_DELAY 2 //2 ticks is about 2/10ths of a second; it was 4 ticks, but that caused too many clicks to be lost due to lag
|
||||
#define UPLOAD_LIMIT 10485760 //Restricts client uploads to the server to 10MB //Boosted this thing. What's the worst that can happen?
|
||||
#define MIN_CLIENT_VERSION 0 //Just an ambiguously low version for now, I don't want to suddenly stop people playing.
|
||||
//I would just like the code ready should it ever need to be used.
|
||||
@@ -242,6 +242,19 @@
|
||||
//send resources to the client. It's here in its own proc so we can move it around easiliy if need be
|
||||
/client/proc/send_resources()
|
||||
getFiles(
|
||||
'nano/js/libraries.min.js',
|
||||
'nano/js/nano_update.js',
|
||||
'nano/js/nano_config.js',
|
||||
'nano/js/nano_base_helpers.js',
|
||||
'nano/css/common.css',
|
||||
'nano/css/icons.css',
|
||||
'nano/templates/cryo.tmpl',
|
||||
'nano/images/uiBackground.png',
|
||||
'nano/images/uiIcons16.png',
|
||||
'nano/images/uiIcons24.png',
|
||||
'nano/images/uiLinkPendingIcon.gif',
|
||||
'nano/images/uiNoticeBackground.jpg',
|
||||
'nano/images/uiTitleFluff.png',
|
||||
'html/search.js',
|
||||
'html/panels.css',
|
||||
'icons/pda_icons/pda_atmos.png',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/mob/Logout()
|
||||
nanomanager.user_logout(src) // this is used to clean up (remove) this user's Nano UIs
|
||||
player_list -= src
|
||||
log_access("Logout: [key_name(src)]")
|
||||
if(admin_datums[src.ckey])
|
||||
|
||||
@@ -719,6 +719,7 @@ note dizziness decrements automatically in the mob's Life() proc.
|
||||
stat(null,"Mch-[master_controller.machines_cost]\t#[machines.len]")
|
||||
stat(null,"Obj-[master_controller.objects_cost]\t#[processing_objects.len]")
|
||||
stat(null,"Net-[master_controller.networks_cost]\tPnet-[master_controller.powernets_cost]")
|
||||
stat(null,"NanoUI-[master_controller.nano_cost]\t#[nanomanager.processing_uis.len]")
|
||||
stat(null,"Tick-[master_controller.ticker_cost]\tALL-[master_controller.total_cost]")
|
||||
else
|
||||
stat(null,"MasterController-ERROR")
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
json_token
|
||||
var
|
||||
value
|
||||
New(v)
|
||||
src.value = v
|
||||
text
|
||||
number
|
||||
word
|
||||
symbol
|
||||
eof
|
||||
|
||||
json_reader
|
||||
var
|
||||
list
|
||||
string = list("'", "\"")
|
||||
symbols = list("{", "}", "\[", "]", ":", "\"", "'", ",")
|
||||
sequences = list("b" = 8, "t" = 9, "n" = 10, "f" = 12, "r" = 13)
|
||||
tokens
|
||||
json
|
||||
i = 1
|
||||
|
||||
|
||||
proc
|
||||
// scanner
|
||||
ScanJson(json)
|
||||
src.json = json
|
||||
. = new/list()
|
||||
src.i = 1
|
||||
while(src.i <= lentext(json))
|
||||
var/char = get_char()
|
||||
if(is_whitespace(char))
|
||||
i++
|
||||
continue
|
||||
if(string.Find(char))
|
||||
. += read_string(char)
|
||||
else if(symbols.Find(char))
|
||||
. += new/json_token/symbol(char)
|
||||
else if(is_digit(char))
|
||||
. += read_number()
|
||||
else
|
||||
. += read_word()
|
||||
i++
|
||||
. += new/json_token/eof()
|
||||
|
||||
read_word()
|
||||
var/val = ""
|
||||
while(i <= lentext(json))
|
||||
var/char = get_char()
|
||||
if(is_whitespace(char) || symbols.Find(char))
|
||||
i-- // let scanner handle this character
|
||||
return new/json_token/word(val)
|
||||
val += char
|
||||
i++
|
||||
|
||||
read_string(delim)
|
||||
var
|
||||
escape = FALSE
|
||||
val = ""
|
||||
while(++i <= lentext(json))
|
||||
var/char = get_char()
|
||||
if(escape)
|
||||
switch(char)
|
||||
if("\\", "'", "\"", "/", "u")
|
||||
val += char
|
||||
else
|
||||
// TODO: support octal, hex, unicode sequences
|
||||
ASSERT(sequences.Find(char))
|
||||
val += ascii2text(sequences[char])
|
||||
else
|
||||
if(char == delim)
|
||||
return new/json_token/text(val)
|
||||
else if(char == "\\")
|
||||
escape = TRUE
|
||||
else
|
||||
val += char
|
||||
CRASH("Unterminated string.")
|
||||
|
||||
read_number()
|
||||
var/val = ""
|
||||
var/char = get_char()
|
||||
while(is_digit(char) || char == "." || lowertext(char) == "e")
|
||||
val += char
|
||||
i++
|
||||
char = get_char()
|
||||
i-- // allow scanner to read the first non-number character
|
||||
return new/json_token/number(text2num(val))
|
||||
|
||||
check_char()
|
||||
ASSERT(args.Find(get_char()))
|
||||
|
||||
get_char()
|
||||
return copytext(json, i, i+1)
|
||||
|
||||
is_whitespace(char)
|
||||
return char == " " || char == "\t" || char == "\n" || text2ascii(char) == 13
|
||||
|
||||
is_digit(char)
|
||||
var/c = text2ascii(char)
|
||||
return 48 <= c && c <= 57 || char == "+" || char == "-"
|
||||
|
||||
|
||||
// parser
|
||||
ReadObject(list/tokens)
|
||||
src.tokens = tokens
|
||||
. = new/list()
|
||||
i = 1
|
||||
read_token("{", /json_token/symbol)
|
||||
while(i <= tokens.len)
|
||||
var/json_token/K = get_token()
|
||||
check_type(/json_token/word, /json_token/text)
|
||||
next_token()
|
||||
read_token(":", /json_token/symbol)
|
||||
|
||||
.[K.value] = read_value()
|
||||
|
||||
var/json_token/S = get_token()
|
||||
check_type(/json_token/symbol)
|
||||
switch(S.value)
|
||||
if(",")
|
||||
next_token()
|
||||
continue
|
||||
if("}")
|
||||
next_token()
|
||||
return
|
||||
else
|
||||
die()
|
||||
|
||||
get_token()
|
||||
return tokens[i]
|
||||
|
||||
next_token()
|
||||
return tokens[++i]
|
||||
|
||||
read_token(val, type)
|
||||
var/json_token/T = get_token()
|
||||
if(!(T.value == val && istype(T, type)))
|
||||
CRASH("Expected '[val]', found '[T.value]'.")
|
||||
next_token()
|
||||
return T
|
||||
|
||||
check_type(...)
|
||||
var/json_token/T = get_token()
|
||||
for(var/type in args)
|
||||
if(istype(T, type))
|
||||
return
|
||||
CRASH("Bad token type: [T.type].")
|
||||
|
||||
check_value(...)
|
||||
var/json_token/T = get_token()
|
||||
ASSERT(args.Find(T.value))
|
||||
|
||||
read_key()
|
||||
var/char = get_char()
|
||||
if(char == "\"" || char == "'")
|
||||
return read_string(char)
|
||||
|
||||
read_value()
|
||||
var/json_token/T = get_token()
|
||||
switch(T.type)
|
||||
if(/json_token/text, /json_token/number)
|
||||
next_token()
|
||||
return T.value
|
||||
if(/json_token/word)
|
||||
next_token()
|
||||
switch(T.value)
|
||||
if("true")
|
||||
return TRUE
|
||||
if("false")
|
||||
return FALSE
|
||||
if("null")
|
||||
return null
|
||||
if(/json_token/symbol)
|
||||
switch(T.value)
|
||||
if("\[")
|
||||
return read_array()
|
||||
if("{")
|
||||
return ReadObject(tokens.Copy(i))
|
||||
die()
|
||||
|
||||
read_array()
|
||||
read_token("\[", /json_token/symbol)
|
||||
. = new/list()
|
||||
var/list/L = .
|
||||
while(i <= tokens.len)
|
||||
// Avoid using Add() or += in case a list is returned.
|
||||
L.len++
|
||||
L[L.len] = read_value()
|
||||
var/json_token/T = get_token()
|
||||
check_type(/json_token/symbol)
|
||||
switch(T.value)
|
||||
if(",")
|
||||
next_token()
|
||||
continue
|
||||
if("]")
|
||||
next_token()
|
||||
return
|
||||
else
|
||||
die()
|
||||
next_token()
|
||||
CRASH("Unterminated array.")
|
||||
|
||||
|
||||
die(json_token/T)
|
||||
if(!T) T = get_token()
|
||||
CRASH("Unexpected token: [T.value].")
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
json_writer
|
||||
proc
|
||||
WriteObject(list/L)
|
||||
. = "{"
|
||||
var/i = 1
|
||||
for(var/k in L)
|
||||
var/val = L[k]
|
||||
. += {"\"[k]\":[write(val)]"}
|
||||
if(i++ < L.len)
|
||||
. += ","
|
||||
.+= "}"
|
||||
|
||||
write(val)
|
||||
if(isnum(val))
|
||||
return num2text(val, 100)
|
||||
else if(isnull(val))
|
||||
return "null"
|
||||
else if(istype(val, /list))
|
||||
if(is_associative(val))
|
||||
return WriteObject(val)
|
||||
else
|
||||
return write_array(val)
|
||||
else
|
||||
. += write_string("[val]")
|
||||
|
||||
write_array(list/L)
|
||||
. = "\["
|
||||
for(var/i = 1 to L.len)
|
||||
. += write(L[i])
|
||||
if(i < L.len)
|
||||
. += ","
|
||||
. += "]"
|
||||
|
||||
write_string(txt)
|
||||
var/static/list/json_escape = list("\\", "\"", "'", "\n")
|
||||
for(var/targ in json_escape)
|
||||
var/start = 1
|
||||
while(start <= lentext(txt))
|
||||
var/i = findtext(txt, targ, start)
|
||||
if(!i)
|
||||
break
|
||||
if(targ == "\n")
|
||||
txt = copytext(txt, 1, i) + "\\n" + copytext(txt, i+1)
|
||||
else
|
||||
txt = copytext(txt, 1, i) + "\\" + copytext(txt, i)
|
||||
start = i + 2
|
||||
return {""[txt]""}
|
||||
|
||||
is_associative(list/L)
|
||||
for(var/key in L)
|
||||
// if the key is a list that means it's actually an array of lists (stupid Byond...)
|
||||
if(!isnum(key) && !istype(key, /list))
|
||||
return TRUE
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
n_Json v11.3.21
|
||||
*/
|
||||
|
||||
proc
|
||||
json2list(json)
|
||||
var/static/json_reader/_jsonr = new()
|
||||
return _jsonr.ReadObject(_jsonr.ScanJson(json))
|
||||
|
||||
list2json(list/L)
|
||||
var/static/json_writer/_jsonw = new()
|
||||
return _jsonw.WriteObject(L)
|
||||
@@ -0,0 +1,6 @@
|
||||
// All movable things can have a Nano UI, always use ui_interact to open/interact with a Nano UI
|
||||
/atom/movable/proc/ui_interact(mob/user, ui_key = "main")
|
||||
return
|
||||
|
||||
// Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob
|
||||
/mob/var/list/open_uis = list()
|
||||
@@ -0,0 +1,69 @@
|
||||
// This is the window/UI manager for Nano UI
|
||||
// There should only ever be one (global) instance of nanomanger
|
||||
/datum/nanomanager
|
||||
var/open_uis[0]
|
||||
var/list/processing_uis = list()
|
||||
|
||||
/datum/nanomanager/New()
|
||||
return
|
||||
|
||||
/datum/nanomanager/proc/get_open_ui(var/mob/user, src_object, ui_key)
|
||||
var/src_object_key = "\ref[src_object]"
|
||||
if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
|
||||
return null
|
||||
else if (isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list))
|
||||
return null
|
||||
|
||||
for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key])
|
||||
if (ui.user == user)
|
||||
return ui
|
||||
|
||||
return null
|
||||
|
||||
/datum/nanomanager/proc/update_uis(src_object)
|
||||
var/src_object_key = "\ref[src_object]"
|
||||
if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
|
||||
return 0
|
||||
|
||||
var/update_count = 0
|
||||
for (var/ui_key in open_uis[src_object_key])
|
||||
for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key])
|
||||
if(ui && ui.src_object && ui.user)
|
||||
ui.process()
|
||||
update_count++
|
||||
return update_count
|
||||
|
||||
/datum/nanomanager/proc/ui_opened(var/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())
|
||||
else if (isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list))
|
||||
open_uis[src_object_key][ui.ui_key] = list();
|
||||
|
||||
ui.user.open_uis.Add(ui)
|
||||
var/list/uis = open_uis[src_object_key][ui.ui_key]
|
||||
uis.Add(ui)
|
||||
processing_uis.Add(ui)
|
||||
|
||||
/datum/nanomanager/proc/ui_closed(var/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 // wasn't open
|
||||
else if (isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list))
|
||||
return 0 // wasn't open
|
||||
|
||||
processing_uis.Remove(ui)
|
||||
ui.user.open_uis.Remove(ui)
|
||||
var/list/uis = open_uis[src_object_key][ui.ui_key]
|
||||
return uis.Remove(ui)
|
||||
|
||||
// user has logged out (or is switching mob) so close/clear all uis
|
||||
/datum/nanomanager/proc/user_logout(var/mob/user)
|
||||
if (isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0)
|
||||
return 0 // has no open uis
|
||||
|
||||
for (var/datum/nanoui/ui in user.open_uis)
|
||||
ui.close();
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
/datum/nanoui
|
||||
var/mob/user
|
||||
var/atom/movable/src_object
|
||||
var/title
|
||||
var/ui_key
|
||||
var/window_id // window_id is used as the window name for browse and onclose
|
||||
var/width = 0
|
||||
var/height = 0
|
||||
var/atom/ref = null
|
||||
var/on_close_logic = 1
|
||||
var/window_options = "focus=0;can_close=1;can_minimize=1;can_maximize=0;can_resize=1;titlebar=1;" // window option is set using window_id
|
||||
var/stylesheets[0]
|
||||
var/scripts[0]
|
||||
var/templates[0]
|
||||
var/title_image
|
||||
var/head_elements
|
||||
var/body_elements
|
||||
var/head_content = ""
|
||||
var/content = "<div id='mainTemplate'></div>" // the #mainTemplate div will contain the compiled "main" template html
|
||||
var/list/initial_data[0]
|
||||
var/is_auto_updating = 0
|
||||
var/status = 2
|
||||
|
||||
|
||||
/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null)
|
||||
user = nuser
|
||||
src_object = nsrc_object
|
||||
ui_key = nui_key
|
||||
window_id = "[ui_key]\ref[src_object]"
|
||||
|
||||
add_template("main", ntemplate)
|
||||
|
||||
if (ntitle)
|
||||
title = ntitle
|
||||
if (nwidth)
|
||||
width = nwidth
|
||||
if (nheight)
|
||||
height = nheight
|
||||
if (nref)
|
||||
ref = nref
|
||||
|
||||
add_common_assets()
|
||||
|
||||
/datum/nanoui/proc/add_common_assets()
|
||||
add_script("libraries", 'nano/js/libraries.min.js') // The jQuery library
|
||||
//add_script("jquery", 'nano/js/jquery.js') // The jQuery library
|
||||
//add_script("jsviews", 'nano/js/jsviews.js') // JSViews, for rendering templates
|
||||
add_script("nanoupdate", 'nano/js/nano_update.js') // The NanoUpdate JS, this is used to receive updates and apply them.
|
||||
add_script("nanoconfig", 'nano/js/nano_config.js') // The NanoUpdate JS, this is used to receive updates and apply them.
|
||||
add_script("nanobasehelpers", 'nano/js/nano_base_helpers.js') // The NanoBaseHelpers JS, this is used to set up template helpers which are common to all templates
|
||||
add_stylesheet("common", 'nano/css/common.css') // this CSS sheet is common to all UIs
|
||||
add_stylesheet("icons", 'nano/css/icons.css') // this CSS sheet is common to all UIs
|
||||
|
||||
/datum/nanoui/proc/set_status(state)
|
||||
if (state != status)
|
||||
status = state
|
||||
push_data(list(), 1) // Update the UI
|
||||
else
|
||||
status = state
|
||||
|
||||
/datum/nanoui/proc/set_auto_update(state = 1)
|
||||
is_auto_updating = state
|
||||
|
||||
/datum/nanoui/proc/set_initial_data(data)
|
||||
initial_data = modify_data(data)
|
||||
|
||||
/datum/nanoui/proc/add_head_content(nhead_content)
|
||||
head_content = nhead_content
|
||||
|
||||
/datum/nanoui/proc/set_window_options(nwindow_options)
|
||||
window_options = nwindow_options
|
||||
|
||||
/datum/nanoui/proc/set_title_image(ntitle_image)
|
||||
//title_image = ntitle_image
|
||||
|
||||
/datum/nanoui/proc/add_stylesheet(name, file)
|
||||
stylesheets[name] = file
|
||||
|
||||
/datum/nanoui/proc/add_script(name, file)
|
||||
scripts[name] = file
|
||||
|
||||
/datum/nanoui/proc/add_template(name, file)
|
||||
templates[name] = file
|
||||
|
||||
/datum/nanoui/proc/set_content(ncontent)
|
||||
content = ncontent
|
||||
|
||||
/datum/nanoui/proc/add_content(ncontent)
|
||||
content += ncontent
|
||||
|
||||
/datum/nanoui/proc/use_on_close_logic(nsetting)
|
||||
on_close_logic = nsetting
|
||||
|
||||
/datum/nanoui/proc/get_header()
|
||||
var/key
|
||||
var/filename
|
||||
|
||||
for (key in stylesheets)
|
||||
filename = "[ckey(key)].css"
|
||||
user << browse_rsc(stylesheets[key], filename)
|
||||
head_content += "<link rel='stylesheet' type='text/css' href='[filename]'>"
|
||||
|
||||
user << browse_rsc('nano/images/uiBackground.png', "uiBackground.png")
|
||||
user << browse_rsc('nano/images/uiIcons16.png', "uiIcons16.png")
|
||||
user << browse_rsc('nano/images/uiIcons24.png', "uiIcons24.png")
|
||||
user << browse_rsc('nano/images/uiLinkPendingIcon.gif', "uiLinkPendingIcon.gif")
|
||||
user << browse_rsc('nano/images/uiNoticeBackground.jpg', "uiNoticeBackground.jpg")
|
||||
user << browse_rsc('nano/images/uiTitleFluff.png', "uiTitleFluff.png")
|
||||
|
||||
var/title_attributes = "id='uiTitle'"
|
||||
if (title_image)
|
||||
title_attributes = "id='uiTitle icon' style='background-image: url([title_image]);'"
|
||||
|
||||
var/templatel_data[0]
|
||||
for (key in templates)
|
||||
filename = "[ckey(key)].tmpl"
|
||||
user << browse_rsc(templates[key], filename)
|
||||
templatel_data[key] = filename;
|
||||
|
||||
var/template_data_json = "{}" // An empty JSON object
|
||||
if (templatel_data.len > 0)
|
||||
template_data_json = list2json(templatel_data)
|
||||
|
||||
var/initial_data_json = "{}" // An empty JSON object
|
||||
if (initial_data.len > 0)
|
||||
initial_data_json = list2json(initial_data)
|
||||
|
||||
var/url_parameters_json = list2json(list("src" = "\ref[src_object]"))
|
||||
|
||||
return {"<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<head>
|
||||
[head_content]
|
||||
</head>
|
||||
<body scroll=auto data-url-parameters='[url_parameters_json]' data-template-data='[template_data_json]' data-initial-data='[initial_data_json]'>
|
||||
<script type='text/javascript'>
|
||||
function receiveUpdateData(jsonString)
|
||||
{
|
||||
// We need both jQuery and NanoUpdate to be able to recieve data
|
||||
if (typeof NanoUpdate != 'undefined' && typeof jQuery != 'undefined')
|
||||
{
|
||||
NanoUpdate.receiveUpdateData(jsonString);
|
||||
}
|
||||
else
|
||||
{
|
||||
alert('receiveUpdateData error: something is not defined!');
|
||||
if (typeof NanoUpdate == 'undefined')
|
||||
{
|
||||
alert('NanoUpdate not defined!');
|
||||
}
|
||||
if (typeof jQuery == 'undefined')
|
||||
{
|
||||
alert('jQuery not defined!');
|
||||
}
|
||||
}
|
||||
// At the moment any data received before those libraries are loaded will be lost
|
||||
}
|
||||
</script>
|
||||
<div id='uiWrapper'>
|
||||
[title ? "<div id='uiTitleWrapper'><div id='uiStatusIcon' class='icon24 uiStatusGood'></div><div [title_attributes]>[title]</div><div id='uiTitleFluff'></div></div>" : ""]
|
||||
<div id='uiContent'>
|
||||
"}
|
||||
|
||||
/datum/nanoui/proc/get_footer()
|
||||
var/key
|
||||
var/filename
|
||||
var/scriptsContent = ""
|
||||
|
||||
for (key in scripts)
|
||||
filename = "[ckey(key)].js"
|
||||
user << browse_rsc(scripts[key], filename)
|
||||
scriptsContent += "<script type='text/javascript' src='[filename]'></script>"
|
||||
|
||||
return {"
|
||||
[scriptsContent]
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>"}
|
||||
|
||||
/datum/nanoui/proc/get_content()
|
||||
return {"
|
||||
[get_header()]
|
||||
[content]
|
||||
[get_footer()]
|
||||
"}
|
||||
|
||||
/datum/nanoui/proc/open()
|
||||
var/window_size = ""
|
||||
if (width && height)
|
||||
window_size = "size=[width]x[height];"
|
||||
user << browse(get_content(), "window=[window_id];[window_size][window_options]")
|
||||
on_close_winset()
|
||||
//onclose(user, window_id)
|
||||
nanomanager.ui_opened(src)
|
||||
|
||||
/datum/nanoui/proc/close()
|
||||
is_auto_updating = 0
|
||||
nanomanager.ui_closed(src)
|
||||
user << browse(null, "window=[window_id]")
|
||||
|
||||
/datum/nanoui/proc/on_close_winset()
|
||||
if(!user.client)
|
||||
world << "ERROR: No user.client!?"
|
||||
return
|
||||
var/params = "\ref[src]"
|
||||
|
||||
winset(user, window_id, "on-close=\"nanoclose [params]\"")
|
||||
|
||||
/datum/nanoui/proc/process(update = 0)
|
||||
var/dist = get_dist(src_object, user)
|
||||
if (dist <= 1)
|
||||
set_status(2) // interactive
|
||||
else if (dist <= 2)
|
||||
set_status(1) // update only
|
||||
else if (dist <= 3)
|
||||
set_status(0) // no updates, completely disabled
|
||||
return // don't auto update
|
||||
else
|
||||
close()
|
||||
return
|
||||
|
||||
if (update || is_auto_updating)
|
||||
src_object.ui_interact(user, ui_key)
|
||||
|
||||
/datum/nanoui/proc/modify_data(data)
|
||||
data["ui"] = list(
|
||||
"status" = status,
|
||||
"user" = list("name" = user.name)
|
||||
)
|
||||
//user << list2json(data)
|
||||
return data
|
||||
|
||||
/datum/nanoui/proc/push_data(data, force_push = 0)
|
||||
if (!status && !force_push)
|
||||
user << "Cannot update UI, user out of range (status [status])"
|
||||
return
|
||||
|
||||
data = modify_data(data)
|
||||
|
||||
user << output(list2params(list(list2json(data))),"[window_id].browser:receiveUpdateData")
|
||||
on_close_winset()
|
||||
|
||||
/client/verb/nanoclose(var/uiref as text)
|
||||
set hidden = 1 // hide this verb from the user's panel
|
||||
set name = "nanoclose" // no autocomplete on cmd line
|
||||
|
||||
//world << "world [src] looking for [uiref]"
|
||||
|
||||
var/datum/nanoui/ui = locate(uiref)
|
||||
|
||||
if (ui)
|
||||
//world << "[src] UI found [ui.window_id]"
|
||||
ui.close()
|
||||
|
||||
if (ui.on_close_logic)
|
||||
if(ui.ref)
|
||||
var/href = "close=1"
|
||||
//world << "[src] Topic [href] [ui.ref]"
|
||||
src.Topic(href, params2list(href), ui.ref) // this will direct to the atom's
|
||||
// Topic() proc via client.Topic()
|
||||
else
|
||||
// no atomref specified (or not found)
|
||||
// so just reset the user mob's machine var
|
||||
if(src && src.mob)
|
||||
//world << "[src] was [src.mob.machine], setting to null"
|
||||
src.mob.unset_machine()
|
||||
else
|
||||
world << "[src] UI not found"
|
||||
return
|
||||
Binary file not shown.
@@ -0,0 +1,377 @@
|
||||
body
|
||||
{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
color: #ffffff;
|
||||
line-height: 170%;
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
background: #272727 url(uiBackground.png) 50% 0 repeat-x;
|
||||
}
|
||||
|
||||
hr
|
||||
{
|
||||
background-color: #40628a;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
a, a:link, a:visited, a:active, .link, .linkOn, .linkOff, .selected, .disabled
|
||||
{
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
background: #40628a;
|
||||
border: 1px solid #161616;
|
||||
padding: 0px 4px 4px 0px;
|
||||
margin: 0 2px 2px 0;
|
||||
cursor:default;
|
||||
}
|
||||
|
||||
.link
|
||||
{
|
||||
float: left;
|
||||
min-width: 40px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
a:hover, .linkActive:hover
|
||||
{
|
||||
background: #507aac;
|
||||
}
|
||||
|
||||
.linkPending, .linkPending:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #507aac;
|
||||
}
|
||||
|
||||
a.white, a.white:link, a.white:visited, a.white:active
|
||||
{
|
||||
color: #40628a;
|
||||
text-decoration: none;
|
||||
background: #ffffff;
|
||||
border: 1px solid #161616;
|
||||
padding: 1px 4px 1px 4px;
|
||||
margin: 0 2px 0 0;
|
||||
cursor:default;
|
||||
}
|
||||
|
||||
a.white:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #40628a;
|
||||
}
|
||||
|
||||
.linkOn, a.linkOn:link, a.linkOn:visited, a.linkOn:active, a.linkOn:hover, .selected, a.selected:link, a.selected:visited, a.selected:active, a.selected:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #2f943c;
|
||||
}
|
||||
|
||||
.linkOff, a.linkOff:link, a.linkOff:visited, a.linkOff:active, a.linkOff:hover, .disabled, a.disabled:link, a.disabled:visited, a.disabled:active, a.disabled:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #999999;
|
||||
border-color: #666666;
|
||||
}
|
||||
|
||||
a.icon, .linkOn.icon, .linkOff.icon, .selected.icon, .disabled.icon
|
||||
{
|
||||
position: relative;
|
||||
padding: 1px 4px 2px 20px;
|
||||
}
|
||||
|
||||
a.icon img, .linkOn.icon img, .linkOff.icon img, .selected.icon img, .disabled.icon img
|
||||
{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
ul
|
||||
{
|
||||
padding: 4px 0 0 10px;
|
||||
margin: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
li
|
||||
{
|
||||
padding: 0 0 2px 0;
|
||||
}
|
||||
|
||||
img, a img
|
||||
{
|
||||
border-style:none;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6
|
||||
{
|
||||
margin: 0;
|
||||
padding: 16px 0 8px 0;
|
||||
color: #517087;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
h1
|
||||
{
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
h2
|
||||
{
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h3
|
||||
{
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
h4
|
||||
{
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#uiWrapper
|
||||
{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#uiTitleWrapper
|
||||
{
|
||||
position: relative;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
#uiTitle
|
||||
{
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
left: 44px;
|
||||
width: 66%;
|
||||
overflow: hidden;
|
||||
color: #E9C183;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#uiTitle.icon
|
||||
{
|
||||
padding: 6px 8px 6px 42px;
|
||||
background-position: 2px 50%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#uiTitleFluff
|
||||
{
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 12px;
|
||||
width: 42px;
|
||||
height: 24px;
|
||||
background: url(uiTitleFluff.png) 50% 50% no-repeat;
|
||||
}
|
||||
|
||||
#uiStatusIcon
|
||||
{
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 12px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#uiContent
|
||||
{
|
||||
clear: both;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.good
|
||||
{
|
||||
color: #4f7529;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.average
|
||||
{
|
||||
color: #cd6500;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bad
|
||||
{
|
||||
color: #ee0000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight
|
||||
{
|
||||
color: #8BA5C4;
|
||||
}
|
||||
|
||||
.dark
|
||||
{
|
||||
color: #272727;
|
||||
}
|
||||
|
||||
.notice
|
||||
{
|
||||
position: relative;
|
||||
background: url(uiNoticeBackground.jpg) 50% 50% no-repeat;
|
||||
color: #15345A;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
padding: 3px 8px 2px 8px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.notice.icon
|
||||
{
|
||||
padding: 2px 4px 0 20px;
|
||||
}
|
||||
|
||||
.notice img
|
||||
{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
div.notice
|
||||
{
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.item
|
||||
{
|
||||
width: 100%;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.itemLabel
|
||||
{
|
||||
float: left;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.itemContent
|
||||
{
|
||||
float: left;
|
||||
width: 69%;
|
||||
}
|
||||
.itemContentSmall
|
||||
{
|
||||
float: left;
|
||||
width: 33%;
|
||||
}
|
||||
|
||||
.statusDisplay
|
||||
{
|
||||
background: #000000;
|
||||
color: #ffffff;
|
||||
border: 1px solid #40628a;
|
||||
padding: 4px;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.statusLabel
|
||||
{
|
||||
width: 138px;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
color: #98B0C3;
|
||||
}
|
||||
|
||||
.statusValue
|
||||
{
|
||||
float: left;
|
||||
}
|
||||
|
||||
.block
|
||||
{
|
||||
padding: 8px;
|
||||
margin: 10px 4px 4px 4px;
|
||||
border: 1px solid #40628a;
|
||||
background-color: #202020;
|
||||
}
|
||||
|
||||
.block h3
|
||||
{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.progressBar
|
||||
{
|
||||
width: 240px;
|
||||
height: 14px;
|
||||
border: 1px solid #666666;
|
||||
float: left;
|
||||
margin: 0 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progressFill
|
||||
{
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
background: #40628a;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progressFill.good
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #4f7529;
|
||||
}
|
||||
|
||||
.progressFill.average
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #cd6500;
|
||||
}
|
||||
|
||||
.progressFill.bad
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #ee0000;
|
||||
}
|
||||
|
||||
.progressFill.highlight
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #8BA5C4;
|
||||
}
|
||||
|
||||
.clearBoth
|
||||
{
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.clearLeft
|
||||
{
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.clearRight
|
||||
{
|
||||
clear: right;
|
||||
}
|
||||
|
||||
.line
|
||||
{
|
||||
width: 100%;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.inactive, , a.inactive:link, a.inactive:visited, a.inactive:active, a.inactive:hover
|
||||
{
|
||||
color: #ffffff;
|
||||
background: #999999;
|
||||
border-color: #666666;
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
.icon24
|
||||
{
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.icon24.uiStatusGood
|
||||
{
|
||||
background: url(uiIcons24.png) 0 0 no-repeat;
|
||||
}
|
||||
|
||||
.icon24.uiStatusAverage
|
||||
{
|
||||
background: url(uiIcons24.png) 0 -24px no-repeat;
|
||||
}
|
||||
|
||||
.icon24.uiStatusBad
|
||||
{
|
||||
background: url(uiIcons24.png) 0 -48px no-repeat;
|
||||
}
|
||||
|
||||
/* states and images */
|
||||
.uiIcon16 {
|
||||
float: left;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 2px 2px 0 2px;
|
||||
background-image: url(uiIcons16.png);
|
||||
}
|
||||
|
||||
.uiLinkPendingIcon {
|
||||
display: none;
|
||||
float: left;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 2px 2px 0 2px;
|
||||
background-image: url(uiLinkPendingIcon.gif);
|
||||
}
|
||||
|
||||
.linkPending .uiIcon16 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.linkPending .uiLinkPendingIcon {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* positioning */
|
||||
.uiIcon16.blank { background-position: 16px 16px; }
|
||||
.uiIcon16.carat-1-n { background-position: 0 0; }
|
||||
.uiIcon16.carat-1-ne { background-position: -16px 0; }
|
||||
.uiIcon16.carat-1-e { background-position: -32px 0; }
|
||||
.uiIcon16.carat-1-se { background-position: -48px 0; }
|
||||
.uiIcon16.carat-1-s { background-position: -64px 0; }
|
||||
.uiIcon16.carat-1-sw { background-position: -80px 0; }
|
||||
.uiIcon16.carat-1-w { background-position: -96px 0; }
|
||||
.uiIcon16.carat-1-nw { background-position: -112px 0; }
|
||||
.uiIcon16.carat-2-n-s { background-position: -128px 0; }
|
||||
.uiIcon16.carat-2-e-w { background-position: -144px 0; }
|
||||
.uiIcon16.triangle-1-n { background-position: 0 -16px; }
|
||||
.uiIcon16.triangle-1-ne { background-position: -16px -16px; }
|
||||
.uiIcon16.triangle-1-e { background-position: -32px -16px; }
|
||||
.uiIcon16.triangle-1-se { background-position: -48px -16px; }
|
||||
.uiIcon16.triangle-1-s { background-position: -64px -16px; }
|
||||
.uiIcon16.triangle-1-sw { background-position: -80px -16px; }
|
||||
.uiIcon16.triangle-1-w { background-position: -96px -16px; }
|
||||
.uiIcon16.triangle-1-nw { background-position: -112px -16px; }
|
||||
.uiIcon16.triangle-2-n-s { background-position: -128px -16px; }
|
||||
.uiIcon16.triangle-2-e-w { background-position: -144px -16px; }
|
||||
.uiIcon16.arrow-1-n { background-position: 0 -32px; }
|
||||
.uiIcon16.arrow-1-ne { background-position: -16px -32px; }
|
||||
.uiIcon16.arrow-1-e { background-position: -32px -32px; }
|
||||
.uiIcon16.arrow-1-se { background-position: -48px -32px; }
|
||||
.uiIcon16.arrow-1-s { background-position: -64px -32px; }
|
||||
.uiIcon16.arrow-1-sw { background-position: -80px -32px; }
|
||||
.uiIcon16.arrow-1-w { background-position: -96px -32px; }
|
||||
.uiIcon16.arrow-1-nw { background-position: -112px -32px; }
|
||||
.uiIcon16.arrow-2-n-s { background-position: -128px -32px; }
|
||||
.uiIcon16.arrow-2-ne-sw { background-position: -144px -32px; }
|
||||
.uiIcon16.arrow-2-e-w { background-position: -160px -32px; }
|
||||
.uiIcon16.arrow-2-se-nw { background-position: -176px -32px; }
|
||||
.uiIcon16.arrowstop-1-n { background-position: -192px -32px; }
|
||||
.uiIcon16.arrowstop-1-e { background-position: -208px -32px; }
|
||||
.uiIcon16.arrowstop-1-s { background-position: -224px -32px; }
|
||||
.uiIcon16.arrowstop-1-w { background-position: -240px -32px; }
|
||||
.uiIcon16.arrowthick-1-n { background-position: 0 -48px; }
|
||||
.uiIcon16.arrowthick-1-ne { background-position: -16px -48px; }
|
||||
.uiIcon16.arrowthick-1-e { background-position: -32px -48px; }
|
||||
.uiIcon16.arrowthick-1-se { background-position: -48px -48px; }
|
||||
.uiIcon16.arrowthick-1-s { background-position: -64px -48px; }
|
||||
.uiIcon16.arrowthick-1-sw { background-position: -80px -48px; }
|
||||
.uiIcon16.arrowthick-1-w { background-position: -96px -48px; }
|
||||
.uiIcon16.arrowthick-1-nw { background-position: -112px -48px; }
|
||||
.uiIcon16.arrowthick-2-n-s { background-position: -128px -48px; }
|
||||
.uiIcon16.arrowthick-2-ne-sw { background-position: -144px -48px; }
|
||||
.uiIcon16.arrowthick-2-e-w { background-position: -160px -48px; }
|
||||
.uiIcon16.arrowthick-2-se-nw { background-position: -176px -48px; }
|
||||
.uiIcon16.arrowthickstop-1-n { background-position: -192px -48px; }
|
||||
.uiIcon16.arrowthickstop-1-e { background-position: -208px -48px; }
|
||||
.uiIcon16.arrowthickstop-1-s { background-position: -224px -48px; }
|
||||
.uiIcon16.arrowthickstop-1-w { background-position: -240px -48px; }
|
||||
.uiIcon16.arrowreturnthick-1-w { background-position: 0 -64px; }
|
||||
.uiIcon16.arrowreturnthick-1-n { background-position: -16px -64px; }
|
||||
.uiIcon16.arrowreturnthick-1-e { background-position: -32px -64px; }
|
||||
.uiIcon16.arrowreturnthick-1-s { background-position: -48px -64px; }
|
||||
.uiIcon16.arrowreturn-1-w { background-position: -64px -64px; }
|
||||
.uiIcon16.arrowreturn-1-n { background-position: -80px -64px; }
|
||||
.uiIcon16.arrowreturn-1-e { background-position: -96px -64px; }
|
||||
.uiIcon16.arrowreturn-1-s { background-position: -112px -64px; }
|
||||
.uiIcon16.arrowrefresh-1-w { background-position: -128px -64px; }
|
||||
.uiIcon16.arrowrefresh-1-n { background-position: -144px -64px; }
|
||||
.uiIcon16.arrowrefresh-1-e { background-position: -160px -64px; }
|
||||
.uiIcon16.arrowrefresh-1-s { background-position: -176px -64px; }
|
||||
.uiIcon16.arrow-4 { background-position: 0 -80px; }
|
||||
.uiIcon16.arrow-4-diag { background-position: -16px -80px; }
|
||||
.uiIcon16.extlink { background-position: -32px -80px; }
|
||||
.uiIcon16.newwin { background-position: -48px -80px; }
|
||||
.uiIcon16.refresh { background-position: -64px -80px; }
|
||||
.uiIcon16.shuffle { background-position: -80px -80px; }
|
||||
.uiIcon16.transfer-e-w { background-position: -96px -80px; }
|
||||
.uiIcon16.transferthick-e-w { background-position: -112px -80px; }
|
||||
.uiIcon16.folder-collapsed { background-position: 0 -96px; }
|
||||
.uiIcon16.folder-open { background-position: -16px -96px; }
|
||||
.uiIcon16.document { background-position: -32px -96px; }
|
||||
.uiIcon16.document-b { background-position: -48px -96px; }
|
||||
.uiIcon16.note { background-position: -64px -96px; }
|
||||
.uiIcon16.mail-closed { background-position: -80px -96px; }
|
||||
.uiIcon16.mail-open { background-position: -96px -96px; }
|
||||
.uiIcon16.suitcase { background-position: -112px -96px; }
|
||||
.uiIcon16.comment { background-position: -128px -96px; }
|
||||
.uiIcon16.person { background-position: -144px -96px; }
|
||||
.uiIcon16.print { background-position: -160px -96px; }
|
||||
.uiIcon16.trash { background-position: -176px -96px; }
|
||||
.uiIcon16.locked { background-position: -192px -96px; }
|
||||
.uiIcon16.unlocked { background-position: -208px -96px; }
|
||||
.uiIcon16.bookmark { background-position: -224px -96px; }
|
||||
.uiIcon16.tag { background-position: -240px -96px; }
|
||||
.uiIcon16.home { background-position: 0 -112px; }
|
||||
.uiIcon16.flag { background-position: -16px -112px; }
|
||||
.uiIcon16.calendar { background-position: -32px -112px; }
|
||||
.uiIcon16.cart { background-position: -48px -112px; }
|
||||
.uiIcon16.pencil { background-position: -64px -112px; }
|
||||
.uiIcon16.clock { background-position: -80px -112px; }
|
||||
.uiIcon16.disk { background-position: -96px -112px; }
|
||||
.uiIcon16.calculator { background-position: -112px -112px; }
|
||||
.uiIcon16.zoomin { background-position: -128px -112px; }
|
||||
.uiIcon16.zoomout { background-position: -144px -112px; }
|
||||
.uiIcon16.search { background-position: -160px -112px; }
|
||||
.uiIcon16.wrench { background-position: -176px -112px; }
|
||||
.uiIcon16.gear { background-position: -192px -112px; }
|
||||
.uiIcon16.heart { background-position: -208px -112px; }
|
||||
.uiIcon16.star { background-position: -224px -112px; }
|
||||
.uiIcon16.link { background-position: -240px -112px; }
|
||||
.uiIcon16.cancel { background-position: 0 -128px; }
|
||||
.uiIcon16.plus { background-position: -16px -128px; }
|
||||
.uiIcon16.plusthick { background-position: -32px -128px; }
|
||||
.uiIcon16.minus { background-position: -48px -128px; }
|
||||
.uiIcon16.minusthick { background-position: -64px -128px; }
|
||||
.uiIcon16.close { background-position: -80px -128px; }
|
||||
.uiIcon16.closethick { background-position: -96px -128px; }
|
||||
.uiIcon16.key { background-position: -112px -128px; }
|
||||
.uiIcon16.lightbulb { background-position: -128px -128px; }
|
||||
.uiIcon16.scissors { background-position: -144px -128px; }
|
||||
.uiIcon16.clipboard { background-position: -160px -128px; }
|
||||
.uiIcon16.copy { background-position: -176px -128px; }
|
||||
.uiIcon16.contact { background-position: -192px -128px; }
|
||||
.uiIcon16.image { background-position: -208px -128px; }
|
||||
.uiIcon16.video { background-position: -224px -128px; }
|
||||
.uiIcon16.script { background-position: -240px -128px; }
|
||||
.uiIcon16.alert { background-position: 0 -144px; }
|
||||
.uiIcon16.info { background-position: -16px -144px; }
|
||||
.uiIcon16.notice { background-position: -32px -144px; }
|
||||
.uiIcon16.help { background-position: -48px -144px; }
|
||||
.uiIcon16.check { background-position: -64px -144px; }
|
||||
.uiIcon16.bullet { background-position: -80px -144px; }
|
||||
.uiIcon16.radio-on { background-position: -96px -144px; }
|
||||
.uiIcon16.radio-off { background-position: -112px -144px; }
|
||||
.uiIcon16.pin-w { background-position: -128px -144px; }
|
||||
.uiIcon16.pin-s { background-position: -144px -144px; }
|
||||
.uiIcon16.play { background-position: 0 -160px; }
|
||||
.uiIcon16.pause { background-position: -16px -160px; }
|
||||
.uiIcon16.seek-next { background-position: -32px -160px; }
|
||||
.uiIcon16.seek-prev { background-position: -48px -160px; }
|
||||
.uiIcon16.seek-end { background-position: -64px -160px; }
|
||||
.uiIcon16.seek-start { background-position: -80px -160px; }
|
||||
/* uiIcon-seek-first is deprecated, use uiIcon-seek-start instead */
|
||||
.uiIcon16.seek-first { background-position: -80px -160px; }
|
||||
.uiIcon16.stop { background-position: -96px -160px; }
|
||||
.uiIcon16.eject { background-position: -112px -160px; }
|
||||
.uiIcon16.volume-off { background-position: -128px -160px; }
|
||||
.uiIcon16.volume-on { background-position: -144px -160px; }
|
||||
.uiIcon16.power { background-position: 0 -176px; }
|
||||
.uiIcon16.signal-diag { background-position: -16px -176px; }
|
||||
.uiIcon16.signal { background-position: -32px -176px; }
|
||||
.uiIcon16.battery-0 { background-position: -48px -176px; }
|
||||
.uiIcon16.battery-1 { background-position: -64px -176px; }
|
||||
.uiIcon16.battery-2 { background-position: -80px -176px; }
|
||||
.uiIcon16.battery-3 { background-position: -96px -176px; }
|
||||
.uiIcon16.circle-plus { background-position: 0 -192px; }
|
||||
.uiIcon16.circle-minus { background-position: -16px -192px; }
|
||||
.uiIcon16.circle-close { background-position: -32px -192px; }
|
||||
.uiIcon16.circle-triangle-e { background-position: -48px -192px; }
|
||||
.uiIcon16.circle-triangle-s { background-position: -64px -192px; }
|
||||
.uiIcon16.circle-triangle-w { background-position: -80px -192px; }
|
||||
.uiIcon16.circle-triangle-n { background-position: -96px -192px; }
|
||||
.uiIcon16.circle-arrow-e { background-position: -112px -192px; }
|
||||
.uiIcon16.circle-arrow-s { background-position: -128px -192px; }
|
||||
.uiIcon16.circle-arrow-w { background-position: -144px -192px; }
|
||||
.uiIcon16.circle-arrow-n { background-position: -160px -192px; }
|
||||
.uiIcon16.circle-zoomin { background-position: -176px -192px; }
|
||||
.uiIcon16.circle-zoomout { background-position: -192px -192px; }
|
||||
.uiIcon16.circle-check { background-position: -208px -192px; }
|
||||
.uiIcon16.circlesmall-plus { background-position: 0 -208px; }
|
||||
.uiIcon16.circlesmall-minus { background-position: -16px -208px; }
|
||||
.uiIcon16.circlesmall-close { background-position: -32px -208px; }
|
||||
.uiIcon16.squaresmall-plus { background-position: -48px -208px; }
|
||||
.uiIcon16.squaresmall-minus { background-position: -64px -208px; }
|
||||
.uiIcon16.squaresmall-close { background-position: -80px -208px; }
|
||||
.uiIcon16.grip-dotted-vertical { background-position: 0 -224px; }
|
||||
.uiIcon16.grip-dotted-horizontal { background-position: -16px -224px; }
|
||||
.uiIcon16.grip-solid-vertical { background-position: -32px -224px; }
|
||||
.uiIcon16.grip-solid-horizontal { background-position: -48px -224px; }
|
||||
.uiIcon16.gripsmall-diagonal-se { background-position: -64px -224px; }
|
||||
.uiIcon16.grip-diagonal-se { background-position: -80px -224px; }
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 673 B |
Binary file not shown.
|
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 670 B |
Vendored
+31
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
jQuery.fn.extend({everyTime:function(c,a,d,b){return this.each(function(){jQuery.timer.add(this,c,a,d,b)})},oneTime:function(c,a,d){return this.each(function(){jQuery.timer.add(this,c,a,d,1)})},stopTime:function(c,a){return this.each(function(){jQuery.timer.remove(this,c,a)})}});
|
||||
jQuery.extend({timer:{global:[],guid:1,dataKey:"jQuery.timer",regex:/^([0-9]+(?:\.[0-9]*)?)\s*(.*s)?$/,powers:{ms:1,cs:10,ds:100,s:1E3,das:1E4,hs:1E5,ks:1E6},timeParse:function(c){if(c==undefined||c==null)return null;var a=this.regex.exec(jQuery.trim(c.toString()));return a[2]?parseFloat(a[1])*(this.powers[a[2]]||1):c},add:function(c,a,d,b,e){var g=0;if(jQuery.isFunction(d)){e||(e=b);b=d;d=a}a=jQuery.timer.timeParse(a);if(!(typeof a!="number"||isNaN(a)||a<0)){if(typeof e!="number"||isNaN(e)||e<0)e=
|
||||
0;e=e||0;var f=jQuery.data(c,this.dataKey)||jQuery.data(c,this.dataKey,{});f[d]||(f[d]={});b.timerID=b.timerID||this.guid++;var h=function(){if(++g>e&&e!==0||b.call(c,g)===false)jQuery.timer.remove(c,d,b)};h.timerID=b.timerID;f[d][b.timerID]||(f[d][b.timerID]=window.setInterval(h,a));this.global.push(c)}},remove:function(c,a,d){var b=jQuery.data(c,this.dataKey),e;if(b){if(a){if(b[a]){if(d){if(d.timerID){window.clearInterval(b[a][d.timerID]);delete b[a][d.timerID]}}else for(d in b[a]){window.clearInterval(b[a][d]);
|
||||
delete b[a][d]}for(e in b[a])break;if(!e){e=null;delete b[a]}}}else for(a in b)this.remove(c,a,d);for(e in b)break;e||jQuery.removeData(c,this.dataKey)}}}});jQuery(window).bind("unload",function(){jQuery.each(jQuery.timer.global,function(c,a){jQuery.timer.remove(a)})});
|
||||
@@ -0,0 +1,116 @@
|
||||
NanoBaseHelpers = function ()
|
||||
{
|
||||
var _urlParameters = {}; // This is populated with the base url parameters, which is probaby just the "src" parameter
|
||||
|
||||
var init = function ()
|
||||
{
|
||||
var body = $('body'); // We store data in the body tag, it's as good a place as any
|
||||
|
||||
_urlParameters = body.data('urlParameters');
|
||||
|
||||
initHelpers();
|
||||
};
|
||||
|
||||
var initHelpers = function ()
|
||||
{
|
||||
$.views.helpers({
|
||||
// Generate a Byond link
|
||||
link: function( text, icon, parameters, status ) {
|
||||
|
||||
var iconHtml = '';
|
||||
if (typeof icon != 'undefined' && icon != null)
|
||||
{
|
||||
iconHtml = '<div class="uiLinkPendingIcon"></div><div class="uiIcon16 ' + icon + '"></div>';
|
||||
}
|
||||
|
||||
if (typeof status != 'undefined' && status != null)
|
||||
{
|
||||
return '<div class="link ' + status + '">' + iconHtml + text + '</div>';
|
||||
}
|
||||
|
||||
return '<div class="link linkActive" data-href="' + generateHref(parameters) + '">' + iconHtml + text + '</div>';
|
||||
},
|
||||
// Round a number to the nearest integer
|
||||
round: function(number) {
|
||||
return Math.round(number);
|
||||
},
|
||||
// Round a number down to integer
|
||||
floor: function(number) {
|
||||
return Math.floor(number);
|
||||
},
|
||||
// Round a number up to integer
|
||||
ceil: function(number) {
|
||||
return Math.ceil(number);
|
||||
},
|
||||
// Format a string (~string("Hello {0}, how are {1}?", 'Martin', 'you') becomes "Hello Martin, how are you?")
|
||||
string: function() {
|
||||
if (arguments.length == 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
else if (arguments.length == 1)
|
||||
{
|
||||
return arguments[0];
|
||||
}
|
||||
else if (arguments.length > 1)
|
||||
{
|
||||
stringArgs = [];
|
||||
for (var i = 1; i < arguments.length; i++)
|
||||
{
|
||||
stringArgs.push(arguments[i]);
|
||||
}
|
||||
return arguments[0].format(stringArgs);
|
||||
}
|
||||
return
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var generateHref = function (parameters)
|
||||
{
|
||||
var queryString = '?';
|
||||
|
||||
for (var key in _urlParameters)
|
||||
{
|
||||
if (_urlParameters.hasOwnProperty(key))
|
||||
{
|
||||
if (queryString !== '?')
|
||||
{
|
||||
queryString += ';';
|
||||
}
|
||||
queryString += key + '=' + _urlParameters[key];
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in parameters)
|
||||
{
|
||||
if (parameters.hasOwnProperty(key))
|
||||
{
|
||||
if (queryString !== '?')
|
||||
{
|
||||
queryString += ';';
|
||||
}
|
||||
queryString += key + '=' + parameters[key];
|
||||
}
|
||||
}
|
||||
return queryString;
|
||||
}
|
||||
|
||||
return {
|
||||
init: function ()
|
||||
{
|
||||
init();
|
||||
}
|
||||
};
|
||||
} ();
|
||||
|
||||
$(document).ready(function()
|
||||
{
|
||||
NanoBaseHelpers.init();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
var NanoConfig = function ()
|
||||
{
|
||||
return {
|
||||
init: function ()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
} ();
|
||||
|
||||
NanoConfig.init();
|
||||
|
||||
if (!Array.prototype.indexOf)
|
||||
{
|
||||
Array.prototype.indexOf = function(elt /*, from*/)
|
||||
{
|
||||
var len = this.length;
|
||||
|
||||
var from = Number(arguments[1]) || 0;
|
||||
from = (from < 0)
|
||||
? Math.ceil(from)
|
||||
: Math.floor(from);
|
||||
if (from < 0)
|
||||
from += len;
|
||||
|
||||
for (; from < len; from++)
|
||||
{
|
||||
if (from in this &&
|
||||
this[from] === elt)
|
||||
return from;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.format)
|
||||
{
|
||||
String.prototype.format = function (args) {
|
||||
var str = this;
|
||||
return str.replace(String.prototype.format.regex, function(item) {
|
||||
var intVal = parseInt(item.substring(1, item.length - 1));
|
||||
var replace;
|
||||
if (intVal >= 0) {
|
||||
replace = args[intVal];
|
||||
} else if (intVal === -1) {
|
||||
replace = "{";
|
||||
} else if (intVal === -2) {
|
||||
replace = "}";
|
||||
} else {
|
||||
replace = "";
|
||||
}
|
||||
return replace;
|
||||
});
|
||||
};
|
||||
String.prototype.format.regex = new RegExp("{-?[0-9]+}", "g");
|
||||
}
|
||||
|
||||
Object.size = function(obj) {
|
||||
var size = 0, key;
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) size++;
|
||||
}
|
||||
return size;
|
||||
};
|
||||
|
||||
if(!window.console) {
|
||||
window.console = {
|
||||
log : function(str) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
String.prototype.toTitleCase = function () {
|
||||
var smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|vs?\.?|via)$/i;
|
||||
|
||||
return this.replace(/([^\W_]+[^\s-]*) */g, function (match, p1, index, title) {
|
||||
if (index > 0 && index + p1.length !== title.length &&
|
||||
p1.search(smallWords) > -1 && title.charAt(index - 2) !== ":" &&
|
||||
title.charAt(index - 1).search(/[^\s-]/) < 0) {
|
||||
return match.toLowerCase();
|
||||
}
|
||||
|
||||
if (p1.substr(1).search(/[A-Z]|\../) > -1) {
|
||||
return match;
|
||||
}
|
||||
|
||||
return match.charAt(0).toUpperCase() + match.substr(1);
|
||||
});
|
||||
};
|
||||
|
||||
$.ajaxSetup({
|
||||
cache: false
|
||||
});
|
||||
@@ -0,0 +1,196 @@
|
||||
NanoUpdate = function ()
|
||||
{
|
||||
var _isInitialised = false;
|
||||
|
||||
var _templates = null;
|
||||
var _data = null;
|
||||
var _earlyUpdateData = null; // This is for newer data which has arrived before the template has been rendered
|
||||
|
||||
var _beforeUpdateCallbacks = [];
|
||||
var _afterUpdateCallbacks = [];
|
||||
|
||||
var _canClick = true;
|
||||
|
||||
var init = function ()
|
||||
{
|
||||
var body = $('body'); // We store data in the body tag, it's as good a place as any
|
||||
|
||||
_data = body.data('initialData');
|
||||
|
||||
var templateData = body.data('templateData');
|
||||
|
||||
var templateCount = 0;
|
||||
for (var key in templateData)
|
||||
{
|
||||
if (templateData.hasOwnProperty(key))
|
||||
{
|
||||
templateCount++;
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in templateData)
|
||||
{
|
||||
if (templateData.hasOwnProperty(key))
|
||||
{
|
||||
$.when($.get(templateData[key]))
|
||||
.done(function(templateData) {
|
||||
if (_templates == null)
|
||||
{
|
||||
_templates = {};
|
||||
}
|
||||
|
||||
templateData += '<div class="clearBoth"></div>'
|
||||
|
||||
try
|
||||
{
|
||||
_templates[key] = $.templates(templateData);
|
||||
_templates[key].link( "#mainTemplate", _data ); // initial data gets applied first, before any updates
|
||||
|
||||
templateCount--;
|
||||
|
||||
if (templateCount <= 0)
|
||||
{
|
||||
_isInitialised = true;
|
||||
}
|
||||
|
||||
if (_earlyUpdateData !== null) // Newer data has already arrived, so update
|
||||
{
|
||||
observedDataUpdateRecursive(_earlyUpdateData, _data);
|
||||
}
|
||||
|
||||
//alert($("#mainTemplate").html());
|
||||
}
|
||||
catch(error)
|
||||
{
|
||||
alert('An error occurred while loading the UI: ' + error.message);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
NanoUpdate.addAfterUpdateCallback(function (updateData) {
|
||||
var uiStatusClass;
|
||||
if (updateData['ui']['status'] == 2)
|
||||
{
|
||||
uiStatusClass = 'icon24 uiStatusGood';
|
||||
$('.linkActive').removeClass('inactive');
|
||||
}
|
||||
else if (updateData['ui']['status'] == 1)
|
||||
{
|
||||
uiStatusClass = 'icon24 uiStatusAverage';
|
||||
$('.linkActive').addClass('inactive');
|
||||
}
|
||||
else
|
||||
{
|
||||
uiStatusClass = 'icon24 uiStatusBad'
|
||||
$('.linkActive').addClass('inactive');
|
||||
}
|
||||
$('#uiStatusIcon').attr('class', uiStatusClass);
|
||||
|
||||
$('.linkActive').stopTime('linkPending');
|
||||
$('.linkActive').removeClass('linkPending');
|
||||
|
||||
$('.linkActive').off('click');
|
||||
$('.linkActive').on('click', function (event) {
|
||||
event.preventDefault();
|
||||
var href = $(this).data('href');
|
||||
if (href != null && _canClick)
|
||||
{
|
||||
_canClick = false;
|
||||
$('body').oneTime(300, 'enableClick', function () {
|
||||
_canClick = true;
|
||||
});
|
||||
$(this).oneTime(300, 'linkPending', function () {
|
||||
$(this).addClass('linkPending');
|
||||
});
|
||||
window.location.href = href;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Receive update data from the server
|
||||
var receiveUpdateData = function (jsonString)
|
||||
{
|
||||
var updateData = jQuery.parseJSON(jsonString);
|
||||
|
||||
if (_isInitialised) // templates have been loaded and are observing the data. We need to update it recursively
|
||||
{
|
||||
executeCallbacks(_beforeUpdateCallbacks, updateData);
|
||||
|
||||
observedDataUpdateRecursive(updateData, _data);
|
||||
|
||||
executeCallbacks(_afterUpdateCallbacks, updateData);
|
||||
}
|
||||
else
|
||||
{
|
||||
_earlyUpdateData = updateData; // templates have not been loaded, therefor they are not observing the data. We set _earlyUpdateData which will be applied after the template is loaded with the initial data
|
||||
}
|
||||
}
|
||||
|
||||
// This function updates the observed data recursively
|
||||
// It has to be done recursively as each piece of data is observed individually and needs to be updated individually
|
||||
var observedDataUpdateRecursive = function (updateData, data, path)
|
||||
{
|
||||
if (path === null || typeof path === 'undefined')
|
||||
{
|
||||
path = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
path += '.';
|
||||
}
|
||||
for (var key in updateData)
|
||||
{
|
||||
if (updateData.hasOwnProperty(key))
|
||||
{
|
||||
var currentPath = path + key;
|
||||
if (updateData[key] != null && typeof updateData[key] === 'object' && !$.isArray(updateData[key]))
|
||||
{
|
||||
observedDataUpdateRecursive(updateData[key], data, currentPath)
|
||||
}
|
||||
else
|
||||
{
|
||||
$.observable(data).setProperty(currentPath, updateData[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var executeCallbacks = function (callbacks, updateData)
|
||||
{
|
||||
for (var index in callbacks)
|
||||
{
|
||||
callbacks[index].call(this, updateData);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
init: function ()
|
||||
{
|
||||
init();
|
||||
},
|
||||
isInitialised: function ()
|
||||
{
|
||||
return _isInitialised;
|
||||
},
|
||||
receiveUpdateData: function (jsonString)
|
||||
{
|
||||
receiveUpdateData(jsonString);
|
||||
},
|
||||
addBeforeUpdateCallback: function (callbackFunction)
|
||||
{
|
||||
_beforeUpdateCallbacks.push(callbackFunction);
|
||||
},
|
||||
addAfterUpdateCallback: function (callbackFunction)
|
||||
{
|
||||
_afterUpdateCallbacks.push(callbackFunction);
|
||||
}
|
||||
};
|
||||
} ();
|
||||
|
||||
$(document).ready(function()
|
||||
{
|
||||
NanoUpdate.init();
|
||||
});
|
||||
@@ -0,0 +1,141 @@
|
||||
<div class='notice'>
|
||||
{^{if locked}}
|
||||
Swipe ID card to unlock interface
|
||||
{{else}}
|
||||
Swipe ID card to lock interface
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<h3>Status</h3>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Main Breaker:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{^{if locked}}
|
||||
{^{if isOperating}}
|
||||
<span class='good'>On</span>
|
||||
{{else}}
|
||||
<span class='bad'>Off</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{^{:~link('On', {'breaker' : 1}, isOperating ? 'selected' : null)}}{^{:~link('Off', {'breaker' : 1}, isOperating ? null : 'selected')}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
External Power:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{^{if externalPower == 2}}
|
||||
<span class='good'>Good</span>
|
||||
{{else externalPower == 1}}
|
||||
<span class='average'>Low</span>
|
||||
{{else}}
|
||||
<span class='bad'>None</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Power Cell:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{^{if powerCellStatus == null}}
|
||||
<span class='bad'>Not connected.</span>
|
||||
{{else}}
|
||||
{^{:powerCellStatus}}%
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{^{if powerCellStatus != null}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Charge Mode:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{^{if locked}}
|
||||
{^{if chargeMode}}
|
||||
<span class='good'>Auto</span>
|
||||
{{else}}
|
||||
<span class='bad'>Off</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{^{:~link('Auto', {'cmode' : 1}, chargeMode ? 'selected' : null)}}{^{:~link('Off', {'cmode' : 1}, chargeMode ? null : 'selected')}}
|
||||
{{/if}}
|
||||
|
||||
{^{if chargingStatus > 1}}
|
||||
(<span class='good'>Fully Charged</span>)
|
||||
{{else chargingStatus == 1}}
|
||||
(<span class='average'>Charging</span>)
|
||||
{{else}}
|
||||
(<span class='bad'>Not Charging</span>)
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<h3>Power Channels</h3>
|
||||
|
||||
{^{for powerChannels}}
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
{^{:title}}
|
||||
</div>
|
||||
<div class="itemContentSmall" style="width: 26%;">
|
||||
{^{:powerLoad}} W
|
||||
</div>
|
||||
<div class="itemContentSmall" style="width: 40%;">
|
||||
{^{if !~root.locked}}
|
||||
{^{:~link('Auto', topicParams.auto, (status == 1 || status == 3) ? 'selected' : null)}}
|
||||
{^{:~link('On', topicParams.on, (status == 2) ? 'selected' : null)}}
|
||||
{^{:~link('Off', topicParams.off, (status == 0) ? 'selected' : null)}}
|
||||
{{/if}}
|
||||
{^{if status <= 1}}
|
||||
<span class='bad'>Off</span>
|
||||
{{else status >= 2}}
|
||||
<span class='good'>On</span>
|
||||
{{/if}}
|
||||
{^{if ~root.locked}}
|
||||
{^{if status == 1 || status == 3}}
|
||||
(Auto)
|
||||
{{else}}
|
||||
(Manual)
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/for}}
|
||||
|
||||
<div class="item"> </div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Total Load:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{{:totalLoad}} W
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Cover Lock:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{^{if locked}}
|
||||
{^{if coverLocked}}
|
||||
<span class='good'>Engaged</span>
|
||||
{{else}}
|
||||
<span class='bad'>Disengaged</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{^{:~link('Engaged', {'lock' : 1}, coverLocked ? 'selected' : null)}}{^{:~link('Disengaged', {'lock' : 1}, coverLocked ? null : 'selected')}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,58 @@
|
||||
<h3>Cryo Cell Status</h3>
|
||||
|
||||
<div class="statusDisplay">
|
||||
{^{if !hasOccupant}}
|
||||
<div class="line">Cell Unoccupied</div>
|
||||
{{else}}
|
||||
<div class="line">
|
||||
{^{:occupant.name}} =>
|
||||
{^{if occupant.stat == 0}}
|
||||
<span class="good">Conscious</span>
|
||||
{{else occupant.stat == 1}}
|
||||
<span class="average">Unconscious</span>
|
||||
{{else}}
|
||||
<span class="bad">DEAD</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="line"><div class="statusLabel">Health:</div><div class="progressBar">{^{:~string('<div class="progressFill good" style="width: {0}%;"></div>', [occupant.health])}}</div><div class="statusValue">{^{:occupant.health}}%</div></div>
|
||||
<div class="line"><div class="statusLabel">=> Brute Damage:</div><div class="progressBar">{^{:~string('<div class="progressFill bad" style="width: {0}%;"></div>', [occupant.bruteLoss])}}</div><div class="statusValue">{^{:occupant.bruteLoss}}%</div></div>
|
||||
<div class="line"><div class="statusLabel">=> Resp. Damage:</div><div class="progressBar">{^{:~string('<div class="progressFill bad" style="width: {0}%;"></div>', [occupant.oxyLoss])}}</div><div class="statusValue">{^{:occupant.oxyLoss}}%</div></div>
|
||||
<div class="line"><div class="statusLabel">=> Toxin Content:</div><div class="progressBar">{^{:~string('<div class="progressFill bad" style="width: {0}%;"></div>', [occupant.toxLoss])}}</div><div class="statusValue">{^{:occupant.toxLoss}}%</div></div>
|
||||
<div class="line"><div class="statusLabel">=> Burn Severity:</div><div class="progressBar">{^{:~string('<div class="progressFill bad" style="width: {0}%;"></div>', [occupant.fireLoss])}}</div><div class="statusValue">{^{:occupant.fireLoss}}%</div></div>
|
||||
<div class="line"><div class="statusLabel">Body Temperature:</div><div class="statusValue">{^{:occupant.bodyTemperature}} K</div></div>
|
||||
{{/if}}
|
||||
<hr>
|
||||
<div class="line"><div class="statusLabel">Cell Temperature:</div><div class="statusValue">
|
||||
{^{:~string('<span class="{0}">{1} K</span>', cellTemperatureStatus, cellTemperature)}} <!-- syntax error, bloody arrays are not allowed... -->
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<h3>Cryo Cell Operation</h3>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Cryo Cell Status:
|
||||
</div>
|
||||
<div class="itemContent">
|
||||
{^{:~link('On', 'power', {'start' : 1}, isOperating ? 'selected' : null)}}{^{:~link('Off', 'close', {'start' : 1}, isOperating ? null : 'selected')}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="item"> </div>
|
||||
<div class="item">
|
||||
<div class="itemLabel">
|
||||
Beaker:
|
||||
</div>
|
||||
<div class="itemContent" style="width: 40%;">
|
||||
{^{if isBeakerLoaded}}
|
||||
{^{for beakerContents}}
|
||||
<span class="highlight">{^{:volume}} units of {^{:name}}</span><br>
|
||||
{{else}}
|
||||
<span class="bad">Beaker is empty</span>
|
||||
{{/for}}
|
||||
{{else}}
|
||||
<span class="average"><i>No beaker loaded</i></span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="itemContent" style="width: 26%;">
|
||||
{^{:~link('Eject Beaker', 'eject', {'eject' : 1}, isBeakerLoaded ? null : 'disabled')}}
|
||||
</div>
|
||||
</div>
|
||||
Binary file not shown.
Reference in New Issue
Block a user