NanoUI atmosphere control and remote air alarm support.

This commit is contained in:
PsiOmega
2015-02-19 18:23:47 +01:00
parent d32cffaefe
commit 5b299e0104
12 changed files with 175 additions and 90 deletions

View File

@@ -479,22 +479,29 @@
ui_interact(user) ui_interact(user)
wires.Interact(user) wires.Interact(user)
/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, remote = 0) /obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/custom_state = null)
var/data[0] var/data[0]
var/is_locked = locked && !user.isAI() var/remote_connection = 0
var/remote_access = 0
if(custom_state)
var/list/state = custom_state.href_list(user)
remote_connection = state["remote_connection"] // Remote connection means we're non-adjacent/connecting from another computer
remote_access = state["remote_access"] // Remote access means we also have the privilege to alter the air alarm.
data["locked"] = is_locked data["locked"] = locked && !user.isSilicon()
data["remote_connection"] = remote_connection
data["remote_access"] = remote_access
data["rcon"] = rcon_setting data["rcon"] = rcon_setting
data["screen"] = screen data["screen"] = screen
populate_status(data) populate_status(data)
if(!is_locked) if(!(locked && !remote_connection) || remote_access || user.isSilicon())
populate_controls(data) populate_controls(data)
ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
if(!ui) if(!ui)
ui = new(user, src, ui_key, "air_alarm.tmpl", src.name, 625, 625) ui = new(user, src, ui_key, "air_alarm.tmpl", src.name, 625, 625, master_ui = master_ui, custom_state = custom_state)
ui.set_initial_data(data) ui.set_initial_data(data)
ui.open() ui.open()
ui.set_auto_update(1) ui.set_auto_update(1)
@@ -619,7 +626,7 @@
data["thresholds"] = thresholds data["thresholds"] = thresholds
/obj/machinery/alarm/CanUseTopic(var/mob/user) /obj/machinery/alarm/CanUseTopic(var/mob/user, href_list, var/datum/topic_state/custom_state)
if(buildstage != 2) if(buildstage != 2)
return STATUS_CLOSE return STATUS_CLOSE
@@ -629,10 +636,18 @@
. = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE
if(. == STATUS_INTERACTIVE)
var/extra_href = custom_state.href_list(usr)
// Prevent remote users from altering RCON settings unless they already have access (I realize the risks)
if(href_list["rcon"] && extra_href["remote_connection"] && !extra_href["remote_access"])
. = STATUS_UPDATE
//TODO: Move the rest of if(!locked || extra_href["remote_access"] || usr.isAI()) and hrefs here
return min(..(), .) return min(..(), .)
/obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/remote = 0) /obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/custom_state)
if(..(href, href_list, nowindow, !remote)) if(..(href, href_list, nowindow, custom_state))
return 1 return 1
// hrefs that can always be called -walter0o // hrefs that can always be called -walter0o
@@ -661,8 +676,8 @@
return 1 return 1
// hrefs that need the AA unlocked -walter0o // hrefs that need the AA unlocked -walter0o
if(!locked || remote || istype(usr, /mob/living/silicon)) var/extra_href = custom_state.href_list(usr)
if(!(locked && !extra_href["remote_connection"]) || extra_href["remote_access"] || usr.isSilicon())
if(href_list["command"]) if(href_list["command"])
var/device_id = href_list["id_tag"] var/device_id = href_list["id_tag"]
switch(href_list["command"]) switch(href_list["command"])

View File

@@ -9,11 +9,11 @@
density = 1 density = 1
anchored = 1.0 anchored = 1.0
circuit = "/obj/item/weapon/circuitboard/atmoscontrol" circuit = "/obj/item/weapon/circuitboard/atmoscontrol"
var/obj/machinery/alarm/current
var/overridden = 0 //not set yet, can't think of a good way to do it var/overridden = 0 //not set yet, can't think of a good way to do it
req_access = list(access_ce) req_access = list(access_ce)
var/list/monitored_alarm_ids = null var/list/monitored_alarm_ids = null
var/list/monitored_alarms = null var/list/monitored_alarms = null
var/ui_ref
/obj/machinery/computer/atmoscontrol/laptop /obj/machinery/computer/atmoscontrol/laptop
name = "Atmospherics Laptop" name = "Atmospherics Laptop"
@@ -32,36 +32,29 @@
monitored_alarms = dd_sortedObjectList(monitored_alarms) monitored_alarms = dd_sortedObjectList(monitored_alarms)
/obj/machinery/computer/atmoscontrol/attack_ai(var/mob/user as mob) /obj/machinery/computer/atmoscontrol/attack_ai(var/mob/user as mob)
return interact(user) return ui_interact(user)
/obj/machinery/computer/atmoscontrol/attack_hand(mob/user) /obj/machinery/computer/atmoscontrol/attack_hand(mob/user)
if(..()) if(..())
return return
return interact(user) return ui_interact(user)
/obj/machinery/computer/atmoscontrol/interact(mob/user) /obj/machinery/computer/atmoscontrol/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
user.set_machine(src) var/data[0]
if(allowed(user)) var/alarms[0]
overridden = 1
else if(!emagged) // TODO: Move these to a cache, similar to cameras
overridden = 0 for(var/obj/machinery/alarm/alarm in (monitored_alarms ? monitored_alarms : machines))
var/dat = "<a href='?src=\ref[src]&reset=1'>Main Menu</a><hr>" alarms[++alarms.len] = list("name" = sanitize(alarm.name), "ref"= "\ref[alarm]", "danger" = max(alarm.danger_level, alarm.alarm_area.atmosalm))
if(monitored_alarms && monitored_alarms.len == 1) data["alarms"] = alarms
current = monitored_alarms[1]
if(current) ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open)
dat += specific() if(!ui)
else ui = new(user, src, ui_key, "atmos_control.tmpl", src.name, 625, 625)
for(var/obj/machinery/alarm/alarm in monitored_alarms ? monitored_alarms : machines) ui.set_initial_data(data)
dat += "<a href='?src=\ref[src]&alarm=\ref[alarm]'>" ui.open()
switch(max(alarm.danger_level, alarm.alarm_area.atmosalm)) ui.set_auto_update(1)
if (0) ui_ref = ui
dat += "<font color=green>"
if (1)
dat += "<font color=blue>"
if (2)
dat += "<font color=red>"
dat += "[sanitize(alarm.name)]</font></a><br/>"
user << browse(dat, "window=atmoscontrol")
/obj/machinery/computer/atmoscontrol/attackby(var/obj/item/I as obj, var/mob/user as mob) /obj/machinery/computer/atmoscontrol/attackby(var/obj/item/I as obj, var/mob/user as mob)
if(istype(I, /obj/item/weapon/card/emag) && !emagged) if(istype(I, /obj/item/weapon/card/emag) && !emagged)
@@ -73,23 +66,33 @@
return return
return ..() return ..()
/obj/machinery/computer/atmoscontrol/proc/specific()
if(!current)
return ""
var/dat = "<h3>[current.name]</h3><hr>"
dat += current.return_status()
if(current.remote_control || overridden)
dat += "<hr>[current.return_controls(src)]"
return dat
//a bunch of this is copied from atmos alarms //a bunch of this is copied from atmos alarms
/obj/machinery/computer/atmoscontrol/Topic(href, href_list) /obj/machinery/computer/atmoscontrol/Topic(href, href_list)
if(..()) if(..())
return return 1
if(href_list["reset"])
current = null
if(href_list["alarm"]) if(href_list["alarm"])
current = locate(href_list["alarm"]) if(ui_ref)
else if(current) var/obj/machinery/alarm/alarm = locate(href_list["alarm"]) in (monitored_alarms ? monitored_alarms : machines)
current.Topic(href, href_list, 1, 1) if(alarm)
interact(usr) var/datum/topic_state/TS = generate_state(alarm)
alarm.ui_interact(usr, master_ui = ui_ref, custom_state = TS)
return 1
/obj/machinery/computer/atmoscontrol/proc/generate_state(var/alarm)
var/datum/topic_state/air_alarm/state = new()
state.atmos_control = src
state.air_alarm = alarm
return state
/datum/topic_state/air_alarm
flags = NANO_IGNORE_DISTANCE
var/obj/machinery/computer/atmoscontrol/atmos_control = null
var/obj/machinery/alarm/air_alarm = null
/datum/topic_state/air_alarm/href_list(var/mob/user)
var/list/extra_href = list()
extra_href["remote_connection"] = 1
extra_href["remote_access"] = user && (atmos_control.allowed(user) || atmos_control.emagged || air_alarm.rcon_setting == RCON_YES || (air_alarm.alarm_area.atmosalm && air_alarm.rcon_setting == RCON_AUTO))
return extra_href

View File

@@ -16,7 +16,7 @@
var/damtype = "brute" var/damtype = "brute"
var/force = 0 var/force = 0
/obj/Topic(href, href_list, var/nowindow = 0, var/checkrange = 1) /obj/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/custom_state)
// Calling Topic without a corresponding window open causes runtime errors // Calling Topic without a corresponding window open causes runtime errors
if(!nowindow && ..()) if(!nowindow && ..())
return 1 return 1
@@ -24,7 +24,7 @@
// In the far future no checks are made in an overriding Topic() beyond if(..()) return // In the far future no checks are made in an overriding Topic() beyond if(..()) return
// Instead any such checks are made in CanUseTopic() // Instead any such checks are made in CanUseTopic()
var/obj/host = nano_host() var/obj/host = nano_host()
if(host.CanUseTopic(usr, checkrange) == STATUS_INTERACTIVE) if(host.CanUseTopic(usr, href_list, custom_state) == STATUS_INTERACTIVE)
CouldUseTopic(usr) CouldUseTopic(usr)
return 0 return 0

View File

@@ -80,6 +80,12 @@
return 1 return 1
return 0 return 0
/mob/proc/isSilicon()
return 0
/mob/living/silicon/isSilicon()
return 1
/mob/proc/isAI() /mob/proc/isAI()
return 0 return 0

View File

@@ -1,5 +1,5 @@
// This file contains all Nano procs/definitions for external classes/objects // This file contains all Nano procs/definitions for external classes/objects
/** /**
* Called when a Nano UI window is closed * Called when a Nano UI window is closed
* This is how Nano handles closed windows * This is how Nano handles closed windows
@@ -15,7 +15,7 @@
if (istype(ui)) if (istype(ui))
ui.close() ui.close()
if(ui.ref) if(ui.ref)
var/href = "close=1" var/href = "close=1"
src.Topic(href, params2list(href), ui.ref) // this will direct to the atom's Topic() proc via client.Topic() src.Topic(href, params2list(href), ui.ref) // this will direct to the atom's Topic() proc via client.Topic()
@@ -31,14 +31,14 @@
* ui_interact is currently defined for /atom/movable * ui_interact is currently defined for /atom/movable
* *
* @param user /mob The mob who is interacting with this ui * @param user /mob The mob who is interacting with this ui
* @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
* @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
* @param force_open boolean Force the UI to (re)open, even if it's already open * @param force_open boolean Force the UI to (re)open, even if it's already open
* *
* @return nothing * @return nothing
*/ */
/atom/movable/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) /atom/movable/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/nano_ui/master_ui = null, var/datum/topic_state/custom_state = null)
return return
// Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob // Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob
/mob/var/list/open_uis = list() /mob/var/list/open_uis = list()

View File

@@ -5,11 +5,11 @@
return loc return loc
/atom/movable/proc/CanUseTopic(var/mob/user, var/be_close) /atom/movable/proc/CanUseTopic(var/mob/user, href_list, var/datum/topic_state/custom_state)
return user.can_use_topic(nano_host(), be_close) return user.can_use_topic(nano_host(), custom_state)
/mob/proc/can_use_topic(var/src_object) /mob/proc/can_use_topic(var/mob/user, var/datum/topic_state/custom_state)
return STATUS_CLOSE // By default no mob can do anything with NanoUI return STATUS_CLOSE // By default no mob can do anything with NanoUI
/mob/dead/observer/can_use_topic() /mob/dead/observer/can_use_topic()
@@ -23,12 +23,13 @@
else else
return ..() return ..()
/mob/living/silicon/robot/can_use_topic(var/src_object) /mob/living/silicon/robot/can_use_topic(var/src_object, var/datum/topic_state/custom_state)
if(stat || !client) if(stat || !client)
return STATUS_CLOSE return STATUS_CLOSE
if(lockcharge || stunned || weakened) if(lockcharge || stunned || weakened)
return STATUS_DISABLED return STATUS_DISABLED
if (src_object in view(src)) // robots can interact with things they can see within their view range // robots can interact with things they can see within their view range
if(!(custom_state.flags & NANO_IGNORE_DISTANCE) && (src_object in view(src)))
return STATUS_INTERACTIVE // interactive (green visibility) return STATUS_INTERACTIVE // interactive (green visibility)
return STATUS_DISABLED // no updates, completely disabled (red visibility) return STATUS_DISABLED // no updates, completely disabled (red visibility)
@@ -96,16 +97,22 @@
return STATUS_DISABLED // no updates, completely disabled (red visibility) return STATUS_DISABLED // no updates, completely disabled (red visibility)
return STATUS_CLOSE return STATUS_CLOSE
/mob/living/can_use_topic(var/src_object, var/be_close = 1) /mob/living/can_use_topic(var/src_object, var/datum/topic_state/custom_state)
. = shared_living_nano_interaction(src_object) . = shared_living_nano_interaction(src_object)
if(. == STATUS_INTERACTIVE && be_close) if(. == STATUS_INTERACTIVE && !(custom_state.flags & NANO_IGNORE_DISTANCE))
. = shared_living_nano_distance(src_object) . = shared_living_nano_distance(src_object)
if(STATUS_INTERACTIVE) if(STATUS_INTERACTIVE)
return STATUS_UPDATE return STATUS_UPDATE
/mob/living/carbon/human/can_use_topic(var/src_object, var/be_close = 1) /mob/living/carbon/human/can_use_topic(var/src_object, var/datum/topic_state/custom_state)
. = shared_living_nano_interaction(src_object) . = shared_living_nano_interaction(src_object)
if(. == STATUS_INTERACTIVE && be_close) if(. == STATUS_INTERACTIVE && !(custom_state.flags & NANO_IGNORE_DISTANCE))
. = shared_living_nano_distance(src_object) . = shared_living_nano_distance(src_object)
if(. == STATUS_UPDATE && (TK in mutations)) // If we have telekinesis and remain close enough, allow interaction. if(. == STATUS_UPDATE && (TK in mutations)) // If we have telekinesis and remain close enough, allow interaction.
return STATUS_INTERACTIVE return STATUS_INTERACTIVE
/datum/topic_state
var/flags = 0
/datum/topic_state/proc/href_list(var/mob/user)
return list()

View File

@@ -186,7 +186,8 @@
return 0 // wasn't open return 0 // wasn't open
processing_uis.Remove(ui) processing_uis.Remove(ui)
ui.user.open_uis.Remove(ui) if(ui.user) // Sanity check in case a user has been deleted (say a blown up borg watching the alarm interface)
ui.user.open_uis.Remove(ui)
var/list/uis = open_uis[src_object_key][ui.ui_key] var/list/uis = open_uis[src_object_key][ui.ui_key]
uis.Remove(ui) uis.Remove(ui)

View File

@@ -52,6 +52,11 @@ nanoui is used to open and update nano browser uis
// the current status/visibility of the ui // the current status/visibility of the ui
var/status = STATUS_INTERACTIVE var/status = STATUS_INTERACTIVE
// Relationship between a master interface and its children. Used in update_status
var/datum/nanoui/master_ui
var/list/datum/nanoui/children = list()
var/datum/topic_state/custom_state = null
var/cached_data = null var/cached_data = null
/** /**
@@ -68,17 +73,22 @@ nanoui is used to open and update nano browser uis
* *
* @return /nanoui new nanoui object * @return /nanoui new nanoui object
*/ */
/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null) /datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, var/datum/nanoui/master_ui = null, var/datum/topic_state/custom_state = null)
user = nuser user = nuser
src_object = nsrc_object src_object = nsrc_object
ui_key = nui_key ui_key = nui_key
window_id = "[ui_key]\ref[src_object]" window_id = "[ui_key]\ref[src_object]"
src.master_ui = master_ui
if(master_ui)
master_ui.children += src
src.custom_state = custom_state ? custom_state : new/datum/topic_state()
// add the passed template filename as the "main" template, this is required // add the passed template filename as the "main" template, this is required
add_template("main", ntemplate_filename) add_template("main", ntemplate_filename)
if (ntitle) if (ntitle)
title = ntitle title = sanitize(ntitle)
if (nwidth) if (nwidth)
width = nwidth width = nwidth
if (nheight) if (nheight)
@@ -132,16 +142,15 @@ nanoui is used to open and update nano browser uis
* @return nothing * @return nothing
*/ */
/datum/nanoui/proc/update_status(var/push_update = 0) /datum/nanoui/proc/update_status(var/push_update = 0)
var/new_status = get_status() var/atom/movable/host = src_object.nano_host()
var/new_status = host.CanUseTopic(user, list(), custom_state)
if(master_ui)
new_status = min(new_status, master_ui.status)
if(new_status == STATUS_CLOSE) if(new_status == STATUS_CLOSE)
close() close()
else else
set_status(new_status, push_update) set_status(new_status, push_update)
/datum/nanoui/proc/get_status()
var/atom/movable/host = src_object.nano_host()
return host.CanUseTopic(user)
/** /**
* Set the ui to auto update (every master_controller tick) * Set the ui to auto update (every master_controller tick)
* *
@@ -383,10 +392,6 @@ nanoui is used to open and update nano browser uis
* @return nothing * @return nothing
*/ */
/datum/nanoui/proc/open() /datum/nanoui/proc/open()
var/new_status = get_status()
if(new_status == STATUS_CLOSE)
return
var/window_size = "" var/window_size = ""
if (width && height) if (width && height)
window_size = "size=[width]x[height];" window_size = "size=[width]x[height];"
@@ -406,6 +411,8 @@ nanoui is used to open and update nano browser uis
is_auto_updating = 0 is_auto_updating = 0
nanomanager.ui_closed(src) nanomanager.ui_closed(src)
user << browse(null, "window=[window_id]") user << browse(null, "window=[window_id]")
for(var/datum/nanoui/child in children)
child.close()
/** /**
* Set the UI window to call the nanoclose verb when the window is closed * Set the UI window to call the nanoclose verb when the window is closed
@@ -470,7 +477,7 @@ nanoui is used to open and update nano browser uis
set_map_z_level(text2num(href_list["mapZLevel"])) set_map_z_level(text2num(href_list["mapZLevel"]))
map_update = 1 map_update = 1
if ((src_object && src_object.Topic(href, href_list)) || map_update) if ((src_object && src_object.Topic(href, href_list, 0, custom_state)) || map_update)
nanomanager.update_uis(src_object) // update all UIs attached to src_object nanomanager.update_uis(src_object) // update all UIs attached to src_object
/** /**
@@ -497,5 +504,4 @@ nanoui is used to open and update nano browser uis
* @return nothing * @return nothing
*/ */
/datum/nanoui/proc/update(var/force_open = 0) /datum/nanoui/proc/update(var/force_open = 0)
src_object.ui_interact(user, ui_key, src, force_open) src_object.ui_interact(user, ui_key, src, force_open, master_ui, custom_state)

View File

@@ -759,3 +759,4 @@ var/list/be_special_flags = list(
#define STATUS_DISABLED 0 // RED Visability #define STATUS_DISABLED 0 // RED Visability
#define STATUS_CLOSE -1 // Close the interface #define STATUS_CLOSE -1 // Close the interface
#define NANO_IGNORE_DISTANCE 1

View File

@@ -60,9 +60,9 @@ Used In File(s): \code\game\machinery\alarm.dm
<td> <td>
<div class="item"> <div class="item">
<div class="itemContent" style="width: 100%;"> <div class="itemContent" style="width: 100%;">
{{:helper.link('Off', null, { 'rcon' : 1}, null, data.rcon == 1 ? 'selected' : null)}} {{:helper.link('Off', null, { 'rcon' : 1}, data.remote_connection && !data.remote_access ? (data.rcon == 1 ? 'yellowButton' : 'disabled') : null, data.rcon == 1 ? 'selected' : null)}}
{{:helper.link('Auto', null, { 'rcon' : 2}, null, data.rcon == 2 ? 'selected' : null)}} {{:helper.link('Auto', null, { 'rcon' : 2}, data.remote_connection && !data.remote_access ? (data.rcon == 2 ? 'yellowButton' : 'disabled') : null, data.rcon == 2 ? 'selected' : null)}}
{{:helper.link('On', null, { 'rcon' : 3}, null, data.rcon == 3 ? 'selected' : null)}} {{:helper.link('On', null, { 'rcon' : 3}, data.remote_connection && !data.remote_access ? (data.rcon == 3 ? 'yellowButton' : 'disabled') : null, data.rcon == 3 ? 'selected' : null)}}
</div> </div>
</div> </div>
</td> </td>
@@ -74,8 +74,12 @@ Used In File(s): \code\game\machinery\alarm.dm
</tr> </tr>
</table> </table>
<HR> <HR>
{{if data.locked}} {{if data.locked || (data.remote_connection && ! data.remote_access)}}
<span class='noticePlaceholder'>(Swipe ID card to unlock interface)</span> {{if data.remote_connection}}
<span class='noticePlaceholder'>(Current remote control settings and alarm status restricts access.)</span>
{{else}}
<span class='noticePlaceholder'>(Swipe ID card to unlock interface.)</span>
{{/if}}
{{else}} {{else}}
{{if data.screen != 1}} {{if data.screen != 1}}
<div class="item">{{:helper.link('Main Menu', null, { 'screen' : 1})}}</div> <div class="item">{{:helper.link('Main Menu', null, { 'screen' : 1})}}</div>

View File

@@ -0,0 +1,5 @@
<div class='item'>
{{for data.alarms}}
{{:helper.link(value.name, null, {'alarm' : value.ref}, null, value.danger == 2 ? 'redButton' : (value.danger == 1 ? 'yellowButton' : null))}}
{{/for}}
</div>

View File

@@ -0,0 +1,37 @@
<!--
Title: Alarm Monitor Console (Main content)
Used In File(s): \code\modules\nano\modules\alarm_monitor.dm
-->
{{for data.categories}}
<H2><span class="itemLabelWidest">{{:value.category}}</span></H2>
{{for value.alarms :alarmValue:alarmIndex}}
{{if alarmValue.origin_lost}}
{{:alarmValue.name}} <span class='notice'>Alarm Origin Lost</span><br>
{{else}}
{{:alarmValue.name}}<br>
{{/if}}
{{if alarmValue.has_cameras || alarmValue.lost_sources != ""}}
<div class="item">
{{if alarmValue.has_cameras}}
<div class="itemContent" style="width: 100%;">
{{for alarmValue.cameras :cameraValue:cameraIndex}}
{{if cameraValue.deact}}
{{:helper.link(cameraValue.name + " (deactivated)", '', {}, 'inactive')}}
{{else}}
{{:helper.link(cameraValue.name, '', {'switchTo' : cameraValue.camera})}}
{{/if}}
{{/for}}
</div>
{{/if}}
{{if alarmValue.lost_sources != ""}}
<div class="itemContent" style="width: 100%;">
<H4><span class='notice'>Lost Alarm Sources: {{:alarmValue.lost_sources}}</span></H4>
</div>
{{/if}}
</div>
{{/if}}
{{empty}}
<span class="white">--All Systems Nominal</span>
{{/for}}
{{/for}}