Merge pull request #13703 from neersighted/nano_reduction

NanoUI Reduction
This commit is contained in:
tkdrg
2015-12-17 15:07:45 -03:00
33 changed files with 1640 additions and 1654 deletions
+233 -240
View File
@@ -1,240 +1,233 @@
/*
Asset cache quick users guide:
Make a datum at the bottom of this file with your assets for your thing.
The simple subsystem will most like be of use for most cases.
Then call get_asset_datum() with the type of the datum you created and store the return
Then call .send(client) on that stored return value.
You can set verify to TRUE if you want send() to sleep until the client has the assets.
*/
// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping.
// This is doubled for the first asset, then added per asset after
#define ASSET_CACHE_SEND_TIMEOUT 7
//When sending mutiple assets, how many before we give the client a quaint little sending resources message
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
/client
var/list/cache = list() // List of all assets sent to this client by the asset cache.
var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement.
var/list/sending = list()
var/last_asset_job = 0 // Last job done.
//This proc sends the asset to the client, but only if it needs it.
//This proc blocks(sleeps) unless verify is set to false
/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE)
if(!istype(client))
if(ismob(client))
var/mob/M = client
if(M.client)
client = M.client
else
return 0
else
return 0
if(client.cache.Find(asset_name) || client.sending.Find(asset_name))
return 0
client << browse_rsc(SSasset.cache[asset_name], asset_name)
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
if (client)
client.cache += asset_name
return 1
if (!client)
return 0
client.sending |= asset_name
var/job = ++client.last_asset_job
client << browse({"
<script>
window.location.href="?asset_cache_confirm_arrival=[job]"
</script>
"}, "window=asset_cache_browser")
var/t = 0
var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
sleep(1) // Lock up the caller until this is received.
t++
if(client)
client.sending -= asset_name
client.cache |= asset_name
client.completed_asset_jobs -= job
return 1
//This proc blocks(sleeps) unless verify is set to false
/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE)
if(!istype(client))
if(ismob(client))
var/mob/M = client
if(M.client)
client = M.client
else
return 0
else
return 0
var/list/unreceived = asset_list - (client.cache + client.sending)
if(!unreceived || !unreceived.len)
return 0
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
client << "Sending Resources..."
for(var/asset in unreceived)
if (asset in SSasset.cache)
client << browse_rsc(SSasset.cache[asset], asset)
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
if (client)
client.cache += unreceived
return 1
if (!client)
return 0
client.sending |= unreceived
var/job = ++client.last_asset_job
client << browse({"
<script>
window.location.href="?asset_cache_confirm_arrival=[job]"
</script>
"}, "window=asset_cache_browser")
var/t = 0
var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
sleep(1) // Lock up the caller until this is received.
t++
if(client)
client.sending -= unreceived
client.cache |= unreceived
client.completed_asset_jobs -= job
return 1
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
//The proc calls procs that sleep for long times.
/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
for(var/file in files)
if (!client)
break
if (register_asset)
register_asset(file,files[file])
send_asset(client,file)
sleep(-1) //queuing calls like this too quickly can cause issues in some client versions
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
//if it's an icon or something be careful, you'll have to copy it before further use.
/proc/register_asset(var/asset_name, var/asset)
SSasset.cache[asset_name] = asset
//These datums are used to populate the asset cache, the proc "register()" does this.
//all of our asset datums, used for referring to these later
/var/global/list/asset_datums = list()
//get a assetdatum or make a new one
/proc/get_asset_datum(var/type)
if (!(type in asset_datums))
return new type()
return asset_datums[type]
/datum/asset/New()
asset_datums[type] = src
/datum/asset/proc/register()
return
/datum/asset/proc/send(client)
return
//If you don't need anything complicated.
/datum/asset/simple
var/assets = list()
var/verify = FALSE
/datum/asset/simple/register()
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
/datum/asset/simple/send(client)
send_asset_list(client,assets,verify)
//DEFINITIONS FOR ASSET DATUMS START HERE.
/datum/asset/simple/nanoui
assets = list(
"nanoui.lib.js" = 'nano/assets/nanoui.lib.js',
"nanoui.main.js" = 'nano/assets/nanoui.main.js',
"nanoui.templates.js" = 'nano/assets/nanoui.templates.js',
"nanoui.lib.css" = 'nano/assets/nanoui.lib.css',
"nanoui.common.css" = 'nano/assets/nanoui.common.css',
"nanoui.generic.css" = 'nano/assets/nanoui.generic.css',
"nanoui.nanotrasen.css" = 'nano/assets/nanoui.nanotrasen.css',
"fontawesome-webfont.eot" = 'nano/assets/fontawesome-webfont.eot',
"fontawesome-webfont.woff2" = 'nano/assets/fontawesome-webfont.woff2'
)
/datum/asset/simple/pda
assets = list(
"pda_atmos.png" = 'icons/pda_icons/pda_atmos.png',
"pda_back.png" = 'icons/pda_icons/pda_back.png',
"pda_bell.png" = 'icons/pda_icons/pda_bell.png',
"pda_blank.png" = 'icons/pda_icons/pda_blank.png',
"pda_boom.png" = 'icons/pda_icons/pda_boom.png',
"pda_bucket.png" = 'icons/pda_icons/pda_bucket.png',
"pda_medbot.png" = 'icons/pda_icons/pda_medbot.png',
"pda_floorbot.png" = 'icons/pda_icons/pda_floorbot.png',
"pda_cleanbot.png" = 'icons/pda_icons/pda_cleanbot.png',
"pda_crate.png" = 'icons/pda_icons/pda_crate.png',
"pda_cuffs.png" = 'icons/pda_icons/pda_cuffs.png',
"pda_eject.png" = 'icons/pda_icons/pda_eject.png',
"pda_exit.png" = 'icons/pda_icons/pda_exit.png',
"pda_flashlight.png" = 'icons/pda_icons/pda_flashlight.png',
"pda_honk.png" = 'icons/pda_icons/pda_honk.png',
"pda_mail.png" = 'icons/pda_icons/pda_mail.png',
"pda_medical.png" = 'icons/pda_icons/pda_medical.png',
"pda_menu.png" = 'icons/pda_icons/pda_menu.png',
"pda_mule.png" = 'icons/pda_icons/pda_mule.png',
"pda_notes.png" = 'icons/pda_icons/pda_notes.png',
"pda_power.png" = 'icons/pda_icons/pda_power.png',
"pda_rdoor.png" = 'icons/pda_icons/pda_rdoor.png',
"pda_reagent.png" = 'icons/pda_icons/pda_reagent.png',
"pda_refresh.png" = 'icons/pda_icons/pda_refresh.png',
"pda_scanner.png" = 'icons/pda_icons/pda_scanner.png',
"pda_signaler.png" = 'icons/pda_icons/pda_signaler.png',
"pda_status.png" = 'icons/pda_icons/pda_status.png'
)
/datum/asset/simple/paper
assets = list(
"large_stamp-clown.png" = 'icons/stamp_icons/large_stamp-clown.png',
"large_stamp-deny.png" = 'icons/stamp_icons/large_stamp-deny.png',
"large_stamp-ok.png" = 'icons/stamp_icons/large_stamp-ok.png',
"large_stamp-hop.png" = 'icons/stamp_icons/large_stamp-hop.png',
"large_stamp-cmo.png" = 'icons/stamp_icons/large_stamp-cmo.png',
"large_stamp-ce.png" = 'icons/stamp_icons/large_stamp-ce.png',
"large_stamp-hos.png" = 'icons/stamp_icons/large_stamp-hos.png',
"large_stamp-rd.png" = 'icons/stamp_icons/large_stamp-rd.png',
"large_stamp-cap.png" = 'icons/stamp_icons/large_stamp-cap.png',
"large_stamp-qm.png" = 'icons/stamp_icons/large_stamp-qm.png',
"large_stamp-law.png" = 'icons/stamp_icons/large_stamp-law.png'
)
//Registers HTML Interface assets.
/datum/asset/HTML_interface/register()
for(var/path in typesof(/datum/html_interface))
var/datum/html_interface/hi = new path()
hi.registerResources()
/*
Asset cache quick users guide:
Make a datum at the bottom of this file with your assets for your thing.
The simple subsystem will most like be of use for most cases.
Then call get_asset_datum() with the type of the datum you created and store the return
Then call .send(client) on that stored return value.
You can set verify to TRUE if you want send() to sleep until the client has the assets.
*/
// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping.
// This is doubled for the first asset, then added per asset after
#define ASSET_CACHE_SEND_TIMEOUT 7
//When sending mutiple assets, how many before we give the client a quaint little sending resources message
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
/client
var/list/cache = list() // List of all assets sent to this client by the asset cache.
var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement.
var/list/sending = list()
var/last_asset_job = 0 // Last job done.
//This proc sends the asset to the client, but only if it needs it.
//This proc blocks(sleeps) unless verify is set to false
/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE)
if(!istype(client))
if(ismob(client))
var/mob/M = client
if(M.client)
client = M.client
else
return 0
else
return 0
if(client.cache.Find(asset_name) || client.sending.Find(asset_name))
return 0
client << browse_rsc(SSasset.cache[asset_name], asset_name)
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
if (client)
client.cache += asset_name
return 1
if (!client)
return 0
client.sending |= asset_name
var/job = ++client.last_asset_job
client << browse({"
<script>
window.location.href="?asset_cache_confirm_arrival=[job]"
</script>
"}, "window=asset_cache_browser")
var/t = 0
var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
sleep(1) // Lock up the caller until this is received.
t++
if(client)
client.sending -= asset_name
client.cache |= asset_name
client.completed_asset_jobs -= job
return 1
//This proc blocks(sleeps) unless verify is set to false
/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE)
if(!istype(client))
if(ismob(client))
var/mob/M = client
if(M.client)
client = M.client
else
return 0
else
return 0
var/list/unreceived = asset_list - (client.cache + client.sending)
if(!unreceived || !unreceived.len)
return 0
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
client << "Sending Resources..."
for(var/asset in unreceived)
if (asset in SSasset.cache)
client << browse_rsc(SSasset.cache[asset], asset)
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
if (client)
client.cache += unreceived
return 1
if (!client)
return 0
client.sending |= unreceived
var/job = ++client.last_asset_job
client << browse({"
<script>
window.location.href="?asset_cache_confirm_arrival=[job]"
</script>
"}, "window=asset_cache_browser")
var/t = 0
var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
sleep(1) // Lock up the caller until this is received.
t++
if(client)
client.sending -= unreceived
client.cache |= unreceived
client.completed_asset_jobs -= job
return 1
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
//The proc calls procs that sleep for long times.
/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
for(var/file in files)
if (!client)
break
if (register_asset)
register_asset(file,files[file])
send_asset(client,file)
sleep(-1) //queuing calls like this too quickly can cause issues in some client versions
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
//if it's an icon or something be careful, you'll have to copy it before further use.
/proc/register_asset(var/asset_name, var/asset)
SSasset.cache[asset_name] = asset
//These datums are used to populate the asset cache, the proc "register()" does this.
//all of our asset datums, used for referring to these later
/var/global/list/asset_datums = list()
//get a assetdatum or make a new one
/proc/get_asset_datum(var/type)
if (!(type in asset_datums))
return new type()
return asset_datums[type]
/datum/asset/New()
asset_datums[type] = src
/datum/asset/proc/register()
return
/datum/asset/proc/send(client)
return
//If you don't need anything complicated.
/datum/asset/simple
var/assets = list()
var/verify = FALSE
/datum/asset/simple/register()
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
/datum/asset/simple/send(client)
send_asset_list(client,assets,verify)
//DEFINITIONS FOR ASSET DATUMS START HERE.
/datum/asset/simple/nanoui
assets = list(
"nanoui.js" = 'nano/assets/nanoui.js',
"nanoui.css" = 'nano/assets/nanoui.css',
)
/datum/asset/simple/pda
assets = list(
"pda_atmos.png" = 'icons/pda_icons/pda_atmos.png',
"pda_back.png" = 'icons/pda_icons/pda_back.png',
"pda_bell.png" = 'icons/pda_icons/pda_bell.png',
"pda_blank.png" = 'icons/pda_icons/pda_blank.png',
"pda_boom.png" = 'icons/pda_icons/pda_boom.png',
"pda_bucket.png" = 'icons/pda_icons/pda_bucket.png',
"pda_medbot.png" = 'icons/pda_icons/pda_medbot.png',
"pda_floorbot.png" = 'icons/pda_icons/pda_floorbot.png',
"pda_cleanbot.png" = 'icons/pda_icons/pda_cleanbot.png',
"pda_crate.png" = 'icons/pda_icons/pda_crate.png',
"pda_cuffs.png" = 'icons/pda_icons/pda_cuffs.png',
"pda_eject.png" = 'icons/pda_icons/pda_eject.png',
"pda_exit.png" = 'icons/pda_icons/pda_exit.png',
"pda_flashlight.png" = 'icons/pda_icons/pda_flashlight.png',
"pda_honk.png" = 'icons/pda_icons/pda_honk.png',
"pda_mail.png" = 'icons/pda_icons/pda_mail.png',
"pda_medical.png" = 'icons/pda_icons/pda_medical.png',
"pda_menu.png" = 'icons/pda_icons/pda_menu.png',
"pda_mule.png" = 'icons/pda_icons/pda_mule.png',
"pda_notes.png" = 'icons/pda_icons/pda_notes.png',
"pda_power.png" = 'icons/pda_icons/pda_power.png',
"pda_rdoor.png" = 'icons/pda_icons/pda_rdoor.png',
"pda_reagent.png" = 'icons/pda_icons/pda_reagent.png',
"pda_refresh.png" = 'icons/pda_icons/pda_refresh.png',
"pda_scanner.png" = 'icons/pda_icons/pda_scanner.png',
"pda_signaler.png" = 'icons/pda_icons/pda_signaler.png',
"pda_status.png" = 'icons/pda_icons/pda_status.png'
)
/datum/asset/simple/paper
assets = list(
"large_stamp-clown.png" = 'icons/stamp_icons/large_stamp-clown.png',
"large_stamp-deny.png" = 'icons/stamp_icons/large_stamp-deny.png',
"large_stamp-ok.png" = 'icons/stamp_icons/large_stamp-ok.png',
"large_stamp-hop.png" = 'icons/stamp_icons/large_stamp-hop.png',
"large_stamp-cmo.png" = 'icons/stamp_icons/large_stamp-cmo.png',
"large_stamp-ce.png" = 'icons/stamp_icons/large_stamp-ce.png',
"large_stamp-hos.png" = 'icons/stamp_icons/large_stamp-hos.png',
"large_stamp-rd.png" = 'icons/stamp_icons/large_stamp-rd.png',
"large_stamp-cap.png" = 'icons/stamp_icons/large_stamp-cap.png',
"large_stamp-qm.png" = 'icons/stamp_icons/large_stamp-qm.png',
"large_stamp-law.png" = 'icons/stamp_icons/large_stamp-law.png'
)
//Registers HTML Interface assets.
/datum/asset/HTML_interface/register()
for(var/path in typesof(/datum/html_interface))
var/datum/html_interface/hi = new path()
hi.registerResources()
+1 -1
View File
@@ -1,5 +1,5 @@
/mob/Logout()
SSnano.user_logout(src) // this is used to clean up (remove) this user's Nano UIs
SSnano.on_logout(src)
player_list -= src
log_access("Logout: [key_name(src)]")
if(admin_datums[src.ckey])
+14 -13
View File
@@ -27,6 +27,20 @@
datum/topic_state/state = default_state)
return -1 // Sorta implemented.
/**
* public
*
* Data to be sent to the NanoUI.
* This must be implemented for a NanoUI to work.
*
* required user mob The mob interacting with the NanoUI.
*
* return list Data to be sent to the NanoUI.
**/
/atom/movable/proc/get_ui_data(mob/user)
return list() // Not implemented.
/**
* public
*
@@ -41,18 +55,6 @@
/atom/movable/proc/ui_act(action, list/params)
return // Not implemented.
/**
* public
*
* Data to be sent to the NanoUI.
* This must be implemented for a NanoUI to work.
*
* required user mob The mob interacting with the NanoUI.
*
* return list Data to be sent to the NanoUI.
**/
/atom/movable/proc/get_ui_data(mob/user)
return list() // Not implemented.
/**
* private
@@ -67,7 +69,6 @@
* global
*
* Used to track NanoUIs for a mob.
*
**/
/mob/var/list/open_uis = list()
+403 -405
View File
@@ -1,405 +1,403 @@
/**
* NanoUI
*
* Contains the NanoUI datum, and its procs.
*
* /tg/station user interface library
* thanks to baystation12
*
* modified by neersighted
**/
/**
* NanoUI datum:
*
* Represents a NanoUI.
**/
/datum/nanoui
var/mob/user // The mob who opened/is using the NanoUI.
var/atom/movable/src_object // The object which owns the NanoUI.
var/title // The title of te NanoUI.
var/ui_key // The ui_key of the NanoUI. This allows multiple UIs for one src_object.
var/window_id // The window_id for browse() and onclose().
var/width = 0 // The window width.
var/height = 0 // The window height
var/window_options = list( // Extra options to winset().
"focus" = 0,
"titlebar" = 1,
"can_resize" = 1,
"can_minimize" = 1,
"can_maximize" = 0,
"can_close" = 1,
"auto_format" = 0
)
var/atom/ref = null // An extra ref to call when the window is closed.
var/layout = "nanotrasen" // The layout to be used for this UI.
var/template // The template to be used for this UI.
var/auto_update = 1 // Update the NanoUI every MC tick.
var/list/initial_data // The data (and datastructure) used to initialize the NanoUI
var/status = NANO_INTERACTIVE // The status/visibility of the NanoUI.
var/datum/topic_state/state = null // Topic state used to determine status. Topic states are in interactions/.
var/datum/nanoui/master_ui // The parent NanoUI.
var/list/datum/nanoui/children = list() // Children of this NanoUI.
/**
* public
*
* Create a new NanoUI.
*
* required user mob The mob who opened/is using the NanoUI.
* required src_object atom/movable The object which owns the NanoUI.
* required ui_key string The ui_key of the NanoUI.
* required template string The template to render the NanoUI content with.
* optional title string The title of the NanoUI.
* optional width int The window width.
* optional height int The window height.
* optional ref atom An extra ref to use when the window is closed.
* optional master_ui datum/nanoui The parent NanoUI.
* optional state datum/topic_state The state used to determine status.
*
* return datum/nanoui The requested NanoUI.
**/
/datum/nanoui/New(mob/user, atom/movable/src_object, ui_key, template, \
title = 0, width = 0, height = 0, \
atom/ref = null, datum/nanoui/master_ui = null, \
datum/topic_state/state = default_state)
src.user = user
src.src_object = src_object
src.ui_key = ui_key
src.window_id = "\ref[src_object]-[ui_key]"
set_template(template)
if (title)
src.title = sanitize(title)
if (width)
src.width = width
if (height)
src.height = height
if (ref)
src.ref = ref
src.master_ui = master_ui
if(master_ui)
master_ui.children += src
src.state = state
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/nanoui)
assets.send(user)
/**
* public
*
* Enable/disable auto-updating of the NanoUI.
*
* required state bool Enable/disable auto-updating.
**/
/datum/nanoui/proc/set_auto_update(state = 1)
auto_update = state
/**
* private
*
* Set the data to initialize the NanoUI with.
* The datastructure cannot be changed by subsequent updates.
*
* optional data list The data/datastructure to initialize the NanoUI with.
**/
/datum/nanoui/proc/set_initial_data(list/data)
initial_data = data
/**
* private
*
* Get the config data/datastructure to initialize the NanoUI with.
*
* return list The config data.
**/
/datum/nanoui/proc/get_config_data()
var/list/config_data = list(
"title" = title,
"status" = status,
"ref" = "\ref[src]",
"window" = list(
"width" = width,
"height" = height,
"ref" = window_id
),
"user" = list(
"name" = user.name,
"fancy" = user.client.prefs.nanoui_fancy,
"ref" = "\ref[user]"
),
"srcObject" = list(
"name" = src_object.name,
"ref" = "\ref[src_object]"
),
"templates" = list(
"layout" = "_[layout]",
"content" = "[template]"
)
)
return config_data
/**
* private
*
* Package the data to send to the UI.
* This is the (regular) data and config data, bundled together.
*
* return list The packaged data.
**/
/datum/nanoui/proc/get_send_data(list/data)
var/list/send_data = list()
send_data["config"] = get_config_data()
if (!isnull(data))
send_data["data"] = data
return send_data
/**
* public
*
* Sets the browse() window options for this NanoUI.
*
* required window_options list The window options to set.
**/
/datum/nanoui/proc/set_window_options(list/window_options)
src.window_options = window_options
/**
* public
*
* Set the layout for this NanoUI.
* This loads custom layout styles and templates for this NanoUI.
*
* required layout string The new UI layout.
**/
/datum/nanoui/proc/set_layout(layout)
src.layout = lowertext(layout)
/**
* public
*
* Set the template for this NanoUI.
*
* required template string The new UI template.
**/
/datum/nanoui/proc/set_template(template)
src.template = lowertext(template)
/**
* private
*
* Generate HTML for this NanoUI.
*
* return string NanoUI HTML output.
**/
/datum/nanoui/proc/get_html()
// Generate <script> and <link> tags.
var/script_html = {"
<script type='text/javascript' src='nanoui.lib.js'></script>
<script type='text/javascript' src='nanoui.main.js'></script>
<script type='text/javascript' src='nanoui.templates.js'></script>
"}
var/stylesheet_html = {"
<link rel="stylesheet" type='text/css' href='http://css-spinners.com/css/spinner/hexdots.css'>
<link rel='stylesheet' type='text/css' href='nanoui.lib.css' />
<link rel='stylesheet' type='text/css' href='nanoui.common.css' />
<link rel='stylesheet' type='text/css' href='nanoui.[layout].css' />
"}
// Generate JSON.
var/list/send_data = get_send_data(initial_data)
var/initial_data_json = replacetext(JSON.stringify(send_data), "'", "\\'")
// Generate the HTML document.
return {"
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<script type='text/javascript'>
function receiveUpdate(dataString) {
if (typeof NanoBus !== 'undefined') {
NanoBus.emit('serverUpdate', dataString);
}
};
</script>
[stylesheet_html]
[script_html]
</head>
<body>
<div id='data' data-initial='[initial_data_json]'></div>
<div id='layout'>
<div class='hexdots-loader' style='position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);-ms-transform:translate(-50%, -50%);'>
Sending Resources...
</div>
</div>
<noscript>
<h1>Javascript Required</h1>
<p>Javascript is required in order to use this NanoUI interface.</p>
<p>Please enable Javascript in Internet Explorer, and restart your game.</p>
</noscript>
</body>
</html>
"}
/**
* public
*
* Open this NanoUI (and initialize it with data).
*
* optional data list The data to intialize the UI with.
**/
/datum/nanoui/proc/open(list/data = null)
if(!user.client) return
if (!initial_data)
if (!data) // If we don't have initial_data and data was not passed, get data from the src_object.
data = src_object.get_ui_data(user)
set_initial_data(data) // Otherwise use the passed data.
var/window_size = ""
if (width && height) // If we have a width and height, use them.
window_size = "size=[width]x[height];"
update_status(push = 0) // Update the window state.
if (status == NANO_CLOSE)
return // Bail if we should close.
user << browse(get_html(), "window=[window_id];[window_size][list2params(window_options)]") // Open the window.
winset(user, window_id, "on-close=\"nanoclose \ref[src]\"") // Instruct the client to signal NanoUI when the window is closed.
SSnano.ui_opened(src) // Call the opened handler.
/**
* public
*
* Reinitialize the NanoUI.
* (Possibly with a new template and/or data).
*
* optional template string The filename of the new template.
* optional data list The new initial data.
**/
/datum/nanoui/proc/reinitialize(template, list/data)
if(template)
src.template = template // Set a new template.
if(data)
set_initial_data(data) // Replace the initial_data.
open()
/**
* public
*
* Close the NanoUI, and all its children.
**/
/datum/nanoui/proc/close()
set_auto_update(0) // Disable auto-updates.
user << browse(null, "window=[window_id]") // Close the window.
SSnano.ui_closed(src) // Call the closed handler.
for(var/datum/nanoui/child in children) // Loop through and close all children.
child.close()
/**
* private
*
* Push data to an already open NanoUI.
*
* required data list The data to send.
* optional force bool If the update should be sent regardless of state.
**/
/datum/nanoui/proc/push_data(data, force = 0)
update_status(push = 0) // Update the window state.
if (status == NANO_DISABLED && !force)
return // Cannot update UI, we have no visibility.
var/list/send_data = get_send_data(data) // Get the data to send.
// Send the new data to the recieveUpdate() Javascript function.
user << output(list2params(list(JSON.stringify(send_data))), "[window_id].browser:receiveUpdate")
/**
* private
*
* Handle clicks from the NanoUI.
* Call the src_object's Topic() if status is NANO_INTERACTIVE.
* If the src_object's Topic() returns 1, update all UIs attacked to it.
**/
/datum/nanoui/Topic(href, href_list)
update_status(push = 0) // Update the window state.
if (status != NANO_INTERACTIVE || user != usr)
return // If UI is not interactive or usr calling Topic is not the UI user.
var/action = href_list["nano"] // Pull the action out.
href_list -= "nano"
var/update = src_object.ui_act(action, href_list, state) // Call Topic() on the src_object.
if (src_object && update)
SSnano.update_uis(src_object) // If we have a src_object and its Topic() told us to update.
/**
* private
*
* Update the NanoUI. Only updates the contents/layout if update is true,
* otherwise only updates the status.
*
* optional force bool If the UI should be forced to update.
**/
/datum/nanoui/process(force = 0)
if (!src_object || !user) // If the object or user died (or something else), abort.
close()
return
if (status && (force || auto_update))
update() // Update the UI if the status and update settings allow it.
else
update_status(push = 1) // Otherwise only update status.
/**
* private
*
* Updates the UI by interacting with the src_object again, which will hopefully
* call try_ui_update on it.
*
* optional force_open bool If force_open should be passed to ui_interact.
**/
/datum/nanoui/proc/update(force_open = 0)
src_object.ui_interact(user, ui_key, src, force_open, master_ui, state)
/**
* private
*
* Set the status/visibility of the NanoUI.
*
* required state int The status to set (NANO_CLOSE/NANO_DISABLED/NANO_UPDATE/NANO_INTERACTIVE).
* optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED).
**/
/datum/nanoui/proc/set_status(state, push = 0)
if (state != status) // Only update if status has changed.
if (status == NANO_DISABLED)
status = state
if (push)
update()
else
status = state
if (push || status == 0) // Force an update if NANO_DISABLED.
push_data(null, 1)
/**
* private
*
* Update the status/visibility of the NanoUI for its user.
*
* optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED).
**/
/datum/nanoui/proc/update_status(push = 0)
var/new_status = src_object.CanUseTopic(user, state)
if(master_ui)
new_status = min(new_status, master_ui.status)
set_status(new_status, push)
if(new_status == NANO_CLOSE)
close()
/**
* NanoUI
*
* Contains the NanoUI datum, and its procs.
*
* /tg/station user interface library
* thanks to baystation12
*
* modified by neersighted
**/
/**
* NanoUI datum:
*
* Represents a NanoUI.
**/
/datum/nanoui
var/mob/user // The mob who opened/is using the NanoUI.
var/atom/movable/src_object // The object which owns the NanoUI.
var/title // The title of te NanoUI.
var/ui_key // The ui_key of the NanoUI. This allows multiple UIs for one src_object.
var/window_id // The window_id for browse() and onclose().
var/width = 0 // The window width.
var/height = 0 // The window height
var/window_options = list( // Extra options to winset().
"focus" = 0,
"titlebar" = 1,
"can_resize" = 1,
"can_minimize" = 1,
"can_maximize" = 0,
"can_close" = 1,
"auto_format" = 0
)
var/atom/ref = null // An extra ref to call when the window is closed.
var/layout = "nanotrasen" // The layout to be used for this UI.
var/template // The template to be used for this UI.
var/auto_update = 1 // Update the NanoUI every MC tick.
var/list/initial_data // The data (and datastructure) used to initialize the NanoUI
var/status = NANO_INTERACTIVE // The status/visibility of the NanoUI.
var/datum/topic_state/state = null // Topic state used to determine status. Topic states are in interactions/.
var/datum/nanoui/master_ui // The parent NanoUI.
var/list/datum/nanoui/children = list() // Children of this NanoUI.
/**
* public
*
* Create a new NanoUI.
*
* required user mob The mob who opened/is using the NanoUI.
* required src_object atom/movable The object which owns the NanoUI.
* required ui_key string The ui_key of the NanoUI.
* required template string The template to render the NanoUI content with.
* optional title string The title of the NanoUI.
* optional width int The window width.
* optional height int The window height.
* optional ref atom An extra ref to use when the window is closed.
* optional master_ui datum/nanoui The parent NanoUI.
* optional state datum/topic_state The state used to determine status.
*
* return datum/nanoui The requested NanoUI.
**/
/datum/nanoui/New(mob/user, atom/movable/src_object, ui_key, template, \
title = 0, width = 0, height = 0, \
atom/ref = null, datum/nanoui/master_ui = null, \
datum/topic_state/state = default_state)
src.user = user
src.src_object = src_object
src.ui_key = ui_key
src.window_id = "\ref[src_object]-[ui_key]"
set_template(template)
if (title)
src.title = sanitize(title)
if (width)
src.width = width
if (height)
src.height = height
if (ref)
src.ref = ref
src.master_ui = master_ui
if(master_ui)
master_ui.children += src
src.state = state
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/nanoui)
assets.send(user)
/**
* public
*
* Open this NanoUI (and initialize it with data).
*
* optional data list The data to intialize the UI with.
**/
/datum/nanoui/proc/open(list/data = null)
if(!user.client)
return // Bail if there is no client.
if (status == NANO_CLOSE)
return // Bail if we should close.
if (!initial_data)
if (!data) // If we don't have initial_data and data was not passed, get data from the src_object.
data = src_object.get_ui_data(user)
set_initial_data(data) // Otherwise use the passed data.
var/window_size = ""
if (width && height) // If we have a width and height, use them.
window_size = "size=[width]x[height];"
update_status(push = 0) // Update the window state.
user << browse(get_html(), "window=[window_id];[window_size][list2params(window_options)]") // Open the window.
winset(user, window_id, "on-close=\"nanoclose \ref[src]\"") // Instruct the client to signal NanoUI when the window is closed.
SSnano.on_open(src)
/**
* public
*
* Reinitialize the NanoUI.
* (Possibly with a new template and/or data).
*
* optional template string The filename of the new template.
* optional data list The new initial data.
**/
/datum/nanoui/proc/reinitialize(template, list/data)
if(template)
set_template(template) // Set a new template.
if(data)
set_initial_data(data) // Replace the initial_data.
open()
/**
* public
*
* Close the NanoUI, and all its children.
**/
/datum/nanoui/proc/close()
set_auto_update(0) // Disable auto-updates.
user << browse(null, "window=[window_id]") // Close the window.
SSnano.on_close(src)
for(var/datum/nanoui/child in children) // Loop through and close all children.
child.close()
/**
* public
*
* Sets the browse() window options for this NanoUI.
*
* required window_options list The window options to set.
**/
/datum/nanoui/proc/set_window_options(list/window_options)
src.window_options = window_options
/**
* public
*
* Set the layout for this NanoUI.
* This loads custom layout styles and templates for this NanoUI.
*
* required layout string The new UI layout.
**/
/datum/nanoui/proc/set_layout(layout)
src.layout = lowertext(layout)
/**
* public
*
* Set the template for this NanoUI.
*
* required template string The new UI template.
**/
/datum/nanoui/proc/set_template(template)
src.template = lowertext(template)
/**
* public
*
* Enable/disable auto-updating of the NanoUI.
*
* required state bool Enable/disable auto-updating.
**/
/datum/nanoui/proc/set_auto_update(state = 1)
auto_update = state
/**
* private
*
* Set the data to initialize the NanoUI with.
* The datastructure cannot be changed by subsequent updates.
*
* optional data list The data/datastructure to initialize the NanoUI with.
**/
/datum/nanoui/proc/set_initial_data(list/data)
initial_data = data
/**
* private
*
* Generate HTML for this NanoUI.
*
* return string NanoUI HTML output.
**/
/datum/nanoui/proc/get_html()
// Generate <script> and <link> tags.
var/script_html = {"
<script type='text/javascript' src='nanoui.js'></script>
"}
var/stylesheet_html = {"
<link rel='stylesheet' type='text/css' href='http://css-spinners.com/css/spinner/hexdots.css' />
<link rel='stylesheet' type='text/css' href='nanoui.css' />
<link rel='stylesheet' type='text/css' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css' />
"}
// Generate JSON.
var/list/send_data = get_send_data(initial_data)
var/initial_data_json = replacetext(JSON.stringify(send_data), "'", "&apos;")
// Generate the HTML document.
return {"
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<script type='text/javascript'>
function receiveUpdate(dataString) {
if (typeof NanoBus !== 'undefined') {
NanoBus.emit('serverUpdate', dataString);
}
};
</script>
[stylesheet_html]
[script_html]
</head>
<body class='[layout]'>
<div id='data' data-initial='[initial_data_json]'></div>
<div id='layout'>
<div class='hexdots-loader' style='position:fixed;top:50%;left:50%;transform:translate(-50%, -50%);-ms-transform:translate(-50%, -50%);'>
Sending Resources...
</div>
</div>
<noscript>
<h1>Javascript Required</h1>
<p>Javascript is required in order to use this NanoUI interface.</p>
<p>Please enable Javascript in Internet Explorer, and restart your game.</p>
</noscript>
</body>
</html>
"}
/**
* private
*
* Get the config data/datastructure to initialize the NanoUI with.
*
* return list The config data.
**/
/datum/nanoui/proc/get_config_data()
var/list/config_data = list(
"title" = title,
"status" = status,
"ref" = "\ref[src]",
"window" = list(
"width" = width,
"height" = height,
"ref" = window_id
),
"user" = list(
"name" = user.name,
"fancy" = user.client.prefs.nanoui_fancy,
"ref" = "\ref[user]"
),
"srcObject" = list(
"name" = src_object.name,
"ref" = "\ref[src_object]"
),
"templates" = list(
"layout" = "_[layout]",
"content" = "[template]"
)
)
return config_data
/**
* private
*
* Package the data to send to the UI.
* This is the (regular) data and config data, bundled together.
*
* return list The packaged data.
**/
/datum/nanoui/proc/get_send_data(list/data)
var/list/send_data = list()
send_data["config"] = get_config_data()
if (!isnull(data))
send_data["data"] = data
return send_data
/**
* private
*
* Handle clicks from the NanoUI.
* Call the src_object's Topic() if status is NANO_INTERACTIVE.
* If the src_object's Topic() returns 1, update all UIs attacked to it.
**/
/datum/nanoui/Topic(href, href_list)
update_status(push = 0) // Update the window state.
if (status != NANO_INTERACTIVE || user != usr)
return // If UI is not interactive or usr calling Topic is not the UI user.
var/action = href_list["nano"] // Pull the action out.
href_list -= "nano"
var/update = src_object.ui_act(action, href_list, state) // Call Topic() on the src_object.
if (src_object && update)
SSnano.update_uis(src_object) // If we have a src_object and its Topic() told us to update.
/**
* private
*
* Update the NanoUI. Only updates the contents/layout if update is true,
* otherwise only updates the status.
*
* optional force bool If the UI should be forced to update.
**/
/datum/nanoui/process(force = 0)
if (!src_object || !user) // If the object or user died (or something else), abort.
close()
return
if (status && (force || auto_update))
update() // Update the UI if the status and update settings allow it.
else
update_status(push = 1) // Otherwise only update status.
/**
* private
*
* Push data to an already open NanoUI.
*
* required data list The data to send.
* optional force bool If the update should be sent regardless of state.
**/
/datum/nanoui/proc/push_data(data, force = 0)
update_status(push = 0) // Update the window state.
if (status == NANO_DISABLED && !force)
return // Cannot update UI, we have no visibility.
var/list/send_data = get_send_data(data) // Get the data to send.
// Send the new data to the recieveUpdate() Javascript function.
user << output(list2params(list(JSON.stringify(send_data))), "[window_id].browser:receiveUpdate")
/**
* private
*
* Updates the UI by interacting with the src_object again, which will hopefully
* call try_ui_update on it.
*
* optional force_open bool If force_open should be passed to ui_interact.
**/
/datum/nanoui/proc/update(force_open = 0)
src_object.ui_interact(user, ui_key, src, force_open, master_ui, state)
/**
* private
*
* Update the status/visibility of the NanoUI for its user.
*
* optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED).
**/
/datum/nanoui/proc/update_status(push = 0)
var/new_status = src_object.nano_state(user, state)
if(master_ui)
new_status = min(new_status, master_ui.status)
set_status(new_status, push)
if(new_status == NANO_CLOSE)
close()
/**
* private
*
* Set the status/visibility of the NanoUI.
*
* required state int The status to set (NANO_CLOSE/NANO_DISABLED/NANO_UPDATE/NANO_INTERACTIVE).
* optional push bool Push an update to the UI (an update is always sent for NANO_DISABLED).
**/
/datum/nanoui/proc/set_status(state, push = 0)
if (state != status) // Only update if status has changed.
if (status == NANO_DISABLED)
status = state
if (push)
update()
else
status = state
if (push || status == 0) // Force an update if NANO_DISABLED.
push_data(null, 1)
+1 -1
View File
@@ -14,7 +14,7 @@
*
* return NANO_state The state of the UI.
**/
/atom/proc/CanUseTopic(mob/user, datum/topic_state/state)
/atom/proc/nano_state(mob/user, datum/topic_state/state)
var/src_object = nano_host()
return state.can_use_topic(src_object, user) // Check if the state allows interaction.
+4 -4
View File
@@ -158,7 +158,7 @@
*
* required ui datum/nanoui The UI to be added.
**/
/datum/subsystem/nano/proc/ui_opened(datum/nanoui/ui)
/datum/subsystem/nano/proc/on_open(datum/nanoui/ui)
var/src_object_key = "\ref[ui.src_object]"
if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
open_uis[src_object_key] = list(ui.ui_key = list()) // Make a list for the ui_key and src_object.
@@ -180,7 +180,7 @@
*
* return bool If the UI was removed or not.
**/
/datum/subsystem/nano/proc/ui_closed(datum/nanoui/ui)
/datum/subsystem/nano/proc/on_close(datum/nanoui/ui)
var/src_object_key = "\ref[ui.src_object]"
if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list))
return 0 // It wasn't open.
@@ -203,7 +203,7 @@
*
* return int The number of NanoUIs closed.
**/
/datum/subsystem/nano/proc/user_logout(mob/user)
/datum/subsystem/nano/proc/on_logout(mob/user)
return close_user_uis(user)
/**
@@ -216,7 +216,7 @@
*
* return bool If the NanoUIs were transferred.
**/
/datum/subsystem/nano/proc/user_transferred(mob/oldMob, mob/newMob)
/datum/subsystem/nano/proc/on_transfer(mob/oldMob, mob/newMob)
if (!oldMob || isnull(oldMob.open_uis) || !istype(oldMob.open_uis, /list) || open_uis.len == 0)
return 0 // The old mob had no open NanoUIs.