code:
<!-- TOC depthFrom:1 depthTo:6 withLinks:1 updateOnSave:1 orderedList:0 -->

- [tgui](#tgui)
	- [Concepts](#concepts)
	- [Using It](#using-it)
	- [Copypasta](#copypasta)

<!-- /TOC -->

# tgui
tgui is the user interface library of /tg/station. It is rendered clientside,  based on JSON data sent from the server. Clicks are processed on the server, in  a similar method to native BYOND `Topic()`.

Basic tgui consists of defining a few procs. In these procs you will handle a request to open or update a UI (typically by updating a UI if it exists or setting up and opening it if it does not), a request for data, in which you build a list to be passed as JSON to the UI, and an action handler, which handles any user input. In addition, you will write a HTML template file which renders your data and provides actionable inputs.

tgui is very different from most UIs you will encounter in BYOND programming, and is heavily reliant of Javascript and web technologies as opposed to DM. However, if you are familiar with NanoUI (a library which can be found on almost every other SS13 codebase), tgui should be fairly easy to pick up.

tgui is a fork of NanoUI. The server-side code (DM) is similar and derived from NanoUI, while the clientside is a wholly new project with no code in common.

## Concepts
tgui is loosely based a MVVM architecture. MVVM stands for model, view, view model.
- A model is the object that a UI represents. This is the atom a UI corresponds to in the game world in most cases, and is known as the `src_object` in tgui.
- The view model is how data is represented in terms of the view. In tgui, this is the `ui_data` proc which munges whatever complex data your `src_object` has into a list.
- The view is how the data is rendered. This is the template, a HTML (plus mustaches and other goodies) file which is compiled into the tgui blob that the browser executes.

Not included in the MVVM model are other important concepts:
- The action/topic handler, `ui_act`, is what recieves input from the user and acts on it.
- The request/update proc, `ui_interact` is where you open your UI and set options like title, size, autoupdate, theme, and more.
- Finally, `ui_state`s (set in `ui_interact`) dictate under what conditions a UI may be interacted with. This may be the standard checks that check if you are in range and conscious, or more.

States are easy to write and extend, and what make tgui interactions so powerful. Because states can over overridden from other procs, you can build powerful interactions for embedded objects or remote access.

## Using It
All these examples and abstracts sound great, you might say. But you also might say, "How do I use it?"

Examples can be as simple or as complex as you would like. Let's start with a very basic hello world.

```DM
/obj/machinery/my_machine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state)
  ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
  if(!ui)
    ui = new(user, src, ui_key, "my_machine", name, 300, 300, master_ui, state)
    ui.open()
```

This is the proc that defines our interface. There's a bit going on here, so let's break it down. First, we override the ui_interact proc on our object. This will be called by `interact` for you, which is in turn called by `attack_hand` (or `attack_self` for items). `ui_interact` is also called to update a UI (hence the `try_update_ui`), so we accept an existing UI to update. The `state` is a default argument so that a caller can overload it with named arguments (`ui_interact(state = overloaded_state)`) if needed.

Inside the `if(!ui)` block (which means we are creating a new UI), we choose our template, title, and size; we can also set various options like `style` (for themes), or autoupdate. These options will be elaborated on later (as will `ui_state`s).

After `ui_interact`, we need to define `ui_data`. This just returns a list of data for our object to use. Let's imagine our object has a few vars:

```DM
/obj/machinery/my_machine/ui_data(mob/user)
  var/list/data = list()
  data["health"] = health
  data["color"] = color

  return data
```

The `ui_data` proc is what people often find the hardest about tgui, but its really quite simple! You just need to represent your object as numbers, strings, and lists, instead of atoms and datums.

Finally, the `ui_act` proc is called by the interface whenever the user used an input. The input's `action` and `params` are passed to the proc.

```DM
/obj/machinery/my_machine/ui_act(action, params)
  if(..())
    return
  switch(action)
    if("change_color")
      var/new_color = params["color"]
      if(!(color in allowed_coors))
        return
      color = new_color
      . = TRUE
  update_icon()
```

The `..()` (parent call) is very important here, as it is how we check that the user is allowed to use this interface (to avoid so-called href exploits). It is also very important to clamp and sanitize all input here. Always assume the user is attempting to exploit the game.

Also note the use of `. = TRUE` (or `FALSE`), which is used to notify the UI that this input caused an update. This is especially important for UIs that do not auto-update, as otherwise the user will never see their change.

Finally, you have a template. This is also a source of confusion for many new users. Some basic HTML knowledge will get you a long way, however.

A template is regular HTML, with mustache for logic and built-in components to quickly build UIs. Here's how we might show some data (components will be elaborated on later).

In a template there are a few special values. `config` is always the same and is part of core tgui (it will be explained later), `data` is the data returned from `ui_data`, and `adata` is the same, but with certain values (numbers at this time) interpolated in order to allow animation.

```html
<ui-display>
  <ui-section label='Health'>
    <span>{{data.health}}</span>
  </ui-section>
  <ui-section label='Color'>
    <span>{{data.color}}</span>
  </ui-section>
</ui-display>
```

Templates can be very confusing at first, as ternary operators, computed properties, and iterators are used quite a bit in more complex interfaces. Start with the basics, and work your way up. Much of the complexity stems from performance concerns. If in doubt, take the simpler approach and refactor if performance becomes an issue.

## Copypasta
We all do it, even the best of us. If you just want to make a tgui **fast**, here's what you need (note that you'll probably be forced to clean your shit up upon code review):

```DM
/obj/copypasta/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = default_state) // Remember to use the appropriate state.
  ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
  if(!ui)
    ui = new(user, src, ui_key, "copypasta", name, 300, 300, master_ui, state)
    ui.open()

/obj/copypasta/ui_data(mob/user)
  var/list/data = list()
  data["var"] = var

  return data

/obj/copypasta/ui_act(action, params)
  if(..())
    return
  switch(action)
    if("copypasta")
      var/newvar = params["var"]
      var = Clamp(newvar, min_val, max_val) // Just a demo of proper input sanitation.
      . = TRUE
  update_icon() // Not applicable to all objects.
```

And the template:

```html
<ui-display title='My Copypasta Section'>
  <ui-section label='Var'>
    <span>{{data.var}}</span>
  </ui-section>
  <ui-section label='Animated Var'>
    <span>{{adata.var}}</span>
  </ui-section>
</ui-display>
```

license: mit


code:
@echo off
echo node.js and all dependencies must be installed for this script to work.
echo If this script fails try installing dependencies again.
REM Build minified assets
cmd /c gulp --min
pause


code:
import * as f from './flags'
import { gulp as g, postcss as s } from './plugins'

const entry = 'tgui.styl'

import gulp from 'gulp'
export function css () {
  return gulp.src(`${f.src}/${entry}`)
    .pipe(g.if(f.debug, g.sourcemaps.init({loadMaps: true})))
    .pipe(g.stylus({
      url: 'data-url',
      paths: [ f.src ]
    }))
    .pipe(g.postcss([
      s.autoprefixer({ browsers: ['last 2 versions', 'ie >= 8'] }),
      s.gradient,
      s.opacity,
      s.rgba({oldie: true}),
      s.plsfilters({oldIE: true}),
      s.fontweights
    ]))
    .pipe(g.bytediff.start())
    .pipe(g.if(f.min, g.cssnano({autoprefixer: false})))
    .pipe(g.if(f.debug, g.sourcemaps.write()))
    .pipe(g.bytediff.stop())
    .pipe(gulp.dest(f.dest))
}
export function watch_css () {
  gulp.watch(`${f.src}/**/*.styl`, css)
  return css()
}


code:
const flags = require('minimist')(process.argv.slice(2))

export const src = flags.src || 'src'
export const dest = flags.dest || 'assets'

export const debug = flags.debug || flags.d
export const min = flags.min || flags.m


code:
import * as f from './flags'
import { browserify as b, gulp as g } from './plugins'

const entry = 'tgui.js'

import { transform as babel } from 'babel-core'
import { readFileSync as read } from 'fs'
b.componentify.compilers['text/javascript'] = function (source, file) {
  const config = { sourceMaps: true }
  Object.assign(config, JSON.parse(read(`${f.src}/.babelrc`, 'utf8')))
  const compiled = babel(source, config)

  return { source: compiled.code, map: compiled.map }
}
import { render as stylus } from 'stylus'
b.componentify.compilers['text/stylus'] = function (source, file) {
  const config = { filename: file }
  const compiled = stylus(source, config)

  return { source: compiled }
}

import browserify from 'browserify'
const bundle = browserify(`${f.src}/${entry}`, {
  debug: f.debug,
  cache: {},
  packageCache: {},
  extensions: [ '.js', '.ract' ],
  paths: [ f.src ]
})
if (f.min) bundle.plugin(b.collapse)
bundle
  .transform(b.babelify)
  .plugin(b.helpers)
  .transform(b.componentify)
  .transform(b.globify)
  .transform(b.es3ify)

import buffer from 'vinyl-buffer'
import gulp from 'gulp'
import source from 'vinyl-source-stream'
export function js () {
  return bundle.bundle()
    .pipe(source(entry))
    .pipe(buffer())
    .pipe(g.if(f.debug, g.sourcemaps.init({loadMaps: true})))
    .pipe(g.bytediff.start())
    .pipe(g.if(f.min, g.uglify({mangle: true, compress: {unsafe: true}})))
    .pipe(g.if(f.debug, g.sourcemaps.write()))
    .pipe(g.bytediff.stop())
    .pipe(gulp.dest(f.dest))
}
import gulplog from 'gulplog'
export function watch_js () {
  bundle.plugin(b.watchify)
  bundle.on('update', js)
  bundle.on('error', err => {
    gulplog.error(err.toString())
    this.emit('end')
  })
  return js()
}


code:
export const browserify = {
  babelify: require('babelify'),
  collapse: require('bundle-collapser/plugin'),
  componentify: require('ractive-componentify'),
  es3ify: require('es3ify'),
  globify: require('require-globify'),
  helpers: require('babelify-external-helpers'),
  watchify: require('watchify')
}

export const gulp = require('gulp-load-plugins')({ replaceString: /^gulp(-|\.)|-/g })

export const postcss = {
  autoprefixer: require('autoprefixer'),
  fontweights: require('postcss-font-weights'),
  gradient: require('postcss-filter-gradient'),
  opacity: require('postcss-opacity'),
  plsfilters: require('pleeease-filters'),
  rgba: require('postcss-color-rgba-fallback')
}


code:
const out = 'assets'

import { exec } from 'child_process'
export function reload () {
  return exec('reload.bat')
}
import gulp from 'gulp'
export function watch_reload () {
  gulp.watch(`${out}/**`, reload)
}


code:
import { gulp as g } from './plugins'

const out = 'assets'

import gulp from 'gulp'
export function size () {
  return gulp.src(`${out}/**`)
    .pipe(g.size())
}


code:
import gulp from 'gulp'

import { css, watch_css } from './gulp/css'
import { js, watch_js } from './gulp/js'
import { reload, watch_reload } from './gulp/reload'
import { size } from './gulp/size'

gulp.task(reload)
gulp.task(size)

gulp.task('default', gulp.series(gulp.parallel(css, js), size))
gulp.task('watch', gulp.parallel(watch_css, watch_js, watch_reload))


code:
@echo off
echo node.js 5.3.0 or newer must be installed for this script to work.
echo If this script fails, try closing editors and running it again first.
echo Any warnings about optional dependencies can be safely ignored.
pause
REM Install Gulp
cmd /c npm install gulp-cli -g
REM Install tgui dependencies
cmd /c npm install
REM Flatten dependency tree
cmd /c npm dedupe
REM Clean dependency tree
cmd /c npm prune
pause


code:
@echo off
REM Get the documents folder from the registry.
for /f "tokens=3* delims= " %%a in (
    'reg query "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders" /v "Personal"'
) do (
    set documents=%%a
)
REM Copy assets to the BYOND cache
cmd /c copy assets\* "%documents%\BYOND\cache" /y


code:
context = selector()

.bar
  display: inline-block
  position: relative
  vertical-align: middle
  width: 100%
  height: 20px
  line-height: @height - 3px
  padding: 1px

  border: 1px solid bar-color-border
  background: bar-color-background

  .barText
    @extend {context} $fontReset
    position: absolute
    top: 0
    right: 3px

  .barFill
    display: block
    height: 100%

    transition: background-color 1s
    background-color: bar-color-normal
    &.good
      background-color: bar-color-good
    &.average
      background-color: bar-color-average
    &.bad
      background-color: bar-color-bad


code:
context = selector()

buttoncolor(selector, color)
  &.{selector}
    transition: background-color 0.5s
    background-color: color
  &.{selector}.active:hover,
  &.{selector}.active:focus
    transition: background-color 0.25s
    background-color: lighten(color, button-lighten-hover)
    outline: 0

span.button
  @extend {context} $fontReset
  display: inline-block
  vertical-align: middle
  height: 20px
  line-height: @height - 3px
  padding: 0 5px
  white-space: nowrap

  border: 1px solid button-color-border

  .fa
    padding-right: 2px

  buttoncolor(normal, button-color-normal)
  buttoncolor(disabled, button-color-disabled)
  buttoncolor(selected, button-color-selected)
  buttoncolor(caution, button-color-caution)
  buttoncolor(danger, button-color-danger)

  &.gridable
    width: 125px
    margin: 2px 0

span:not(.button) + span.button
  margin-left: 5px

span.button + span:not(.button)
  margin-left: 5px


code:
  {{#if title}}

{{title}}

      {{#if button}}

{{yield button}}

      {{/if}}

  {{/if}}

    {{yield}}


code:
div.display
  width: 100%
  padding: 4px
  margin: 6px 0

  background-color: display-color-background // Transparent background.
  box-shadow: inset 0 0 5px display-color-shadow

  header
    display: block
    position: relative
    width: 100%
    padding: 0 4px
    margin-bottom: 6px

    color: display-color-title

    border-bottom: rule-size solid rule-color-normal

    .buttonRight
      position: absolute
      bottom: 6px
      right: 4px

  article
    display: table
    width: 100%

    border-collapse: collapse;


code:
<script>
component.exports = {
  oninit () {
    this.on('clear', () => {
      this.set('value', '')
      this.find('input').focus()
    })
  }
}
</script>

<input type='text' value='{{value}}' placeholder='{{placeholder}}'/>
<ui-button icon='refresh' on-press='clear'/>


code:
input
  display: inline-block
  vertical-align: middle
  height: 20px
  line-height: @height - 3px
  padding: 0 5px
  white-space: nowrap

  color: input-color-text
  background-color: input-color-background
  border: 1px solid input-color-border

  &::placeholder
    color: input-color-placeholder
  &::-ms-clear
    display: none


code:
<script>
  component.exports = {
    data: {
      graph: require('paths-js/smooth-line'),
      xaccessor: point => point.x,
      yaccessor: point => point.y
    },
    computed: {
      size () {
        const points = this.get('points')
        return points[0].length
      },
      scale () {
        const points = this.get('points')
        return Math.max(...Array.map(points, a => Math.max(...Array.map(a, p => p.y))))
      },
      xaxis () {
        const xinc = this.get('xinc')
        const size = this.get('size')
        return Array.from(Array(size).keys()).filter(num => num && num % xinc == 0)
      },
      yaxis () {
        const yinc = this.get('yinc')
        const scale = this.get('scale')
        return Array.from(Array(yinc).keys()).map(num => Math.round((scale * (++num / 100)) * 10))
      }
    },
    oninit () {
      this.on({
        enter (event) {
          this.set('selected', event.index.count)
        },
        exit (event) {
          this.set('selected')
        }
      })
      window.addEventListener('resize', (event) => {
        this.set('width', this.el.clientWidth)
      })
    },
    onrender () {
      this.set('width', this.el.clientWidth)
    }
  }
</script>

<svg class='linegraph' width='100%' height='{{height + 10}}'>
  <g transform='translate(0, 5)'>
    {{#graph({data: points, xaccessor: xaccessor, yaccessor: yaccessor, width: width, height: height})}}
      {{#each xaxis}}
        <line x1='{{xscale(.)}}' x2='{{xscale(.)}}' y1='0' y2='{{height}}' stroke='darkgray'/>
        {{#if @index % 2 == 0}}
          <text x='{{xscale(.)}}' y='{{height - 5}}' text-anchor='middle' fill='white'>{{(size - .) * xfactor}} {{xunit}}</text>
        {{/if}}
      {{/each}}
      {{#each yaxis}}
        <line x1='0' x2='{{width}}' y1='{{yscale(.)}}' y2='{{yscale(.)}}' stroke='darkgray'/>
        <text x='0' y='{{yscale(.) - 5}}' text-anchor='begin' fill='white'>{{. * yfactor}} {{yunit}}</text>
      {{/each}}
      {{#each curves:curve}}
        <path d='{{area.path.print()}}' fill='{{colors[curve]}}' opacity='0.1'/>
      {{/each}}
      {{#each curves:curve}}
        <path d='{{line.path.print()}}' stroke='{{colors[curve]}}' fill='none'/>
      {{/each}}
      {{#each curves:curve}}
        {{#each line.path.points():count}}
          <circle transform='translate({{.}})' r='{{selected == count ? 10 : 4}}' fill='{{colors[curve]}}' on-mouseenter='enter' on-mouseleave='exit'/>
        {{/each}}
      {{/each}}
      {{#each curves:curve}}
        {{#each line.path.points():count}}
          {{#if selected == count }}
            <text transform='translate({{.}}) {{count <= size / 2 ? "translate(15, 4)" : "translate(-15, 4)"}}' text-anchor='{{count <= size / 2 ? "start" : "end"}}' fill='white'>
              {{item[count].y * yfactor}} {{yunit}} @ {{(size - item[count].x) * xfactor}} {{xunit}}
            </text>
          {{/if}}
        {{/each}}
      {{/each}}
      {{#each curves:curve}}
        <g transform='translate({{(width / (curves.length + 1)) * (@index + 1)}}, 10)'>
          <circle r='4' fill='{{colors[curve]}}'/>
          <text x='8' y='4' fill='white'>{{legend[curve]}}</text>
        </g>
      {{/each}}
    {{/graph}}
  </g>
</svg>


code:
div.notice
  margin: 8px 0
  padding: 4px

  box-shadow: none

  color: text-color-inverse
  font-weight: bold
  font-style: italic

  background-color: notice-color-first
  background-image: repeating-linear-gradient(
    -45deg,
    notice-color-first,
    notice-color-first 10px,
    notice-color-second 10px,
    notice-color-second 20px
  )

  .label
    color: text-color-inverse

  .content:only-of-type
    padding: 0

  hr
    background-color: rule-color-dark


code:
div.resize
  position: fixed
  bottom: 0
  right: 0
  width: 0
  height: 0

  border-style: solid;
  border-width: 0 0 45px 45px;
  border-color: transparent transparent resize-color transparent

  transform: rotate(360deg)


code:
  {{#if label}}
    {{label}}:
  {{/if}}
  {{#if nowrap}}
    {{yield}}
  {{else}}

      {{yield}}

  {{/if}}


code:
/$cell
  display: table-cell
  margin: 0
  text-align: left
  vertical-align: middle
  padding: 3px 2px

section
  display: table-row
  width: 100%

  &:not(:first-child)
    padding-top: 4px

  &.candystripe:nth-child(even)
    background-color: section-color-candystripe

  .label
    @extend $cell
    width: 1%
    padding-right: 32px
    white-space: nowrap

    color: section-color-label

  .content
    @extend $cell
    &:not(:last-child)
      padding-right: 16px

  .line
    @extend $cell
    width: 100%


code:
  {{#if title}}

{{title}}

      {{#if button}}{{yield button}}{{/if}}

  {{/if}}

    {{yield}}


code:
context = selector()

div.subdisplay
  width: 100%
  margin: 0

  header
    @extend {context} div.display header

  article
    @extend {context} div.display article


code:
<link rel='ractive' href='components/tabs/tab.ract'>

<script>
  component.exports = {
    oninit () {
      this.set('active', this.findComponent('tab').get('name'))
      this.on('switch', event => {
        this.set('active', event.node.textContent.trim()) // Hack but it works...
      })

      this.observe('active', (newkey, oldkey, path) => {
        for (let tab of this.findAllComponents('tab')) {
          tab.set('shown', (tab.get('name') === newkey))
        }
      })
    }
  }
</script>

<header>
  {{#each tabs}}
    <ui-button pane='{{.}}' on-press='switch'>{{.}}</ui-button>
  {{/each}}
</header>
<ui-display>
  {{>content}}
</ui-display>


code:
  {{yield}}
  {{#if config.fancy}}

  {{/if}}


code:
context = selector()

$titleButton
  display: inline-block
  position: relative
  padding: 7px // To make a bigger clickable area.
  margin: -7px

  color: titlebar-color-button;

  &:hover
    color: lighten(titlebar-color-button, button-lighten-hover)

header.titlebar
  position: fixed;
  z-index: 1
  top: 0;
  left: 0;
  width: 100%;
  height: 32px;

  background-color: titlebar-color-background
  border-bottom: 1px solid titlebar-color-coreshadow
  box-shadow: 0 3px 3px titlebar-color-shadow

  .statusicon
    position: absolute
    top: 4px
    left: 12px
    transition: color 0.5s

  .title
    position: absolute
    top: 6px
    left: 46px

    color: titlebar-color-text
    font-size: 16px
    white-space: nowrap

  .minimize
    @extend {context} $titleButton
    position: absolute
    top: 6px
    right: 46px
  .close
    @extend {context} $titleButton
    position: absolute
    top: 4px
    right: 12px

/.no-icons header.titlebar
  .statusicon
    font-size: 20px
    &::after
      content: "O"
  .minimize
    top: -2px
    font-size: 20px
    &::after
      content: "—"
  .close
    font-size: 20px
    &::after
      content: "X"


code:
{{#if config.fancy && ie && ie < 11}}
  <ui-notice>
    You have an old (IE{{ie}}), end-of-life (click 'EOL Info' for more information) version of Internet Explorer installed.

    To upgrade, click 'Upgrade IE' to download IE11 from Microsoft.

    If you are unable to upgrade directly, click 'IE VMs' to download a VM with IE11 or Edge from Microsoft.

    Otherwise, click 'No Frills' below to disable potentially incompatible features (and this message).

    <ui-button icon='close' action='tgui:nofrills'>No Frills</ui-button>
    <ui-button icon='internet-explorer' action='tgui:link' params='{"url": "http://windows.microsoft.com/en-us/internet-explorer/download-ie"}'>
      Upgrade IE</ui-button>
    <ui-button icon='edge' action='tgui:link' params='{"url": "https://dev.windows.com/en-us/microsoft-edge/tools/vms"}'>
      IE VMs</ui-button>
    <ui-button icon='info' action='tgui:link' params='{"url": "https://support.microsoft.com/en-us/lifecycle#gp/Microsoft-Internet-Explorer"}'>
      EOL Info</ui-button>
    <ui-button icon='bug' on-press='debug'>Debug Info</ui-button>
    {{#if debug}}

      Detected: IE{{ie}}

      User Agent: {{userAgent}}
    {{/if}}
  </ui-notice>
{{/if}}


code:
<link rel='ractive' href='./airalarm/scrubbers.ract'>
<link rel='ractive' href='./airalarm/status.ract'>
<link rel='ractive' href='./airalarm/thresholds.ract'>
<link rel='ractive' href='./airalarm/modes.ract'>
<link rel='ractive' href='./airalarm/vents.ract'>

<ui-notice>
  {{#if data.siliconUser}}
    <ui-section label='Interface Lock'>
      <ui-button icon='{{data.locked ? "lock" : "unlock"}}' action='lock'>{{data.locked ? "Engaged" : "Disengaged"}}</ui-button>
    </ui-section>
  {{else}}
    Swipe an ID card to {{data.locked ? "unlock" : "lock"}} this interface.
  {{/if}}
</ui-notice>
<status/>
{{#if !data.locked || data.siliconUser}}
  {{#if config.screen == "home"}}
      <ui-display title='Air Controls'>
        <ui-section>
          <ui-button icon='{{data.atmos_alarm ? "exclamation-triangle" : "exclamation"}}' style='{{data.atmos_alarm ? "caution" : null}}'
              action='{{data.atmos_alarm ? "reset" : "alarm"}}'>Area Atmosphere Alarm</ui-button>
        </ui-section>
        <ui-section>
          <ui-button icon='{{data.mode == 3 ? "exclamation-triangle" : "exclamation"}}' style='{{data.mode == 3 ? "danger" : null}}'
            action='mode' params='{"mode": {{data.mode == 3 ? 1 : 3}}}'>Panic Siphon</ui-button>
        </ui-section>

        <ui-section>
          <ui-button icon='sign-out' action='tgui:view' params='{"screen": "vents"}'>Vent Controls</ui-button>
        </ui-section>
        <ui-section>
          <ui-button icon='filter' action='tgui:view' params='{"screen": "scrubbers"}'>Scrubber Controls</ui-button>
        </ui-section>
        <ui-section>
          <ui-button icon='cog' action='tgui:view' params='{"screen": "modes"}'>Operating Mode</ui-button>
        </ui-section>
        <ui-section>
          <ui-button icon='bar-chart' action='tgui:view' params='{"screen": "thresholds"}'>Alarm Thresholds</ui-button>
        </ui-section>
      </ui-display>
  {{elseif config.screen == "vents"}}
    <vents/>
  {{elseif config.screen == "scrubbers"}}
    <scrubbers/>
  {{elseif config.screen == "modes"}}
    <modes/>
  {{elseif config.screen == "thresholds"}}
    <thresholds/>
  {{/if}}
{{/if}}


code: <ui-button icon='arrow-left' action='tgui:view' params='{"screen": "home"}'>Back</ui-button>


code:
<link rel='ractive' href='./back.ract'>

<ui-display title='Operating Modes' button>
  {{#partial button}}
    <back/>
  {{/partial}}
  {{#each data.modes}}
    <ui-section>
      <ui-button icon='{{selected ? "check-square-o" : "square-o"}}'
        state='{{selected ? danger ? "danger" : "selected" : null}}'
        action='mode' params='{"mode": {{mode}}}'>{{name}}</ui-button>
    </ui-section>
  {{/each}}
</ui-display>


code:
<link rel='ractive' href='./back.ract'>

<ui-display title='Scrubber Controls' button>
  {{#partial button}}
    <back/>
  {{/partial}}
  {{#each data.scrubbers}}
    <ui-subdisplay title='{{long_name}}'>
      <ui-section label='Power'>
        <ui-button icon='{{power ? "power-off" : "close"}}' style='{{power ? "selected" : null}}'
          action='power' params='{"id_tag": "{{id_tag}}", "val": {{+!power}}}'>{{power ? "On" : "Off"}}</ui-button>
      </ui-section>
      <ui-section label='Mode'>
        <ui-button icon='{{scrubbing ? "filter" : "sign-in"}}' style='{{scrubbing ? null : "danger"}}'
          action='scrubbing' params='{"id_tag": "{{id_tag}}", "val": {{+!scrubbing}}}'>{{scrubbing ? "Scrubbing" : "Siphoning"}}</ui-button>
      </ui-section>
      <ui-section label='Range'>
        <ui-button icon='{{widenet ? "expand" : "compress"}}' style='{{widenet ? "selected" : null}}'
          action='widenet' params='{"id_tag": "{{id_tag}}", "val": {{+!widenet}}}'>{{widenet ? "Expanded" : "Normal"}}</ui-button>
      </ui-section>
      <ui-section label='Filters'>
        <ui-button icon='{{filter_co2 ? "check-square-o" : "square-o"}}' style='{{filter_co2 ? "selected" : null}}'
          action='co2_scrub' params='{"id_tag": "{{id_tag}}", "val": {{+!filter_co2}}}'>CO2</ui-button>
        <ui-button icon='{{filter_n2o ? "check-square-o" : "square-o"}}' style='{{filter_n2o ? "selected" : null}}'
          action='n2o_scrub' params='{"id_tag": "{{id_tag}}", "val": {{+!filter_n2o}}}'>N2O</ui-button>
        <ui-button icon='{{filter_toxins ? "check-square-o" : "square-o"}}' style='{{filter_toxins ? "selected" : null}}'
          action='tox_scrub' params='{"id_tag": "{{id_tag}}", "val": {{+!filter_toxins}}}'>Plasma</ui-button>
      </ui-section>
    </ui-subdisplay>
  {{else}}
    Error: No scrubbers connected.
  {{/each}}
</ui-display>


code:
<ui-display title='Air Status'>
  {{#if data.environment_data}}
    {{#each adata.environment_data}}
      <ui-section label='{{name}}'>

  		{{Math.fixed(value, 2)}}{{unit}}

  	</ui-section>
    {{/each}}
    <ui-section label='Local Status'>

  	  {{data.danger_level == 2 ? "Danger (Internals Required)" : data.danger_level == 1 ? "Caution" : "Optimal"}}

    </ui-section>
    <ui-section label='Area Status'>

    	  {{data.atmos_alarm ? "Atmosphere Alarm" : fire_alarm ? "Fire Alarm" : "Nominal"}}

    </ui-section>
  {{else}}
    <ui-section label='Warning'>
      Cannot obtain air sample for analysis.
    </ui-section>
  {{/if}}
  {{#if data.emagged}}
    <ui-section label='Warning'>
      Safety measures offline. Device may exhibit abnormal behavior.
    </ui-section>
  {{/if}}
</ui-display>


code:
<link rel='ractive' href='./back.ract'>

<ui-display title='Alarm Thresholds' button>
  {{#partial button}}
    <back/>
  {{/partial}}

      min2
      min1
      max1
      max2

      {{#each data.thresholds}}

        {{{name}}}
        {{#each settings}}
          <ui-button action='threshold' params='{"env": "{{env}}", "var": "{{val}}"}'>{{Math.fixed(selected, 2)}}</ui-button>
        	{{/each}}

{{/each}}

</ui-display>


code:
<link rel='ractive' href='./back.ract'>

<ui-display title='Vent Controls' button>
  {{#partial button}}
    <back/>
  {{/partial}}
  {{#each data.vents}}
    <ui-subdisplay title='{{long_name}}'>
      <ui-section label='Power'>
        <ui-button icon='{{power ? "power-off" : "close"}}' style='{{power ? "selected" : null}}'
          action='power' params='{"id_tag": "{{id_tag}}", "val": {{+!power}}}'>{{power ? "On" : "Off"}}</ui-button>
      </ui-section>
      <ui-section label='Mode'>
        {{direction == "release" ? "Pressurizing" : "Siphoning"}}
      </ui-section>
      <ui-section label='Pressure Regulator'>
        <ui-button icon='sign-in' style='{{incheck ? "selected" : null}}'
          action='incheck' params='{"id_tag": "{{id_tag}}", "val": {{checks}}}'>Internal</ui-button>
        <ui-button icon='sign-out' style='{{excheck ? "selected" : null}}'
          action='excheck' params='{"id_tag": "{{id_tag}}", "val": {{checks}}}'>External</ui-button>
      </ui-section>
      <ui-section label='Target Pressure'>
        <ui-button icon='pencil' action='set_external_pressure'
          params='{"id_tag": "{{id_tag}}"}'>{{Math.fixed(external)}}</ui-button>
        <ui-button icon='refresh' state='{{extdefault ? "disabled" : null}}' action='reset_external_pressure'
          params='{"id_tag": "{{id_tag}}"}'>Reset</ui-button>
      </ui-section>
    </ui-subdisplay>
  {{else}}
    Error: No vents connected.
  {{/each}}
</ui-display>


code:
<ui-display>
  <ui-section>
    <ui-button icon='{{data.oneAccess ? "unlock" : "lock"}}' action='one_access'>{{data.oneAccess ? "One" : "All"}} Required</ui-button>
    <ui-button icon='refresh' action='clear'>Clear</ui-button>
  </ui-section>

{{#each data.regions}}
				{{name}}
			{{/each}}

{{#each data.regions}}
				{{#each accesses}}
          <ui-button icon='{{req ? "check-square-o" : "square-o"}}' style='{{req ? "selected" : null}}'
            action='set' params='{"access": "{{id}}"}'>{{name}}</ui-button>

				{{/each}}
			{{/each}}

</ui-display>


code:
<ui-notice>
  {{#if data.siliconUser}}
    <ui-section label='Interface Lock'>
      <ui-button icon='{{data.locked ? "lock" : "unlock"}}' action='lock'>{{data.locked ? "Engaged" : "Disengaged"}}</ui-button>
    </ui-section>
  {{else}}
    Swipe an ID card to {{data.locked ? "unlock" : "lock"}} this interface.
  {{/if}}
</ui-notice>
<ui-display title='Power Status'>
  <ui-section label='Main Breaker'>
    {{#if data.locked && !data.siliconUser}}
      {{data.isOperating ? "On" : "Off"}}
    {{else}}
      <ui-button icon='{{data.isOperating ? "power-off" : "close"}}' style='{{data.isOperating ? "selected" : null}}'
        action='breaker'>{{data.isOperating ? "On" : "Off"}}</ui-button>
    {{/if}}
  </ui-section>
  <ui-section label='External Power'>
    {{data.externalPower == 2 ? "Good" : data.externalPower == 1 ? "Low" : "None"}}
  </ui-section>
  <ui-section label='Power Cell'>
    {{#if data.powerCellStatus != null}}
      <ui-bar min='0' max='100' value='{{data.powerCellStatus}}' state='{{powerCellStatusState}}'>{{Math.fixed(adata.powerCellStatus)}}%</ui-bar>
    {{else}}
      Removed
    {{/if}}
  </ui-section>
  {{#if data.powerCellStatus != null}}
    <ui-section label='Charge Mode'>
      {{#if data.locked && !data.siliconUser}}
        {{data.chargeMode ? "Auto" : "Off"}}
      {{else}}
        <ui-button icon='{{data.chargeMode ? "refresh" : "close"}}' style='{{data.chargeMode ? "selected" : null}}'
          action='charge'>{{data.chargeMode ? "Auto" : "Off"}}</ui-button>
      {{/if}}

      [{{data.chargingStatus == 2 ? "Fully Charged" : data.chargingStatus == 1 ? "Charging" : "Not Charging"}}]
    </ui-section>
  {{/if}}
</ui-display>
<ui-display title='Power Channels'>
  {{#each data.powerChannels}}
    <ui-section label='{{title}}' nowrap>

{{Math.round(adata.powerChannels[@index].powerLoad)}} W

= 2 ? "good" : "bad"}}'>{{status >= 2 ? "On" : "Off"}}

[{{status == 1 || status == 3 ? "Auto" : "Manual"}}]

        {{#if !data.locked || data.siliconUser}}
          <ui-button icon='refresh' state='{{status == 1 || status == 3 ? "selected" : null}}'
            action='channel' params='{{topicParams.auto}}'>Auto</ui-button>
          <ui-button icon='power-off' state='{{status == 2 ? "selected" : null}}' action='channel'
            params='{{topicParams.on}}'>On</ui-button>
          <ui-button icon='close' state='{{status == 0 ? "selected" : null}}' action='channel'
            params='{{topicParams.off}}'>Off</ui-button>
        {{/if}}

    </ui-section>
  {{/each}}
  <ui-section label='Total Load'>
    {{Math.round(adata.totalLoad)}} W
  </ui-section>
</ui-display>
{{#if data.siliconUser}}
  <ui-display title='System Overrides'>
    <ui-button icon='lightbulb-o' action='overload'>Overload</ui-button>
    {{#if data.malfStatus}}
      <ui-button icon='{{malfIcon}}' state='{{data.malfStatus == 4 ? "disabled" : null}}' action='{{malfAction}}'>{{malfButton}}</ui-button>
    {{/if}}
  </ui-display>
{{/if}}
<ui-notice>
  <ui-section label='Cover Lock'>
    {{#if data.locked && !data.siliconUser}}
      {{data.coverLocked ? "Engaged" : "Disengaged"}}
    {{else}}
      <ui-button icon='{{data.coverLocked ? "lock" : "unlock"}}' action='cover'>{{data.coverLocked ? "Engaged" : "Disengaged"}}</ui-button>
    {{/if}}
  </ui-section>
</ui-notice>


code:
<ui-display title='Alarms'>

    {{#each data.priority}}

<ui-button icon='close' style='danger' action='clear' params='{"zone": "{{.}}"}'>{{.}}</ui-button>

    {{else}}

No Priority Alerts

    {{/each}}
    {{#each data.minor}}

<ui-button icon='close' style='caution' action='clear' params='{"zone": "{{.}}"}'>{{.}}</ui-button>

    {{else}}

No Minor Alerts

    {{/each}}

</ui-display>


code:
<ui-display title='{{data.tank ? data.sensors[0].long_name : null}}'>
  {{#each adata.sensors}}
    <ui-subdisplay title='{{!data.tank ? long_name : null}}'>
      <ui-section label='Pressure'>
		{{Math.fixed(pressure, 2)}} kPa
      </ui-section>
      {{#if temperature}}
        <ui-section label='Temperature'>
          {{Math.fixed(temperature, 2)}} K
        </ui-section>
      {{/if}}
	  {{#each gases:id}}
        <ui-section label='{{id}}'>
		  {{Math.fixed(., 2)}}%
        </ui-section>
	  {{/each}}
    </ui-subdisplay>
  {{/each}}
</ui-display>
{{#if data.tank}}
  <ui-display title='Controls' button>
  {{#partial button}}
    <ui-button icon='refresh' action='reconnect'>Reconnect</ui-button>
  {{/partial}}
    <ui-section label='Input Injector'>
      <ui-button icon='{{data.inputting ? "power-off" : "close"}}' style='{{data.inputting ? "selected" : null}}' action='input'>
        {{data.inputting ? "Injecting": "Off"}}</ui-button>
    </ui-section>
    <ui-section label='Input Rate'>
      {{Math.fixed(adata.inputRate)}} L/s
    </ui-section>
    <ui-section label='Output Regulator'>
      <ui-button icon='{{data.outputting ? "power-off" : "close"}}' style='{{data.outputting ? "selected" : null}}' action='output'>
        {{data.outputting ? "Open": "Closed"}}</ui-button>
    </ui-section>
    <ui-section label='Output Pressure'>
      <ui-button icon='pencil' action='pressure'>{{Math.round(adata.outputPressure)}} kPa</ui-button>
    </ui-section>
  </ui-display>
{{/if}}


code:
<ui-display>
  <ui-section label='Power'>
    <ui-button icon='{{data.on ? "power-off" : "close"}}' style='{{data.on ? "selected" : null}}'
      action='power'>{{data.on ? "On" : "Off"}}</ui-button>
  </ui-section>
  <ui-section label='Output Pressure'>
    <ui-button icon='pencil' action='pressure' params='{"pressure": "input"}'>Set</ui-button>
    <ui-button icon='plus' state='{{data.pressure == data.max_pressure ? "disabled" : null}}' action='pressure' params='{"pressure": "max"}'>Max</ui-button>
    <span>{{Math.round(adata.pressure)}} kPa</span>
  </ui-section>
  <ui-section label='Filter'>
    <ui-button state='{{data.filter_type == "" ? "selected" : null}}'
      action='filter' params='{"mode": ""}'>Nothing</ui-button>
    <ui-button state='{{data.filter_type == "plasma" ? "selected" : null}}'
      action='filter' params='{"mode": "plasma"}'>Plasma</ui-button>
    <ui-button state='{{data.filter_type == "o2" ? "selected" : null}}'
      action='filter' params='{"mode": "o2"}'>O2</ui-button>
    <ui-button state='{{data.filter_type == "n2" ? "selected" : null}}'
      action='filter' params='{"mode": "n2"}'>N2</ui-button>
    <ui-button state='{{data.filter_type == "co2" ? "selected" : null}}'
      action='filter' params='{"mode": "co2"}'>CO2</ui-button>
    <ui-button state='{{data.filter_type == "n2o" ? "selected" : null}}'
      action='filter' params='{"mode": "n2o"}'>N2O</ui-button>
	</ui-section>
</ui-display>


code:
<ui-display>
  <ui-section label='Power'>
    <ui-button icon='{{data.on ? "power-off" : "close"}}' style='{{data.on ? "selected" : null}}'
      action='power'>{{data.on ? "On" : "Off"}}</ui-button>
  </ui-section>
  <ui-section label='Output Pressure'>
    <ui-button icon='pencil' action='pressure' params='{"pressure": "input"}'>Set</ui-button>
    <ui-button icon='plus' state='{{data.set_pressure == data.max_pressure ? "disabled" : null}}' action='pressure' params='{"pressure": "max"}'>Max</ui-button>
    {{Math.round(adata.set_pressure)}} kPa
  </ui-section>
  <ui-section label='Node 1'>
    <ui-button icon='fast-backward' state='{{data.node1_concentration == 0 ? "disabled" : null}}'
      action='node1' params='{"concentration": -0.1}'/>
    <ui-button icon='backward' state='{{data.node1_concentration == 0 ? "disabled" : null}}'
      action='node1' params='{"concentration": -0.01}'/>
    <ui-button icon='forward' state='{{data.node1_concentration == 100 ? "disabled" : null}}'
      action='node1' params='{"concentration": 0.01}'/>
    <ui-button icon='fast-forward' state='{{data.node1_concentration == 100 ? "disabled" : null}}'
      action='node1' params='{"concentration": 0.1}'/>
    {{Math.round(adata.node1_concentration)}}%
  </ui-section>
  <ui-section label='Node 2'>
    <ui-button icon='fast-backward' state='{{data.node2_concentration == 0 ? "disabled" : null}}'
      action='node2' params='{"concentration": -0.1}'/>
    <ui-button icon='backward' state='{{data.node2_concentration == 0 ? "disabled" : null}}'
      action='node2' params='{"concentration": -0.01}'/>
    <ui-button icon='forward' state='{{data.node2_concentration == 100 ? "disabled" : null}}'
      action='node2' params='{"concentration": 0.01}'/>
    <ui-button icon='fast-forward' state='{{data.node2_concentration == 100 ? "disabled" : null}}'
      action='node2' params='{"concentration": 0.1}'/>
    {{Math.round(adata.node2_concentration)}}%
  </ui-section>
</ui-display>


code:
<ui-display>
  <ui-section label='Power'>
    <ui-button icon='{{data.on ? "power-off" : "close"}}' style='{{data.on ? "selected" : null}}'
      action='power'>{{data.on ? "On" : "Off"}}</ui-button>
  </ui-section>
  {{#if data.max_rate}}
    <ui-section label='Transfer Rate'>
      <ui-button icon='pencil' action='rate' params='{"rate": "input"}'>Set</ui-button>
      <ui-button icon='plus' state='{{data.rate == data.max_rate ? "disabled" : null}}' action='transfer' params='{"rate": "max"}'>Max</ui-button>
      {{Math.round(adata.rate)}} L/s
    </ui-section>
  {{else}}
    <ui-section label='Output Pressure'>
      <ui-button icon='pencil' action='pressure' params='{"pressure": "input"}'>Set</ui-button>
      <ui-button icon='plus' state='{{data.pressure == data.max_pressure ? "disabled" : null}}' action='pressure' params='{"pressure": "max"}'>Max</ui-button>
      {{Math.round(adata.pressure)}} kPa
    </ui-section>
  {{/if}}
</ui-display>


code:
<ui-display title='Cell Timer' button>
  {{#partial button}}
    <ui-button icon='clock-o' style='{{data.timing ? "selected" : null}}' action='{{data.timing ? "stop" : "start"}}'>{{data.timing ? "Stop" : "Start"}}</ui-button>
    <ui-button icon='lightbulb-o' action='flash' style='{{data.flash_charging ? "disabled" : null}}'>{{data.flash_charging ? "Recharging" : "Flash"}}</ui-button>
  {{/partial}}
  <ui-section>
    <ui-button icon='fast-backward' action='time' params='{"adjust": -600}'></ui-button>
    <ui-button icon='backward' action='time' params='{"adjust": -100}'></ui-button>
    <span>{{text.zeroPad(data.minutes, 2)}}:{{text.zeroPad(data.seconds, 2)}}</span>
    <ui-button icon='forward' action='time' params='{"adjust": 100}'></ui-button>
    <ui-button icon='fast-forward' action='time' params='{"adjust": 600}'></ui-button>
  </ui-section>
  <ui-section>
      <ui-button icon='hourglass-start' action='preset' params='{"preset": "short"}'>Short</ui-button>
      <ui-button icon='hourglass-start' action='preset' params='{"preset": "medium"}'>Medium</ui-button>
      <ui-button icon='hourglass-start' action='preset' params='{"preset": "long"}'>Long</ui-button>
  </ui-section>
</ui-display>


code:
<ui-notice>
  The regulator {{data.hasHoldingTank ? "is" : "is not"}} connected to a tank.
</ui-notice>
<ui-display title='Canister' button>
  {{#partial button}}
    <ui-button icon='pencil' action='relabel'>Relabel</ui-button>
  {{/partial}}
  <ui-section label='Pressure'>
    {{Math.round(adata.tankPressure)}} kPa
  </ui-section>
  <ui-section label='Port'>
    {{data.portConnected ? "Connected" : "Not Connected"}}
  </ui-section>
</ui-display>
<ui-display title='Valve'>
  <ui-section label='Release Pressure'>
    <ui-bar min='{{data.minReleasePressure}}' max='{{data.maxReleasePressure}}'
      value='{{data.releasePressure}}'>{{Math.round(adata.releasePressure)}} kPa</ui-bar>
  </ui-section>
  <ui-section label='Pressure Regulator'>
    <ui-button icon='refresh' state='{{data.releasePressure != data.defaultReleasePressure ? null : "disabled"}}'
      action='pressure' params='{"pressure": "reset"}'>Reset</ui-button>
    <ui-button icon='minus' state='{{data.releasePressure > data.minReleasePressure ? null : "disabled"}}'
      action='pressure' params='{"pressure": "min"}'>Min</ui-button>
    <ui-button icon='pencil' action='pressure' params='{"pressure": "input"}'>Set</ui-button>
    <ui-button icon='plus' state='{{data.releasePressure < data.maxReleasePressure ? null : "disabled"}}'
      action='pressure' params='{"pressure": "max"}'>Max</ui-button>
  </ui-section>
  <ui-section label='Valve'>
    <ui-button icon='{{data.valveOpen ? "unlock" : "lock"}}'
      style='{{data.valveOpen ? data.hasHoldingTank ? "caution" : "danger" : null}}'
      action='valve'>{{data.valveOpen ? "Open" : "Closed"}}</ui-button>
  </ui-section>
</ui-display>
<ui-display title='Holding Tank' button>
  {{#partial button}}
    {{#if data.hasHoldingTank}}
      <ui-button icon='eject' style='{{data.valveOpen ? "danger" : null}}' action='eject'>Eject</ui-button>
    {{/if}}
  {{/partial}}
  {{#if data.hasHoldingTank}}
		<ui-section label='Label'>
			{{data.holdingTank.name}}
		</ui-section>
		<ui-section label='Pressure'>
			{{Math.round(adata.holdingTank.tankPressure)}} kPa
		</ui-section>
	{{else}}
		<ui-section>
			No Holding Tank
		</ui-section>
	{{/if}}
</ui-display>


code:
<ui-display title='Cargo'>
  <ui-section label='Shuttle'>
    {{#if data.docked && !data.requestonly}}
      <ui-button action='send'>{{data.location}}</ui-button>
    {{else}}
      {{data.location}}
    {{/if}}
  </ui-section>
  <ui-section label='Credits'>
    {{Math.floor(adata.points)}}
  </ui-section>
  <ui-section label='Centcom Message'>
      {{data.message}}
  </ui-section>
  {{#if data.loan && !data.requestonly}}
    <ui-section label='Loan'>
      {{#if !data.loan_dispatched}}
        <ui-button action='loan'>Loan Shuttle</ui-button>
      {{else}}
        Loaned to Centcom
      {{/if}}
    </ui-section>
  {{/if}}
</ui-display>
{{#if !data.requestonly}}
  <ui-display title='Cart' button>
    {{#partial button}}
      <ui-button icon='close' state='{{data.cart.length ? null : "disabled"}}' action='clear'>Clear</ui-button>
    {{/partial}}
    {{#each data.cart}}
      <ui-section candystripe nowrap>

#{{id}}

{{object}}

{{cost}} Credits

          <ui-button icon='minus' action='remove' params='{"id": "{{id}}"}'/>

      </ui-section>
    {{else}}
      Nothing in Cart
    {{/each}}
  </ui-display>
{{/if}}
<ui-display title='Requests' button>
  {{#partial button}}
    {{#if !data.requestonly}}
      <ui-button icon='close' state='{{data.requests.length ? null : "disabled"}}' action='denyall'>Clear</ui-button>
    {{/if}}
  {{/partial}}
  {{#each data.requests}}
    <ui-section candystripe nowrap>

#{{id}}

{{object}}

{{cost}} Credits

By {{orderer}}

Comment: {{reason}}

      {{#if !data.requestonly}}

          <ui-button icon='check' action='approve' params='{"id": "{{id}}"}'/>
          <ui-button icon='close' action='deny' params='{"id": "{{id}}"}'/>

      {{/if}}
    </ui-section>
    {{else}}
      No Requests
    {{/each}}
</ui-display>
<ui-tabs tabs='{{tabs}}'>
  {{#each data.supplies}}
    <tab name='{{name}}'>
      {{#each packs}}
        <ui-section label='{{name}}' candystripe right>
          <ui-button action='add' params='{"id": "{{id}}"}'>{{cost}} Credits</ui-button>
        </ui-section>
      {{/each}}
    </tab>
  {{/each}}
</ui-tabs>


code:
<ui-display title='Status'>
  <ui-section label='Energy'>
    <ui-bar min='0' max='{{data.maxEnergy}}' value='{{data.energy}}'>{{Math.fixed(adata.energy)}} Units</ui-bar>
  </ui-section>
</ui-display>
<ui-display title='Dispense' button>
  {{#partial button}}
    {{#each data.beakerTransferAmounts}}
      <ui-button icon='plus' state='{{data.amount == . ? "selected" : null}}' action='amount' params='{"target": {{.}}}'>{{.}}</ui-button>
    {{/each}}
  {{/partial}}
  <ui-section>
    {{#each data.chemicals}}
      <ui-button grid icon='tint' action='dispense' params='{"reagent": "{{id}}"}'>{{title}}</ui-button>
    {{/each}}
  </ui-section>
</ui-display>
<ui-display title='Beaker' button>
  {{#partial button}}
    {{#each data.beakerTransferAmounts}}
      <ui-button icon='minus' action='remove' params='{"amount": {{.}}}'>{{.}}</ui-button>
    {{/each}}
    <ui-button icon='eject' state='{{data.isBeakerLoaded ? null : "disabled"}}' action='eject'>Eject</ui-button>
  {{/partial}}
  <ui-section label='Contents'>
    {{#if data.isBeakerLoaded}}
      {{Math.round(adata.beakerCurrentVolume)}}/{{data.beakerMaxVolume}} Units

      {{#each adata.beakerContents}}
        {{Math.fixed(volume, 2)}} units of {{name}}

      {{else}}
        Beaker Empty
      {{/each}}
    {{else}}
      No Beaker
    {{/if}}
  </ui-section>
</ui-display>


code:
<ui-display title='Thermostat'>
  <ui-section label='Power'>
    <ui-button icon='{{data.isActive ? "power-off" : "close"}}'
		style='{{data.isActive ? "selected" : null}}'
		state='{{data.isBeakerLoaded ? null : "disabled"}}'
		action='power'>{{data.isActive ? "On" : "Off"}}</ui-button>
  </ui-section>
  <ui-section label='Target'>
	  <ui-button icon='pencil' action='temperature' params='{"target": "input"}'>{{Math.round(adata.targetTemp)}} K</ui-button>
  </ui-section>
</ui-display>
<ui-display title='Beaker' button>
  {{#partial button}}
    <ui-button icon='eject' state='{{data.isBeakerLoaded ? null : "disabled"}}' action='eject'>Eject</ui-button>
  {{/partial}}
  <ui-section label='Contents'>
    {{#if data.isBeakerLoaded}}
      Temperature: {{Math.round(adata.currentTemp)}} K

      {{#each adata.beakerContents}}
        {{Math.fixed(volume, 2)}} units of {{name}}

      {{else}}
        Beaker Empty
      {{/each}}
    {{else}}
      No Beaker
    {{/if}}
  </ui-section>
</ui-display>


code:
<ui-display title='Occupant'>
  <ui-section label='Occupant'>
	 {{data.occupant.name ? data.occupant.name : "No Occupant"}}
  </ui-section>
  {{#if data.hasOccupant}}
    <ui-section label='State'>
      {{data.occupant.stat == 0 ? "Conscious" : data.occupant.stat == 1 ? "Unconcious" : "Dead"}}
  	</ui-section>
  	<ui-section label='Temperature'>
  	  {{Math.round(adata.occupant.bodyTemperature)}} K
  	</ui-section>
    <ui-section label='Health'>
      <ui-bar min='{{data.occupant.minHealth}}' max='{{data.occupant.maxHealth}}' value='{{data.occupant.health}}'
        state='{{data.occupant.health >= 0 ? "good" : "average"}}'>{{Math.round(adata.occupant.health)}}</ui-bar>
    </ui-section>
    {{#each [{label: "Brute", type: "bruteLoss"}, {label: "Respiratory", type: "oxyLoss"}, {label: "Toxin", type: "toxLoss"}, {label: "Burn", type: "fireLoss"}]}}
      <ui-section label='{{label}}'>
        <ui-bar min='0' max='{{data.occupant.maxHealth}}' value='{{data.occupant[type]}}' state='bad'>{{Math.round(adata.occupant[type])}}</ui-bar>
      </ui-section>
    {{/each}}
  {{/if}}
</ui-display>
<ui-display title='Cell'>
  <ui-section label='Power'>
    <ui-button icon='{{data.isOperating ? "power-off" : "close"}}'
      style='{{data.isOperating ? "selected" : null}}'
      state='{{data.isOpen ? "disabled" : null}}'
      action='power'>{{data.isOperating ? "On" : "Off"}}</ui-button>
  </ui-section>
  <ui-section label='Temperature'>
		{{Math.round(adata.cellTemperature)}} K
	</ui-section>
	<ui-section label='Door'>
    <ui-button icon='{{data.isOpen ? "unlock" : "lock"}}' action='door'>{{data.isOpen ? "Open" : "Closed"}}</ui-button>
    <ui-button icon='{{data.autoEject ? "sign-out" : "sign-in"}}' action='autoeject'>{{data.autoEject ? "Auto" : "Manual"}}</ui-button>
	</ui-section>
</ui-display>
<ui-display title='Beaker' button>
  {{#partial button}}
    <ui-button icon='eject' state='{{data.isBeakerLoaded ? null : "disabled"}}' action='ejectbeaker'>Eject</ui-button>
  {{/partial}}
  <ui-section label='Contents'>
    {{#if data.isBeakerLoaded}}
      {{#each adata.beakerContents}}
        {{Math.fixed(volume, 2)}} units of {{name}}

      {{else}}
        Beaker Empty
      {{/each}}
    {{else}}
      No Beaker
    {{/if}}
  </ui-section>
</ui-display>


code:
<ui-notice>
  The requested interface ({{config.interface}}) was not found. Does it exist?
</ui-notice>


code:
<ui-display>
    <ui-section label='Alert Level'>
        {{text.titleCase(data.seclevel)}}
    </ui-section>
    <ui-section label='Controls'>
        <ui-button icon='{{data.alarm ? "close" : "bell-o"}}' action='{{data.alarm ? "reset" : "alarm"}}'>
            {{data.alarm ? "Reset" : "Activate"}}</ui-button>
    </ui-section>
    {{#if data.emagged}}
      <ui-section label='Warning'>
        Safety measures offline. Device may exhibit abnormal behavior.
      </ui-section>
    {{/if}}
</ui-display>


code:
{{#if data.wiping}}
  <ui-notice>
    Wipe in progress!
  </ui-notice>
{{/if}}
<ui-display title={{data.name || "Empty Card"}} button>
  {{#partial button}}
    {{#if data.name}}
      <ui-button icon='trash' state='{{data.wiping || data.isDead ? "disabled" : null}}' action='wipe'>Wipe AI</ui-button>
    {{/if}}
  {{/partial}}
  {{#if data.name}}
    <ui-section label='Status'>
        {{data.isDead || data.isBraindead ? "Offline" : "Operational"}}
    </ui-section>
    <ui-section label='Software Integrity'>
      <ui-bar min='0' max='100' value='{{data.health}}' state='{{healthState}}'>{{Math.round(adata.health)}}%</ui-bar>
    </ui-section>
    <ui-section label='Laws'>
      {{#each data.laws}}
        {{.}}

      {{/each}}
    </ui-section>
    <ui-section label='Settings'>
      <ui-button icon='signal' style='{{data.wireless ? "selected" : null}}' action="wireless">Wireless Activity</ui-button>
      <ui-button icon='microphone' style='{{data.radio ? "selected" : null}}' action="radio">Subspace Radio</ui-button>
    </ui-section>
  {{/if}}
</ui-display>


code:
{{#if data.waiting}}
	<ui-notice>
		Waiting for another device to confirm your request...
	</ui-notice>
{{else}}
	<ui-display>
		<ui-section>
			{{#if data.auth_required}}
				<ui-button icon='check' action='auth_swipe'>Authorize {{data.auth_required}}</ui-button>
			{{else}}
				<ui-button icon='warning' state='{{data.red_alert ? 'disabled' : null}}' action='red_alert'>Red Alert</ui-button>
				<ui-button icon='wrench' state='{{data.emergency_maint ? 'disabled' : null}}' action='emergency_maint'>Emergency Maintenance Access</ui-button>
			{{/if}}
		</ui-section>
	</ui-display>
{{/if}}


code:
<ui-notice>
  {{#if data.siliconUser}}
    <ui-section label='Interface Lock'>
      <ui-button icon='{{data.locked ? "lock" : "unlock"}}' action='lock'>{{data.locked ? "Engaged" : "Disengaged"}}</ui-button>
    </ui-section>
  {{else}}
    Swipe an ID card to {{data.locked ? "unlock" : "lock"}} this interface.
  {{/if}}
</ui-notice>
<ui-display title='Status'>
  <ui-section label='Power'>
    {{#if !data.locked || data.siliconUser }}
      <ui-button icon='{{data.on ? "power-off" : "close"}}' style='{{data.on ? "selected" : null}}' action="power">{{data.on ? "On" : "Off"}}</ui-button>
    {{else}}
      {{data.on ? "On" : "Off"}}
    {{/if}}
  </ui-section>
  <ui-section label='Cell'>
    {{data.cell ? data.cellPercent + "%" : "No Cell"}}
  </ui-section>
  <ui-section label='Mode'>
    {{data.mode}}
  </ui-section>
  <ui-section label='Load'>
    {{data.load ? data.load : "None"}}
  </ui-section>
  <ui-section label='Destination'>
    {{data.destination ? data.destination : "None"}}
  </ui-section>
</ui-display>
{{#if !data.locked || data.siliconUser}}
  <ui-display title='Controls' button>
    {{#partial button}}
      {{#if data.load}}
        <ui-button icon='eject' action='unload'>Unload</ui-button>
      {{/if}}
      {{#if data.haspai}}
        <ui-button icon='eject' action='ejectpai'>Eject PAI</ui-button>
      {{/if}}
      <ui-button icon='pencil' action='setid'>Set ID</ui-button>
    {{/partial}}
    <ui-section label='Destination'>
      <ui-button icon='pencil' action='destination'>Set Destination</ui-button>
      <ui-button icon='stop' action='stop'>Stop</ui-button>
      <ui-button icon='play' action='go'>Go</ui-button>
    </ui-section>
    <ui-section label='Home'>
      <ui-button icon='home' action='home'>Go Home</ui-button>
      <ui-button icon='pencil' action='sethome'>Set Home</ui-button>
    </ui-section>
    <ui-section label='Settings'>
      <ui-button icon='{{data.autoReturn ? "check-square-o" : "square-o"}}' style='{{data.autoReturn ? "selected" : null}}' action='autoret'>
        Auto-Return Home</ui-button>
      <ui-button icon='{{data.autoPickup ? "check-square-o" : "square-o"}}' style='{{data.autoPickup ? "selected" : null}}' action='autopick'>
        Auto-Pickup Crate</ui-button>
      <ui-button icon='{{data.reportDelivery ? "check-square-o" : "square-o"}}' style='{{data.reportDelivery ? "selected" : null}}' action='report'>
        Report Deliveries</ui-button>
    </ui-section>
  </ui-display>
{{/if}}


code:
<ui-notice>
  The regulator {{data.holding ? "is" : "is not"}} connected to a tank.
</ui-notice>
<ui-display title='Status' button>
  <ui-section label='Pressure'>
    {{Math.round(adata.pressure)}} kPa
  </ui-section>
  <ui-section label='Port'>
    {{data.connected ? "Connected" : "Not Connected"}}
  </ui-section>
</ui-display>
<ui-display title='Pump'>
  <ui-section label='Power'>
    <ui-button icon='{{data.on ? "power-off" : "close"}}'
      style='{{data.on ? "selected" : "null"}}'
      action='power'>{{data.on ? "On" : "Off"}}</ui-button>
  </ui-section>
  <ui-section label='Direction'>
    <ui-button icon='{{data.direction == "out" ? "sign-out" : "sign-in"}}'
      action='direction'>{{data.direction == "out" ? "Out" : "In"}}</ui-button>
  </ui-section>
  <ui-section label='Target Pressure'>
    <ui-bar min='{{data.min_pressure}}' max='{{data.max_pressure}}'
      value='{{data.target_pressure}}'>{{Math.round(adata.target_pressure)}} kPa</ui-bar>
  </ui-section>
  <ui-section label='Pressure Regulator'>
    <ui-button icon='refresh' state='{{data.target_pressure != data.default_pressure ? null : "disabled"}}'
      action='pressure' params='{"pressure": "reset"}'>Reset</ui-button>
    <ui-button icon='minus' state='{{data.target_pressure > data.min_pressure ? null : "disabled"}}'
      action='pressure' params='{"pressure": "min"}'>Min</ui-button>
    <ui-button icon='pencil' action='pressure' params='{"pressure": "input"}'>Set</ui-button>
    <ui-button icon='plus' state='{{data.target_pressure < data.max_pressure ? null : "disabled"}}'
      action='pressure' params='{"pressure": "max"}'>Max</ui-button>
  </ui-section>
</ui-display>
<ui-display title='Holding Tank' button>
  {{#partial button}}
    {{#if data.holding}}
      <ui-button icon='eject' style='{{data.on ? "danger" : null}}' action='eject'>Eject</ui-button>
    {{/if}}
  {{/partial}}
  {{#if data.holding}}
		<ui-section label='Label'>
			{{data.holding.name}}
		</ui-section>
		<ui-section label='Pressure'>
			{{Math.round(adata.holding.pressure)}} kPa
		</ui-section>
	{{else}}
		<ui-section>
			No Holding Tank
		</ui-section>
	{{/if}}
</ui-display>


code:
<ui-notice>
  The regulator {{data.holding ? "is" : "is not"}} connected to a tank.
</ui-notice>
<ui-display title='Status' button>
  <ui-section label='Pressure'>
    {{Math.round(adata.pressure)}} kPa
  </ui-section>
  <ui-section label='Port'>
    {{data.connected ? "Connected" : "Not Connected"}}
  </ui-section>
</ui-display>
<ui-display title='Filter'>
  <ui-section label='Power'>
    <ui-button icon='{{data.on ? "power-off" : "close"}}'
      style='{{data.on ? "selected" : "null"}}'
      action='power'>{{data.on ? "On" : "Off"}}</ui-button>
  </ui-section>
</ui-display>
<ui-display title='Holding Tank' button>
  {{#partial button}}
    {{#if data.holding}}
      <ui-button icon='eject' style='{{data.on ? "danger" : null}}' action='eject'>Eject</ui-button>
    {{/if}}
  {{/partial}}
  {{#if data.holding}}
		<ui-section label='Label'>
			{{data.holding.name}}
		</ui-section>
		<ui-section label='Pressure'>
			{{Math.round(adata.holding.pressure)}} kPa
		</ui-section>
	{{else}}
		<ui-section>
			No Holding Tank
		</ui-section>
	{{/if}}
</ui-display>


code:
<ui-display title='Network'>
  {{#if config.fancy}}
    <ui-linegraph points='{{graphData}}' height='500'
        legend='["Available", "Load"]' colors='["rgb(0, 102, 0)", "rgb(153, 0, 0)"]'
        xunit='seconds ago' xfactor='{{data.interval}}' yunit='W' yfactor='1'
        xinc='{{data.stored / 10}}' yinc='9'/>
  {{else}}
    <ui-section label='Available'>
      {{data.supply}} W
    </ui-section>
    <ui-section label='Load'>
        {{data.demand}} W
    </ui-section>
  {{/if}}
</ui-display>
<ui-display title='Areas'>
  <ui-section nowrap>

Area

Charge

Load

Status

Equipment

Lighting

Environment

  </ui-section>
  {{#each data.areas}}
    <ui-section label='{{name}}' nowrap>

{{Math.round(adata.areas[@index].charge)}} %

{{Math.round(adata.areas[@index].load)}} W

{{chargingMode(charging)}}

{{channelPower(eqp)}} [{{channelMode(eqp)}}]

{{channelPower(lgt)}} [{{channelMode(lgt)}}]

{{channelPower(env)}} [{{channelMode(env)}}]

    </ui-section>
  {{/each}}
</ui-display>


code:
<ui-display title='Settings'>
  {{#if data.headset}}
    <ui-section label='Power'>
      <ui-button icon='{{data.listening ? "power-off" : "close"}}' style='{{data.listening ? "selected" : null}}'
        action='listen'>
        {{data.listening ? "On": "Off"}}</ui-button>
    </ui-section>
  {{else}}
    <ui-section label='Microphone'>
      <ui-button icon='{{data.broadcasting ? "power-off" : "close"}}' style='{{data.broadcasting ? "selected" : null}}'
        action='broadcast'>
        {{data.broadcasting ? "Engaged": "Disengaged"}}</ui-button>
    </ui-section>
    <ui-section label='Speaker'>
      <ui-button icon='{{data.listening ? "power-off" : "close"}}' style='{{data.listening ? "selected" : null}}'
        action='listen'>
        {{data.listening ? "Engaged": "Disengaged"}}</ui-button>
    </ui-section>
  {{/if}}
  {{#if data.command}}
    <ui-section label='High Volume'>
      <ui-button icon='{{data.useCommand ? "power-off" : "close"}}' style='{{data.useCommand ? "selected" : null}}'
        action='command'>
        {{data.useCommand ? "On": "Off"}}</ui-button>
    </ui-section>
  {{/if}}
</ui-display>
<ui-display title='Channel'>
  <ui-section label='Frequency'>
    {{#if data.freqlock}}
      {{readableFrequency}}
    {{else}}
      <ui-button icon='fast-backward' state='{{data.frequency == data.minFrequency ? "disabled": null}}' action='frequency' params='{"adjust": -1}'/>
      <ui-button icon='backward' state='{{data.frequency == data.minFrequency ? "disabled": null}}' action='frequency' params='{"adjust": -.2}'/>
      <ui-button icon='pencil' action='frequency' params='{"tune": "input"}'>{{readableFrequency}}</ui-button>
      <ui-button icon='forward' state='{{data.frequency == data.maxFrequency ? "disabled": null}}' action='frequency' params='{"adjust": .2}'/>
      <ui-button icon='fast-forward' state='{{data.frequency == data.maxFrequency ? "disabled": null}}' action='frequency' params='{"adjust": 1}'/>
    {{/if}}
  </ui-section>
  {{#if data.subspaceSwitchable}}
    <ui-section label='Subspace Transmission'>
      <ui-button icon='{{data.subspace ? "power-off" : "close"}}' style='{{data.subspace ? "selected" : null}}'
          action='subspace'>{{data.subspace ? "Active" : "Inactive"}}</ui-button>
    </ui-section>
  {{/if}}
  {{#if data.subspace && data.channels}}
    <ui-section label='Channels'>
      {{#each data.channels:channel}}
        <ui-button icon='{{. ? "check-square-o" : "square-o"}}'
          style='{{. ? "selected" : null}}'
          action='channel' params='{"channel": "{{channel}}"}'>
          {{channel}}</ui-button>

      {{/each}}
    </ui-section>
  {{/if}}
</ui-display>


code:
<ui-display title='Occupant'>
  <ui-section label='Occupant'>
	 {{data.occupant.name ? data.occupant.name : "No Occupant"}}
  </ui-section>
  {{#if data.occupied}}
    <ui-section label='State'>
      {{data.occupant.stat == 0 ? "Conscious" : data.occupant.stat == 1 ? "Unconcious" : "Dead"}}
  	</ui-section>
    <ui-section label='Health'>
      <ui-bar min='{{data.occupant.minHealth}}' max='{{data.occupant.maxHealth}}' value='{{data.occupant.health}}'
        state='{{data.occupant.health >= 0 ? "good" : "average"}}'>{{Math.round(adata.occupant.health)}}</ui-bar>
    </ui-section>
    {{#each [{label: "Brute", type: "bruteLoss"}, {label: "Respiratory", type: "oxyLoss"}, {label: "Toxin", type: "toxLoss"}, {label: "Burn", type: "fireLoss"}]}}
      <ui-section label='{{label}}'>
        <ui-bar min='0' max='{{data.occupant.maxHealth}}' value='{{data.occupant[type]}}' state='bad'>{{Math.round(adata.occupant[type])}}</ui-bar>
      </ui-section>
    {{/each}}
    <ui-section label='Cells'>
        {{data.occupant.cloneLoss ? "Damaged" : "Healthy"}}
    </ui-section>
    <ui-section label='Brain'>
        {{data.occupant.brainLoss ? "Abnormal" : "Healthy"}}
    </ui-section>
    <ui-section label='Bloodstream'>
        {{#each adata.occupant.reagents}}
          {{Math.fixed(volume, 1)}} units of {{name}}

        {{else}}
          Pure
        {{/each}}
    </ui-section>
  {{/if}}
</ui-display>
<ui-display title='Controls'>
	<ui-section label='Door'>
    <ui-button icon='{{data.open ? "unlock" : "lock"}}' action='door'>{{data.open ? "Open" : "Closed"}}</ui-button>
	</ui-section>
  <ui-section label='Inject'>
    {{#each data.chems}}
      <ui-button icon='flask' state='{{data.occupied && allowed ? null : "disabled"}}' action='inject' params='{"chem": "{{id}}"}'>{{name}}</ui-button>

    {{/each}}
  </ui-section>
</ui-display>


code:
<ui-display title='Storage'>
  <ui-section label='Stored Energy'>
    <ui-bar min='0' max='100' value='{{data.capacityPercent}}' state='{{capacityPercentState}}'>{{Math.fixed(adata.capacityPercent)}}%</ui-bar>
  </ui-section>
</ui-display>
<ui-display title='Input'>
  <ui-section label='Charge Mode'>
    <ui-button icon='{{data.inputAttempt ? "refresh" : "close"}}' style='{{data.inputAttempt ? "selected" : null}}'
      action='tryinput'>{{data.inputAttempt ? "Auto" : "Off"}}</ui-button>

    [{{data.capacityPercent >= 100 ? "Fully Charged" : data.inputting ? "Charging" : "Not Charging"}}]
	</ui-section>
  <ui-section label='Target Input'>
    <ui-bar min='0' max='{{data.inputLevelMax}}' value='{{data.inputLevel}}'>{{Math.round(adata.inputLevel)}}W</ui-bar>
	</ui-section>
  <ui-section label='Adjust Input'>
    <ui-button icon='fast-backward' state='{{data.inputLevel == 0 ? "disabled" : null}}' action='input' params='{"target": "min"}'/>
    <ui-button icon='backward' state='{{data.inputLevel == 0 ? "disabled" : null}}' action='input' params='{"adjust": -10000}'/>
    <ui-button icon='pencil' action='input' params='{"target": "input"}'>Set</ui-button>
    <ui-button icon='forward' state='{{data.inputLevel == data.inputLevelMax ? "disabled" : null}}' action='input' params='{"adjust": 10000}'/>
    <ui-button icon='fast-forward' state='{{data.inputLevel == data.inputLevelMax ? "disabled" : null}}' action='input' params='{"target": "max"}'/>
  </ui-section>
  <ui-section label='Available'>
		{{Math.round(adata.inputAvailable)}}W
	</ui-section>
</ui-display>
<ui-display title='Output'>
  <ui-section label='Output Mode'>
    <ui-button icon='{{data.outputAttempt ? "power-off" : "close"}}' style='{{data.outputAttempt ? "selected" : null}}'
      action='tryoutput'>{{data.outputAttempt ? "On" : "Off"}}</ui-button>

    [{{data.outputting ? "Sending" : data.charge > 0 ? "Not Sending" : "No Charge"}}]
	</ui-section>
  <ui-section label='Target Output'>
    <ui-bar min='0' max='{{data.outputLevelMax}}' value='{{data.outputLevel}}'>{{Math.round(adata.outputLevel)}}W</ui-bar>
	</ui-section>
  <ui-section label='Adjust Output'>
    <ui-button icon='fast-backward' state='{{data.outputLevel == 0 ? "disabled" : null}}' action='output' params='{"target": "min"}'/>
    <ui-button icon='backward' state='{{data.outputLevel == 0 ? "disabled" : null}}' action='output' params='{"adjust": -10000}'/>
    <ui-button icon='pencil' action='output' params='{"target": "input"}'>Set</ui-button>
    <ui-button icon='forward' state='{{data.outputLevel == data.outputLevelMax ? "disabled" : null}}' action='output' params='{"adjust": 10000}'/>
    <ui-button icon='fast-forward' state='{{data.outputLevel == data.outputLevelMax ? "disabled" : null}}' action='output' params='{"target": "max"}'/>
  </ui-section>
  <ui-section label='Outputting'>
		{{Math.round(adata.outputUsed)}}W
	</ui-section>
</ui-display>


code:
<ui-display title='Status'>
  <ui-section label='Generated Power'>
    {{Math.round(adata.generated)}}W
  </ui-section>
  <ui-section label='Orientation'>
    {{Math.round(adata.angle)}}° ({{data.direction}})
  </ui-section>
  <ui-section label='Adjust Angle'>
    <ui-button icon='step-backward' action='angle' params='{"adjust": -15}'>15°</ui-button>
    <ui-button icon='backward' action='angle' params='{"adjust": -5}'>5°</ui-button>
    <ui-button icon='forward' action='angle' params='{"adjust": 5}'>5°</ui-button>
    <ui-button icon='step-forward' action='angle' params='{"adjust": 15}'>15°</ui-button>
	</ui-section>
</ui-display>
<ui-display title='Tracking'>
  <ui-section label='Tracker Mode'>
    <ui-button icon='close' state='{{data.tracking_state == 0 ? "selected" : null}}'
      action='tracking' params='{"mode": 0}'>Off</ui-button>
    <ui-button icon='clock-o' state='{{data.tracking_state == 1 ? "selected" : null}}'
      action='tracking' params='{"mode": 1}'>Timed</ui-button>
    <ui-button icon='refresh' state='{{data.connected_tracker ? data.tracking_state == 2 ? "selected" : null : "disabled"}}'
      action='tracking' params='{"mode": 2}'>Auto</ui-button>
	</ui-section>
  <ui-section label='Tracking Rate'>
		{{Math.round(adata.tracking_rate)}}°/h ({{data.rotating_way}})
	</ui-section>
  <ui-section label='Adjust Rate'>
    <ui-button icon='fast-backward' action='rate' params='{"adjust": -180}'>180°</ui-button>
    <ui-button icon='step-backward' action='rate' params='{"adjust": -30}'>30°</ui-button>
    <ui-button icon='backward' action='rate' params='{"adjust": -5}'>5°</ui-button>
    <ui-button icon='forward' action='rate' params='{"adjust": 5}'>5°</ui-button>
    <ui-button icon='step-forward' action='rate' params='{"adjust": 30}'>30°</ui-button>
    <ui-button icon='fast-forward' action='rate' params='{"adjust": 180}'>180°</ui-button>
	</ui-section>
</ui-display>
<ui-display title="Devices" button>
  {{#partial button}}
    <ui-button icon='refresh' action='refresh'>Refresh</ui-button>
  {{/partial}}
	<ui-section label='Solar Tracker'>
    {{data.connected_tracker ? "" : "Not "}}Found
	</ui-section>
	<ui-section label='Solar Panels'>
		{{Math.round(adata.connected_panels)}} Panels Connected
	</ui-section>
</ui-display>


code:
<ui-display title='Power' button>
  {{#partial button}}
    {{#if data.open}}
      <ui-button icon='eject' state='{{data.hasPowercell ? null : "disabled"}}' action='eject'>Eject</ui-button>
    {{/if}}
  {{/partial}}
  <ui-section label='Power'>
    <ui-button icon='{{data.on ? "power-off" : "close"}}'
      style='{{data.on ? "selected" : null}}' state='{{data.hasPowercell ? null : "disabled"}}'
      action='power'>{{data.on ? "On" : "Off"}}</ui-button>
  </ui-section>
  <ui-section label='Cell'>
    {{#if data.hasPowercell}}
      <ui-bar min='0' max='100' value='{{data.powerLevel}}'>{{Math.fixed(adata.powerLevel)}}%</ui-bar>
		{{else}}
			No Cell
		{{/if}}
  </ui-section>
</ui-display>
<ui-display title='Thermostat'>
  <ui-section label='Current Temperature'>
		{{Math.round(adata.currentTemp)}}°C
	</ui-section>
	<ui-section label='Target Temperature'>
		{{Math.round(adata.targetTemp)}}°C
	</ui-section>
  {{#if data.open}}
    <ui-section label='Adjust Target'>
      <ui-button icon='fast-backward' state='{{data.targetTemp > data.minTemp ? null : "disabled"}}' action='target' params='{"adjust": -20}'/>
      <ui-button icon='backward' state='{{data.targetTemp > data.minTemp ? null : "disabled"}}' action='target' params='{"adjust": -5}'/>
      <ui-button icon='pencil' action='target' params='{"target": "input"}'>Set</ui-button>
      <ui-button icon='forward' state='{{data.targetTemp < data.maxTemp ? null : "disabled"}}' action='target' params='{"adjust": 5}'/>
      <ui-button icon='fast-forward' state='{{data.targetTemp < data.maxTemp ? null : "disabled"}}' action='target' params='{"adjust": 20}'/>
	  </ui-section>
	{{/if}}
  <ui-section label='Mode'>
		{{#if data.open}}
      <ui-button icon='long-arrow-up' state='{{data.mode == "heat" ? "selected" : null}}' action='mode' params='{"mode": "heat"}'>Heat</ui-button>
      <ui-button icon='long-arrow-down' state='{{data.mode == "cool" ? "selected" : null}}' action='mode' params='{"mode": "cool"}'>Cool</ui-button>
      <ui-button icon='arrows-v' state='{{data.mode == "auto" ? "selected" : null}}' action='mode' params='{"mode": "auto"}'>Auto</ui-button>
		{{else}}
			{{text.titleCase(data.mode)}}
		{{/if}}
	</ui-section>
</ui-display>


code:
{{#each data.alarms:class}}
  <ui-display title='{{class}} Alarms'>

      {{#each .}}

{{.}}

      {{else}}

System Nominal

      {{/each}}

  </ui-display>
{{/each}}


code:
{{#if data.occupied && data.safeties}}
  <ui-notice>
    Biological entity detected in contents. Please remove.
  </ui-notice>
{{/if}}
{{#if data.uv_active}}
  <ui-notice>
    Contents are being disinfected. Please wait.
  </ui-notice>
{{else}}
  <ui-display title='Storage' button>
    {{#partial button}}
      {{#if !data.open}}<ui-button icon='{{data.locked ? "unlock" : "lock"}}' action='lock'>{{data.locked ? 'Unlock' : 'Lock'}}</ui-button>{{/if}}
      {{#if !data.locked}}<ui-button icon='{{data.open ? "sign-out" : "sign-in"}}' action='door'>{{data.open ? 'Close' : 'Open'}}</ui-button>{{/if}}
    {{/partial}}
    {{#if data.locked}}
      <ui-notice>
        Unit Locked
      </ui-notice>
    {{elseif data.open}}
        <ui-section label='Helmet'>
          <ui-button icon='{{data.helmet ? "square" : "square-o"}}'state='{{data.helmet ? null : "disabled"}}'
            action='dispense' params='{"item": "helmet"}'>{{data.helmet || "Empty"}}</ui-button>
        </ui-section>
        <ui-section label='Suit'>
          <ui-button icon='{{data.suit ? "square" : "square-o"}}' state='{{data.suit ? null : "disabled"}}'
            action='dispense' params='{"item": "suit"}'>{{data.suit || "Empty"}}</ui-button>
        </ui-section>
        <ui-section label='Mask'>
          <ui-button icon='{{data.mask ? "square" : "square-o"}}' state='{{data.mask ? null : "disabled"}}'
            action='dispense' params='{"item": "mask"}'>{{data.mask || "Empty"}}</ui-button>
        </ui-section>
        <ui-section label='Storage'>
          <ui-button icon='{{data.storage ? "square" : "square-o"}}' state='{{data.storage ? null : "disabled"}}'
            action='dispense' params='{"item": "storage"}'>{{data.storage || "Empty"}}</ui-button>
        </ui-section>
    {{else}}
      <ui-button icon='recycle' state='{{data.occupied && data.safeties ? "disabled" : null}}' action='uv'>Disinfect</ui-button>
    {{/if}}
  </ui-display>
{{/if}}


code:
<ui-display>
    <ui-section label='Dispense'>
        <ui-button icon='{{data.plasma ? "square" : "square-o"}}' state='{{data.plasma ? null : "disabled"}}'
            action='plasma'>Plasma ({{Math.round(adata.plasma)}})</ui-button>
        <ui-button icon='{{data.oxygen ? "square" : "square-o"}}' state='{{data.oxygen ? null : "disabled"}}'
            action='oxygen'>Oxygen ({{Math.round(adata.oxygen)}})</ui-button>
    </ui-section>
</ui-display>


code:
<ui-notice>
  The regulator {{data.connected? "is" : "is not"}} connected to a mask.
</ui-notice>
<ui-display>
  <ui-section label='Tank Pressure'>
      <ui-bar min='0' max='1013' value='{{data.tankPressure}}'
        state='{{tankPressureState}}'>{{Math.round(adata.tankPressure)}} kPa</ui-bar>
  </ui-section>
  <ui-section label='Release Pressure'>
    <ui-bar min='{{data.minReleasePressure}}' max='{{data.maxReleasePressure}}'
      value='{{data.releasePressure}}'>{{Math.round(adata.releasePressure)}} kPa</ui-bar>
  </ui-section>
  <ui-section label='Pressure Regulator'>
    <ui-button icon='refresh' state='{{data.releasePressure != data.defaultReleasePressure ? null : "disabled"}}'
      action='pressure' params='{"pressure": "reset"}'>Reset</ui-button>
    <ui-button icon='minus' state='{{data.releasePressure > data.minReleasePressure ? null : "disabled"}}'
      action='pressure' params='{"pressure": "min"}'>Min</ui-button>
    <ui-button icon='pencil' action='pressure' params='{"pressure": "input"}'>Set</ui-button>
    <ui-button icon='plus' state='{{data.releasePressure < data.maxReleasePressure ? null : "disabled"}}'
      action='pressure' params='{"pressure": "max"}'>Max</ui-button>
  </ui-section>
</ui-display>


code:
<ui-display title='Status'>
    <ui-section label='Temperature'>
        {{Math.fixed(adata.temperature, 2)}} K
    </ui-section>
    <ui-section label='Pressure'>
        {{Math.fixed(adata.pressure, 2)}} kPa
    </ui-section>
</ui-display>
<ui-display title='Controls'>
    <ui-section label='Power'>
        <ui-button icon='{{data.on ? "power-off" : "close"}}' style="{{data.on ? "selected" : null}}"
            action='power'>{{data.on ? "On": "Off"}}</ui-button>
    </ui-section>
    <ui-section label='Target Temperature'>
        <ui-button icon='fast-backward' style='{{data.target == data.min ? "disabled" : null}}'
            action='target' params='{"adjust": -20}'/>
        <ui-button icon='backward' style='{{data.target == data.min ? "disabled" : null}}'
            action='target' params='{"adjust": -5}'/>
        <ui-button icon='pencil' action='target' params='{"target": "input"}'>{{Math.fixed(adata.target, 2)}}</ui-button>
        <ui-button icon='forward' style='{{data.target == data.max ? "disabled" : null}}'
            action='target' params='{"adjust": 5}'/>
        <ui-button icon='fast-forward' style='{{data.target == data.max ? "disabled" : null}}'
            action='target' params='{"adjust": 20}'/>
    </ui-section>
</ui-display>


code:
<ui-display title='Uplink' button>
  {{#partial button}}
    {{#if config.fancy}}
      <ui-input value='{{filter}}' placeholder='Filter...'/>
    {{/if}}
    {{#if data.lockable}}
      <ui-button icon='lock' action='lock'>Lock</ui-button>
    {{/if}}
  {{/partial}}
  <ui-section label='Telecrystals' right>
     0 ? "good" : "bad"}}'>{{data.telecrystals}} TC
  </ui-section>
</ui-display>
{{#each data.categories}}
  <ui-display title='{{name}}'>
    {{#each items}}
      <ui-section label='{{name}}' candystripe right>
        <ui-button tooltip='{{name}}: {{desc}}' tooltip-side='left'
          state='{{data.telecrystals < cost || (data.telecrystals - hovered.cost < cost && hovered.item != name) ? "disabled" : null}}'
          action='buy' params='{"category": "{{category}}", "item": {{name}}, "cost": {{cost}}}'
          on-hover='hover' on-unhover='unhover'>{{cost}} TC</ui-button>
      </ui-section>
    {{/each}}
  </ui-display>
{{/each}}


code:
<ui-display>
  {{#each data.wires}}
    <ui-section label='{{color}}{{wire ? " (" + wire + ")" : ""}}' labelcolor='{{color}}' candystripe right>
      <ui-button action='cut' params='{"wire":"{{color}}"}'>{{cut ? "Mend" : "Cut"}}</ui-button>
      <ui-button action='pulse' params='{"wire":"{{color}}"}'>Pulse</ui-button>
      <ui-button action='attach' params='{"wire":"{{color}}"}'>{{attached ? "Detach" : "Attach"}}</ui-button>
    </ui-section>
  {{/each}}
</ui-display>
{{#if data.status}}
  <ui-display>
    {{#each data.status}}
      <ui-section>{{.}}</ui-section>
    {{/each}}
  </ui-display>
{{/if}}


code:
body.nanotrasen
  background: data-url('images/nanotrasen.svg') no-repeat fixed center/70% 70%,
    linear-gradient(to bottom,
      background-color-start 0%,
      background-color-end 100%)
  @import "util/*"
  @import "components/*"


code:
body.syndicate
  color-good               = pale-green
  color-highlight          = black
  background-color-start   = #750000
  background-color-end     = #340404
  rule-color-normal        = dark-gray
  titlebar-color-text      = pale-red
  titlebar-color-button    = pale-red
  display-color-background = alpha(#000, 0.5)
  display-color-shadow     = alpha(#000, 0.75)
  notice-color-first       = #750000
  notice-color-second      = #910101
  section-color-label      = white
  bar-color-normal         = black
  bar-color-good           = pale-green
  bar-color-border         = black
  button-color-normal      = dark-green
  button-color-disabled    = gray
  button-color-selected    = dark-red
  button-color-caution     = yellow-orange
  button-color-danger      = yellow
  input-color-text         = white
  input-color-background   = dark-red

  background: data-url('images/syndicate.svg') no-repeat fixed center/70% 70%,
    linear-gradient(to bottom,
      background-color-start 0%,
      background-color-end 100%)
  @import "util/*"
  @import "components/*"


code:
// Temporarily import Ractive first to keep it from detecting ie8's object.defineProperty shim, which it misuses (ractivejs/ractive#2343).
import Ractive from 'ractive'
Ractive.DEBUG = /minified/.test(() => {/* minified */})

import 'ie8'
import 'babel-polyfill'
import 'dom4'
import 'html5shiv'

// Extend the Math builtin with our own utilities.
Object.assign(Math, require('util/math'))

// Set up the initialize function. This is either called below if JSON is provided
// inline, or called by the server if it was not.
import TGUI from 'tgui.ract'
window.initialize = (dataString) => {
  if (window.tgui) return // Don't run twice.
  window.tgui = new TGUI({
    el: '#container',
    data () {
      const initial = JSON.parse(dataString)
      return {
        constants: require('util/constants'),
        text: require('util/text'),
        config: initial.config,
        data: initial.data,
        adata: initial.data
      }
    }
  })
}

// Try to find data in the page. If the JSON was inlined, load it.
const holder = document.getElementById('data')
const data = holder.textContent
const ref = holder.getAttribute('data-ref')
if (data !== '{}') {
  window.initialize(data)
  holder.remove()
}
// Let the server know we're set up. This also sends data if it was not inlined.
import { act } from 'util/byond'
act(ref, 'tgui:initialize')

// Load fonts.
import { loadCSS } from 'fg-loadcss'
loadCSS('https://cdn.jsdelivr.net/fontawesome/4.5.0/css/font-awesome.min.css')
// Handle font loads.
import FontFaceObserver from 'fontfaceobserver'
const fontawesome = new FontFaceObserver('FontAwesome')
fontawesome.check('\uf240')
  .then(() => document.body.classList.add('icons'))
  .catch(() => document.body.classList.add('no-icons'))


code:
<link rel='ractive' href='components/resize.ract'>
<link rel='ractive' href='components/titlebar.ract'>
<link rel='ractive' href='components/warnings.ract'>

<script>
import { winset } from 'util/byond'
import { lock } from 'util/dragresize'

component.exports = {
  components: {
    'ui-bar': require('components/bar'),
    'ui-button': require('components/button'),
    'ui-display': require('components/display'),
    'ui-input': require('components/input'),
    'ui-linegraph': require('components/linegraph'),
    'ui-notice': require('components/notice'),
    'ui-section': require('components/section'),
    'ui-subdisplay': require('components/subdisplay'),
    'ui-tabs': require('components/tabs'),
  },
  events: {
    enter: require('ractive-events-keys').enter,
    space: require('ractive-events-keys').space,
  },
  transitions: {
    fade: require('ractive-transitions-fade'),
  },
  onconfig () {
    const requested  = this.get('config.interface')
    const interfaces = require('interfaces/**/*.ract', { mode: 'hash' })
    if (requested in interfaces) {
      this.components.interface = interfaces[requested] // Use the interface specified in the config...
    } else {
      this.components.interface = interfaces.error // ...unless it does not exist.
    }
  },
  oninit () {
    this.observe('config.style', (newkey, oldkey, keypath) => {
      if (newkey) document.body.classList.add(newkey)
      if (oldkey) document.body.classList.remove(oldkey)
    })
  },
  oncomplete () {
    // If the window starts off screen, pull it back.
    if (this.get('config.locked')) {
      let {x, y} = lock(window.screenLeft, window.screenTop)
      winset(this.get('config.window'), 'pos', `${x},${y}`)
    }

    // Give focus back to the map.
    winset('mapwindow.map', 'focus', true)
  }
}
</script>

<titlebar>{{{config.title}}}</titlebar>
<main>
  <warnings/>
  <interface/>
</main>
<resize/>


code:
@charset "utf-8"

white = white
pale-red = #e74242
red = #b00e0e
dark-red = #9d0808
yellow-orange = #be6209
yellow = #9a9d00
pale-green = #73E573
green = #2f943c
grass-green = #537d29
dark-green = #397439
royal-blue = #40628a
pale-blue = #8ba5c4
black = black
black-gray = #161616
dark-gray = #272727
gray = #363636
light-gray = #999999

// Branding Colors
color-normal    = royal-blue
color-good      = grass-green
color-average   = yellow-orange
color-bad       = red
color-highlight = pale-blue

// Text
text-color-normal  = white
text-color-inverse = black

// Background
background-color-start = #2a2a2a
background-color-end   = #202020

// Rules (<hr/> etc)
rule-color-normal = royal-blue
rule-color-dark   = dark-gray
rule-size         = 2px

// Titlebar
titlebar-color-text       = pale-blue
titlebar-color-button     = pale-blue
titlebar-color-background = gray
titlebar-color-coreshadow = black-gray
titlebar-color-shadow     = alpha(#000, 0.1)

// Resize
resize-color = gray

// Display
display-color-title      = white
display-color-background = alpha(#000, 0.33)
display-color-shadow     = alpha(#000, 0.5)

// Notice
notice-color-first  = #bb9b68
notice-color-second = #b1905d
notice-color-border = dark-gray

// Section
section-color-label = pale-blue
section-color-candystripe = alpha(#000, 0.2)

// Bar
bar-color-normal     = color-normal
bar-color-good       = color-good
bar-color-average    = color-average
bar-color-bad        = color-bad
bar-color-border     = royal-blue
bar-color-background = dark-gray

// Buttons
button-color-normal   = royal-blue
button-color-disabled = light-gray
button-color-selected = green
button-color-caution  = yellow
button-color-danger   = dark-red
button-color-border   = dark-gray
button-lighten-hover  = 15%
button-alpha-disabled = .75

// Input
input-color-text         = black
input-color-placeholder  = light-gray
input-color-border       = dark-gray
input-color-background   = white

// Tooltips
tooltip-color-border = dark-gray
tooltip-color-background = gray

html, body
  box-sizing: border-box
  height: 100%
  margin: 0

html
  overflow: hidden
  cursor: default // Reset the cursor.

body
  overflow: auto

  font-family: Verdana, Geneva, sans-serif
  font-size: 12px
  color: text-color-normal

  background-color: background-color-start
  background-image: linear-gradient(to bottom,
    background-color-start 0%,
    background-color-end 100%)

*, *:before, *:after
  box-sizing: inherit

$h
  display: inline-block
  margin: 0
  padding: 6px 0
h1
  @extend $h
  font-size: 18px
h2
  @extend $h
  font-size: 16px
h3
  @extend $h
  font-size: 14px
h4
  @extend $h
  font-size: 12px

@require "styles/*"


code:
const encode = encodeURIComponent

// Helper to generate a BYOND href given 'params' as an object (with an optional 'url' for eg winset).
export function href (params = {}, url = '') {
  return `byond://${url}?` + Object.keys(params).map(key => `${encode(key)}=${encode(params[key])}`).join('&')
}

// Helper to make a BYOND ui_act() call on the UI 'src' given an 'action' and optional 'params'.
export function act (src, action, params = {}) {
  window.location.href = href(Object.assign({ src, action }, params))
}

// Helper to make a BYOND winset() call on 'window', setting 'key' to 'value'
export function winset (win, key, value) {
  window.location.href = href({[`${win}.${key}`]: value}, 'winset')
}


code:
.normal
  color: color-normal
.good
  color: color-good
.average
  color: color-average
.bad
  color: color-bad
.highlight
  color: color-highlight


code:
// Constants used in tgui; these are mirrored from the BYOND code.
export const UI_INTERACTIVE = 2
export const UI_UPDATE = 1
export const UI_DISABLED = 0
export const UI_CLOSE = -1


code:
import {winset} from './byond'

export function lock (x, y) {
  if (x < 0) { // Left
    x = 0
  } else if (x + window.innerWidth > window.screen.availWidth) { // Right
    x = window.screen.availWidth - window.innerWidth
  }

  if (y < 0) { // Top
    y = 0
  } else if (y + window.innerHeight > window.screen.availHeight) { // Bottom
    y = window.screen.availHeight - window.innerHeight
  }

  return {x, y}
}

export function drag (event) {
  event.preventDefault()

  if (!this.get('drag')) return

  if (this.get('x')) {
    let x = (event.screenX - this.get('x')) + window.screenLeft
    let y = (event.screenY - this.get('y')) + window.screenTop
    if (this.get('config.locked')) ({x, y} = lock(x, y)) // Lock to primary monitor.
    winset(this.get('config.window'), 'pos', `${x},${y}`)
  }
  this.set({ x: event.screenX, y: event.screenY })
}

export function sane (x, y) {
  x = Math.clamp(100, window.screen.width, x)
  y = Math.clamp(100, window.screen.height, y)
  return {x, y}
}

export function resize (event) {
  event.preventDefault()

  if (!this.get('resize')) return

  if (this.get('x')) {
    let x = (event.screenX - this.get('x')) + window.innerWidth
    let y = (event.screenY - this.get('y')) + window.innerHeight
    ;({x, y} = sane(x, y))
    winset(this.get('config.window'), 'size', `${x},${y}`)
  }
  this.set({ x: event.screenX, y: event.screenY })
}


code:
export function filterMulti (displays, string) {
  for (let display of displays) { // First check if the display includes the search term in the first place.
    if (display.textContent.toLowerCase().includes(string)) {
      display.style.display = ''
      filter(display, string)
    } else {
      display.style.display = 'none'
    }
  }
}

export function filter (display, string) {
  const items = display.queryAll('section')
  const titleMatch = display.query('header').textContent.toLowerCase().includes(string)
  for (let item of items) { // Check if the item or its displays title contains the search term.
    if (titleMatch || item.textContent.toLowerCase().includes(string)) {
      item.style.display = ''
    } else {
      item.style.display = 'none'
    }
  }
}


code:
// Helper to limit a number to be inside 'min' and 'max'.
export function clamp (min, max, number) {
  return Math.max(min, Math.min(number, max))
}

// Helper to round a number to 'decimals' decimals.
export function fixed (number, decimals = 1) {
  return Number(Math.round(number + 'e' + decimals) + 'e-' + decimals)
}


code:
main
  display: block
  margin-top: 32px
  padding: 2px 6px 0

hr
  height: rule-size
  background-color: rule-color-normal
  border: none

.hidden
  display: none


code:
export function upperCaseFirst (str) {
  return str[0].toUpperCase() + str.slice(1).toLowerCase()
}

export function titleCase (str) {
  return str.replace(/\w\S*/g, upperCaseFirst)
}

export function zeroPad (str, pad_size) {
  str = str.toString()
  while(str.length < pad_size)
  	str = '0' + str
  return str
}


code:
// Helper to reset font options to default.
$fontReset
  color: text-color-normal
  font-size: 12px
  font-weight: normal
  font-style: normal
  text-decoration: none

// Utility classes to set font styles.
.bold
  font-weight: bold
.italic
  font-style: italic

// Make 'unselectable' text unselectable on all browsers.
[unselectable=on]
  user-select: none


code:
div[data-tooltip], span[data-tooltip]
  position: relative

  &::after
    position: absolute
    display: block
    z-index: 2
    width: 250px
    padding: 10px
    transform: translateX(-50%)

    visibility: hidden
    opacity: 0

    white-space: normal
    text-align: left
    content: attr(data-tooltip)

    transition: all .5s
    border: 1px solid tooltip-color-border
    background-color: tooltip-color-background

  &:hover::after
    visibility: visible
    opacity: 1

  &.tooltip-top::after
    bottom: 100%
    left: 50%
    transform: translateX(-50%) translateY(8px)
  &.tooltip-top:hover::after
    transform: translateX(-50%) translateY(-8px)

  &.tooltip-bottom::after
    top: 100%
    left: 50%
    transform: translateX(-50%) translateY(-8px)
  &.tooltip-bottom:hover::after
    transform: translateX(-50%) translateY(8px)

  &.tooltip-left::after
    top: 50%
    right: 100%
    transform: translateX(8px) translateY(-50%)
  &.tooltip-left:hover::after
    transform: translateX(-8px) translateY(-50%)

  &.tooltip-right::after
    top: 50%
    left: 100%
    transform: translateX(-8px) translateY(-50%)
  &.tooltip-right:hover::after
    transform: translateX(8px) translateY(-50%)


code:
<!DOCTYPE html>

    <meta http-equiv='X-UA-Compatible' content='IE=edge'/>
    <meta charset='utf-8'/>

    <link rel='stylesheet' href='tgui.css'/>

        Loading...

        Javascript is required in order to use this interface.
        Please enable Javascript and restart the game.