[
  {
    "path": ".gitignore",
    "content": "node_modules\n*.log\n*/linuxDash.min.js.map\ntemp\n"
  },
  {
    "path": "LICENSE.md",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 afaqurk\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">\n  <a href=\"https://afaqurk.github.io/linux-dash\">\n    <img src=\"https://raw.githubusercontent.com/afaqurk/screenshots/master/linux-dash/v2.0-logo.png\"/>\n  </a>\n</h1>\n\n<p align=\"center\">\n  <sub>v2.0</sub><br/>\n  <small>A simple & low-overhead web dashboard for linux systems</small>\n</p>\n\n<p align=\"center\">\n  <small>\n    <a href=\"https://afaqurk.github.io/linux-dash\">Demo</a> &nbsp;|&nbsp;\n    <a href=\"https://github.com/afaqurk/linux-dash/wiki\">\n      Docs\n    </a>\n  </small>\n</p>\n\n\n<p align=\"center\">\n  <a href=\"https://gitter.im/afaqurk/linux-dash\">\n    <img\n      src=\"https://badges.gitter.im/gitterHQ/gitter.png\"\n      alt=\"linux-dash Gitter chat\">\n  </a>\n</p>\n\n<br/>\n\n## Features\n* **Small** ----- Under 400KB on disk _(with .git removed)!_\n* **Simple** ---- A minimalist, beautiful dashboard\n* **Easy** ------ Drop-in installation\n* **Versatile** -- Choose your stack from Node.js, Go, Python, PHP\n\n## Installation\n\n### Step 1\n```sh\n## 1. clone the repo\ngit clone --depth 1 https://github.com/afaqurk/linux-dash.git\n\n## 2. go to the cloned directory\ncd linux-dash/app/server\n\n```\nOR, if you prefer to download manually:\n\n```sh\n## 1. Download the .zip\ncurl -LOk https://github.com/afaqurk/linux-dash/archive/master.zip && unzip master.zip\n\n## 2. navigate to downloaded & unzipped dir\ncd linux-dash-master/app/server\n\n```\n\n### Step 2\n\nSee instructions for preferred server linux-dash server _(all included)_:\n\n* [Node.js](#if-using-nodejs) _(recommended)_\n* [Go](#if-using-go)\n* [Python](#if-using-python)\n* [PHP](#if-using-php)\n\n#### If Using Node.js\n```sh\n## install dependencies\nnpm install --production\n\n## start linux-dash (on port 80 by default; may require sudo)\n## You may change this with the `LINUX_DASH_SERVER_PORT` environment variable (eg. `LINUX_DASH_SERVER_PORT=8080 node server`)\n## or provide a --port flag to the command below\n## Additionally, the server will listen on every network interface (`0.0.0.0`).\n## You may change this with the `LINUX_DASH_SERVER_HOST` environment variable (eg. `LINUX_DASH_SERVER_HOST=127.0.0.1 node server`)\n## or provide a --host flag to the command below\nnode index.js\n\n```\n\n#### If Using Go\n```sh\n## start the server (on port 80 by default; may require sudo)\ngo run index.go\n```\n\nTo build a binary, run `go build && ./server -h`. See [@tehbilly](https://github.com/sergeifilippov)'s notes [here](https://github.com/afaqurk/linux-dash/pull/281) for binary usage options\n\n#### If Using Python\n```sh\n# Start the server (on port 80 by default; may require sudo).\npython index.py\n```\n\n#### If Using PHP\n\n1. Make sure you have the `exec`, `shell_exec`, and `escapeshellarg` functions enabled\n2. Point your web server to `app/` directory under `linux-dash`\n2. Restart your web server (Apache, nginx, etc.)\n  - For PHP + Apache setup follow the [Digital Ocean tutorial](https://www.digitalocean.com/community/tutorials/how-to-install-linux-dash-on-ubuntu-14-04).\n  - For help with nginx setup, see [this gist](https://gist.github.com/sergeifilippov/8909839) by [@sergeifilippov](https://github.com/sergeifilippov).\n\n## Support\n\nFor general help, please use the [Gitter chat room](https://gitter.im/afaqurk/linux-dash).\n\n## Security\n\n**It is strongly recommended** that all linux-dash installations be protected via a security measure of your choice.\n\nLinux Dash does not provide any security or authentication features.\n"
  },
  {
    "path": "app/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" ng-app=\"linuxDash\">\n    <head>\n        <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n        <title>Linux Dash : Simple, beautiful server monitoring web dashboard</title>\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n        <meta name=\"description\" content=\"Monitor your Linux server through a simple web dashboard. Open source and free!\">\n        <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n        <link href='//fonts.googleapis.com/css?family=Merriweather:300italic,300|Open+Sans:400,600' rel='stylesheet' type='text/css'>\n        <link href='linuxDash.min.css' rel='stylesheet' type='text/css'>\n\n        <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->\n        <!--[if lt IE 9]>\n          <script src=\"https://html5shim.googlecode.com/svn/trunk/html5.js\"></script>\n        <![endif]-->\n    </head>\n    <body>\n\n        <nav-bar></nav-bar>\n\n        <!-- Templates Get Rendered Here -->\n        <div id=\"plugins\" ng-view></div>\n\n        <!-- Javascript-->\n        <script src=\"linuxDash.min.js\" type=\"text/javascript\"></script>\n    </body>\n</html>\n"
  },
  {
    "path": "app/server/config/ping_hosts",
    "content": "google.com\nyahoo.com\ntwitter.com\n"
  },
  {
    "path": "app/server/index.go",
    "content": "package main\r\n\r\nimport (\r\n\t\"bytes\"\r\n\t\"flag\"\r\n\t\"fmt\"\r\n\t\"net/http\"\r\n\t\"os\"\r\n\t\"os/exec\"\r\n)\r\n\r\nvar (\r\n\tlistenAddress = flag.String(\"listen\", \"0.0.0.0:80\", \"Where the server listens for connections. [interface]:port\")\r\n\tstaticPath    = flag.String(\"static\", \"../\", \"Location of static files.\")\r\n)\r\n\r\nfunc init() {\r\n\tflag.Parse()\r\n}\r\n\r\nfunc main() {\r\n\thttp.Handle(\"/\", http.FileServer(http.Dir(*staticPath)))\r\n\thttp.HandleFunc(\"/server/\", func(w http.ResponseWriter, r *http.Request) {\r\n\t\tmodule := r.URL.Query().Get(\"module\")\r\n\t\tif module == \"\" {\r\n\t\t\thttp.Error(w, \"No module specified, or requested module doesn't exist.\", 406)\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\t// Execute the command\r\n\t\tcmd := exec.Command(\"./linux_json_api.sh\", module)\r\n\t\tvar output bytes.Buffer\r\n\t\tcmd.Stdout = &output\r\n\t\terr := cmd.Run()\r\n\t\tif err != nil {\r\n\t\t\tfmt.Printf(\"Error executing '%s': %s\\n\\tScript output: %s\\n\", module, err.Error(), output.String())\r\n\t\t\thttp.Error(w, \"Unable to execute module.\", http.StatusInternalServerError)\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tw.Write(output.Bytes())\r\n\t})\r\n\r\n\tfmt.Println(\"Starting http server at:\", *listenAddress)\r\n\terr := http.ListenAndServe(*listenAddress, nil)\r\n\tif err != nil {\r\n\t\tfmt.Println(\"Error starting http server:\", err)\r\n\t\tos.Exit(1)\r\n\t}\r\n}\r\n"
  },
  {
    "path": "app/server/index.js",
    "content": "var express = require('express')\nvar app     = require('express')()\nvar server  = require('http').Server(app)\nvar path    = require('path')\nvar spawn   = require('child_process').spawn\nvar fs      = require('fs')\nvar ws      = require('websocket').server\nvar args    = require('yargs').argv\nvar host    = args.host || process.env.LINUX_DASH_SERVER_HOST || '0.0.0.0'\nvar port    = args.port || process.env.LINUX_DASH_SERVER_PORT || 80\n\nserver.listen(port, host, function() {\n  console.log('Linux Dash Server Started on ' + host + ':' + port + '!');\n})\n\napp.use(express.static(path.resolve(__dirname + '/../')))\n\napp.get('/', function (req, res) {\n\tres.sendFile(path.resolve(__dirname + '/../index.html'))\n})\n\napp.get('/websocket', function (req, res) {\n\n  res.send({\n    websocket_support: true,\n  })\n\n})\n\nwsServer = new ws({\n\thttpServer: server\n})\n\nvar nixJsonAPIScript = __dirname + '/linux_json_api.sh'\n\nfunction getPluginData(pluginName, callback) {\n  var command = spawn(nixJsonAPIScript, [ pluginName, '' ])\n  var output  = []\n\n  command.stdout.on('data', function(chunk) {\n    output.push(chunk.toString())\n  })\n\n  command.on('close', function (code) {\n    callback(code, output)\n  })\n}\n\nwsServer.on('request', function(request) {\n\n\tvar wsClient = request.accept('', request.origin)\n\n  wsClient.on('message', function(wsReq) {\n\n    var moduleName = wsReq.utf8Data\n    var sendDataToClient = function(code, output) {\n      if (code === 0) {\n        var wsResponse = '{ \"moduleName\": \"' + moduleName + '\", \"output\": \"'+ output.join('') +'\" }'\n        wsClient.sendUTF(wsResponse)\n      }\n    }\n\n    getPluginData(moduleName, sendDataToClient)\n\n  })\n\n})\n\napp.get('/server/', function (req, res) {\n\n\tvar respondWithData = function(code, output) {\n\t\tif (code === 0) res.send(output.toString())\n\t\telse res.sendStatus(500)\n\t}\n\n  getPluginData(req.query.module, respondWithData)\n})\n"
  },
  {
    "path": "app/server/index.php",
    "content": "<?php\n\theader(\"Cache-Control: no-store, no-cache, must-revalidate\");\n\theader(\"Pragma: no-cache\");\n\n\t$shell_file = dirname(__FILE__) . '/linux_json_api.sh';\n\t$module = escapeshellcmd($_GET['module']);\n\n\techo stripcslashes(shell_exec( $shell_file . \" \" . $module ));\n"
  },
  {
    "path": "app/server/index.py",
    "content": "#!/usr/bin/env python\n\nfrom __future__ import print_function\nimport os\nimport sys\nfrom BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer, test as _test\nimport subprocess\nfrom SocketServer import ThreadingMixIn\nimport argparse\n\n\nparser = argparse.ArgumentParser(description='Simple Threaded HTTP server to run linux-dash.')\nparser.add_argument('--port', metavar='PORT', type=int, nargs='?', default=80,\n                    help='Port to run the server on.')\n\nmodulesSubPath = '/server/linux_json_api.sh'\nappRootPath = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))\n\nclass ThreadedHTTPServer(ThreadingMixIn, HTTPServer):\n    pass\n\nclass MainHandler(BaseHTTPRequestHandler):\n    def do_GET(self):\n        try:\n            data = ''\n            contentType = 'text/html'\n            if self.path.startswith(\"/server/\"):\n                module = self.path.split('=')[1]\n                output = subprocess.Popen(\n                    appRootPath + modulesSubPath + \" \" + module,\n                    shell = True,\n                    stdout = subprocess.PIPE)\n                data = output.communicate()[0]\n            else:\n                if self.path == '/':\n                    self.path = 'index.html'\n                f = open(appRootPath + os.sep + self.path)\n                data = f.read()\n                if self.path.startswith('/linuxDash.min.css'):\n                    contentType = 'text/css'\n                f.close()\n            self.send_response(200)\n            self.send_header('Content-type', contentType)\n            self.end_headers()\n            self.wfile.write(data)\n\n        except IOError:\n            self.send_error(404, 'File Not Found: %s' % self.path)\n\nif __name__ == '__main__':\n    args = parser.parse_args()\n    server = ThreadedHTTPServer(('0.0.0.0', args.port), MainHandler)\n    print('Starting server, use <Ctrl-C> to stop')\n    server.serve_forever()\n"
  },
  {
    "path": "app/server/linux_json_api.sh",
    "content": "#!/bin/bash\n\nECHO=$(type -P echo)\nSED=$(type -P sed)\nGREP=$(type -P grep)\nTR=$(type -P tr)\nAWK=$(type -P awk)\nCAT=$(type -P cat)\nHEAD=$(type -P head)\nCUT=$(type -P cut)\nPS=$(type -P ps)\n\n_parseAndPrint() {\n  while read data; do\n    $ECHO -n \"$data\" | $SED -r 's/\\\\//g' | $TR -d \"\\n\";\n  done;\n}\n\narp_cache() {\n  local arpCommand=$(type -P arp)\n\n  result=$($arpCommand | $AWK 'BEGIN {print \"[\"} NR>1 \\\n              {print \"{ \\\"addr\\\": \\\"\" $1 \"\\\", \" \\\n                    \"\\\"hw_type\\\": \\\"\" $2 \"\\\", \" \\\n                    \"\\\"hw_addr.\\\": \\\"\" $3 \"\\\", \" \\\n                    \"\\\"mask\\\": \\\"\" $5 \"\\\" }, \" \\\n                    } \\\n            END {print \"]\"}' \\\n        | $SED 'N;$s/},/}/;P;D')\n\n  if [ -z \"$result\" ]; then\n    $ECHO {}\n  else\n    $ECHO $result | _parseAndPrint\n  fi\n}\n\nbandwidth() {\n\n  $CAT /proc/net/dev \\\n  | $AWK 'BEGIN {print \"[\"} NR>2 {print \"{ \\\"interface\\\": \\\"\" $1 \"\\\",\" \\\n            \" \\\"tx\\\": \" $2 \",\" \\\n            \" \\\"rx\\\": \" $10 \" },\" } END {print \"]\"}' \\\n  | $SED 'N;$s/,\\n/\\n/;P;D' \\\n  | _parseAndPrint\n}\n\ncommon_applications() {\n  result=$(whereis php node mysql mongo vim python ruby java apache2 nginx openssl vsftpd make \\\n  | $AWK -F: '{if(length($2)==0) { installed=\"false\"; } else { installed=\"true\"; } \\\n        print \\\n        \"{ \\\n          \\\"binary\\\": \\\"\"$1\"\\\", \\\n          \\\"location\\\": \\\"\"$2\"\\\", \\\n          \\\"installed\\\": \"installed\" \\\n        },\"}')\n\n  $ECHO \"[\" ${result%?} \"]\" | _parseAndPrint\n}\n\ncpu_info() {\n  local lscpuCommand=$(type -P lscpu)\n\n  result=$($lscpuCommand \\\n      | $AWK -F: '{print \"\\\"\"$1\"\\\": \\\"\"$2\"\\\",\" }  '\\\n      )\n\n  $ECHO \"{\" ${result%?} \"}\" | _parseAndPrint\n}\n\ncpu_intensive_processes() {\n\n  result=$($PS axo pid,user,pcpu,rss,vsz,comm --sort -pcpu,-rss,-vsz \\\n        | $HEAD -n 15 \\\n        | $AWK 'BEGIN{OFS=\":\"} NR>1 {print \"{ \\\"pid\\\": \" $1 \\\n                \", \\\"user\\\": \\\"\" $2 \"\\\"\" \\\n                \", \\\"cpu%\\\": \" $3 \\\n                \", \\\"rss\\\": \" $4 \\\n                \", \\\"vsz\\\": \" $5 \\\n                \", \\\"cmd\\\": \\\"\" $6 \"\\\"\" \"},\"\\\n              }')\n\n  $ECHO \"[\" ${result%?} \"]\" | _parseAndPrint\n}\n\ncpu_temp() {\n  local ID=*\n  [ -f /etc/os-release  ] && source /etc/os-release\n  case \"$ID\" in\n    \"raspbian\")\n      cpu=$(</sys/class/thermal/thermal_zone0/temp)\n      echo \"$((cpu/1000))\" | _parseAndPrint\n    ;;\n    *)\n      if type -P sensors 2>/dev/null; then\n        returnString=`sensors`\n        #amd\n        if [[ \"${returnString/\"k10\"}\" != \"${returnString}\" ]] ; then\n          $ECHO ${returnString##*k10} | $CUT -d ' ' -f 6 | $CUT -c 2- | $CUT -c 1-4\n        #intel\n        elif [[ \"${returnString/\"core\"}\" != \"${returnString}\" ]] ; then\n          fromcore=${returnString##*\"coretemp\"}\n          $ECHO ${fromcore##*Physical}  | $CUT -d ' ' -f 3 | $CUT -c 2-5 | _parseAndPrint\n        fi\n      else\n        $ECHO \"[]\" | _parseAndPrint\n      fi\n    ;;\n  esac\n}\n\n# by Paul Colby (http://colby.id.au), no rights reserved ;)\ncpu_utilization() {\n\n  PREV_TOTAL=0\n  PREV_IDLE=0\n  iteration=0\n\n  while [[ iteration -lt 2 ]]; do\n    # Get the total CPU statistics, discarding the 'cpu ' prefix.\n    CPU=(`$SED -n 's/^cpu\\s//p' /proc/stat`)\n    IDLE=${CPU[3]} # Just the idle CPU time.\n\n    # Calculate the total CPU time.\n    TOTAL=0\n    for VALUE in \"${CPU[@]}\"; do\n      let \"TOTAL=$TOTAL+$VALUE\"\n    done\n\n    # Calculate the CPU usage since we last checked.\n    let \"DIFF_IDLE=$IDLE-$PREV_IDLE\"\n    let \"DIFF_TOTAL=$TOTAL-$PREV_TOTAL\"\n    let \"DIFF_USAGE=(1000*($DIFF_TOTAL-$DIFF_IDLE)/$DIFF_TOTAL+5)/10\"\n    #echo -en \"\\rCPU: $DIFF_USAGE%  \\b\\b\"\n\n    # Remember the total and idle CPU times for the next check.\n    PREV_TOTAL=\"$TOTAL\"\n    PREV_IDLE=\"$IDLE\"\n\n    # Wait before checking again.\n    sleep 1\n    iteration=\"$iteration+1\"\n  done\n  $ECHO -en \"$DIFF_USAGE\"\n}\n\ncron_history() {\n\n  local cronLog='/var/log/syslog'\n  local numberOfLines='50'\n\n  # Month, Day, Time, Hostname, tag, user,\n\n  result=$($GREP -m $numberOfLines CRON $cronLog \\\n    | $AWK '{ s = \"\"; for (i = 6; i <= NF; i++) s = s $i \" \"; \\\n        print \"{\\\"time\\\" : \\\"\" $1\" \"$2\" \"$3 \"\\\",\" \\\n            \"\\\"user\\\" : \\\"\" $6 \"\\\",\" \\\n            \"\\\"message\\\" : \\\"\" $5\" \"gensub(\"\\\"\", \"\\\\\\\\\\\"\", \"g\", s) \"\\\"\" \\\n          \"},\"\n        }'\n    )\n\n  $ECHO [${result%?}] | _parseAndPrint\n}\n\ncurrent_ram() {\n\n  local memInfoFile=\"/proc/meminfo\"\n\n  # References:\n  #   Calculations: http://zcentric.com/2012/05/29/mapping-procmeminfo-to-output-of-free-command/\n  #   Fields: https://www.kernel.org/doc/Documentation/filesystems/proc.txt\n\n  memInfo=$($CAT $memInfoFile | $GREP 'MemTotal\\|MemFree\\|Buffers\\|Cached')\n\n  $ECHO $memInfo | $AWK '{print \"{ \\\"total\\\": \" ($2/1024) \", \\\"used\\\": \" ( ($2-($5+$8+$11))/1024 ) \", \\\"available\\\": \" (($5+$8+$11)/1024) \" }\"  }' | _parseAndPrint\n}\n\ndisk_partitions() {\n  local dfCommand=$(type -P df)\n\n  result=$($dfCommand -Ph | $AWK 'NR>1 {print \"{\\\"file_system\\\": \\\"\" $1 \"\\\", \\\"size\\\": \\\"\" $2 \"\\\", \\\"used\\\": \\\"\" $3 \"\\\", \\\"avail\\\": \\\"\" $4 \"\\\", \\\"used%\\\": \\\"\" $5 \"\\\", \\\"mounted\\\": \\\"\" $6 \"\\\"},\"}')\n\n  $ECHO [ ${result%?} ] | _parseAndPrint\n}\n\ndocker_processes() {\n\n  local result=\"\"\n  local dockerCommand=$(type -P docker)\n  local containers=\"$($dockerCommand ps | $AWK '{if(NR>1) print $NF}')\"\n\n  for i in $containers; do\n  result=\"$result $($dockerCommand top $i axo pid,user,pcpu,pmem,comm --sort -pcpu,-pmem \\\n        | $HEAD -n 15 \\\n        | $AWK -v cnt=\"$i\" 'BEGIN{OFS=\":\"} NR>1 {print \"{ \\\"cname\\\": \\\"\" cnt \\\n                \"\\\", \\\"pid\\\": \" $1 \\\n                \", \\\"user\\\": \\\"\" $2 \"\\\"\" \\\n                \", \\\"cpu%\\\": \" $3 \\\n                \", \\\"mem%\\\": \" $4 \\\n                \", \\\"cmd\\\": \\\"\" $5 \"\\\"\" \"},\"\\\n              }')\"\n  done\n\n  $ECHO \"[\" ${result%?} \"]\" | _parseAndPrint\n}\n\ndownload_transfer_rate() {\n\n\tlocal files=(/sys/class/net/*)\n\tlocal pos=$(( ${#files[*]} - 1 ))\n\tlocal last=${files[$pos]}\n\n\tlocal json_output=\"{\"\n\n\tfor interface in \"${files[@]}\"\n\tdo\n\t\tbasename=$(basename \"$interface\")\n\n\t\t# find the number of bytes transfered for this interface\n\t\tin1=$($CAT /sys/class/net/\"$basename\"/statistics/rx_bytes)\n\n\t\t# wait a second\n\t\tsleep 1\n\n\t\t# check same interface again\n\t\tin2=$($CAT /sys/class/net/\"$basename\"/statistics/rx_bytes)\n\n\t\t# get the difference (transfer rate)\n\t\tin_bytes=$((in2 - in1))\n\n\t\t# convert transfer rate to KB\n\t\tin_kbytes=$((in_bytes / 1024))\n\n\t\t# convert transfer rate to KB\n\t\tjson_output=\"$json_output \\\"$basename\\\": $in_kbytes\"\n\n\t\t# if it is not the last line\n\t\tif [[ ! $interface == $last ]]\n\t\tthen\n\t\t\t# add a comma to the line (JSON formatting)\n\t\t\tjson_output=\"$json_output,\"\n\t\tfi\n\tdone\n\n\t# close the JSON object & print to screen\n\t$ECHO \"$json_output}\" | _parseAndPrint\n}\n\ngeneral_info() {\n  local lsbRelease=$(type -P lsb_release)\n  local uName=$(type -P uname)\n  local hostName=$(type -P hostname)\n\n  function displaytime {\n    local T=$1\n    local D=$((T/60/60/24))\n    local H=$((T/60/60%24))\n    local M=$((T/60%60))\n    local S=$((T%60))\n    [[ $D > 0 ]] && printf '%d days ' $D\n    [[ $H > 0 ]] && printf '%d hours ' $H\n    [[ $M > 0 ]] && printf '%d minutes ' $M\n    [[ $D > 0 || $H > 0 || $M > 0 ]] && printf 'and '\n    printf '%d seconds\\n' $S\n  }\n\n  local lsbRelease=$($lsbRelease -ds | $SED -e 's/^\"//'  -e 's/\"$//')\n  local uname=$($uName -r | $SED -e 's/^\"//'  -e 's/\"$//')\n  local os=$($ECHO $lsbRelease $uname)\n  local hostname=$($hostName)\n  local uptime_seconds=$($CAT /proc/uptime | awk '{print $1}')\n  local server_time=$(date)\n\n  $ECHO \"{ \\\"OS\\\": \\\"$os\\\", \\\"Hostname\\\": \\\"$hostname\\\", \\\"Uptime\\\": \\\" $(displaytime ${uptime_seconds%.*}) \\\", \\\"Server Time\\\": \\\"$server_time\\\" }\" | _parseAndPrint\n}\n\nio_stats() {\n\n  result=$($CAT /proc/diskstats | $AWK \\\n          '{ if($4==0 && $8==0 && $12==0 && $13==0) next } \\\n          {print \"{ \\\"device\\\": \\\"\" $3 \"\\\", \\\"reads\\\": \\\"\"$4\"\\\", \\\"writes\\\": \\\"\" $8 \"\\\", \\\"in_prog.\\\": \\\"\" $12 \"\\\", \\\"time\\\": \\\"\" $13 \"\\\"},\"}'\n      )\n\n  $ECHO [ ${result%?} ] | _parseAndPrint\n}\n\nip_addresses() {\n\n  local ifconfigCmd=$(type -P ifconfig)\n  local digCmd=$(type -P dig)\n\n  externalIp=$($digCmd +short myip.opendns.com @resolver1.opendns.com)\n\n  $ECHO -n \"[\"\n\n  for item in $($ifconfigCmd | $GREP -oP \"^[a-zA-Z0-9:]*(?=:)\")\n  do\n      $ECHO -n \"{\\\"interface\\\" : \\\"\"$item\"\\\", \\\"ip\\\" : \\\"$( $ifconfigCmd $item | $GREP \"inet\" | $AWK '{match($0,\"inet (addr:)?([0-9.]*)\",a)}END{ if (NR != 0){print a[2]; exit}{print \"none\"}}')\\\"}, \"\n  done\n\n  $ECHO \"{ \\\"interface\\\": \\\"external\\\", \\\"ip\\\": \\\"$externalIp\\\" } ]\" | _parseAndPrint\n}\n\nload_avg() {\n\n  local numberOfCores=$($GREP -c 'processor' /proc/cpuinfo)\n\n  if [ $numberOfCores -eq 0 ]; then\n    numberOfCores=1\n  fi\n\n  result=$($CAT /proc/loadavg | $AWK '{print \"{ \\\"1_min_avg\\\": \" ($1*100)/'$numberOfCores' \", \\\"5_min_avg\\\": \" ($2*100)/'$numberOfCores' \", \\\"15_min_avg\\\": \" ($3*100)/'$numberOfCores' \"},\" }')\n\n  $ECHO ${result%?} | _parseAndPrint\n}\n\nlogged_in_users() {\n  local whoCommand=$(type -P w)\n\n  result=$(COLUMNS=300 $whoCommand -h | $AWK '{print \"{\\\"user\\\": \\\"\" $1 \"\\\", \\\"from\\\": \\\"\" $3 \"\\\", \\\"when\\\": \\\"\" $4 \"\\\"},\"}')\n\n  $ECHO [ ${result%?} ] | _parseAndPrint\n}\n\nmemcached() {\n  local ncCommand=$(type -P nc)\n\n  $ECHO \"stats\" \\\n    | $ncCommand -w 1 127.0.0.1 11211 \\\n    | $GREP 'bytes' \\\n    | $AWK 'BEGIN {print \"{\"} {print \"\\\"\" $2 \"\\\": \" $3 } END {print \"}\"}' \\\n    | $TR '\\r' ',' \\\n    | $SED 'N;$s/,\\n/\\n/;P;D' \\\n    | _parseAndPrint\n}\n\nmemory_info() {\n\n  $CAT /proc/meminfo \\\n    | $AWK -F: 'BEGIN {print \"{\"} {print \"\\\"\" $1 \"\\\": \\\"\" $2 \"\\\",\" } END {print \"}\"}' \\\n    | $SED 'N;$s/,\\n/\\n/;P;D' \\\n    | _parseAndPrint\n}\n\nnetwork_connections() {\n\n  local netstatCmd=$(type -P netstat)\n  local sortCmd=$(type -P sort)\n  local uniqCmd=$(type -P uniq)\n\n  $netstatCmd -ntu \\\n  | $AWK 'NR>2 {print $5}' \\\n  | $sortCmd \\\n  | $uniqCmd -c \\\n  | $AWK 'BEGIN {print \"[\"} {print \"{ \\\"connections\\\": \" $1 \", \\\"address\\\": \\\"\" $2 \"\\\" },\" } END {print \"]\"}' \\\n  | $SED 'N;$s/},/}/;P;D' \\\n  | _parseAndPrint\n}\n\nnumber_of_cpu_cores() {\n\n  local numberOfCPUCores=$($GREP -c 'model name' /proc/cpuinfo)\n\n  if [ -z $numberOfCPUCores ]; then\n    echo \"cannot be found\";\n  fi\n}\n\n# http://askubuntu.com/questions/413367/ping-multiple-ips-using-bash\nping() {\n\n\t# get absolute path to config file\n    local SCRIPTPATH=$(dirname $(readlink -f $0))\n\tlocal CONFIG_PATH=$SCRIPTPATH\"/config/ping_hosts\"\n\n    local pingCmd=$(type -P ping)\n    local numOfLinesInConfig=$($SED -n '$=' $CONFIG_PATH)\n\tlocal result='['\n\n\t$CAT $CONFIG_PATH \\\n\t|  while read output\n\t\tdo\n\t\t   \tsinglePing=$($pingCmd -qc 2 $output \\\n\t\t    | $AWK -F/ 'BEGIN { endLine=\"},\" } /^rtt/ { if ('$numOfLinesInConfig'==1){endLine=\"}\"} print \"{\" \"\\\"host\\\": \\\"'$output'\\\", \\\"ping\\\": \" $5 \" \" endLine }' \\\n\t\t    )\n\t\t    numOfLinesInConfig=$(($numOfLinesInConfig-1))\n\t\t    result=$result$singlePing\n\t\t\tif [ $numOfLinesInConfig -eq 0 ]\n\t\t\t\tthen\n\t\t\t\t\t$ECHO $result\"]\"\n\t\t\tfi\n\t\tdone \\\n\t| $SED 's/\\},]/}]/g' \\\n  | _parseAndPrint\n}\n\npm2_stats() {\n\n\t#get data\n\tlocal data=\"$(pm2 list)\"\n    local tailCommand=$(type -P tail)\n\n\t#only process data if variable has a length\n\t#this should handle cases where pm2 is not installed\n\tif [ -n \"$data\" ]; then\n\n\t\t#start processing data on line 4\n\t\t#don't process last 2 lines\n\t\tjson=$( $ECHO \"$data\" | $tailCommand -n +4 | $HEAD -n +2 \\\n\t\t| $AWK \t'{print \"{\"}\\\n\t\t\t{print \"\\\"appName\\\":\\\"\" $2 \"\\\",\"} \\\n\t\t\t{print \"\\\"id\\\":\\\"\" $4 \"\\\",\"} \\\n\t\t\t{print \"\\\"mode\\\":\\\"\" $6 \"\\\",\"} \\\n\t\t\t{print \"\\\"pid\\\":\\\"\" $8 \"\\\",\"}\\\n\t\t\t{print \"\\\"status\\\":\\\"\" $10 \"\\\",\"}\\\n\t\t\t{print \"\\\"restart\\\":\\\"\" $12 \"\\\",\"}\\\n\t\t\t{print \"\\\"uptime\\\":\\\"\" $14 \"\\\",\"}\\\n\t\t\t{print \"\\\"memory\\\":\\\"\" $16 $17 \"\\\",\"}\\\n\t\t\t{print \"\\\"watching\\\":\\\"\" $19 \"\\\"\"}\\\n\t\t\t{print \"},\"}')\n\t\t#make sure to remove last comma and print in array\n\t\t$ECHO \"[\" ${json%?} \"]\" | _parseAndPrint\n\telse\n\t\t#no data found\n\t\t$ECHO \"[]\" | _parseAndPrint\n\tfi\n}\n\nram_intensive_processes() {\n\n  local psCommand=$(type -P ps)\n\n  result=$($psCommand axo pid,user,pmem,rss,vsz,comm --sort -pmem,-rss,-vsz \\\n        | $HEAD -n 15 \\\n        | $AWK 'NR>1 {print \"{ \\\"pid\\\": \" $1 \\\n                      \", \\\"user\\\": \\\"\" $2 \\\n                      \"\\\", \\\"mem%\\\": \" $3 \\\n                      \", \\\"rss\\\": \" $4 \\\n                      \", \\\"vsz\\\": \" $5 \\\n                      \", \\\"cmd\\\": \\\"\" $6 \\\n                      \"\\\"},\"}')\n\n  $ECHO [ ${result%?} ] | _parseAndPrint\n}\n\nrecent_account_logins() {\n\n  local lastLogCommand=$(type -p lastlog)\n\n  result=$($lastLogCommand -t 365 \\\n        | $AWK 'NR>1 {\\\n          print \"{ \\\n            \\\"user\\\": \\\"\" $1 \"\\\", \\\n            \\\"ip\\\": \\\"\" $3 \"\\\",\"\" \\\n            \\\"date\\\": \\\"\" $5\" \"$6\" \"$7\" \"$8\" \"$9 \"\\\"},\"\n          }'\n      )\n  $ECHO [ ${result%?} ] | _parseAndPrint\n}\n\nredis() {\n\n  ########### Enter Your Redis Password  HERE #########\n  local redisPassword=''\n  ########### Enter Your Redis Password  HERE #########\n\n  local redisCommand=$(type -P redis-cli);\n\n  if [ -n \"$redisPassword\" ]; then\n    redisCommand=\"$redisCommand -a $redisPassword\"\n  fi\n\n  result=$($redisCommand INFO \\\n        | $GREP 'redis_version\\|connected_clients\\|connected_slaves\\|used_memory_human\\|total_connections_received\\|total_commands_processed' \\\n        | $AWK -F: '{print \"\\\"\" $1 \"\\\":\" \"\\\"\" $2 }' \\\n        | $TR '\\r' '\"' | $TR '\\n' ','\n      )\n  $ECHO { ${result%?} } | _parseAndPrint\n}\n\nscheduled_crons() {\n\n    ######\n    # Credit: http://stackoverflow.com/questions/134906/how-do-i-list-all-cron-jobs-for-all-users#answer-137173\n    ######\n\n    local egrepCmd=$(type -P egrep)\n    local crontabCmd=$(type -P crontab)\n\n    # System-wide crontab file and cron job directory. Change these for your system.\n    CRONTAB='/etc/crontab'\n    CRONDIR='/etc/cron.d'\n\n    # Single tab character. Annoyingly necessary.\n    tab=$($ECHO -en \"\\t\")\n\n    # Given a stream of crontab lines, exclude non-cron job lines, replace\n    # whitespace characters with a single space, and remove any spaces from the\n    # beginning of each line.\n    function clean_cron_lines() {\n        while read line ; do\n            $ECHO \"${line}\" |\n                $egrepCmd --invert-match '^($|\\s*#|\\s*[[:alnum:]_]+=)' |\n                $SED --regexp-extended \"s/\\s+/ /g\" |\n                $SED --regexp-extended \"s/^ //\"\n        done;\n    }\n\n    # Given a stream of cleaned crontab lines, $echoCmd any that don't include the\n    # run-parts command, and for those that do, show each job file in the run-parts\n    # directory as if it were scheduled explicitly.\n    function lookup_run_parts() {\n        while read line ; do\n            match=$($ECHO \"${line}\" | $egrepCmd -o 'run-parts (-{1,2}\\S+ )*\\S+')\n\n            if [[ -z \"${match}\" ]] ; then\n                $echoCmd \"${line}\"\n            else\n                cron_fields=$($ECHO \"${line}\" | $CUT -f1-6 -d' ')\n                cron_job_dir=$($ECHO  \"${match}\" | $AWK '{print $NF}')\n\n                if [[ -d \"${cron_job_dir}\" ]] ; then\n                    for cron_job_file in \"${cron_job_dir}\"/* ; do  # */ <not a comment>\n                        [[ -f \"${cron_job_file}\" ]] && $ECHO \"${cron_fields} ${cron_job_file}\"\n                    done\n                fi\n            fi\n        done;\n    }\n\n    # Temporary file for crontab lines.\n    temp=$(mktemp) || exit 1\n\n    # Add all of the jobs from the system-wide crontab file.\n    $CAT \"${CRONTAB}\" | clean_cron_lines | lookup_run_parts >\"${temp}\"\n\n    # Add all of the jobs from the system-wide cron directory.\n    $CAT \"${CRONDIR}\"/* | clean_cron_lines >>\"${temp}\"  # */ <not a comment>\n\n    # Add each user's crontab (if it exists). Insert the user's name between the\n    # five time fields and the command.\n    while read user ; do\n        $crontabCmd -l -u \"${user}\" 2>/dev/null |\n            clean_cron_lines |\n            $SED --regexp-extended \"s/^((\\S+ +){5})(.+)$/\\1${user} \\3/\" >>\"${temp}\"\n    done < <($CUT --fields=1 --delimiter=: /etc/passwd)\n\n    # Output the collected crontab lines.\n\n    ## Changes: Parses output into JSON\n\n    $CAT \"${temp}\" \\\n        | $AWK 'BEGIN {print \"[\"} \\\n                    {print \"{ \\\"min\\\": \\\"\" $1 \\\n                    \"\\\", \\\"hrs\\\": \\\"\" $2 \"\\\", \" \\\n                    \" \\\"day\\\": \\\"\" $3 \"\\\", \" \\\n                    \" \\\"month\\\": \\\"\" $4 \"\\\", \" \\\n                    \" \\\"wkday\\\": \\\"\" $5 \"\\\", \" \\\n                    \" \\\"user\\\": \\\"\" $6 \"\\\", \" \\\n                    \" \\\"CMD\\\": \\\"\"} \\\n                        {for(i=7;i<=NF;++i) printf(\"%s \", gensub(\"\\\"\", \"\\\\\\\\\\\"\", \"g\", $i) ) } \\\n                    {print \"\\\" \" \\\n                    \"},\" } \\\n                END {print \"]\"}' \\\n        | $SED 'N;$s/,\\n//;P;D' \\\n        | _parseAndPrint\n\n    rm --force \"${temp}\"\n}\n\nswap() {\n\n  local wcCmd=$(which wc)\n\n  local swapLineCount=$($CAT /proc/swaps | $wcCmd -l)\n\n  if [ \"$swapLineCount\" -gt 1 ]; then\n\n    result=$($CAT /proc/swaps \\\n        | $AWK 'NR>1 {print \"{ \\\"filename\\\": \\\"\" $1\"\\\", \\\"type\\\": \\\"\"$2\"\\\", \\\"size\\\": \\\"\"$3\"\\\", \\\"used\\\": \\\"\"$4\"\\\", \\\"priority\\\": \\\"\"$5\"\\\"},\" }'\n      )\n\n    $ECHO [ ${result%?} ] | _parseAndPrint\n\n  else\n    $ECHO [] | _parseAndPrint\n  fi\n}\n\nupload_transfer_rate() {\n\n\tlocal files=(/sys/class/net/*)\n\tlocal pos=$(( ${#files[*]} - 1 ))\n\tlocal last=${files[$pos]}\n\n\tlocal json_output=\"{\"\n\n\tfor interface in \"${files[@]}\"\n\tdo\n\t\tbasename=$(basename \"$interface\")\n\n\t\t# find the number of bytes transfered for this interface\n\t\tout1=$($CAT /sys/class/net/\"$basename\"/statistics/tx_bytes)\n\n\t\t# wait a second\n\t\tsleep 1\n\n\t\t# check same interface again\n\t\tout2=$($CAT /sys/class/net/\"$basename\"/statistics/tx_bytes)\n\n\t\t# get the difference (transfer rate)\n\t\tout_bytes=$((out2 - out1))\n\n\t\t# convert transfer rate to KB\n\t\tout_kbytes=$((out_bytes / 1024))\n\n\t\t# convert transfer rate to KB\n\t\tjson_output=\"$json_output \\\"$basename\\\": $out_kbytes\"\n\n\t\t# if it is not the last line\n\t\tif [[ ! $interface == $last ]]\n\t\tthen\n\t\t\t# add a comma to the line (JSON formatting)\n\t\t\tjson_output=\"$json_output,\"\n\t\tfi\n\tdone\n\n\t# close the JSON object & print to screen\n\t$ECHO \"$json_output}\" | _parseAndPrint\n}\n\nuser_accounts() {\n  result=$($AWK -F: '{ \\\n          if ($3<=499){userType=\"system\";} \\\n          else {userType=\"user\";} \\\n          print \"{ \\\"type\\\": \\\"\" userType \"\\\"\" \", \\\"user\\\": \\\"\" $1 \"\\\", \\\"home\\\": \\\"\" $6 \"\\\" },\" }' < /etc/passwd\n      )\n\n  length=$($ECHO ${#result})\n\n  if [ $length -eq 0 ]; then\n    result=$(getent passwd | $AWK -F: '{ if ($3<=499){userType=\"system\";} else {userType=\"user\";} print \"{ \\\"type\\\": \\\"\" userType \"\\\"\" \", \\\"user\\\": \\\"\" $1 \"\\\", \\\"home\\\": \\\"\" $6 \"\\\" },\" }')\n  fi\n\n  $ECHO [ ${result%?} ] | _parseAndPrint\n}\n\nfnCalled=\"$1\"\n\n# Check if the function call is indeed a function.\nif [ -n \"$(type -t $fnCalled)\" ] && [ \"$(type -t $fnCalled)\" = function ]; then\n    ${fnCalled}\nelse\n    echo '{\\\"success\\\":false,\\\"status\\\":\\\"Invalid module\\\"}'\nfi\n\n"
  },
  {
    "path": "bin/linux-dash",
    "content": "#!/usr/bin/env node\nrequire('../app/server/index.js')\n"
  },
  {
    "path": "demo.js",
    "content": "angular\n  .module('linuxDashDemo', ['linuxDash', 'ngMockE2E'])\n  .run(function($httpBackend) {\n\n// signal to not use websockets for demo\n$httpBackend.whenGET('/websocket').respond(false)\n\n  /** System Status */\n    $httpBackend.whenGET('server/?module=current_ram').respond(function () {\n      return [200, {total: 512, used: 200, available: 312 }]\n    })\n\n    $httpBackend.whenGET('server/?module=ram_intensive_processes').respond(function () {\n      return [200, [ { 'pid': 2782, 'user': 'user1', 'mem%': 2.2, 'rss': 232332, 'vsz': 1118240, 'cmd': 'nginx'}, { 'pid': 2623, 'user': 'user1', 'mem%': 2.0, 'rss': 209732, 'vsz': 1321800, 'cmd': 'nginx'}, { 'pid': 1558, 'user': 'user1', 'mem%': 1.3, 'rss': 140796, 'vsz': 1321400, 'cmd': 'gnome-software'}, { 'pid': 5026, 'user': 'user1', 'mem%': 1.2, 'rss': 126312, 'vsz': 875072, 'cmd': 'nginx'}, { 'pid': 5051, 'user': 'user1', 'mem%': 1.1, 'rss': 112776, 'vsz': 872512, 'cmd': 'nginx'}, { 'pid': 1477, 'user': 'user1', 'mem%': 1.0, 'rss': 108248, 'vsz': 1471548, 'cmd': 'compiz'}, { 'pid': 2997, 'user': 'user1', 'mem%': 1.0, 'rss': 107208, 'vsz': 869076, 'cmd': 'nginx'}, { 'pid': 2298, 'user': 'root', 'mem%': 1.0, 'rss': 101356, 'vsz': 286772, 'cmd': 'aptd'}, { 'pid': 2700, 'user': 'user1', 'mem%': 0.9, 'rss': 95784, 'vsz': 460272, 'cmd': 'nginx'}, { 'pid': 2799, 'user': 'user1', 'mem%': 0.8, 'rss': 83940, 'vsz': 778512, 'cmd': 'nginx'}, { 'pid': 4248, 'user': 'user1', 'mem%': 0.8, 'rss': 82276, 'vsz': 1059420, 'cmd': 'redis'}, { 'pid': 1095, 'user': 'root', 'mem%': 0.7, 'rss': 78424, 'vsz': 457156, 'cmd': 'Xorg'}, { 'pid': 2810, 'user': 'user1', 'mem%': 0.7, 'rss': 72884, 'vsz': 750856, 'cmd': 'nginx'}, { 'pid': 2809, 'user': 'user1', 'mem%': 0.7, 'rss': 72872, 'vsz': 752908, 'cmd': 'nginx'} ]]\n    })\n\n    $httpBackend.whenGET('server/?module=cpu_intensive_processes').respond(function () {\n      return [200, [ { 'pid': 5026, 'user': 'user1', 'cpu%': 25.5, 'rss': 137944, 'vsz': 907236, 'cmd': 'nginx'}, { 'pid': 2623, 'user': 'user1', 'cpu%': 9.5, 'rss': 229016, 'vsz': 1327948, 'cmd': 'nginx'}, { 'pid': 2700, 'user': 'user1', 'cpu%': 8.8, 'rss': 106324, 'vsz': 474796, 'cmd': 'nginx'}, { 'pid': 4248, 'user': 'user1', 'cpu%': 8.8, 'rss': 96848, 'vsz': 1151460, 'cmd': 'sublime_text'}, { 'pid': 1477, 'user': 'user1', 'cpu%': 8.6, 'rss': 111252, 'vsz': 1476524, 'cmd': 'compiz'}, { 'pid': 1571, 'user': 'user1', 'cpu%': 7.7, 'rss': 62984, 'vsz': 551884, 'cmd': 'orca'}, { 'pid': 1095, 'user': 'root', 'cpu%': 6.2, 'rss': 86048, 'vsz': 457112, 'cmd': 'Xorg'}, { 'pid': 1535, 'user': 'user1', 'cpu%': 3.9, 'rss': 17152, 'vsz': 574624, 'cmd': 'pulseaudio'}, { 'pid': 5051, 'user': 'user1', 'cpu%': 1.6, 'rss': 131828, 'vsz': 888712, 'cmd': 'nginx'}, { 'pid': 2890, 'user': 'user1', 'cpu%': 1.1, 'rss': 52968, 'vsz': 657408, 'cmd': 'unity-panel-ser'}, { 'pid': 2782, 'user': 'user1', 'cpu%': 1.0, 'rss': 226536, 'vsz': 1110556, 'cmd': 'nginx'}, { 'pid': 1438, 'user': 'user1', 'cpu%': 0.9, 'rss': 45840, 'vsz': 660352, 'cmd': 'hud-service'}, { 'pid': 1631, 'user': 'user1', 'cpu%': 0.9, 'rss': 26208, 'vsz': 514684, 'cmd': 'indicator-multi'}, { 'pid': 1804, 'user': 'user1', 'cpu%': 0.9, 'rss': 9752, 'vsz': 523936, 'cmd': 'sd_espeak'} ]]\n    })\n\n    $httpBackend.whenGET('server/?module=disk_partitions').respond(function () {\n      return [200, [ {'file_system': 'udev', 'size': '4.9G', 'used': '0', 'avail': '4.9G', 'used%': '0%', 'mounted': '/dev'}, {'file_system': '/dev/sda1', 'size': '449G', 'used': '224.5G', 'avail': '224.5G', 'used%': '50%', 'mounted': '/'}, {'file_system': 'tmpfs', 'size': '5.0M', 'used': '4.0K', 'avail': '5.0M', 'used%': '1%', 'mounted': '/run/lock'} ]]\n    })\n\n    $httpBackend.whenGET('server/?module=cpu_temp').respond(function () {\n      return [200, [ ]]\n    })\n\n    $httpBackend.whenGET('server/?module=cpu_temp').respond(function () {\n      return [200, [ ]]\n    })\n\n    $httpBackend.whenGET('server/?module=cpu_utilization').respond(function () {\n      return [200, 25]\n    })\n\n    $httpBackend.whenGET('server/?module=load_avg').respond(function () {\n      return [200, { '1_min_avg': 40, '5_min_avg': 22, '15_min_avg': 10}]\n    })\n\n    $httpBackend.whenGET('server/?module=docker_processes').respond(function () {\n      return [200, []]\n    })\n\n    $httpBackend.whenGET('server/?module=swap').respond(function () {\n      return [200, [ { 'filename': '/dev/sda5', 'type': 'partition', 'size': '10364924', 'used': '0', 'priority': '-1'} ]]\n    })\n\n  /** Basic Info **/\n    $httpBackend.whenGET('server/?module=cron_history').respond(function () {\n      return [200, []]\n    })\n\n    $httpBackend.whenGET('server/?module=io_stats').respond(function () {\n      return [200, [ { 'device': 'sda', 'reads': '75406', 'writes': '119939', 'in_prog.': '0', 'time': '816604'}, { 'device': 'sda1', 'reads': '74587', 'writes': '81983', 'in_prog.': '0', 'time': '796964'}, { 'device': 'sda2', 'reads': '6', 'writes': '0', 'in_prog.': '0', 'time': '304'}, { 'device': 'sda5', 'reads': '73', 'writes': '0', 'in_prog.': '0', 'time': '3344'}, { 'device': 'sdb', 'reads': '276', 'writes': '0', 'in_prog.': '0', 'time': '72'}, { 'device': 'sdb1', 'reads': '234', 'writes': '0', 'in_prog.': '0', 'time': '64'} ]]\n    })\n\n    $httpBackend.whenGET('server/?module=cpu_info').respond(function () {\n      return [200, { \"Architecture\": \" x86_64\", \"CPU op-mode(s)\": \" 32-bit, 64-bit\", \"Byte Order\": \" Little Endian\", \"CPU(s)\": \" 4\", \"On-line CPU(s) list\": \" 0-3\", \"Thread(s) per core\": \" 2\", \"Core(s) per socket\": \" 2\", \"Socket(s)\": \" 1\", \"NUMA node(s)\": \" 1\", \"Vendor ID\": \" GenuineIntel\", \"CPU family\": \" 6\", \"Model\": \" 69\", \"Model name\": \" Intel(R) Core(TM) i5-4200U CPU @ 1.60GHz\", \"Stepping\": \" 1\", \"CPU MHz\": \" 1402.101\", \"CPU max MHz\": \" 2600.0000\", \"CPU min MHz\": \" 800.0000\", \"BogoMIPS\": \" 4589.14\", \"Virtualization\": \" VT-x\", \"L1d cache\": \" 32K\", \"L1i cache\": \" 32K\", \"L2 cache\": \" 256K\", \"L3 cache\": \" 3072K\", \"NUMA node0 CPU(s)\": \" 0-3\", \"Flags\": \" fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm epb tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts\" }]\n    })\n\n    $httpBackend.whenGET('server/?module=scheduled_crons').respond(function () {\n      return [200, [ ]]\n    })\n\n    $httpBackend.whenGET('server/?module=memory_info').respond(function () {\n      return [200, {'MemTotal': '10120412 kB','MemFree': '7257156 kB','MemAvailable': '8173888 kB','Buffers': '   257824 kB','Cached': '   1175940 kB','SwapCached': ' 0 kB','Active': '   1815224 kB','Inactive': '  783844 kB','Active(anon)': '1174832 kB','Inactive(anon)': '   334816 kB','Active(file)': ' 640392 kB','Inactive(file)': '   449028 kB','Unevictable': '  384 kB','Mlocked': '  384 kB','SwapTotal': '  10364924 kB','SwapFree': '10364924 kB','Dirty': ' 156 kB','Writeback': '  0 kB','AnonPages': '1165736 kB','Mapped': '420416 kB','Shmem': ' 344344 kB','Slab': '  143556 kB','SReclaimable': ' 109592 kB','SUnreclaim': ' 33964 kB','KernelStack': ' 8560 kB','PageTables': ' 32700 kB','NFS_Unstable': '   0 kB','Bounce': '  0 kB','WritebackTmp': '   0 kB','CommitLimit': '15425128 kB','Committed_AS': '5073516 kB','VmallocTotal': '   34359738367 kB','VmallocUsed': '0 kB','VmallocChunk': '   0 kB','HardwareCorrupted': ' 0 kB','AnonHugePages': '352256 kB','CmaTotal': '0 kB','CmaFree': ' 0 kB','HugePages_Total': '0','HugePages_Free': ' 0','HugePages_Rsvd': ' 0','HugePages_Surp': ' 0','Hugepagesize': '2048 kB','DirectMap4k': '  130800 kB','DirectMap2M': ' 4993024 kB','DirectMap1G': ' 6291456 kB'}]\n    })\n\n    $httpBackend.whenGET('server/?module=general_info').respond(function () {\n      return [200, { 'OS': 'Ubuntu 16.04.2 LTS 4.4.0-77-generic', 'Hostname': 'webserver-prod-983742', 'Uptime': '5 hours and 32 minutes and 27 seconds ', 'Server Time': 'Sat May 13 00:11:39 EST 2017' }]\n    })\n\n  /** Network **/\n    $httpBackend.whenGET('server/?module=download_transfer_rate').respond(function () {\n      return [200, { \"lo\": 2048, \"wlp3s0\": 1024 }]\n    })\n\n    $httpBackend.whenGET('server/?module=upload_transfer_rate').respond(function () {\n      return [200, { \"lo\": 1024, \"wlp3s0\": 512 }]\n    })\n\n    $httpBackend.whenGET('server/?module=bandwidth').respond(function () {\n      return [200, [{ 'interface': 'wlp3s0:', 'tx': 226076758, 'rx': 9037438 },{ 'interface': 'lo:', 'tx': 12016995, 'rx': 12016995 }]]\n    })\n\n    $httpBackend.whenGET('server/?module=ping').respond(function () {\n      return [200, [{'host': 'google.com', 'ping': 23.234 },{'host': 'yahoo.com', 'ping': 67.412 },{'host': 'twitter.com', 'ping': 34.560 }]]\n    })\n\n    $httpBackend.whenGET('server/?module=ip_addresses').respond(function () {\n      return [200, [{ 'interface': 'external', 'ip': '70.113.122.2' } ]]\n    })\n\n    $httpBackend.whenGET('server/?module=network_connections').respond(function () {\n      return [200, [{ 'connections': 1, 'address': '127.0.0.1:48562' }\n        ,{ 'connections': 1, 'address': '127.0.0.1:48564' }\n        ,{ 'connections': 1, 'address': '127.0.0.1:48708' }\n        ,{ 'connections': 3, 'address': '127.0.0.1:8080' }\n        ,{ 'connections': 1, 'address': '192.241.178.140:443' }\n        ,{ 'connections': 2, 'address': '2657:f9b0:9000:802::443' }\n        ,{ 'connections': 1, 'address': '2657:f9b0:0000:80c::443' }\n        ,{ 'connections': 1, 'address': '2657:f9b0:0000:80f:::80' }\n        ,{ 'connections': 2, 'address': '2657:f9b0:0000:816:::80' }\n        ,{ 'connections': 1, 'address': '2657:f9b0:0003:c09:5228' }]]\n    })\n\n    $httpBackend.whenGET('server/?module=arp_cache').respond(function () {\n      return [200, [ { 'addr': '192.168.0.1', 'hw_type': 'ether', 'hw_addr.': '15:db:45:eb:4d:6a', 'mask': 'wlp3s0' } ]]\n    })\n\n    $httpBackend.whenGET('server/?module=logged_in_users').respond(function () {\n      return [200, [ {'user': 'user1', 'from': ':0', 'when': 'Fri23'}, {'user': 'user1', 'from': 'webserver-prod-983742', 'when': 'Fri23'}, {'user': 'user1', 'from': 'webserver-prod-983742', 'when': 'Fri23'}, {'user': 'user1', 'from': 'webserver-prod-983742', 'when': '01:19'} ]]\n    })\n\n    $httpBackend.whenGET('server/?module=user_accounts').respond(function () {\n      return [200, [ {\"type\":\"system\", \"user\":\"root\", \"home\":\"/root\"}, {\"type\":\"system\", \"user\":\"daemon\", \"home\":\"/usr/sbin\"}, {\"type\":\"system\", \"user\":\"bin\", \"home\":\"/bin\"}, {\"type\":\"system\", \"user\":\"sys\", \"home\":\"/dev\"}, {\"type\":\"system\", \"user\":\"sync\", \"home\":\"/bin\"}, {\"type\":\"system\", \"user\":\"games\", \"home\":\"/usr/games\"}, {\"type\":\"system\", \"user\":\"man\", \"home\":\"/var/cache/man\"}, {\"type\":\"system\", \"user\":\"lp\", \"home\":\"/var/spool/lpd\"}, {\"type\":\"system\", \"user\":\"mail\", \"home\":\"/var/mail\"}, {\"type\":\"system\", \"user\":\"news\", \"home\":\"/var/spool/news\"}, {\"type\":\"system\", \"user\":\"uucp\", \"home\":\"/var/spool/uucp\"}, {\"type\":\"system\", \"user\":\"proxy\", \"home\":\"/bin\"}, {\"type\":\"system\", \"user\":\"www-data\", \"home\":\"/var/www\"}, {\"type\":\"system\", \"user\":\"backup\", \"home\":\"/var/backups\"}, {\"type\":\"system\", \"user\":\"list\", \"home\":\"/var/list\"}, {\"type\":\"system\", \"user\":\"irc\", \"home\":\"/var/run/ircd\"}, {\"type\":\"system\", \"user\":\"gnats\", \"home\":\"/var/lib/gnats\"}, {\"type\":\"user\", \"user\":\"nobody\", \"home\":\"/nonexistent\"}, {\"type\":\"system\", \"user\":\"systemd-timesync\", \"home\":\"/run/systemd\"} ]]\n    })\n\n    $httpBackend.whenGET('server/?module=recent_account_logins').respond(function () {\n      return [200, [ {\"type\":\"system\", \"user\":\"root\", \"home\":\"/root\"}, {\"type\":\"system\", \"user\":\"daemon\", \"home\":\"/usr/sbin\"}, {\"type\":\"system\", \"user\":\"bin\", \"home\":\"/bin\"}, {\"type\":\"system\", \"user\":\"sys\", \"home\":\"/dev\"}, {\"type\":\"system\", \"user\":\"sync\", \"home\":\"/bin\"}, {\"type\":\"system\", \"user\":\"games\", \"home\":\"/usr/games\"}, {\"type\":\"system\", \"user\":\"man\", \"home\":\"/var/cache/man\"}, {\"type\":\"system\", \"user\":\"lp\", \"home\":\"/var/spool/lpd\"}, {\"type\":\"system\", \"user\":\"mail\", \"home\":\"/var/mail\"}, {\"type\":\"system\", \"user\":\"news\", \"home\":\"/var/spool/news\"}, {\"type\":\"system\", \"user\":\"uucp\", \"home\":\"/var/spool/uucp\"}, {\"type\":\"system\", \"user\":\"proxy\", \"home\":\"/bin\"}, {\"type\":\"system\", \"user\":\"www-data\", \"home\":\"/var/www\"}, {\"type\":\"system\", \"user\":\"backup\", \"home\":\"/var/backups\"}, {\"type\":\"system\", \"user\":\"list\", \"home\":\"/var/list\"}, {\"type\":\"system\", \"user\":\"irc\", \"home\":\"/var/run/ircd\"}, {\"type\":\"system\", \"user\":\"gnats\", \"home\":\"/var/lib/gnats\"}, {\"type\":\"user\", \"user\":\"nobody\", \"home\":\"/nonexistent\"}, {\"type\":\"system\", \"user\":\"systemd-timesync\", \"home\":\"/run/systemd\"} ]]\n    })\n\n  /** Applications **/\n    $httpBackend.whenGET('server/?module=common_applications').respond(function () {\n      return [200, [ { \"binary\": \"php\", \"location\": \"\", \"installed\": \"installed\" }, { \"binary\": \"node\", \"location\": \" /usr/bin/node /usr/include/node /usr/share/man/man1/node.1.gz\", \"installed\": \"installed\" }, { \"binary\": \"mysql\", \"location\": \"\", \"installed\": \"installed\" }, { \"binary\": \"mongo\", \"location\": \"\", \"installed\": \"installed\" }, { \"binary\": \"vim\", \"location\": \" /usr/bin/vim.basic /usr/bin/vim /usr/bin/vim.tiny /etc/vim /usr/share/vim /usr/share/man/man1/vim.1.gz\", \"installed\": \"installed\" }, { \"binary\": \"python\", \"location\": \" /usr/bin/python3.5 /usr/bin/python /usr/bin/python2.7-config /usr/bin/python3.5m /usr/bin/python2.7 /usr/lib/python3.5 /usr/lib/python2.7 /etc/python3.5 /etc/python /etc/python2.7 /usr/local/lib/python3.5 /usr/local/lib/python2.7 /usr/include/python3.5m /usr/include/python2.7 /usr/share/python /usr/share/man/man1/python.1.gz\", \"installed\": \"installed\" }, { \"binary\": \"ruby\", \"location\": \"\", \"installed\": \"installed\" }, { \"binary\": \"java\", \"location\": \" /usr/share/java\", \"installed\": \"installed\" }, { \"binary\": \"apache2\", \"location\": \"\", \"installed\": \"installed\" }, { \"binary\": \"nginx\", \"location\": \"\", \"installed\": \"installed\" }, { \"binary\": \"openssl\", \"location\": \" /usr/bin/openssl /usr/share/man/man1/openssl.1ssl.gz\", \"installed\": \"installed\" }, { \"binary\": \"vsftpd\", \"location\": \"\", \"installed\": \"installed\" }, { \"binary\": \"make\", \"location\": \" /usr/bin/make /usr/share/man/man1/make.1.gz\", \"installed\": \"installed\" } ]]\n    })\n\n    $httpBackend.whenGET('server/?module=memcached').respond(function () { return [200, {}] })\n    $httpBackend.whenGET('server/?module=redis').respond(function () { return [200, {}] })\n    $httpBackend.whenGET('server/?module=pm2_stats').respond(function () { return [200, []] })\n\n  })\n"
  },
  {
    "path": "ecosystem.config.js",
    "content": "module.exports = {\n  apps : [{\n    name: 'Linux Dash',\n    script: './app/server/index.js',\n    watch: false,\n    env: {\n      \"LINUX_DASH_SERVER_PORT\": 2800\n    },\n  }],\n};\n"
  },
  {
    "path": "gulpfile.js",
    "content": "var g             = require('gulp')\nvar concat        = require('gulp-concat')\nvar uglify        = require('gulp-uglify')\nvar cssmin        = require('gulp-cssmin')\nvar gutil         = require('gulp-util')\nvar ngAnnotate    = require('gulp-ng-annotate')\nvar templateCache = require('gulp-angular-templatecache')\n\ng.task('template-cache', function () {\n  return g.src('src/**/*.html')\n    .pipe(templateCache('templates.js', {\n        module: 'linuxDash',\n        standAlone: false,\n        root: 'src/'\n      }))\n    .pipe(g.dest('temp/'))\n})\n\ng.task('generate-js-dist', ['template-cache'], function () {\n  return g.src([\n    'node_modules/angular/angular.min.js',\n    'node_modules/angular-route/angular-route.min.js',\n    'node_modules/smoothie/smoothie.js',\n    'node_modules/sortablejs/Sortable.min.js',\n    'src/js/**/*.js',\n    'temp/templates.js'\n  ])\n  .pipe(concat('linuxDash.min.js'))\n  .pipe(ngAnnotate())\n  // .pipe(uglify())\n  .on('error', gutil.log)\n  .pipe(g.dest('app/'))\n})\n\ng.task('generate-css-dist', function () {\n  return g.src([ 'src/**/*.css' ])\n    .pipe(cssmin())\n    .pipe(concat('linuxDash.min.css'))\n    .pipe(g.dest('app/'))\n})\n\ng.task('build', [\n  'generate-js-dist',\n  'generate-css-dist'\n])\n\ng.task('watch', function () {\n  g.watch('src/**/*.css', ['generate-css-dist'])\n  g.watch(['src/**/*.js', 'src/**/*.html'], ['generate-js-dist'])\n})\n\ng.task('default', ['build', 'watch'])\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" ng-app=\"linuxDashDemo\">\n    <head>\n        <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n        <title>Linux Dash : Simple, beautiful server monitoring web dashboard</title>\n        <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n        <meta name=\"description\" content=\"Monitor your Linux server through a simple web dashboard. Open source and free!\">\n        <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n        <link href='//fonts.googleapis.com/css?family=Merriweather:300italic,300|Open+Sans:400,600' rel='stylesheet' type='text/css'>\n        <link href='app/linuxDash.min.css' rel='stylesheet' type='text/css'>\n\n        <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->\n        <!--[if lt IE 9]>\n          <script src=\"https://html5shim.googlecode.com/svn/trunk/html5.js\"></script>\n        <![endif]-->\n        <!-- Javascript-->\n\n    </head>\n    <body>\n\n        <nav-bar></nav-bar>\n\n        <!-- Templates Get Rendered Here -->\n        <div id=\"plugins\" ng-view></div>\n\n        <script src=\"app/linuxDash.min.js\" type=\"text/javascript\"></script>\n        <script src=\"//ajax.googleapis.com/ajax/libs/angularjs/1.3.4/angular-mocks.js\"></script>\n        <script src=\"demo.js\" type=\"text/javascript\"></script>\n    </body>\n</html>\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"linux-dash\",\n  \"version\": \"2.0.0\",\n  \"description\": \"A simple, low overhead web dashboard for linux.\",\n  \"main\": \"app/server/index.js\",\n  \"bin\": \"bin/linux-dash\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/afaqurk/linux-dash.git\"\n  },\n  \"keywords\": [\n    \"linux\",\n    \"gnu\",\n    \"dashboard\",\n    \"monitor\",\n    \"server\"\n  ],\n  \"author\": \"Afaq Tariq\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/afaqurk/linux-dash/issues\"\n  },\n  \"scripts\": {\n    \"start\": \"gulp\"\n  },\n  \"homepage\": \"https://github.com/afaqurk/linux-dash\",\n  \"dependencies\": {\n    \"express\": \"^4.11.1\",\n    \"websocket\": \"^1.0.23\",\n    \"yargs\": \"^8.0.1\"\n  },\n  \"devDependencies\": {\n    \"angular\": \"1.3.4\",\n    \"angular-route\": \"1.3.4\",\n    \"del\": \"^2.2.0\",\n    \"gulp\": \"^3.9.1\",\n    \"gulp-angular-templatecache\": \"^1.8.0\",\n    \"gulp-concat\": \"^2.6.0\",\n    \"gulp-cssmin\": \"^0.1.7\",\n    \"gulp-ng-annotate\": \"^2.0.0\",\n    \"gulp-uglify\": \"^1.5.3\",\n    \"gulp-util\": \"^3.0.7\",\n    \"smoothie\": \"^1.27.0\",\n    \"sortablejs\": \"^1.4.2\"\n  }\n}\n"
  },
  {
    "path": "src/css/README.md",
    "content": "# Global CSS\n\nThese are CSS files which set global & base element style rules.\n\n## main.css\n\nStyles for body & html tags.\n\n## plugins-container.css\n\nAll plugins rendered on a page are inside of this element.\n\n## tables.css\n\nBase table element styling.\n\nAlso contains styling for the `metrics-table` class, which is used to display the tabular data underneath the line and multiline charts.\n\n"
  },
  {
    "path": "src/css/buttons.css",
    "content": "button {\n  outline: none;\n}\n"
  },
  {
    "path": "src/css/main.css",
    "content": "html {\n  margin-top: 0px;\n  padding-top: 0;\n\n  -webkit-transition: all 1s ease;\n  -moz-transition: all 1s ease;\n  -ms-transition: all 1s ease;\n  -o-transition: all 1s ease;\n  transition: all 1s ease;\n}\n\nbody {\n  letter-spacing: .1rem;\n  font-family: 'Merriweather', Arial, sans-serif;\n  padding: 0;\n  margin: 0;\n}\n\n.centered {\n  margin: 0 auto;\n}\n"
  },
  {
    "path": "src/css/plugins-cotnainer.css",
    "content": "#plugins {\n  padding-top: 0;\n  margin-top: 0;\n  border: 1px;\n  margin-left: 10%;\n}\n\n@media (min-width: 1080px) {\n  #plugins {\n  }\n}\n\n@media (max-width: 1079px) {\n  #plugins {\n  }\n}\n"
  },
  {
    "path": "src/css/tables.css",
    "content": "table {\n  width: 100%;\n  font-size: 10px;\n  margin: 0;\n  border-collapse: collapse;\n  text-align: left;\n  table-layout:fixed;\n}\n\ntable th,\ntable td {\n  padding: 2px;\n  max-width: 250px;\n  word-wrap: break-word;\n}\n\ntable th{\n  font-weight: 600;\n  text-transform: uppercase;\n  border-bottom: 1px solid #f1f1f1;\n}\n\ntable td {\n  border-bottom: 1px solid #f1f1f1;\n  font-family: Arial, sans-serif;\n  font-size: 10px;\n  color: rgba(0,0,0,.65);\n}\n\ntable tbody tr:hover td {\n  background-color: #fafafa;\n}\n\ntable.metrics-table {\n  text-align: center;\n}\n\ntable.metrics-table tr:last-child td {\n  border: none;\n}\n"
  },
  {
    "path": "src/js/README.md",
    "content": "# JS (UI)\n\nThe root directory for _all_ of the UI JavaScript source files which power Linux Dash\n\nIt is an Angular JS (v1.3) SPA\n\n## core/\n\nContains all of the directives, services, and module declaration for the Angular SPA\n\nThis is where you can find all of the base UI elements that power the UI\n\nFor example, in this directory, you will find the base `line-chart` directive used by several plugins, the global `navbar` for Linux Dash pages, and other base UI elements.\n\n## plugins/\n\nContains the code for the individual plugins the user interacts with on the Linux Dash UI.\n"
  },
  {
    "path": "src/js/core/app.js",
    "content": "function runFn(server, $location, $rootScope) {\n  server.checkIfWebsocketsAreSupported()\n\n  $rootScope.$on(\"$locationChangeSuccess\", function(event, next, current) {\n    var nextRoute = next.split('#')[1]\n    if (nextRoute !== '/loading') {\n      localStorage.setItem('currentTab', nextRoute)\n    }\n  });\n\n  $location.path('/loading')\n}\n\nangular\n  .module('linuxDash', ['ngRoute'])\n  .run([ 'server', '$location', '$rootScope', runFn])\n  .config(['$compileProvider', function ($compileProvider) {\n    $compileProvider.debugInfoEnabled(false)\n  }])\n"
  },
  {
    "path": "src/js/core/features/key-value-list/key-value-list.directive.js",
    "content": "angular.module('linuxDash').directive('keyValueList', ['server', '$rootScope', function (server, $rootScope) {\n  return {\n    scope: {\n      heading: '@',\n      info: '@',\n      moduleName: '@',\n    },\n    templateUrl: 'src/js/core/features/key-value-list/key-value-list.html',\n    link: function(scope, element) {\n\n      scope.getData = function() {\n        delete scope.tableRows\n\n        server.get(scope.moduleName, function(serverResponseData) {\n          scope.tableRows = serverResponseData\n          scope.lastGet = new Date().getTime()\n\n          if (Object.keys(serverResponseData).length === 0) {\n            scope.emptyResult = true\n          }\n\n          if (!scope.$$phase && !$rootScope.$$phase) scope.$digest()\n        })\n      }\n\n      scope.getData()\n    }\n  }\n}])\n"
  },
  {
    "path": "src/js/core/features/key-value-list/key-value-list.html",
    "content": "<plugin\n  heading=\"{{ heading }}\"\n  last-updated=\"lastGet\"\n  on-refresh=\"getData()\"\n  info=\"{{ info }}\">\n\n  <loader ng-if=\"!tableRows\"></loader>\n\n  <div ng-show=\"tableRows\">\n    <table class=\"key-value-list\">\n      <tbody>\n        <tr ng-repeat=\"(name, value) in tableRows\">\n          <td><strong>{{ name }}</strong></td>\n          <td>{{ value }}</td>\n        </tr>\n      </tbody>\n    </table>\n\n  </div>\n\n  <span ng-show=\"emptyResult\">No data</span>\n</plugin>\n"
  },
  {
    "path": "src/js/core/features/line-chart/line-chart-plugin.directive.js",
    "content": "angular.module('linuxDash').directive('lineChartPlugin', [\n  '$interval', '$compile', 'server', '$window',\n  function ($interval, $compile, server, $window) {\n    return {\n      scope: {\n        heading: '@',\n        moduleName: '@',\n        refreshRate: '=',\n        maxValue: '=',\n        minValue: '=',\n        getDisplayValue: '=',\n        metrics: '=',\n        color: '@'\n      },\n      templateUrl: 'src/js/core/features/line-chart/line-chart-plugin.html',\n      link: function(scope, element) {\n\n        scope.initializing = true\n\n        // wrap the entire plugin into an initializing function\n        var start_rendering_line_chart = function () {\n\n          if (!scope.color)\n            scope.color = '0, 255, 0'\n\n          var series, w, h, canvas\n\n          angular.element($window).bind('resize', function() {\n            canvas.width = w\n            canvas.height = h\n          })\n\n          // smoothieJS - Create new chart\n          var chart = new SmoothieChart({\n            borderVisible: false,\n            sharpLines: true,\n            grid: {\n              fillStyle: '#ffffff',\n              strokeStyle: 'rgba(232,230,230,0.93)',\n              sharpLines: true,\n              millisPerLine: 3000,\n              borderVisible: false\n            },\n            labels: {\n              fontSize: 11,\n              precision: 0,\n              fillStyle: '#0f0e0e'\n            },\n            maxValue: parseInt(scope.maxValue),\n            minValue: parseInt(scope.minValue),\n            horizontalLines: [{\n              value: 5,\n              color: '#eff',\n              lineWidth: 1\n            }]\n          })\n\n          var initializeChart = function () {\n            // smoothieJS - set up canvas element for chart\n            var checkForCanvasReadyState = $interval(function () {\n              if (element.find('canvas')[0]) {\n                canvas  = element.find('canvas')[0]\n                series  = series || new TimeSeries()\n                w       = canvas.width\n                h       = canvas.height\n\n                if (chart.seriesSet.length > 0)\n                  chart.removeTimeSeries(chart.seriesSet[0].timeSeries)\n\n                chart.addTimeSeries(series, {\n                  strokeStyle: 'rgba(' + scope.color + ', 1)',\n                  fillStyle: 'rgba(' + scope.color + ', 0.2)',\n                  lineWidth: 2\n                })\n\n                chart.streamTo(canvas, 1000)\n                $interval.cancel(checkForCanvasReadyState)\n              }\n            }, 100)\n          }\n\n          scope.reInitializeChart = function () {\n            initializeChart()\n          }\n\n          if (!scope.isHidden)\n            initializeChart()\n\n          var dataCallInProgress = false\n\n          // update data on chart\n          scope.getData = function() {\n\n            if(scope.initializing)\n              scope.initializing = false\n\n            if (dataCallInProgress || !element.find('canvas')[0]) return\n\n            dataCallInProgress = true\n\n            server.get(scope.moduleName, function(serverResponseData) {\n\n              if (serverResponseData.length < 1) {\n                scope.emptyResult = true\n                return\n              }\n\n              dataCallInProgress = false\n              scope.lastGet      = new Date().getTime()\n\n              // change graph colour depending on usage\n              if (scope.maxValue / 4 * 3 < scope.getDisplayValue(serverResponseData)) {\n                chart.seriesSet[0].options.strokeStyle = 'rgba(255, 89, 0, 1)'\n                chart.seriesSet[0].options.fillStyle = 'rgba(255, 89, 0, 0.2)'\n              } else if (scope.maxValue / 3 < scope.getDisplayValue(serverResponseData)) {\n                chart.seriesSet[0].options.strokeStyle = 'rgba(255, 238, 0, 1)'\n                chart.seriesSet[0].options.fillStyle = 'rgba(255, 238, 0, 0.2)'\n              } else {\n                chart.seriesSet[0].options.strokeStyle = 'rgba(' + scope.color + ', 1)'\n                chart.seriesSet[0].options.fillStyle = 'rgba(' + scope.color + ', 0.2)'\n              }\n\n              scope.newData = scope.getDisplayValue(serverResponseData)\n\n              // update chart with this response\n              series.append(scope.lastGet, scope.newData)\n\n              // update the metrics for this chart\n              scope.metrics.forEach(function(metricObj) {\n                metricObj.data = metricObj.generate(serverResponseData)\n              })\n\n            })\n          }\n\n          // set the directive-provided interval\n          // at which to run the chart update\n          var intervalRef = $interval(scope.getData, scope.refreshRate)\n          var removeInterval = function() {\n            $interval.cancel(intervalRef)\n          }\n\n          element.on(\"$destroy\", removeInterval)\n        }\n\n        // only start rendering plugin when we know the scale of max/min for the canvas chart (smoothie)\n        var stopWatching = scope.$watch('maxValue', function (n, o) {\n          if (n) {\n            start_rendering_line_chart()\n            stopWatching()\n          }\n        })\n\n\n      }\n    }\n  }\n])\n"
  },
  {
    "path": "src/js/core/features/line-chart/line-chart-plugin.html",
    "content": "<plugin chart-plugin>\n\n  <loader ng-if=\"!maxValue || initializing\"></loader>\n\n  <canvas\n    ng-show=\"!initializing && !emptyResult\"\n    class=\"canvas\"\n    width=\"400\"\n    height=\"150\">\n  </canvas>\n\n  <table ng-show=\"!initializing && !emptyResult\" border=\"0\" class=\"metrics-table\">\n    <tbody>\n      <tr ng-repeat=\"metric in metrics\">\n        <td><strong>{{ metric.name }}</strong></td>\n        <td>{{ metric.data }}</td>\n      </tr>\n    </tbody>\n  </table>\n\n  <span ng-show=\"emptyResult\">No data</span>\n\n</plugin>\n"
  },
  {
    "path": "src/js/core/features/loader/loader.css",
    "content": ".spinner {\n  margin: 100px auto;\n  width: 50px;\n  height: 30px;\n  text-align: center;\n  font-size: 10px;\n}\n\n.spinner > div {\n  background-color: #009587;\n  height: 100%;\n  width: 6px;\n  display: inline-block;\n\n  -webkit-animation: stretchdelay 1.2s infinite ease-in-out;\n  animation: stretchdelay 1.2s infinite ease-in-out;\n}\n\n.spinner .rect2 {\n  -webkit-animation-delay: -1.1s;\n  animation-delay: -1.1s;\n}\n\n.spinner .rect3 {\n  -webkit-animation-delay: -1.0s;\n  animation-delay: -1.0s;\n}\n\n.spinner .rect4 {\n  -webkit-animation-delay: -0.9s;\n  animation-delay: -0.9s;\n}\n\n.spinner .rect5 {\n  -webkit-animation-delay: -0.8s;\n  animation-delay: -0.8s;\n}\n\n@-webkit-keyframes stretchdelay {\n  0%, 40%, 100% { -webkit-transform: scaleY(0.4) }\n  20% { -webkit-transform: scaleY(1.0) }\n}\n\n@keyframes stretchdelay {\n  0%, 40%, 100% {\n    transform: scaleY(0.4);\n    -webkit-transform: scaleY(0.4);\n  }  20% {\n    transform: scaleY(1.0);\n    -webkit-transform: scaleY(1.0);\n  }\n}\n"
  },
  {
    "path": "src/js/core/features/loader/loader.directive.js",
    "content": "angular.module('linuxDash').directive('loader', function() {\n  return {\n    scope: {\n      width: '@'\n    },\n    template: '\\\n      <div class=\"spinner\">\\\n       <div class=\"rect1\"></div>\\\n       <div class=\"rect2\"></div>\\\n       <div class=\"rect3\"></div>\\\n       <div class=\"rect4\"></div>\\\n       <div class=\"rect5\"></div>\\\n      </div>\\\n    '\n  }\n})\n"
  },
  {
    "path": "src/js/core/features/multi-line-chart/multi-line-chart-plugin.directive.js",
    "content": "angular.module('linuxDash').directive('multiLineChartPlugin', [\n  '$interval', '$compile', 'server', '$window',\n  function ($interval, $compile, server, $window) {\n    return {\n      scope: {\n        heading: '@',\n        moduleName: '@',\n        refreshRate: '=',\n        getDisplayValue: '=',\n        units: '=',\n        delay: '='\n      },\n      templateUrl: 'src/js/core/features/multi-line-chart/multi-line-chart-plugin.html',\n      link: function(scope, element) {\n\n        var w, h, canvas\n\n        angular.element($window).bind('resize', function() {\n          canvas.width = w\n          canvas.height = h\n        })\n\n        // smoothieJS - Create new chart\n        var chart = new SmoothieChart({\n          borderVisible: false,\n          sharpLines: true,\n          grid: {\n            fillStyle: '#ffffff',\n            strokeStyle: 'rgba(232,230,230,0.93)',\n            sharpLines: true,\n            borderVisible: false\n          },\n          labels: {\n            fontSize: 12,\n            precision: 0,\n            fillStyle: '#0f0e0e'\n          },\n          maxValue: 100,\n          minValue: 0,\n          horizontalLines: [{\n            value: 1,\n            color: '#ecc',\n            lineWidth: 1\n          }]\n        })\n\n        var seriesOptions = [\n          {\n            strokeStyle: 'rgba(255, 0, 0, 1)',\n            lineWidth: 2\n          },\n          {\n            strokeStyle: 'rgba(0, 255, 0, 1)',\n            lineWidth: 2\n          },\n          {\n            strokeStyle: 'rgba(0, 0, 255, 1)',\n            lineWidth: 2\n          },\n          {\n            strokeStyle: 'rgba(255, 255, 0, 1)',\n            lineWidth: 1\n          }\n        ]\n\n        // smoothieJS - set up canvas element for chart\n        scope.seriesArray  = []\n        scope.metricsArray = []\n\n        var delay = 1000\n\n        if (angular.isDefined(scope.delay))\n          delay = scope.delay\n\n        var initializeChart = function () {\n          // smoothieJS - set up canvas element for chart\n          var checkForCanvasReadyState = $interval(function () {\n            if (element.find('canvas')[0]) {\n              canvas  = element.find('canvas')[0]\n              w       = canvas.width\n              h       = canvas.height\n\n              // get the data once to set up # of lines on chart\n              server.get(scope.moduleName, function(serverResponseData) {\n\n                var numberOfLines = Object.keys(serverResponseData).length\n\n                for (var x = 0; x < numberOfLines; x++) {\n\n                  var keyForThisLine = Object.keys(serverResponseData)[x];\n\n                  scope.seriesArray[x] = new TimeSeries();\n                  chart.addTimeSeries(scope.seriesArray[x], seriesOptions[x]);\n                  scope.metricsArray[x] = {\n                    name: keyForThisLine,\n                    color: seriesOptions[x].strokeStyle,\n                  }\n                }\n\n              })\n\n              chart.streamTo(canvas, delay)\n              $interval.cancel(checkForCanvasReadyState)\n            }\n          }, 100)\n        }\n\n        scope.reInitializeChart = function () {\n          chart.seriesSet.forEach(function (ts) {\n            chart.removeTimeSeries(ts.timeSeries)\n          })\n\n          initializeChart()\n        }\n\n        if (!scope.isHidden)\n          initializeChart()\n\n        var dataCallInProgress = false\n\n        // update data on chart\n        scope.getData = function() {\n\n          if (dataCallInProgress) return\n\n          if (!scope.seriesArray.length) return\n\n          dataCallInProgress = true\n\n          server.get(scope.moduleName, function(serverResponseData) {\n\n            dataCallInProgress = false\n            scope.lastGet = new Date().getTime()\n            var keyCount = 0\n            var maxAvg = 100\n\n            // update chart with current response\n            for (var key in serverResponseData) {\n              scope.seriesArray[keyCount].append(scope.lastGet, serverResponseData[key])\n              keyCount++\n              maxAvg = Math.max(maxAvg, serverResponseData[key])\n            }\n\n            // update the metrics for this chart\n            scope.metricsArray.forEach(function(metricObj) {\n              metricObj.data = serverResponseData[metricObj.name].toString() + ' ' + scope.units\n            })\n\n            // round up the average and set the maximum scale\n            var len = parseInt(Math.log(maxAvg) / Math.log(10))\n            var div = Math.pow(10, len)\n            chart.options.maxValue = Math.ceil(maxAvg / div) * div\n\n          })\n\n        }\n\n        var refreshRate = (angular.isDefined(scope.refreshRate)) ? scope.refreshRate : 1000\n        var intervalRef = $interval(scope.getData, refreshRate)\n        var removeInterval = function() {\n          $interval.cancel(intervalRef)\n        }\n\n        element.on(\"$destroy\", removeInterval)\n      }\n    }\n}])\n"
  },
  {
    "path": "src/js/core/features/multi-line-chart/multi-line-chart-plugin.html",
    "content": "<plugin chart-plugin>\n\n  <canvas class=\"canvas\" width=\"400\" height=\"145\"></canvas>\n\n  <table class=\"metrics-table\" border=\"0\">\n    <tbody>\n      <tr ng-repeat=\"metric in metricsArray\">\n        <td>\n          <div\n            class=\"metric-square\"\n            style=\"display: inline-block; border: 1px solid {{metric.color}}; width: 8px; height: 8px; background: {{metric.color}}\">\n          </div>\n        </td>\n        <td>{{ metric.name }}</td>\n        <td>{{ metric.data }}</td>\n      </tr>\n    </tbody>\n  </table>\n\n</plugin>\n"
  },
  {
    "path": "src/js/core/features/navbar/navbar.css",
    "content": "nav-bar {\n  display: block;\n  background-color: #f6f8f8;\n  height: 40px;\n  padding-left: 10%;\n  padding-top: 3px;\n  padding-right: 5%;\n}\n\nnav-bar ul {\n  margin-left: 10px;\n  padding: 0;\n  list-style-type: none;\n  display: inline;\n}\n\nnav-bar ul li a {\n  font-size: 11px;\n  text-transform: uppercase;\n  text-decoration: none;\n  line-height: 2.3rem;\n  color: grey;\n}\n\nnav-bar ul li a:hover {\n  color: rgb(0,0,0);\n}\n\nnav-bar ul li.active {\n  border-bottom: 2px solid #009587;\n}\n\nnav-bar ul li.active a {\n  color: #009587;\n}\n\nnav-bar ul li {\n  margin-left: 20px;\n  display: inline;\n  padding-left: 5px;\n  padding-bottom: 10px;\n}\n\nnav-bar .title {\n  color: #009587;\n  font-weight: 300;\n  font-size: 20px;\n  letter-spacing: .1rem;\n  float: left;\n  padding-top: 5px;\n}\n\nnav-bar .right-content {\n  float: right;\n}\n\nnav-bar .right-content,\nnav-bar .right-content a {\n  font-size: 11px;\n  color: grey;\n  padding-top: 10px;\n}\n\nnav-bar .right-content a:hover {\n  color: #000;\n}\n"
  },
  {
    "path": "src/js/core/features/navbar/navbar.directive.js",
    "content": "angular.module('linuxDash').directive('navBar', ['$location', function($location) {\n  return {\n    template: '\\\n      \\\n      <span class=\"title\">Linux Dash</span>\\\n      \\\n      <ul> \\\n        <li ng-class=\"{active: isActive(navItem) }\" ng-repeat=\"navItem in items\"> \\\n          <a href=\"#/{{navItem}}\" ng-bind=\"getNavItemName(navItem)\"></a> \\\n        </li> \\\n      </ul> \\\n      <span class=\"right-content\">\\\n        Resources:\\\n        <a target=\"_blank\" href=\"https://github.com/afaqurk/linux-dash\">GitHub</a> | \\\n        <a target=\"_blank\" href=\"https://gitter.im/afaqurk/linux-dash\">Gitter Chat Room</a> | \\\n        <a target=\"_blank\" href=\"https://github.com/afaqurk/linux-dash/wiki\">Docs</a> \\\n      </span>\\\n    ',\n    link: function(scope) {\n      scope.items = [\n        'system-status',\n        'basic-info',\n        'network',\n        'accounts',\n        'apps'\n      ]\n\n      scope.getNavItemName = function(url) {\n        return url.replace('-', ' ')\n      }\n\n      scope.isActive = function(route) {\n        return '/' + route === $location.path()\n      }\n    }\n  }\n}])\n"
  },
  {
    "path": "src/js/core/features/plugin/plugin.css",
    "content": "\n.plugin {\n  transition: all .1s linear;\n\n  vertical-align: text-top;\n  width: 400px;\n  display: inline-block;\n  padding: 0;\n  color: black;\n  text-align: center;\n  border-radius: 2px;\n  box-shadow: 0 1px 10px rgba(0,0,0,.13),0px 5px 10px rgba(0,0,0,.16);\n  margin: 30px 15px 0 0;\n  max-height: 400px;\n  overflow: hidden;\n\n  resize: both;\n  padding-bottom: 15px;\n  background-color: rgba(0,0,0,0.015);\n}\n\n.plugin-hidden .top-bar .heading {\n  color: grey;\n  font-style: italic;\n}\n\n.plugin-enlarged {\n  width: 50%;\n}\n\n@media (max-width: 768px) {\n  .plugin {\n    max-width: 80%;\n    float: none;\n    margin: 0 auto;\n    margin-bottom: 10px;\n  }\n\n  .plugin-body {\n    max-height: 323px;\n  }\n\n  .plugin-enlarged {\n    width: 80%;\n  }\n}\n\n@media (max-width: 950px) {\n  .plugin {\n    margin: 15px 10px 0 0;\n  }\n  .plugin-enlarged {\n    width: 90%;\n  }\n}\n\n.plugin.chart-plugin {\n  padding: 0px;\n  resize: none;\n  padding-bottom: 0;\n}\n\n.plugin-body {\n  background-color: #fff;\n  height: 323px;\n  font-size: 12px;\n  padding: 10px;\n  line-height: 30px;\n  overflow: auto;\n  border-top: 1px solid #ececec;\n}\n\n.plugin.chart-plugin,\n.plugin.chart-plugin .plugin-body {\n  overflow: hidden;\n  padding: 0;\n}\n\n.plugin-body-short {\n  height: 263px;\n}\n\n.plugin last-update{\n  font-size: 11px;\n  float: left;\n}\n\n.plugin ::-webkit-scrollbar {\n  width: 8px;\n  height: 8px;\n}\n.plugin ::-webkit-scrollbar-track {\n  background: #eee;\n  border: thin solid lightgray;\n  box-shadow: 0px 0px 3px #dfdfdf inset;\n}\n.plugin ::-webkit-scrollbar-thumb {\n  background: rgba(0,0,0,0.1);\n  border: thin solid rgba(0,0,0,0.1);\n  border-radius: 0;\n}\n.plugin ::-webkit-scrollbar-thumb:hover {\n  background: rgba(0,0,0,0.2);\n}\n"
  },
  {
    "path": "src/js/core/features/plugin/plugin.directive.js",
    "content": "angular.module('linuxDash').directive('plugin', ['$rootScope', function($rootScope) {\n  return {\n    transclude: true,\n    templateUrl: 'src/js/core/features/plugin/plugin.html',\n    link: function (s, el, attr) {\n\n      if (attr.hasOwnProperty('chartPlugin'))\n        s.isChartPlugin = true\n\n      if ($rootScope.hiddenPlugins.indexOf(s.moduleName) > -1)\n        s.isHidden = true\n\n      s.toggleWidth = function () {\n        el.find('div')[0].removeAttribute('style')\n        s.enlarged = !s.enlarged\n      }\n\n      var setPluginVisibility = function (shouldShow) {\n        s.isHidden = !shouldShow\n\n        if (shouldShow) {\n          $rootScope.$emit('show-plugin', s.moduleName)\n          if (s.isChartPlugin) s.reInitializeChart()\n        } else {\n          $rootScope.$emit('hide-plugin', s.moduleName)\n        }\n      }\n\n      s.toggleVisibility = function () {\n        setPluginVisibility(s.isHidden)\n      }\n\n\n      s.$watch('emptyResult', function (n, o) {\n        if (n) {\n          setPluginVisibility(false)\n        }\n      })\n    }\n  }\n}])\n"
  },
  {
    "path": "src/js/core/features/plugin/plugin.html",
    "content": "<div\n  class=\"plugin\"\n  ng-class=\"{\n    'plugin-hidden': isHidden,\n    'plugin-enlarged': enlarged,\n    'chart-plugin': isChartPlugin,\n  }\"\n  >\n\n  <top-bar\n    heading=\"heading\"\n    last-updated=\"lastGet\"\n    info=\"info\"\n    toggle-visibility=\"toggleVisibility()\"\n    is-hidden=\"isHidden\"\n    toggle-width=\"toggleWidth()\"\n    refresh=\"getData()\"\n    is-chart=\"isChartPlugin\">\n  </top-bar>\n\n  <div\n    ng-if=\"!isHidden\"\n    class=\"plugin-body\"\n    ng-class=\"{ 'plugin-body-short no-padding chart-plugin': isChartPlugin, }\"\n    ng-transclude>\n  </div>\n\n</div>\n"
  },
  {
    "path": "src/js/core/features/progress-bar/progress-bar-plugin.directive.js",
    "content": "angular.module('linuxDash').directive('progressBarPlugin', function() {\n  return {\n    scope: {\n      width: '@',\n      moduleName: '@',\n      name: '@',\n      value: '@',\n      max: '@'\n    },\n    template: '\\\n      <div class=\"progress-bar-container\">\\\n        <div class=\"progress-bar\" style=\"width:{{width}};\">\\\n          <div style=\"width: {{ (value/max) * 100 }}%;\"></div>\\\n        </div>\\\n      </div>\\\n    '\n  }\n})\n"
  },
  {
    "path": "src/js/core/features/progress-bar/progress-bar.css",
    "content": ".progress-bar {\n  background-color: #eec;\n  border-radius: 10px;\n  padding: 0px;\n  clear: both;\n  display: inline-block;\n  overflow: hidden;\n  white-space: nowrap;\n}\n\n.progress-bar > div {\n  background-color: #1EAEDB;\n  width: 0%;\n  height: 5px;\n  border-radius: 5px;\n}\n"
  },
  {
    "path": "src/js/core/features/table-data/table-data.css",
    "content": ".table-data-plugin .filter-container {\n  padding-bottom: 0;\n  margin: 0;\n}\n\n.table-data-plugin .filter,\n.table-data-plugin .filter:focus,\n.table-data-plugin .filter:active {\n  height: 20px;\n  padding: 5px;\n  margin: 5px;\n  border: none;\n  outline-color: transparent;\n  background: transparent;\n  width: 100%;\n  margin: 0;\n  text-align: center;\n  font-size: 15px;\n}\n\n.table-data-plugin .filter:focus {\n  border-bottom: 1px solid #ff5722;\n}\n\n.table-data-plugin thead tr th a,\n.table-data-plugin thead tr th a:visited {\n  color: black;\n  text-decoration: none;\n}\n\n.table-data-plugin .column-sort-caret {\n  font-size: 10px;\n  color: #1EAEDB;\n}\n"
  },
  {
    "path": "src/js/core/features/table-data/table-data.directive.js",
    "content": "angular.module('linuxDash').directive('tableData', ['server', '$rootScope', function (server, $rootScope) {\n  return {\n    scope: {\n      heading: '@',\n      info: '@',\n      moduleName: '@',\n      width: '@',\n      height: '@'\n    },\n    templateUrl: 'src/js/core/features/table-data/table-data.html',\n    link: function(scope, element) {\n\n      scope.sortByColumn = null\n      scope.sortReverse = null\n\n      // set the column to sort by\n      scope.setSortColumn = function(column) {\n\n        // if the column is already being sorted\n        // reverse the order\n        if (column === scope.sortByColumn) {\n          scope.sortReverse = !scope.sortReverse\n        } else {\n          scope.sortByColumn = column\n        }\n\n        scope.sortTableRows()\n      }\n\n      scope.sortTableRows = function() {\n        scope.tableRows.sort(function(currentRow, nextRow) {\n\n          var sortResult = 0\n\n          if (currentRow[scope.sortByColumn] < nextRow[scope.sortByColumn]) {\n            sortResult = -1\n          } else if (currentRow[scope.sortByColumn] === nextRow[scope.sortByColumn]) {\n            sortResult = 0\n          } else {\n            sortResult = 1\n          }\n\n          if (scope.sortReverse) {\n            sortResult = -1 * sortResult\n          }\n\n          return sortResult\n        })\n      }\n\n      scope.getData = function() {\n        delete scope.tableRows\n\n        server.get(scope.moduleName, function(serverResponseData) {\n\n          if (serverResponseData.length > 0) {\n            scope.tableHeaders = Object.keys(serverResponseData[0])\n          }\n\n          scope.tableRows = serverResponseData\n\n          if (scope.sortByColumn) {\n            scope.sortTableRows()\n          }\n\n          scope.lastGet = new Date().getTime()\n\n          if (serverResponseData.length < 1) {\n            scope.emptyResult = true\n          }\n\n          if (!scope.$$phase && !$rootScope.$$phase) scope.$digest()\n        })\n      }\n\n      scope.getData()\n    }\n  }\n}])\n"
  },
  {
    "path": "src/js/core/features/table-data/table-data.html",
    "content": "<plugin\n  heading=\"{{ heading }}\"\n  last-updated=\"lastGet\"\n  on-refresh=\"getData()\"\n  info=\"{{ info }}\">\n\n  <loader ng-if=\"!tableRows\"></loader>\n\n  <div ng-show=\"tableRows\">\n\n    <table class=\"table-data-plugin\" width=\"{{ width }}\" height=\"{{ height }}\">\n      <thead>\n        <tr class=\"table-data-filter-container\" ng-show=\"tableRows.length\">\n          <th colspan=\"{{ tableHeaders.length }}\" class=\"filter-container\">\n            <input class=\"filter\" ng-model=\"keyword\" placeholder=\"Search\">\n          </th>\n        </tr>\n        <tr>\n          <th ng-repeat=\"header in tableHeaders track by $index\">\n            <a href=\"\" ng-click=\"setSortColumn(header)\">{{ header }}</a>\n            <span class=\"column-sort-caret\">\n              {{ (header === sortByColumn && !sortReverse) ? '&#9650;': ''; }}\n              {{ (header === sortByColumn && sortReverse) ? '&#9660;': ''; }}\n            </span>\n          </th>\n        </tr>\n      </thead>\n      <tbody>\n        <tr ng-repeat=\"row in tableRows | filter:keyword\">\n          <td ng-repeat=\"header in tableHeaders track by $index\">\n            {{ row[header] }}\n          </td>\n        </tr>\n      </tbody>\n    </table>\n\n  </div>\n\n  <span ng-show=\"emptyResult\">No data</span>\n</plugin>\n"
  },
  {
    "path": "src/js/core/features/top-bar/topbar.css",
    "content": ".top-bar {\n\n  height: 15px;\n  padding: 15px;\n\n  font-size: 13px;\n  text-transform: capitalize;\n  color: #009587;\n\n  background-color: #f6f8f8;\n}\n\n.top-bar .heading {\n  float: left;\n\n  cursor: grab;\n  cursor: -moz-grab;\n  cursor: -webkit-grab;\n}\n\n.ld-top-bar-btn {\n\n  float: right;\n  font-size: 17px;\n\n  color: #009587;\n  background: #fff;\n\n  border: 1px solid #eee;\n  border-radius: 50%;\n\n  width: 30px;\n  height: 30px;\n  margin-top: -5px;\n\n  -webkit-transition: all 0.5s ease;\n  -moz-transition: all 0.5s ease;\n  -ms-transition: all 0.5s ease;\n  -o-transition: all 0.5s ease;\n\n  cursor: pointer;\n  transition: all 0.5s ease;\n}\n\n.minimize-btn {\n  font-size: 19px;\n}\n\n.minimize-btn.active {\n}\n\n.ld-refresh-btn:hover {\n  background-color: #ffeb3b;\n  color: black;\n}\n\n.ld-refresh-btn:active {\n  background-color: #0f9d58;\n}\n\n.plugin-hidden .width-toggle-btn {\n  display: none;\n}\n\n.plugin-hidden .minimize-btn,\n.plugin-enlarged .width-toggle-btn {\n  background: #eee;\n  color: #000;\n}\n"
  },
  {
    "path": "src/js/core/features/top-bar/topbar.directive.js",
    "content": "angular.module('linuxDash').directive('topBar', ['$rootScope', function($rootScope) {\n  return {\n    scope: {\n      heading: '=',\n      refresh: '&',\n      lastUpdated: '=',\n      toggleVisibility: '&',\n      isHidden: '=',\n      toggleWidth: '&',\n      isChart: '=',\n      info: '=', // not being used; needs a good ui solution\n    },\n    template: '\\\n      <div class=\"top-bar\"> \\\n        <span class=\"heading\"> &#9776; {{ heading }}</span> \\\n        \\\n        <button \\\n          class=\"ld-top-bar-btn minimize-btn\" \\\n          ng-click=\"toggleVisibility()\" \\\n          ng-class=\"{ active: isHidden }\">-</button> \\\n        \\\n        \\\n        <button class=\"ld-top-bar-btn width-toggle-btn\" ng-if=\"toggleWidth && !isChart\" ng-click=\"toggleWidth()\">&harr;</button> \\\n        <button ng-if=\"!isChart && !isHidden\" class=\"ld-top-bar-btn refresh-btn\" ng-click=\"refresh()\">↺</button> \\\n      </div> \\\n    ',\n  }\n}])\n"
  },
  {
    "path": "src/js/core/rootscope-event-handlers/hide-plugin.run.js",
    "content": "angular\n  .module('linuxDash')\n  .run(['$rootScope', '$location', function ($rootScope, $location) {\n\n    var key = 'hiddenPlugins'\n\n    var getHiddenPlugins = function () {\n      var hiddenPluginsCSV = localStorage.getItem(key) || ''\n      return hiddenPluginsCSV.split(',')\n    }\n\n    var updateHiddenPlugins = function (hiddenPlugins) {\n      localStorage.setItem(key, hiddenPlugins.join(','))\n    }\n\n    $rootScope.$on('hide-plugin', function (e, m) {\n      var hiddenPlugins = getHiddenPlugins()\n\n      if(hiddenPlugins.indexOf(m) < 0)\n        hiddenPlugins.push(m)\n\n      updateHiddenPlugins(hiddenPlugins)\n    })\n\n    $rootScope.$on('show-plugin', function (e, m) {\n      var hiddenPlugins = getHiddenPlugins()\n      var indexOfPlugin = hiddenPlugins.indexOf(m)\n\n      if(indexOfPlugin > -1)\n        hiddenPlugins.splice(indexOfPlugin, 1)\n\n      updateHiddenPlugins(hiddenPlugins)\n    })\n\n    $rootScope.hiddenPlugins = getHiddenPlugins()\n\n  }])\n"
  },
  {
    "path": "src/js/core/rootscope-event-handlers/make-plugins-draggable.run.js",
    "content": "angular\n  .module('linuxDash')\n  .run(['$rootScope', '$location', function ($rootScope, $location) {\n\n    $rootScope.$on('$routeChangeSuccess', function () {\n\n      var intervalId = setInterval(function () {\n\n        var el = document.getElementById('plugins')\n\n        if (el) {\n\n          var sortable = Sortable.create(el, {\n            group: 'plugin-order-' + $location.path().replace('/', ''),\n            handle: '.heading',\n            ghostClass: 'ld-ghost',\n            chosenClass: 'ld-chosen',\n            dataIdAttr: 'sortablejs-id',\n            animation: 1050,\n            store: {\n              get: function (sortable) {\n                var order = localStorage.getItem(sortable.options.group.name);\n                return order ? order.split('|') : [];\n              },\n              set: function (sortable) {\n                var order = sortable.toArray();\n                localStorage.setItem(sortable.options.group.name, order.join('|'));\n              }\n            }\n          })\n\n          clearInterval(intervalId)\n        }\n      })\n    })\n\n  }])\n"
  },
  {
    "path": "src/js/core/routes.js",
    "content": "function appLoadController($scope, $location, $rootScope) {\n  var loadUrl = localStorage.getItem('currentTab') || 'system-status'\n  var loadLinuxDash = function () {\n    $location.path(loadUrl)\n  }\n\n  $rootScope.$on('start-linux-dash', loadLinuxDash)\n}\n\nfunction routesFn($routeProvider) {\n\n  $routeProvider\n\n    .when('/loading', {\n      template: [\n        '<div class=\"lead\" style=\"text-align: center;\">',\n          '<loader></loader>',\n          'Loading...',\n        '</div>',\n      ].join(''),\n      controller: ['$scope', '$location', '$rootScope', appLoadController],\n    })\n\n    .when('/system-status', {\n      template: [\n        '<ram-chart sortablejs-id=\"ram-chart\"></ram-chart> ',\n        '<cpu-avg-load-chart sortablejs-id=\"cpu-avg-load-chart\"></cpu-avg-load-chart> ',\n        '<cpu-utilization-chart sortablejs-id=\"cpu-util-chart\"></cpu-utilization-chart> ',\n        '<cpu-temp sortablejs-id=\"cpu-temp\"></cpu-temp> ',\n        '<ram-intensive-processes sortablejs-id=\"ram-intensive-processes\"></ram-intensive-processes> ',\n        '<cpu-intensive-processes sortablejs-id=\"cpu-intensive-processes\"></cpu-intensive-processes> ',\n        '<disk-space sortablejs-id=\"disk-space\"></disk-space> ',\n        '<swap-usage sortablejs-id=\"swap-usage\"></swap-usage> ',\n        '<docker-processes sortablejs-id=\"docker\"></docker-processes> ',\n      ].join(''),\n    })\n\n    .when('/basic-info', {\n      template: [\n        '<machine-info sortablejs-id=\"machine-info\"></machine-info>',\n        '<memory-info sortablejs-id=\"memory-info\"></memory-info>',\n        '<cpu-info sortablejs-id=\"cpu-info\"></cpu-info>',\n        '<scheduled-crons sortablejs-id=\"scheduled-crons\"></scheduled-crons>',\n        '<cron-history sortablejs-id=\"cron-history\"></cron-history>',\n        '<io-stats sortablejs-id=\"io-stats\"></io-stats>',\n      ].join(''),\n    })\n\n    .when('/network', {\n    template: [\n        '<upload-transfer-rate-chart sortablejs-id=\"upload\"></upload-transfer-rate-chart> ',\n        '<download-transfer-rate-chart sortablejs-id=\"download\"></download-transfer-rate-chart> ',\n        '<ip-addresses sortablejs-id=\"ip-addresses\"></ip-addresses> ',\n        '<network-connections sortablejs-id=\"net-cons\"></network-connections> ',\n        '<arp-cache-table sortablejs-id=\"arp\"></arp-cache-table> ',\n        '<ping-speeds sortablejs-id=\"ping\"></ping-speeds> ',\n        '<bandwidth sortablejs-id=\"bandwidth\"></bandwidth> ',\n      ].join(''),\n    })\n\n    .when('/accounts', {\n      template: [\n        '<server-accounts sortablejs-id=\"server-accounts\"></server-accounts> ',\n        '<logged-in-accounts sortablejs-id=\"logged-in\"></logged-in-accounts> ',\n        '<recent-logins sortablejs-id=\"recent\"></recent-logins> ',\n      ].join(''),\n    })\n\n    .when('/apps', {\n      template: [\n        '<common-applications sortablejs-id=\"common-applications\"></common-applications>',\n        '<memcached sortablejs-id=\"memcached\"></memcached>',\n        '<redis sortablejs-id=\"redis\"></redis>',\n        '<pm2 sortablejs-id=\"pm2\"></pm2>',\n      ].join(''),\n    })\n    .otherwise({\n      redirectTo: '/loading'\n    })\n}\n\nangular.module('linuxDash').config(['$routeProvider', routesFn])\n"
  },
  {
    "path": "src/js/core/server.service.js",
    "content": "angular\n  .module('linuxDash')\n  .service('server', [\n    '$http', '$rootScope', '$location',\n    function($http, $rootScope, $location) {\n\n      var websocket = {\n        connection: null,\n        onMessageEventHandlers: {}\n      };\n\n      /**\n       * @description:\n       *   Establish a websocket connection with server\n       *\n       * @return Null\n       */\n      var establishWebsocketConnection = function() {\n\n        var websocketUrl = (location.protocol === 'https:' ? 'wss://' : 'ws://') + window.location.hostname + ':' + window.location.port;\n\n        if (websocket.connection === null) {\n\n          websocket.connection = new WebSocket(websocketUrl);\n\n          websocket.connection.onopen = function() {\n            $rootScope.$broadcast(\"start-linux-dash\", {});\n            $rootScope.$apply();\n            console.info('Websocket connection is open');\n          };\n\n          websocket.connection.onmessage = function(event) {\n\n            var response = JSON.parse(event.data);\n            var moduleName = response.moduleName;\n            var moduleData = JSON.parse(response.output);\n\n            if (!!websocket.onMessageEventHandlers[moduleName]) {\n              websocket.onMessageEventHandlers[moduleName](moduleData);\n            } else {\n              console.info(\"Websocket could not find module\", moduleName, \"in:\", websocket.onMessageEventHandlers);\n            }\n\n          };\n\n          websocket.connection.onclose = function() {\n            websocket.connection = null;\n          }\n        }\n\n      };\n\n      /**\n       * @description:\n       *   Check if websockets are supported\n       *   If so, call establishWebsocketConnection()\n       *\n       * @return Null\n       */\n      this.checkIfWebsocketsAreSupported = function() {\n\n        var websocketSupport = {\n          browser: null,\n          server: null,\n        };\n\n        // does browser support websockets?\n        if (window.WebSocket) {\n\n          websocketSupport.browser = true;\n\n          // does backend support websockets?\n          $http.get(\"/websocket\").then(function(response) {\n\n            // if websocket_support property exists and is trurthy\n            // websocketSupport.server will equal true.\n            websocketSupport.server = !!response.data[\"websocket_support\"];\n\n          }).catch(function websocketNotSupportedByServer() {\n\n            websocketSupport.server = false;\n            $rootScope.$broadcast(\"start-linux-dash\", {});\n\n          }).then(function finalDecisionOnWebsocket() {\n\n            if (websocketSupport.browser && websocketSupport.server) {\n\n              establishWebsocketConnection();\n\n            } else {\n              $rootScope.$broadcast(\"start-linux-dash\", {});\n            }\n\n          });\n\n        }\n\n      };\n\n      /**\n       * Handles requests from modules for data from server\n       *\n       * @param  {String}   moduleName\n       * @param  {Function} callback\n       * @return {[ Null || callback(server response) ]}\n       */\n      this.get = function(moduleName, callback) {\n\n        // if we have a websocket connection\n        if (websocket.connection) {\n\n          // and the connection is ready\n          if (websocket.connection.readyState === 1) {\n\n            // set the callback as the event handler\n            // for server response.\n            //\n            // Callback instance needs to be overwritten\n            // each time for this to work. Not sure why.\n            websocket.onMessageEventHandlers[moduleName] = callback;\n\n            //\n            websocket.connection.send(moduleName);\n\n          } else {\n            console.log(\"Websocket not ready yet.\", moduleName);\n          }\n\n        }\n        // otherwise\n        else {\n\n          var moduleAddress = 'server/?module=' + moduleName;\n\n          return $http.get(moduleAddress).then(function(response) {\n            return callback(response.data);\n          });\n\n        }\n\n      };\n\n    }\n])\n"
  },
  {
    "path": "src/js/plugins/README.md",
    "content": "# Plugins\n\nThese are the individual plugins which display server data to the user in the UI.\n\nMajority of the implementations of plugins are very simple since they leverage the Linux Dash core (`src/js/core/features`)\n\n### disk-space\n\nShows the disk\n"
  },
  {
    "path": "src/js/plugins/cpu-avg-load-chart.directive.js",
    "content": "angular.module('linuxDash').directive('cpuAvgLoadChart', ['server', function(server) {\n  return {\n    restrict: 'E',\n    scope: {},\n    template: '\\\n      <multi-line-chart-plugin \\\n          heading=\"CPU Avg Load\" \\\n          module-name=\"load_avg\" \\\n          units=\"units\"> \\\n      </multi-line-chart-plugin> \\\n    ',\n    link: function(scope) {\n      scope.units = '%'\n    }\n  }\n}])\n"
  },
  {
    "path": "src/js/plugins/cpu-temp.directive.js",
    "content": "angular.module('linuxDash').directive('cpuTemp', ['server', function(server) {\n  return {\n    restrict: 'E',\n    scope: {},\n    template: ' \\\n      <line-chart-plugin \\\n\\\n        heading=\"CPU temp\" \\\n        module-name=\"cpu_temp\" \\\n        color=\"0,255,0\" \\\n\\\n        max-value=\"max\" \\\n        min-value=\"min\" \\\n        refresh-rate=\"1500\" \\\n\\\n        get-display-value=\"displayValue\" \\\n        metrics=\"utilMetrics\"> \\\n      </line-chart-plugin> \\\n    ',\n    link: function(scope) {\n      scope.min = 0\n      scope.max = 100\n\n      scope.displayValue = function (serverResponseData) {\n        return serverResponseData\n      }\n\n      scope.utilMetrics = [{\n        name: 'Temprature',\n        generate: function (serverResponseData) {\n          return serverResponseData + ' °C'\n        }\n      }]\n\n    }\n  }\n}])\n"
  },
  {
    "path": "src/js/plugins/cpu-utilization-chart.directive.js",
    "content": "angular.module('linuxDash').directive('cpuUtilizationChart', ['server', function(server) {\n  return {\n    restrict: 'E',\n    scope: {},\n    template: ' \\\n      <line-chart-plugin \\\n \\\n          heading=\"CPU Utilization\" \\\n          module-name=\"cpu_utilization\" \\\n          color=\"0,255,0\" \\\n \\\n          max-value=\"max\" \\\n          min-value=\"min\" \\\n          refresh-rate=\"1500\" \\\n \\\n          get-display-value=\"displayValue\" \\\n          metrics=\"utilMetrics\"> \\\n      </line-chart-plugin> \\\n    ',\n    link: function(scope) {\n      scope.min = 0\n      scope.max = 100\n\n      scope.displayValue = function(serverResponseData) {\n        return serverResponseData\n      }\n\n      scope.utilMetrics = [{\n        name: 'Usage',\n        generate: function(serverResponseData) {\n          return serverResponseData + ' %'\n        }\n      }]\n\n    }\n  }\n}])\n"
  },
  {
    "path": "src/js/plugins/disk-space/disk-space.directive.js",
    "content": "angular.module('linuxDash').directive('diskSpace', ['server', function(server) {\n  return {\n    restrict: 'E',\n    scope: {},\n    templateUrl: 'src/js/plugins/disk-space/disk-space.html',\n    link: function(scope) {\n\n      var getKBMultiplierFn = function (size, power) {\n        return function () {\n          return size * Math.pow(1024, power)\n        }\n      }\n\n      var kbDictionary = {\n        'M': function () { return getKBMultiplierFn(size, 1) },\n        'G': function () { return getKBMultiplierFn(size, 2) },\n        'T': function () { return getKBMultiplierFn(size, 3) },\n        'P': function () { return getKBMultiplierFn(size, 4) },\n        'E': function () { return getKBMultiplierFn(size, 5) },\n        'Z': function () { return getKBMultiplierFn(size, 6) },\n        'Y': function () { return getKBMultiplierFn(size, 7) },\n      }\n\n      scope.heading = \"Disk Partitions\"\n      scope.moduleName = 'disk_partitions'\n\n      scope.getData = function() {\n        server.get(scope.moduleName, function(serverResponseData) {\n          scope.diskSpaceData = serverResponseData\n        })\n\n        scope.lastGet = new Date().getTime()\n      }\n\n      scope.getData()\n\n      scope.getKB = function(stringSize) {\n\n        var lastChar = stringSize.slice(-1)\n        var size = parseFloat(stringSize.replace(\",\", \".\"))\n\n        try {\n          return kbDictionary[lastChar](size)\n        } catch (err) {\n          return size\n        }\n      }\n\n    }\n  }\n}])\n"
  },
  {
    "path": "src/js/plugins/disk-space/disk-space.html",
    "content": "<plugin\n  heading=\"Disk Partitions\"\n  last-updated=\"lastGet\"\n  on-refresh=\"getData()\">\n\n  <loader ng-hide=\"diskSpaceData\"></loader>\n\n  <table ng-show=\"diskSpaceData\">\n    <thead>\n      <tr>\n        <th>Name</th>\n        <th></th>\n        <th>Stats</th>\n        <th>Used</th>\n        <th>Mount</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr  ng-repeat=\"partition in diskSpaceData\">\n        <td>{{partition['file_system']}}</td>\n        <td>\n          <progress-bar-plugin\n              width=\"70px\"\n              value=\"{{ getKB(partition['used']) }}\"\n              max=\"{{ getKB(partition['size']) }}\">\n          </progress-bar-plugin>\n        </td>\n        <td>\n          {{ partition['used'] }} / {{ partition['size'] }}\n        </td>\n        <td>\n          {{ partition['used%'] }}\n        </td>\n        <td>{{ partition['mounted'] }}</td>\n      </tr>\n    </tbody>\n  </table>\n\n</plugin>\n"
  },
  {
    "path": "src/js/plugins/download-transfer-rate-chart.directive.js",
    "content": "angular.module('linuxDash').directive('downloadTransferRateChart', ['server', function(server) {\n  return {\n    restrict: 'E',\n    scope: {},\n    template: ' \\\n      <multi-line-chart-plugin \\\n        heading=\"Download Transfer Rate\" \\\n        module-name=\"download_transfer_rate\" \\\n        units=\"units\"> \\\n      </multi-line-chart-plugin> \\\n    ',\n    link: function(scope) {\n      scope.delay = 2000\n      scope.units = 'KB/s'\n    }\n  }\n}])\n"
  },
  {
    "path": "src/js/plugins/ram-chart.directive.js",
    "content": "angular.module('linuxDash').directive('ramChart', ['server', function (server) {\n  return {\n    restrict: 'E',\n    scope: {},\n    template: '\\\n      <line-chart-plugin \\\n\\\n          heading=\"RAM Usage\" \\\n          module-name=\"current_ram\" \\\n          color=\"0,255,0\" \\\n\\\n          max-value=\"maxRam\" \\\n          min-value=\"minRam\" \\\n          refresh-rate=\"1000\" \\\n\\\n          get-display-value=\"ramToDisplay\" \\\n          metrics=\"ramMetrics\"> \\\n      </line-chart-plugin> \\\n    ',\n    link: function(scope) {\n\n      // get max ram available on machine before we\n      // can start charting\n      server.get('current_ram', function(resp) {\n        scope.maxRam = resp.total\n        scope.minRam = 0\n      })\n\n      scope.ramToDisplay = function(serverResponseData) {\n        return serverResponseData.used\n      }\n\n      var humanizeRam = function (ramInMB) {\n        var ram = {\n          value: parseInt(ramInMB, 10),\n          unit: 'MB',\n        }\n\n        // if ram > 1,000 MB, use GB\n        if (ram.value > 1000) {\n          ram = {\n            value: (ramInMB/1024).toFixed(2),\n            unit: 'GB',\n          }\n        }\n\n        return ram.value + ' ' + ram.unit\n      }\n\n      scope.ramMetrics = [{\n        name: 'Used',\n        generate: function(serverResponseData) {\n          var ratio = serverResponseData.used / serverResponseData.total\n          var percentage = parseInt(ratio * 100)\n\n          var usedRam = humanizeRam(serverResponseData.used)\n          return usedRam + ' (' + percentage.toString() + '%)'\n        }\n      },\n      {\n        name: 'Available',\n        generate: function(serverResponseData) {\n\n          var availableRam = humanizeRam(serverResponseData.available)\n          var totalRam = humanizeRam(serverResponseData.total)\n          return  availableRam + ' of ' + totalRam\n        }\n      }]\n    }\n  }\n}])\n"
  },
  {
    "path": "src/js/plugins/simple-table-data-plugins.directive.js",
    "content": "var simpleTableModules = [\n  {\n    name: 'machineInfo',\n    template: '<key-value-list heading=\"General Info.\" module-name=\"general_info\" info=\"System Information\"></key-value-list>'\n  },\n  {\n    name: 'ipAddresses',\n    template: '<table-data heading=\"IP Addresses\" module-name=\"ip_addresses\" info=\"IPs assigned to this server\"></table-data>'\n  },\n  {\n    name: 'ramIntensiveProcesses',\n    template: '<table-data heading=\"RAM Processes\" module-name=\"ram_intensive_processes\" info=\"Processes which are using most RAM.\"></table-data>'\n  },\n  {\n    name: 'cpuIntensiveProcesses',\n    template: '<table-data heading=\"CPU Processes\" module-name=\"cpu_intensive_processes\" info=\"Processes which are using most CPU.\"></table-data>'\n  },\n  {\n    name: 'dockerProcesses',\n    template: '<table-data heading=\"Docker Processes\" module-name=\"docker_processes\" info=\"Processes in Docker Containers sorted by CPU.\"></table-data>'\n  },\n  {\n    name: 'networkConnections',\n    template: '<table-data heading=\"Network Connections\" module-name=\"network_connections\"></table-data>'\n  },\n  {\n    name: 'serverAccounts',\n    template: '<table-data heading=\"Accounts\" module-name=\"user_accounts\" info=\"User accounts on this server.\"></table-data>'\n  },\n  {\n    name: 'loggedInAccounts',\n    template: '<table-data heading=\"Logged In Accounts\" module-name=\"logged_in_users\" info=\"Users currently logged in.\"></table-data>'\n  },\n  {\n    name: 'recentLogins',\n    template: '<table-data heading=\"Recent Logins\" module-name=\"recent_account_logins\" info=\"Recent user sessions.\"></table-data>'\n  },\n  {\n    name: 'arpCacheTable',\n    template: '<table-data heading=\"ARP Cache Table\" module-name=\"arp_cache\"></table-data>'\n  },\n  {\n    name: 'commonApplications',\n    template: '<table-data heading=\"Common Applications\" module-name=\"common_applications\" info=\"List of commonly installed applications.\"></table-data>'\n  },\n  {\n    name: 'pingSpeeds',\n    template: '<table-data heading=\"Ping Speeds\" module-name=\"ping\" info=\"Ping speed in milliseconds.\"></table-data>'\n  },\n  {\n    name: 'bandwidth',\n    template: '<table-data heading=\"Bandwidth\" module-name=\"bandwidth\"></table-data>'\n  },\n  {\n    name: 'swapUsage',\n    template: '<table-data heading=\"Swap Usage\" module-name=\"swap\"></table-data>'\n  },\n  {\n    name: 'internetSpeed',\n    template: '<key-value-list heading=\"Internet Speed\" module-name=\"internet_speed\" info=\"Internet connection speed of server.\"></key-value-list>'\n  },\n  {\n    name: 'memcached',\n    template: '<key-value-list heading=\"Memcached\" module-name=\"memcached\"></key-value-list>'\n  },\n  {\n    name: 'redis',\n    template: '<key-value-list heading=\"Redis\" module-name=\"redis\"></key-value-list>'\n  },\n  {\n    name: 'pm2',\n    template: '<table-data heading=\"PM2\" module-name=\"pm2_stats\" info=\"Process Manager 2 (PM2) Node Module stats\"></table-data>'\n  },\n  {\n    name: 'memoryInfo',\n    template: '<key-value-list heading=\"Memory Info\" module-name=\"memory_info\" info=\"/proc/meminfo read-out.\"></key-value-list>'\n  },\n  {\n    name: 'cpuInfo',\n    template: '<key-value-list heading=\"CPU Info\" module-name=\"cpu_info\" info=\"/usr/bin/lscpu read-out.\"></key-value-list>'\n  },\n  {\n    name: 'ioStats',\n    template: '<table-data heading=\"IO Stats\" module-name=\"io_stats\" info=\"/proc/diskstats read-out.\"></table-data>'\n  },\n  {\n    name: 'scheduledCrons',\n    template: '<table-data heading=\"Scheduled Cron Jobs\" module-name=\"scheduled_crons\" info=\"Crons for all users on the server.\"></table-data>'\n  },\n  {\n    name: 'cronHistory',\n    template: '<table-data heading=\"Cron Job History\" module-name=\"cron_history\" info=\"Crons which have run recently.\"></table-data>'\n  }\n]\n\nsimpleTableModules.forEach(function(module, key) {\n\n  angular.module('linuxDash').directive(module.name, ['server', function(server) {\n\n    var moduleDirective = {\n      restrict: 'E',\n      scope: {}\n    }\n\n    moduleDirective['template'] = module.template\n\n    return moduleDirective\n  }])\n\n})\n"
  },
  {
    "path": "src/js/plugins/upload-transfer-rate-chart.directive.js",
    "content": "angular.module('linuxDash').directive('uploadTransferRateChart', ['server', function(server) {\n  return {\n    restrict: 'E',\n    scope: {},\n    template: ' \\\n      <multi-line-chart-plugin \\\n          heading=\"Upload Transfer Rate\" \\\n          module-name=\"upload_transfer_rate\" \\\n          units=\"units\"> \\\n      </multi-line-chart-plugin> \\\n    ',\n    link: function(scope) {\n      scope.delay = 2000\n      scope.units = 'KB/s'\n    }\n  }\n}])\n"
  }
]