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:
Mark Aherne (Faerdan)
2013-08-19 22:52:23 +01:00
parent 5a8f6ae0cc
commit 5f4cc7e108
37 changed files with 16054 additions and 33 deletions
+8
View File
@@ -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"
+19 -1
View File
@@ -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
View File
@@ -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))
+3
View File
@@ -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)
+14 -1
View File
@@ -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
View File
@@ -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])
+1
View File
@@ -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")
+205
View File
@@ -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].")
+54
View File
@@ -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
+12
View File
@@ -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)
+6
View File
@@ -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()
+69
View File
@@ -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();
+271
View File
@@ -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.
+377
View File
@@ -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;
}
+227
View File
@@ -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

+31
View File
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
+4
View File
@@ -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)})});
+116
View File
@@ -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();
});
+94
View File
@@ -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
});
+196
View File
@@ -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();
});
+141
View File
@@ -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}}
&nbsp;
{^{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">&nbsp;</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>
+58
View File
@@ -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}}&nbsp;=>&nbsp;
{^{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">=&gt; 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">=&gt; 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">=&gt; 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">=&gt; 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">&nbsp;</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.