robot 1 year ago
parent
commit
8c66c0bc58
100 changed files with 10413 additions and 2 deletions
  1. 16 0
      Guardfile
  2. 127 2
      README.md
  3. 27 0
      Rakefile
  4. 31 0
      Vagrantfile.txt
  5. 2 0
      dashboard/.gitignore
  6. 14 0
      dashboard/Dockerfile
  7. 12 0
      dashboard/Gemfile
  8. 127 0
      dashboard/Gemfile.lock
  9. BIN
      dashboard/assets/fonts/FontAwesome.otf
  10. BIN
      dashboard/assets/fonts/climacons-webfont.eot
  11. 917 0
      dashboard/assets/fonts/climacons-webfont.svg
  12. BIN
      dashboard/assets/fonts/climacons-webfont.ttf
  13. BIN
      dashboard/assets/fonts/climacons-webfont.woff
  14. BIN
      dashboard/assets/fonts/fontawesome-webfont.eot
  15. 196 0
      dashboard/assets/fonts/fontawesome-webfont.svg
  16. BIN
      dashboard/assets/fonts/fontawesome-webfont.ttf
  17. BIN
      dashboard/assets/fonts/fontawesome-webfont.woff
  18. BIN
      dashboard/assets/fonts/fontawesome-webfont.woff2
  19. BIN
      dashboard/assets/images/logo.png
  20. BIN
      dashboard/assets/images/weather_widget.png
  21. 25 0
      dashboard/assets/javascripts/application.coffee
  22. 0 0
      dashboard/assets/javascripts/d3-3.2.8.js
  23. 37 0
      dashboard/assets/javascripts/dashing.gridster.coffee
  24. 38 0
      dashboard/assets/javascripts/forecast.coffee
  25. 0 0
      dashboard/assets/javascripts/gridster/jquery.gridster.min.js
  26. 5 0
      dashboard/assets/javascripts/gridster/jquery.leanModal.min.js
  27. 646 0
      dashboard/assets/javascripts/jquery.knob.js
  28. 3054 0
      dashboard/assets/javascripts/jquery.sparkline.js
  29. 56 0
      dashboard/assets/javascripts/lodash.min.js
  30. 492 0
      dashboard/assets/javascripts/moment.min.js
  31. 0 0
      dashboard/assets/javascripts/rickshaw-1.4.3.min.js
  32. 258 0
      dashboard/assets/stylesheets/application.scss
  33. 298 0
      dashboard/assets/stylesheets/climacons-font.css
  34. 2086 0
      dashboard/assets/stylesheets/font-awesome.css
  35. 1 0
      dashboard/assets/stylesheets/jquery.gridster.min.css
  36. 28 0
      dashboard/config.ru
  37. 24 0
      dashboard/dashboards/layout.erb
  38. 61 0
      dashboard/dashboards/main.erb
  39. 68 0
      dashboard/jobs/daily_xkcd.rb
  40. 107 0
      dashboard/jobs/jira_list_current_sprint_issues.rb
  41. 27 0
      dashboard/jobs/main.rb
  42. 34 0
      dashboard/jobs/mta.rb
  43. 34 0
      dashboard/jobs/news.rb
  44. 48 0
      dashboard/jobs/slack_presence.rb
  45. 68 0
      dashboard/jobs/weather.rb
  46. 48 0
      dashboard/lib/graphite.rb
  47. 26 0
      dashboard/public/404.html
  48. BIN
      dashboard/public/favicon.ico
  49. 18 0
      dashboard/widgets/clock/clock.coffee
  50. 2 0
      dashboard/widgets/clock/clock.html
  51. 13 0
      dashboard/widgets/clock/clock.scss
  52. 24 0
      dashboard/widgets/comments/comments.coffee
  53. 7 0
      dashboard/widgets/comments/comments.html
  54. 33 0
      dashboard/widgets/comments/comments.scss
  55. 9 0
      dashboard/widgets/daily_xkcd/daily_xkcd.coffee
  56. 7 0
      dashboard/widgets/daily_xkcd/daily_xkcd.html
  57. 60 0
      dashboard/widgets/daily_xkcd/daily_xkcd.scss
  58. 17 0
      dashboard/widgets/fullscreen/fullscreen.coffee
  59. 1 0
      dashboard/widgets/fullscreen/fullscreen.html
  60. 3 0
      dashboard/widgets/fullscreen/fullscreen.scss
  61. 107 0
      dashboard/widgets/gauge/coffeelint.json
  62. 200 0
      dashboard/widgets/gauge/gauge.coffee
  63. 10 0
      dashboard/widgets/gauge/gauge.html
  64. 89 0
      dashboard/widgets/gauge/gauge.scss
  65. 37 0
      dashboard/widgets/graph/graph.coffee
  66. 5 0
      dashboard/widgets/graph/graph.html
  67. 65 0
      dashboard/widgets/graph/graph.scss
  68. 36 0
      dashboard/widgets/graphite/graphite.coffee
  69. 1 0
      dashboard/widgets/graphite/graphite.html
  70. 3 0
      dashboard/widgets/graphite/graphite.scss
  71. 9 0
      dashboard/widgets/iframe/iframe.coffee
  72. 1 0
      dashboard/widgets/iframe/iframe.html
  73. 8 0
      dashboard/widgets/iframe/iframe.scss
  74. 9 0
      dashboard/widgets/image/image.coffee
  75. 1 0
      dashboard/widgets/image/image.html
  76. 13 0
      dashboard/widgets/image/image.scss
  77. 9 0
      dashboard/widgets/jira_list_current_sprint_issues/jira_list_current_sprint_issues.coffee
  78. 17 0
      dashboard/widgets/jira_list_current_sprint_issues/jira_list_current_sprint_issues.html
  79. 71 0
      dashboard/widgets/jira_list_current_sprint_issues/jira_list_current_sprint_issues.scss
  80. 6 0
      dashboard/widgets/list/list.coffee
  81. 18 0
      dashboard/widgets/list/list.html
  82. 60 0
      dashboard/widgets/list/list.scss
  83. 14 0
      dashboard/widgets/meter/meter.coffee
  84. 7 0
      dashboard/widgets/meter/meter.html
  85. 35 0
      dashboard/widgets/meter/meter.scss
  86. 1 0
      dashboard/widgets/mta/mta.coffee
  87. 7 0
      dashboard/widgets/mta/mta.html
  88. 70 0
      dashboard/widgets/mta/mta.sass
  89. 21 0
      dashboard/widgets/news/news.coffee
  90. 12 0
      dashboard/widgets/news/news.html
  91. 24 0
      dashboard/widgets/number/number.coffee
  92. 11 0
      dashboard/widgets/number/number.html
  93. 39 0
      dashboard/widgets/number/number.scss
  94. 9 0
      dashboard/widgets/outboard/outboard.coffee
  95. 13 0
      dashboard/widgets/outboard/outboard.html
  96. 56 0
      dashboard/widgets/outboard/outboard.scss
  97. 9 0
      dashboard/widgets/slack_presence/slack_presence.coffee
  98. 12 0
      dashboard/widgets/slack_presence/slack_presence.html
  99. 65 0
      dashboard/widgets/slack_presence/slack_presence.scss
  100. 1 0
      dashboard/widgets/text/text.coffee

+ 16 - 0
Guardfile

@@ -0,0 +1,16 @@
+# A sample Guardfile
+# More info at https://github.com/guard/guard#readme
+
+## Uncomment and set this to only include directories you want to watch
+# directories %w(app lib config test spec features) \
+#  .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
+
+## Note: if you are using the `directories` clause above and you are not
+## watching the project directory ('.'), then you will want to move
+## the Guardfile to a watched dir and symlink it back, e.g.
+#
+#  $ mkdir config
+#  $ mv Guardfile config/
+#  $ ln -s config/Guardfile .
+#
+# and, you'll have to watch "config/Guardfile" instead of "Guardfile"

+ 127 - 2
README.md

@@ -1,2 +1,127 @@
-# trafficker
-Federated Authentication Network
+
+![Trafficker](webui/src/images/trafficker.jpg?raw=true "Trafficker")
+
+<hr>
+
+Trafficker is a scalable, API Gateway built using Kong, Pushpin, 
+StatsD, Graphite and Carbon to afford EclecticLabs developers the ability 
+to add highly-available micro-services quickly and easily. 
+Requests routing, authentication, rate limiting, and usage metrics can be instantly applied to any new service with its convenient admin UI.
+<hr><br>
+
+
+## Getting started
+vagrant up
+
+### Using Docker 
+
+```shell
+
+$ docker-compose up
+
+```
+
+
+### From Source 
+
+
+```shell
+
+# Initial Setup
+apt-get -y update && apt-get -y upgrade
+git clone http://github.com/eclecticLabs/Trafficker
+cd Trafficker
+
+pip install -r requirements.txt
+sudo pip install forever -g
+apt-get -y --force-yes install vim nginx python-dev \ 
+python-flup python-pip python-ldap expect git memcached \ 
+ sqlite3 libcairo2 libcairo2-dev python-cairo pkg-config nodejs npm
+
+pip install django==1.5.12 \ 
+ python-memcached==1.53 \ 
+ django-tagging==0.3.1 \ 
+ twisted==11.1.0 \ 
+ txAMQP==0.6.2
+
+# Graphite
+git clone -b 0.9.15 --depth 1 https://github.com/graphite-project/graphite-web.git /usr/local/src/graphite-web
+/usr/local/src/graphite-web
+python ./setup.py install
+cp conf/opt/graphite/conf/*.conf /opt/graphite/conf/
+cp conf/opt/graphite/webapp/graphite/local_settings.py /opt/graphite/webapp/graphite/local_settings.py
+
+# Whisper
+git clone -b 0.9.15 --depth 1 https://github.com/graphite-project/whisper.git /usr/local/src/whisper
+cd /usr/local/src/whisper
+python ./setup.py install
+
+# Carbon
+git clone -b 0.9.15 --depth 1 https://github.com/graphite-project/carbon.git /usr/local/src/carbon
+cd /usr/local/src/carbon
+python ./setup.py install
+
+# StatsD
+git clone -b v0.7.2 https://github.com/etsy/statsd.git /opt/statsd
+cp conf/opt/statsd/config.js /opt/statsd/config.js
+forever start /opt/statsd/stats.js /opt/statsd/config.js
+
+# nginx
+rm /etc/nginx/sites-enabled/default
+cp conf/etc/nginx/nginx.conf /etc/nginx/nginx.conf
+cp conf/etc/nginx/sites-enabled/graphite-statsd.conf /etc/nginx/sites-enabled/graphite-statsd.conf
+
+# Admin panel
+cp conf/usr/local/bin/django_admin_init.exp /usr/local/bin/django_admin_init.exp
+./usr/local/bin/django_admin_init.exp
+
+# Logging
+mkdir -p /var/log/carbon /var/log/graphite /var/log/nginx
+cp conf/etc/logrotate.d/graphite-statsd /etc/logrotate.d/graphite-statsd
+
+# Daemons
+cp conf/etc/service/carbon/run /etc/service/carbon/run
+cp conf/etc/service/carbon-aggregator/run /etc/service/carbon-aggregator/run
+cp conf/etc/service/graphite/run /etc/service/graphite/run
+cp conf/etc/service/statsd/run /etc/service/statsd/run
+cp conf/etc/service/nginx/run /etc/service/nginx/run
+cp conf /etc/graphite-statsd/conf
+cp conf/etc/my_init.d/01_conf_init.sh /etc/my_init.d/01_conf_init.sh
+
+# Analytics
+cd /opt/dashboard
+(sudo gem install bundler)
+bundle install
+dashing start -d
+
+# Management GUI
+cd /opt/webui
+npm install
+bin/dashboard.js build
+forever start bin/dashboard.js start [-p 8080]
+
+# Cleanup
+apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+```
+
+
+## Usage
+
+Manage your APIs using these endpoints:
+
+<ul>
+<li>Management GUI: http://localhost:8080</li>
+<li>WebSocket Proxy: http://localhost:7999</li>
+<li>API Endpoint: http://localhost:8000</li>
+<li>Admin Endpoint: http://localhost:8001</li>
+</ul>
+
+
+
+
+# Slammer #
+
+A collection of micro-services that allows developers to centralize authentication as a RESTful HTTP proxy into LDAP-compatible cloud/on-premises directory systems such as Microsoft Active Directory. It makes use of CAS for enterprise single sign-on, which is an open and well-documented authentication protocol. It also has a user sync application and a web-portal application which can heirarchy authenicated service chains, allowing for the use of multiple credential sources and types.
+
+For more information, including instructions on LDAP, Active Directory, the CAS protocal, and setting up Slammer for getting all these things to play nicely with one another for the purposes of your applications, please <a href="https://github.com/eclecticlabs/slammer/wiki">visit the wiki</a>.

+ 27 - 0
Rakefile

@@ -0,0 +1,27 @@
+require 'rake'
+require 'rspec/core/rake_task'
+
+task :spec    => 'spec:all'
+task :default => :spec
+
+namespace :spec do
+  targets = []
+  Dir.glob('./spec/*').each do |dir|
+    next unless File.directory?(dir)
+    target = File.basename(dir)
+    target = "_#{target}" if target == "default"
+    targets << target
+  end
+
+  task :all     => targets
+  task :default => :all
+
+  targets.each do |target|
+    original_target = target == "_default" ? target[1..-1] : target
+    desc "Run serverspec tests to #{original_target}"
+    RSpec::Core::RakeTask.new(target.to_sym) do |t|
+      ENV['TARGET_HOST'] = original_target
+      t.pattern = "spec/#{original_target}/*_spec.rb"
+    end
+  end
+end

+ 31 - 0
Vagrantfile.txt

@@ -0,0 +1,31 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+Vagrant.configure(2) do |config|
+
+     config.vm.provider :aws do |aws, override|
+       config.vm.box = "dummy"
+       config.vm.synced_folder ".", "/vagrant", disabled: true
+       aws.access_key_id = ENV['AWS_ACCESS_KEY_ID']
+       aws.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
+       aws.region = 'us-east-west-1'
+       aws.keypair_name = 'peter'
+       aws.region_config "us-east-west-1", :ami => "ami-36c07857"
+       aws.instance_type = 'm4.large'
+       aws.elastic_ip = '52.222.29.215'
+       aws.subnet_id = 'subnet-4d318428'
+       aws.security_groups = ['sg-df4efbba', 'sg-4549fc20', 'sg-b14efbd4', 'sg-d63782b3', 'sg-c719e0a2']
+       aws.ami = 'ami-36c07857'
+       aws.ssh_host_attribute = :public_ip_address
+       override.ssh.pty = true
+       override.ssh.username = 'ubuntu'
+       override.ssh.private_key_path = '/Users/peteralcock/.ssh/peter.pem'
+    end
+
+    config.vm.provision "ansible" do |ansible|
+       ansible.verbose = true
+       ansible.playbook = 'playbook.yml'
+    end
+
+end
+

+ 2 - 0
dashboard/.gitignore

@@ -0,0 +1,2 @@
+*DS_STORE
+history.yml

+ 14 - 0
dashboard/Dockerfile

@@ -0,0 +1,14 @@
+FROM ruby:2.2
+# Install packages for building ruby
+RUN apt-get update
+RUN apt-get install -y --force-yes build-essential wget git
+RUN apt-get install -y --force-yes zlib1g-dev libssl-dev libreadline-dev libyaml-dev libxml2-dev libxslt-dev
+RUN apt-get clean
+RUN gem update --system
+RUN gem install bundler
+COPY Gemfile Gemfile
+COPY Gemfile.lock Gemfile.lock
+RUN bundle install
+COPY . .
+EXPOSE 3030
+CMD ["dashing","start"]

+ 12 - 0
dashboard/Gemfile

@@ -0,0 +1,12 @@
+source 'https://rubygems.org'
+gem 'json'
+gem 'therubyracer', :platforms => :ruby
+gem 'nokogiri'
+gem 'httparty'
+gem 'simple-rss'
+gem 'rest-client'
+gem 'graphite'
+gem 'dashing'
+gem "sinatra-cross_origin", "~> 0.3.1"
+gem 'slack-api'
+gem 'xml-simple'

+ 127 - 0
dashboard/Gemfile.lock

@@ -0,0 +1,127 @@
+GEM
+  remote: https://rubygems.org/
+  specs:
+    backports (3.6.8)
+    coffee-script (2.2.0)
+      coffee-script-source
+      execjs
+    coffee-script-source (1.10.0)
+    daemons (1.2.4)
+    dashing (1.3.7)
+      coffee-script (~> 2.2.0)
+      execjs (~> 2.0.2)
+      rack (~> 1.5.4)
+      rufus-scheduler (~> 2.0.24)
+      sass (~> 3.2.12)
+      sinatra (~> 1.4.4)
+      sinatra-contrib (~> 1.4.2)
+      sprockets (~> 2.10.1)
+      thin (~> 1.6.1)
+      thor (> 0.18.1)
+    domain_name (0.5.20160615)
+      unf (>= 0.0.5, < 1.0.0)
+    eventmachine (1.2.0.1)
+    execjs (2.0.2)
+    faraday (0.9.2)
+      multipart-post (>= 1.2, < 3)
+    faraday_middleware (0.10.0)
+      faraday (>= 0.7.4, < 0.10)
+    faye-websocket (0.9.2)
+      eventmachine (>= 0.12.0)
+      websocket-driver (>= 0.5.1)
+    graphite (0.2.0)
+      eventmachine
+      rufus-scheduler
+    hike (1.2.3)
+    http-cookie (1.0.2)
+      domain_name (~> 0.5)
+    httparty (0.14.0)
+      multi_xml (>= 0.5.2)
+    json (2.0.2)
+    libv8 (3.16.14.15)
+    mime-types (3.1)
+      mime-types-data (~> 3.2015)
+    mime-types-data (3.2016.0521)
+    mini_portile2 (2.1.0)
+    multi_json (1.12.1)
+    multi_xml (0.5.5)
+    multipart-post (2.0.0)
+    netrc (0.11.0)
+    nokogiri (1.6.8)
+      mini_portile2 (~> 2.1.0)
+      pkg-config (~> 1.1.7)
+    pkg-config (1.1.7)
+    rack (1.5.5)
+    rack-protection (1.5.3)
+      rack
+    rack-test (0.6.3)
+      rack (>= 1.0)
+    ref (2.0.0)
+    rest-client (2.0.0)
+      http-cookie (>= 1.0.2, < 2.0)
+      mime-types (>= 1.16, < 4.0)
+      netrc (~> 0.8)
+    rufus-scheduler (2.0.24)
+      tzinfo (>= 0.3.22)
+    sass (3.2.19)
+    simple-rss (1.3.1)
+    sinatra (1.4.7)
+      rack (~> 1.5)
+      rack-protection (~> 1.4)
+      tilt (>= 1.3, < 3)
+    sinatra-contrib (1.4.7)
+      backports (>= 2.0)
+      multi_json
+      rack-protection
+      rack-test
+      sinatra (~> 1.4.0)
+      tilt (>= 1.3, < 3)
+    sinatra-cross_origin (0.3.2)
+    slack-api (1.2.4)
+      faraday (>= 0.7, < 0.10)
+      faraday_middleware (~> 0.8)
+      faye-websocket (~> 0.9.2)
+      multi_json (~> 1.0, >= 1.0.3)
+    sprockets (2.10.2)
+      hike (~> 1.2)
+      multi_json (~> 1.0)
+      rack (~> 1.0)
+      tilt (~> 1.1, != 1.3.0)
+    therubyracer (0.12.2)
+      libv8 (~> 3.16.14.0)
+      ref
+    thin (1.6.4)
+      daemons (~> 1.0, >= 1.0.9)
+      eventmachine (~> 1.0, >= 1.0.4)
+      rack (~> 1.0)
+    thor (0.19.1)
+    thread_safe (0.3.5)
+    tilt (1.4.1)
+    tzinfo (1.2.2)
+      thread_safe (~> 0.1)
+    unf (0.1.4)
+      unf_ext
+    unf_ext (0.0.7.2)
+    websocket-driver (0.6.4)
+      websocket-extensions (>= 0.1.0)
+    websocket-extensions (0.1.2)
+    xml-simple (1.1.5)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  dashing
+  graphite
+  httparty
+  json
+  nokogiri
+  rest-client
+  simple-rss
+  sinatra-cross_origin (~> 0.3.1)
+  slack-api
+  therubyracer
+  xml-simple
+
+BUNDLED WITH
+   1.13.2

BIN
dashboard/assets/fonts/FontAwesome.otf


BIN
dashboard/assets/fonts/climacons-webfont.eot


+ 917 - 0
dashboard/assets/fonts/climacons-webfont.svg

@@ -0,0 +1,917 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<svg xmlns="http://www.w3.org/2000/svg">
+<metadata>
+This is a custom SVG font generated by IcoMoon.
+<iconset grid="16"></iconset>
+</metadata>
+<defs>
+<font id="Climacons-Font" horiz-adv-x="512" >
+<font-face units-per-em="512" ascent="480" descent="-32" />
+<missing-glyph horiz-adv-x="512" />
+<glyph unicode="&#xe025;" d="M 255.87,96.00c-2.242,0.00-5.195,0.00-8.50,0.00l 28.684,35.293
+		c 25.447,8.462, 43.816,32.426, 43.816,60.711c0.00,35.348-28.65,63.992-64.00,63.992c-12.805,0.00-24.666-3.844-34.668-10.321
+		c-9.861,42.536-47.795,74.329-93.333,74.329c-53.014,0.00-95.996-42.981-95.996-96.004c0.00-43.576, 29.059-80.314, 68.837-92.043
+		l-10.79-30.23C 37.757,117.90-0.13,166.524-0.13,224.00c0.00,70.688, 57.312,128.00, 128.00,128.00c 48.037,0.00, 89.84-26.495, 111.733-65.641
+		C 244.917,287.273, 250.30,288.00, 255.87,288.00c 53.023,0.00, 96.004-42.974, 96.004-95.996S 308.894,96.00, 255.87,96.00z M 159.874,208.006l 79.995,0.00 
+		l-47.998-80.01l 56.00,0.00 l-103.999-128.00L 172.672,96.00L 119.87,96.00 L 159.874,208.006z" horiz-adv-x="352"  />
+<glyph unicode="&#xe000;" d="M 127.999,96.00C 57.311,96.00,0.00,153.312,0.00,224.00
+		C0.00,294.697, 57.311,352.00, 127.999,352.00c 48.036,0.00, 89.838-26.496, 111.731-65.633c 5.312,0.907, 10.703,1.641, 16.268,1.641
+		c 53.021,0.00, 96.004-42.989, 96.004-96.004c0.00-53.014-42.982-96.004-96.004-96.004C 233.792,96.00, 153.579,96.00, 127.999,96.00z
+		 M 255.997,128.004c 35.348,0.00, 64.006,28.66, 64.006,64.00c0.00,35.348-28.658,64.00-64.006,64.00c-12.799,0.00-24.66-3.852-34.668-10.329
+		c-9.854,42.536-47.794,74.329-93.331,74.329c-53.014,0.00-95.995-42.982-95.995-96.005c0.00-53.013, 42.981-95.996, 95.995-95.996
+		C 156.931,128.004, 231.837,128.004, 255.997,128.004z" horiz-adv-x="352"  />
+<glyph unicode="&#xe002;" d="M 334.565,247.074L 334.565,247.074c 10.945-15.604, 17.432-34.561, 17.432-55.069
+		c0.00-53.015-42.974-96.005-95.996-96.005c-22.206,0.00-102.419,0.00-128.00,0.00C 57.305,96.00,0.00,153.312,0.00,224.00
+		c0.00,70.697, 57.305,128.001, 128.001,128.001c 16.018,0.00, 31.316-3.00, 45.442-8.376l0.00,0.00c 13.08,37.458, 48.632,64.383, 90.559,64.383
+		c 6.04,0.00, 11.932-0.625, 17.658-1.696c-1.047-4.602-1.656-9.384-1.656-14.307c0.00-35.348, 28.652-64.00, 63.992-64.00
+		c 4.931,0.00, 9.705,0.609, 14.313,1.664c 1.062-5.735, 1.688-11.626, 1.688-17.666C 359.998,286.931, 350.31,264.17, 334.565,247.074z
+		 M 32.004,224.00c0.00-53.014, 42.975-95.996, 95.997-95.996c 28.933,0.00, 103.841,0.00, 128.00,0.00c 35.349,0.00, 64.00,28.66, 64.00,64.001
+		c0.00,35.348-28.651,64.00-64.00,64.00c-12.799,0.00-24.667-3.853-34.676-10.329c-9.854,42.536-47.788,74.329-93.324,74.329
+		C 74.979,320.005, 32.004,277.023, 32.004,224.00z M 188.414,336.78c 0.008,0.00, 0.016,0.00, 0.016,0.00S 188.422,336.78, 188.414,336.78z
+		 M 249.695,374.348c-23.206-5.313-41.403-23.284-47.404-46.209l0.00,0.00c 15.346-10.978, 28.191-25.222, 37.441-41.771
+		c 5.313,0.907, 10.697,1.641, 16.269,1.641c 20.948,0.00, 40.271-6.79, 56.046-18.175l0.00,0.00c 6.868,7.838, 11.885,17.347, 14.291,27.863
+		C 287.505,304.933, 256.923,335.499, 249.695,374.348z" horiz-adv-x="384"  />
+<glyph unicode="&#xe003;" d="M 287.878,101.578l0.00,35.06 
+		c 19.104,11.071, 31.996,31.691, 31.996,55.366c0.00,35.348-28.652,64.00-64.00,64.00c-12.807,0.00-24.674-3.852-34.676-10.33
+		c-9.854,42.537-47.788,74.33-93.325,74.33c-53.021,0.00-95.996-42.982-95.996-96.004c0.00-28.394, 12.40-53.834, 31.996-71.407l0.00-39.325 
+		c-38.224,22.143-64.00,63.383-64.00,110.732c0.00,70.697, 57.304,128.00, 128.00,128.00c 48.037,0.00, 89.84-26.496, 111.733-65.633
+		c 5.312,0.907, 10.689,1.641, 16.268,1.641c 53.023,0.00, 95.996-42.99, 95.996-96.004C 351.87,150.225, 325.134,114.775, 287.878,101.578z
+		 M 111.872,192.004c 8.838,0.00, 16.002-7.165, 16.002-16.002l0.00-31.997 c0.00-8.837-7.164-16.002-16.002-16.002
+		c-8.837,0.00-16.002,7.165-16.002,16.002l0.00,31.997 C 95.87,184.839, 103.035,192.004, 111.872,192.004z M 111.872,95.999
+		c 8.838,0.00, 16.002-7.157, 16.002-15.994l0.00-31.996 c0.00-8.845-7.164-16.002-16.002-16.002c-8.837,0.00-16.002,7.157-16.002,16.002l0.00,31.996 
+		C 95.87,88.842, 103.035,95.999, 111.872,95.999z M 175.872,160.008c 8.838,0.00, 16.002-7.165, 16.002-16.003l0.00-32.004 
+		c0.00-8.829-7.164-16.002-16.002-16.002c-8.837,0.00-15.994,7.173-15.994,16.002l0.00,32.004 
+		C 159.877,152.843, 167.035,160.008, 175.872,160.008z M 175.872,64.003c 8.838,0.00, 16.002-7.165, 16.002-15.994l0.00-32.004 
+		c0.00-8.837-7.164-16.002-16.002-16.002c-8.837,0.00-15.994,7.165-15.994,16.002l0.00,32.004 
+		C 159.877,56.838, 167.035,64.003, 175.872,64.003z M 239.872,192.004c 8.838,0.00, 16.002-7.165, 16.002-16.002l0.00-31.997 
+		c0.00-8.837-7.164-16.002-16.002-16.002c-8.836,0.00-16.002,7.165-16.002,16.002l0.00,31.997 C 223.87,184.839, 231.036,192.004, 239.872,192.004
+		z M 239.872,95.999c 8.838,0.00, 16.002-7.157, 16.002-15.994l0.00-31.996 c0.00-8.845-7.164-16.002-16.002-16.002
+		c-8.836,0.00-16.002,7.157-16.002,16.002l0.00,31.996 C 223.87,88.842, 231.036,95.999, 239.872,95.999z" horiz-adv-x="352"  />
+<glyph unicode="&#xe004;" d="M 415.996,272.003L 384.00,272.003 c-8.836,0.00-16.002,7.157-16.002,16.002
+		c0.00,8.829, 7.166,15.994, 16.002,15.994l 31.996,0.00 c 8.838,0.00, 16.002-7.165, 16.002-15.994C 431.998,279.16, 424.834,272.003, 415.996,272.003z
+		 M 353.137,378.516c-6.25-6.251-16.385-6.251-22.627,0.00c-6.251,6.243-6.251,16.377,0.00,22.62l 22.627,22.628
+		c 6.244,6.25, 16.377,6.25, 22.629,0.00c 6.242-6.243, 6.242-16.377,0.00-22.628L 353.137,378.516z M 329.557,253.641L 329.557,253.641
+		c 13.994-16.682, 22.439-38.16, 22.439-61.64c0.00-41.779-26.737-77.229-64.00-90.426l0.00,35.06 C 307.107,147.706, 320.00,168.326, 320.00,192.001
+		c0.00,35.348-28.66,64.00-64.00,64.00c-12.807,0.00-24.675-3.853-34.676-10.329c-9.854,42.536-47.787,74.329-93.324,74.329
+		c-53.022,0.00-96.004-42.982-96.004-96.005c0.00-28.394, 12.407-53.834, 32.004-71.406l0.00-39.325 C 25.775,135.407,0.00,176.647,0.00,223.996
+		c0.00,70.697, 57.303,128.001, 128.00,128.001c 12.58,0.00, 24.714-1.859, 36.191-5.235c 17.564,22.62, 44.943,37.239, 75.807,37.239
+		c 53.022,0.00, 95.996-42.982, 95.996-95.996C 335.994,275.878, 333.658,264.322, 329.557,253.641z M 239.998,351.997
+		c-17.705,0.00-33.723-7.188-45.311-18.799c 18.729-11.486, 34.293-27.621, 45.045-46.834c 5.312,0.907, 10.689,1.641, 16.268,1.641
+		c 17.002,0.00, 32.949-4.469, 46.803-12.22l0.00,0.00c 0.766,3.954, 1.195,8.032, 1.195,12.22C 303.998,323.345, 275.346,351.997, 239.998,351.997z
+		 M 239.998,416.005c-8.837,0.00-16.002,7.157-16.002,15.994L 223.996,463.996 c0.00,8.845, 7.165,16.002, 16.002,16.002S 256.00,472.84, 256.00,463.996L 256.00,432.00 
+		C 256.00,423.162, 248.835,416.005, 239.998,416.005z M 126.859,378.516l-22.629,22.62c-6.242,6.25-6.242,16.385,0.00,22.628
+		c 6.252,6.25, 16.378,6.25, 22.629,0.00l 22.627-22.628c 6.252-6.243, 6.252-16.377,0.00-22.62
+		C 143.236,372.265, 133.109,372.265, 126.859,378.516z M 111.998,192.001c 8.837,0.00, 16.002-7.165, 16.002-16.002l0.00-31.997 
+		c0.00-8.837-7.165-16.002-16.002-16.002c-8.838,0.00-16.002,7.165-16.002,16.002l0.00,31.997 C 95.996,184.836, 103.16,192.001, 111.998,192.001z
+		 M 111.998,95.996c 8.837,0.00, 16.002-7.157, 16.002-15.994l0.00-31.996 c0.00-8.845-7.165-16.002-16.002-16.002
+		c-8.838,0.00-16.002,7.157-16.002,16.002l0.00,31.996 C 95.996,88.839, 103.16,95.996, 111.998,95.996z M 175.998,160.005
+		c 8.837,0.00, 16.002-7.165, 16.002-16.003l0.00-32.004 c0.00-8.829-7.165-16.002-16.002-16.002c-8.838,0.00-16.002,7.173-16.002,16.002l0.00,32.004 
+		C 159.996,152.84, 167.16,160.005, 175.998,160.005z M 175.998,64.00c 8.837,0.00, 16.002-7.165, 16.002-15.994l0.00-32.004 
+		c0.00-8.837-7.165-16.002-16.002-16.002c-8.838,0.00-16.002,7.165-16.002,16.002l0.00,32.004 C 159.996,56.835, 167.16,64.00, 175.998,64.00z
+		 M 239.998,192.001c 8.837,0.00, 16.002-7.165, 16.002-16.002l0.00-31.997 c0.00-8.837-7.165-16.002-16.002-16.002s-16.002,7.165-16.002,16.002
+		l0.00,31.997 C 223.996,184.836, 231.161,192.001, 239.998,192.001z M 239.998,95.996c 8.837,0.00, 16.002-7.157, 16.002-15.994l0.00-31.996 
+		c0.00-8.845-7.165-16.002-16.002-16.002s-16.002,7.157-16.002,16.002l0.00,31.996 C 223.996,88.839, 231.161,95.996, 239.998,95.996z" horiz-adv-x="448"  />
+<glyph unicode="&#xe005;" d="M 334.558,247.427L 334.558,247.427
+		c 10.955-15.604, 17.439-34.559, 17.439-55.069c0.00-41.779-26.738-77.229-64.00-90.426l0.00,35.06 c 19.111,11.071, 31.996,31.691, 31.996,55.366
+		c0.00,35.348-28.652,64.00-63.992,64.00c-12.807,0.00-24.676-3.852-34.676-10.33c-9.854,42.537-47.796,74.33-93.332,74.33
+		c-53.016,0.00-95.997-42.982-95.997-96.004c0.00-28.394, 12.399-53.834, 31.996-71.407l0.00-39.325 C 25.776,135.765,0.00,177.005,0.00,224.354
+		c0.00,70.697, 57.304,128.00, 127.993,128.00c 16.025,0.00, 31.316-3.00, 45.442-8.376l0.00,0.00c 13.088,37.458, 48.64,64.383, 90.559,64.383
+		c 6.048,0.00, 11.931-0.625, 17.666-1.696c-1.055-4.602-1.664-9.384-1.664-14.307c0.00-35.348, 28.651-64.00, 64.00-64.00
+		c 4.923,0.00, 9.704,0.609, 14.307,1.664c 1.07-5.735, 1.695-11.626, 1.695-17.666C 359.998,287.284, 350.302,264.523, 334.558,247.427z
+		 M 249.688,374.701c-23.206-5.313-41.396-23.284-47.396-46.209l0.00,0.00c 15.338-10.978, 28.183-25.222, 37.442-41.771
+		c 5.305,0.907, 10.688,1.641, 16.268,1.641c 20.939,0.00, 40.263-6.79, 56.046-18.174l0.00,0.00c 6.868,7.837, 11.884,17.346, 14.291,27.863
+		C 287.497,305.286, 256.923,335.853, 249.688,374.701z M 111.998,192.358c 8.837,0.00, 15.995-7.165, 15.995-16.002l0.00-31.997 
+		c0.00-8.837-7.158-16.002-15.995-16.002s-16.002,7.165-16.002,16.002l0.00,31.997 C 95.996,185.193, 103.161,192.358, 111.998,192.358z
+		 M 111.998,96.354c 8.837,0.00, 15.995-7.157, 15.995-15.994l0.00-31.996 c0.00-8.845-7.158-16.002-15.995-16.002s-16.002,7.157-16.002,16.002
+		l0.00,31.996 C 95.996,89.196, 103.161,96.354, 111.998,96.354z M 175.999,160.362c 8.836,0.00, 15.994-7.165, 15.994-16.003l0.00-32.004 
+		c0.00-8.829-7.158-16.002-15.994-16.002c-8.838,0.00-16.002,7.173-16.002,16.002l0.00,32.004 
+		C 159.997,153.197, 167.161,160.362, 175.999,160.362z M 175.999,64.357c 8.836,0.00, 15.994-7.165, 15.994-15.994l0.00-32.004 
+		c0.00-8.837-7.158-16.002-15.994-16.002c-8.838,0.00-16.002,7.165-16.002,16.002l0.00,32.004 
+		C 159.997,57.192, 167.161,64.357, 175.999,64.357z M 239.999,192.358c 8.837,0.00, 16.002-7.165, 16.002-16.002l0.00-31.997 
+		c0.00-8.837-7.165-16.002-16.002-16.002c-8.838,0.00-16.002,7.165-16.002,16.002l0.00,31.997 
+		C 223.997,185.193, 231.161,192.358, 239.999,192.358z M 239.999,96.354c 8.837,0.00, 16.002-7.157, 16.002-15.994l0.00-31.996 
+		c0.00-8.845-7.165-16.002-16.002-16.002c-8.838,0.00-16.002,7.157-16.002,16.002l0.00,31.996 
+		C 223.997,89.196, 231.161,96.354, 239.999,96.354z" horiz-adv-x="384"  />
+<glyph unicode="&#xe006;" d="M 255.871,288.008c-5.572,0.00-10.955-0.734-16.268-1.641
+				C 217.709,325.504, 175.907,352.00, 127.87,352.00C 57.182,352.00-0.131,294.696-0.131,224.00c0.00-47.943, 26.402-89.667, 65.423-111.592
+				c 2.212,10.096, 7.134,19.722, 14.986,27.566l 0.453,0.453C 51.579,156.913, 31.873,188.136, 31.873,224.00
+				c0.00,53.022, 42.982,96.004, 95.997,96.004c 45.537,0.00, 83.472-31.793, 93.332-74.33c 10.001,6.478, 21.87,10.33, 34.669,10.33
+				c 35.348,0.00, 64.00-28.652, 64.00-64.00c0.00-27.559-17.455-50.983-41.889-60.00c 6.306-9.173, 9.557-19.769, 9.775-30.457
+				c 37.324,13.158, 64.117,48.631, 64.117,90.457C 351.875,245.018, 308.893,288.008, 255.871,288.008z M 119.869,134.316l-16.971-16.971
+				c-9.369-9.368-9.369-24.565,0.00-33.941c 9.376-9.368, 24.573-9.368, 33.941,0.00c 9.377,9.376, 9.377,24.573,0.00,33.941L 119.869,134.316z
+				 M 175.876,70.324l-16.971-16.971c-9.376-9.376-9.376-24.573,0.00-33.941c 9.368-9.377, 24.565-9.377, 33.935,0.00
+				c 9.375,9.368, 9.375,24.565,0.00,33.941L 175.876,70.324z M 231.875,134.316l-16.971-16.971c-9.376-9.368-9.376-24.565,0.00-33.941
+				c 9.369-9.368, 24.566-9.368, 33.941,0.00c 9.369,9.376, 9.369,24.573,0.00,33.941L 231.875,134.316z" horiz-adv-x="384"  />
+<glyph unicode="&#xe007;" d="M 416.006,271.86l-32.004,0.00 c-8.838,0.00-16.002,7.157-16.002,16.002
+		c0.00,8.829, 7.164,15.994, 16.002,15.994l 32.004,0.00 c 8.836,0.00, 15.994-7.165, 15.994-15.994C 432.00,279.018, 424.842,271.86, 416.006,271.86z
+		 M 353.139,378.374c-6.252-6.251-16.377-6.251-22.629,0.00c-6.25,6.243-6.25,16.377,0.00,22.62l 22.629,22.628
+		c 6.25,6.25, 16.377,6.25, 22.627,0.00c 6.251-6.243, 6.251-16.377,0.00-22.628L 353.139,378.374z M 329.557,253.498L 329.557,253.498
+		c 13.994-16.682, 22.448-38.16, 22.448-61.64l0.00,0.00c0.00-41.826-26.792-77.299-64.117-90.457c-0.219,10.688-3.47,21.284-9.774,30.457
+		c 24.433,9.017, 41.888,32.441, 41.888,60.00c0.00,35.348-28.651,64.00-64.00,64.00c-12.806,0.00-24.667-3.853-34.669-10.329
+		c-9.86,42.536-47.795,74.329-93.332,74.329c-53.014,0.00-95.996-42.982-95.996-96.005c0.00-11.235, 2.031-21.971, 5.579-31.995L 37.70,191.858 
+		c 7.798-21.933, 23.159-40.264, 43.161-51.577l-0.461-0.453c-7.844-7.845-12.767-17.471-14.979-27.566
+		c-30.012,16.862-52.412,45.49-61.227,79.597l0.00,0.00C 1.555,202.102,0.00,212.79,0.00,223.854c0.00,70.697, 57.312,128.001, 128.00,128.001
+		c 12.58,0.00, 24.715-1.859, 36.192-5.235c 17.565,22.62, 44.951,37.239, 75.807,37.239c 53.022,0.00, 96.004-42.982, 96.004-95.996
+		C 336.003,275.736, 333.659,264.18, 329.557,253.498z M 239.999,351.854c-17.698,0.00-33.716-7.188-45.303-18.799
+		c 18.722-11.486, 34.294-27.621, 45.037-46.834c 5.313,0.907, 10.696,1.641, 16.268,1.641c 17.01,0.00, 32.949-4.469, 46.803-12.22l0.00,0.00
+		c 0.766,3.954, 1.195,8.032, 1.195,12.22C 303.999,323.203, 275.348,351.854, 239.999,351.854z M 239.999,415.863
+		c-8.837,0.00-15.994,7.157-15.994,15.994L 224.005,463.853 c0.00,8.845, 7.157,16.002, 15.994,16.002s 16.002-7.157, 16.002-16.002l0.00-31.996 
+		C 256.001,423.02, 248.836,415.863, 239.999,415.863z M 126.867,378.374l-22.627,22.62c-6.252,6.25-6.252,16.385,0.00,22.628
+		c 6.242,6.25, 16.377,6.25, 22.627,0.00l 22.629-22.628c 6.242-6.243, 6.242-16.377,0.00-22.62C 143.244,372.123, 133.11,372.123, 126.867,378.374
+		z M 120.00,134.171l 16.971-16.971c 9.376-9.368, 9.376-24.565,0.00-33.941c-9.369-9.368-24.566-9.368-33.941,0.00
+		c-9.369,9.376-9.369,24.573,0.00,33.941L 120.00,134.171z M 175.998,70.179l 16.972-16.971c 9.376-9.376, 9.376-24.573,0.00-33.941
+		c-9.368-9.377-24.565-9.377-33.942,0.00c-9.367,9.368-9.367,24.565,0.00,33.941L 175.998,70.179z M 231.998,134.171l 16.971-16.971
+		c 9.376-9.368, 9.376-24.565,0.00-33.941c-9.368-9.368-24.565-9.368-33.934,0.00c-9.377,9.376-9.377,24.573,0.00,33.941L 231.998,134.171z" horiz-adv-x="448"  />
+<glyph unicode="&#xe008;" d="M 334.564,246.927c 10.955-15.604, 17.433-34.559, 17.433-55.069l0.00,0.00
+		c0.00-41.826-26.792-77.299-64.11-90.457c-0.219,10.688-3.469,21.284-9.781,30.457c 24.439,9.017, 41.895,32.441, 41.895,60.00l0.00,0.00l0.00,0.00
+		c0.00,35.348-28.651,64.00-64.00,64.00c-12.806,0.00-24.666-3.852-34.676-10.33c-9.853,42.537-47.787,74.33-93.324,74.33
+		c-53.021,0.00-96.004-42.982-96.004-96.005c0.00-11.235, 2.031-21.971, 5.586-31.995l 0.117,0.00 c 7.799-21.933, 23.16-40.264, 43.154-51.577
+		l-0.453-0.453c-7.853-7.845-12.768-17.471-14.979-27.566c-30.012,16.862-52.412,45.49-61.227,79.597l0.00,0.00
+		C 1.555,202.102,0.00,212.79,0.00,223.854c0.00,70.697, 57.304,128.001, 128.00,128.001c 16.018,0.00, 31.316-3.00, 45.436-8.376
+		c 13.088,37.458, 48.639,64.383, 90.566,64.383c 6.039,0.00, 11.931-0.625, 17.658-1.696c-1.055-4.602-1.664-9.384-1.664-14.307
+		c0.00-35.348, 28.659-64.00, 64.008-64.00c 4.922,0.00, 9.696,0.609, 14.299,1.664c 1.07-5.735, 1.695-11.626, 1.695-17.666
+		C 359.998,286.784, 350.309,264.023, 334.564,246.927z M 249.695,374.201c-23.207-5.313-41.404-23.284-47.396-46.209l0.00,0.00
+		c 15.338-10.978, 28.175-25.222, 37.434-41.771c 5.313,0.907, 10.697,1.641, 16.268,1.641c 20.948,0.00, 40.264-6.79, 56.047-18.174
+		c 6.867,7.837, 11.884,17.346, 14.291,27.863C 287.496,304.786, 256.922,335.353, 249.695,374.201z M 119.999,134.171l 16.972-16.971
+		c 9.375-9.368, 9.375-24.565,0.00-33.941c-9.369-9.368-24.566-9.368-33.942,0.00c-9.368,9.376-9.368,24.573,0.00,33.941L 119.999,134.171z
+		 M 175.998,70.179l 16.971-16.971c 9.377-9.376, 9.377-24.573,0.00-33.941c-9.368-9.377-24.564-9.377-33.941,0.00
+		c-9.368,9.368-9.368,24.565,0.00,33.941L 175.998,70.179z M 231.998,134.171l 16.971-16.971c 9.376-9.368, 9.376-24.565,0.00-33.941
+		c-9.369-9.368-24.566-9.368-33.942,0.00c-9.368,9.376-9.368,24.573,0.00,33.941L 231.998,134.171z" horiz-adv-x="384"  />
+<glyph unicode="&#xe009;" d="M 287.875,101.586l0.00,35.051 
+		c 19.111,11.071, 32.003,31.691, 32.003,55.366c0.00,35.349-28.659,64.00-64.008,64.00c-12.798,0.00-24.659-3.844-34.668-10.329
+		c-9.853,42.544-47.795,74.33-93.332,74.33c-53.015,0.00-95.997-42.982-95.997-95.997c0.00-28.401, 12.408-53.835, 32.004-71.407l0.00-39.333 
+		C 25.654,135.41-0.13,176.649-0.13,224.007c0.00,70.688, 57.312,127.993, 128.00,127.993c 48.037,0.00, 89.839-26.488, 111.732-65.634
+		c 5.313,0.906, 10.704,1.641, 16.268,1.641c 53.022,0.00, 96.005-42.981, 96.005-96.004C 351.875,150.225, 325.137,114.782, 287.875,101.586z
+		 M 111.876,192.003c 8.837,0.00, 15.994-7.165, 15.994-15.994l0.00-128.00 c0.00-8.845-7.157-16.003-15.994-16.003s-16.002,7.158-16.002,16.003l0.00,128.00 
+		C 95.874,184.838, 103.039,192.003, 111.876,192.003z M 175.876,160.007c 8.837,0.00, 16.002-7.165, 16.002-16.002l0.00-128.001 
+		c0.00-8.837-7.165-16.002-16.002-16.002s-16.002,7.165-16.002,16.002L 159.874,144.005 C 159.874,152.842, 167.039,160.007, 175.876,160.007z
+		 M 239.876,192.003c 8.837,0.00, 15.994-7.165, 15.994-15.994l0.00-128.00 c0.00-8.845-7.157-16.003-15.994-16.003s-16.002,7.158-16.002,16.003l0.00,128.00 
+		C 223.874,184.838, 231.039,192.003, 239.876,192.003z" horiz-adv-x="352"  />
+<glyph unicode="&#xe00a;" d="M 416.005,272.003l-32.004,0.00 c-8.829,0.00-16.002,7.157-16.002,16.002
+		c0.00,8.829, 7.173,16.002, 16.002,16.002l 32.004,0.00 c 8.838,0.00, 16.002-7.173, 16.002-16.002C 432.007,279.16, 424.843,272.003, 416.005,272.003z
+		 M 353.138,378.517c-6.25-6.251-16.377-6.251-22.628,0.00c-6.25,6.243-6.25,16.377,0.00,22.628l 22.628,22.628
+		c 6.251,6.243, 16.377,6.243, 22.628,0.00c 6.251-6.251, 6.251-16.385,0.00-22.628L 353.138,378.517z M 329.557,253.642L 329.557,253.642
+		c 14.002-16.674, 22.448-38.161, 22.448-61.641c0.00-41.778-26.745-77.221-64.00-90.417l0.00,35.051 c 19.111,11.071, 32.004,31.691, 32.004,55.366
+		c0.00,35.349-28.66,64.00-64.008,64.00c-12.799,0.00-24.667-3.844-34.669-10.329c-9.853,42.544-47.795,74.33-93.331,74.33
+		c-53.015,0.00-95.997-42.982-95.997-95.997c0.00-28.401, 12.408-53.835, 32.004-71.407l0.00-39.333 C 25.784,135.408,0.00,176.647,0.00,224.005
+		c0.00,70.688, 57.312,127.993, 128.001,127.993c 12.579,0.00, 24.722-1.853, 36.199-5.235c 17.557,22.62, 44.943,37.239, 75.799,37.239
+		c 53.022,0.00, 96.004-42.975, 96.004-95.997C 336.003,275.879, 333.667,264.322, 329.557,253.642z M 239.999,351.998
+		c-17.689,0.00-33.715-7.188-45.303-18.80c 18.729-11.478, 34.293-27.62, 45.037-46.834c 5.312,0.906, 10.704,1.641, 16.268,1.641
+		c 17.01,0.00, 32.957-4.469, 46.803-12.22l0.00,0.00c 0.766,3.953, 1.203,8.032, 1.203,12.22C 304.007,323.354, 275.347,351.998, 239.999,351.998z
+		 M 239.999,416.006c-8.829,0.00-15.994,7.157-15.994,16.002L 224.005,464.004 c0.00,8.837, 7.165,15.994, 15.994,15.994
+		c 8.845,0.00, 16.002-7.157, 16.002-15.994l0.00-31.996 C 256.001,423.163, 248.844,416.006, 239.999,416.006z M 126.867,378.517l-22.628,22.628
+		c-6.242,6.243-6.242,16.377,0.00,22.628c 6.243,6.243, 16.385,6.243, 22.628,0.00l 22.628-22.628c 6.243-6.251, 6.243-16.385,0.00-22.628
+		C 143.244,372.266, 133.11,372.266, 126.867,378.517z M 111.998,192.001c 8.846,0.00, 16.003-7.165, 16.003-15.994l0.00-128.00 
+		c0.00-8.845-7.157-16.003-16.003-16.003c-8.829,0.00-15.994,7.158-15.994,16.003l0.00,128.00 C 96.004,184.836, 103.169,192.001, 111.998,192.001z
+		 M 176.007,160.005c 8.829,0.00, 16.002-7.165, 16.002-16.002l0.00-128.001 c0.00-8.837-7.173-16.002-16.002-16.002
+		c-8.838,0.00-16.002,7.165-16.002,16.002L 160.005,144.003 C 160.005,152.84, 167.169,160.005, 176.007,160.005z M 239.999,192.001
+		c 8.845,0.00, 16.002-7.165, 16.002-15.994l0.00-128.00 c0.00-8.845-7.157-16.003-16.002-16.003c-8.829,0.00-15.994,7.158-15.994,16.003l0.00,128.00 
+		C 224.005,184.836, 231.17,192.001, 239.999,192.001z" horiz-adv-x="448"  />
+<glyph unicode="&#xe00b;" d="M 111.998,192.29c-8.829,0.00-15.994-7.165-15.994-15.994l0.00-128.00 
+				c0.00-8.845, 7.165-16.003, 15.994-16.003c 8.845,0.00, 16.002,7.158, 16.002,16.003l0.00,128.00 C 128.00,185.125, 120.843,192.29, 111.998,192.29z
+				 M 176.006,160.294c-8.845,0.00-16.002-7.165-16.002-16.002l0.00-128.001 c0.00-8.837, 7.157-16.002, 16.002-16.002
+				c 8.829,0.00, 16.002,7.165, 16.002,16.002L 192.008,144.292 C 192.008,153.129, 184.835,160.294, 176.006,160.294z M 239.998,192.29
+				c-8.829,0.00-15.994-7.165-15.994-15.994l0.00-128.00 c0.00-8.845, 7.165-16.003, 15.994-16.003c 8.846,0.00, 16.002,7.158, 16.002,16.003l0.00,128.00 
+				C 256.00,185.125, 248.844,192.29, 239.998,192.29z M 359.998,312.297c0.00,6.032-0.625,11.924-1.688,17.659
+				c-4.61-1.056-9.385-1.665-14.307-1.665c-35.349,0.00-64.00,28.652-64.00,64.001c0.00,4.922, 0.609,9.704, 1.656,14.306
+				c-5.728,1.071-11.611,1.696-17.659,1.696c-41.927,0.00-77.478-26.926-90.558-64.384c-14.127,5.376-29.426,8.377-45.443,8.377
+				c-70.688,0.00-128.00-57.305-128.00-127.993c0.00-47.357, 25.784-88.597, 64.008-110.74l0.00,39.333 c-19.604,17.572-32.004,43.006-32.004,71.407
+				c0.00,53.015, 42.974,95.997, 95.996,95.997c 45.537,0.00, 83.479-31.786, 93.332-74.33c 10.001,6.485, 21.87,10.329, 34.668,10.329
+				c 35.349,0.00, 64.001-28.651, 64.001-64.00c0.00-23.675-12.885-44.295-31.996-55.366l0.00-35.051 c 37.254,13.196, 64.00,48.639, 64.00,90.417
+				c0.00,20.511-6.485,39.474-17.44,55.069C 350.31,264.463, 359.998,287.216, 359.998,312.297z M 312.055,270.12
+				c-15.783,11.392-35.106,18.174-56.055,18.174c-5.57,0.00-10.954-0.734-16.268-1.641c-9.251,16.549-22.096,30.793-37.434,41.771
+				c 6.00,22.925, 24.198,40.896, 47.396,46.209c 7.235-38.849, 37.809-69.414, 76.642-76.65C 323.931,287.466, 318.914,277.965, 312.055,270.12
+				z" horiz-adv-x="384"  />
+<glyph unicode="&#xe00c;" d="M 256.003,96.003c-2.125,0.00-4.916,0.00-8.002,0.00
+		c0.00,10.978-2.555,21.94-7.547,32.00c 6.219,0.00, 11.609,0.00, 15.549,0.00c 35.348,0.00, 64.00,28.651, 64.00,63.996c0.00,35.349-28.652,64.00-64.00,64.00
+		c-12.799,0.00-24.668-3.848-34.676-10.325C 211.474,288.214, 173.539,320.00, 128.002,320.00c-53.022,0.00-95.997-42.979-95.997-95.997
+		c0.00-47.627, 34.716-87.046, 80.198-94.594c-5.095-9.731-7.751-20.386-8.095-31.098C 44.851,109.512, 0.001,161.483, 0.001,224.003
+		C 0.001,294.691, 57.306,352.00, 128.002,352.00c 48.037,0.00, 89.838-26.496, 111.733-65.638c 5.312,0.91, 10.695,1.637, 16.268,1.637
+		c 53.021,0.00, 95.996-42.978, 95.996-96.00C 351.999,138.98, 309.024,96.003, 256.003,96.003z M 176.008,152.568l 28.284-28.285
+		c 15.611-15.619, 15.611-40.946,0.00-56.565c-15.627-15.623-40.958-15.623-56.569,0.00c-15.627,15.619-15.627,40.946,0.00,56.565
+		L 176.008,152.568z" horiz-adv-x="352"  />
+<glyph unicode="&#xe00d;" d="M 415.997,271.792l-31.996,0.00 c-8.837,0.00-16.002,7.157-16.002,16.002
+		c0.00,8.829, 7.165,16.002, 16.002,16.002l 31.996,0.00 c 8.837,0.00, 16.002-7.173, 16.002-16.002C 431.999,278.949, 424.834,271.792, 415.997,271.792z
+		 M 353.138,378.306c-6.251-6.251-16.377-6.251-22.628,0.00c-6.25,6.243-6.25,16.377,0.00,22.628l 22.628,22.628
+		c 6.251,6.243, 16.377,6.243, 22.628,0.00c 6.25-6.251, 6.25-16.385,0.00-22.628L 353.138,378.306z M 329.557,253.431L 329.557,253.431
+		c 13.986-16.674, 22.44-38.161, 22.44-61.641l0.00,0.00c0.00-53.015-42.974-95.996-95.997-95.996c-2.125,0.00-4.914,0.00-8.00,0.00
+		c0.00,10.978-2.556,21.94-7.549,31.996c 6.221,0.00, 11.611,0.00, 15.549,0.00c 35.349,0.00, 64.001,28.659, 64.001,64.00c0.00,35.349-28.652,64.00-64.001,64.00
+		c-12.806,0.00-24.674-3.844-34.676-10.329c-9.853,42.544-47.787,74.33-93.324,74.33c-53.021,0.00-95.996-42.982-95.996-95.997
+		c0.00-11.243, 2.031-21.979, 5.578-32.004l-0.039,0.00 c 11.494-32.418, 39.904-56.827, 74.658-62.594c-5.094-9.728-7.751-20.378-8.095-31.098
+		c-48.771,9.22-87.722,46.084-99.997,93.691l 0.086,0.00 C 1.555,202.033,0.00,212.723,0.00,223.794c0.00,70.688, 57.304,127.993, 128.00,127.993
+		c 12.58,0.00, 24.715-1.853, 36.192-5.235c 17.564,22.62, 44.942,37.239, 75.806,37.239c 53.023,0.00, 95.997-42.975, 95.997-95.997
+		C 335.995,275.668, 333.658,264.111, 329.557,253.431z M 239.998,351.787c-17.705,0.00-33.723-7.188-45.303-18.80
+		c 18.722-11.478, 34.286-27.62, 45.037-46.834c 5.313,0.906, 10.689,1.641, 16.268,1.641c 17.002,0.00, 32.95-4.469, 46.803-12.22l0.00,0.00
+		c 0.766,3.953, 1.195,8.032, 1.195,12.22C 303.998,323.143, 275.347,351.787, 239.998,351.787z M 239.998,415.795
+		c-8.836,0.00-16.002,7.157-16.002,16.002L 223.996,463.793 c0.00,8.837, 7.166,15.994, 16.002,15.994C 248.836,479.787, 256.00,472.63, 256.00,463.793l0.00-31.996 
+		C 256.00,422.952, 248.836,415.795, 239.998,415.795z M 126.859,378.306l-22.628,22.628c-6.251,6.243-6.251,16.377,0.00,22.628
+		c 6.251,6.243, 16.377,6.243, 22.628,0.00l 22.628-22.628c 6.251-6.251, 6.251-16.385,0.00-22.628
+		C 143.236,372.055, 133.11,372.055, 126.859,378.306z M 175.998,152.355l 28.285-28.285c 15.619-15.611, 15.619-40.942,0.00-56.562
+		s-40.951-15.619-56.57,0.00s-15.619,40.95,0.00,56.562L 175.998,152.355z" horiz-adv-x="448"  />
+<glyph unicode="&#xe00e;" d="M 431.998,288.294L 431.998,288.294L 431.998,288.294
+		L 431.998,288.294z M 351.996,192.29c0.00-53.015-42.981-95.996-95.996-95.996c-2.125,0.00-4.914,0.00-8.001,0.00
+		c0.00,10.978-2.555,21.94-7.548,31.996c 6.22,0.00, 11.604,0.00, 15.549,0.00c 35.34,0.00, 64.00,28.659, 64.00,64.00c0.00,35.349-28.66,64.00-64.00,64.00
+		c-12.807,0.00-24.675-3.844-34.676-10.329c-9.854,42.544-47.787,74.33-93.324,74.33c-53.022,0.00-96.004-42.982-96.004-95.997
+		c0.00-11.243, 2.031-21.979, 5.578-32.004l-0.031,0.00 c 11.494-32.418, 39.904-56.827, 74.658-62.594c-5.103-9.728-7.752-20.378-8.096-31.098
+		c-48.779,9.22-87.721,46.084-99.996,93.691l 0.086,0.00 C 1.555,202.533,0.00,213.223,0.00,224.294c0.00,70.688, 57.303,127.993, 128.00,127.993
+		c 16.018,0.00, 31.316-3.001, 45.436-8.377c 13.087,37.458, 48.639,64.384, 90.565,64.384c 6.04,0.00, 11.923-0.625, 17.659-1.696
+		c-1.055-4.602-1.665-9.384-1.665-14.306c0.00-35.349, 28.66-64.001, 64.001-64.001c 4.93,0.00, 9.703,0.609, 14.306,1.665
+		c 1.071-5.735, 1.696-11.627, 1.696-17.659c0.00-25.081-9.697-47.834-25.441-64.938C 345.512,231.764, 351.996,212.801, 351.996,192.29z
+		 M 249.688,374.633c-23.199-5.312-41.396-23.284-47.397-46.209l0.00,0.00c 15.338-10.978, 28.184-25.222, 37.442-41.771
+		c 5.312,0.906, 10.689,1.641, 16.268,1.641c 20.94,0.00, 40.264-6.782, 56.047-18.174c 6.867,7.845, 11.884,17.346, 14.29,27.862
+		C 287.496,305.219, 256.922,335.784, 249.688,374.633z M 239.998,480.287L 239.998,480.287L 239.998,480.287L 239.998,480.287z M 175.998,152.855
+		l 28.285-28.285c 15.619-15.611, 15.619-40.942,0.00-56.562s-40.951-15.619-56.57,0.00s-15.619,40.95,0.00,56.562L 175.998,152.855z" horiz-adv-x="384"  />
+<glyph unicode="&#xe00f;" d="M 287.998,101.586l0.00,35.051 
+		c 19.111,11.071, 31.996,31.691, 31.996,55.366c0.00,35.349-28.652,64.00-63.992,64.00c-12.807,0.00-24.676-3.844-34.676-10.329
+		c-9.854,42.544-47.796,74.33-93.332,74.33c-53.016,0.00-95.997-42.982-95.997-95.997c0.00-28.401, 12.399-53.835, 31.996-71.407l0.00-39.333 
+		C 25.777,135.41, 0.001,176.649, 0.001,224.007C 0.001,294.695, 57.305,352.00, 127.994,352.00c 48.037,0.00, 89.839-26.488, 111.74-65.634
+		c 5.305,0.906, 10.688,1.641, 16.268,1.641c 53.014,0.00, 95.996-42.981, 95.996-96.004C 351.998,150.225, 325.26,114.782, 287.998,101.586z
+		 M 111.999,96.007c-8.837,0.00-8.001,7.165-8.001,15.994l0.00,64.008 c0.00,8.829-0.836,15.994, 8.001,15.994S 120.00,184.838, 120.00,176.009l0.00-64.008 
+		C 120.00,103.172, 120.836,96.007, 111.999,96.007z M 111.999,64.011c 8.837,0.00, 15.995-7.173, 15.995-16.002
+		c0.00-8.845-7.158-16.003-15.995-16.003s-16.002,7.158-16.002,16.003C 95.997,56.838, 103.162,64.011, 111.999,64.011z M 176.00,64.011
+		c-8.838,0.00-8.002,7.157-8.002,15.994l0.00,64.00 c0.00,8.837-0.836,16.002, 8.002,16.002c 8.836,0.00, 8.00-7.165, 8.00-16.002l0.00-64.00 
+		C 184.00,71.168, 184.836,64.011, 176.00,64.011z M 176.00,32.006c 8.836,0.00, 15.994-7.164, 15.994-16.002c0.00-8.837-7.158-16.002-15.994-16.002
+		c-8.838,0.00-16.002,7.165-16.002,16.002C 159.998,24.842, 167.162,32.006, 176.00,32.006z M 240.00,96.007
+		c-8.838,0.00-8.002,7.165-8.002,15.994l0.00,64.008 c0.00,8.829-0.836,15.994, 8.002,15.994c 8.837,0.00, 8.001-7.165, 8.001-15.994l0.00-64.008 
+		C 248.001,103.172, 248.837,96.007, 240.00,96.007z M 240.00,64.011c 8.837,0.00, 16.002-7.173, 16.002-16.002
+		c0.00-8.845-7.165-16.003-16.002-16.003c-8.838,0.00-16.002,7.158-16.002,16.003C 223.998,56.838, 231.162,64.011, 240.00,64.011z" horiz-adv-x="352"  />
+<glyph unicode="&#xe010;" d="M 416.006,272.003l-32.004,0.00 c-8.838,0.00-16.002,7.157-16.002,16.002
+		c0.00,8.829, 7.164,16.002, 16.002,16.002l 32.004,0.00 c 8.837,0.00, 15.994-7.173, 15.994-16.002C 432.00,279.16, 424.843,272.003, 416.006,272.003z
+		 M 353.139,378.517c-6.243-6.251-16.377-6.251-22.628,0.00c-6.243,6.243-6.243,16.377,0.00,22.628l 22.628,22.628
+		c 6.25,6.243, 16.385,6.243, 22.627,0.00c 6.252-6.251, 6.252-16.385,0.00-22.628L 353.139,378.517z M 329.558,253.642L 329.558,253.642
+		c 13.993-16.674, 22.448-38.161, 22.448-61.641c0.00-41.778-26.738-77.221-64.00-90.417l0.00,35.051 c 19.111,11.071, 31.996,31.691, 31.996,55.366
+		c0.00,35.349-28.652,64.00-64.00,64.00c-12.799,0.00-24.668-3.844-34.669-10.329c-9.86,42.544-47.795,74.33-93.332,74.33
+		c-53.015,0.00-95.997-42.982-95.997-95.997c0.00-28.401, 12.40-53.835, 31.997-71.407l0.00-39.333 C 25.785,135.408,0.00,176.647,0.00,224.005
+		c0.00,70.688, 57.312,127.993, 128.001,127.993c 12.579,0.00, 24.714-1.853, 36.20-5.235c 17.557,22.62, 44.943,37.239, 75.807,37.239
+		c 53.014,0.00, 95.996-42.975, 95.996-95.997C 336.004,275.879, 333.66,264.322, 329.558,253.642z M 240.008,351.998
+		c-17.706,0.00-33.724-7.188-45.311-18.80c 18.729-11.478, 34.293-27.62, 45.037-46.834c 5.312,0.906, 10.695,1.641, 16.268,1.641
+		c 17.01,0.00, 32.949-4.469, 46.803-12.22l0.00,0.00c 0.766,3.953, 1.195,8.032, 1.195,12.22C 304.00,323.354, 275.348,351.998, 240.008,351.998z
+		 M 240.008,416.006c-8.838,0.00-16.002,7.157-16.002,16.002L 224.006,464.004 c0.00,8.837, 7.164,15.994, 16.002,15.994
+		c 8.828,0.00, 15.994-7.157, 15.994-15.994l0.00-31.996 C 256.002,423.163, 248.836,416.006, 240.008,416.006z M 126.868,378.517L 104.24,401.145
+		c-6.251,6.243-6.251,16.377,0.00,22.628c 6.25,6.243, 16.377,6.243, 22.628,0.00l 22.628-22.628c 6.242-6.251, 6.242-16.385,0.00-22.628
+		C 143.245,372.266, 133.119,372.266, 126.868,378.517z M 112.007,96.005c-8.837,0.00-8.001,7.165-8.001,15.994l0.00,64.008 
+		c0.00,8.829-0.836,15.994, 8.001,15.994c 8.829,0.00, 7.993-7.165, 7.993-15.994l0.00-64.008 C 120.00,103.17, 120.836,96.005, 112.007,96.005z
+		 M 112.007,64.009c 8.829,0.00, 15.994-7.173, 15.994-16.002c0.00-8.845-7.165-16.003-15.994-16.003c-8.837,0.00-16.002,7.158-16.002,16.003
+		C 96.005,56.836, 103.17,64.009, 112.007,64.009z M 176.007,64.009c-8.837,0.00-8.001,7.157-8.001,15.994l0.00,64.00 
+		c0.00,8.837-0.836,16.002, 8.001,16.002c 8.829,0.00, 7.993-7.165, 7.993-16.002l0.00-64.00 C 184.00,71.166, 184.836,64.009, 176.007,64.009z
+		 M 176.007,32.004c 8.829,0.00, 15.994-7.164, 15.994-16.002c0.00-8.837-7.165-16.002-15.994-16.002c-8.837,0.00-16.002,7.165-16.002,16.002
+		C 160.005,24.84, 167.17,32.004, 176.007,32.004z M 240.008,96.005c-8.838,0.00-8.002,7.165-8.002,15.994l0.00,64.008 
+		c0.00,8.829-0.836,15.994, 8.002,15.994c 8.828,0.00, 7.992-7.165, 7.992-15.994l0.00-64.008 C 248.00,103.17, 248.836,96.005, 240.008,96.005z
+		 M 240.008,64.009c 8.828,0.00, 15.994-7.173, 15.994-16.002c0.00-8.845-7.166-16.003-15.994-16.003c-8.838,0.00-16.002,7.158-16.002,16.003
+		C 224.006,56.836, 231.17,64.009, 240.008,64.009z" horiz-adv-x="448"  />
+<glyph unicode="&#xe011;" d="M 334.565,247.359L 334.565,247.359
+		c 10.954-15.596, 17.439-34.559, 17.439-55.069c0.00-41.778-26.745-77.221-64.008-90.417l0.00,35.051 
+		c 19.112,11.071, 32.004,31.691, 32.004,55.366c0.00,35.349-28.651,64.00-64.00,64.00c-12.806,0.00-24.667-3.844-34.669-10.329
+		c-9.86,42.544-47.795,74.33-93.332,74.33c-53.014,0.00-95.996-42.982-95.996-95.997c0.00-28.401, 12.40-53.835, 31.996-71.407l0.00-39.333 
+		c-38.223,22.144-64.00,63.383-64.00,110.74c0.00,70.688, 57.312,127.993, 128.00,127.993c 16.018,0.00, 31.316-3.001, 45.443-8.377l0.00,0.00
+		c 13.08,37.458, 48.631,64.384, 90.559,64.384c 6.039,0.00, 11.932-0.625, 17.666-1.696c-1.055-4.602-1.664-9.384-1.664-14.306
+		c0.00-35.349, 28.652-64.001, 64.00-64.001c 4.923,0.00, 9.697,0.609, 14.307,1.665c 1.062-5.735, 1.688-11.627, 1.688-17.659
+		C 359.998,287.216, 350.31,264.463, 334.565,247.359z M 249.695,374.633c-23.206-5.312-41.396-23.284-47.396-46.209l0.00,0.00
+		c 15.338-10.978, 28.176-25.222, 37.435-41.771c 5.313,0.906, 10.696,1.641, 16.268,1.641c 20.948,0.00, 40.263-6.782, 56.046-18.174l0.00,0.00
+		c 6.868,7.845, 11.885,17.346, 14.291,27.862C 287.497,305.219, 256.931,335.784, 249.695,374.633z M 111.998,96.294
+		c-8.836,0.00-7.992,7.165-7.992,15.994l0.00,64.008 c0.00,8.829-0.844,15.994, 7.992,15.994c 8.838,0.00, 8.002-7.165, 8.002-15.994l0.00-64.008 
+		C 120.00,103.459, 120.836,96.294, 111.998,96.294z M 111.998,64.298c 8.838,0.00, 16.002-7.173, 16.002-16.002
+		c0.00-8.845-7.164-16.003-16.002-16.003c-8.836,0.00-15.994,7.158-15.994,16.003C 96.004,57.125, 103.162,64.298, 111.998,64.298z
+		 M 175.998,64.298c-8.836,0.00-8.00,7.157-8.00,15.994l0.00,64.00 c0.00,8.837-0.836,16.002, 8.00,16.002c 8.838,0.00, 8.002-7.165, 8.002-16.002l0.00-64.00 
+		C 184.00,71.455, 184.836,64.298, 175.998,64.298z M 175.998,32.293c 8.838,0.00, 16.002-7.164, 16.002-16.002
+		c0.00-8.837-7.164-16.002-16.002-16.002c-8.836,0.00-15.994,7.165-15.994,16.002C 160.004,25.129, 167.162,32.293, 175.998,32.293z
+		 M 239.999,96.294c-8.837,0.00-8.001,7.165-8.001,15.994l0.00,64.008 c0.00,8.829-0.836,15.994, 8.001,15.994S 248.00,185.125, 248.00,176.296l0.00-64.008 
+		C 248.00,103.459, 248.836,96.294, 239.999,96.294z M 239.999,64.298c 8.837,0.00, 16.002-7.173, 16.002-16.002
+		c0.00-8.845-7.165-16.003-16.002-16.003s-15.994,7.158-15.994,16.003C 224.005,57.125, 231.162,64.298, 239.999,64.298z" horiz-adv-x="384"  />
+<glyph unicode="&#xe012;" d="M 359.998,48.009c-8.838,0.00-15.994,7.157-15.994,16.002
+		c0.00,8.829, 7.156,15.994, 15.994,15.994c 8.837,0.00, 16.002-7.165, 16.002-15.994C 376.00,55.166, 368.835,48.009, 359.998,48.009z
+		 M 287.996,101.586l0.00,35.051 C 307.108,147.708, 320.00,168.328, 320.00,192.003c0.00,35.349-28.651,64.00-64.00,64.00
+		c-12.806,0.00-24.666-3.844-34.676-10.329c-9.853,42.544-47.787,74.33-93.324,74.33c-53.021,0.00-96.004-42.982-96.004-95.997
+		c0.00-28.401, 12.408-53.835, 32.004-71.407l0.00-39.333 C 25.776,135.41,0.00,176.649,0.00,224.007C0.00,294.695, 57.304,352.00, 128.00,352.00
+		c 48.037,0.00, 89.84-26.488, 111.732-65.634c 5.313,0.906, 10.697,1.641, 16.268,1.641c 53.016,0.00, 95.997-42.981, 95.997-96.004
+		C 351.997,150.225, 325.26,114.782, 287.996,101.586z M 24.003,40.008c 8.837,0.00, 15.994-7.165, 15.994-16.002
+		c0.00-8.838-7.157-16.002-15.994-16.002S 8.00,15.168, 8.00,24.006C 8.00,32.843, 15.166,40.008, 24.003,40.008z M 103.997,176.009
+		c0.00,8.829-0.836,15.994, 8.001,15.994s 8.001-7.165, 8.001-15.994L 119.999-16.00 l-16.002,0.00 l0.00,0.00l0.00,0.00c0.00,17.675-14.322,32.004-31.996,32.004l0.00,16.002 
+		c 12.345,0.00, 23.495-4.797, 31.996-12.454L 103.997,176.009 z M 175.998,64.011c-8.837,0.00-8.001,7.157-8.001,15.994l0.00,64.00 
+		c0.00,8.837-0.836,16.002, 8.001,16.002c 8.838,0.00, 8.002-7.165, 8.002-16.002l0.00-64.00 C 184.00,71.168, 184.836,64.011, 175.998,64.011z
+		 M 175.998,32.006c 8.838,0.00, 16.002-7.164, 16.002-16.002c0.00-8.837-7.164-16.002-16.002-16.002c-8.837,0.00-16.002,7.165-16.002,16.002
+		C 159.996,24.842, 167.161,32.006, 175.998,32.006z M 239.998,192.003c 8.838,0.00, 8.002-7.165, 8.002-15.994l0.00-144.151 
+		c 14.588,19.479, 37.785,32.153, 64.00,32.153l0.00-16.002 c-35.349,0.00-64.00-28.66-64.00-64.009l-16.002,0.00 c0.00,2.181, 0.156,4.321, 0.328,6.454
+		c-0.43,2.673-0.328,5.954-0.328,9.548L 231.998,176.009 C 231.998,184.838, 231.162,192.003, 239.998,192.003z" horiz-adv-x="384"  />
+<glyph unicode="&#xe013;" d="M 416.005,272.232l-32.004,0.00 c-8.828,0.00-15.994,7.165-15.994,16.002
+		s 7.166,16.002, 15.994,16.002l 32.004,0.00 c 8.838,0.00, 16.002-7.165, 16.002-16.002S 424.843,272.232, 416.005,272.232z M 353.138,378.745
+		c-6.242-6.251-16.377-6.251-22.627,0.00c-6.244,6.251-6.244,16.378,0.00,22.628l 22.627,22.628c 6.252,6.251, 16.385,6.251, 22.629,0.00
+		c 6.25-6.251, 6.25-16.377,0.00-22.628L 353.138,378.745z M 329.558,253.87L 329.558,253.87c 14.002-16.674, 22.447-38.161, 22.447-61.632
+		c0.00-41.787-26.738-77.229-64.00-90.426l0.00,35.051 c 19.111,11.072, 32.004,31.699, 32.004,55.375c0.00,35.34-28.66,63.992-64.008,63.992
+		c-12.799,0.00-24.66-3.845-34.668-10.33c-9.854,42.545-47.795,74.338-93.333,74.338c-53.015,0.00-95.997-42.99-95.997-96.004
+		c0.00-11.244, 2.016-21.995, 5.579-31.996l0.00,0.00c 5.438-15.283, 14.58-28.801, 26.425-39.412l0.00-39.333 
+		c-29.285,16.972-51.147,45.193-59.82,78.745C 1.532,202.474,0.00,213.162,0.00,224.234c0.00,70.688, 57.312,128.00, 128.00,128.00
+		c 12.587,0.00, 24.722-1.859, 36.20-5.243c 17.564,22.628, 44.951,37.239, 75.806,37.239c 53.014,0.00, 96.004-42.975, 96.004-95.996
+		C 336.011,276.107, 333.667,264.56, 329.558,253.87z M 240.007,352.234c-17.697,0.00-33.723-7.188-45.311-18.807
+		c 18.729-11.479, 34.293-27.621, 45.037-46.834c 5.312,0.906, 10.703,1.641, 16.268,1.641c 17.01,0.00, 32.957-4.47, 46.803-12.221l0.00,0.00
+		c 0.773,3.954, 1.203,8.032, 1.203,12.221C 304.007,323.582, 275.347,352.234, 240.007,352.234z M 240.007,416.234
+		c-8.838,0.00-16.002,7.157-16.002,16.002L 224.005,464.232 c0.00,8.837, 7.164,16.002, 16.002,16.002c 8.836,0.00, 15.994-7.165, 15.994-16.002l0.00-31.996 
+		C 256.001,423.392, 248.843,416.234, 240.007,416.234z M 126.868,378.745L 104.24,401.373c-6.243,6.251-6.243,16.377,0.00,22.628
+		c 6.25,6.251, 16.385,6.251, 22.628,0.00l 22.628-22.628c 6.25-6.25, 6.25-16.377,0.00-22.628C 143.252,372.494, 133.118,372.494, 126.868,378.745
+		z M 24.003,40.234c 8.845,0.00, 16.002-7.165, 16.002-16.002s-7.157-16.002-16.002-16.002c-8.829,0.00-16.002,7.165-16.002,16.002
+		S 15.174,40.234, 24.003,40.234z M 104.005,176.235c0.00,8.829-0.836,16.003, 8.001,16.003l0.00,0.00c 8.837,0.00, 8.001-7.174, 8.001-16.003
+		l0.00-192.008 l-16.002,0.00 l0.00,0.00l0.00,0.00c0.00,17.674-14.322,32.004-31.996,32.004l0.00,16.002 c 12.345,0.00, 23.487-4.798, 31.996-12.455L 104.005,176.235 z
+		 M 176.006,64.237c-8.837,0.00-8.001,7.157-8.001,15.994l0.00,64.00 c0.00,8.837-0.836,16.002, 8.001,16.002c 8.836,0.00, 8.00-7.165, 8.00-16.002l0.00-64.00 
+		C 184.007,71.395, 184.843,64.237, 176.006,64.237z M 176.006,32.233c 8.836,0.00, 16.002-7.165, 16.002-16.002
+		c0.00-8.829-7.166-16.002-16.002-16.002c-8.837,0.00-16.002,7.173-16.002,16.002C 160.004,25.068, 167.169,32.233, 176.006,32.233z
+		 M 240.007,192.238L 240.007,192.238c 8.836,0.00, 8.00-7.174, 8.00-16.003l0.00-144.143 c 14.588,19.472, 37.787,32.145, 64.002,32.145l0.00-16.002 
+		c-35.35,0.00-64.002-28.66-64.002-64.008l-16.002,0.00 c0.00,2.188, 0.156,4.32, 0.32,6.454c-0.422,2.672-0.32,5.953-0.32,9.548L 232.005,176.235 
+		C 232.005,185.064, 231.169,192.238, 240.007,192.238z M 360.007,80.231c 8.836,0.00, 15.994-7.165, 15.994-15.994
+		c0.00-8.845-7.158-16.002-15.994-16.002c-8.838,0.00-16.002,7.157-16.002,16.002C 344.005,73.066, 351.169,80.231, 360.007,80.231z" horiz-adv-x="448"  />
+<glyph unicode="&#xe014;" d="M 359.998,48.235c-8.829,0.00-15.994,7.157-15.994,16.002
+		c0.00,8.829, 7.165,15.994, 15.994,15.994c 8.845,0.00, 16.002-7.165, 16.002-15.994C 376.00,55.393, 368.843,48.235, 359.998,48.235z
+		 M 334.565,247.299c 10.954-15.596, 17.439-34.559, 17.439-55.061c0.00-41.787-26.745-77.229-64.00-90.426l0.00,35.051 
+		c 19.111,11.072, 32.004,31.699, 32.004,55.375c0.00,35.34-28.66,63.992-64.008,63.992c-12.799,0.00-24.667-3.845-34.669-10.33
+		c-9.853,42.545-47.795,74.338-93.331,74.338c-53.015,0.00-95.997-42.99-95.997-96.004c0.00-11.244, 2.017-21.995, 5.579-31.996l0.00,0.00
+		c 5.438-15.283, 14.58-28.801, 26.425-39.412l0.00-39.333 c-29.284,16.972-51.146,45.193-59.819,78.745C 1.531,202.474,0.00,213.162,0.00,224.234
+		c0.00,70.688, 57.312,128.00, 128.001,128.00c 16.018,0.00, 31.316-3.008, 45.442-8.384c 13.08,37.458, 48.632,64.383, 90.559,64.383
+		c 6.048,0.00, 11.931-0.625, 17.666-1.688c-1.055-4.61-1.664-9.392-1.664-14.314c0.00-35.349, 28.66-63.992, 64.00-63.992
+		c 4.931,0.00, 9.696,0.602, 14.307,1.656c 1.062-5.735, 1.688-11.626, 1.688-17.658C 359.998,287.156, 350.31,264.403, 334.565,247.299z
+		 M 249.703,374.573c-23.206-5.313-41.403-23.284-47.404-46.209l0.00,0.00c 15.338-10.978, 28.184-25.222, 37.435-41.771
+		c 5.312,0.906, 10.704,1.641, 16.268,1.641c 20.947,0.00, 40.271-6.782, 56.054-18.175c 6.868,7.845, 11.885,17.347, 14.283,27.863
+		C 287.505,305.158, 256.931,335.732, 249.703,374.573z M 239.999,480.234L 239.999,480.234L 239.999,480.234L 239.999,480.234z M 24.003,40.234
+		c 8.837,0.00, 16.002-7.165, 16.002-16.002S 32.84,8.23, 24.003,8.23s-16.002,7.165-16.002,16.002S 15.166,40.234, 24.003,40.234z
+		 M 104.005,176.235c0.00,8.829-0.836,16.003, 7.993,16.003l0.00,0.00c 8.846,0.00, 8.001-7.174, 8.001-16.003l0.00-192.008 l-15.994,0.00 l0.00,0.00l0.00,0.00
+		c0.00,17.674-14.321,32.004-31.996,32.004l0.00,16.002 c 12.346,0.00, 23.487-4.798, 31.996-12.455L 104.005,176.235 z M 176.007,64.237
+		c-8.838,0.00-8.001,7.157-8.001,15.994l0.00,64.00 c0.00,8.837-0.837,16.002, 8.001,16.002c 8.829,0.00, 8.001-7.165, 8.001-16.002l0.00-64.00 
+		C 184.008,71.395, 184.836,64.237, 176.007,64.237z M 176.007,32.233c 8.829,0.00, 16.002-7.165, 16.002-16.002
+		c0.00-8.829-7.173-16.002-16.002-16.002c-8.838,0.00-16.002,7.173-16.002,16.002C 160.005,25.068, 167.169,32.233, 176.007,32.233z
+		 M 239.999,192.238L 239.999,192.238c 8.845,0.00, 8.001-7.174, 8.001-16.003l0.00-144.143 c 14.596,19.472, 37.786,32.145, 64.008,32.145l0.00-16.002 
+		c-35.349,0.00-64.008-28.66-64.008-64.008l-15.994,0.00 c0.00,2.188, 0.148,4.32, 0.32,6.454c-0.422,2.672-0.32,5.953-0.32,9.548L 232.006,176.235 
+		C 232.006,185.064, 231.17,192.238, 239.999,192.238z" horiz-adv-x="384"  />
+<glyph unicode="&#xe015;" d="M 288.003,101.578l0.00,35.051 
+		c 19.111,11.072, 31.996,31.699, 31.996,55.375c0.00,35.34-28.652,63.993-64.002,63.993c-12.797,0.00-24.666-3.845-34.668-10.33
+		c-9.852,42.545-47.794,74.338-93.332,74.338c-53.022,0.00-95.996-42.99-95.996-96.004c0.00-28.403, 12.399-53.834, 32.004-71.409l0.00-39.332 
+		C 25.782,135.402-0.002,176.65-0.002,224.00c0.00,70.688, 57.312,128.00, 128.00,128.00c 48.037,0.00, 89.839-26.496, 111.732-65.641
+		c 5.314,0.906, 10.697,1.641, 16.268,1.641c 53.023,0.00, 96.006-42.982, 96.006-95.997C 352.003,150.217, 325.257,114.775, 288.003,101.578z
+		 M 111.996,160.00c 8.845,0.00, 16.002-7.166, 16.002-16.002c0.00-8.838-7.157-16.002-16.002-16.002c-8.829,0.00-15.994,7.164-15.994,16.002
+		C 96.001,152.834, 103.167,160.00, 111.996,160.00z M 111.996,96.00c 8.845,0.00, 16.002-7.166, 16.002-16.002c0.00-8.838-7.157-15.994-16.002-15.994
+		c-8.829,0.00-15.994,7.156-15.994,15.994C 96.001,88.834, 103.167,96.00, 111.996,96.00z M 176.003,127.996
+		c 8.83,0.00, 16.001-7.158, 16.001-16.002c0.00-8.83-7.172-15.994-16.001-15.994c-8.845,0.00-16.002,7.164-16.002,15.994
+		C 160.001,120.838, 167.159,127.996, 176.003,127.996z M 176.003,64.004c 8.83,0.00, 16.001-7.174, 16.001-16.002
+		c0.00-8.838-7.172-16.002-16.001-16.002c-8.845,0.00-16.002,7.164-16.002,16.002C 160.001,56.83, 167.159,64.004, 176.003,64.004z
+		 M 239.995,160.00c 8.846,0.00, 16.002-7.166, 16.002-16.002c0.00-8.838-7.156-16.002-16.002-16.002c-8.828,0.00-15.994,7.164-15.994,16.002
+		C 224.001,152.834, 231.167,160.00, 239.995,160.00z M 239.995,96.00c 8.846,0.00, 16.002-7.166, 16.002-16.002c0.00-8.838-7.156-15.994-16.002-15.994
+		c-8.828,0.00-15.994,7.156-15.994,15.994C 224.001,88.834, 231.167,96.00, 239.995,96.00z" horiz-adv-x="352"  />
+<glyph unicode="&#xe016;" d="M 416.006,271.999l-32.005,0.00 c-8.837,0.00-16.002,7.165-16.002,16.002
+		s 7.165,16.002, 16.002,16.002l 32.005,0.00 c 8.828,0.00, 15.994-7.165, 15.994-16.002S 424.834,271.999, 416.006,271.999z M 353.138,378.511
+		c-6.25-6.251-16.377-6.251-22.627,0.00c-6.251,6.251-6.251,16.378,0.00,22.628l 22.627,22.628c 6.251,6.251, 16.378,6.251, 22.628,0.00
+		c 6.251-6.251, 6.251-16.377,0.00-22.628L 353.138,378.511z M 329.557,253.636L 329.557,253.636c 13.994-16.674, 22.44-38.162, 22.44-61.632
+		c0.00-41.787-26.737-77.229-63.992-90.426l0.00,35.051 c 19.112,11.072, 31.996,31.699, 31.996,55.375c0.00,35.34-28.651,63.993-64.00,63.993
+		c-12.799,0.00-24.667-3.845-34.676-10.33c-9.854,42.545-47.788,74.338-93.324,74.338c-53.022,0.00-95.997-42.99-95.997-96.004
+		c0.00-28.403, 12.40-53.834, 31.996-71.409l0.00-39.332 C 25.785,135.402,0.00,176.65,0.00,224.00c0.00,70.688, 57.305,128.00, 128.001,128.00
+		c 12.579,0.00, 24.722-1.859, 36.191-5.243c 17.564,22.628, 44.951,37.239, 75.807,37.239c 53.022,0.00, 96.004-42.975, 96.004-95.996
+		C 336.003,275.874, 333.659,264.326, 329.557,253.636z M 239.999,352.00c-17.697,0.00-33.723-7.188-45.303-18.807
+		c 18.722-11.479, 34.286-27.621, 45.037-46.834c 5.313,0.906, 10.696,1.641, 16.268,1.641c 17.002,0.00, 32.949-4.47, 46.803-12.221l0.00,0.00
+		c 0.766,3.954, 1.203,8.032, 1.203,12.221C 304.007,323.348, 275.347,352.00, 239.999,352.00z M 239.999,416.00c-8.829,0.00-16.002,7.157-16.002,16.002
+		L 223.997,463.999 C 223.997,472.835, 231.17,480.00, 239.999,480.00c 8.837,0.00, 16.002-7.165, 16.002-16.002l0.00-31.996 C 256.001,423.158, 248.836,416.00, 239.999,416.00z
+		 M 126.859,378.511l-22.627,22.628c-6.243,6.251-6.243,16.377,0.00,22.628c 6.25,6.251, 16.385,6.251, 22.627,0.00l 22.628-22.628
+		c 6.251-6.25, 6.251-16.377,0.00-22.628C 143.245,372.26, 133.11,372.26, 126.859,378.511z M 111.999,160.00
+		c 8.837,0.00, 16.002-7.166, 16.002-16.002c0.00-8.838-7.165-16.002-16.002-16.002c-8.83,0.00-16.002,7.164-16.002,16.002
+		C 95.997,152.834, 103.169,160.00, 111.999,160.00z M 111.999,96.00c 8.837,0.00, 16.002-7.166, 16.002-16.002c0.00-8.838-7.165-15.994-16.002-15.994
+		c-8.83,0.00-16.002,7.156-16.002,15.994C 95.997,88.834, 103.169,96.00, 111.999,96.00z M 176.007,127.996
+		c 8.829,0.00, 15.994-7.158, 15.994-16.002c0.00-8.83-7.165-15.994-15.994-15.994c-8.845,0.00-16.002,7.164-16.002,15.994
+		C 160.005,120.838, 167.162,127.996, 176.007,127.996z M 176.007,64.004c 8.829,0.00, 15.994-7.174, 15.994-16.002
+		c0.00-8.838-7.165-16.002-15.994-16.002c-8.845,0.00-16.002,7.164-16.002,16.002C 160.005,56.83, 167.162,64.004, 176.007,64.004z
+		 M 239.999,160.00c 8.837,0.00, 16.002-7.166, 16.002-16.002c0.00-8.838-7.165-16.002-16.002-16.002c-8.829,0.00-16.002,7.164-16.002,16.002
+		C 223.997,152.834, 231.17,160.00, 239.999,160.00z M 239.999,96.00c 8.837,0.00, 16.002-7.166, 16.002-16.002c0.00-8.838-7.165-15.994-16.002-15.994
+		c-8.829,0.00-16.002,7.156-16.002,15.994C 223.997,88.834, 231.17,96.00, 239.999,96.00z" horiz-adv-x="448"  />
+<glyph unicode="&#xe017;" d="M 334.557,247.065L 334.557,247.065
+		c 10.955-15.596, 17.44-34.559, 17.44-55.061c0.00-41.787-26.737-77.229-63.993-90.426L 288.004,136.63 c 19.104,11.072, 31.997,31.699, 31.997,55.375
+		c0.00,35.34-28.652,63.992-64.001,63.992c-12.806,0.00-24.674-3.845-34.676-10.33c-9.853,42.545-47.787,74.338-93.324,74.338
+		c-53.021,0.00-95.996-42.99-95.996-96.004c0.00-28.402, 12.40-53.835, 31.996-71.408l0.00-39.333 C 25.776,135.403,0.00,176.651,0.00,224.001
+		c0.00,70.688, 57.304,128.00, 128.00,128.00c 16.018,0.00, 31.316-3.008, 45.443-8.384l0.00,0.00C 186.523,381.075, 222.074,408.00, 264.002,408.00
+		c 6.039,0.00, 11.923-0.625, 17.658-1.688c-1.047-4.61-1.656-9.392-1.656-14.314c0.00-35.349, 28.645-63.992, 63.992-63.992
+		c 4.93,0.00, 9.704,0.602, 14.314,1.656c 1.062-5.735, 1.688-11.626, 1.688-17.658C 359.998,286.923, 350.309,264.17, 334.557,247.065z
+		 M 249.688,374.34c-23.198-5.313-41.396-23.284-47.396-46.209c 15.346-10.978, 28.191-25.222, 37.441-41.771
+		c 5.313,0.906, 10.689,1.641, 16.268,1.641c 20.94,0.00, 40.271-6.782, 56.047-18.175l0.00,0.00c 6.867,7.845, 11.885,17.347, 14.291,27.863
+		C 287.496,304.925, 256.922,335.499, 249.688,374.34z M 111.998,160.00c 8.838,0.00, 16.002-7.165, 16.002-16.002s-7.164-16.002-16.002-16.002
+		c-8.837,0.00-16.002,7.165-16.002,16.002S 103.161,160.00, 111.998,160.00z M 111.998,96.00c 8.838,0.00, 16.002-7.165, 16.002-16.002
+		s-7.164-15.994-16.002-15.994c-8.837,0.00-16.002,7.157-16.002,15.994S 103.161,96.00, 111.998,96.00z M 175.998,127.996
+		c 8.838,0.00, 16.002-7.157, 16.002-16.002c0.00-8.829-7.164-15.994-16.002-15.994c-8.837,0.00-15.994,7.165-15.994,15.994
+		C 160.004,120.839, 167.161,127.996, 175.998,127.996z M 175.998,64.004c 8.838,0.00, 16.002-7.173, 16.002-16.002
+		c0.00-8.837-7.164-16.002-16.002-16.002c-8.837,0.00-15.994,7.165-15.994,16.002C 160.004,56.831, 167.161,64.004, 175.998,64.004z
+		 M 239.998,160.00c 8.838,0.00, 16.002-7.165, 16.002-16.002s-7.164-16.002-16.002-16.002c-8.836,0.00-16.002,7.165-16.002,16.002
+		S 231.162,160.00, 239.998,160.00z M 239.998,96.00c 8.838,0.00, 16.002-7.165, 16.002-16.002s-7.164-15.994-16.002-15.994
+		c-8.836,0.00-16.002,7.157-16.002,15.994S 231.162,96.00, 239.998,96.00z" horiz-adv-x="384"  />
+<glyph unicode="&#xe018;" d="M 271.868,97.437l0.00,32.661 c 27.59,7.117, 48.006,32.09, 48.006,61.906
+		c0.00,35.34-28.66,63.992-64.00,63.992c-12.807,0.00-24.675-3.845-34.676-10.33c-9.854,42.545-47.787,74.338-93.324,74.338
+		c-53.022,0.00-96.004-42.99-96.004-96.004c0.00-35.513, 19.338-66.446, 47.998-83.05l0.00-35.56 C 32.979,124.386-0.126,170.298-0.126,224.00
+		c0.00,70.688, 57.303,128.00, 128.00,128.00c 48.037,0.00, 89.839-26.496, 111.732-65.641c 5.312,0.906, 10.689,1.641, 16.268,1.641
+		c 53.015,0.00, 95.996-42.982, 95.996-95.996C 351.87,144.442, 317.257,105.07, 271.868,97.437z M 120.443,143.997
+		c 4.423,7.657, 14.204,10.275, 21.862,5.86l 17.564-10.142l0.00,20.283 c0.00,8.837, 7.164,16.002, 16.002,16.002
+		c 8.837,0.00, 16.002-7.165, 16.002-16.002l0.00-20.283 l 17.564,10.142c 7.657,4.415, 17.439,1.797, 21.854-5.86
+		c 4.423-7.649, 1.797-17.439-5.853-21.854l-17.572-10.149l 17.572-10.142c 7.649-4.415, 10.275-14.205, 5.853-21.854
+		c-4.415-7.649-14.197-10.274-21.854-5.853l-17.564,10.143l0.00-20.284 c0.00-8.845-7.165-16.002-16.002-16.002
+		c-8.838,0.00-16.002,7.157-16.002,16.002l0.00,20.284 l-17.564-10.143c-7.658-4.422-17.439-1.797-21.862,5.853
+		c-4.415,7.649-1.798,17.439, 5.86,21.854l 17.564,10.142l-17.564,10.149C 118.646,126.558, 116.028,136.348, 120.443,143.997z
+		 M 175.872,95.999c 8.837,0.00, 16.002,7.165, 16.002,15.994c0.00,8.845-7.165,16.002-16.002,16.002c-8.838,0.00-16.002-7.157-16.002-16.002
+		C 159.87,103.164, 167.034,95.999, 175.872,95.999z" horiz-adv-x="352"  />
+<glyph unicode="&#xe019;" d="M 415.997,272.232l-32.004,0.00 c-8.837,0.00-15.994,7.165-15.994,16.002
+		s 7.157,16.002, 15.994,16.002l 32.004,0.00 c 8.838,0.00, 16.002-7.165, 16.002-16.002S 424.835,272.232, 415.997,272.232z M 353.138,378.745
+		c-6.25-6.251-16.385-6.251-22.627,0.00c-6.252,6.251-6.252,16.378,0.00,22.628l 22.627,22.628c 6.243,6.251, 16.377,6.251, 22.62,0.00
+		c 6.251-6.251, 6.251-16.377,0.00-22.628L 353.138,378.745z M 329.549,253.87L 329.549,253.87c 13.994-16.674, 22.448-38.161, 22.448-61.632
+		c0.00-47.562-34.613-86.934-80.002-94.567l0.00,32.661 c 27.59,7.117, 47.998,32.09, 47.998,61.906c0.00,35.34-28.652,63.992-63.992,63.992
+		c-12.807,0.00-24.676-3.845-34.676-10.33c-9.854,42.545-47.796,74.338-93.332,74.338c-53.016,0.00-95.997-42.99-95.997-96.004
+		c0.00-35.513, 19.331-66.446, 47.998-83.05l0.00-35.56 C 33.105,124.62,0.00,170.532,0.00,224.234c0.00,70.688, 57.304,128.00, 127.993,128.00
+		c 12.587,0.00, 24.721-1.859, 36.199-5.243c 17.564,22.628, 44.943,37.239, 75.807,37.239c 53.014,0.00, 95.996-42.975, 95.996-95.996
+		C 335.995,276.107, 333.651,264.56, 329.549,253.87z M 239.999,352.234c-17.705,0.00-33.723-7.188-45.311-18.807
+		c 18.729-11.479, 34.293-27.621, 45.045-46.834c 5.305,0.906, 10.688,1.641, 16.268,1.641c 17.002,0.00, 32.949-4.47, 46.803-12.221l0.00,0.00
+		c 0.766,3.954, 1.195,8.032, 1.195,12.221C 303.999,323.582, 275.339,352.234, 239.999,352.234z M 239.999,416.234
+		c-8.838,0.00-16.002,7.157-16.002,16.002L 223.997,464.232 c0.00,8.837, 7.164,16.002, 16.002,16.002c 8.837,0.00, 16.002-7.165, 16.002-16.002l0.00-31.996 
+		C 256.001,423.392, 248.836,416.234, 239.999,416.234z M 126.859,378.745l-22.628,22.628c-6.25,6.251-6.25,16.377,0.00,22.628
+		c 6.251,6.251, 16.377,6.251, 22.628,0.00l 22.628-22.628c 6.251-6.25, 6.251-16.377,0.00-22.628
+		C 143.237,372.494, 133.11,372.494, 126.859,378.745z M 120.569,144.231c 4.423,7.657, 14.205,10.275, 21.855,5.86l 17.572-10.142l0.00,20.283 
+		c0.00,8.837, 7.164,16.002, 16.002,16.002c 8.836,0.00, 15.994-7.165, 15.994-16.002l0.00-20.283 l 17.572,10.142
+		c 7.649,4.415, 17.439,1.797, 21.854-5.86c 4.423-7.649, 1.798-17.439-5.852-21.854l-17.572-10.149l 17.572-10.142
+		c 7.649-4.415, 10.274-14.205, 5.852-21.854c-4.414-7.649-14.204-10.274-21.854-5.853l-17.572,10.143l0.00-20.284 
+		c0.00-8.845-7.158-16.002-15.994-16.002c-8.838,0.00-16.002,7.157-16.002,16.002l0.00,20.284 l-17.572-10.143
+		c-7.65-4.422-17.433-1.797-21.855,5.853c-4.414,7.649-1.797,17.439, 5.86,21.854l 17.565,10.142l-17.565,10.149
+		C 118.772,126.792, 116.155,136.582, 120.569,144.231z M 175.999,96.233c 8.836,0.00, 15.994,7.165, 15.994,15.994
+		c0.00,8.845-7.158,16.002-15.994,16.002c-8.838,0.00-16.002-7.157-16.002-16.002C 159.997,103.398, 167.161,96.233, 175.999,96.233z" horiz-adv-x="448"  />
+<glyph unicode="&#xe01a;" d="M 334.566,246.799L 334.566,246.799
+		c 10.954-15.596, 17.439-34.559, 17.439-55.061c0.00-41.787-26.738-77.229-64.00-90.426l0.00,35.051 c 19.111,11.072, 31.996,31.699, 31.996,55.375
+		c0.00,35.34-28.652,63.992-64.00,63.992c-12.799,0.00-24.668-3.845-34.669-10.33c-9.86,42.545-47.795,74.338-93.332,74.338
+		c-53.015,0.00-95.997-42.99-95.997-96.004c0.00-28.402, 12.40-53.835, 31.997-71.408l0.00-39.333 C 25.785,135.137,0.00,176.385,0.00,223.734
+		c0.00,70.688, 57.312,128.00, 128.001,128.00c 16.018,0.00, 31.316-3.008, 45.443-8.384l0.00,0.00c 13.079,37.458, 48.631,64.383, 90.558,64.383
+		c 6.04,0.00, 11.932-0.625, 17.666-1.688c-1.055-4.61-1.664-9.392-1.664-14.314c0.00-35.349, 28.652-63.992, 64.00-63.992
+		c 4.924,0.00, 9.697,0.602, 14.307,1.656c 1.07-5.735, 1.695-11.626, 1.695-17.658C 360.006,286.656, 350.311,263.903, 334.566,246.799z
+		 M 249.695,374.073c-23.205-5.313-41.395-23.284-47.396-46.209l0.00,0.00c 15.338-10.978, 28.184-25.222, 37.436-41.771
+		c 5.312,0.906, 10.695,1.641, 16.268,1.641c 20.947,0.00, 40.27-6.782, 56.045-18.175l0.00,0.00c 6.877,7.845, 11.893,17.347, 14.291,27.863
+		C 287.498,304.658, 256.932,335.232, 249.695,374.073z M 120.578,143.731c 4.414,7.657, 14.205,10.275, 21.854,5.86l 17.572-10.142l0.00,20.283 
+		c0.00,8.837, 7.165,16.002, 16.002,16.002c 8.829,0.00, 15.994-7.165, 15.994-16.002l0.00-20.283 l 17.573,10.142
+		c 7.648,4.415, 17.439,1.797, 21.854-5.86c 4.423-7.649, 1.797-17.439-5.852-21.854l-17.573-10.149l 17.573-10.142
+		c 7.648-4.415, 10.274-14.205, 5.852-21.854c-4.414-7.649-14.205-10.274-21.854-5.853l-17.573,10.143l0.00-20.284 
+		c0.00-8.845-7.165-16.002-15.994-16.002c-8.837,0.00-16.002,7.157-16.002,16.002l0.00,20.284 l-17.572-10.143
+		c-7.649-4.422-17.44-1.797-21.854,5.853s-1.797,17.439, 5.853,21.854l 17.572,10.142l-17.572,10.149
+		C 118.781,126.292, 116.164,136.082, 120.578,143.731z M 176.007,95.733c 8.829,0.00, 15.994,7.165, 15.994,15.994
+		c0.00,8.845-7.165,16.002-15.994,16.002c-8.837,0.00-16.002-7.157-16.002-16.002C 160.005,102.898, 167.17,95.733, 176.007,95.733z" horiz-adv-x="384"  />
+<glyph unicode="&#xe01b;" d="M 335.872,96.00l-320.00,0.00 c-8.837,0.00-16.002,7.164-16.002,15.994
+		c0.00,8.844, 7.165,16.002, 16.002,16.002l 320.00,0.00 c 8.838,0.00, 16.002-7.158, 16.002-16.002C 351.874,103.164, 344.71,96.00, 335.872,96.00z
+		 M 335.872,160.00l-320.00,0.00 c-8.837,0.00-16.002,7.164-16.002,16.002c0.00,8.828, 7.165,16.002, 16.002,16.002l 320.00,0.00 
+		c 8.838,0.00, 16.002-7.174, 16.002-16.002C 351.874,167.164, 344.71,160.00, 335.872,160.00z M 255.87,255.997
+		c-12.805,0.00-24.666-3.845-34.668-10.33c-9.861,42.545-47.795,74.338-93.333,74.338c-53.014,0.00-95.996-42.99-95.996-96.004L-0.13,224.001 
+		c0.00,70.688, 57.312,128.00, 128.00,128.00c 48.037,0.00, 89.84-26.496, 111.733-65.641C 244.917,287.266, 250.30,288.00, 255.87,288.00
+		c 41.779,0.00, 77.229-26.738, 90.418-64.00l-35.051,0.00 C 300.165,243.112, 279.546,255.997, 255.87,255.997z M 15.872,64.004l 320.00,0.00 
+		c 8.838,0.00, 16.002-7.174, 16.002-16.002c0.00-8.838-7.164-16.002-16.002-16.002l-320.00,0.00 c-8.837,0.00-16.002,7.164-16.002,16.002
+		C-0.13,56.83, 7.035,64.004, 15.872,64.004z" horiz-adv-x="352"  />
+<glyph unicode="&#xe01c;" d="M 415.997,271.999l-31.996,0.00 c-8.837,0.00-16.002,7.165-16.002,16.002
+		s 7.165,16.002, 16.002,16.002l 31.996,0.00 c 8.837,0.00, 16.002-7.165, 16.002-16.002S 424.834,271.999, 415.997,271.999z M 353.138,378.511
+		c-6.251-6.251-16.377-6.251-22.628,0.00s-6.251,16.378,0.00,22.628l 22.628,22.628c 6.251,6.251, 16.377,6.251, 22.628,0.00
+		c 6.242-6.251, 6.242-16.377,0.00-22.628L 353.138,378.511z M 329.557,253.636L 329.557,253.636c 7.298-8.696, 13.041-18.721, 16.908-29.636
+		l-35.098,0.00 c-11.072,19.111-31.691,31.996-55.367,31.996c-12.806,0.00-24.666-3.845-34.676-10.33
+		c-9.853,42.545-47.787,74.338-93.324,74.338c-53.021,0.00-96.004-42.99-96.004-96.004L0.00,224.00 c0.00,70.688, 57.304,128.00, 128.00,128.00
+		c 12.58,0.00, 24.715-1.859, 36.191-5.243c 17.565,22.628, 44.951,37.239, 75.807,37.239c 53.022,0.00, 96.004-42.975, 96.004-95.996
+		C 336.002,275.874, 333.658,264.326, 329.557,253.636z M 239.998,352.00c-17.697,0.00-33.723-7.188-45.303-18.807
+		c 18.722-11.479, 34.286-27.621, 45.037-46.834C 245.046,287.266, 250.43,288.00, 256.00,288.00c 17.002,0.00, 32.949-4.47, 46.803-12.221l0.00,0.00
+		c 0.766,3.954, 1.195,8.032, 1.195,12.221C 303.998,323.348, 275.347,352.00, 239.998,352.00z M 239.998,416.00c-8.836,0.00-16.002,7.157-16.002,16.002
+		L 223.996,463.999 C 223.996,472.835, 231.162,480.00, 239.998,480.00C 248.836,480.00, 256.00,472.835, 256.00,463.999l0.00-31.996 C 256.00,423.158, 248.836,416.00, 239.998,416.00z
+		 M 126.859,378.511l-22.62,22.628c-6.251,6.251-6.251,16.377,0.00,22.628c 6.243,6.251, 16.377,6.251, 22.62,0.00l 22.628-22.628
+		c 6.251-6.25, 6.251-16.377,0.00-22.628C 143.244,372.26, 133.11,372.26, 126.859,378.511z M 16.002,192.004l 320.00,0.00 
+		c 8.83,0.00, 15.995-7.174, 15.995-16.002c0.00-8.838-7.165-16.002-15.995-16.002l-320.00,0.00 C 7.164,160.00,0.00,167.164,0.00,176.002
+		C0.00,184.83, 7.164,192.004, 16.002,192.004z M 16.002,127.996l 320.00,0.00 c 8.83,0.00, 15.995-7.158, 15.995-16.002
+		c0.00-8.83-7.165-15.994-15.995-15.994l-320.00,0.00 C 7.164,96.00,0.00,103.164,0.00,111.994C0.00,120.838, 7.164,127.996, 16.002,127.996z M 16.002,64.004
+		l 320.00,0.00 c 8.83,0.00, 15.995-7.174, 15.995-16.002c0.00-8.838-7.165-16.002-15.995-16.002l-320.00,0.00 C 7.164,32.00,0.00,39.164,0.00,48.002
+		C0.00,56.83, 7.164,64.004, 16.002,64.004z" horiz-adv-x="448"  />
+<glyph unicode="&#xe01d;" d="M 334.573,247.074L 334.573,247.074
+		c 4.938-7.04, 8.814-14.838, 11.752-23.073l 0.148,0.00 l0.00,0.00l-35.10,0.00 l0.00,0.00l0.00,0.00c-11.07,19.111-31.699,31.996-55.373,31.996
+		c-12.799,0.00-24.66-3.844-34.668-10.321c-9.854,42.536-47.795,74.329-93.333,74.329c-53.015,0.00-95.997-42.981-95.997-96.004l0.00,0.00L0.00,224.001 l0.00,0.00
+		c0.00,70.688, 57.312,128.00, 128.00,128.00c 16.025,0.00, 31.316-3.008, 45.443-8.384l0.00,0.00c 13.079,37.466, 48.639,64.384, 90.557,64.384
+		c 6.049,0.00, 11.939-0.625, 17.666-1.688c-1.055-4.609-1.664-9.384-1.664-14.313c0.00-35.349, 28.66-63.993, 64.002-63.993
+		c 4.93,0.00, 9.695,0.609, 14.305,1.657c 1.072-5.735, 1.697-11.619, 1.697-17.659C 360.007,286.923, 350.31,264.17, 334.573,247.074z
+		 M 249.702,374.34c-23.205-5.305-41.402-23.284-47.396-46.201l0.00,0.00c 15.33-10.978, 28.176-25.229, 37.428-41.778
+		c 5.312,0.914, 10.703,1.641, 16.268,1.641c 20.955,0.00, 40.27-6.782, 56.055-18.174l0.00,0.00c 6.867,7.845, 11.883,17.354, 14.281,27.862
+		C 287.505,304.925, 256.931,335.499, 249.702,374.34z M 16.002,192.005l 320.009,0.00 c 8.83,0.00, 15.994-7.165, 15.994-16.002
+		s-7.164-16.002-15.994-16.002L 16.002,160.001 C 7.173,160.001,0.00,167.166,0.00,176.003S 7.173,192.005, 16.002,192.005z M 16.002,127.997l 320.009,0.00 
+		c 8.83,0.00, 15.994-7.157, 15.994-15.994s-7.164-16.002-15.994-16.002L 16.002,96.001 C 7.173,96.001,0.00,103.166,0.00,112.003
+		S 7.173,127.997, 16.002,127.997z M 16.002,64.005l 320.009,0.00 c 8.83,0.00, 15.994-7.165, 15.994-16.003c0.00-8.837-7.164-16.002-15.994-16.002
+		L 16.002,32.00 C 7.173,32.00,0.00,39.165,0.00,48.002C0.00,56.84, 7.173,64.005, 16.002,64.005z" horiz-adv-x="384"  />
+<glyph unicode="&#xe01e;" d="M 9.261,176.002c-2.094,5.165-3.781,10.524-5.188,16.002l 347.80,0.00 
+		c0.00-5.47-0.562-10.782-1.445-16.002L 9.261,176.002 z M 65.957,336.006C 84.31,346.172, 105.399,352.00, 127.871,352.00
+		c 22.447,0.00, 43.489-5.844, 61.82-15.994L 65.957,336.006 z M 0.979,240.002c 0.68,5.454, 1.743,10.774, 3.095,15.994l 323.212,0.00 
+		c 4.406-4.907, 8.312-10.251, 11.641-15.994L 0.979,240.002 z M 9.261,271.998c 2.251,5.548, 4.907,10.877, 7.876,16.002l 221.481,0.00 
+		c 0.32-0.547, 0.672-1.078, 0.984-1.641c 5.312,0.914, 10.705,1.641, 16.268,1.641c 19.611,0.00, 37.834-5.907, 53.023-16.002L 9.261,271.998 z
+		 M 28.045,304.002c 4.641,5.782, 9.798,11.104, 15.361,16.002l 168.997,0.00 c 5.525-4.883, 10.611-10.243, 15.229-16.002L 28.045,304.002 z
+		 M 350.429,208.006L 0.979,208.006 C 0.323,213.257-0.13,218.569-0.13,224.00l 346.426,0.00 C 348.116,218.851, 349.507,213.507, 350.429,208.006z
+		 M 346.296,160.00c-1.977-5.595-4.438-10.939-7.369-16.002L 28.045,143.998 c-4.04,5.031-7.657,10.392-10.908,16.002L 346.296,160.00 z M 43.407,127.996
+		l 283.879,0.00 c-5.439-6.063-11.58-11.471-18.393-15.994L 65.957,112.002 C 57.83,116.494, 50.322,121.917, 43.407,127.996z" horiz-adv-x="384"  />
+<glyph unicode="&#xe01f;" d="M 416.069,271.994l-32.004,0.00 c-8.837,0.00-16.002,7.173-16.002,16.006
+		c0.00,8.837, 7.165,16.002, 16.002,16.002l 32.004,0.00 c 8.829,0.00, 16.002-7.165, 16.002-16.002C 432.071,279.167, 424.898,271.994, 416.069,271.994z
+		 M 353.202,378.511c-6.251-6.251-16.377-6.251-22.628,0.00s-6.251,16.377,0.00,22.628l 22.628,22.628c 6.251,6.25, 16.377,6.25, 22.628,0.00
+		c 6.251-6.251, 6.251-16.377,0.00-22.628L 353.202,378.511z M 334.629,271.994L 9.456,271.994 c 2.25,5.548, 4.906,10.877, 7.876,16.006l 221.402,0.00 
+		c 0.328-0.562, 0.75-1.07, 1.062-1.641c 5.313,0.914, 10.697,1.641, 16.268,1.641c 17.003,0.00, 32.958-4.473, 46.803-12.224l0.00,0.00
+		c 0.767,3.953, 1.204,8.048, 1.204,12.224c0.00,35.349-28.66,64.00-64.009,64.00c-16.205,0.00-30.957-6.07-42.224-15.994L 66.15,336.006 
+		C 84.505,346.171, 105.593,352.00, 128.064,352.00c 12.58,0.00, 24.722-1.859, 36.192-5.242c 17.564,22.628, 44.95,37.238, 75.806,37.238
+		c 53.022,0.00, 96.005-42.974, 96.005-95.996C 336.067,282.526, 335.528,277.198, 334.629,271.994z M 240.062,416.00
+		c-8.829,0.00-15.994,7.165-15.994,16.002L 224.068,463.999 C 224.068,472.835, 231.233,480.00, 240.062,480.00c 8.846,0.00, 16.002-7.165, 16.002-16.002l0.00-31.996 
+		C 256.064,423.166, 248.908,416.00, 240.062,416.00z M 126.924,378.511l-22.62,22.628c-6.251,6.251-6.251,16.377,0.00,22.628
+		c 6.243,6.25, 16.377,6.25, 22.62,0.00l 22.628-22.628c 6.251-6.251, 6.251-16.377,0.00-22.628C 143.309,372.26, 133.175,372.26, 126.924,378.511z
+		 M 43.601,320.003l 168.67,0.00 c 5.571-4.891, 10.696-10.228, 15.346-16.002L 28.239,304.001 C 32.873,309.784, 38.038,315.105, 43.601,320.003z
+		 M 350.687,208.002L 1.174,208.002 c-0.656,5.25-1.109,10.562-1.109,16.001l 346.465,0.00 C 348.357,218.846, 349.764,213.502, 350.687,208.002z
+		 M 43.601,127.992l 283.88,0.00 c-5.438-6.064-11.58-11.471-18.394-15.986L 66.15,112.006 C 58.024,116.49, 50.508,121.912, 43.601,127.992z
+		 M 17.332,159.996l 329.15,0.00 c-1.969-5.596-4.438-10.939-7.36-16.002L 28.239,143.994 C 24.20,149.025, 20.582,154.385, 17.332,159.996z
+		 M 1.174,240.005c 0.68,5.454, 1.742,10.767, 3.094,15.986l 326.15,0.00 c-0.281-0.781-0.50-1.578-0.797-2.359l0.00,0.00
+		c 3.562-4.235, 6.759-8.813, 9.556-13.627L 1.174,240.005 z M 4.268,192.00l 347.802,0.00 c0.00-5.471-0.57-10.783-1.445-16.002L 9.456,175.998 
+		C 7.362,181.17, 5.674,186.529, 4.268,192.00z" horiz-adv-x="448"  />
+<glyph unicode="&#xe020;" d="M 313.727,272.007c 5.97,7.415, 10.424,16.104, 12.611,25.69
+		c-38.833,7.235-69.415,37.81-76.643,76.65c-20.471-4.68-37.013-19.236-44.756-38.333L 66.087,336.014 
+		c 18.346,10.166, 39.442,15.994, 61.914,15.994c 16.018,0.00, 31.316-3.008, 45.442-8.384c 13.08,37.466, 48.632,64.384, 90.559,64.384
+		c 6.04,0.00, 11.932-0.625, 17.658-1.688c-1.047-4.609-1.656-9.384-1.656-14.313c0.00-35.349, 28.652-63.993, 63.992-63.993
+		c 4.931,0.00, 9.705,0.609, 14.314,1.657c 1.062-5.735, 1.688-11.619, 1.688-17.659c0.00-14.314-3.242-27.823-8.876-40.005L 313.727,272.007 z
+		 M 351.997,192.012c0.00-5.469-0.562-10.781-1.438-16.002L 9.392,176.01 c-2.094,5.166-3.781,10.525-5.195,16.002L 351.997,192.012 z M 346.419,160.008
+		c-1.978-5.594-4.438-10.939-7.36-16.002L 28.168,144.006 c-4.04,5.031-7.649,10.393-10.90,16.002L 346.419,160.008 z M 327.416,128.004
+		c-5.438-6.062-11.579-11.471-18.393-15.994L 66.087,112.01 c-8.126,4.492-15.643,9.916-22.55,15.994L 327.416,128.004 z M 1.109,208.014
+		C 0.453,213.266,0.00,218.578,0.00,224.009l 346.317,0.00 c 1.836-5.165, 3.336-10.478, 4.273-15.995L 1.109,208.014 z M 334.565,247.082
+		c 1.57-2.25, 2.836-4.704, 4.22-7.071L 1.109,240.011 c 0.68,5.454, 1.743,10.774, 3.087,15.994l 337.565,0.00 
+		C 339.512,252.911, 337.151,249.887, 334.565,247.082z M 308.789,272.007L 9.392,272.007 c 2.251,5.548, 4.907,10.877, 7.876,16.002l 221.427,0.00 
+		c 0.32-0.562, 0.727-1.07, 1.039-1.641c 5.313,0.914, 10.696,1.641, 16.268,1.641C 275.55,288.009, 293.631,282.047, 308.789,272.007z
+		 M 43.537,320.013l 168.911,0.00 c 5.517-4.859, 10.518-10.267, 15.127-16.002L 28.168,304.011 C 32.809,309.793, 37.974,315.114, 43.537,320.013z" horiz-adv-x="384"  />
+<glyph unicode="&#xe021;" d="M 303.872,192.172L 303.872,192.172l-23.994,0.00 
+		c-8.846,0.00-16.002,7.157-16.002,15.994s 7.156,16.002, 16.002,16.002l 23.994,0.00 c 8.838,0.00, 16.002,7.165, 16.002,15.994
+		c0.00,8.845-7.164,16.002-16.002,16.002c-8.836,0.00-15.994,7.165-15.994,16.002s 7.158,16.002, 15.994,16.002
+		c 1.102,0.00, 2.18-0.109, 3.227-0.328c 24.973-1.688, 44.771-22.269, 44.771-47.678C 351.87,213.659, 330.384,192.172, 303.872,192.172z
+		 M 223.87,224.168l-16.002,0.00 l-55.992,0.00 L 47.872,224.168 c-8.837,0.00-15.994-7.165-15.994-16.002s 7.157-15.994, 15.994-15.994l 16.002,0.00 L 95.87,192.172 l 24.003,0.00 
+		l 32.004,0.00 l 23.995,0.00 l 31.997,0.00 l 16.002,0.00 c 26.512,0.00, 48.006-21.495, 48.006-48.006c0.00-25.409-19.799-45.99-44.779-47.678
+		c-1.039-0.211-2.117-0.32-3.227-0.32c-8.828,0.00-16.002,7.165-16.002,15.994c0.00,8.845, 7.174,16.002, 16.002,16.002
+		c 8.846,0.00, 16.002,7.165, 16.002,16.002s-7.156,16.002-16.002,16.002l-16.002,0.00 l-31.997,0.00 l-23.995,0.00 l-32.004,0.00 L 95.87,160.168 L 63.874,160.168 L 47.872,160.168 
+		c-26.512,0.00-47.998,21.487-47.998,47.998c0.00,25.401, 19.799,45.982, 44.771,47.678c 1.047,0.211, 2.125,0.32, 3.227,0.32l0.00,0.00l 104.005,0.00 
+		l 55.992,0.00 l 16.002,0.00 c 8.846,0.00, 16.002,7.165, 16.002,16.002s-7.156,16.002-16.002,16.002c-8.828,0.00-16.002,7.157-16.002,16.002
+		c0.00,8.829, 7.174,16.002, 16.002,16.002c 1.109,0.00, 2.188-0.117, 3.227-0.328c 24.98-1.688, 44.779-22.268, 44.779-47.678
+		C 271.876,245.663, 250.382,224.168, 223.87,224.168z" horiz-adv-x="352"  />
+<glyph unicode="&#xe022;" d="M 389.994,64.41l0.00-0.406 L 384.00,64.004 l-24.002,0.00 
+		c-8.838,0.00-16.002,7.156-16.002,16.002c0.00,8.83, 7.164,15.994, 16.002,15.994L 384.00,96.00 c 8.838,0.00, 15.994,7.166, 15.994,16.002
+		c0.00,8.838-7.156,15.994-15.994,15.994c-8.836,0.00-16.002,7.174-16.002,16.002c0.00,8.846, 7.166,16.002, 16.002,16.002
+		c 1.102,0.00, 2.181-0.109, 3.22-0.328c 24.979-1.688, 44.778-22.268, 44.778-47.67C 431.998,87.531, 413.668,67.379, 389.994,64.41z
+		 M 303.998,160.00c-8.837,0.00-16.002,7.166-16.002,16.002c0.00,8.838, 7.165,16.002, 16.002,16.002c 1.102,0.00, 2.18-0.109, 3.227-0.328
+		c 24.973-1.688, 44.771-22.268, 44.771-47.678c0.00-24.471-18.33-44.623-41.997-47.584L 309.999,96.00 l-6.001,0.00 l-63.891,0.00 c-32.098,0.00-90.879,0.00-112.107,0.00
+		c-8.838,0.00-16.002-7.164-16.002-15.994c0.00-8.846, 7.164-16.002, 16.002-16.002l 175.998,0.00 l 6.001,0.00 l0.00-0.422 
+		c 23.667-2.953, 41.997-23.111, 41.997-47.584c0.00-25.402-19.799-45.982-44.771-47.67c-1.047-0.211-2.125-0.328-3.227-0.328
+		c-8.837,0.00-16.002,7.164-16.002,16.002c0.00,8.836, 7.165,15.994, 16.002,15.994c 8.838,0.00, 16.002,7.172, 16.002,16.002
+		c0.00,8.846-7.164,16.002-16.002,16.002L 128.00,32.00 l-6.001,0.00 l0.00,0.414 c-23.675,2.961-42.005,23.119-42.005,47.592
+		c0.00,8.439, 2.352,16.26, 6.188,23.121C 36.059,120.473,0.00,167.971,0.00,224.00c0.00,70.688, 57.303,128.00, 128.00,128.00
+		c 48.037,0.00, 89.839-26.495, 111.732-65.641C 245.045,287.274, 250.422,288.00, 256.00,288.00c 47.17,0.00, 86.315-34.051, 94.395-78.909
+		c-11.157,7.963-24.401,13.166-38.794,14.526c-11.017,19.33-31.77,32.379-55.601,32.379c-12.807,0.00-24.675-3.844-34.676-10.321
+		c-9.854,42.536-47.787,74.329-93.324,74.329c-53.022,0.00-96.004-42.981-96.004-96.004c0.00-53.014, 42.981-96.004, 96.004-96.004
+		c 20.783,0.00, 65.227,0.00, 97.605,0.00l 78.393,0.00 c 8.838,0.00, 16.002,7.174, 16.002,16.002C 320.00,152.844, 312.836,160.00, 303.998,160.00z" horiz-adv-x="448"  />
+<glyph unicode="&#xe023;" d="M 415.997,271.998l-32.004,0.00 c-8.837,0.00-15.994,7.165-15.994,16.002
+		s 7.157,16.002, 15.994,16.002l 32.004,0.00 c 8.838,0.00, 16.002-7.165, 16.002-16.002S 424.835,271.998, 415.997,271.998z M 353.138,378.512
+		c-6.25-6.251-16.385-6.251-22.627,0.00c-6.252,6.251-6.252,16.377,0.00,22.628l 22.627,22.628c 6.243,6.25, 16.377,6.25, 22.62,0.00
+		c 6.251-6.251, 6.251-16.377,0.00-22.628L 353.138,378.512z M 329.549,253.637L 329.549,253.637c 7.298-8.696, 13.041-18.722, 16.917-29.637
+		l-0.078,0.00 c 1.703-4.813, 3.078-9.774, 4.00-14.908c-11.15,7.962-24.394,13.166-38.786,14.525c-0.078,0.133-0.18,0.25-0.258,0.383l 0.023,0.00 
+		c-11.071,19.111-31.691,31.996-55.366,31.996c-12.807,0.00-24.676-3.844-34.676-10.321c-9.854,42.536-47.796,74.329-93.332,74.329
+		c-53.016,0.00-95.997-42.981-95.997-96.004l0.00,0.00c0.00-53.015, 42.981-96.004, 95.997-96.004c 20.791,0.00, 65.234,0.00, 97.613,0.00l 78.393,0.00 
+		c 8.837,0.00, 15.994,7.173, 15.994,16.002c0.00,8.845-7.157,16.002-15.994,16.002s-16.002,7.165-16.002,16.002s 7.165,16.002, 16.002,16.002
+		c 1.102,0.00, 2.18-0.109, 3.219-0.328c 24.98-1.688, 44.779-22.269, 44.779-47.678c0.00-24.472-18.33-44.623-41.998-47.584L 309.999,96.00 l-6.00,0.00 l-63.891,0.00 
+		c-32.098,0.00-90.879,0.00-112.115,0.00c-8.83,0.00-15.995-7.165-15.995-15.994c0.00-8.846, 7.165-16.002, 15.995-16.002l 176.006,0.00 l 6.00,0.00 l0.00-0.423 
+		c 23.668-2.953, 41.998-23.111, 41.998-47.584c0.00-25.401-19.799-45.981-44.779-47.669c-1.039-0.212-2.117-0.329-3.219-0.329
+		c-8.837,0.00-16.002,7.165-16.002,16.003c0.00,8.836, 7.165,15.994, 16.002,15.994s 15.994,7.172, 15.994,16.001
+		c0.00,8.846-7.157,16.002-15.994,16.002L 127.993,31.999 l-5.994,0.00 l0.00,0.414 c-23.674,2.962-42.005,23.12-42.005,47.593
+		c0.00,8.438, 2.353,16.26, 6.188,23.12C 36.052,120.472,0.00,167.97,0.00,224.00c0.00,70.688, 57.304,128.00, 127.993,128.00
+		c 12.587,0.00, 24.721-1.859, 36.199-5.242c 17.564,22.628, 44.943,37.238, 75.807,37.238c 53.014,0.00, 95.996-42.974, 95.996-95.996
+		C 335.995,275.874, 333.651,264.325, 329.549,253.637z M 239.999,352.00c-17.705,0.00-33.723-7.188-45.311-18.807
+		c 18.729-11.479, 34.293-27.613, 45.045-46.834c 5.305,0.914, 10.688,1.641, 16.268,1.641c 17.002,0.00, 32.949-4.469, 46.803-12.22l0.00,0.00
+		c 0.766,3.953, 1.195,8.04, 1.195,12.22C 303.999,323.349, 275.339,352.00, 239.999,352.00z M 239.999,416.001
+		c-8.838,0.00-16.002,7.165-16.002,16.002L 223.997,463.999 c0.00,8.837, 7.164,16.002, 16.002,16.002c 8.837,0.00, 16.002-7.165, 16.002-16.002l0.00-31.996 
+		C 256.001,423.166, 248.836,416.001, 239.999,416.001z M 126.859,378.512L 104.231,401.14c-6.25,6.251-6.25,16.377,0.00,22.628
+		c 6.251,6.25, 16.377,6.25, 22.628,0.00l 22.628-22.628c 6.251-6.251, 6.251-16.377,0.00-22.628
+		C 143.237,372.261, 133.11,372.261, 126.859,378.512z M 343.996,80.006c0.00,8.829, 7.165,15.994, 16.002,15.994l 23.995,0.00 
+		c 8.838,0.00, 16.002,7.165, 16.002,16.002s-7.164,15.994-16.002,15.994c-8.837,0.00-15.994,7.173-15.994,16.002
+		c0.00,8.845, 7.157,16.002, 15.994,16.002c 1.109,0.00, 2.188-0.109, 3.227-0.328c 24.98-1.688, 44.779-22.269, 44.779-47.67
+		c0.00-24.472-18.33-44.623-42.005-47.592l0.00-0.406 l-6.001,0.00 l-23.995,0.00 C 351.161,64.004, 343.996,71.16, 343.996,80.006z" horiz-adv-x="448"  />
+<glyph unicode="&#xe024;" d="M 390.002,64.411l0.00-0.406 l-6.00,0.00 l-23.996,0.00 
+		c-8.836,0.00-16.002,7.156-16.002,16.002c0.00,8.829, 7.166,15.994, 16.002,15.994l 23.996,0.00 c 8.837,0.00, 16.002,7.165, 16.002,16.002
+		s-7.165,15.994-16.002,15.994c-8.838,0.00-16.002,7.173-16.002,16.002c0.00,8.845, 7.164,16.002, 16.002,16.002
+		c 1.109,0.00, 2.188-0.109, 3.227-0.328c 24.98-1.688, 44.771-22.269, 44.771-47.67C 432.00,87.531, 413.678,67.38, 390.002,64.411z
+		 M 346.317,224.001l 0.078,0.00 c 1.703-4.813, 3.079-9.774, 4.001-14.908c-11.15,7.962-24.402,13.166-38.795,14.525
+		c-0.07,0.133-0.172,0.25-0.25,0.383l 0.023,0.00 c-11.071,19.111-31.699,31.996-55.373,31.996c-12.799,0.00-24.668-3.844-34.669-10.321
+		c-9.86,42.536-47.795,74.329-93.332,74.329c-53.015,0.00-95.997-42.981-95.997-96.004l0.00,0.00c0.00-53.015, 42.982-96.004, 95.997-96.004
+		c 20.784,0.00, 65.228,0.00, 97.606,0.00L 304.00,127.997 c 8.836,0.00, 16.002,7.173, 16.002,16.002c0.00,8.845-7.166,16.002-16.002,16.002
+		c-8.83,0.00-15.994,7.165-15.994,16.002s 7.164,16.002, 15.994,16.002c 1.109,0.00, 2.188-0.109, 3.227-0.328
+		c 24.979-1.688, 44.779-22.269, 44.779-47.678c0.00-24.472-18.33-44.623-42.006-47.584l0.00-0.414 l-6.00,0.00 l-63.891,0.00 c-32.099,0.00-90.871,0.00-112.108,0.00
+		c-8.837,0.00-15.994-7.165-15.994-15.994c0.00-8.846, 7.157-16.002, 15.994-16.002L 304.00,64.005 l 6.00,0.00 l0.00-0.423 c 23.676-2.953, 42.006-23.111, 42.006-47.584
+		c0.00-25.401-19.80-45.981-44.779-47.669C 306.188-31.883, 305.109-32.00, 304.00-32.00c-8.83,0.00-15.994,7.165-15.994,16.003
+		c0.00,8.836, 7.164,15.994, 15.994,15.994c 8.836,0.00, 16.002,7.172, 16.002,16.001c0.00,8.846-7.166,16.002-16.002,16.002L 128.001,32.00 L 122.00,32.00 l0.00,0.414 
+		c-23.667,2.962-41.997,23.12-41.997,47.593c0.00,8.438, 2.352,16.26, 6.188,23.12C 36.06,120.473,0.00,167.971,0.00,224.001
+		c0.00,70.688, 57.312,128.00, 128.001,128.00c 16.018,0.00, 31.316-3.008, 45.443-8.384c 13.079,37.466, 48.631,64.384, 90.558,64.384
+		c 6.04,0.00, 11.932-0.625, 17.666-1.688c-1.055-4.609-1.664-9.384-1.664-14.313c0.00-35.349, 28.652-63.993, 64.00-63.993
+		c 4.924,0.00, 9.697,0.609, 14.307,1.657c 1.07-5.735, 1.695-11.619, 1.695-17.659c0.00-25.081-9.695-47.834-25.439-64.93
+		C 339.504,240.034, 343.379,232.236, 346.317,224.001z M 249.695,374.34c-23.205-5.305-41.395-23.284-47.396-46.201l0.00,0.00
+		c 15.338-10.978, 28.184-25.229, 37.436-41.778c 5.312,0.914, 10.695,1.641, 16.268,1.641c 20.947,0.00, 40.27-6.782, 56.045-18.174
+		c 6.877,7.845, 11.893,17.354, 14.291,27.862C 287.498,304.925, 256.932,335.499, 249.695,374.34z" horiz-adv-x="448"  />
+<glyph unicode="&#xe001;" d="M 416.005,272.006l-32.004,0.00 c-8.837,0.00-16.002,7.157-16.002,16.002
+		c0.00,8.829, 7.165,15.994, 16.002,15.994l 32.004,0.00 c 8.829,0.00, 16.002-7.165, 16.002-15.994C 432.007,279.163, 424.834,272.006, 416.005,272.006z
+		 M 353.138,378.519c-6.251-6.25-16.377-6.25-22.628,0.00c-6.251,6.244-6.251,16.377,0.00,22.621l 22.628,22.628
+		c 6.251,6.25, 16.377,6.25, 22.628,0.00c 6.251-6.243, 6.251-16.377,0.00-22.628L 353.138,378.519z M 329.557,253.644L 329.557,253.644
+		c 13.994-16.682, 22.448-38.16, 22.448-61.64c0.00-53.015-42.982-96.005-96.005-96.005c-22.205,0.00-102.419,0.00-128.00,0.00
+		c-70.688,0.00-128.00,57.312-128.00,128.00C0.00,294.696, 57.312,352.00, 128.00,352.00c 12.58,0.00, 24.722-1.859, 36.192-5.235
+		c 17.564,22.621, 44.95,37.239, 75.806,37.239c 53.022,0.00, 96.005-42.981, 96.005-95.996C 336.003,275.882, 333.658,264.325, 329.557,253.644
+		z M 32.004,223.999c0.00-53.014, 42.974-95.996, 95.996-95.996c 28.934,0.00, 103.841,0.00, 128.00,0.00c 35.349,0.00, 64.001,28.66, 64.001,64.001
+		c0.00,35.348-28.652,64.00-64.001,64.00c-12.798,0.00-24.667-3.853-34.668-10.329c-9.853,42.536-47.795,74.329-93.332,74.329
+		C 74.978,320.004, 32.004,277.022, 32.004,223.999z M 239.998,352.00c-17.697,0.00-33.723-7.188-45.302-18.80
+		c 18.721-11.485, 34.285-27.62, 45.036-46.834c 5.313,0.907, 10.697,1.642, 16.268,1.642c 17.003,0.00, 32.958-4.47, 46.803-12.22l0.00,0.00
+		c 0.767,3.953, 1.204,8.031, 1.204,12.22C 304.007,323.349, 275.347,352.00, 239.998,352.00z M 239.998,416.008
+		c-8.829,0.00-15.994,7.157-15.994,15.994L 224.004,463.999 C 224.004,472.843, 231.169,480.00, 239.998,480.00C 248.844,480.00, 256.00,472.843, 256.00,463.999l0.00-31.996 
+		C 256.00,423.165, 248.844,416.008, 239.998,416.008z M 126.859,378.519l-22.62,22.621c-6.251,6.25-6.251,16.385,0.00,22.628
+		c 6.243,6.25, 16.377,6.25, 22.62,0.00l 22.628-22.628c 6.251-6.243, 6.251-16.377,0.00-22.621C 143.244,372.269, 133.11,372.269, 126.859,378.519
+		z" horiz-adv-x="448"  />
+<glyph unicode="&#xe026;" d="M 415.997,271.997l-31.996,0.00 c-8.837,0.00-16.002,7.165-16.002,16.002
+		s 7.165,16.002, 16.002,16.002l 31.996,0.00 c 8.837,0.00, 16.002-7.165, 16.002-16.002S 424.834,271.997, 415.997,271.997z M 353.138,378.511
+		c-6.251-6.251-16.377-6.251-22.628,0.00s-6.251,16.377,0.00,22.628l 22.628,22.628c 6.251,6.25, 16.377,6.25, 22.628,0.00
+		c 6.242-6.251, 6.242-16.377,0.00-22.628L 353.138,378.511z M 329.557,253.636L 329.557,253.636c 10.736-12.799, 18.166-28.441, 21.064-45.631
+		l-0.062,0.00 c 0.875-5.22, 1.438-10.54, 1.438-16.002c0.00-53.022-42.981-96.004-95.997-96.004c-2.242,0.00-5.195,0.00-8.50,0.00l 28.675,35.293
+		C 301.623,139.754, 320.00,163.718, 320.00,192.003c0.00,5.532-0.781,10.876-2.102,16.002l0.00,0.00c-7.109,27.581-32.09,47.99-61.898,47.99
+		c-12.806,0.00-24.666-3.844-34.676-10.321c-9.853,42.536-47.787,74.329-93.324,74.329c-53.021,0.00-96.004-42.981-96.004-96.004
+		c0.00-5.462, 0.562-10.782, 1.445-15.994l-0.109,0.00 c 6.11-36.388, 32.707-65.789, 67.508-76.049l-10.798-30.23
+		c-47.232,14.643-82.651,55.93-88.94,106.279l0.00,0.00C 0.445,213.256,0.00,218.568,0.00,223.999c0.00,70.688, 57.304,128.00, 128.00,128.00
+		c 12.58,0.00, 24.715-1.859, 36.191-5.242c 17.565,22.628, 44.951,37.238, 75.807,37.238c 53.022,0.00, 96.004-42.974, 96.004-95.996
+		C 336.002,275.873, 333.658,264.324, 329.557,253.636z M 239.998,351.999c-17.697,0.00-33.723-7.188-45.303-18.807
+		c 18.722-11.479, 34.286-27.613, 45.037-46.834c 5.313,0.914, 10.697,1.641, 16.268,1.641c 17.002,0.00, 32.949-4.469, 46.803-12.22l0.00,0.00
+		c 0.766,3.953, 1.195,8.04, 1.195,12.22C 303.998,323.348, 275.347,351.999, 239.998,351.999z M 239.998,416.00
+		c-8.836,0.00-16.002,7.165-16.002,16.002L 223.996,463.998 C 223.996,472.835, 231.162,480.00, 239.998,480.00C 248.836,480.00, 256.00,472.835, 256.00,463.998l0.00-31.996 
+		C 256.00,423.165, 248.836,416.00, 239.998,416.00z M 126.859,378.511l-22.62,22.628c-6.251,6.251-6.251,16.377,0.00,22.628
+		c 6.243,6.25, 16.377,6.25, 22.62,0.00l 22.628-22.628c 6.251-6.251, 6.251-16.377,0.00-22.628C 143.244,372.26, 133.11,372.26, 126.859,378.511z
+		 M 159.996,208.005l 80.002,0.00 L 192.00,127.995l 56.00,0.00 l-103.998-128.00l 28.801,96.004l-52.804,0.00 L 159.996,208.005z" horiz-adv-x="448"  />
+<glyph unicode="&#xe027;" d="M 334.573,247.07L 334.573,247.07
+		c 8.031-11.447, 13.596-24.723, 16.025-39.068l-0.031,0.00 c 0.875-5.219, 1.438-10.54, 1.438-16.002c0.00-53.021-42.982-96.004-96.004-96.004
+		c-2.234,0.00-5.189,0.00-8.494,0.00l 28.676,35.302c 25.449,8.454, 43.826,32.41, 43.826,60.702c0.00,5.532-0.781,10.877-2.102,16.002l0.00,0.00
+		c-7.119,27.582-32.09,47.999-61.906,47.999c-12.799,0.00-24.66-3.853-34.668-10.33c-9.854,42.537-47.795,74.33-93.333,74.33
+		c-53.015,0.00-95.997-42.982-95.997-96.005c0.00-5.461, 0.562-10.782, 1.438-15.994l-0.109,0.00 c 6.11-36.387, 32.707-65.789, 67.517-76.048
+		l-10.799-30.23C 42.818,116.374, 7.392,157.66, 1.109,208.002l0.00,0.00C 0.453,213.253,0.00,218.566,0.00,223.996
+		c0.00,70.697, 57.312,128.001, 128.00,128.001c 16.025,0.00, 31.316-3.001, 45.443-8.384l0.00,0.00c 13.079,37.466, 48.639,64.383, 90.557,64.383
+		c 6.049,0.00, 11.939-0.625, 17.666-1.688c-1.055-4.61-1.664-9.384-1.664-14.307c0.00-35.348, 28.66-64.00, 64.002-64.00
+		c 4.93,0.00, 9.695,0.609, 14.305,1.656c 1.072-5.728, 1.697-11.619, 1.697-17.658C 360.007,286.926, 350.31,264.166, 334.573,247.07z
+		 M 249.702,374.336c-23.205-5.306-41.402-23.276-47.396-46.201l0.00,0.00c 15.33-10.979, 28.176-25.229, 37.428-41.779
+		c 5.312,0.915, 10.703,1.642, 16.268,1.642c 20.955,0.00, 40.27-6.782, 56.055-18.167l0.00,0.00c 6.867,7.838, 11.883,17.347, 14.281,27.863
+		C 287.505,304.929, 256.931,335.495, 249.702,374.336z M 160.004,208.002l 80.002,0.00 L 192.009,128.00l 55.998,0.00 L 144.002,0.00l 28.80,95.996l-52.795,0.00 
+		L 160.004,208.002z" horiz-adv-x="384"  />
+<glyph unicode="&#xe028;" d="M 368.003,196.107l-31.996,0.00 c-8.846,0.00-16.002,7.158-16.002,15.994
+		c0.00,8.838, 7.156,16.002, 16.002,16.002l 31.996,0.00 c 8.836,0.00, 15.994-7.164, 15.994-16.002C 383.997,203.266, 376.839,196.107, 368.003,196.107z
+		 M 305.144,302.613c-6.252-6.251-16.385-6.251-22.629,0.00c-6.25,6.251-6.25,16.377,0.00,22.628l 22.629,22.628
+		c 6.242,6.251, 16.377,6.251, 22.627,0.00c 6.244-6.251, 6.244-16.377,0.00-22.628L 305.144,302.613z M 192.005,116.105
+		c-53.023,0.00-96.005,42.982-96.005,95.996c0.00,53.022, 42.982,96.005, 96.005,96.005c 53.021,0.00, 95.996-42.982, 95.996-96.005
+		C 288.001,159.088, 245.026,116.105, 192.005,116.105z M 192.005,276.103c-35.349,0.00-64.009-28.652-64.009-64.001
+		c0.00-35.34, 28.66-64.00, 64.009-64.00c 35.348,0.00, 63.992,28.66, 63.992,64.00C 255.997,247.45, 227.353,276.103, 192.005,276.103z M 192.005,340.103
+		c-8.838,0.00-16.002,7.165-16.002,16.002L 176.003,388.101 c0.00,8.837, 7.165,16.002, 16.002,16.002c 8.828,0.00, 16.002-7.165, 16.002-16.002l0.00-31.996 
+		C 208.007,347.268, 200.833,340.103, 192.005,340.103z M 78.865,302.613l-22.628,22.628c-6.25,6.251-6.25,16.377,0.00,22.628
+		c 6.251,6.251, 16.377,6.251, 22.628,0.00l 22.628-22.628c 6.251-6.251, 6.251-16.377,0.00-22.628S 85.116,296.362, 78.865,302.613z
+		 M 64.003,212.102c0.00-8.836-7.165-15.994-16.002-15.994L 15.998,196.108 c-8.829,0.00-15.994,7.158-15.994,15.994
+		c0.00,8.838, 7.165,16.002, 15.994,16.002l 32.004,0.00 C 56.838,228.104, 64.003,220.939, 64.003,212.102z M 78.865,121.599
+		c 6.251,6.243, 16.377,6.243, 22.628,0.00c 6.251-6.251, 6.251-16.385,0.00-22.628l-22.628-22.628c-6.251-6.251-16.377-6.251-22.628,0.00
+		c-6.25,6.243-6.25,16.377,0.00,22.628L 78.865,121.599z M 192.005,84.102c 8.828,0.00, 16.002-7.157, 16.002-15.994l0.00-32.004 
+		c0.00-8.837-7.174-16.002-16.002-16.002c-8.838,0.00-16.002,7.165-16.002,16.002l0.00,32.004 
+		C 176.002,76.944, 183.167,84.102, 192.005,84.102z M 305.144,121.599l 22.627-22.628c 6.244-6.251, 6.244-16.385,0.00-22.628
+		c-6.25-6.251-16.385-6.251-22.627,0.00l-22.629,22.628c-6.25,6.243-6.25,16.377,0.00,22.628
+		C 288.759,127.842, 298.892,127.842, 305.144,121.599z" horiz-adv-x="384"  />
+<glyph unicode="&#xe029;" d="M 368.007,175.997l-32.004,0.00 c-8.838,0.00-15.994,7.165-15.994,16.002
+		c0.00,8.838, 7.156,16.002, 15.994,16.002l 32.004,0.00 c 8.836,0.00, 15.994-7.164, 15.994-16.002C 384.001,183.162, 376.843,175.997, 368.007,175.997z
+		 M 305.147,282.511c-6.252-6.251-16.385-6.251-22.629,0.00c-6.25,6.251-6.25,16.377,0.00,22.628l 22.629,22.628
+		c 6.242,6.242, 16.377,6.242, 22.619,0.00c 6.25-6.251, 6.25-16.377,0.00-22.628L 305.147,282.511z M 282.425,160.003l-35.051,0.00 
+		c 5.453,9.416, 8.625,20.323, 8.625,31.996c0.00,35.349-28.65,64.00-63.992,64.00c-35.347,0.00-64.007-28.651-64.007-64.00
+		c0.00-11.673, 3.18-22.58, 8.642-31.996l-35.059,0.00 c-3.548,10.018-5.579,20.761-5.579,31.996c0.00,53.022, 42.981,95.997, 96.003,95.997
+		c 53.016,0.00, 95.996-42.975, 95.996-95.997C 288.003,180.764, 285.972,170.021, 282.425,160.003z M 192.007,320.00
+		c-8.844,0.00-16.001,7.165-16.001,16.002l0.00,31.996 c0.00,8.837, 7.158,16.002, 16.001,16.002c 8.83,0.00, 16.002-7.165, 16.002-16.002l0.00-31.996 
+		C 208.009,327.165, 200.837,320.00, 192.007,320.00z M 78.869,282.511l-22.628,22.628c-6.251,6.251-6.251,16.377,0.00,22.628
+		c 6.251,6.242, 16.377,6.242, 22.628,0.00l 22.628-22.628c 6.25-6.251, 6.25-16.377,0.00-22.628C 95.246,276.26, 85.12,276.26, 78.869,282.511z
+		 M 64.007,191.999c0.00-8.837-7.165-16.002-16.002-16.002L 16.001,175.997 C 7.172,175.997,0.00,183.162,0.00,191.999
+		c0.00,8.838, 7.173,16.002, 16.002,16.002l 32.004,0.00 C 56.842,208.001, 64.007,200.837, 64.007,191.999z M 192.007,224.003
+		c 4.416,0.00, 8.002-3.586, 8.002-8.00l0.00-30.629 l 20.283,20.283c 3.125,3.125, 8.189,3.125, 11.314,0.00s 3.125-8.188,0.00-11.313l-33.941-33.942
+		c-3.125-3.125-8.189-3.125-11.314,0.00l-33.942,33.942c-3.125,3.125-3.125,8.188,0.00,11.313c 3.126,3.125, 8.188,3.125, 11.314,0.00
+		l 20.284-20.283l0.00,30.629 C 184.007,220.417, 187.585,224.003, 192.007,224.003z M 112.005,127.999l 159.996,0.00 
+		c 8.838,0.00, 16.002-7.165, 16.002-16.002s-7.164-15.994-16.002-15.994L 112.005,96.003 c-8.837,0.00-16.002,7.157-16.002,15.994
+		S 103.168,127.999, 112.005,127.999z" horiz-adv-x="384"  />
+<glyph unicode="&#xe02a;" d="M 272.003,127.999L 111.999,127.999 c-8.83,0.00-15.995-7.165-15.995-16.002
+				s 7.165-15.994, 15.995-15.994l 160.004,0.00 c 8.836,0.00, 16.002,7.157, 16.002,15.994S 280.839,127.999, 272.003,127.999z M 101.497,305.139
+				l-22.628,22.628c-6.251,6.242-16.377,6.242-22.628,0.00c-6.25-6.251-6.25-16.377,0.00-22.628l 22.628-22.628
+				c 6.251-6.251, 16.377-6.251, 22.628,0.00C 107.74,288.762, 107.74,298.888, 101.497,305.139z M 48.005,208.001L 16.001,208.001 
+				C 7.165,208.001,0.00,200.837,0.00,191.999c0.00-8.837, 7.165-16.002, 16.002-16.002l 32.004,0.00 c 8.829,0.00, 16.002,7.165, 16.002,16.002
+				C 64.007,200.837, 56.834,208.001, 48.005,208.001z M 367.999,208.001l-31.996,0.00 c-8.838,0.00-15.994-7.164-15.994-16.002
+				c0.00-8.837, 7.156-16.002, 15.994-16.002l 31.996,0.00 c 8.844,0.00, 16.002,7.165, 16.002,16.002
+				C 384.001,200.837, 376.843,208.001, 367.999,208.001z M 192.009,320.00c 8.828,0.00, 15.994,7.165, 15.994,16.002l0.00,31.996 
+				c0.00,8.837-7.166,16.002-15.994,16.002c-8.846,0.00-16.002-7.165-16.002-16.002l0.00-31.996 C 176.006,327.165, 183.163,320.00, 192.009,320.00z
+				 M 327.767,327.767c-6.242,6.242-16.385,6.242-22.627,0.00l-22.629-22.628c-6.242-6.251-6.242-16.377,0.00-22.628
+				c 6.252-6.251, 16.385-6.251, 22.629,0.00l 22.627,22.628C 334.011,311.39, 334.011,321.516, 327.767,327.767z M 192.009,287.996
+				c-53.023,0.00-96.005-42.975-96.005-95.997c0.00-11.235, 2.032-21.979, 5.579-31.996l 35.052,0.00 c-5.462,9.416-8.634,20.323-8.634,31.996
+				c0.00,35.349, 28.659,64.00, 64.008,64.00c 35.34,0.00, 63.992-28.651, 63.992-64.00c0.00-11.673-3.172-22.58-8.627-31.996l 35.051,0.00 
+				c 3.549,10.018, 5.58,20.761, 5.58,31.996C 288.005,245.021, 245.022,287.996, 192.009,287.996z M 192.009,158.003
+				c 4.414,0.00, 7.992,3.578, 7.992,8.001l0.00,30.621 l 20.291-20.284c 3.119-3.125, 8.189-3.125, 11.314,0.00c 3.117,3.126, 3.117,8.189,0.00,11.314
+				l-33.941,33.941c-3.125,3.125-8.197,3.125-11.314,0.00l-33.941-33.941c-3.126-3.125-3.126-8.188,0.00-11.314
+				c 3.125-3.125, 8.188-3.125, 11.313,0.00l 20.284,20.284l0.00-30.621 C 184.007,161.581, 187.585,158.003, 192.009,158.003z" horiz-adv-x="384"  />
+<glyph unicode="&#xe02b;" d="M 367.999,175.998l-31.996,0.00 c-8.838,0.00-16.004,7.164-16.004,16.002
+		s 7.166,16.002, 16.004,16.002l 31.996,0.00 c 8.844,0.00, 16.002-7.164, 16.002-16.002S 376.843,175.998, 367.999,175.998z M 305.14,282.511
+		c-6.252-6.251-16.387-6.251-22.629,0.00s-6.242,16.377,0.00,22.628l 22.629,22.628c 6.242,6.242, 16.377,6.242, 22.627,0.00
+		c 6.242-6.251, 6.242-16.377,0.00-22.628L 305.14,282.511z M 282.425,160.004l-35.051,0.00 c 5.453,9.416, 8.625,20.322, 8.625,31.996
+		c0.00,35.348-28.65,64.00-64.00,64.00c-35.339,0.00-64.00-28.651-64.00-64.00c0.00-11.674, 3.172-22.58, 8.634-31.996l-35.052,0.00 
+		c-3.555,10.018-5.586,20.76-5.586,31.996c0.00,53.022, 42.99,95.997, 96.003,95.997c 53.023,0.00, 96.004-42.975, 96.004-95.997
+		C 288.003,180.764, 285.972,170.021, 282.425,160.004z M 191.999,320.00c-8.836,0.00-15.994,7.165-15.994,16.002l0.00,31.996 
+		c0.00,8.837, 7.158,16.002, 15.994,16.002c 8.838,0.00, 16.002-7.165, 16.002-16.002l0.00-31.996 C 208.001,327.166, 200.837,320.00, 191.999,320.00z
+		 M 78.869,282.511l-22.628,22.628c-6.251,6.251-6.251,16.377,0.00,22.628c 6.243,6.242, 16.377,6.242, 22.628,0.00l 22.627-22.628
+		c 6.244-6.251, 6.244-16.377,0.00-22.628C 95.246,276.26, 85.111,276.26, 78.869,282.511z M 64.00,192.00c0.00-8.838-7.165-16.002-15.994-16.002
+		L 16.001,175.998 C 7.164,175.998,0.00,183.162,0.00,192.00s 7.164,16.002, 16.002,16.002l 32.004,0.00 C 56.834,208.002, 64.00,200.838, 64.00,192.00z M 111.998,128.00l 160.003,0.00 
+		c 8.838,0.00, 16.002-7.166, 16.002-16.002c0.00-8.838-7.164-15.994-16.002-15.994L 111.998,96.004 c-8.829,0.00-16.002,7.156-16.002,15.994
+		C 95.996,120.834, 103.168,128.00, 111.998,128.00z" horiz-adv-x="384"  />
+<glyph unicode="&#xe02c;" d="M 253.594,218.511c-6.25-6.243-16.393-6.243-22.627,0.00
+		c-6.252,6.251-6.252,16.385,0.00,22.628l 22.627,22.628c 6.235,6.25, 16.377,6.25, 22.629,0.00c 6.234-6.243, 6.234-16.377,0.00-22.628
+		L 253.594,218.511z M 195.821,160.003c-11.079,19.112-31.691,32.004-55.366,32.004s-44.303-12.892-55.366-32.004L 50.037,160.003 
+		c 13.189,37.263, 48.631,64.00, 90.418,64.00c 41.771,0.00, 77.229-26.737, 90.418-64.00L 195.821,160.003 z M 140.455,256.00c-8.846,0.00-16.002,7.165-16.002,16.002
+		l0.00,32.004 c0.00,8.837, 7.156,15.994, 16.002,15.994c 8.829,0.00, 16.002-7.157, 16.002-15.994l0.00-32.004 C 156.457,263.165, 149.284,256.00, 140.455,256.00z
+		 M 27.316,218.511L 4.688,241.139c-6.25,6.251-6.25,16.385,0.00,22.628c 6.252,6.25, 16.377,6.25, 22.629,0.00l 22.627-22.628
+		c 6.251-6.243, 6.251-16.377,0.00-22.628C 43.693,212.268, 33.566,212.268, 27.316,218.511z M 60.445,128.007l 160.004,0.00 
+		c 8.846,0.00, 16.002-7.164, 16.002-16.002c0.00-8.837-7.156-16.002-16.002-16.002L 60.445,96.003 c-8.83,0.00-16.002,7.165-16.002,16.002
+		C 44.443,120.843, 51.615,128.007, 60.445,128.007z" horiz-adv-x="288"  />
+<glyph unicode="&#xe02d;" d="M 96.004,116.105C 42.981,116.105,0.00,159.088,0.00,212.102
+		c0.00,53.022, 42.981,96.005, 96.005,96.005c 6.039,0.00, 11.931-0.625, 17.658-1.695c-1.055-4.61-1.664-9.384-1.664-14.307
+		c0.00-35.349, 28.66-64.001, 64.008-64.001c 4.922,0.00, 9.696,0.61, 14.307,1.665c 1.062-5.735, 1.688-11.619, 1.688-17.667
+		C 192.001,159.088, 149.018,116.105, 96.004,116.105z M 81.698,274.445c-28.449-6.508-49.694-31.918-49.694-62.344
+		c0.00-35.34, 28.652-64.00, 64.001-64.00c 30.418,0.00, 55.826,21.245, 62.336,49.694C 119.499,205.031, 88.924,235.605, 81.698,274.445z" horiz-adv-x="192"  />
+<glyph unicode="&#xe02e;" d="M 96.003,116.105c-53.022,0.00-96.004,42.982-96.004,95.996
+		c0.00,53.022, 42.981,96.005, 96.004,96.005c 53.015,0.00, 95.997-42.982, 95.997-96.005C 192.00,159.088, 149.018,116.105, 96.003,116.105z" horiz-adv-x="192"  />
+<glyph unicode="&#xe02f;" d="M 95.996,116.105c 53.023,0.00, 96.005,42.982, 96.005,95.996
+		c0.00,53.022-42.981,96.005-96.005,96.005C 42.982,308.106,0.00,265.124,0.00,212.102C0.00,159.088, 42.982,116.105, 95.996,116.105z
+		 M 80.002,274.008c 5.119,1.321, 10.463,2.095, 15.994,2.095c 35.349,0.00, 64.001-28.652, 64.001-64.001c0.00-35.34-28.652-64.00-64.001-64.00
+		c-5.531,0.00-10.875,0.781-15.994,2.103c 27.59,7.11, 47.999,32.09, 47.999,61.897C 128.001,241.918, 107.592,266.891, 80.002,274.008z" horiz-adv-x="192"  />
+<glyph unicode="&#xe030;" d="M 95.997,116.105c-53.021,0.00-95.996,42.982-95.996,95.996
+		c0.00,53.022, 42.975,96.005, 95.996,96.005c 53.022,0.00, 96.004-42.982, 96.004-96.005C 192.001,159.088, 149.02,116.105, 95.997,116.105z
+		 M 95.997,148.102c 35.349,0.00, 64.00,28.66, 64.00,64.00c0.00,35.349-28.651,64.001-64.00,64.001L 95.997,148.102 z" horiz-adv-x="192"  />
+<glyph unicode="&#xe031;" d="M 96.00,116.042c-53.022,0.00-95.996,42.981-95.996,96.004
+		c0.00,53.015, 42.974,95.996, 95.996,95.996s 95.997-42.981, 95.997-95.996C 191.997,159.023, 149.022,116.042, 96.00,116.042z M 96.00,276.038
+		c-17.674,0.00-32.004-28.651-32.004-63.992c0.00-35.349, 14.33-64.00, 32.004-64.00c 35.349,0.00, 64.00,28.651, 64.00,64.00
+		C 160.00,247.387, 131.349,276.038, 96.00,276.038z" horiz-adv-x="192"  />
+<glyph unicode="&#xe032;" d="M 96.005,116.042C 42.982,116.042,0.00,159.023,0.00,212.046
+		c0.00,53.015, 42.982,95.996, 96.005,95.996c 53.021,0.00, 95.996-42.981, 95.996-95.996C 192.001,159.023, 149.026,116.042, 96.005,116.042z
+		 M 96.005,276.038c-35.349,0.00-64.009-28.651-64.009-63.992c0.00-35.349, 28.66-64.00, 64.009-64.00c 35.348,0.00, 63.992,28.651, 63.992,64.00
+		C 159.997,247.387, 131.353,276.038, 96.005,276.038z" horiz-adv-x="192"  />
+<glyph unicode="&#xe033;" d="M 96.004,116.05c 53.015,0.00, 95.996,42.981, 95.996,96.004
+		c0.00,53.015-42.981,95.996-95.996,95.996C 42.981,308.05,0.00,265.068,0.00,212.054C0.00,159.031, 42.981,116.05, 96.004,116.05z M 96.004,276.046
+		c 17.674,0.00, 31.996-28.651, 31.996-63.992c0.00-35.349-14.322-64.00-31.996-64.00c-35.348,0.00-64.008,28.651-64.008,64.00
+		C 31.996,247.395, 60.656,276.046, 96.004,276.046z" horiz-adv-x="192"  />
+<glyph unicode="&#xe034;" d="M 96.003,116.05c 53.015,0.00, 95.998,42.981, 95.998,96.004
+		c0.00,53.015-42.983,95.996-95.998,95.996C 42.981,308.05,0.00,265.068,0.00,212.054C0.00,159.031, 42.981,116.05, 96.003,116.05z M 96.003,148.054
+		c-35.348,0.00-64.008,28.651-64.008,64.00c0.00,35.341, 28.66,63.992, 64.008,63.992L 96.003,148.054 z" horiz-adv-x="192"  />
+<glyph unicode="&#xe035;" d="M 96.00,116.05c-53.014,0.00-96.004,42.981-96.004,96.004
+		c0.00,53.015, 42.99,95.996, 96.004,95.996c 53.022,0.00, 96.004-42.981, 96.004-95.996C 192.005,159.031, 149.023,116.05, 96.00,116.05z
+		 M 112.002,273.952c-5.125,1.32-10.462,2.094-16.002,2.094c-35.34,0.00-64.00-28.651-64.00-63.992c0.00-35.349, 28.66-64.00, 64.00-64.00
+		c 5.54,0.00, 10.877,0.773, 16.002,2.094c-27.589,7.118-47.998,32.09-47.998,61.906C 64.004,241.862, 84.414,266.834, 112.002,273.952z" horiz-adv-x="192"  />
+<glyph unicode="&#xe036;" d="M 162.568,238.189l-15.666-9.04
+		c 1.508-5.454, 2.383-11.165, 2.383-17.096c0.00-5.938-0.875-11.649-2.383-17.104l 15.666-9.04c 7.649-4.423, 10.274-14.205, 5.852-21.862
+		c-4.414-7.649-14.197-10.274-21.854-5.853l-15.635,9.024c-8.025-8.157-18.221-14.126-29.645-17.072l0.00-18.096 
+		c0.00-8.837-7.165-16.002-16.002-16.002c-8.838,0.00-16.002,7.165-16.002,16.002l0.00,18.096 c-11.424,2.946-21.628,8.915-29.645,17.072
+		l-15.635-9.024c-7.657-4.422-17.439-1.797-21.854,5.853c-4.423,7.657-1.797,17.439, 5.853,21.862l 15.666,9.04
+		c-1.516,5.454-2.383,11.165-2.383,17.104c0.00,5.931, 0.867,11.642, 2.383,17.096l-15.666,9.04c-7.649,4.423-10.275,14.205-5.853,21.862
+		c 4.415,7.649, 14.197,10.274, 21.854,5.853l 15.635-9.024c 8.017,8.157, 18.221,14.126, 29.645,17.072l0.00,18.096 
+		c0.00,8.837, 7.164,16.002, 16.002,16.002c 8.837,0.00, 16.002-7.165, 16.002-16.002l0.00-18.096 c 11.424-2.946, 21.619-8.915, 29.645-17.072
+		l 15.635,9.024c 7.656,4.422, 17.439,1.797, 21.854-5.853C 172.842,252.395, 170.217,242.612, 162.568,238.189z M 85.285,244.05
+		c-17.674,0.00-32.004-14.33-32.004-31.996c0.00-17.674, 14.33-32.004, 32.004-32.004s 31.996,14.33, 31.996,32.004
+		C 117.281,229.72, 102.958,244.05, 85.285,244.05z" horiz-adv-x="192"  />
+<glyph unicode="&#xe037;" d="M 320.001,320.003L 16.002,320.003 C 7.165,320.003,0.00,327.168,0.00,335.998
+		C0.00,344.834, 7.165,352.00, 16.002,352.00l 303.999,0.00 c 8.837,0.00, 16.002-7.166, 16.002-16.002C 336.003,327.168, 328.838,320.003, 320.001,320.003z
+		 M 32.004,287.999l 239.999,0.00 c 8.837,0.00, 16.002-7.165, 16.002-16.002c0.00-8.829-7.165-15.994-16.002-15.994L 32.004,256.003 
+		c-8.837,0.00-16.002,7.165-16.002,15.994C 16.002,280.834, 23.167,287.999, 32.004,287.999z M 128.00,223.999l 176.007,0.00 
+		c 8.837,0.00, 15.994-7.165, 15.994-15.994c0.00-8.837-7.157-16.002-15.994-16.002L 128.00,192.003 c-8.837,0.00-15.994,7.165-15.994,16.002
+		C 112.006,216.834, 119.163,223.999, 128.00,223.999z M 144.002,95.999L 192.00,95.999 c 8.838,0.00, 16.002-7.165, 16.002-15.995
+		c0.00-8.836-7.164-16.002-16.002-16.002l-47.998,0.00 c-8.836,0.00-16.002,7.166-16.002,16.002C 128.00,88.834, 135.166,95.999, 144.002,95.999z
+		 M 152.004,144.005c0.00,8.829, 7.164,15.994, 16.002,15.994l 111.998,0.00 c 8.837,0.00, 16.002-7.165, 16.002-15.994
+		c0.00-8.837-7.165-16.002-16.002-16.002L 168.006,128.003 C 159.168,128.003, 152.004,135.168, 152.004,144.005z" horiz-adv-x="352"  />
+<glyph unicode="&#xe038;" d="M 64.00,64.002c-35.349,0.00-64.00,28.652-64.00,64.001
+		c0.00,16.205, 6.071,30.957, 15.994,42.231L 15.994,304.001 C 15.994,330.513, 37.489,352.00, 64.00,352.00c 26.503,0.00, 47.999-21.487, 47.999-47.999l0.00-133.767 
+		C 121.92,158.96, 128.00,144.208, 128.00,128.003C 128.00,92.654, 99.34,64.002, 64.00,64.002z M 79.994,155.568l0.00,34.341 l0.00,82.096 l0.00,31.996 
+		c0.00,8.837-7.165,16.002-15.994,16.002c-8.837,0.00-16.002-7.165-16.002-16.002l0.00-31.996 l0.00-82.096 l0.00-34.341 
+		c-9.524-5.547-16.002-15.752-16.002-27.565c0.00-17.675, 14.33-31.996, 32.004-31.996c 17.667,0.00, 31.996,14.321, 31.996,31.996
+		C 95.996,139.816, 89.518,150.021, 79.994,155.568z" horiz-adv-x="128"  />
+<glyph unicode="&#xe039;" d="M 64.00,64.002c-35.34,0.00-64.00,28.652-64.00,64.001
+		c0.00,16.205, 6.072,30.957, 16.002,42.231L 16.002,304.001 C 16.001,330.513, 37.498,352.00, 64.00,352.00c 26.512,0.00, 48.007-21.487, 48.007-47.999l0.00-133.767 
+		C 121.929,158.96, 128.00,144.208, 128.00,128.003C 128.00,92.654, 99.348,64.002, 64.00,64.002z M 80.001,155.568l0.00,34.341 l0.00,82.096 l0.00,31.996 
+		c0.00,8.837-7.164,16.002-16.002,16.002c-8.836,0.00-15.994-7.165-15.994-16.002l0.00-31.996 l0.00-82.096 l0.00-34.341 
+		c-9.523-5.547-16.002-15.752-16.002-27.565c0.00-17.675, 14.33-31.996, 31.996-31.996c 17.675,0.00, 32.005,14.321, 32.005,31.996
+		C 96.004,139.816, 89.527,150.021, 80.001,155.568z M 64.00,104.00c-13.251,0.00-23.994,10.751-23.994,24.003
+		c0.00,13.251, 10.743,24.003, 23.994,24.003c 13.26,0.00, 24.004-10.752, 24.004-24.003C 88.003,114.751, 77.259,104.00, 64.00,104.00z" horiz-adv-x="128"  />
+<glyph unicode="&#xe03a;" d="M 63.992,64.002C 28.645,64.002,0.00,92.654,0.00,128.003
+		c0.00,16.205, 6.063,30.957, 16.002,42.231L 16.002,304.001 c0.00,26.512, 21.487,47.999, 47.99,47.999c 26.52,0.00, 48.006-21.487, 48.006-47.999l0.00-133.767 
+		C 121.922,158.96, 128.00,144.208, 128.00,128.003C 128.00,92.654, 99.341,64.002, 63.992,64.002z M 79.994,155.568l0.00,34.341 l0.00,82.096 l0.00,31.996 
+		c0.00,8.837-7.156,16.002-16.002,16.002c-8.829,0.00-16.002-7.165-16.002-16.002l0.00-31.996 l0.00-82.096 l0.00-34.341 
+		c-9.517-5.547-15.986-15.752-15.986-27.565c0.00-17.675, 14.314-31.996, 31.988-31.996s 32.004,14.321, 32.004,31.996
+		C 95.996,139.816, 89.527,150.021, 79.994,155.568z M 63.992,104.00c-13.252,0.00-23.987,10.751-23.987,24.003
+		c0.00,10.438, 6.688,19.221, 15.986,22.534l0.00,41.466 l 16.003,0.00 l0.00-41.466 c 9.297-3.313, 16.002-12.096, 16.002-22.534
+		C 87.996,114.751, 77.244,104.00, 63.992,104.00z" horiz-adv-x="128"  />
+<glyph unicode="&#xe03b;" d="M 64.00,63.995c-35.349,0.00-64.00,28.66-64.00,64.00
+		c0.00,16.205, 6.071,30.965, 15.994,42.239l0.00,133.76 C 15.994,330.505, 37.489,352.00, 64.00,352.00c 26.503,0.00, 47.998-21.495, 47.998-48.006l0.00-133.76 
+		C 121.929,158.96, 128.00,144.20, 128.00,127.995C 128.00,92.655, 99.349,63.995, 64.00,63.995z M 80.002,155.562l0.00,34.34 l0.00,82.097 l0.00,31.996 
+		c0.00,8.837-7.165,16.002-16.002,16.002s-16.002-7.165-16.002-16.002l0.00-31.996 l0.00-82.097 l0.00-34.34 c-9.524-5.548-16.002-15.752-16.002-27.566
+		c0.00-17.674, 14.33-31.996, 32.004-31.996c 17.675,0.00, 32.004,14.322, 32.004,31.996C 96.004,139.81, 89.519,150.014, 80.002,155.562z M 64.00,104.00
+		c-13.251,0.00-24.003,10.744-24.003,23.995c0.00,10.438, 6.704,19.229, 16.002,22.534L 55.999,232.50 l 16.002,0.00 l0.00-81.971 
+		c 9.298-3.305, 16.002-12.096, 16.002-22.534C 88.003,114.744, 77.252,104.00, 64.00,104.00z" horiz-adv-x="128"  />
+<glyph unicode="&#xe03c;" d="M 64.008,63.995c-35.349,0.00-64.009,28.66-64.009,64.00
+		c0.00,16.205, 6.079,30.965, 16.003,42.239l0.00,133.76 c0.00,26.511, 21.494,48.006, 48.006,48.006c 26.503,0.00, 47.998-21.495, 47.998-48.006l0.00-133.76 
+		C 121.937,158.96, 128.00,144.20, 128.00,127.995C 128.00,92.655, 99.356,63.995, 64.008,63.995z M 80.01,155.562l0.00,34.34 l0.00,82.097 l0.00,31.996 
+		c0.00,8.837-7.173,16.002-16.002,16.002c-8.838,0.00-16.002-7.165-16.002-16.002l0.00-31.996 l0.00-82.097 l0.00-34.34 
+		c-9.525-5.548-16.002-15.752-16.002-27.566c0.00-17.674, 14.329-31.996, 32.004-31.996c 17.674,0.00, 31.996,14.322, 31.996,31.996
+		C 96.004,139.81, 89.527,150.014, 80.01,155.562z M 64.008,104.00c-13.252,0.00-24.004,10.744-24.004,23.995
+		c0.00,10.438, 6.704,19.229, 16.003,22.534L 56.007,272.498 l 16.002,0.00 l0.00-121.969 c 9.298-3.305, 16.002-12.096, 16.002-22.534
+		C 88.011,114.744, 77.259,104.00, 64.008,104.00z" horiz-adv-x="128"  />
+<glyph unicode="&#xe03d;" d="M 64.008,63.995c-35.348,0.00-64.008,28.66-64.008,64.00
+		c0.00,16.205, 6.079,30.965, 16.002,42.239l0.00,133.76 c0.00,26.511, 21.487,48.006, 48.006,48.006c 26.504,0.00, 47.998-21.495, 47.998-48.006l0.00-133.76 
+		C 121.938,158.96, 128.00,144.20, 128.00,127.995C 128.00,92.655, 99.349,63.995, 64.008,63.995z M 80.01,155.562l0.00,34.34 l0.00,82.097 l0.00,31.996 
+		c0.00,8.837-7.173,16.002-16.002,16.002c-8.845,0.00-16.002-7.165-16.002-16.002l0.00-31.996 l0.00-82.097 l0.00-34.34 
+		c-9.524-5.548-16.002-15.752-16.002-27.566c0.00-17.674, 14.33-31.996, 32.004-31.996s 31.996,14.322, 31.996,31.996
+		C 96.004,139.81, 89.526,150.014, 80.01,155.562z M 64.008,104.00c-13.26,0.00-24.003,10.744-24.003,23.995
+		c0.00,10.438, 6.704,19.229, 16.002,22.534L 56.007,272.498 l0.00,31.496 c0.00,4.422, 3.579,8.001, 8.001,8.001c 4.415,0.00, 8.001-3.579, 8.001-8.001l0.00-31.496 
+		l0.00-121.969 c 9.298-3.305, 15.994-12.096, 15.994-22.534C 88.003,114.744, 77.26,104.00, 64.008,104.00z" horiz-adv-x="128"  />
+<glyph unicode="&#xe03e;" d="M 207.995,271.649c-17.674,0.00-32.004-14.33-32.004-32.004l0.00-64.001 
+		c0.00-17.674, 14.33-31.996, 32.004-31.996s 32.004,14.322, 32.004,31.996l 31.996,0.00 c0.00-35.348-28.652-64.00-64.00-64.00c-35.349,0.00-64.00,28.652-64.00,64.00
+		l0.00,64.001 c0.00,35.348, 28.651,64.00, 64.00,64.00c 35.348,0.00, 64.00-28.652, 64.00-64.00l-31.996,0.00 C 239.999,257.319, 225.669,271.649, 207.995,271.649z
+		 M 55.992,191.647C 25.066,191.647,0.00,216.72,0.00,247.646s 25.066,56.00, 55.992,56.00c 30.933,0.00, 56.007-25.074, 56.007-56.00
+		S 86.925,191.647, 55.992,191.647z M 55.992,271.649c-13.252,0.00-23.996-10.752-23.996-24.004c0.00-13.251, 10.744-24.002, 23.996-24.002
+		c 13.259,0.00, 24.003,10.751, 24.003,24.002C 79.995,260.897, 69.251,271.649, 55.992,271.649z" horiz-adv-x="288"  />
+<glyph unicode="&#xe03f;" d="M 239.999,239.646c0.00,17.674-14.33,32.004-32.004,32.004
+		s-32.004-14.33-32.004-32.004l0.00-31.996 l 64.008,0.00 l0.00-32.005 l-64.008,0.00 l0.00-64.00 l-31.996,0.00 l0.00,64.00 l0.00,32.005 l0.00,31.996 c0.00,35.348, 28.651,64.00, 64.00,64.00
+		c 35.348,0.00, 64.00-28.652, 64.00-64.00L 239.999,239.646 z M 55.991,191.647C 25.065,191.647,0.00,216.72,0.00,247.646s 25.065,56.00, 55.991,56.00
+		s 56.007-25.074, 56.007-56.00S 86.917,191.647, 55.991,191.647z M 55.991,271.649c-13.252,0.00-23.995-10.752-23.995-24.004
+		c0.00-13.251, 10.743-24.002, 23.995-24.002c 13.26,0.00, 24.003,10.751, 24.003,24.002C 79.994,260.897, 69.251,271.649, 55.991,271.649z" horiz-adv-x="288"  />
+<glyph unicode="&#xe040;" d="M 111.998,95.995C 50.131,95.995,0.00,146.143,0.00,208.002
+		C0.00,269.853, 50.131,320.00, 111.998,320.00c 61.852,0.00, 111.998-50.147, 111.998-111.998C 223.996,146.143, 173.85,95.995, 111.998,95.995z
+		 M 111.998,287.996c-44.193,0.00-80.01-35.817-80.01-79.994c0.00-44.186, 35.816-80.003, 80.01-80.003c 44.178,0.00, 79.994,35.817, 79.994,80.003
+		C 191.992,252.179, 156.176,287.996, 111.998,287.996z M 136.954,188.233c-0.766-1.109-1.547-2.07-2.328-2.86
+		c-0.798-0.789-1.767-1.57-2.876-2.344c-3.422-2.719-7.25-4.633-11.282-5.742c-20.80-8.502-53.726-14.541-53.726-14.541
+		s 6.032,32.918, 14.533,53.725c 1.109,4.04, 3.031,7.861, 5.75,11.291c 0.767,1.109, 1.548,2.078, 2.345,2.867
+		c 0.781,0.789, 1.75,1.57, 2.86,2.337c 3.438,2.719, 7.25,4.641, 11.298,5.75c 20.80,8.493, 53.726,14.541, 53.726,14.541
+		s-6.048-32.926-14.549-53.733C 141.596,195.484, 139.674,191.671, 136.954,188.233z M 117.654,213.658
+		c-3.125,3.125-8.188,3.125-11.313,0.00c-3.125-3.125-3.125-8.188,0.00-11.313c 3.126-3.126, 8.188-3.126, 11.313,0.00
+		C 120.78,205.47, 120.78,210.533, 117.654,213.658z" horiz-adv-x="223"  />
+<glyph unicode="&#xe041;" d="M 111.798,320.00C 49.938,320.00-0.209,269.853-0.209,208.002
+				c0.00-61.859, 50.147-112.007, 112.007-112.007c 61.852,0.00, 111.998,50.147, 111.998,112.007C 223.796,269.853, 173.649,320.00, 111.798,320.00z
+				 M 111.798,127.999c-44.186,0.00-80.003,35.817-80.003,80.003c0.00,44.177, 35.817,79.994, 80.003,79.994
+				c 44.177,0.00, 80.002-35.817, 80.002-79.994C 191.80,163.816, 155.975,127.999, 111.798,127.999z M 79.794,208.002
+				c0.00-17.675, 14.33-32.005, 32.004-32.005s 31.996,14.33, 31.996,32.005c0.00,17.674-31.996,64.00-31.996,64.00S 79.794,225.676, 79.794,208.002z
+				 M 119.798,208.002c0.00-4.423-3.586-8.002-8.00-8.002c-4.423,0.00-8.002,3.579-8.002,8.002c0.00,4.414, 3.579,8.00, 8.002,8.00
+				C 116.212,216.002, 119.798,212.416, 119.798,208.002z" horiz-adv-x="223"  />
+<glyph unicode="&#xe042;" d="M 111.801,320.00C 49.934,320.00-0.213,269.853-0.213,208.002
+				c0.00-61.859, 50.146-112.007, 112.014-112.007c 61.852,0.00, 111.999,50.147, 111.999,112.007C 223.80,269.853, 173.652,320.00, 111.801,320.00z
+				 M 111.801,127.999c-44.192,0.00-80.01,35.817-80.01,80.003c0.00,44.177, 35.817,79.994, 80.01,79.994
+				c 44.178,0.00, 79.995-35.817, 79.995-79.994C 191.796,163.816, 155.979,127.999, 111.801,127.999z M 111.801,239.998
+				c-17.674,0.00-32.004-14.322-32.004-31.996c0.00-17.675, 14.33-32.005, 32.004-32.005c 17.659,0.00, 63.993,32.005, 63.993,32.005
+				S 129.46,239.998, 111.801,239.998z M 111.801,200.00c-4.423,0.00-8.001,3.579-8.001,8.002c0.00,4.414, 3.578,8.00, 8.001,8.00
+				c 4.407,0.00, 7.985-3.586, 7.985-8.00C 119.786,203.579, 116.208,200.00, 111.801,200.00z" horiz-adv-x="223"  />
+<glyph unicode="&#xe043;" d="M 111.793,320.00C 49.942,320.00-0.205,269.853-0.205,208.002
+				c0.00-61.859, 50.147-112.007, 111.998-112.007c 61.859,0.00, 111.999,50.147, 111.999,112.007C 223.792,269.853, 173.652,320.00, 111.793,320.00z
+				 M 111.793,127.999c-44.177,0.00-79.994,35.817-79.994,80.003c0.00,44.177, 35.817,79.994, 79.994,79.994
+				c 44.186,0.00, 80.003-35.817, 80.003-79.994C 191.796,163.816, 155.979,127.999, 111.793,127.999z M 111.793,239.998
+				c-17.666,0.00-31.996-14.322-31.996-31.996c0.00-17.675, 31.996-64.001, 31.996-64.001s 32.005,46.326, 32.005,64.001
+				C 143.798,225.676, 129.468,239.998, 111.793,239.998z M 111.793,200.00c-4.414,0.00-8.00,3.579-8.00,8.002c0.00,4.414, 3.586,8.00, 8.00,8.00
+				c 4.424,0.00, 8.002-3.586, 8.002-8.00C 119.795,203.579, 116.217,200.00, 111.793,200.00z" horiz-adv-x="223"  />
+<glyph unicode="&#xe044;" d="M 111.793,320.00C 49.942,320.00-0.205,269.853-0.205,208.002
+				c0.00-61.859, 50.146-112.007, 111.998-112.007c 61.859,0.00, 111.999,50.147, 111.999,112.007C 223.792,269.853, 173.653,320.00, 111.793,320.00z
+				 M 111.793,127.999c-44.186,0.00-80.002,35.817-80.002,80.003c0.00,44.177, 35.816,79.994, 80.002,79.994s 80.002-35.817, 80.002-79.994
+				C 191.796,163.816, 155.979,127.999, 111.793,127.999z M 111.793,239.998c-17.674,0.00-64.00-31.996-64.00-31.996s 46.326-32.005, 64.00-32.005
+				s 32.004,14.33, 32.004,32.005C 143.797,225.676, 129.467,239.998, 111.793,239.998z M 111.793,200.00c-4.414,0.00-8.001,3.579-8.001,8.002
+				c0.00,4.414, 3.587,8.00, 8.001,8.00c 4.423,0.00, 8.002-3.586, 8.002-8.00C 119.795,203.579, 116.216,200.00, 111.793,200.00z" horiz-adv-x="223"  />
+<glyph unicode="&#xe045;" d="M 308.005,255.995c-24.30,0.00-44.006-10.744-44.006-23.995
+		c0.00,13.251-19.689,23.995-43.997,23.995c-14.916,0.00-28.043-4.071-35.997-10.26l0.00-69.734 l 8.001,0.00 l0.00-32.005 
+		c0.00-8.837, 7.157-16.002, 16.002-16.002c 8.829,0.00, 15.994,7.165, 15.994,16.002c0.00,8.838, 7.165,16.002, 16.002,16.002
+		s 15.994-7.164, 15.994-16.002c0.00-26.511-21.487-47.998-47.99-47.998c-26.519,0.00-48.006,21.487-48.006,47.998l0.00,32.005 l 8.001,0.00 l0.00,69.734 
+		c-7.954,6.188-21.08,10.26-36.004,10.26c-24.301,0.00-43.99-10.744-43.99-23.995l0.00,0.00c0.00,13.251-19.706,23.995-44.006,23.995
+		S-0.002,245.251-0.002,232.00c0.00,57.437, 78.807,128.00, 176.006,128.00c 97.20,0.00, 175.999-70.563, 175.999-128.00
+		C 352.003,245.251, 332.305,255.995, 308.005,255.995z" horiz-adv-x="352"  />
+<glyph unicode="&#xe046;" d="M 215.996,207.67l-8.001,0.00 l0.00-16.002 
+		c0.00-13.252-10.744-23.996-24.003-23.996l-31.996,0.00 c-13.26,0.00-23.995,10.744-23.995,23.996l-7.923,31.676
+		c-2.767,0.195-5.563,0.328-8.329,0.328c-2.657,0.00-5.282-0.148-7.845-0.375l-7.907-31.629c0.00-13.252-10.744-23.996-24.004-23.996
+		L 39.997,167.672 c-13.259,0.00-24.003,10.744-24.003,23.996L 15.994,207.67 L 8.001,207.67 c-4.422,0.00-8.001,3.578-8.001,8.00l0.00,8.001 c0.00,4.414, 3.579,7.993, 8.001,7.993
+		l 9.471,0.00 c 3.297,9.306, 12.095,16.002, 22.525,16.002l 39.998,0.00 c 7.562,0.00, 14.228-3.562, 18.627-9.032c 4.203,0.609, 8.61,1.031, 13.127,1.031
+		c 4.602,0.00, 9.235-0.367, 13.759-0.867c 4.399,5.368, 11.002,8.868, 18.487,8.868l 39.997,0.00 c 10.438,0.00, 19.229-6.696, 22.534-16.002l 9.47,0.00 
+		c 4.423,0.00, 8.001-3.579, 8.001-7.993l0.00-8.001 C 223.997,211.248, 220.419,207.67, 215.996,207.67z M 66.712,234.391
+		c-2.336,2.345-6.142,2.345-8.485,0.00l-25.448-25.456c-2.345-2.344-2.345-6.143,0.00-8.486c 2.336-2.344, 6.141-2.344, 8.478,0.00
+		l 25.456,25.457C 69.056,228.25, 69.056,232.047, 66.712,234.391z M 178.718,234.391c-2.344,2.345-6.142,2.345-8.485,0.00l-25.456-25.456
+		c-2.344-2.344-2.344-6.143,0.00-8.486s 6.142-2.344, 8.485,0.00l 25.456,25.457C 181.062,228.25, 181.062,232.047, 178.718,234.391z" horiz-adv-x="224"  />
+<glyph unicode="&#xe047;" d="M 287.721,101.508c-0.547,11.213-2.719,21.964-6.32,32.074
+		c 22.644,9.838, 38.599,32.152, 38.599,58.414c0.00,35.348-28.652,64.00-64.001,64.00c-12.798,0.00-24.667-3.853-34.668-10.321
+		c-9.853,42.536-47.795,74.321-93.332,74.321c-53.022,0.00-95.996-42.974-95.996-95.996c0.00-33.387, 17.143-62.633, 43.005-79.822
+		c-4.703-9.846-7.993-20.464-9.657-31.652C 26.384,134.457-0.002,176.096-0.002,224.00c0.00,70.688, 57.312,128.00, 128.00,128.00
+		c 48.037,0.00, 89.84-26.503, 111.732-65.641c 5.313,0.914, 10.697,1.641, 16.268,1.641c 53.022,0.00, 96.005-42.981, 96.005-96.004
+		C 352.003,150.115, 325.132,114.604, 287.721,101.508z M 176.004,179.002l0.00-0.133 l 23.878,0.00 l-17.814,17.814
+		c-6.251,6.251-6.251,16.377,0.00,22.628c 6.243,6.243, 16.377,6.243, 22.62,0.00l 45.256-45.256c 6.25-6.25, 6.25-16.377,0.00-22.627
+		l-45.256-45.256c-6.243-6.251-16.377-6.251-22.62,0.00c-6.251,6.251-6.251,16.377,0.00,22.628l 18.064,18.064l-24.128,0.00 l0.00,0.00
+		c-26.519,0.00-48.006-21.487-48.006-47.998c0.00-26.512, 21.487-47.998, 48.006-47.998c 26.504,0.00, 47.998,21.486, 47.998,47.998
+		c0.00,1.242-0.273,2.406-0.367,3.625l 26.504,26.496c 3.75-9.267, 5.859-19.378, 5.859-29.988c0.00-44.186-35.816-80.002-79.994-80.002
+		c-44.185,0.00-80.002,35.816-80.002,80.002S 131.819,179.002, 176.004,179.002z" horiz-adv-x="352"  />
+<glyph unicode="&#xe048;" d="M 256.003,95.999c-4.141,0.00-10.298,0.00-17.736,0.00l 16.807,16.808
+		c 4.712,4.719, 8.236,10.173, 10.58,15.994c 30.754,4.664, 54.351,31.145, 54.351,63.195c0.00,35.348-28.652,64.00-64.001,64.00
+		c-12.392,0.00-23.861-3.664-33.66-9.767c-1.109,4.469-2.867,8.642-5.125,12.517c-13.908,35.802-48.49,61.25-89.215,61.25
+		c-53.021,0.00-95.996-42.974-95.996-95.996c0.00-36.896, 20.83-68.884, 51.35-84.956c 1.562-9.603, 5.923-18.83, 13.33-26.237l 15.768-15.768
+		C 49.11,104.72, 0.003,158.577, 0.003,224.00c0.00,70.688, 57.304,128.00, 128.00,128.00c 48.037,0.00, 89.84-26.503, 111.732-65.641
+		c 5.313,0.914, 10.689,1.641, 16.268,1.641C 309.026,288.00, 352.00,245.019, 352.00,191.996C 352.00,138.981, 309.026,95.999, 256.003,95.999z
+		 M 141.935,158.062l 17.947-17.939l0.00,94.629 c0.00,8.837, 7.157,15.994, 15.994,15.994c 8.838,0.00, 16.002-7.157, 16.002-15.994l0.00-94.629 
+		l 17.939,17.939c 6.244,6.251, 16.385,6.251, 22.629,0.00c 6.242-6.25, 6.242-16.377,0.00-22.627l-45.256-45.256
+		c-6.243-6.243-16.377-6.243-22.628,0.00l-45.248,45.256c-6.251,6.25-6.251,16.377,0.00,22.627
+		C 125.558,164.312, 135.691,164.312, 141.935,158.062z" horiz-adv-x="352"  />
+<glyph unicode="&#xe049;" d="M 256.004,95.999c-6.657,0.00-18.596,0.00-32.652,0.00
+		c 0.195,1.751, 0.523,3.454, 0.523,5.251l0.00,26.746 c 13.447,0.00, 25.019,0.00, 32.129,0.00c 35.349,0.00, 64.00,28.659, 64.00,64.00c0.00,35.348-28.651,64.00-64.00,64.00
+		c-9.845,0.00-19.111-2.312-27.426-6.306l-12.946,12.947c-14.854,33.738-48.389,57.358-87.628,57.358
+		c-53.022,0.00-95.997-42.974-95.997-95.996c0.00-52.976, 42.912-95.926, 95.872-95.996l0.00-26.754 c0.00-1.797, 0.336-3.50, 0.531-5.251
+		c-0.125,0.00-0.281,0.00-0.406,0.00C 57.308,95.999, 0.003,153.304, 0.003,224.00c0.00,70.688, 57.305,128.00, 128.001,128.00
+		c 48.037,0.00, 89.839-26.503, 111.732-65.641c 5.313,0.914, 10.696,1.641, 16.268,1.641C 309.026,288.00, 352.00,245.019, 352.00,191.996
+		C 352.00,138.981, 309.026,95.999, 256.004,95.999z M 119.314,177.939c-6.25,6.243-6.25,16.377,0.00,22.62l 45.256,45.256
+		c 6.243,6.25, 16.377,6.25, 22.628,0.00l 45.248-45.256c 6.251-6.243, 6.251-16.377,0.00-22.62c-6.243-6.251-16.377-6.251-22.62,0.00
+		l-17.947,17.939l0.00-94.629 c0.00-8.837-7.165-16.002-15.994-16.002c-8.845,0.00-16.002,7.165-16.002,16.002l0.00,94.629 l-17.94-17.939
+		C 135.692,171.688, 125.558,171.688, 119.314,177.939z" horiz-adv-x="352"  />
+<glyph unicode="&#x20;" horiz-adv-x="256" />
+<glyph class="hidden" unicode="&#xf000;" d="M0,480L 512 -32L0 -32 z" horiz-adv-x="0" />
+</font></defs></svg>

BIN
dashboard/assets/fonts/climacons-webfont.ttf


BIN
dashboard/assets/fonts/climacons-webfont.woff


BIN
dashboard/assets/fonts/fontawesome-webfont.eot


File diff suppressed because it is too large
+ 196 - 0
dashboard/assets/fonts/fontawesome-webfont.svg


BIN
dashboard/assets/fonts/fontawesome-webfont.ttf


BIN
dashboard/assets/fonts/fontawesome-webfont.woff


BIN
dashboard/assets/fonts/fontawesome-webfont.woff2


BIN
dashboard/assets/images/logo.png


BIN
dashboard/assets/images/weather_widget.png


+ 25 - 0
dashboard/assets/javascripts/application.coffee

@@ -0,0 +1,25 @@
+# dashing.js is located in the dashing framework
+# It includes jquery & batman for you.
+#= require dashing.js
+
+#= require_directory .
+#= require_tree ../../widgets
+
+console.log("Yeah! The dashboard has started!")
+
+Dashing.on 'ready', ->
+  Dashing.widget_margins ||= [5, 5]
+  Dashing.widget_base_dimensions ||= [300, 360]
+  Dashing.numColumns ||= 4
+
+  contentWidth = (Dashing.widget_base_dimensions[0] + Dashing.widget_margins[0] * 2) * Dashing.numColumns
+
+  Batman.setImmediate ->
+    $('.gridster').width(contentWidth)
+    $('.gridster ul:first').gridster
+      widget_margins: Dashing.widget_margins
+      widget_base_dimensions: Dashing.widget_base_dimensions
+      avoid_overlapped_widgets: !Dashing.customGridsterLayout
+      draggable:
+        stop: Dashing.showGridsterInstructions
+        start: -> Dashing.currentWidgetPositions = Dashing.getWidgetPositions()

File diff suppressed because it is too large
+ 0 - 0
dashboard/assets/javascripts/d3-3.2.8.js


+ 37 - 0
dashboard/assets/javascripts/dashing.gridster.coffee

@@ -0,0 +1,37 @@
+#= require_directory ./gridster
+
+# This file enables gridster integration (http://gridster.net/)
+# Delete it if you'd rather handle the layout yourself.
+# You'll miss out on a lot if you do, but we won't hold it against you.
+
+Dashing.gridsterLayout = (positions) ->
+  Dashing.customGridsterLayout = true
+  positions = positions.replace(/^"|"$/g, '')
+  positions = $.parseJSON(positions)
+  widgets = $("[data-row^=]")
+  for widget, index in widgets
+    $(widget).attr('data-row', positions[index].row)
+    $(widget).attr('data-col', positions[index].col)
+
+Dashing.getWidgetPositions = ->
+  $(".gridster ul:first").gridster().data('gridster').serialize()
+
+Dashing.showGridsterInstructions = ->
+  newWidgetPositions = Dashing.getWidgetPositions()
+
+  unless JSON.stringify(newWidgetPositions) == JSON.stringify(Dashing.currentWidgetPositions)
+    Dashing.currentWidgetPositions = newWidgetPositions
+    $('#save-gridster').slideDown()
+    $('#gridster-code').text("
+      <script type='text/javascript'>\n
+      $(function() {\n
+      \ \ Dashing.gridsterLayout('#{JSON.stringify(Dashing.currentWidgetPositions)}')\n
+      });\n
+      </script>
+    ")
+
+$ ->
+  $('#save-gridster').leanModal()
+
+  $('#save-gridster').click ->
+    $('#save-gridster').slideUp()

+ 38 - 0
dashboard/assets/javascripts/forecast.coffee

@@ -0,0 +1,38 @@
+class Dashing.Forecast extends Dashing.Widget
+
+  # Overrides Dashing.Widget method in dashing.coffee
+  @accessor 'updatedAtMessage', ->
+    if updatedAt = @get('updatedAt')
+      timestamp = new Date(updatedAt * 1000)
+      hours = timestamp.getHours()
+      minutes = ("0" + timestamp.getMinutes()).slice(-2)
+      "Updated at #{hours}:#{minutes}"
+
+  constructor: ->
+    super
+    @forecast_icons = new Skycons({"color": "white"})
+    @forecast_icons.play()
+
+  ready: ->
+# This is fired when the widget is done being rendered
+    @setIcons()
+
+  onData: (data) ->
+# Handle incoming data
+# We want to make sure the first time they're set is after ready()
+# has been called, or the Skycons code will complain.
+    if @forecast_icons.list.length
+      @setIcons()
+
+  setIcons: ->
+    @setIcon('current_icon')
+    @setIcon('next_icon')
+    @setIcon('later_icon')
+
+  setIcon: (name) ->
+    skycon = @toSkycon(name)
+    @forecast_icons.set(name, eval(skycon)) if skycon
+
+  toSkycon: (data) ->
+    if @get(data)
+      'Skycons.' + @get(data).replace(/-/g, "_").toUpperCase()

File diff suppressed because it is too large
+ 0 - 0
dashboard/assets/javascripts/gridster/jquery.gridster.min.js


+ 5 - 0
dashboard/assets/javascripts/gridster/jquery.leanModal.min.js

@@ -0,0 +1,5 @@
+// leanModal v1.1 by Ray Stone - http://finelysliced.com.au
+// Dual licensed under the MIT and GPL
+
+(function($){$.fn.extend({leanModal:function(options){var defaults={top:100,overlay:0.5,closeButton:null};var overlay=$("<div id='lean_overlay'></div>");$("body").append(overlay);options=$.extend(defaults,options);return this.each(function(){var o=options;$(this).click(function(e){var modal_id=$(this).attr("href");$("#lean_overlay").click(function(){close_modal(modal_id)});$(o.closeButton).click(function(){close_modal(modal_id)});var modal_height=$(modal_id).outerHeight();var modal_width=$(modal_id).outerWidth();
+$("#lean_overlay").css({"display":"block",opacity:0});$("#lean_overlay").fadeTo(200,o.overlay);$(modal_id).css({"display":"block","position":"fixed","opacity":0,"z-index":11000,"left":50+"%","margin-left":-(modal_width/2)+"px","top":o.top+"px"});$(modal_id).fadeTo(200,1);e.preventDefault()})});function close_modal(modal_id){$("#lean_overlay").fadeOut(200);$(modal_id).css({"display":"none"})}}})})(jQuery);

+ 646 - 0
dashboard/assets/javascripts/jquery.knob.js

@@ -0,0 +1,646 @@
+/*!jQuery Knob*/
+/**
+ * Downward compatible, touchable dial
+ *
+ * Version: 1.2.0 (15/07/2012)
+ * Requires: jQuery v1.7+
+ *
+ * Copyright (c) 2012 Anthony Terrien
+ * Under MIT and GPL licenses:
+ *  http://www.opensource.org/licenses/mit-license.php
+ *  http://www.gnu.org/licenses/gpl.html
+ *
+ * Thanks to vor, eskimoblood, spiffistan, FabrizioC
+ */
+$(function () {
+
+    /**
+     * Kontrol library
+     */
+    "use strict";
+
+    /**
+     * Definition of globals and core
+     */
+    var k = {}, // kontrol
+        max = Math.max,
+        min = Math.min;
+
+    k.c = {};
+    k.c.d = $(document);
+    k.c.t = function (e) {
+        return e.originalEvent.touches.length - 1;
+    };
+
+    /**
+     * Kontrol Object
+     *
+     * Definition of an abstract UI control
+     *
+     * Each concrete component must call this one.
+     * <code>
+     * k.o.call(this);
+     * </code>
+     */
+    k.o = function () {
+        var s = this;
+
+        this.o = null; // array of options
+        this.$ = null; // jQuery wrapped element
+        this.i = null; // mixed HTMLInputElement or array of HTMLInputElement
+        this.g = null; // 2D graphics context for 'pre-rendering'
+        this.v = null; // value ; mixed array or integer
+        this.cv = null; // change value ; not commited value
+        this.x = 0; // canvas x position
+        this.y = 0; // canvas y position
+        this.$c = null; // jQuery canvas element
+        this.c = null; // rendered canvas context
+        this.t = 0; // touches index
+        this.isInit = false;
+        this.fgColor = null; // main color
+        this.pColor = null; // previous color
+        this.dH = null; // draw hook
+        this.cH = null; // change hook
+        this.eH = null; // cancel hook
+        this.rH = null; // release hook
+
+        this.run = function () {
+            var cf = function (e, conf) {
+                var k;
+                for (k in conf) {
+                    s.o[k] = conf[k];
+                }
+                s.init();
+                s._configure()
+                 ._draw();
+            };
+
+            if(this.$.data('kontroled')) return;
+            this.$.data('kontroled', true);
+
+            this.extend();
+            this.o = $.extend(
+                {
+                    // Config
+                    min : this.$.data('min') || 0,
+                    max : this.$.data('max') || 100,
+                    stopper : true,
+                    readOnly : this.$.data('readonly'),
+
+                    // UI
+                    cursor : (this.$.data('cursor') === true && 30)
+                                || this.$.data('cursor')
+                                || 0,
+                    thickness : this.$.data('thickness') || 0.35,
+                    width : this.$.data('width') || 200,
+                    height : this.$.data('height') || 200,
+                    displayInput : this.$.data('displayinput') == null || this.$.data('displayinput'),
+                    displayPrevious : this.$.data('displayprevious'),
+                    fgColor : this.$.data('fgcolor') || '#87CEEB',
+                    inline : false,
+
+                    // Hooks
+                    draw : null, // function () {}
+                    change : null, // function (value) {}
+                    cancel : null, // function () {}
+                    release : null // function (value) {}
+                }, this.o
+            );
+
+            // routing value
+            if(this.$.is('fieldset')) {
+
+                // fieldset = array of integer
+                this.v = {};
+                this.i = this.$.find('input')
+                this.i.each(function(k) {
+                    var $this = $(this);
+                    s.i[k] = $this;
+                    s.v[k] = $this.val();
+
+                    $this.bind(
+                        'change'
+                        , function () {
+                            var val = {};
+                            val[k] = $this.val();
+                            s.val(val);
+                        }
+                    );
+                });
+                this.$.find('legend').remove();
+
+            } else {
+                // input = integer
+                this.i = this.$;
+                this.v = this.$.val();
+                (this.v == '') && (this.v = this.o.min);
+
+                this.$.bind(
+                    'change'
+                    , function () {
+                        s.val(s.$.val());
+                    }
+                );
+            }
+
+            (!this.o.displayInput) && this.$.hide();
+
+            this.$c = $('<canvas width="' +
+                            this.o.width + 'px" height="' +
+                            this.o.height + 'px"></canvas>');
+            this.c = this.$c[0].getContext("2d");
+
+            this.$
+                .wrap($('<div style="' + (this.o.inline ? 'display:inline;' : '') +
+                        'width:' + this.o.width + 'px;height:' +
+                        this.o.height + 'px;"></div>'))
+                .before(this.$c);
+
+            if (this.v instanceof Object) {
+                this.cv = {};
+                this.copy(this.v, this.cv);
+            } else {
+                this.cv = this.v;
+            }
+
+            this.$
+                .bind("configure", cf)
+                .parent()
+                .bind("configure", cf);
+
+            this._listen()
+                ._configure()
+                ._xy()
+                .init();
+
+            this.isInit = true;
+
+            this._draw();
+
+            return this;
+        };
+
+        this._draw = function () {
+
+            // canvas pre-rendering
+            var d = true,
+                c = document.createElement('canvas');
+
+            c.width = s.o.width;
+            c.height = s.o.height;
+            s.g = c.getContext('2d');
+
+            s.clear();
+
+            s.dH
+            && (d = s.dH());
+
+            (d !== false) && s.draw();
+
+            s.c.drawImage(c, 0, 0);
+            c = null;
+        };
+
+        this._touch = function (e) {
+
+            var touchMove = function (e) {
+
+                var v = s.xy2val(
+                            e.originalEvent.touches[s.t].pageX,
+                            e.originalEvent.touches[s.t].pageY
+                            );
+
+                if (v == s.cv) return;
+
+                if (
+                    s.cH
+                    && (s.cH(v) === false)
+                ) return;
+
+
+                s.change(v);
+                s._draw();
+            };
+
+            // get touches index
+            this.t = k.c.t(e);
+
+            // First touch
+            touchMove(e);
+
+            // Touch events listeners
+            k.c.d
+                .bind("touchmove.k", touchMove)
+                .bind(
+                    "touchend.k"
+                    , function () {
+                        k.c.d.unbind('touchmove.k touchend.k');
+
+                        if (
+                            s.rH
+                            && (s.rH(s.cv) === false)
+                        ) return;
+
+                        s.val(s.cv);
+                    }
+                );
+
+            return this;
+        };
+
+        this._mouse = function (e) {
+
+            var mouseMove = function (e) {
+                var v = s.xy2val(e.pageX, e.pageY);
+                if (v == s.cv) return;
+
+                if (
+                    s.cH
+                    && (s.cH(v) === false)
+                ) return;
+
+                s.change(v);
+                s._draw();
+            };
+
+            // First click
+            mouseMove(e);
+
+            // Mouse events listeners
+            k.c.d
+                .bind("mousemove.k", mouseMove)
+                .bind(
+                    // Escape key cancel current change
+                    "keyup.k"
+                    , function (e) {
+                        if (e.keyCode === 27) {
+                            k.c.d.unbind("mouseup.k mousemove.k keyup.k");
+
+                            if (
+                                s.eH
+                                && (s.eH() === false)
+                            ) return;
+
+                            s.cancel();
+                        }
+                    }
+                )
+                .bind(
+                    "mouseup.k"
+                    , function (e) {
+                        k.c.d.unbind('mousemove.k mouseup.k keyup.k');
+
+                        if (
+                            s.rH
+                            && (s.rH(s.cv) === false)
+                        ) return;
+
+                        s.val(s.cv);
+                    }
+                );
+
+            return this;
+        };
+
+        this._xy = function () {
+            var o = this.$c.offset();
+            this.x = o.left;
+            this.y = o.top;
+            return this;
+        };
+
+        this._listen = function () {
+
+            if (!this.o.readOnly) {
+                this.$c
+                    .bind(
+                        "mousedown"
+                        , function (e) {
+                            e.preventDefault();
+                            s._xy()._mouse(e);
+                         }
+                    )
+                    .bind(
+                        "touchstart"
+                        , function (e) {
+                            e.preventDefault();
+                            s._xy()._touch(e);
+                         }
+                    );
+                this.listen();
+            } else {
+                this.$.attr('readonly', 'readonly');
+            }
+
+            return this;
+        };
+
+        this._configure = function () {
+
+            // Hooks
+            if (this.o.draw) this.dH = this.o.draw;
+            if (this.o.change) this.cH = this.o.change;
+            if (this.o.cancel) this.eH = this.o.cancel;
+            if (this.o.release) this.rH = this.o.release;
+
+            if (this.o.displayPrevious) {
+                this.pColor = this.h2rgba(this.o.fgColor, "0.4");
+                this.fgColor = this.h2rgba(this.o.fgColor, "0.6");
+            } else {
+                this.fgColor = this.o.fgColor;
+            }
+
+            return this;
+        };
+
+        this._clear = function () {
+            this.$c[0].width = this.$c[0].width;
+        };
+
+        // Abstract methods
+        this.listen = function () {}; // on start, one time
+        this.extend = function () {}; // each time configure triggered
+        this.init = function () {}; // each time configure triggered
+        this.change = function (v) {}; // on change
+        this.val = function (v) {}; // on release
+        this.xy2val = function (x, y) {}; //
+        this.draw = function () {}; // on change / on release
+        this.clear = function () { this._clear(); };
+
+        // Utils
+        this.h2rgba = function (h, a) {
+            var rgb;
+            h = h.substring(1,7)
+            rgb = [parseInt(h.substring(0,2),16)
+                   ,parseInt(h.substring(2,4),16)
+                   ,parseInt(h.substring(4,6),16)];
+            return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + a + ")";
+        };
+
+        this.copy = function (f, t) {
+            for (var i in f) { t[i] = f[i]; }
+        };
+    };
+
+
+    /**
+     * k.Dial
+     */
+    k.Dial = function () {
+        k.o.call(this);
+
+        this.startAngle = null;
+        this.xy = null;
+        this.radius = null;
+        this.lineWidth = null;
+        this.cursorExt = null;
+        this.w2 = null;
+        this.PI2 = 2*Math.PI;
+
+        this.extend = function () {
+            this.o = $.extend(
+                {
+                    bgColor : this.$.data('bgcolor') || '#EEEEEE',
+                    angleOffset : this.$.data('angleoffset') || 0,
+                    angleArc : this.$.data('anglearc') || 360,
+                    inline : true
+                }, this.o
+            );
+        };
+
+        this.val = function (v) {
+            if (null != v) {
+                this.cv = this.o.stopper ? max(min(v, this.o.max), this.o.min) : v;
+                this.v = this.cv;
+                this.$.val(this.v);
+                this._draw();
+            } else {
+                return this.v;
+            }
+        };
+
+        this.xy2val = function (x, y) {
+            var a, ret;
+
+            a = Math.atan2(
+                        x - (this.x + this.w2)
+                        , - (y - this.y - this.w2)
+                    ) - this.angleOffset;
+
+            if(this.angleArc != this.PI2 && (a < 0) && (a > -0.5)) {
+                // if isset angleArc option, set to min if .5 under min
+                a = 0;
+            } else if (a < 0) {
+                a += this.PI2;
+            }
+
+            ret = ~~ (0.5 + (a * (this.o.max - this.o.min) / this.angleArc))
+                    + this.o.min;
+
+            this.o.stopper
+            && (ret = max(min(ret, this.o.max), this.o.min));
+
+            return ret;
+        };
+
+        this.listen = function () {
+            // bind MouseWheel
+            var s = this,
+                mw = function (e) {
+                            e.preventDefault();
+
+                            var ori = e.originalEvent
+                                ,deltaX = ori.detail || ori.wheelDeltaX
+                                ,deltaY = ori.detail || ori.wheelDeltaY
+                                ,v = parseInt(s.$.val()) + (deltaX>0 || deltaY>0 ? 1 : deltaX<0 || deltaY<0 ? -1 : 0);
+
+                            if (
+                                s.cH
+                                && (s.cH(v) === false)
+                            ) return;
+
+                            s.val(v);
+                        }
+                , kval, to, m = 1, kv = {37:-1, 38:1, 39:1, 40:-1};
+
+            this.$
+                .bind(
+                    "keydown"
+                    ,function (e) {
+                        var kc = e.keyCode;
+                        kval = parseInt(String.fromCharCode(kc));
+
+                        if (isNaN(kval)) {
+
+                            (kc !== 13)         // enter
+                            && (kc !== 8)       // bs
+                            && (kc !== 9)       // tab
+                            && (kc !== 189)     // -
+                            && e.preventDefault();
+
+                            // arrows
+                            if ($.inArray(kc,[37,38,39,40]) > -1) {
+                                e.preventDefault();
+
+                                var v = parseInt(s.$.val()) + kv[kc] * m;
+
+                                s.o.stopper
+                                && (v = max(min(v, s.o.max), s.o.min));
+
+                                s.change(v);
+                                s._draw();
+
+                                // long time keydown speed-up
+                                to = window.setTimeout(
+                                    function () { m*=2; }
+                                    ,30
+                                );
+                            }
+                        }
+                    }
+                )
+                .bind(
+                    "keyup"
+                    ,function (e) {
+                        if (isNaN(kval)) {
+                            if (to) {
+                                window.clearTimeout(to);
+                                to = null;
+                                m = 1;
+                                s.val(s.$.val());
+                            }
+                        } else {
+                            // kval postcond
+                            (s.$.val() > s.o.max && s.$.val(s.o.max))
+                            || (s.$.val() < s.o.min && s.$.val(s.o.min));
+                        }
+
+                    }
+                );
+
+            this.$c.bind("mousewheel DOMMouseScroll", mw);
+            this.$.bind("mousewheel DOMMouseScroll", mw)
+        };
+
+        this.init = function () {
+
+            if (
+                this.v < this.o.min
+                || this.v > this.o.max
+            ) this.v = this.o.min;
+
+            this.$.val(this.v);
+            this.w2 = this.o.width / 2;
+            this.cursorExt = this.o.cursor / 100;
+            this.xy = this.w2;
+            this.lineWidth = this.xy * this.o.thickness;
+            this.radius = this.xy - this.lineWidth / 2;
+
+            this.o.angleOffset
+            && (this.o.angleOffset = isNaN(this.o.angleOffset) ? 0 : this.o.angleOffset);
+
+            this.o.angleArc
+            && (this.o.angleArc = isNaN(this.o.angleArc) ? this.PI2 : this.o.angleArc);
+
+            // deg to rad
+            this.angleOffset = this.o.angleOffset * Math.PI / 180;
+            this.angleArc = this.o.angleArc * Math.PI / 180;
+
+            // compute start and end angles
+            this.startAngle = 1.5 * Math.PI + this.angleOffset;
+            this.endAngle = 1.5 * Math.PI + this.angleOffset + this.angleArc;
+
+            var s = max(
+                            String(Math.abs(this.o.max)).length
+                            , String(Math.abs(this.o.min)).length
+                            , 2
+                            ) + 2;
+
+            this.o.displayInput
+                && this.i.css({
+                        'width' : ((this.o.width / 2 + 4) >> 0) + 'px'
+                        ,'height' : ((this.o.width / 3) >> 0) + 'px'
+                        ,'position' : 'absolute'
+                        ,'vertical-align' : 'middle'
+                        ,'margin-top' : ((this.o.width / 3) >> 0) + 'px'
+                        ,'margin-left' : '-' + ((this.o.width * 3 / 4 + 2) >> 0) + 'px'
+                        ,'border' : 0
+                        ,'background' : 'none'
+                        ,'font' : 'bold ' + ((this.o.width / s) >> 0) + 'px Arial'
+                        ,'text-align' : 'center'
+                        ,'color' : this.o.fgColor
+                        ,'padding' : '0px'
+                        ,'-webkit-appearance': 'none'
+                        })
+                || this.i.css({
+                        'width' : '0px'
+                        ,'visibility' : 'hidden'
+                        });
+        };
+
+        this.change = function (v) {
+            this.cv = v;
+            this.$.val(v);
+        };
+
+        this.angle = function (v) {
+            return (v - this.o.min) * this.angleArc / (this.o.max - this.o.min);
+        };
+
+        this.draw = function () {
+
+            var c = this.g,                 // context
+                a = this.angle(this.cv)    // Angle
+                , sat = this.startAngle     // Start angle
+                , eat = sat + a             // End angle
+                , sa, ea                    // Previous angles
+                , r = 1;
+
+            c.lineWidth = this.lineWidth;
+
+            this.o.cursor
+                && (sat = eat - this.cursorExt)
+                && (eat = eat + this.cursorExt);
+
+            c.beginPath();
+                c.strokeStyle = this.o.bgColor;
+                c.arc(this.xy, this.xy, this.radius, this.endAngle, this.startAngle, true);
+            c.stroke();
+
+            if (this.o.displayPrevious) {
+                ea = this.startAngle + this.angle(this.v);
+                sa = this.startAngle;
+                this.o.cursor
+                    && (sa = ea - this.cursorExt)
+                    && (ea = ea + this.cursorExt);
+
+                c.beginPath();
+                    c.strokeStyle = this.pColor;
+                    c.arc(this.xy, this.xy, this.radius, sa, ea, false);
+                c.stroke();
+                r = (this.cv == this.v);
+            }
+
+            c.beginPath();
+                c.strokeStyle = r ? this.o.fgColor : this.fgColor ;
+                c.arc(this.xy, this.xy, this.radius, sat, eat, false);
+            c.stroke();
+        };
+
+        this.cancel = function () {
+            this.val(this.v);
+        };
+    };
+
+    $.fn.dial = $.fn.knob = function (o) {
+        return this.each(
+            function () {
+                var d = new k.Dial();
+                d.o = o;
+                d.$ = $(this);
+                d.run();
+            }
+        ).parent();
+    };
+
+});

+ 3054 - 0
dashboard/assets/javascripts/jquery.sparkline.js

@@ -0,0 +1,3054 @@
+/**
+ *
+ * jquery.sparkline.js
+ *
+ * v2.1.2
+ * (c) Splunk, Inc
+ * Contact: Gareth Watts (gareth@splunk.com)
+ * http://omnipotent.net/jquery.sparkline/
+ *
+ * Generates inline sparkline charts from data supplied either to the method
+ * or inline in HTML
+ *
+ * Compatible with Internet Explorer 6.0+ and modern browsers equipped with the canvas tag
+ * (Firefox 2.0+, Safari, Opera, etc)
+ *
+ * License: New BSD License
+ *
+ * Copyright (c) 2012, Splunk Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of Splunk Inc nor the names of its contributors may
+ *       be used to endorse or promote products derived from this software without
+ *       specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * Usage:
+ *  $(selector).sparkline(values, options)
+ *
+ * If values is undefined or set to 'html' then the data values are read from the specified tag:
+ *   <p>Sparkline: <span class="sparkline">1,4,6,6,8,5,3,5</span></p>
+ *   $('.sparkline').sparkline();
+ * There must be no spaces in the enclosed data set
+ *
+ * Otherwise values must be an array of numbers or null values
+ *    <p>Sparkline: <span id="sparkline1">This text replaced if the browser is compatible</span></p>
+ *    $('#sparkline1').sparkline([1,4,6,6,8,5,3,5])
+ *    $('#sparkline2').sparkline([1,4,6,null,null,5,3,5])
+ *
+ * Values can also be specified in an HTML comment, or as a values attribute:
+ *    <p>Sparkline: <span class="sparkline"><!--1,4,6,6,8,5,3,5 --></span></p>
+ *    <p>Sparkline: <span class="sparkline" values="1,4,6,6,8,5,3,5"></span></p>
+ *    $('.sparkline').sparkline();
+ *
+ * For line charts, x values can also be specified:
+ *   <p>Sparkline: <span class="sparkline">1:1,2.7:4,3.4:6,5:6,6:8,8.7:5,9:3,10:5</span></p>
+ *    $('#sparkline1').sparkline([ [1,1], [2.7,4], [3.4,6], [5,6], [6,8], [8.7,5], [9,3], [10,5] ])
+ *
+ * By default, options should be passed in as teh second argument to the sparkline function:
+ *   $('.sparkline').sparkline([1,2,3,4], {type: 'bar'})
+ *
+ * Options can also be set by passing them on the tag itself.  This feature is disabled by default though
+ * as there's a slight performance overhead:
+ *   $('.sparkline').sparkline([1,2,3,4], {enableTagOptions: true})
+ *   <p>Sparkline: <span class="sparkline" sparkType="bar" sparkBarColor="red">loading</span></p>
+ * Prefix all options supplied as tag attribute with "spark" (configurable by setting tagOptionPrefix)
+ *
+ * Supported options:
+ *   lineColor - Color of the line used for the chart
+ *   fillColor - Color used to fill in the chart - Set to '' or false for a transparent chart
+ *   width - Width of the chart - Defaults to 3 times the number of values in pixels
+ *   height - Height of the chart - Defaults to the height of the containing element
+ *   chartRangeMin - Specify the minimum value to use for the Y range of the chart - Defaults to the minimum value supplied
+ *   chartRangeMax - Specify the maximum value to use for the Y range of the chart - Defaults to the maximum value supplied
+ *   chartRangeClip - Clip out of range values to the max/min specified by chartRangeMin and chartRangeMax
+ *   chartRangeMinX - Specify the minimum value to use for the X range of the chart - Defaults to the minimum value supplied
+ *   chartRangeMaxX - Specify the maximum value to use for the X range of the chart - Defaults to the maximum value supplied
+ *   composite - If true then don't erase any existing chart attached to the tag, but draw
+ *           another chart over the top - Note that width and height are ignored if an
+ *           existing chart is detected.
+ *   tagValuesAttribute - Name of tag attribute to check for data values - Defaults to 'values'
+ *   enableTagOptions - Whether to check tags for sparkline options
+ *   tagOptionPrefix - Prefix used for options supplied as tag attributes - Defaults to 'spark'
+ *   disableHiddenCheck - If set to true, then the plugin will assume that charts will never be drawn into a
+ *           hidden dom element, avoding a browser reflow
+ *   disableInteraction - If set to true then all mouseover/click interaction behaviour will be disabled,
+ *       making the plugin perform much like it did in 1.x
+ *   disableTooltips - If set to true then tooltips will be disabled - Defaults to false (tooltips enabled)
+ *   disableHighlight - If set to true then highlighting of selected chart elements on mouseover will be disabled
+ *       defaults to false (highlights enabled)
+ *   highlightLighten - Factor to lighten/darken highlighted chart values by - Defaults to 1.4 for a 40% increase
+ *   tooltipContainer - Specify which DOM element the tooltip should be rendered into - defaults to document.body
+ *   tooltipClassname - Optional CSS classname to apply to tooltips - If not specified then a default style will be applied
+ *   tooltipOffsetX - How many pixels away from the mouse pointer to render the tooltip on the X axis
+ *   tooltipOffsetY - How many pixels away from the mouse pointer to render the tooltip on the r axis
+ *   tooltipFormatter  - Optional callback that allows you to override the HTML displayed in the tooltip
+ *       callback is given arguments of (sparkline, options, fields)
+ *   tooltipChartTitle - If specified then the tooltip uses the string specified by this setting as a title
+ *   tooltipFormat - A format string or SPFormat object  (or an array thereof for multiple entries)
+ *       to control the format of the tooltip
+ *   tooltipPrefix - A string to prepend to each field displayed in a tooltip
+ *   tooltipSuffix - A string to append to each field displayed in a tooltip
+ *   tooltipSkipNull - If true then null values will not have a tooltip displayed (defaults to true)
+ *   tooltipValueLookups - An object or range map to map field values to tooltip strings
+ *       (eg. to map -1 to "Lost", 0 to "Draw", and 1 to "Win")
+ *   numberFormatter - Optional callback for formatting numbers in tooltips
+ *   numberDigitGroupSep - Character to use for group separator in numbers "1,234" - Defaults to ","
+ *   numberDecimalMark - Character to use for the decimal point when formatting numbers - Defaults to "."
+ *   numberDigitGroupCount - Number of digits between group separator - Defaults to 3
+ *
+ * There are 7 types of sparkline, selected by supplying a "type" option of 'line' (default),
+ * 'bar', 'tristate', 'bullet', 'discrete', 'pie' or 'box'
+ *    line - Line chart.  Options:
+ *       spotColor - Set to '' to not end each line in a circular spot
+ *       minSpotColor - If set, color of spot at minimum value
+ *       maxSpotColor - If set, color of spot at maximum value
+ *       spotRadius - Radius in pixels
+ *       lineWidth - Width of line in pixels
+ *       normalRangeMin
+ *       normalRangeMax - If set draws a filled horizontal bar between these two values marking the "normal"
+ *                      or expected range of values
+ *       normalRangeColor - Color to use for the above bar
+ *       drawNormalOnTop - Draw the normal range above the chart fill color if true
+ *       defaultPixelsPerValue - Defaults to 3 pixels of width for each value in the chart
+ *       highlightSpotColor - The color to use for drawing a highlight spot on mouseover - Set to null to disable
+ *       highlightLineColor - The color to use for drawing a highlight line on mouseover - Set to null to disable
+ *       valueSpots - Specify which points to draw spots on, and in which color.  Accepts a range map
+ *
+ *   bar - Bar chart.  Options:
+ *       barColor - Color of bars for postive values
+ *       negBarColor - Color of bars for negative values
+ *       zeroColor - Color of bars with zero values
+ *       nullColor - Color of bars with null values - Defaults to omitting the bar entirely
+ *       barWidth - Width of bars in pixels
+ *       colorMap - Optional mappnig of values to colors to override the *BarColor values above
+ *                  can be an Array of values to control the color of individual bars or a range map
+ *                  to specify colors for individual ranges of values
+ *       barSpacing - Gap between bars in pixels
+ *       zeroAxis - Centers the y-axis around zero if true
+ *
+ *   tristate - Charts values of win (>0), lose (<0) or draw (=0)
+ *       posBarColor - Color of win values
+ *       negBarColor - Color of lose values
+ *       zeroBarColor - Color of draw values
+ *       barWidth - Width of bars in pixels
+ *       barSpacing - Gap between bars in pixels
+ *       colorMap - Optional mappnig of values to colors to override the *BarColor values above
+ *                  can be an Array of values to control the color of individual bars or a range map
+ *                  to specify colors for individual ranges of values
+ *
+ *   discrete - Options:
+ *       lineHeight - Height of each line in pixels - Defaults to 30% of the graph height
+ *       thesholdValue - Values less than this value will be drawn using thresholdColor instead of lineColor
+ *       thresholdColor
+ *
+ *   bullet - Values for bullet graphs msut be in the order: target, performance, range1, range2, range3, ...
+ *       options:
+ *       targetColor - The color of the vertical target marker
+ *       targetWidth - The width of the target marker in pixels
+ *       performanceColor - The color of the performance measure horizontal bar
+ *       rangeColors - Colors to use for each qualitative range background color
+ *
+ *   pie - Pie chart. Options:
+ *       sliceColors - An array of colors to use for pie slices
+ *       offset - Angle in degrees to offset the first slice - Try -90 or +90
+ *       borderWidth - Width of border to draw around the pie chart, in pixels - Defaults to 0 (no border)
+ *       borderColor - Color to use for the pie chart border - Defaults to #000
+ *
+ *   box - Box plot. Options:
+ *       raw - Set to true to supply pre-computed plot points as values
+ *             values should be: low_outlier, low_whisker, q1, median, q3, high_whisker, high_outlier
+ *             When set to false you can supply any number of values and the box plot will
+ *             be computed for you.  Default is false.
+ *       showOutliers - Set to true (default) to display outliers as circles
+ *       outlierIQR - Interquartile range used to determine outliers.  Default 1.5
+ *       boxLineColor - Outline color of the box
+ *       boxFillColor - Fill color for the box
+ *       whiskerColor - Line color used for whiskers
+ *       outlierLineColor - Outline color of outlier circles
+ *       outlierFillColor - Fill color of the outlier circles
+ *       spotRadius - Radius of outlier circles
+ *       medianColor - Line color of the median line
+ *       target - Draw a target cross hair at the supplied value (default undefined)
+ *
+ *
+ *
+ *   Examples:
+ *   $('#sparkline1').sparkline(myvalues, { lineColor: '#f00', fillColor: false });
+ *   $('.barsparks').sparkline('html', { type:'bar', height:'40px', barWidth:5 });
+ *   $('#tristate').sparkline([1,1,-1,1,0,0,-1], { type:'tristate' }):
+ *   $('#discrete').sparkline([1,3,4,5,5,3,4,5], { type:'discrete' });
+ *   $('#bullet').sparkline([10,12,12,9,7], { type:'bullet' });
+ *   $('#pie').sparkline([1,1,2], { type:'pie' });
+ */
+
+/*jslint regexp: true, browser: true, jquery: true, white: true, nomen: false, plusplus: false, maxerr: 500, indent: 4 */
+
+(function(document, Math, undefined) { // performance/minified-size optimization
+    (function(factory) {
+        if(typeof define === 'function' && define.amd) {
+            define(['jquery'], factory);
+        } else if (jQuery && !jQuery.fn.sparkline) {
+            factory(jQuery);
+        }
+    }
+    (function($) {
+        'use strict';
+
+        var UNSET_OPTION = {},
+            getDefaults, createClass, SPFormat, clipval, quartile, normalizeValue, normalizeValues,
+            remove, isNumber, all, sum, addCSS, ensureArray, formatNumber, RangeMap,
+            MouseHandler, Tooltip, barHighlightMixin,
+            line, bar, tristate, discrete, bullet, pie, box, defaultStyles, initStyles,
+            VShape, VCanvas_base, VCanvas_canvas, VCanvas_vml, pending, shapeCount = 0;
+
+        /**
+         * Default configuration settings
+         */
+        getDefaults = function () {
+            return {
+                // Settings common to most/all chart types
+                common: {
+                    type: 'line',
+                    lineColor: '#00f',
+                    fillColor: '#cdf',
+                    defaultPixelsPerValue: 3,
+                    width: 'auto',
+                    height: 'auto',
+                    composite: false,
+                    tagValuesAttribute: 'values',
+                    tagOptionsPrefix: 'spark',
+                    enableTagOptions: false,
+                    enableHighlight: true,
+                    highlightLighten: 1.4,
+                    tooltipSkipNull: true,
+                    tooltipPrefix: '',
+                    tooltipSuffix: '',
+                    disableHiddenCheck: false,
+                    numberFormatter: false,
+                    numberDigitGroupCount: 3,
+                    numberDigitGroupSep: ',',
+                    numberDecimalMark: '.',
+                    disableTooltips: false,
+                    disableInteraction: false
+                },
+                // Defaults for line charts
+                line: {
+                    spotColor: '#f80',
+                    highlightSpotColor: '#5f5',
+                    highlightLineColor: '#f22',
+                    spotRadius: 1.5,
+                    minSpotColor: '#f80',
+                    maxSpotColor: '#f80',
+                    lineWidth: 1,
+                    normalRangeMin: undefined,
+                    normalRangeMax: undefined,
+                    normalRangeColor: '#ccc',
+                    drawNormalOnTop: false,
+                    chartRangeMin: undefined,
+                    chartRangeMax: undefined,
+                    chartRangeMinX: undefined,
+                    chartRangeMaxX: undefined,
+                    tooltipFormat: new SPFormat('<span style="color: {{color}}">&#9679;</span> {{prefix}}{{y}}{{suffix}}')
+                },
+                // Defaults for bar charts
+                bar: {
+                    barColor: '#3366cc',
+                    negBarColor: '#f44',
+                    stackedBarColor: ['#3366cc', '#dc3912', '#ff9900', '#109618', '#66aa00',
+                        '#dd4477', '#0099c6', '#990099'],
+                    zeroColor: undefined,
+                    nullColor: undefined,
+                    zeroAxis: true,
+                    barWidth: 4,
+                    barSpacing: 1,
+                    chartRangeMax: undefined,
+                    chartRangeMin: undefined,
+                    chartRangeClip: false,
+                    colorMap: undefined,
+                    tooltipFormat: new SPFormat('<span style="color: {{color}}">&#9679;</span> {{prefix}}{{value}}{{suffix}}')
+                },
+                // Defaults for tristate charts
+                tristate: {
+                    barWidth: 4,
+                    barSpacing: 1,
+                    posBarColor: '#6f6',
+                    negBarColor: '#f44',
+                    zeroBarColor: '#999',
+                    colorMap: {},
+                    tooltipFormat: new SPFormat('<span style="color: {{color}}">&#9679;</span> {{value:map}}'),
+                    tooltipValueLookups: { map: { '-1': 'Loss', '0': 'Draw', '1': 'Win' } }
+                },
+                // Defaults for discrete charts
+                discrete: {
+                    lineHeight: 'auto',
+                    thresholdColor: undefined,
+                    thresholdValue: 0,
+                    chartRangeMax: undefined,
+                    chartRangeMin: undefined,
+                    chartRangeClip: false,
+                    tooltipFormat: new SPFormat('{{prefix}}{{value}}{{suffix}}')
+                },
+                // Defaults for bullet charts
+                bullet: {
+                    targetColor: '#f33',
+                    targetWidth: 3, // width of the target bar in pixels
+                    performanceColor: '#33f',
+                    rangeColors: ['#d3dafe', '#a8b6ff', '#7f94ff'],
+                    base: undefined, // set this to a number to change the base start number
+                    tooltipFormat: new SPFormat('{{fieldkey:fields}} - {{value}}'),
+                    tooltipValueLookups: { fields: {r: 'Range', p: 'Performance', t: 'Target'} }
+                },
+                // Defaults for pie charts
+                pie: {
+                    offset: 0,
+                    sliceColors: ['#3366cc', '#dc3912', '#ff9900', '#109618', '#66aa00',
+                        '#dd4477', '#0099c6', '#990099'],
+                    borderWidth: 0,
+                    borderColor: '#000',
+                    tooltipFormat: new SPFormat('<span style="color: {{color}}">&#9679;</span> {{value}} ({{percent.1}}%)')
+                },
+                // Defaults for box plots
+                box: {
+                    raw: false,
+                    boxLineColor: '#000',
+                    boxFillColor: '#cdf',
+                    whiskerColor: '#000',
+                    outlierLineColor: '#333',
+                    outlierFillColor: '#fff',
+                    medianColor: '#f00',
+                    showOutliers: true,
+                    outlierIQR: 1.5,
+                    spotRadius: 1.5,
+                    target: undefined,
+                    targetColor: '#4a2',
+                    chartRangeMax: undefined,
+                    chartRangeMin: undefined,
+                    tooltipFormat: new SPFormat('{{field:fields}}: {{value}}'),
+                    tooltipFormatFieldlistKey: 'field',
+                    tooltipValueLookups: { fields: { lq: 'Lower Quartile', med: 'Median',
+                        uq: 'Upper Quartile', lo: 'Left Outlier', ro: 'Right Outlier',
+                        lw: 'Left Whisker', rw: 'Right Whisker'} }
+                }
+            };
+        };
+
+        // You can have tooltips use a css class other than jqstooltip by specifying tooltipClassname
+        defaultStyles = '.jqstooltip { ' +
+            'position: absolute;' +
+            'left: 0px;' +
+            'top: 0px;' +
+            'visibility: hidden;' +
+            'background: rgb(0, 0, 0) transparent;' +
+            'background-color: rgba(0,0,0,0.6);' +
+            'filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000);' +
+            '-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#99000000, endColorstr=#99000000)";' +
+            'color: white;' +
+            'font: 10px arial, san serif;' +
+            'text-align: left;' +
+            'white-space: nowrap;' +
+            'padding: 5px;' +
+            'border: 1px solid white;' +
+            'z-index: 10000;' +
+            '}' +
+            '.jqsfield { ' +
+            'color: white;' +
+            'font: 10px arial, san serif;' +
+            'text-align: left;' +
+            '}';
+
+        /**
+         * Utilities
+         */
+
+        createClass = function (/* [baseclass, [mixin, ...]], definition */) {
+            var Class, args;
+            Class = function () {
+                this.init.apply(this, arguments);
+            };
+            if (arguments.length > 1) {
+                if (arguments[0]) {
+                    Class.prototype = $.extend(new arguments[0](), arguments[arguments.length - 1]);
+                    Class._super = arguments[0].prototype;
+                } else {
+                    Class.prototype = arguments[arguments.length - 1];
+                }
+                if (arguments.length > 2) {
+                    args = Array.prototype.slice.call(arguments, 1, -1);
+                    args.unshift(Class.prototype);
+                    $.extend.apply($, args);
+                }
+            } else {
+                Class.prototype = arguments[0];
+            }
+            Class.prototype.cls = Class;
+            return Class;
+        };
+
+        /**
+         * Wraps a format string for tooltips
+         * {{x}}
+         * {{x.2}
+     * {{x:months}}
+     */
+        $.SPFormatClass = SPFormat = createClass({
+            fre: /\{\{([\w.]+?)(:(.+?))?\}\}/g,
+            precre: /(\w+)\.(\d+)/,
+
+            init: function (format, fclass) {
+                this.format = format;
+                this.fclass = fclass;
+            },
+
+            render: function (fieldset, lookups, options) {
+                var self = this,
+                    fields = fieldset,
+                    match, token, lookupkey, fieldvalue, prec;
+                return this.format.replace(this.fre, function () {
+                    var lookup;
+                    token = arguments[1];
+                    lookupkey = arguments[3];
+                    match = self.precre.exec(token);
+                    if (match) {
+                        prec = match[2];
+                        token = match[1];
+                    } else {
+                        prec = false;
+                    }
+                    fieldvalue = fields[token];
+                    if (fieldvalue === undefined) {
+                        return '';
+                    }
+                    if (lookupkey && lookups && lookups[lookupkey]) {
+                        lookup = lookups[lookupkey];
+                        if (lookup.get) { // RangeMap
+                            return lookups[lookupkey].get(fieldvalue) || fieldvalue;
+                        } else {
+                            return lookups[lookupkey][fieldvalue] || fieldvalue;
+                        }
+                    }
+                    if (isNumber(fieldvalue)) {
+                        if (options.get('numberFormatter')) {
+                            fieldvalue = options.get('numberFormatter')(fieldvalue);
+                        } else {
+                            fieldvalue = formatNumber(fieldvalue, prec,
+                                options.get('numberDigitGroupCount'),
+                                options.get('numberDigitGroupSep'),
+                                options.get('numberDecimalMark'));
+                        }
+                    }
+                    return fieldvalue;
+                });
+            }
+        });
+
+        // convience method to avoid needing the new operator
+        $.spformat = function(format, fclass) {
+            return new SPFormat(format, fclass);
+        };
+
+        clipval = function (val, min, max) {
+            if (val < min) {
+                return min;
+            }
+            if (val > max) {
+                return max;
+            }
+            return val;
+        };
+
+        quartile = function (values, q) {
+            var vl;
+            if (q === 2) {
+                vl = Math.floor(values.length / 2);
+                return values.length % 2 ? values[vl] : (values[vl-1] + values[vl]) / 2;
+            } else {
+                if (values.length % 2 ) { // odd
+                    vl = (values.length * q + q) / 4;
+                    return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 : values[vl-1];
+                } else { //even
+                    vl = (values.length * q + 2) / 4;
+                    return vl % 1 ? (values[Math.floor(vl)] + values[Math.floor(vl) - 1]) / 2 :  values[vl-1];
+
+                }
+            }
+        };
+
+        normalizeValue = function (val) {
+            var nf;
+            switch (val) {
+                case 'undefined':
+                    val = undefined;
+                    break;
+                case 'null':
+                    val = null;
+                    break;
+                case 'true':
+                    val = true;
+                    break;
+                case 'false':
+                    val = false;
+                    break;
+                default:
+                    nf = parseFloat(val);
+                    if (val == nf) {
+                        val = nf;
+                    }
+            }
+            return val;
+        };
+
+        normalizeValues = function (vals) {
+            var i, result = [];
+            for (i = vals.length; i--;) {
+                result[i] = normalizeValue(vals[i]);
+            }
+            return result;
+        };
+
+        remove = function (vals, filter) {
+            var i, vl, result = [];
+            for (i = 0, vl = vals.length; i < vl; i++) {
+                if (vals[i] !== filter) {
+                    result.push(vals[i]);
+                }
+            }
+            return result;
+        };
+
+        isNumber = function (num) {
+            return !isNaN(parseFloat(num)) && isFinite(num);
+        };
+
+        formatNumber = function (num, prec, groupsize, groupsep, decsep) {
+            var p, i;
+            num = (prec === false ? parseFloat(num).toString() : num.toFixed(prec)).split('');
+            p = (p = $.inArray('.', num)) < 0 ? num.length : p;
+            if (p < num.length) {
+                num[p] = decsep;
+            }
+            for (i = p - groupsize; i > 0; i -= groupsize) {
+                num.splice(i, 0, groupsep);
+            }
+            return num.join('');
+        };
+
+        // determine if all values of an array match a value
+        // returns true if the array is empty
+        all = function (val, arr, ignoreNull) {
+            var i;
+            for (i = arr.length; i--; ) {
+                if (ignoreNull && arr[i] === null) continue;
+                if (arr[i] !== val) {
+                    return false;
+                }
+            }
+            return true;
+        };
+
+        // sums the numeric values in an array, ignoring other values
+        sum = function (vals) {
+            var total = 0, i;
+            for (i = vals.length; i--;) {
+                total += typeof vals[i] === 'number' ? vals[i] : 0;
+            }
+            return total;
+        };
+
+        ensureArray = function (val) {
+            return $.isArray(val) ? val : [val];
+        };
+
+        // http://paulirish.com/2008/bookmarklet-inject-new-css-rules/
+        addCSS = function(css) {
+            var tag;
+            //if ('\v' == 'v') /* ie only */ {
+            if (document.createStyleSheet) {
+                document.createStyleSheet().cssText = css;
+            } else {
+                tag = document.createElement('style');
+                tag.type = 'text/css';
+                document.getElementsByTagName('head')[0].appendChild(tag);
+                tag[(typeof document.body.style.WebkitAppearance == 'string') /* webkit only */ ? 'innerText' : 'innerHTML'] = css;
+            }
+        };
+
+        // Provide a cross-browser interface to a few simple drawing primitives
+        $.fn.simpledraw = function (width, height, useExisting, interact) {
+            var target, mhandler;
+            if (useExisting && (target = this.data('_jqs_vcanvas'))) {
+                return target;
+            }
+
+            if ($.fn.sparkline.canvas === false) {
+                // We've already determined that neither Canvas nor VML are available
+                return false;
+
+            } else if ($.fn.sparkline.canvas === undefined) {
+                // No function defined yet -- need to see if we support Canvas or VML
+                var el = document.createElement('canvas');
+                if (!!(el.getContext && el.getContext('2d'))) {
+                    // Canvas is available
+                    $.fn.sparkline.canvas = function(width, height, target, interact) {
+                        return new VCanvas_canvas(width, height, target, interact);
+                    };
+                } else if (document.namespaces && !document.namespaces.v) {
+                    // VML is available
+                    document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', '#default#VML');
+                    $.fn.sparkline.canvas = function(width, height, target, interact) {
+                        return new VCanvas_vml(width, height, target);
+                    };
+                } else {
+                    // Neither Canvas nor VML are available
+                    $.fn.sparkline.canvas = false;
+                    return false;
+                }
+            }
+
+            if (width === undefined) {
+                width = $(this).innerWidth();
+            }
+            if (height === undefined) {
+                height = $(this).innerHeight();
+            }
+
+            target = $.fn.sparkline.canvas(width, height, this, interact);
+
+            mhandler = $(this).data('_jqs_mhandler');
+            if (mhandler) {
+                mhandler.registerCanvas(target);
+            }
+            return target;
+        };
+
+        $.fn.cleardraw = function () {
+            var target = this.data('_jqs_vcanvas');
+            if (target) {
+                target.reset();
+            }
+        };
+
+        $.RangeMapClass = RangeMap = createClass({
+            init: function (map) {
+                var key, range, rangelist = [];
+                for (key in map) {
+                    if (map.hasOwnProperty(key) && typeof key === 'string' && key.indexOf(':') > -1) {
+                        range = key.split(':');
+                        range[0] = range[0].length === 0 ? -Infinity : parseFloat(range[0]);
+                        range[1] = range[1].length === 0 ? Infinity : parseFloat(range[1]);
+                        range[2] = map[key];
+                        rangelist.push(range);
+                    }
+                }
+                this.map = map;
+                this.rangelist = rangelist || false;
+            },
+
+            get: function (value) {
+                var rangelist = this.rangelist,
+                    i, range, result;
+                if ((result = this.map[value]) !== undefined) {
+                    return result;
+                }
+                if (rangelist) {
+                    for (i = rangelist.length; i--;) {
+                        range = rangelist[i];
+                        if (range[0] <= value && range[1] >= value) {
+                            return range[2];
+                        }
+                    }
+                }
+                return undefined;
+            }
+        });
+
+        // Convenience function
+        $.range_map = function(map) {
+            return new RangeMap(map);
+        };
+
+        MouseHandler = createClass({
+            init: function (el, options) {
+                var $el = $(el);
+                this.$el = $el;
+                this.options = options;
+                this.currentPageX = 0;
+                this.currentPageY = 0;
+                this.el = el;
+                this.splist = [];
+                this.tooltip = null;
+                this.over = false;
+                this.displayTooltips = !options.get('disableTooltips');
+                this.highlightEnabled = !options.get('disableHighlight');
+            },
+
+            registerSparkline: function (sp) {
+                this.splist.push(sp);
+                if (this.over) {
+                    this.updateDisplay();
+                }
+            },
+
+            registerCanvas: function (canvas) {
+                var $canvas = $(canvas.canvas);
+                this.canvas = canvas;
+                this.$canvas = $canvas;
+                $canvas.mouseenter($.proxy(this.mouseenter, this));
+                $canvas.mouseleave($.proxy(this.mouseleave, this));
+                $canvas.click($.proxy(this.mouseclick, this));
+            },
+
+            reset: function (removeTooltip) {
+                this.splist = [];
+                if (this.tooltip && removeTooltip) {
+                    this.tooltip.remove();
+                    this.tooltip = undefined;
+                }
+            },
+
+            mouseclick: function (e) {
+                var clickEvent = $.Event('sparklineClick');
+                clickEvent.originalEvent = e;
+                clickEvent.sparklines = this.splist;
+                this.$el.trigger(clickEvent);
+            },
+
+            mouseenter: function (e) {
+                $(document.body).unbind('mousemove.jqs');
+                $(document.body).bind('mousemove.jqs', $.proxy(this.mousemove, this));
+                this.over = true;
+                this.currentPageX = e.pageX;
+                this.currentPageY = e.pageY;
+                this.currentEl = e.target;
+                if (!this.tooltip && this.displayTooltips) {
+                    this.tooltip = new Tooltip(this.options);
+                    this.tooltip.updatePosition(e.pageX, e.pageY);
+                }
+                this.updateDisplay();
+            },
+
+            mouseleave: function () {
+                $(document.body).unbind('mousemove.jqs');
+                var splist = this.splist,
+                    spcount = splist.length,
+                    needsRefresh = false,
+                    sp, i;
+                this.over = false;
+                this.currentEl = null;
+
+                if (this.tooltip) {
+                    this.tooltip.remove();
+                    this.tooltip = null;
+                }
+
+                for (i = 0; i < spcount; i++) {
+                    sp = splist[i];
+                    if (sp.clearRegionHighlight()) {
+                        needsRefresh = true;
+                    }
+                }
+
+                if (needsRefresh) {
+                    this.canvas.render();
+                }
+            },
+
+            mousemove: function (e) {
+                this.currentPageX = e.pageX;
+                this.currentPageY = e.pageY;
+                this.currentEl = e.target;
+                if (this.tooltip) {
+                    this.tooltip.updatePosition(e.pageX, e.pageY);
+                }
+                this.updateDisplay();
+            },
+
+            updateDisplay: function () {
+                var splist = this.splist,
+                    spcount = splist.length,
+                    needsRefresh = false,
+                    offset = this.$canvas.offset(),
+                    localX = this.currentPageX - offset.left,
+                    localY = this.currentPageY - offset.top,
+                    tooltiphtml, sp, i, result, changeEvent;
+                if (!this.over) {
+                    return;
+                }
+                for (i = 0; i < spcount; i++) {
+                    sp = splist[i];
+                    result = sp.setRegionHighlight(this.currentEl, localX, localY);
+                    if (result) {
+                        needsRefresh = true;
+                    }
+                }
+                if (needsRefresh) {
+                    changeEvent = $.Event('sparklineRegionChange');
+                    changeEvent.sparklines = this.splist;
+                    this.$el.trigger(changeEvent);
+                    if (this.tooltip) {
+                        tooltiphtml = '';
+                        for (i = 0; i < spcount; i++) {
+                            sp = splist[i];
+                            tooltiphtml += sp.getCurrentRegionTooltip();
+                        }
+                        this.tooltip.setContent(tooltiphtml);
+                    }
+                    if (!this.disableHighlight) {
+                        this.canvas.render();
+                    }
+                }
+                if (result === null) {
+                    this.mouseleave();
+                }
+            }
+        });
+
+
+        Tooltip = createClass({
+            sizeStyle: 'position: static !important;' +
+            'display: block !important;' +
+            'visibility: hidden !important;' +
+            'float: left !important;',
+
+            init: function (options) {
+                var tooltipClassname = options.get('tooltipClassname', 'jqstooltip'),
+                    sizetipStyle = this.sizeStyle,
+                    offset;
+                this.container = options.get('tooltipContainer') || document.body;
+                this.tooltipOffsetX = options.get('tooltipOffsetX', 10);
+                this.tooltipOffsetY = options.get('tooltipOffsetY', 12);
+                // remove any previous lingering tooltip
+                $('#jqssizetip').remove();
+                $('#jqstooltip').remove();
+                this.sizetip = $('<div/>', {
+                    id: 'jqssizetip',
+                    style: sizetipStyle,
+                    'class': tooltipClassname
+                });
+                this.tooltip = $('<div/>', {
+                    id: 'jqstooltip',
+                    'class': tooltipClassname
+                }).appendTo(this.container);
+                // account for the container's location
+                offset = this.tooltip.offset();
+                this.offsetLeft = offset.left;
+                this.offsetTop = offset.top;
+                this.hidden = true;
+                $(window).unbind('resize.jqs scroll.jqs');
+                $(window).bind('resize.jqs scroll.jqs', $.proxy(this.updateWindowDims, this));
+                this.updateWindowDims();
+            },
+
+            updateWindowDims: function () {
+                this.scrollTop = $(window).scrollTop();
+                this.scrollLeft = $(window).scrollLeft();
+                this.scrollRight = this.scrollLeft + $(window).width();
+                this.updatePosition();
+            },
+
+            getSize: function (content) {
+                this.sizetip.html(content).appendTo(this.container);
+                this.width = this.sizetip.width() + 1;
+                this.height = this.sizetip.height();
+                this.sizetip.remove();
+            },
+
+            setContent: function (content) {
+                if (!content) {
+                    this.tooltip.css('visibility', 'hidden');
+                    this.hidden = true;
+                    return;
+                }
+                this.getSize(content);
+                this.tooltip.html(content)
+                    .css({
+                        'width': this.width,
+                        'height': this.height,
+                        'visibility': 'visible'
+                    });
+                if (this.hidden) {
+                    this.hidden = false;
+                    this.updatePosition();
+                }
+            },
+
+            updatePosition: function (x, y) {
+                if (x === undefined) {
+                    if (this.mousex === undefined) {
+                        return;
+                    }
+                    x = this.mousex - this.offsetLeft;
+                    y = this.mousey - this.offsetTop;
+
+                } else {
+                    this.mousex = x = x - this.offsetLeft;
+                    this.mousey = y = y - this.offsetTop;
+                }
+                if (!this.height || !this.width || this.hidden) {
+                    return;
+                }
+
+                y -= this.height + this.tooltipOffsetY;
+                x += this.tooltipOffsetX;
+
+                if (y < this.scrollTop) {
+                    y = this.scrollTop;
+                }
+                if (x < this.scrollLeft) {
+                    x = this.scrollLeft;
+                } else if (x + this.width > this.scrollRight) {
+                    x = this.scrollRight - this.width;
+                }
+
+                this.tooltip.css({
+                    'left': x,
+                    'top': y
+                });
+            },
+
+            remove: function () {
+                this.tooltip.remove();
+                this.sizetip.remove();
+                this.sizetip = this.tooltip = undefined;
+                $(window).unbind('resize.jqs scroll.jqs');
+            }
+        });
+
+        initStyles = function() {
+            addCSS(defaultStyles);
+        };
+
+        $(initStyles);
+
+        pending = [];
+        $.fn.sparkline = function (userValues, userOptions) {
+            return this.each(function () {
+                var options = new $.fn.sparkline.options(this, userOptions),
+                    $this = $(this),
+                    render, i;
+                render = function () {
+                    var values, width, height, tmp, mhandler, sp, vals;
+                    if (userValues === 'html' || userValues === undefined) {
+                        vals = this.getAttribute(options.get('tagValuesAttribute'));
+                        if (vals === undefined || vals === null) {
+                            vals = $this.html();
+                        }
+                        values = vals.replace(/(^\s*<!--)|(-->\s*$)|\s+/g, '').split(',');
+                    } else {
+                        values = userValues;
+                    }
+
+                    width = options.get('width') === 'auto' ? values.length * options.get('defaultPixelsPerValue') : options.get('width');
+                    if (options.get('height') === 'auto') {
+                        if (!options.get('composite') || !$.data(this, '_jqs_vcanvas')) {
+                            // must be a better way to get the line height
+                            tmp = document.createElement('span');
+                            tmp.innerHTML = 'a';
+                            $this.html(tmp);
+                            height = $(tmp).innerHeight() || $(tmp).height();
+                            $(tmp).remove();
+                            tmp = null;
+                        }
+                    } else {
+                        height = options.get('height');
+                    }
+
+                    if (!options.get('disableInteraction')) {
+                        mhandler = $.data(this, '_jqs_mhandler');
+                        if (!mhandler) {
+                            mhandler = new MouseHandler(this, options);
+                            $.data(this, '_jqs_mhandler', mhandler);
+                        } else if (!options.get('composite')) {
+                            mhandler.reset();
+                        }
+                    } else {
+                        mhandler = false;
+                    }
+
+                    if (options.get('composite') && !$.data(this, '_jqs_vcanvas')) {
+                        if (!$.data(this, '_jqs_errnotify')) {
+                            alert('Attempted to attach a composite sparkline to an element with no existing sparkline');
+                            $.data(this, '_jqs_errnotify', true);
+                        }
+                        return;
+                    }
+
+                    sp = new $.fn.sparkline[options.get('type')](this, values, options, width, height);
+
+                    sp.render();
+
+                    if (mhandler) {
+                        mhandler.registerSparkline(sp);
+                    }
+                };
+                if (($(this).html() && !options.get('disableHiddenCheck') && $(this).is(':hidden')) || !$(this).parents('body').length) {
+                    if (!options.get('composite') && $.data(this, '_jqs_pending')) {
+                        // remove any existing references to the element
+                        for (i = pending.length; i; i--) {
+                            if (pending[i - 1][0] == this) {
+                                pending.splice(i - 1, 1);
+                            }
+                        }
+                    }
+                    pending.push([this, render]);
+                    $.data(this, '_jqs_pending', true);
+                } else {
+                    render.call(this);
+                }
+            });
+        };
+
+        $.fn.sparkline.defaults = getDefaults();
+
+
+        $.sparkline_display_visible = function () {
+            var el, i, pl;
+            var done = [];
+            for (i = 0, pl = pending.length; i < pl; i++) {
+                el = pending[i][0];
+                if ($(el).is(':visible') && !$(el).parents().is(':hidden')) {
+                    pending[i][1].call(el);
+                    $.data(pending[i][0], '_jqs_pending', false);
+                    done.push(i);
+                } else if (!$(el).closest('html').length && !$.data(el, '_jqs_pending')) {
+                    // element has been inserted and removed from the DOM
+                    // If it was not yet inserted into the dom then the .data request
+                    // will return true.
+                    // removing from the dom causes the data to be removed.
+                    $.data(pending[i][0], '_jqs_pending', false);
+                    done.push(i);
+                }
+            }
+            for (i = done.length; i; i--) {
+                pending.splice(done[i - 1], 1);
+            }
+        };
+
+
+        /**
+         * User option handler
+         */
+        $.fn.sparkline.options = createClass({
+            init: function (tag, userOptions) {
+                var extendedOptions, defaults, base, tagOptionType;
+                this.userOptions = userOptions = userOptions || {};
+                this.tag = tag;
+                this.tagValCache = {};
+                defaults = $.fn.sparkline.defaults;
+                base = defaults.common;
+                this.tagOptionsPrefix = userOptions.enableTagOptions && (userOptions.tagOptionsPrefix || base.tagOptionsPrefix);
+
+                tagOptionType = this.getTagSetting('type');
+                if (tagOptionType === UNSET_OPTION) {
+                    extendedOptions = defaults[userOptions.type || base.type];
+                } else {
+                    extendedOptions = defaults[tagOptionType];
+                }
+                this.mergedOptions = $.extend({}, base, extendedOptions, userOptions);
+            },
+
+
+            getTagSetting: function (key) {
+                var prefix = this.tagOptionsPrefix,
+                    val, i, pairs, keyval;
+                if (prefix === false || prefix === undefined) {
+                    return UNSET_OPTION;
+                }
+                if (this.tagValCache.hasOwnProperty(key)) {
+                    val = this.tagValCache.key;
+                } else {
+                    val = this.tag.getAttribute(prefix + key);
+                    if (val === undefined || val === null) {
+                        val = UNSET_OPTION;
+                    } else if (val.substr(0, 1) === '[') {
+                        val = val.substr(1, val.length - 2).split(',');
+                        for (i = val.length; i--;) {
+                            val[i] = normalizeValue(val[i].replace(/(^\s*)|(\s*$)/g, ''));
+                        }
+                    } else if (val.substr(0, 1) === '{') {
+                        pairs = val.substr(1, val.length - 2).split(',');
+                        val = {};
+                        for (i = pairs.length; i--;) {
+                            keyval = pairs[i].split(':', 2);
+                            val[keyval[0].replace(/(^\s*)|(\s*$)/g, '')] = normalizeValue(keyval[1].replace(/(^\s*)|(\s*$)/g, ''));
+                        }
+                    } else {
+                        val = normalizeValue(val);
+                    }
+                    this.tagValCache.key = val;
+                }
+                return val;
+            },
+
+            get: function (key, defaultval) {
+                var tagOption = this.getTagSetting(key),
+                    result;
+                if (tagOption !== UNSET_OPTION) {
+                    return tagOption;
+                }
+                return (result = this.mergedOptions[key]) === undefined ? defaultval : result;
+            }
+        });
+
+
+        $.fn.sparkline._base = createClass({
+            disabled: false,
+
+            init: function (el, values, options, width, height) {
+                this.el = el;
+                this.$el = $(el);
+                this.values = values;
+                this.options = options;
+                this.width = width;
+                this.height = height;
+                this.currentRegion = undefined;
+            },
+
+            /**
+             * Setup the canvas
+             */
+            initTarget: function () {
+                var interactive = !this.options.get('disableInteraction');
+                if (!(this.target = this.$el.simpledraw(this.width, this.height, this.options.get('composite'), interactive))) {
+                    this.disabled = true;
+                } else {
+                    this.canvasWidth = this.target.pixelWidth;
+                    this.canvasHeight = this.target.pixelHeight;
+                }
+            },
+
+            /**
+             * Actually render the chart to the canvas
+             */
+            render: function () {
+                if (this.disabled) {
+                    this.el.innerHTML = '';
+                    return false;
+                }
+                return true;
+            },
+
+            /**
+             * Return a region id for a given x/y co-ordinate
+             */
+            getRegion: function (x, y) {
+            },
+
+            /**
+             * Highlight an item based on the moused-over x,y co-ordinate
+             */
+            setRegionHighlight: function (el, x, y) {
+                var currentRegion = this.currentRegion,
+                    highlightEnabled = !this.options.get('disableHighlight'),
+                    newRegion;
+                if (x > this.canvasWidth || y > this.canvasHeight || x < 0 || y < 0) {
+                    return null;
+                }
+                newRegion = this.getRegion(el, x, y);
+                if (currentRegion !== newRegion) {
+                    if (currentRegion !== undefined && highlightEnabled) {
+                        this.removeHighlight();
+                    }
+                    this.currentRegion = newRegion;
+                    if (newRegion !== undefined && highlightEnabled) {
+                        this.renderHighlight();
+                    }
+                    return true;
+                }
+                return false;
+            },
+
+            /**
+             * Reset any currently highlighted item
+             */
+            clearRegionHighlight: function () {
+                if (this.currentRegion !== undefined) {
+                    this.removeHighlight();
+                    this.currentRegion = undefined;
+                    return true;
+                }
+                return false;
+            },
+
+            renderHighlight: function () {
+                this.changeHighlight(true);
+            },
+
+            removeHighlight: function () {
+                this.changeHighlight(false);
+            },
+
+            changeHighlight: function (highlight)  {},
+
+            /**
+             * Fetch the HTML to display as a tooltip
+             */
+            getCurrentRegionTooltip: function () {
+                var options = this.options,
+                    header = '',
+                    entries = [],
+                    fields, formats, formatlen, fclass, text, i,
+                    showFields, showFieldsKey, newFields, fv,
+                    formatter, format, fieldlen, j;
+                if (this.currentRegion === undefined) {
+                    return '';
+                }
+                fields = this.getCurrentRegionFields();
+                formatter = options.get('tooltipFormatter');
+                if (formatter) {
+                    return formatter(this, options, fields);
+                }
+                if (options.get('tooltipChartTitle')) {
+                    header += '<div class="jqs jqstitle">' + options.get('tooltipChartTitle') + '</div>\n';
+                }
+                formats = this.options.get('tooltipFormat');
+                if (!formats) {
+                    return '';
+                }
+                if (!$.isArray(formats)) {
+                    formats = [formats];
+                }
+                if (!$.isArray(fields)) {
+                    fields = [fields];
+                }
+                showFields = this.options.get('tooltipFormatFieldlist');
+                showFieldsKey = this.options.get('tooltipFormatFieldlistKey');
+                if (showFields && showFieldsKey) {
+                    // user-selected ordering of fields
+                    newFields = [];
+                    for (i = fields.length; i--;) {
+                        fv = fields[i][showFieldsKey];
+                        if ((j = $.inArray(fv, showFields)) != -1) {
+                            newFields[j] = fields[i];
+                        }
+                    }
+                    fields = newFields;
+                }
+                formatlen = formats.length;
+                fieldlen = fields.length;
+                for (i = 0; i < formatlen; i++) {
+                    format = formats[i];
+                    if (typeof format === 'string') {
+                        format = new SPFormat(format);
+                    }
+                    fclass = format.fclass || 'jqsfield';
+                    for (j = 0; j < fieldlen; j++) {
+                        if (!fields[j].isNull || !options.get('tooltipSkipNull')) {
+                            $.extend(fields[j], {
+                                prefix: options.get('tooltipPrefix'),
+                                suffix: options.get('tooltipSuffix')
+                            });
+                            text = format.render(fields[j], options.get('tooltipValueLookups'), options);
+                            entries.push('<div class="' + fclass + '">' + text + '</div>');
+                        }
+                    }
+                }
+                if (entries.length) {
+                    return header + entries.join('\n');
+                }
+                return '';
+            },
+
+            getCurrentRegionFields: function () {},
+
+            calcHighlightColor: function (color, options) {
+                var highlightColor = options.get('highlightColor'),
+                    lighten = options.get('highlightLighten'),
+                    parse, mult, rgbnew, i;
+                if (highlightColor) {
+                    return highlightColor;
+                }
+                if (lighten) {
+                    // extract RGB values
+                    parse = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(color) || /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(color);
+                    if (parse) {
+                        rgbnew = [];
+                        mult = color.length === 4 ? 16 : 1;
+                        for (i = 0; i < 3; i++) {
+                            rgbnew[i] = clipval(Math.round(parseInt(parse[i + 1], 16) * mult * lighten), 0, 255);
+                        }
+                        return 'rgb(' + rgbnew.join(',') + ')';
+                    }
+
+                }
+                return color;
+            }
+
+        });
+
+        barHighlightMixin = {
+            changeHighlight: function (highlight) {
+                var currentRegion = this.currentRegion,
+                    target = this.target,
+                    shapeids = this.regionShapes[currentRegion],
+                    newShapes;
+                // will be null if the region value was null
+                if (shapeids) {
+                    newShapes = this.renderRegion(currentRegion, highlight);
+                    if ($.isArray(newShapes) || $.isArray(shapeids)) {
+                        target.replaceWithShapes(shapeids, newShapes);
+                        this.regionShapes[currentRegion] = $.map(newShapes, function (newShape) {
+                            return newShape.id;
+                        });
+                    } else {
+                        target.replaceWithShape(shapeids, newShapes);
+                        this.regionShapes[currentRegion] = newShapes.id;
+                    }
+                }
+            },
+
+            render: function () {
+                var values = this.values,
+                    target = this.target,
+                    regionShapes = this.regionShapes,
+                    shapes, ids, i, j;
+
+                if (!this.cls._super.render.call(this)) {
+                    return;
+                }
+                for (i = values.length; i--;) {
+                    shapes = this.renderRegion(i);
+                    if (shapes) {
+                        if ($.isArray(shapes)) {
+                            ids = [];
+                            for (j = shapes.length; j--;) {
+                                shapes[j].append();
+                                ids.push(shapes[j].id);
+                            }
+                            regionShapes[i] = ids;
+                        } else {
+                            shapes.append();
+                            regionShapes[i] = shapes.id; // store just the shapeid
+                        }
+                    } else {
+                        // null value
+                        regionShapes[i] = null;
+                    }
+                }
+                target.render();
+            }
+        };
+
+        /**
+         * Line charts
+         */
+        $.fn.sparkline.line = line = createClass($.fn.sparkline._base, {
+            type: 'line',
+
+            init: function (el, values, options, width, height) {
+                line._super.init.call(this, el, values, options, width, height);
+                this.vertices = [];
+                this.regionMap = [];
+                this.xvalues = [];
+                this.yvalues = [];
+                this.yminmax = [];
+                this.hightlightSpotId = null;
+                this.lastShapeId = null;
+                this.initTarget();
+            },
+
+            getRegion: function (el, x, y) {
+                var i,
+                    regionMap = this.regionMap; // maps regions to value positions
+                for (i = regionMap.length; i--;) {
+                    if (regionMap[i] !== null && x >= regionMap[i][0] && x <= regionMap[i][1]) {
+                        return regionMap[i][2];
+                    }
+                }
+                return undefined;
+            },
+
+            getCurrentRegionFields: function () {
+                var currentRegion = this.currentRegion;
+                return {
+                    isNull: this.yvalues[currentRegion] === null,
+                    x: this.xvalues[currentRegion],
+                    y: this.yvalues[currentRegion],
+                    color: this.options.get('lineColor'),
+                    fillColor: this.options.get('fillColor'),
+                    offset: currentRegion
+                };
+            },
+
+            renderHighlight: function () {
+                var currentRegion = this.currentRegion,
+                    target = this.target,
+                    vertex = this.vertices[currentRegion],
+                    options = this.options,
+                    spotRadius = options.get('spotRadius'),
+                    highlightSpotColor = options.get('highlightSpotColor'),
+                    highlightLineColor = options.get('highlightLineColor'),
+                    highlightSpot, highlightLine;
+
+                if (!vertex) {
+                    return;
+                }
+                if (spotRadius && highlightSpotColor) {
+                    highlightSpot = target.drawCircle(vertex[0], vertex[1],
+                        spotRadius, undefined, highlightSpotColor);
+                    this.highlightSpotId = highlightSpot.id;
+                    target.insertAfterShape(this.lastShapeId, highlightSpot);
+                }
+                if (highlightLineColor) {
+                    highlightLine = target.drawLine(vertex[0], this.canvasTop, vertex[0],
+                        this.canvasTop + this.canvasHeight, highlightLineColor);
+                    this.highlightLineId = highlightLine.id;
+                    target.insertAfterShape(this.lastShapeId, highlightLine);
+                }
+            },
+
+            removeHighlight: function () {
+                var target = this.target;
+                if (this.highlightSpotId) {
+                    target.removeShapeId(this.highlightSpotId);
+                    this.highlightSpotId = null;
+                }
+                if (this.highlightLineId) {
+                    target.removeShapeId(this.highlightLineId);
+                    this.highlightLineId = null;
+                }
+            },
+
+            scanValues: function () {
+                var values = this.values,
+                    valcount = values.length,
+                    xvalues = this.xvalues,
+                    yvalues = this.yvalues,
+                    yminmax = this.yminmax,
+                    i, val, isStr, isArray, sp;
+                for (i = 0; i < valcount; i++) {
+                    val = values[i];
+                    isStr = typeof(values[i]) === 'string';
+                    isArray = typeof(values[i]) === 'object' && values[i] instanceof Array;
+                    sp = isStr && values[i].split(':');
+                    if (isStr && sp.length === 2) { // x:y
+                        xvalues.push(Number(sp[0]));
+                        yvalues.push(Number(sp[1]));
+                        yminmax.push(Number(sp[1]));
+                    } else if (isArray) {
+                        xvalues.push(val[0]);
+                        yvalues.push(val[1]);
+                        yminmax.push(val[1]);
+                    } else {
+                        xvalues.push(i);
+                        if (values[i] === null || values[i] === 'null') {
+                            yvalues.push(null);
+                        } else {
+                            yvalues.push(Number(val));
+                            yminmax.push(Number(val));
+                        }
+                    }
+                }
+                if (this.options.get('xvalues')) {
+                    xvalues = this.options.get('xvalues');
+                }
+
+                this.maxy = this.maxyorg = Math.max.apply(Math, yminmax);
+                this.miny = this.minyorg = Math.min.apply(Math, yminmax);
+
+                this.maxx = Math.max.apply(Math, xvalues);
+                this.minx = Math.min.apply(Math, xvalues);
+
+                this.xvalues = xvalues;
+                this.yvalues = yvalues;
+                this.yminmax = yminmax;
+
+            },
+
+            processRangeOptions: function () {
+                var options = this.options,
+                    normalRangeMin = options.get('normalRangeMin'),
+                    normalRangeMax = options.get('normalRangeMax');
+
+                if (normalRangeMin !== undefined) {
+                    if (normalRangeMin < this.miny) {
+                        this.miny = normalRangeMin;
+                    }
+                    if (normalRangeMax > this.maxy) {
+                        this.maxy = normalRangeMax;
+                    }
+                }
+                if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < this.miny)) {
+                    this.miny = options.get('chartRangeMin');
+                }
+                if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > this.maxy)) {
+                    this.maxy = options.get('chartRangeMax');
+                }
+                if (options.get('chartRangeMinX') !== undefined && (options.get('chartRangeClipX') || options.get('chartRangeMinX') < this.minx)) {
+                    this.minx = options.get('chartRangeMinX');
+                }
+                if (options.get('chartRangeMaxX') !== undefined && (options.get('chartRangeClipX') || options.get('chartRangeMaxX') > this.maxx)) {
+                    this.maxx = options.get('chartRangeMaxX');
+                }
+
+            },
+
+            drawNormalRange: function (canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey) {
+                var normalRangeMin = this.options.get('normalRangeMin'),
+                    normalRangeMax = this.options.get('normalRangeMax'),
+                    ytop = canvasTop + Math.round(canvasHeight - (canvasHeight * ((normalRangeMax - this.miny) / rangey))),
+                    height = Math.round((canvasHeight * (normalRangeMax - normalRangeMin)) / rangey);
+                this.target.drawRect(canvasLeft, ytop, canvasWidth, height, undefined, this.options.get('normalRangeColor')).append();
+            },
+
+            render: function () {
+                var options = this.options,
+                    target = this.target,
+                    canvasWidth = this.canvasWidth,
+                    canvasHeight = this.canvasHeight,
+                    vertices = this.vertices,
+                    spotRadius = options.get('spotRadius'),
+                    regionMap = this.regionMap,
+                    rangex, rangey, yvallast,
+                    canvasTop, canvasLeft,
+                    vertex, path, paths, x, y, xnext, xpos, xposnext,
+                    last, next, yvalcount, lineShapes, fillShapes, plen,
+                    valueSpots, hlSpotsEnabled, color, xvalues, yvalues, i;
+
+                if (!line._super.render.call(this)) {
+                    return;
+                }
+
+                this.scanValues();
+                this.processRangeOptions();
+
+                xvalues = this.xvalues;
+                yvalues = this.yvalues;
+
+                if (!this.yminmax.length || this.yvalues.length < 2) {
+                    // empty or all null valuess
+                    return;
+                }
+
+                canvasTop = canvasLeft = 0;
+
+                rangex = this.maxx - this.minx === 0 ? 1 : this.maxx - this.minx;
+                rangey = this.maxy - this.miny === 0 ? 1 : this.maxy - this.miny;
+                yvallast = this.yvalues.length - 1;
+
+                if (spotRadius && (canvasWidth < (spotRadius * 4) || canvasHeight < (spotRadius * 4))) {
+                    spotRadius = 0;
+                }
+                if (spotRadius) {
+                    // adjust the canvas size as required so that spots will fit
+                    hlSpotsEnabled = options.get('highlightSpotColor') &&  !options.get('disableInteraction');
+                    if (hlSpotsEnabled || options.get('minSpotColor') || (options.get('spotColor') && yvalues[yvallast] === this.miny)) {
+                        canvasHeight -= Math.ceil(spotRadius);
+                    }
+                    if (hlSpotsEnabled || options.get('maxSpotColor') || (options.get('spotColor') && yvalues[yvallast] === this.maxy)) {
+                        canvasHeight -= Math.ceil(spotRadius);
+                        canvasTop += Math.ceil(spotRadius);
+                    }
+                    if (hlSpotsEnabled ||
+                        ((options.get('minSpotColor') || options.get('maxSpotColor')) && (yvalues[0] === this.miny || yvalues[0] === this.maxy))) {
+                        canvasLeft += Math.ceil(spotRadius);
+                        canvasWidth -= Math.ceil(spotRadius);
+                    }
+                    if (hlSpotsEnabled || options.get('spotColor') ||
+                        (options.get('minSpotColor') || options.get('maxSpotColor') &&
+                        (yvalues[yvallast] === this.miny || yvalues[yvallast] === this.maxy))) {
+                        canvasWidth -= Math.ceil(spotRadius);
+                    }
+                }
+
+
+                canvasHeight--;
+
+                if (options.get('normalRangeMin') !== undefined && !options.get('drawNormalOnTop')) {
+                    this.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey);
+                }
+
+                path = [];
+                paths = [path];
+                last = next = null;
+                yvalcount = yvalues.length;
+                for (i = 0; i < yvalcount; i++) {
+                    x = xvalues[i];
+                    xnext = xvalues[i + 1];
+                    y = yvalues[i];
+                    xpos = canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex));
+                    xposnext = i < yvalcount - 1 ? canvasLeft + Math.round((xnext - this.minx) * (canvasWidth / rangex)) : canvasWidth;
+                    next = xpos + ((xposnext - xpos) / 2);
+                    regionMap[i] = [last || 0, next, i];
+                    last = next;
+                    if (y === null) {
+                        if (i) {
+                            if (yvalues[i - 1] !== null) {
+                                path = [];
+                                paths.push(path);
+                            }
+                            vertices.push(null);
+                        }
+                    } else {
+                        if (y < this.miny) {
+                            y = this.miny;
+                        }
+                        if (y > this.maxy) {
+                            y = this.maxy;
+                        }
+                        if (!path.length) {
+                            // previous value was null
+                            path.push([xpos, canvasTop + canvasHeight]);
+                        }
+                        vertex = [xpos, canvasTop + Math.round(canvasHeight - (canvasHeight * ((y - this.miny) / rangey)))];
+                        path.push(vertex);
+                        vertices.push(vertex);
+                    }
+                }
+
+                lineShapes = [];
+                fillShapes = [];
+                plen = paths.length;
+                for (i = 0; i < plen; i++) {
+                    path = paths[i];
+                    if (path.length) {
+                        if (options.get('fillColor')) {
+                            path.push([path[path.length - 1][0], (canvasTop + canvasHeight)]);
+                            fillShapes.push(path.slice(0));
+                            path.pop();
+                        }
+                        // if there's only a single point in this path, then we want to display it
+                        // as a vertical line which means we keep path[0]  as is
+                        if (path.length > 2) {
+                            // else we want the first value
+                            path[0] = [path[0][0], path[1][1]];
+                        }
+                        lineShapes.push(path);
+                    }
+                }
+
+                // draw the fill first, then optionally the normal range, then the line on top of that
+                plen = fillShapes.length;
+                for (i = 0; i < plen; i++) {
+                    target.drawShape(fillShapes[i],
+                        options.get('fillColor'), options.get('fillColor')).append();
+                }
+
+                if (options.get('normalRangeMin') !== undefined && options.get('drawNormalOnTop')) {
+                    this.drawNormalRange(canvasLeft, canvasTop, canvasHeight, canvasWidth, rangey);
+                }
+
+                plen = lineShapes.length;
+                for (i = 0; i < plen; i++) {
+                    target.drawShape(lineShapes[i], options.get('lineColor'), undefined,
+                        options.get('lineWidth')).append();
+                }
+
+                if (spotRadius && options.get('valueSpots')) {
+                    valueSpots = options.get('valueSpots');
+                    if (valueSpots.get === undefined) {
+                        valueSpots = new RangeMap(valueSpots);
+                    }
+                    for (i = 0; i < yvalcount; i++) {
+                        color = valueSpots.get(yvalues[i]);
+                        if (color) {
+                            target.drawCircle(canvasLeft + Math.round((xvalues[i] - this.minx) * (canvasWidth / rangex)),
+                                canvasTop + Math.round(canvasHeight - (canvasHeight * ((yvalues[i] - this.miny) / rangey))),
+                                spotRadius, undefined,
+                                color).append();
+                        }
+                    }
+
+                }
+                if (spotRadius && options.get('spotColor') && yvalues[yvallast] !== null) {
+                    target.drawCircle(canvasLeft + Math.round((xvalues[xvalues.length - 1] - this.minx) * (canvasWidth / rangex)),
+                        canvasTop + Math.round(canvasHeight - (canvasHeight * ((yvalues[yvallast] - this.miny) / rangey))),
+                        spotRadius, undefined,
+                        options.get('spotColor')).append();
+                }
+                if (this.maxy !== this.minyorg) {
+                    if (spotRadius && options.get('minSpotColor')) {
+                        x = xvalues[$.inArray(this.minyorg, yvalues)];
+                        target.drawCircle(canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex)),
+                            canvasTop + Math.round(canvasHeight - (canvasHeight * ((this.minyorg - this.miny) / rangey))),
+                            spotRadius, undefined,
+                            options.get('minSpotColor')).append();
+                    }
+                    if (spotRadius && options.get('maxSpotColor')) {
+                        x = xvalues[$.inArray(this.maxyorg, yvalues)];
+                        target.drawCircle(canvasLeft + Math.round((x - this.minx) * (canvasWidth / rangex)),
+                            canvasTop + Math.round(canvasHeight - (canvasHeight * ((this.maxyorg - this.miny) / rangey))),
+                            spotRadius, undefined,
+                            options.get('maxSpotColor')).append();
+                    }
+                }
+
+                this.lastShapeId = target.getLastShapeId();
+                this.canvasTop = canvasTop;
+                target.render();
+            }
+        });
+
+        /**
+         * Bar charts
+         */
+        $.fn.sparkline.bar = bar = createClass($.fn.sparkline._base, barHighlightMixin, {
+            type: 'bar',
+
+            init: function (el, values, options, width, height) {
+                var barWidth = parseInt(options.get('barWidth'), 10),
+                    barSpacing = parseInt(options.get('barSpacing'), 10),
+                    chartRangeMin = options.get('chartRangeMin'),
+                    chartRangeMax = options.get('chartRangeMax'),
+                    chartRangeClip = options.get('chartRangeClip'),
+                    stackMin = Infinity,
+                    stackMax = -Infinity,
+                    isStackString, groupMin, groupMax, stackRanges,
+                    numValues, i, vlen, range, zeroAxis, xaxisOffset, min, max, clipMin, clipMax,
+                    stacked, vlist, j, slen, svals, val, yoffset, yMaxCalc, canvasHeightEf;
+                bar._super.init.call(this, el, values, options, width, height);
+
+                // scan values to determine whether to stack bars
+                for (i = 0, vlen = values.length; i < vlen; i++) {
+                    val = values[i];
+                    isStackString = typeof(val) === 'string' && val.indexOf(':') > -1;
+                    if (isStackString || $.isArray(val)) {
+                        stacked = true;
+                        if (isStackString) {
+                            val = values[i] = normalizeValues(val.split(':'));
+                        }
+                        val = remove(val, null); // min/max will treat null as zero
+                        groupMin = Math.min.apply(Math, val);
+                        groupMax = Math.max.apply(Math, val);
+                        if (groupMin < stackMin) {
+                            stackMin = groupMin;
+                        }
+                        if (groupMax > stackMax) {
+                            stackMax = groupMax;
+                        }
+                    }
+                }
+
+                this.stacked = stacked;
+                this.regionShapes = {};
+                this.barWidth = barWidth;
+                this.barSpacing = barSpacing;
+                this.totalBarWidth = barWidth + barSpacing;
+                this.width = width = (values.length * barWidth) + ((values.length - 1) * barSpacing);
+
+                this.initTarget();
+
+                if (chartRangeClip) {
+                    clipMin = chartRangeMin === undefined ? -Infinity : chartRangeMin;
+                    clipMax = chartRangeMax === undefined ? Infinity : chartRangeMax;
+                }
+
+                numValues = [];
+                stackRanges = stacked ? [] : numValues;
+                var stackTotals = [];
+                var stackRangesNeg = [];
+                for (i = 0, vlen = values.length; i < vlen; i++) {
+                    if (stacked) {
+                        vlist = values[i];
+                        values[i] = svals = [];
+                        stackTotals[i] = 0;
+                        stackRanges[i] = stackRangesNeg[i] = 0;
+                        for (j = 0, slen = vlist.length; j < slen; j++) {
+                            val = svals[j] = chartRangeClip ? clipval(vlist[j], clipMin, clipMax) : vlist[j];
+                            if (val !== null) {
+                                if (val > 0) {
+                                    stackTotals[i] += val;
+                                }
+                                if (stackMin < 0 && stackMax > 0) {
+                                    if (val < 0) {
+                                        stackRangesNeg[i] += Math.abs(val);
+                                    } else {
+                                        stackRanges[i] += val;
+                                    }
+                                } else {
+                                    stackRanges[i] += Math.abs(val - (val < 0 ? stackMax : stackMin));
+                                }
+                                numValues.push(val);
+                            }
+                        }
+                    } else {
+                        val = chartRangeClip ? clipval(values[i], clipMin, clipMax) : values[i];
+                        val = values[i] = normalizeValue(val);
+                        if (val !== null) {
+                            numValues.push(val);
+                        }
+                    }
+                }
+                this.max = max = Math.max.apply(Math, numValues);
+                this.min = min = Math.min.apply(Math, numValues);
+                this.stackMax = stackMax = stacked ? Math.max.apply(Math, stackTotals) : max;
+                this.stackMin = stackMin = stacked ? Math.min.apply(Math, numValues) : min;
+
+                if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < min)) {
+                    min = options.get('chartRangeMin');
+                }
+                if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > max)) {
+                    max = options.get('chartRangeMax');
+                }
+
+                this.zeroAxis = zeroAxis = options.get('zeroAxis', true);
+                if (min <= 0 && max >= 0 && zeroAxis) {
+                    xaxisOffset = 0;
+                } else if (zeroAxis == false) {
+                    xaxisOffset = min;
+                } else if (min > 0) {
+                    xaxisOffset = min;
+                } else {
+                    xaxisOffset = max;
+                }
+                this.xaxisOffset = xaxisOffset;
+
+                range = stacked ? (Math.max.apply(Math, stackRanges) + Math.max.apply(Math, stackRangesNeg)) : max - min;
+
+                // as we plot zero/min values a single pixel line, we add a pixel to all other
+                // values - Reduce the effective canvas size to suit
+                this.canvasHeightEf = (zeroAxis && min < 0) ? this.canvasHeight - 2 : this.canvasHeight - 1;
+
+                if (min < xaxisOffset) {
+                    yMaxCalc = (stacked && max >= 0) ? stackMax : max;
+                    yoffset = (yMaxCalc - xaxisOffset) / range * this.canvasHeight;
+                    if (yoffset !== Math.ceil(yoffset)) {
+                        this.canvasHeightEf -= 2;
+                        yoffset = Math.ceil(yoffset);
+                    }
+                } else {
+                    yoffset = this.canvasHeight;
+                }
+                this.yoffset = yoffset;
+
+                if ($.isArray(options.get('colorMap'))) {
+                    this.colorMapByIndex = options.get('colorMap');
+                    this.colorMapByValue = null;
+                } else {
+                    this.colorMapByIndex = null;
+                    this.colorMapByValue = options.get('colorMap');
+                    if (this.colorMapByValue && this.colorMapByValue.get === undefined) {
+                        this.colorMapByValue = new RangeMap(this.colorMapByValue);
+                    }
+                }
+
+                this.range = range;
+            },
+
+            getRegion: function (el, x, y) {
+                var result = Math.floor(x / this.totalBarWidth);
+                return (result < 0 || result >= this.values.length) ? undefined : result;
+            },
+
+            getCurrentRegionFields: function () {
+                var currentRegion = this.currentRegion,
+                    values = ensureArray(this.values[currentRegion]),
+                    result = [],
+                    value, i;
+                for (i = values.length; i--;) {
+                    value = values[i];
+                    result.push({
+                        isNull: value === null,
+                        value: value,
+                        color: this.calcColor(i, value, currentRegion),
+                        offset: currentRegion
+                    });
+                }
+                return result;
+            },
+
+            calcColor: function (stacknum, value, valuenum) {
+                var colorMapByIndex = this.colorMapByIndex,
+                    colorMapByValue = this.colorMapByValue,
+                    options = this.options,
+                    color, newColor;
+                if (this.stacked) {
+                    color = options.get('stackedBarColor');
+                } else {
+                    color = (value < 0) ? options.get('negBarColor') : options.get('barColor');
+                }
+                if (value === 0 && options.get('zeroColor') !== undefined) {
+                    color = options.get('zeroColor');
+                }
+                if (colorMapByValue && (newColor = colorMapByValue.get(value))) {
+                    color = newColor;
+                } else if (colorMapByIndex && colorMapByIndex.length > valuenum) {
+                    color = colorMapByIndex[valuenum];
+                }
+                return $.isArray(color) ? color[stacknum % color.length] : color;
+            },
+
+            /**
+             * Render bar(s) for a region
+             */
+            renderRegion: function (valuenum, highlight) {
+                var vals = this.values[valuenum],
+                    options = this.options,
+                    xaxisOffset = this.xaxisOffset,
+                    result = [],
+                    range = this.range,
+                    stacked = this.stacked,
+                    target = this.target,
+                    x = valuenum * this.totalBarWidth,
+                    canvasHeightEf = this.canvasHeightEf,
+                    yoffset = this.yoffset,
+                    y, height, color, isNull, yoffsetNeg, i, valcount, val, minPlotted, allMin;
+
+                vals = $.isArray(vals) ? vals : [vals];
+                valcount = vals.length;
+                val = vals[0];
+                isNull = all(null, vals);
+                allMin = all(xaxisOffset, vals, true);
+
+                if (isNull) {
+                    if (options.get('nullColor')) {
+                        color = highlight ? options.get('nullColor') : this.calcHighlightColor(options.get('nullColor'), options);
+                        y = (yoffset > 0) ? yoffset - 1 : yoffset;
+                        return target.drawRect(x, y, this.barWidth - 1, 0, color, color);
+                    } else {
+                        return undefined;
+                    }
+                }
+                yoffsetNeg = yoffset;
+                for (i = 0; i < valcount; i++) {
+                    val = vals[i];
+
+                    if (stacked && val === xaxisOffset) {
+                        if (!allMin || minPlotted) {
+                            continue;
+                        }
+                        minPlotted = true;
+                    }
+
+                    if (range > 0) {
+                        height = Math.floor(canvasHeightEf * ((Math.abs(val - xaxisOffset) / range))) + 1;
+                    } else {
+                        height = 1;
+                    }
+                    if (val < xaxisOffset || (val === xaxisOffset && yoffset === 0)) {
+                        y = yoffsetNeg;
+                        yoffsetNeg += height;
+                    } else {
+                        y = yoffset - height;
+                        yoffset -= height;
+                    }
+                    color = this.calcColor(i, val, valuenum);
+                    if (highlight) {
+                        color = this.calcHighlightColor(color, options);
+                    }
+                    result.push(target.drawRect(x, y, this.barWidth - 1, height - 1, color, color));
+                }
+                if (result.length === 1) {
+                    return result[0];
+                }
+                return result;
+            }
+        });
+
+        /**
+         * Tristate charts
+         */
+        $.fn.sparkline.tristate = tristate = createClass($.fn.sparkline._base, barHighlightMixin, {
+            type: 'tristate',
+
+            init: function (el, values, options, width, height) {
+                var barWidth = parseInt(options.get('barWidth'), 10),
+                    barSpacing = parseInt(options.get('barSpacing'), 10);
+                tristate._super.init.call(this, el, values, options, width, height);
+
+                this.regionShapes = {};
+                this.barWidth = barWidth;
+                this.barSpacing = barSpacing;
+                this.totalBarWidth = barWidth + barSpacing;
+                this.values = $.map(values, Number);
+                this.width = width = (values.length * barWidth) + ((values.length - 1) * barSpacing);
+
+                if ($.isArray(options.get('colorMap'))) {
+                    this.colorMapByIndex = options.get('colorMap');
+                    this.colorMapByValue = null;
+                } else {
+                    this.colorMapByIndex = null;
+                    this.colorMapByValue = options.get('colorMap');
+                    if (this.colorMapByValue && this.colorMapByValue.get === undefined) {
+                        this.colorMapByValue = new RangeMap(this.colorMapByValue);
+                    }
+                }
+                this.initTarget();
+            },
+
+            getRegion: function (el, x, y) {
+                return Math.floor(x / this.totalBarWidth);
+            },
+
+            getCurrentRegionFields: function () {
+                var currentRegion = this.currentRegion;
+                return {
+                    isNull: this.values[currentRegion] === undefined,
+                    value: this.values[currentRegion],
+                    color: this.calcColor(this.values[currentRegion], currentRegion),
+                    offset: currentRegion
+                };
+            },
+
+            calcColor: function (value, valuenum) {
+                var values = this.values,
+                    options = this.options,
+                    colorMapByIndex = this.colorMapByIndex,
+                    colorMapByValue = this.colorMapByValue,
+                    color, newColor;
+
+                if (colorMapByValue && (newColor = colorMapByValue.get(value))) {
+                    color = newColor;
+                } else if (colorMapByIndex && colorMapByIndex.length > valuenum) {
+                    color = colorMapByIndex[valuenum];
+                } else if (values[valuenum] < 0) {
+                    color = options.get('negBarColor');
+                } else if (values[valuenum] > 0) {
+                    color = options.get('posBarColor');
+                } else {
+                    color = options.get('zeroBarColor');
+                }
+                return color;
+            },
+
+            renderRegion: function (valuenum, highlight) {
+                var values = this.values,
+                    options = this.options,
+                    target = this.target,
+                    canvasHeight, height, halfHeight,
+                    x, y, color;
+
+                canvasHeight = target.pixelHeight;
+                halfHeight = Math.round(canvasHeight / 2);
+
+                x = valuenum * this.totalBarWidth;
+                if (values[valuenum] < 0) {
+                    y = halfHeight;
+                    height = halfHeight - 1;
+                } else if (values[valuenum] > 0) {
+                    y = 0;
+                    height = halfHeight - 1;
+                } else {
+                    y = halfHeight - 1;
+                    height = 2;
+                }
+                color = this.calcColor(values[valuenum], valuenum);
+                if (color === null) {
+                    return;
+                }
+                if (highlight) {
+                    color = this.calcHighlightColor(color, options);
+                }
+                return target.drawRect(x, y, this.barWidth - 1, height - 1, color, color);
+            }
+        });
+
+        /**
+         * Discrete charts
+         */
+        $.fn.sparkline.discrete = discrete = createClass($.fn.sparkline._base, barHighlightMixin, {
+            type: 'discrete',
+
+            init: function (el, values, options, width, height) {
+                discrete._super.init.call(this, el, values, options, width, height);
+
+                this.regionShapes = {};
+                this.values = values = $.map(values, Number);
+                this.min = Math.min.apply(Math, values);
+                this.max = Math.max.apply(Math, values);
+                this.range = this.max - this.min;
+                this.width = width = options.get('width') === 'auto' ? values.length * 2 : this.width;
+                this.interval = Math.floor(width / values.length);
+                this.itemWidth = width / values.length;
+                if (options.get('chartRangeMin') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMin') < this.min)) {
+                    this.min = options.get('chartRangeMin');
+                }
+                if (options.get('chartRangeMax') !== undefined && (options.get('chartRangeClip') || options.get('chartRangeMax') > this.max)) {
+                    this.max = options.get('chartRangeMax');
+                }
+                this.initTarget();
+                if (this.target) {
+                    this.lineHeight = options.get('lineHeight') === 'auto' ? Math.round(this.canvasHeight * 0.3) : options.get('lineHeight');
+                }
+            },
+
+            getRegion: function (el, x, y) {
+                return Math.floor(x / this.itemWidth);
+            },
+
+            getCurrentRegionFields: function () {
+                var currentRegion = this.currentRegion;
+                return {
+                    isNull: this.values[currentRegion] === undefined,
+                    value: this.values[currentRegion],
+                    offset: currentRegion
+                };
+            },
+
+            renderRegion: function (valuenum, highlight) {
+                var values = this.values,
+                    options = this.options,
+                    min = this.min,
+                    max = this.max,
+                    range = this.range,
+                    interval = this.interval,
+                    target = this.target,
+                    canvasHeight = this.canvasHeight,
+                    lineHeight = this.lineHeight,
+                    pheight = canvasHeight - lineHeight,
+                    ytop, val, color, x;
+
+                val = clipval(values[valuenum], min, max);
+                x = valuenum * interval;
+                ytop = Math.round(pheight - pheight * ((val - min) / range));
+                color = (options.get('thresholdColor') && val < options.get('thresholdValue')) ? options.get('thresholdColor') : options.get('lineColor');
+                if (highlight) {
+                    color = this.calcHighlightColor(color, options);
+                }
+                return target.drawLine(x, ytop, x, ytop + lineHeight, color);
+            }
+        });
+
+        /**
+         * Bullet charts
+         */
+        $.fn.sparkline.bullet = bullet = createClass($.fn.sparkline._base, {
+            type: 'bullet',
+
+            init: function (el, values, options, width, height) {
+                var min, max, vals;
+                bullet._super.init.call(this, el, values, options, width, height);
+
+                // values: target, performance, range1, range2, range3
+                this.values = values = normalizeValues(values);
+                // target or performance could be null
+                vals = values.slice();
+                vals[0] = vals[0] === null ? vals[2] : vals[0];
+                vals[1] = values[1] === null ? vals[2] : vals[1];
+                min = Math.min.apply(Math, values);
+                max = Math.max.apply(Math, values);
+                if (options.get('base') === undefined) {
+                    min = min < 0 ? min : 0;
+                } else {
+                    min = options.get('base');
+                }
+                this.min = min;
+                this.max = max;
+                this.range = max - min;
+                this.shapes = {};
+                this.valueShapes = {};
+                this.regiondata = {};
+                this.width = width = options.get('width') === 'auto' ? '4.0em' : width;
+                this.target = this.$el.simpledraw(width, height, options.get('composite'));
+                if (!values.length) {
+                    this.disabled = true;
+                }
+                this.initTarget();
+            },
+
+            getRegion: function (el, x, y) {
+                var shapeid = this.target.getShapeAt(el, x, y);
+                return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined;
+            },
+
+            getCurrentRegionFields: function () {
+                var currentRegion = this.currentRegion;
+                return {
+                    fieldkey: currentRegion.substr(0, 1),
+                    value: this.values[currentRegion.substr(1)],
+                    region: currentRegion
+                };
+            },
+
+            changeHighlight: function (highlight) {
+                var currentRegion = this.currentRegion,
+                    shapeid = this.valueShapes[currentRegion],
+                    shape;
+                delete this.shapes[shapeid];
+                switch (currentRegion.substr(0, 1)) {
+                    case 'r':
+                        shape = this.renderRange(currentRegion.substr(1), highlight);
+                        break;
+                    case 'p':
+                        shape = this.renderPerformance(highlight);
+                        break;
+                    case 't':
+                        shape = this.renderTarget(highlight);
+                        break;
+                }
+                this.valueShapes[currentRegion] = shape.id;
+                this.shapes[shape.id] = currentRegion;
+                this.target.replaceWithShape(shapeid, shape);
+            },
+
+            renderRange: function (rn, highlight) {
+                var rangeval = this.values[rn],
+                    rangewidth = Math.round(this.canvasWidth * ((rangeval - this.min) / this.range)),
+                    color = this.options.get('rangeColors')[rn - 2];
+                if (highlight) {
+                    color = this.calcHighlightColor(color, this.options);
+                }
+                return this.target.drawRect(0, 0, rangewidth - 1, this.canvasHeight - 1, color, color);
+            },
+
+            renderPerformance: function (highlight) {
+                var perfval = this.values[1],
+                    perfwidth = Math.round(this.canvasWidth * ((perfval - this.min) / this.range)),
+                    color = this.options.get('performanceColor');
+                if (highlight) {
+                    color = this.calcHighlightColor(color, this.options);
+                }
+                return this.target.drawRect(0, Math.round(this.canvasHeight * 0.3), perfwidth - 1,
+                    Math.round(this.canvasHeight * 0.4) - 1, color, color);
+            },
+
+            renderTarget: function (highlight) {
+                var targetval = this.values[0],
+                    x = Math.round(this.canvasWidth * ((targetval - this.min) / this.range) - (this.options.get('targetWidth') / 2)),
+                    targettop = Math.round(this.canvasHeight * 0.10),
+                    targetheight = this.canvasHeight - (targettop * 2),
+                    color = this.options.get('targetColor');
+                if (highlight) {
+                    color = this.calcHighlightColor(color, this.options);
+                }
+                return this.target.drawRect(x, targettop, this.options.get('targetWidth') - 1, targetheight - 1, color, color);
+            },
+
+            render: function () {
+                var vlen = this.values.length,
+                    target = this.target,
+                    i, shape;
+                if (!bullet._super.render.call(this)) {
+                    return;
+                }
+                for (i = 2; i < vlen; i++) {
+                    shape = this.renderRange(i).append();
+                    this.shapes[shape.id] = 'r' + i;
+                    this.valueShapes['r' + i] = shape.id;
+                }
+                if (this.values[1] !== null) {
+                    shape = this.renderPerformance().append();
+                    this.shapes[shape.id] = 'p1';
+                    this.valueShapes.p1 = shape.id;
+                }
+                if (this.values[0] !== null) {
+                    shape = this.renderTarget().append();
+                    this.shapes[shape.id] = 't0';
+                    this.valueShapes.t0 = shape.id;
+                }
+                target.render();
+            }
+        });
+
+        /**
+         * Pie charts
+         */
+        $.fn.sparkline.pie = pie = createClass($.fn.sparkline._base, {
+            type: 'pie',
+
+            init: function (el, values, options, width, height) {
+                var total = 0, i;
+
+                pie._super.init.call(this, el, values, options, width, height);
+
+                this.shapes = {}; // map shape ids to value offsets
+                this.valueShapes = {}; // maps value offsets to shape ids
+                this.values = values = $.map(values, Number);
+
+                if (options.get('width') === 'auto') {
+                    this.width = this.height;
+                }
+
+                if (values.length > 0) {
+                    for (i = values.length; i--;) {
+                        total += values[i];
+                    }
+                }
+                this.total = total;
+                this.initTarget();
+                this.radius = Math.floor(Math.min(this.canvasWidth, this.canvasHeight) / 2);
+            },
+
+            getRegion: function (el, x, y) {
+                var shapeid = this.target.getShapeAt(el, x, y);
+                return (shapeid !== undefined && this.shapes[shapeid] !== undefined) ? this.shapes[shapeid] : undefined;
+            },
+
+            getCurrentRegionFields: function () {
+                var currentRegion = this.currentRegion;
+                return {
+                    isNull: this.values[currentRegion] === undefined,
+                    value: this.values[currentRegion],
+                    percent: this.values[currentRegion] / this.total * 100,
+                    color: this.options.get('sliceColors')[currentRegion % this.options.get('sliceColors').length],
+                    offset: currentRegion
+                };
+            },
+
+            changeHighlight: function (highlight) {
+                var currentRegion = this.currentRegion,
+                    newslice = this.renderSlice(currentRegion, highlight),
+                    shapeid = this.valueShapes[currentRegion];
+                delete this.shapes[shapeid];
+                this.target.replaceWithShape(shapeid, newslice);
+                this.valueShapes[currentRegion] = newslice.id;
+                this.shapes[newslice.id] = currentRegion;
+            },
+
+            renderSlice: function (valuenum, highlight) {
+                var target = this.target,
+                    options = this.options,
+                    radius = this.radius,
+                    borderWidth = options.get('borderWidth'),
+                    offset = options.get('offset'),
+                    circle = 2 * Math.PI,
+                    values = this.values,
+                    total = this.total,
+                    next = offset ? (2*Math.PI)*(offset/360) : 0,
+                    start, end, i, vlen, color;
+
+                vlen = values.length;
+                for (i = 0; i < vlen; i++) {
+                    start = next;
+                    end = next;
+                    if (total > 0) {  // avoid divide by zero
+                        end = next + (circle * (values[i] / total));
+                    }
+                    if (valuenum === i) {
+                        color = options.get('sliceColors')[i % options.get('sliceColors').length];
+                        if (highlight) {
+                            color = this.calcHighlightColor(color, options);
+                        }
+
+                        return target.drawPieSlice(radius, radius, radius - borderWidth, start, end, undefined, color);
+                    }
+                    next = end;
+                }
+            },
+
+            render: function () {
+                var target = this.target,
+                    values = this.values,
+                    options = this.options,
+                    radius = this.radius,
+                    borderWidth = options.get('borderWidth'),
+                    shape, i;
+
+                if (!pie._super.render.call(this)) {
+                    return;
+                }
+                if (borderWidth) {
+                    target.drawCircle(radius, radius, Math.floor(radius - (borderWidth / 2)),
+                        options.get('borderColor'), undefined, borderWidth).append();
+                }
+                for (i = values.length; i--;) {
+                    if (values[i]) { // don't render zero values
+                        shape = this.renderSlice(i).append();
+                        this.valueShapes[i] = shape.id; // store just the shapeid
+                        this.shapes[shape.id] = i;
+                    }
+                }
+                target.render();
+            }
+        });
+
+        /**
+         * Box plots
+         */
+        $.fn.sparkline.box = box = createClass($.fn.sparkline._base, {
+            type: 'box',
+
+            init: function (el, values, options, width, height) {
+                box._super.init.call(this, el, values, options, width, height);
+                this.values = $.map(values, Number);
+                this.width = options.get('width') === 'auto' ? '4.0em' : width;
+                this.initTarget();
+                if (!this.values.length) {
+                    this.disabled = 1;
+                }
+            },
+
+            /**
+             * Simulate a single region
+             */
+            getRegion: function () {
+                return 1;
+            },
+
+            getCurrentRegionFields: function () {
+                var result = [
+                    { field: 'lq', value: this.quartiles[0] },
+                    { field: 'med', value: this.quartiles[1] },
+                    { field: 'uq', value: this.quartiles[2] }
+                ];
+                if (this.loutlier !== undefined) {
+                    result.push({ field: 'lo', value: this.loutlier});
+                }
+                if (this.routlier !== undefined) {
+                    result.push({ field: 'ro', value: this.routlier});
+                }
+                if (this.lwhisker !== undefined) {
+                    result.push({ field: 'lw', value: this.lwhisker});
+                }
+                if (this.rwhisker !== undefined) {
+                    result.push({ field: 'rw', value: this.rwhisker});
+                }
+                return result;
+            },
+
+            render: function () {
+                var target = this.target,
+                    values = this.values,
+                    vlen = values.length,
+                    options = this.options,
+                    canvasWidth = this.canvasWidth,
+                    canvasHeight = this.canvasHeight,
+                    minValue = options.get('chartRangeMin') === undefined ? Math.min.apply(Math, values) : options.get('chartRangeMin'),
+                    maxValue = options.get('chartRangeMax') === undefined ? Math.max.apply(Math, values) : options.get('chartRangeMax'),
+                    canvasLeft = 0,
+                    lwhisker, loutlier, iqr, q1, q2, q3, rwhisker, routlier, i,
+                    size, unitSize;
+
+                if (!box._super.render.call(this)) {
+                    return;
+                }
+
+                if (options.get('raw')) {
+                    if (options.get('showOutliers') && values.length > 5) {
+                        loutlier = values[0];
+                        lwhisker = values[1];
+                        q1 = values[2];
+                        q2 = values[3];
+                        q3 = values[4];
+                        rwhisker = values[5];
+                        routlier = values[6];
+                    } else {
+                        lwhisker = values[0];
+                        q1 = values[1];
+                        q2 = values[2];
+                        q3 = values[3];
+                        rwhisker = values[4];
+                    }
+                } else {
+                    values.sort(function (a, b) { return a - b; });
+                    q1 = quartile(values, 1);
+                    q2 = quartile(values, 2);
+                    q3 = quartile(values, 3);
+                    iqr = q3 - q1;
+                    if (options.get('showOutliers')) {
+                        lwhisker = rwhisker = undefined;
+                        for (i = 0; i < vlen; i++) {
+                            if (lwhisker === undefined && values[i] > q1 - (iqr * options.get('outlierIQR'))) {
+                                lwhisker = values[i];
+                            }
+                            if (values[i] < q3 + (iqr * options.get('outlierIQR'))) {
+                                rwhisker = values[i];
+                            }
+                        }
+                        loutlier = values[0];
+                        routlier = values[vlen - 1];
+                    } else {
+                        lwhisker = values[0];
+                        rwhisker = values[vlen - 1];
+                    }
+                }
+                this.quartiles = [q1, q2, q3];
+                this.lwhisker = lwhisker;
+                this.rwhisker = rwhisker;
+                this.loutlier = loutlier;
+                this.routlier = routlier;
+
+                unitSize = canvasWidth / (maxValue - minValue + 1);
+                if (options.get('showOutliers')) {
+                    canvasLeft = Math.ceil(options.get('spotRadius'));
+                    canvasWidth -= 2 * Math.ceil(options.get('spotRadius'));
+                    unitSize = canvasWidth / (maxValue - minValue + 1);
+                    if (loutlier < lwhisker) {
+                        target.drawCircle((loutlier - minValue) * unitSize + canvasLeft,
+                            canvasHeight / 2,
+                            options.get('spotRadius'),
+                            options.get('outlierLineColor'),
+                            options.get('outlierFillColor')).append();
+                    }
+                    if (routlier > rwhisker) {
+                        target.drawCircle((routlier - minValue) * unitSize + canvasLeft,
+                            canvasHeight / 2,
+                            options.get('spotRadius'),
+                            options.get('outlierLineColor'),
+                            options.get('outlierFillColor')).append();
+                    }
+                }
+
+                // box
+                target.drawRect(
+                    Math.round((q1 - minValue) * unitSize + canvasLeft),
+                    Math.round(canvasHeight * 0.1),
+                    Math.round((q3 - q1) * unitSize),
+                    Math.round(canvasHeight * 0.8),
+                    options.get('boxLineColor'),
+                    options.get('boxFillColor')).append();
+                // left whisker
+                target.drawLine(
+                    Math.round((lwhisker - minValue) * unitSize + canvasLeft),
+                    Math.round(canvasHeight / 2),
+                    Math.round((q1 - minValue) * unitSize + canvasLeft),
+                    Math.round(canvasHeight / 2),
+                    options.get('lineColor')).append();
+                target.drawLine(
+                    Math.round((lwhisker - minValue) * unitSize + canvasLeft),
+                    Math.round(canvasHeight / 4),
+                    Math.round((lwhisker - minValue) * unitSize + canvasLeft),
+                    Math.round(canvasHeight - canvasHeight / 4),
+                    options.get('whiskerColor')).append();
+                // right whisker
+                target.drawLine(Math.round((rwhisker - minValue) * unitSize + canvasLeft),
+                    Math.round(canvasHeight / 2),
+                    Math.round((q3 - minValue) * unitSize + canvasLeft),
+                    Math.round(canvasHeight / 2),
+                    options.get('lineColor')).append();
+                target.drawLine(
+                    Math.round((rwhisker - minValue) * unitSize + canvasLeft),
+                    Math.round(canvasHeight / 4),
+                    Math.round((rwhisker - minValue) * unitSize + canvasLeft),
+                    Math.round(canvasHeight - canvasHeight / 4),
+                    options.get('whiskerColor')).append();
+                // median line
+                target.drawLine(
+                    Math.round((q2 - minValue) * unitSize + canvasLeft),
+                    Math.round(canvasHeight * 0.1),
+                    Math.round((q2 - minValue) * unitSize + canvasLeft),
+                    Math.round(canvasHeight * 0.9),
+                    options.get('medianColor')).append();
+                if (options.get('target')) {
+                    size = Math.ceil(options.get('spotRadius'));
+                    target.drawLine(
+                        Math.round((options.get('target') - minValue) * unitSize + canvasLeft),
+                        Math.round((canvasHeight / 2) - size),
+                        Math.round((options.get('target') - minValue) * unitSize + canvasLeft),
+                        Math.round((canvasHeight / 2) + size),
+                        options.get('targetColor')).append();
+                    target.drawLine(
+                        Math.round((options.get('target') - minValue) * unitSize + canvasLeft - size),
+                        Math.round(canvasHeight / 2),
+                        Math.round((options.get('target') - minValue) * unitSize + canvasLeft + size),
+                        Math.round(canvasHeight / 2),
+                        options.get('targetColor')).append();
+                }
+                target.render();
+            }
+        });
+
+        // Setup a very simple "virtual canvas" to make drawing the few shapes we need easier
+        // This is accessible as $(foo).simpledraw()
+
+        VShape = createClass({
+            init: function (target, id, type, args) {
+                this.target = target;
+                this.id = id;
+                this.type = type;
+                this.args = args;
+            },
+            append: function () {
+                this.target.appendShape(this);
+                return this;
+            }
+        });
+
+        VCanvas_base = createClass({
+            _pxregex: /(\d+)(px)?\s*$/i,
+
+            init: function (width, height, target) {
+                if (!width) {
+                    return;
+                }
+                this.width = width;
+                this.height = height;
+                this.target = target;
+                this.lastShapeId = null;
+                if (target[0]) {
+                    target = target[0];
+                }
+                $.data(target, '_jqs_vcanvas', this);
+            },
+
+            drawLine: function (x1, y1, x2, y2, lineColor, lineWidth) {
+                return this.drawShape([[x1, y1], [x2, y2]], lineColor, lineWidth);
+            },
+
+            drawShape: function (path, lineColor, fillColor, lineWidth) {
+                return this._genShape('Shape', [path, lineColor, fillColor, lineWidth]);
+            },
+
+            drawCircle: function (x, y, radius, lineColor, fillColor, lineWidth) {
+                return this._genShape('Circle', [x, y, radius, lineColor, fillColor, lineWidth]);
+            },
+
+            drawPieSlice: function (x, y, radius, startAngle, endAngle, lineColor, fillColor) {
+                return this._genShape('PieSlice', [x, y, radius, startAngle, endAngle, lineColor, fillColor]);
+            },
+
+            drawRect: function (x, y, width, height, lineColor, fillColor) {
+                return this._genShape('Rect', [x, y, width, height, lineColor, fillColor]);
+            },
+
+            getElement: function () {
+                return this.canvas;
+            },
+
+            /**
+             * Return the most recently inserted shape id
+             */
+            getLastShapeId: function () {
+                return this.lastShapeId;
+            },
+
+            /**
+             * Clear and reset the canvas
+             */
+            reset: function () {
+                alert('reset not implemented');
+            },
+
+            _insert: function (el, target) {
+                $(target).html(el);
+            },
+
+            /**
+             * Calculate the pixel dimensions of the canvas
+             */
+            _calculatePixelDims: function (width, height, canvas) {
+                // XXX This should probably be a configurable option
+                var match;
+                match = this._pxregex.exec(height);
+                if (match) {
+                    this.pixelHeight = match[1];
+                } else {
+                    this.pixelHeight = $(canvas).height();
+                }
+                match = this._pxregex.exec(width);
+                if (match) {
+                    this.pixelWidth = match[1];
+                } else {
+                    this.pixelWidth = $(canvas).width();
+                }
+            },
+
+            /**
+             * Generate a shape object and id for later rendering
+             */
+            _genShape: function (shapetype, shapeargs) {
+                var id = shapeCount++;
+                shapeargs.unshift(id);
+                return new VShape(this, id, shapetype, shapeargs);
+            },
+
+            /**
+             * Add a shape to the end of the render queue
+             */
+            appendShape: function (shape) {
+                alert('appendShape not implemented');
+            },
+
+            /**
+             * Replace one shape with another
+             */
+            replaceWithShape: function (shapeid, shape) {
+                alert('replaceWithShape not implemented');
+            },
+
+            /**
+             * Insert one shape after another in the render queue
+             */
+            insertAfterShape: function (shapeid, shape) {
+                alert('insertAfterShape not implemented');
+            },
+
+            /**
+             * Remove a shape from the queue
+             */
+            removeShapeId: function (shapeid) {
+                alert('removeShapeId not implemented');
+            },
+
+            /**
+             * Find a shape at the specified x/y co-ordinates
+             */
+            getShapeAt: function (el, x, y) {
+                alert('getShapeAt not implemented');
+            },
+
+            /**
+             * Render all queued shapes onto the canvas
+             */
+            render: function () {
+                alert('render not implemented');
+            }
+        });
+
+        VCanvas_canvas = createClass(VCanvas_base, {
+            init: function (width, height, target, interact) {
+                VCanvas_canvas._super.init.call(this, width, height, target);
+                this.canvas = document.createElement('canvas');
+                if (target[0]) {
+                    target = target[0];
+                }
+                $.data(target, '_jqs_vcanvas', this);
+                $(this.canvas).css({ display: 'inline-block', width: width, height: height, verticalAlign: 'top' });
+                this._insert(this.canvas, target);
+                this._calculatePixelDims(width, height, this.canvas);
+                this.canvas.width = this.pixelWidth;
+                this.canvas.height = this.pixelHeight;
+                this.interact = interact;
+                this.shapes = {};
+                this.shapeseq = [];
+                this.currentTargetShapeId = undefined;
+                $(this.canvas).css({width: this.pixelWidth, height: this.pixelHeight});
+            },
+
+            _getContext: function (lineColor, fillColor, lineWidth) {
+                var context = this.canvas.getContext('2d');
+                if (lineColor !== undefined) {
+                    context.strokeStyle = lineColor;
+                }
+                context.lineWidth = lineWidth === undefined ? 1 : lineWidth;
+                if (fillColor !== undefined) {
+                    context.fillStyle = fillColor;
+                }
+                return context;
+            },
+
+            reset: function () {
+                var context = this._getContext();
+                context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);
+                this.shapes = {};
+                this.shapeseq = [];
+                this.currentTargetShapeId = undefined;
+            },
+
+            _drawShape: function (shapeid, path, lineColor, fillColor, lineWidth) {
+                var context = this._getContext(lineColor, fillColor, lineWidth),
+                    i, plen;
+                context.beginPath();
+                context.moveTo(path[0][0] + 0.5, path[0][1] + 0.5);
+                for (i = 1, plen = path.length; i < plen; i++) {
+                    context.lineTo(path[i][0] + 0.5, path[i][1] + 0.5); // the 0.5 offset gives us crisp pixel-width lines
+                }
+                if (lineColor !== undefined) {
+                    context.stroke();
+                }
+                if (fillColor !== undefined) {
+                    context.fill();
+                }
+                if (this.targetX !== undefined && this.targetY !== undefined &&
+                    context.isPointInPath(this.targetX, this.targetY)) {
+                    this.currentTargetShapeId = shapeid;
+                }
+            },
+
+            _drawCircle: function (shapeid, x, y, radius, lineColor, fillColor, lineWidth) {
+                var context = this._getContext(lineColor, fillColor, lineWidth);
+                context.beginPath();
+                context.arc(x, y, radius, 0, 2 * Math.PI, false);
+                if (this.targetX !== undefined && this.targetY !== undefined &&
+                    context.isPointInPath(this.targetX, this.targetY)) {
+                    this.currentTargetShapeId = shapeid;
+                }
+                if (lineColor !== undefined) {
+                    context.stroke();
+                }
+                if (fillColor !== undefined) {
+                    context.fill();
+                }
+            },
+
+            _drawPieSlice: function (shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) {
+                var context = this._getContext(lineColor, fillColor);
+                context.beginPath();
+                context.moveTo(x, y);
+                context.arc(x, y, radius, startAngle, endAngle, false);
+                context.lineTo(x, y);
+                context.closePath();
+                if (lineColor !== undefined) {
+                    context.stroke();
+                }
+                if (fillColor) {
+                    context.fill();
+                }
+                if (this.targetX !== undefined && this.targetY !== undefined &&
+                    context.isPointInPath(this.targetX, this.targetY)) {
+                    this.currentTargetShapeId = shapeid;
+                }
+            },
+
+            _drawRect: function (shapeid, x, y, width, height, lineColor, fillColor) {
+                return this._drawShape(shapeid, [[x, y], [x + width, y], [x + width, y + height], [x, y + height], [x, y]], lineColor, fillColor);
+            },
+
+            appendShape: function (shape) {
+                this.shapes[shape.id] = shape;
+                this.shapeseq.push(shape.id);
+                this.lastShapeId = shape.id;
+                return shape.id;
+            },
+
+            replaceWithShape: function (shapeid, shape) {
+                var shapeseq = this.shapeseq,
+                    i;
+                this.shapes[shape.id] = shape;
+                for (i = shapeseq.length; i--;) {
+                    if (shapeseq[i] == shapeid) {
+                        shapeseq[i] = shape.id;
+                    }
+                }
+                delete this.shapes[shapeid];
+            },
+
+            replaceWithShapes: function (shapeids, shapes) {
+                var shapeseq = this.shapeseq,
+                    shapemap = {},
+                    sid, i, first;
+
+                for (i = shapeids.length; i--;) {
+                    shapemap[shapeids[i]] = true;
+                }
+                for (i = shapeseq.length; i--;) {
+                    sid = shapeseq[i];
+                    if (shapemap[sid]) {
+                        shapeseq.splice(i, 1);
+                        delete this.shapes[sid];
+                        first = i;
+                    }
+                }
+                for (i = shapes.length; i--;) {
+                    shapeseq.splice(first, 0, shapes[i].id);
+                    this.shapes[shapes[i].id] = shapes[i];
+                }
+
+            },
+
+            insertAfterShape: function (shapeid, shape) {
+                var shapeseq = this.shapeseq,
+                    i;
+                for (i = shapeseq.length; i--;) {
+                    if (shapeseq[i] === shapeid) {
+                        shapeseq.splice(i + 1, 0, shape.id);
+                        this.shapes[shape.id] = shape;
+                        return;
+                    }
+                }
+            },
+
+            removeShapeId: function (shapeid) {
+                var shapeseq = this.shapeseq,
+                    i;
+                for (i = shapeseq.length; i--;) {
+                    if (shapeseq[i] === shapeid) {
+                        shapeseq.splice(i, 1);
+                        break;
+                    }
+                }
+                delete this.shapes[shapeid];
+            },
+
+            getShapeAt: function (el, x, y) {
+                this.targetX = x;
+                this.targetY = y;
+                this.render();
+                return this.currentTargetShapeId;
+            },
+
+            render: function () {
+                var shapeseq = this.shapeseq,
+                    shapes = this.shapes,
+                    shapeCount = shapeseq.length,
+                    context = this._getContext(),
+                    shapeid, shape, i;
+                context.clearRect(0, 0, this.pixelWidth, this.pixelHeight);
+                for (i = 0; i < shapeCount; i++) {
+                    shapeid = shapeseq[i];
+                    shape = shapes[shapeid];
+                    this['_draw' + shape.type].apply(this, shape.args);
+                }
+                if (!this.interact) {
+                    // not interactive so no need to keep the shapes array
+                    this.shapes = {};
+                    this.shapeseq = [];
+                }
+            }
+
+        });
+
+        VCanvas_vml = createClass(VCanvas_base, {
+            init: function (width, height, target) {
+                var groupel;
+                VCanvas_vml._super.init.call(this, width, height, target);
+                if (target[0]) {
+                    target = target[0];
+                }
+                $.data(target, '_jqs_vcanvas', this);
+                this.canvas = document.createElement('span');
+                $(this.canvas).css({ display: 'inline-block', position: 'relative', overflow: 'hidden', width: width, height: height, margin: '0px', padding: '0px', verticalAlign: 'top'});
+                this._insert(this.canvas, target);
+                this._calculatePixelDims(width, height, this.canvas);
+                this.canvas.width = this.pixelWidth;
+                this.canvas.height = this.pixelHeight;
+                groupel = '<v:group coordorigin="0 0" coordsize="' + this.pixelWidth + ' ' + this.pixelHeight + '"' +
+                    ' style="position:absolute;top:0;left:0;width:' + this.pixelWidth + 'px;height=' + this.pixelHeight + 'px;"></v:group>';
+                this.canvas.insertAdjacentHTML('beforeEnd', groupel);
+                this.group = $(this.canvas).children()[0];
+                this.rendered = false;
+                this.prerender = '';
+            },
+
+            _drawShape: function (shapeid, path, lineColor, fillColor, lineWidth) {
+                var vpath = [],
+                    initial, stroke, fill, closed, vel, plen, i;
+                for (i = 0, plen = path.length; i < plen; i++) {
+                    vpath[i] = '' + (path[i][0]) + ',' + (path[i][1]);
+                }
+                initial = vpath.splice(0, 1);
+                lineWidth = lineWidth === undefined ? 1 : lineWidth;
+                stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="' + lineWidth + 'px" strokeColor="' + lineColor + '" ';
+                fill = fillColor === undefined ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" ';
+                closed = vpath[0] === vpath[vpath.length - 1] ? 'x ' : '';
+                vel = '<v:shape coordorigin="0 0" coordsize="' + this.pixelWidth + ' ' + this.pixelHeight + '" ' +
+                    ' id="jqsshape' + shapeid + '" ' +
+                    stroke +
+                    fill +
+                    ' style="position:absolute;left:0px;top:0px;height:' + this.pixelHeight + 'px;width:' + this.pixelWidth + 'px;padding:0px;margin:0px;" ' +
+                    ' path="m ' + initial + ' l ' + vpath.join(', ') + ' ' + closed + 'e">' +
+                    ' </v:shape>';
+                return vel;
+            },
+
+            _drawCircle: function (shapeid, x, y, radius, lineColor, fillColor, lineWidth) {
+                var stroke, fill, vel;
+                x -= radius;
+                y -= radius;
+                stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="' + lineWidth + 'px" strokeColor="' + lineColor + '" ';
+                fill = fillColor === undefined ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" ';
+                vel = '<v:oval ' +
+                    ' id="jqsshape' + shapeid + '" ' +
+                    stroke +
+                    fill +
+                    ' style="position:absolute;top:' + y + 'px; left:' + x + 'px; width:' + (radius * 2) + 'px; height:' + (radius * 2) + 'px"></v:oval>';
+                return vel;
+
+            },
+
+            _drawPieSlice: function (shapeid, x, y, radius, startAngle, endAngle, lineColor, fillColor) {
+                var vpath, startx, starty, endx, endy, stroke, fill, vel;
+                if (startAngle === endAngle) {
+                    return '';  // VML seems to have problem when start angle equals end angle.
+                }
+                if ((endAngle - startAngle) === (2 * Math.PI)) {
+                    startAngle = 0.0;  // VML seems to have a problem when drawing a full circle that doesn't start 0
+                    endAngle = (2 * Math.PI);
+                }
+
+                startx = x + Math.round(Math.cos(startAngle) * radius);
+                starty = y + Math.round(Math.sin(startAngle) * radius);
+                endx = x + Math.round(Math.cos(endAngle) * radius);
+                endy = y + Math.round(Math.sin(endAngle) * radius);
+
+                if (startx === endx && starty === endy) {
+                    if ((endAngle - startAngle) < Math.PI) {
+                        // Prevent very small slices from being mistaken as a whole pie
+                        return '';
+                    }
+                    // essentially going to be the entire circle, so ignore startAngle
+                    startx = endx = x + radius;
+                    starty = endy = y;
+                }
+
+                if (startx === endx && starty === endy && (endAngle - startAngle) < Math.PI) {
+                    return '';
+                }
+
+                vpath = [x - radius, y - radius, x + radius, y + radius, startx, starty, endx, endy];
+                stroke = lineColor === undefined ? ' stroked="false" ' : ' strokeWeight="1px" strokeColor="' + lineColor + '" ';
+                fill = fillColor === undefined ? ' filled="false"' : ' fillColor="' + fillColor + '" filled="true" ';
+                vel = '<v:shape coordorigin="0 0" coordsize="' + this.pixelWidth + ' ' + this.pixelHeight + '" ' +
+                    ' id="jqsshape' + shapeid + '" ' +
+                    stroke +
+                    fill +
+                    ' style="position:absolute;left:0px;top:0px;height:' + this.pixelHeight + 'px;width:' + this.pixelWidth + 'px;padding:0px;margin:0px;" ' +
+                    ' path="m ' + x + ',' + y + ' wa ' + vpath.join(', ') + ' x e">' +
+                    ' </v:shape>';
+                return vel;
+            },
+
+            _drawRect: function (shapeid, x, y, width, height, lineColor, fillColor) {
+                return this._drawShape(shapeid, [[x, y], [x, y + height], [x + width, y + height], [x + width, y], [x, y]], lineColor, fillColor);
+            },
+
+            reset: function () {
+                this.group.innerHTML = '';
+            },
+
+            appendShape: function (shape) {
+                var vel = this['_draw' + shape.type].apply(this, shape.args);
+                if (this.rendered) {
+                    this.group.insertAdjacentHTML('beforeEnd', vel);
+                } else {
+                    this.prerender += vel;
+                }
+                this.lastShapeId = shape.id;
+                return shape.id;
+            },
+
+            replaceWithShape: function (shapeid, shape) {
+                var existing = $('#jqsshape' + shapeid),
+                    vel = this['_draw' + shape.type].apply(this, shape.args);
+                existing[0].outerHTML = vel;
+            },
+
+            replaceWithShapes: function (shapeids, shapes) {
+                // replace the first shapeid with all the new shapes then toast the remaining old shapes
+                var existing = $('#jqsshape' + shapeids[0]),
+                    replace = '',
+                    slen = shapes.length,
+                    i;
+                for (i = 0; i < slen; i++) {
+                    replace += this['_draw' + shapes[i].type].apply(this, shapes[i].args);
+                }
+                existing[0].outerHTML = replace;
+                for (i = 1; i < shapeids.length; i++) {
+                    $('#jqsshape' + shapeids[i]).remove();
+                }
+            },
+
+            insertAfterShape: function (shapeid, shape) {
+                var existing = $('#jqsshape' + shapeid),
+                    vel = this['_draw' + shape.type].apply(this, shape.args);
+                existing[0].insertAdjacentHTML('afterEnd', vel);
+            },
+
+            removeShapeId: function (shapeid) {
+                var existing = $('#jqsshape' + shapeid);
+                this.group.removeChild(existing[0]);
+            },
+
+            getShapeAt: function (el, x, y) {
+                var shapeid = el.id.substr(8);
+                return shapeid;
+            },
+
+            render: function () {
+                if (!this.rendered) {
+                    // batch the intial render into a single repaint
+                    this.group.innerHTML = this.prerender;
+                    this.rendered = true;
+                }
+            }
+        });
+
+    }))}(document, Math));

+ 56 - 0
dashboard/assets/javascripts/lodash.min.js

@@ -0,0 +1,56 @@
+/**
+ * @license
+ * Lo-Dash 2.4.1 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs.org/LICENSE
+ * Build: `lodash modern -o ./dist/lodash.js`
+ */
+;(function(){function n(n,t,e){e=(e||0)-1;for(var r=n?n.length:0;++e<r;)if(n[e]===t)return e;return-1}function t(t,e){var r=typeof e;if(t=t.l,"boolean"==r||null==e)return t[e]?0:-1;"number"!=r&&"string"!=r&&(r="object");var u="number"==r?e:m+e;return t=(t=t[r])&&t[u],"object"==r?t&&-1<n(t,e)?0:-1:t?0:-1}function e(n){var t=this.l,e=typeof n;if("boolean"==e||null==n)t[n]=true;else{"number"!=e&&"string"!=e&&(e="object");var r="number"==e?n:m+n,t=t[e]||(t[e]={});"object"==e?(t[r]||(t[r]=[])).push(n):t[r]=true
+}}function r(n){return n.charCodeAt(0)}function u(n,t){for(var e=n.m,r=t.m,u=-1,o=e.length;++u<o;){var i=e[u],a=r[u];if(i!==a){if(i>a||typeof i=="undefined")return 1;if(i<a||typeof a=="undefined")return-1}}return n.n-t.n}function o(n){var t=-1,r=n.length,u=n[0],o=n[r/2|0],i=n[r-1];if(u&&typeof u=="object"&&o&&typeof o=="object"&&i&&typeof i=="object")return false;for(u=f(),u["false"]=u["null"]=u["true"]=u.undefined=false,o=f(),o.k=n,o.l=u,o.push=e;++t<r;)o.push(n[t]);return o}function i(n){return"\\"+U[n]
+}function a(){return h.pop()||[]}function f(){return g.pop()||{k:null,l:null,m:null,"false":false,n:0,"null":false,number:null,object:null,push:null,string:null,"true":false,undefined:false,o:null}}function l(n){n.length=0,h.length<_&&h.push(n)}function c(n){var t=n.l;t&&c(t),n.k=n.l=n.m=n.object=n.number=n.string=n.o=null,g.length<_&&g.push(n)}function p(n,t,e){t||(t=0),typeof e=="undefined"&&(e=n?n.length:0);var r=-1;e=e-t||0;for(var u=Array(0>e?0:e);++r<e;)u[r]=n[t+r];return u}function s(e){function h(n,t,e){if(!n||!V[typeof n])return n;
+    t=t&&typeof e=="undefined"?t:tt(t,e,3);for(var r=-1,u=V[typeof n]&&Fe(n),o=u?u.length:0;++r<o&&(e=u[r],false!==t(n[e],e,n)););return n}function g(n,t,e){var r;if(!n||!V[typeof n])return n;t=t&&typeof e=="undefined"?t:tt(t,e,3);for(r in n)if(false===t(n[r],r,n))break;return n}function _(n,t,e){var r,u=n,o=u;if(!u)return o;for(var i=arguments,a=0,f=typeof e=="number"?2:i.length;++a<f;)if((u=i[a])&&V[typeof u])for(var l=-1,c=V[typeof u]&&Fe(u),p=c?c.length:0;++l<p;)r=c[l],"undefined"==typeof o[r]&&(o[r]=u[r]);
+    return o}function U(n,t,e){var r,u=n,o=u;if(!u)return o;var i=arguments,a=0,f=typeof e=="number"?2:i.length;if(3<f&&"function"==typeof i[f-2])var l=tt(i[--f-1],i[f--],2);else 2<f&&"function"==typeof i[f-1]&&(l=i[--f]);for(;++a<f;)if((u=i[a])&&V[typeof u])for(var c=-1,p=V[typeof u]&&Fe(u),s=p?p.length:0;++c<s;)r=p[c],o[r]=l?l(o[r],u[r]):u[r];return o}function H(n){var t,e=[];if(!n||!V[typeof n])return e;for(t in n)me.call(n,t)&&e.push(t);return e}function J(n){return n&&typeof n=="object"&&!Te(n)&&me.call(n,"__wrapped__")?n:new Q(n)
+}function Q(n,t){this.__chain__=!!t,this.__wrapped__=n}function X(n){function t(){if(r){var n=p(r);be.apply(n,arguments)}if(this instanceof t){var o=nt(e.prototype),n=e.apply(o,n||arguments);return wt(n)?n:o}return e.apply(u,n||arguments)}var e=n[0],r=n[2],u=n[4];return $e(t,n),t}function Z(n,t,e,r,u){if(e){var o=e(n);if(typeof o!="undefined")return o}if(!wt(n))return n;var i=ce.call(n);if(!K[i])return n;var f=Ae[i];switch(i){case T:case F:return new f(+n);case W:case P:return new f(n);case z:return o=f(n.source,C.exec(n)),o.lastIndex=n.lastIndex,o
+}if(i=Te(n),t){var c=!r;r||(r=a()),u||(u=a());for(var s=r.length;s--;)if(r[s]==n)return u[s];o=i?f(n.length):{}}else o=i?p(n):U({},n);return i&&(me.call(n,"index")&&(o.index=n.index),me.call(n,"input")&&(o.input=n.input)),t?(r.push(n),u.push(o),(i?St:h)(n,function(n,i){o[i]=Z(n,t,e,r,u)}),c&&(l(r),l(u)),o):o}function nt(n){return wt(n)?ke(n):{}}function tt(n,t,e){if(typeof n!="function")return Ut;if(typeof t=="undefined"||!("prototype"in n))return n;var r=n.__bindData__;if(typeof r=="undefined"&&(De.funcNames&&(r=!n.name),r=r||!De.funcDecomp,!r)){var u=ge.call(n);
+    De.funcNames||(r=!O.test(u)),r||(r=E.test(u),$e(n,r))}if(false===r||true!==r&&1&r[1])return n;switch(e){case 1:return function(e){return n.call(t,e)};case 2:return function(e,r){return n.call(t,e,r)};case 3:return function(e,r,u){return n.call(t,e,r,u)};case 4:return function(e,r,u,o){return n.call(t,e,r,u,o)}}return Mt(n,t)}function et(n){function t(){var n=f?i:this;if(u){var h=p(u);be.apply(h,arguments)}return(o||c)&&(h||(h=p(arguments)),o&&be.apply(h,o),c&&h.length<a)?(r|=16,et([e,s?r:-4&r,h,null,i,a])):(h||(h=arguments),l&&(e=n[v]),this instanceof t?(n=nt(e.prototype),h=e.apply(n,h),wt(h)?h:n):e.apply(n,h))
+}var e=n[0],r=n[1],u=n[2],o=n[3],i=n[4],a=n[5],f=1&r,l=2&r,c=4&r,s=8&r,v=e;return $e(t,n),t}function rt(e,r){var u=-1,i=st(),a=e?e.length:0,f=a>=b&&i===n,l=[];if(f){var p=o(r);p?(i=t,r=p):f=false}for(;++u<a;)p=e[u],0>i(r,p)&&l.push(p);return f&&c(r),l}function ut(n,t,e,r){r=(r||0)-1;for(var u=n?n.length:0,o=[];++r<u;){var i=n[r];if(i&&typeof i=="object"&&typeof i.length=="number"&&(Te(i)||yt(i))){t||(i=ut(i,t,e));var a=-1,f=i.length,l=o.length;for(o.length+=f;++a<f;)o[l++]=i[a]}else e||o.push(i)}return o
+}function ot(n,t,e,r,u,o){if(e){var i=e(n,t);if(typeof i!="undefined")return!!i}if(n===t)return 0!==n||1/n==1/t;if(n===n&&!(n&&V[typeof n]||t&&V[typeof t]))return false;if(null==n||null==t)return n===t;var f=ce.call(n),c=ce.call(t);if(f==D&&(f=q),c==D&&(c=q),f!=c)return false;switch(f){case T:case F:return+n==+t;case W:return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case z:case P:return n==oe(t)}if(c=f==$,!c){var p=me.call(n,"__wrapped__"),s=me.call(t,"__wrapped__");if(p||s)return ot(p?n.__wrapped__:n,s?t.__wrapped__:t,e,r,u,o);
+    if(f!=q)return false;if(f=n.constructor,p=t.constructor,f!=p&&!(dt(f)&&f instanceof f&&dt(p)&&p instanceof p)&&"constructor"in n&&"constructor"in t)return false}for(f=!u,u||(u=a()),o||(o=a()),p=u.length;p--;)if(u[p]==n)return o[p]==t;var v=0,i=true;if(u.push(n),o.push(t),c){if(p=n.length,v=t.length,(i=v==p)||r)for(;v--;)if(c=p,s=t[v],r)for(;c--&&!(i=ot(n[c],s,e,r,u,o)););else if(!(i=ot(n[v],s,e,r,u,o)))break}else g(t,function(t,a,f){return me.call(f,a)?(v++,i=me.call(n,a)&&ot(n[a],t,e,r,u,o)):void 0}),i&&!r&&g(n,function(n,t,e){return me.call(e,t)?i=-1<--v:void 0
+});return u.pop(),o.pop(),f&&(l(u),l(o)),i}function it(n,t,e,r,u){(Te(t)?St:h)(t,function(t,o){var i,a,f=t,l=n[o];if(t&&((a=Te(t))||Pe(t))){for(f=r.length;f--;)if(i=r[f]==t){l=u[f];break}if(!i){var c;e&&(f=e(l,t),c=typeof f!="undefined")&&(l=f),c||(l=a?Te(l)?l:[]:Pe(l)?l:{}),r.push(t),u.push(l),c||it(l,t,e,r,u)}}else e&&(f=e(l,t),typeof f=="undefined"&&(f=t)),typeof f!="undefined"&&(l=f);n[o]=l})}function at(n,t){return n+he(Re()*(t-n+1))}function ft(e,r,u){var i=-1,f=st(),p=e?e.length:0,s=[],v=!r&&p>=b&&f===n,h=u||v?a():s;
+    for(v&&(h=o(h),f=t);++i<p;){var g=e[i],y=u?u(g,i,e):g;(r?!i||h[h.length-1]!==y:0>f(h,y))&&((u||v)&&h.push(y),s.push(g))}return v?(l(h.k),c(h)):u&&l(h),s}function lt(n){return function(t,e,r){var u={};e=J.createCallback(e,r,3),r=-1;var o=t?t.length:0;if(typeof o=="number")for(;++r<o;){var i=t[r];n(u,i,e(i,r,t),t)}else h(t,function(t,r,o){n(u,t,e(t,r,o),o)});return u}}function ct(n,t,e,r,u,o){var i=1&t,a=4&t,f=16&t,l=32&t;if(!(2&t||dt(n)))throw new ie;f&&!e.length&&(t&=-17,f=e=false),l&&!r.length&&(t&=-33,l=r=false);
+    var c=n&&n.__bindData__;return c&&true!==c?(c=p(c),c[2]&&(c[2]=p(c[2])),c[3]&&(c[3]=p(c[3])),!i||1&c[1]||(c[4]=u),!i&&1&c[1]&&(t|=8),!a||4&c[1]||(c[5]=o),f&&be.apply(c[2]||(c[2]=[]),e),l&&we.apply(c[3]||(c[3]=[]),r),c[1]|=t,ct.apply(null,c)):(1==t||17===t?X:et)([n,t,e,r,u,o])}function pt(n){return Be[n]}function st(){var t=(t=J.indexOf)===Wt?n:t;return t}function vt(n){return typeof n=="function"&&pe.test(n)}function ht(n){var t,e;return n&&ce.call(n)==q&&(t=n.constructor,!dt(t)||t instanceof t)?(g(n,function(n,t){e=t
+}),typeof e=="undefined"||me.call(n,e)):false}function gt(n){return We[n]}function yt(n){return n&&typeof n=="object"&&typeof n.length=="number"&&ce.call(n)==D||false}function mt(n,t,e){var r=Fe(n),u=r.length;for(t=tt(t,e,3);u--&&(e=r[u],false!==t(n[e],e,n)););return n}function bt(n){var t=[];return g(n,function(n,e){dt(n)&&t.push(e)}),t.sort()}function _t(n){for(var t=-1,e=Fe(n),r=e.length,u={};++t<r;){var o=e[t];u[n[o]]=o}return u}function dt(n){return typeof n=="function"}function wt(n){return!(!n||!V[typeof n])
+}function jt(n){return typeof n=="number"||n&&typeof n=="object"&&ce.call(n)==W||false}function kt(n){return typeof n=="string"||n&&typeof n=="object"&&ce.call(n)==P||false}function xt(n){for(var t=-1,e=Fe(n),r=e.length,u=Xt(r);++t<r;)u[t]=n[e[t]];return u}function Ct(n,t,e){var r=-1,u=st(),o=n?n.length:0,i=false;return e=(0>e?Ie(0,o+e):e)||0,Te(n)?i=-1<u(n,t,e):typeof o=="number"?i=-1<(kt(n)?n.indexOf(t,e):u(n,t,e)):h(n,function(n){return++r<e?void 0:!(i=n===t)}),i}function Ot(n,t,e){var r=true;t=J.createCallback(t,e,3),e=-1;
+    var u=n?n.length:0;if(typeof u=="number")for(;++e<u&&(r=!!t(n[e],e,n)););else h(n,function(n,e,u){return r=!!t(n,e,u)});return r}function Nt(n,t,e){var r=[];t=J.createCallback(t,e,3),e=-1;var u=n?n.length:0;if(typeof u=="number")for(;++e<u;){var o=n[e];t(o,e,n)&&r.push(o)}else h(n,function(n,e,u){t(n,e,u)&&r.push(n)});return r}function It(n,t,e){t=J.createCallback(t,e,3),e=-1;var r=n?n.length:0;if(typeof r!="number"){var u;return h(n,function(n,e,r){return t(n,e,r)?(u=n,false):void 0}),u}for(;++e<r;){var o=n[e];
+    if(t(o,e,n))return o}}function St(n,t,e){var r=-1,u=n?n.length:0;if(t=t&&typeof e=="undefined"?t:tt(t,e,3),typeof u=="number")for(;++r<u&&false!==t(n[r],r,n););else h(n,t);return n}function Et(n,t,e){var r=n?n.length:0;if(t=t&&typeof e=="undefined"?t:tt(t,e,3),typeof r=="number")for(;r--&&false!==t(n[r],r,n););else{var u=Fe(n),r=u.length;h(n,function(n,e,o){return e=u?u[--r]:--r,t(o[e],e,o)})}return n}function Rt(n,t,e){var r=-1,u=n?n.length:0;if(t=J.createCallback(t,e,3),typeof u=="number")for(var o=Xt(u);++r<u;)o[r]=t(n[r],r,n);
+else o=[],h(n,function(n,e,u){o[++r]=t(n,e,u)});return o}function At(n,t,e){var u=-1/0,o=u;if(typeof t!="function"&&e&&e[t]===n&&(t=null),null==t&&Te(n)){e=-1;for(var i=n.length;++e<i;){var a=n[e];a>o&&(o=a)}}else t=null==t&&kt(n)?r:J.createCallback(t,e,3),St(n,function(n,e,r){e=t(n,e,r),e>u&&(u=e,o=n)});return o}function Dt(n,t,e,r){if(!n)return e;var u=3>arguments.length;t=J.createCallback(t,r,4);var o=-1,i=n.length;if(typeof i=="number")for(u&&(e=n[++o]);++o<i;)e=t(e,n[o],o,n);else h(n,function(n,r,o){e=u?(u=false,n):t(e,n,r,o)
+});return e}function $t(n,t,e,r){var u=3>arguments.length;return t=J.createCallback(t,r,4),Et(n,function(n,r,o){e=u?(u=false,n):t(e,n,r,o)}),e}function Tt(n){var t=-1,e=n?n.length:0,r=Xt(typeof e=="number"?e:0);return St(n,function(n){var e=at(0,++t);r[t]=r[e],r[e]=n}),r}function Ft(n,t,e){var r;t=J.createCallback(t,e,3),e=-1;var u=n?n.length:0;if(typeof u=="number")for(;++e<u&&!(r=t(n[e],e,n)););else h(n,function(n,e,u){return!(r=t(n,e,u))});return!!r}function Bt(n,t,e){var r=0,u=n?n.length:0;if(typeof t!="number"&&null!=t){var o=-1;
+    for(t=J.createCallback(t,e,3);++o<u&&t(n[o],o,n);)r++}else if(r=t,null==r||e)return n?n[0]:v;return p(n,0,Se(Ie(0,r),u))}function Wt(t,e,r){if(typeof r=="number"){var u=t?t.length:0;r=0>r?Ie(0,u+r):r||0}else if(r)return r=zt(t,e),t[r]===e?r:-1;return n(t,e,r)}function qt(n,t,e){if(typeof t!="number"&&null!=t){var r=0,u=-1,o=n?n.length:0;for(t=J.createCallback(t,e,3);++u<o&&t(n[u],u,n);)r++}else r=null==t||e?1:Ie(0,t);return p(n,r)}function zt(n,t,e,r){var u=0,o=n?n.length:u;for(e=e?J.createCallback(e,r,1):Ut,t=e(t);u<o;)r=u+o>>>1,e(n[r])<t?u=r+1:o=r;
+    return u}function Pt(n,t,e,r){return typeof t!="boolean"&&null!=t&&(r=e,e=typeof t!="function"&&r&&r[t]===n?null:t,t=false),null!=e&&(e=J.createCallback(e,r,3)),ft(n,t,e)}function Kt(){for(var n=1<arguments.length?arguments:arguments[0],t=-1,e=n?At(Ve(n,"length")):0,r=Xt(0>e?0:e);++t<e;)r[t]=Ve(n,t);return r}function Lt(n,t){var e=-1,r=n?n.length:0,u={};for(t||!r||Te(n[0])||(t=[]);++e<r;){var o=n[e];t?u[o]=t[e]:o&&(u[o[0]]=o[1])}return u}function Mt(n,t){return 2<arguments.length?ct(n,17,p(arguments,2),null,t):ct(n,1,null,null,t)
+}function Vt(n,t,e){function r(){c&&ve(c),i=c=p=v,(g||h!==t)&&(s=Ue(),a=n.apply(l,o),c||i||(o=l=null))}function u(){var e=t-(Ue()-f);0<e?c=_e(u,e):(i&&ve(i),e=p,i=c=p=v,e&&(s=Ue(),a=n.apply(l,o),c||i||(o=l=null)))}var o,i,a,f,l,c,p,s=0,h=false,g=true;if(!dt(n))throw new ie;if(t=Ie(0,t)||0,true===e)var y=true,g=false;else wt(e)&&(y=e.leading,h="maxWait"in e&&(Ie(t,e.maxWait)||0),g="trailing"in e?e.trailing:g);return function(){if(o=arguments,f=Ue(),l=this,p=g&&(c||!y),false===h)var e=y&&!c;else{i||y||(s=f);var v=h-(f-s),m=0>=v;
+    m?(i&&(i=ve(i)),s=f,a=n.apply(l,o)):i||(i=_e(r,v))}return m&&c?c=ve(c):c||t===h||(c=_e(u,t)),e&&(m=true,a=n.apply(l,o)),!m||c||i||(o=l=null),a}}function Ut(n){return n}function Gt(n,t,e){var r=true,u=t&&bt(t);t&&(e||u.length)||(null==e&&(e=t),o=Q,t=n,n=J,u=bt(t)),false===e?r=false:wt(e)&&"chain"in e&&(r=e.chain);var o=n,i=dt(o);St(u,function(e){var u=n[e]=t[e];i&&(o.prototype[e]=function(){var t=this.__chain__,e=this.__wrapped__,i=[e];if(be.apply(i,arguments),i=u.apply(n,i),r||t){if(e===i&&wt(i))return this;
+    i=new o(i),i.__chain__=t}return i})})}function Ht(){}function Jt(n){return function(t){return t[n]}}function Qt(){return this.__wrapped__}e=e?Y.defaults(G.Object(),e,Y.pick(G,A)):G;var Xt=e.Array,Yt=e.Boolean,Zt=e.Date,ne=e.Function,te=e.Math,ee=e.Number,re=e.Object,ue=e.RegExp,oe=e.String,ie=e.TypeError,ae=[],fe=re.prototype,le=e._,ce=fe.toString,pe=ue("^"+oe(ce).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),se=te.ceil,ve=e.clearTimeout,he=te.floor,ge=ne.prototype.toString,ye=vt(ye=re.getPrototypeOf)&&ye,me=fe.hasOwnProperty,be=ae.push,_e=e.setTimeout,de=ae.splice,we=ae.unshift,je=function(){try{var n={},t=vt(t=re.defineProperty)&&t,e=t(n,n,n)&&t
+}catch(r){}return e}(),ke=vt(ke=re.create)&&ke,xe=vt(xe=Xt.isArray)&&xe,Ce=e.isFinite,Oe=e.isNaN,Ne=vt(Ne=re.keys)&&Ne,Ie=te.max,Se=te.min,Ee=e.parseInt,Re=te.random,Ae={};Ae[$]=Xt,Ae[T]=Yt,Ae[F]=Zt,Ae[B]=ne,Ae[q]=re,Ae[W]=ee,Ae[z]=ue,Ae[P]=oe,Q.prototype=J.prototype;var De=J.support={};De.funcDecomp=!vt(e.a)&&E.test(s),De.funcNames=typeof ne.name=="string",J.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:N,variable:"",imports:{_:J}},ke||(nt=function(){function n(){}return function(t){if(wt(t)){n.prototype=t;
+    var r=new n;n.prototype=null}return r||e.Object()}}());var $e=je?function(n,t){M.value=t,je(n,"__bindData__",M)}:Ht,Te=xe||function(n){return n&&typeof n=="object"&&typeof n.length=="number"&&ce.call(n)==$||false},Fe=Ne?function(n){return wt(n)?Ne(n):[]}:H,Be={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},We=_t(Be),qe=ue("("+Fe(We).join("|")+")","g"),ze=ue("["+Fe(Be).join("")+"]","g"),Pe=ye?function(n){if(!n||ce.call(n)!=q)return false;var t=n.valueOf,e=vt(t)&&(e=ye(t))&&ye(e);return e?n==e||ye(n)==e:ht(n)
+}:ht,Ke=lt(function(n,t,e){me.call(n,e)?n[e]++:n[e]=1}),Le=lt(function(n,t,e){(me.call(n,e)?n[e]:n[e]=[]).push(t)}),Me=lt(function(n,t,e){n[e]=t}),Ve=Rt,Ue=vt(Ue=Zt.now)&&Ue||function(){return(new Zt).getTime()},Ge=8==Ee(d+"08")?Ee:function(n,t){return Ee(kt(n)?n.replace(I,""):n,t||0)};return J.after=function(n,t){if(!dt(t))throw new ie;return function(){return 1>--n?t.apply(this,arguments):void 0}},J.assign=U,J.at=function(n){for(var t=arguments,e=-1,r=ut(t,true,false,1),t=t[2]&&t[2][t[1]]===n?1:r.length,u=Xt(t);++e<t;)u[e]=n[r[e]];
+    return u},J.bind=Mt,J.bindAll=function(n){for(var t=1<arguments.length?ut(arguments,true,false,1):bt(n),e=-1,r=t.length;++e<r;){var u=t[e];n[u]=ct(n[u],1,null,null,n)}return n},J.bindKey=function(n,t){return 2<arguments.length?ct(t,19,p(arguments,2),null,n):ct(t,3,null,null,n)},J.chain=function(n){return n=new Q(n),n.__chain__=true,n},J.compact=function(n){for(var t=-1,e=n?n.length:0,r=[];++t<e;){var u=n[t];u&&r.push(u)}return r},J.compose=function(){for(var n=arguments,t=n.length;t--;)if(!dt(n[t]))throw new ie;
+    return function(){for(var t=arguments,e=n.length;e--;)t=[n[e].apply(this,t)];return t[0]}},J.constant=function(n){return function(){return n}},J.countBy=Ke,J.create=function(n,t){var e=nt(n);return t?U(e,t):e},J.createCallback=function(n,t,e){var r=typeof n;if(null==n||"function"==r)return tt(n,t,e);if("object"!=r)return Jt(n);var u=Fe(n),o=u[0],i=n[o];return 1!=u.length||i!==i||wt(i)?function(t){for(var e=u.length,r=false;e--&&(r=ot(t[u[e]],n[u[e]],null,true)););return r}:function(n){return n=n[o],i===n&&(0!==i||1/i==1/n)
+}},J.curry=function(n,t){return t=typeof t=="number"?t:+t||n.length,ct(n,4,null,null,null,t)},J.debounce=Vt,J.defaults=_,J.defer=function(n){if(!dt(n))throw new ie;var t=p(arguments,1);return _e(function(){n.apply(v,t)},1)},J.delay=function(n,t){if(!dt(n))throw new ie;var e=p(arguments,2);return _e(function(){n.apply(v,e)},t)},J.difference=function(n){return rt(n,ut(arguments,true,true,1))},J.filter=Nt,J.flatten=function(n,t,e,r){return typeof t!="boolean"&&null!=t&&(r=e,e=typeof t!="function"&&r&&r[t]===n?null:t,t=false),null!=e&&(n=Rt(n,e,r)),ut(n,t)
+},J.forEach=St,J.forEachRight=Et,J.forIn=g,J.forInRight=function(n,t,e){var r=[];g(n,function(n,t){r.push(t,n)});var u=r.length;for(t=tt(t,e,3);u--&&false!==t(r[u--],r[u],n););return n},J.forOwn=h,J.forOwnRight=mt,J.functions=bt,J.groupBy=Le,J.indexBy=Me,J.initial=function(n,t,e){var r=0,u=n?n.length:0;if(typeof t!="number"&&null!=t){var o=u;for(t=J.createCallback(t,e,3);o--&&t(n[o],o,n);)r++}else r=null==t||e?1:t||r;return p(n,0,Se(Ie(0,u-r),u))},J.intersection=function(){for(var e=[],r=-1,u=arguments.length,i=a(),f=st(),p=f===n,s=a();++r<u;){var v=arguments[r];
+    (Te(v)||yt(v))&&(e.push(v),i.push(p&&v.length>=b&&o(r?e[r]:s)))}var p=e[0],h=-1,g=p?p.length:0,y=[];n:for(;++h<g;){var m=i[0],v=p[h];if(0>(m?t(m,v):f(s,v))){for(r=u,(m||s).push(v);--r;)if(m=i[r],0>(m?t(m,v):f(e[r],v)))continue n;y.push(v)}}for(;u--;)(m=i[u])&&c(m);return l(i),l(s),y},J.invert=_t,J.invoke=function(n,t){var e=p(arguments,2),r=-1,u=typeof t=="function",o=n?n.length:0,i=Xt(typeof o=="number"?o:0);return St(n,function(n){i[++r]=(u?t:n[t]).apply(n,e)}),i},J.keys=Fe,J.map=Rt,J.mapValues=function(n,t,e){var r={};
+    return t=J.createCallback(t,e,3),h(n,function(n,e,u){r[e]=t(n,e,u)}),r},J.max=At,J.memoize=function(n,t){function e(){var r=e.cache,u=t?t.apply(this,arguments):m+arguments[0];return me.call(r,u)?r[u]:r[u]=n.apply(this,arguments)}if(!dt(n))throw new ie;return e.cache={},e},J.merge=function(n){var t=arguments,e=2;if(!wt(n))return n;if("number"!=typeof t[2]&&(e=t.length),3<e&&"function"==typeof t[e-2])var r=tt(t[--e-1],t[e--],2);else 2<e&&"function"==typeof t[e-1]&&(r=t[--e]);for(var t=p(arguments,1,e),u=-1,o=a(),i=a();++u<e;)it(n,t[u],r,o,i);
+    return l(o),l(i),n},J.min=function(n,t,e){var u=1/0,o=u;if(typeof t!="function"&&e&&e[t]===n&&(t=null),null==t&&Te(n)){e=-1;for(var i=n.length;++e<i;){var a=n[e];a<o&&(o=a)}}else t=null==t&&kt(n)?r:J.createCallback(t,e,3),St(n,function(n,e,r){e=t(n,e,r),e<u&&(u=e,o=n)});return o},J.omit=function(n,t,e){var r={};if(typeof t!="function"){var u=[];g(n,function(n,t){u.push(t)});for(var u=rt(u,ut(arguments,true,false,1)),o=-1,i=u.length;++o<i;){var a=u[o];r[a]=n[a]}}else t=J.createCallback(t,e,3),g(n,function(n,e,u){t(n,e,u)||(r[e]=n)
+});return r},J.once=function(n){var t,e;if(!dt(n))throw new ie;return function(){return t?e:(t=true,e=n.apply(this,arguments),n=null,e)}},J.pairs=function(n){for(var t=-1,e=Fe(n),r=e.length,u=Xt(r);++t<r;){var o=e[t];u[t]=[o,n[o]]}return u},J.partial=function(n){return ct(n,16,p(arguments,1))},J.partialRight=function(n){return ct(n,32,null,p(arguments,1))},J.pick=function(n,t,e){var r={};if(typeof t!="function")for(var u=-1,o=ut(arguments,true,false,1),i=wt(n)?o.length:0;++u<i;){var a=o[u];a in n&&(r[a]=n[a])
+}else t=J.createCallback(t,e,3),g(n,function(n,e,u){t(n,e,u)&&(r[e]=n)});return r},J.pluck=Ve,J.property=Jt,J.pull=function(n){for(var t=arguments,e=0,r=t.length,u=n?n.length:0;++e<r;)for(var o=-1,i=t[e];++o<u;)n[o]===i&&(de.call(n,o--,1),u--);return n},J.range=function(n,t,e){n=+n||0,e=typeof e=="number"?e:+e||1,null==t&&(t=n,n=0);var r=-1;t=Ie(0,se((t-n)/(e||1)));for(var u=Xt(t);++r<t;)u[r]=n,n+=e;return u},J.reject=function(n,t,e){return t=J.createCallback(t,e,3),Nt(n,function(n,e,r){return!t(n,e,r)
+})},J.remove=function(n,t,e){var r=-1,u=n?n.length:0,o=[];for(t=J.createCallback(t,e,3);++r<u;)e=n[r],t(e,r,n)&&(o.push(e),de.call(n,r--,1),u--);return o},J.rest=qt,J.shuffle=Tt,J.sortBy=function(n,t,e){var r=-1,o=Te(t),i=n?n.length:0,p=Xt(typeof i=="number"?i:0);for(o||(t=J.createCallback(t,e,3)),St(n,function(n,e,u){var i=p[++r]=f();o?i.m=Rt(t,function(t){return n[t]}):(i.m=a())[0]=t(n,e,u),i.n=r,i.o=n}),i=p.length,p.sort(u);i--;)n=p[i],p[i]=n.o,o||l(n.m),c(n);return p},J.tap=function(n,t){return t(n),n
+},J.throttle=function(n,t,e){var r=true,u=true;if(!dt(n))throw new ie;return false===e?r=false:wt(e)&&(r="leading"in e?e.leading:r,u="trailing"in e?e.trailing:u),L.leading=r,L.maxWait=t,L.trailing=u,Vt(n,t,L)},J.times=function(n,t,e){n=-1<(n=+n)?n:0;var r=-1,u=Xt(n);for(t=tt(t,e,1);++r<n;)u[r]=t(r);return u},J.toArray=function(n){return n&&typeof n.length=="number"?p(n):xt(n)},J.transform=function(n,t,e,r){var u=Te(n);if(null==e)if(u)e=[];else{var o=n&&n.constructor;e=nt(o&&o.prototype)}return t&&(t=J.createCallback(t,r,4),(u?St:h)(n,function(n,r,u){return t(e,n,r,u)
+})),e},J.union=function(){return ft(ut(arguments,true,true))},J.uniq=Pt,J.values=xt,J.where=Nt,J.without=function(n){return rt(n,p(arguments,1))},J.wrap=function(n,t){return ct(t,16,[n])},J.xor=function(){for(var n=-1,t=arguments.length;++n<t;){var e=arguments[n];if(Te(e)||yt(e))var r=r?ft(rt(r,e).concat(rt(e,r))):e}return r||[]},J.zip=Kt,J.zipObject=Lt,J.collect=Rt,J.drop=qt,J.each=St,J.eachRight=Et,J.extend=U,J.methods=bt,J.object=Lt,J.select=Nt,J.tail=qt,J.unique=Pt,J.unzip=Kt,Gt(J),J.clone=function(n,t,e,r){return typeof t!="boolean"&&null!=t&&(r=e,e=t,t=false),Z(n,t,typeof e=="function"&&tt(e,r,1))
+},J.cloneDeep=function(n,t,e){return Z(n,true,typeof t=="function"&&tt(t,e,1))},J.contains=Ct,J.escape=function(n){return null==n?"":oe(n).replace(ze,pt)},J.every=Ot,J.find=It,J.findIndex=function(n,t,e){var r=-1,u=n?n.length:0;for(t=J.createCallback(t,e,3);++r<u;)if(t(n[r],r,n))return r;return-1},J.findKey=function(n,t,e){var r;return t=J.createCallback(t,e,3),h(n,function(n,e,u){return t(n,e,u)?(r=e,false):void 0}),r},J.findLast=function(n,t,e){var r;return t=J.createCallback(t,e,3),Et(n,function(n,e,u){return t(n,e,u)?(r=n,false):void 0
+}),r},J.findLastIndex=function(n,t,e){var r=n?n.length:0;for(t=J.createCallback(t,e,3);r--;)if(t(n[r],r,n))return r;return-1},J.findLastKey=function(n,t,e){var r;return t=J.createCallback(t,e,3),mt(n,function(n,e,u){return t(n,e,u)?(r=e,false):void 0}),r},J.has=function(n,t){return n?me.call(n,t):false},J.identity=Ut,J.indexOf=Wt,J.isArguments=yt,J.isArray=Te,J.isBoolean=function(n){return true===n||false===n||n&&typeof n=="object"&&ce.call(n)==T||false},J.isDate=function(n){return n&&typeof n=="object"&&ce.call(n)==F||false
+},J.isElement=function(n){return n&&1===n.nodeType||false},J.isEmpty=function(n){var t=true;if(!n)return t;var e=ce.call(n),r=n.length;return e==$||e==P||e==D||e==q&&typeof r=="number"&&dt(n.splice)?!r:(h(n,function(){return t=false}),t)},J.isEqual=function(n,t,e,r){return ot(n,t,typeof e=="function"&&tt(e,r,2))},J.isFinite=function(n){return Ce(n)&&!Oe(parseFloat(n))},J.isFunction=dt,J.isNaN=function(n){return jt(n)&&n!=+n},J.isNull=function(n){return null===n},J.isNumber=jt,J.isObject=wt,J.isPlainObject=Pe,J.isRegExp=function(n){return n&&typeof n=="object"&&ce.call(n)==z||false
+},J.isString=kt,J.isUndefined=function(n){return typeof n=="undefined"},J.lastIndexOf=function(n,t,e){var r=n?n.length:0;for(typeof e=="number"&&(r=(0>e?Ie(0,r+e):Se(e,r-1))+1);r--;)if(n[r]===t)return r;return-1},J.mixin=Gt,J.noConflict=function(){return e._=le,this},J.noop=Ht,J.now=Ue,J.parseInt=Ge,J.random=function(n,t,e){var r=null==n,u=null==t;return null==e&&(typeof n=="boolean"&&u?(e=n,n=1):u||typeof t!="boolean"||(e=t,u=true)),r&&u&&(t=1),n=+n||0,u?(t=n,n=0):t=+t||0,e||n%1||t%1?(e=Re(),Se(n+e*(t-n+parseFloat("1e-"+((e+"").length-1))),t)):at(n,t)
+},J.reduce=Dt,J.reduceRight=$t,J.result=function(n,t){if(n){var e=n[t];return dt(e)?n[t]():e}},J.runInContext=s,J.size=function(n){var t=n?n.length:0;return typeof t=="number"?t:Fe(n).length},J.some=Ft,J.sortedIndex=zt,J.template=function(n,t,e){var r=J.templateSettings;n=oe(n||""),e=_({},e,r);var u,o=_({},e.imports,r.imports),r=Fe(o),o=xt(o),a=0,f=e.interpolate||S,l="__p+='",f=ue((e.escape||S).source+"|"+f.source+"|"+(f===N?x:S).source+"|"+(e.evaluate||S).source+"|$","g");n.replace(f,function(t,e,r,o,f,c){return r||(r=o),l+=n.slice(a,c).replace(R,i),e&&(l+="'+__e("+e+")+'"),f&&(u=true,l+="';"+f+";\n__p+='"),r&&(l+="'+((__t=("+r+"))==null?'':__t)+'"),a=c+t.length,t
+}),l+="';",f=e=e.variable,f||(e="obj",l="with("+e+"){"+l+"}"),l=(u?l.replace(w,""):l).replace(j,"$1").replace(k,"$1;"),l="function("+e+"){"+(f?"":e+"||("+e+"={});")+"var __t,__p='',__e=_.escape"+(u?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":";")+l+"return __p}";try{var c=ne(r,"return "+l).apply(v,o)}catch(p){throw p.source=l,p}return t?c(t):(c.source=l,c)},J.unescape=function(n){return null==n?"":oe(n).replace(qe,gt)},J.uniqueId=function(n){var t=++y;return oe(null==n?"":n)+t
+},J.all=Ot,J.any=Ft,J.detect=It,J.findWhere=It,J.foldl=Dt,J.foldr=$t,J.include=Ct,J.inject=Dt,Gt(function(){var n={};return h(J,function(t,e){J.prototype[e]||(n[e]=t)}),n}(),false),J.first=Bt,J.last=function(n,t,e){var r=0,u=n?n.length:0;if(typeof t!="number"&&null!=t){var o=u;for(t=J.createCallback(t,e,3);o--&&t(n[o],o,n);)r++}else if(r=t,null==r||e)return n?n[u-1]:v;return p(n,Ie(0,u-r))},J.sample=function(n,t,e){return n&&typeof n.length!="number"&&(n=xt(n)),null==t||e?n?n[at(0,n.length-1)]:v:(n=Tt(n),n.length=Se(Ie(0,t),n.length),n)
+},J.take=Bt,J.head=Bt,h(J,function(n,t){var e="sample"!==t;J.prototype[t]||(J.prototype[t]=function(t,r){var u=this.__chain__,o=n(this.__wrapped__,t,r);return u||null!=t&&(!r||e&&typeof t=="function")?new Q(o,u):o})}),J.VERSION="2.4.1",J.prototype.chain=function(){return this.__chain__=true,this},J.prototype.toString=function(){return oe(this.__wrapped__)},J.prototype.value=Qt,J.prototype.valueOf=Qt,St(["join","pop","shift"],function(n){var t=ae[n];J.prototype[n]=function(){var n=this.__chain__,e=t.apply(this.__wrapped__,arguments);
+    return n?new Q(e,n):e}}),St(["push","reverse","sort","unshift"],function(n){var t=ae[n];J.prototype[n]=function(){return t.apply(this.__wrapped__,arguments),this}}),St(["concat","slice","splice"],function(n){var t=ae[n];J.prototype[n]=function(){return new Q(t.apply(this.__wrapped__,arguments),this.__chain__)}}),J}var v,h=[],g=[],y=0,m=+new Date+"",b=75,_=40,d=" \t\x0B\f\xa0\ufeff\n\r\u2028\u2029\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000",w=/\b__p\+='';/g,j=/\b(__p\+=)''\+/g,k=/(__e\(.*?\)|\b__t\))\+'';/g,x=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,C=/\w*$/,O=/^\s*function[ \n\r\t]+\w/,N=/<%=([\s\S]+?)%>/g,I=RegExp("^["+d+"]*0+(?=.$)"),S=/($^)/,E=/\bthis\b/,R=/['\n\r\t\u2028\u2029\\]/g,A="Array Boolean Date Function Math Number Object RegExp String _ attachEvent clearTimeout isFinite isNaN parseInt setTimeout".split(" "),D="[object Arguments]",$="[object Array]",T="[object Boolean]",F="[object Date]",B="[object Function]",W="[object Number]",q="[object Object]",z="[object RegExp]",P="[object String]",K={};
+    K[B]=false,K[D]=K[$]=K[T]=K[F]=K[W]=K[q]=K[z]=K[P]=true;var L={leading:false,maxWait:0,trailing:false},M={configurable:false,enumerable:false,value:null,writable:false},V={"boolean":false,"function":true,object:true,number:false,string:false,undefined:false},U={"\\":"\\","'":"'","\n":"n","\r":"r","\t":"t","\u2028":"u2028","\u2029":"u2029"},G=V[typeof window]&&window||this,H=V[typeof exports]&&exports&&!exports.nodeType&&exports,J=V[typeof module]&&module&&!module.nodeType&&module,Q=J&&J.exports===H&&H,X=V[typeof global]&&global;!X||X.global!==X&&X.window!==X||(G=X);
+    var Y=s();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(G._=Y, define(function(){return Y})):H&&J?Q?(J.exports=Y)._=Y:H._=Y:G._=Y}).call(this);

+ 492 - 0
dashboard/assets/javascripts/moment.min.js

@@ -0,0 +1,492 @@
+//! moment.js
+//! version : 2.14.1
+//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
+//! license : MIT
+//! momentjs.com
+!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b():"function"==typeof define&&define.amd?define(b):a.moment=b()}(this,function(){"use strict";function a(){return md.apply(null,arguments)}
+// This is done to register the method called with moment()
+// without creating circular dependencies.
+    function b(a){md=a}function c(a){return a instanceof Array||"[object Array]"===Object.prototype.toString.call(a)}function d(a){return"[object Object]"===Object.prototype.toString.call(a)}function e(a){var b;for(b in a)
+// even if its not own property I'd still call it non-empty
+        return!1;return!0}function f(a){return a instanceof Date||"[object Date]"===Object.prototype.toString.call(a)}function g(a,b){var c,d=[];for(c=0;c<a.length;++c)d.push(b(a[c],c));return d}function h(a,b){return Object.prototype.hasOwnProperty.call(a,b)}function i(a,b){for(var c in b)h(b,c)&&(a[c]=b[c]);return h(b,"toString")&&(a.toString=b.toString),h(b,"valueOf")&&(a.valueOf=b.valueOf),a}function j(a,b,c,d){return qb(a,b,c,d,!0).utc()}function k(){
+// We need to deep clone this object.
+        return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null}}function l(a){return null==a._pf&&(a._pf=k()),a._pf}function m(a){if(null==a._isValid){var b=l(a),c=nd.call(b.parsedDateParts,function(a){return null!=a});a._isValid=!isNaN(a._d.getTime())&&b.overflow<0&&!b.empty&&!b.invalidMonth&&!b.invalidWeekday&&!b.nullInput&&!b.invalidFormat&&!b.userInvalidated&&(!b.meridiem||b.meridiem&&c),a._strict&&(a._isValid=a._isValid&&0===b.charsLeftOver&&0===b.unusedTokens.length&&void 0===b.bigHour)}return a._isValid}function n(a){var b=j(NaN);return null!=a?i(l(b),a):l(b).userInvalidated=!0,b}function o(a){return void 0===a}function p(a,b){var c,d,e;if(o(b._isAMomentObject)||(a._isAMomentObject=b._isAMomentObject),o(b._i)||(a._i=b._i),o(b._f)||(a._f=b._f),o(b._l)||(a._l=b._l),o(b._strict)||(a._strict=b._strict),o(b._tzm)||(a._tzm=b._tzm),o(b._isUTC)||(a._isUTC=b._isUTC),o(b._offset)||(a._offset=b._offset),o(b._pf)||(a._pf=l(b)),o(b._locale)||(a._locale=b._locale),od.length>0)for(c in od)d=od[c],e=b[d],o(e)||(a[d]=e);return a}
+// Moment prototype object
+    function q(b){p(this,b),this._d=new Date(null!=b._d?b._d.getTime():NaN),pd===!1&&(pd=!0,a.updateOffset(this),pd=!1)}function r(a){return a instanceof q||null!=a&&null!=a._isAMomentObject}function s(a){return 0>a?Math.ceil(a)||0:Math.floor(a)}function t(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=s(b)),c}
+// compare two arrays, return the number of differences
+    function u(a,b,c){var d,e=Math.min(a.length,b.length),f=Math.abs(a.length-b.length),g=0;for(d=0;e>d;d++)(c&&a[d]!==b[d]||!c&&t(a[d])!==t(b[d]))&&g++;return g+f}function v(b){a.suppressDeprecationWarnings===!1&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+b)}function w(b,c){var d=!0;return i(function(){return null!=a.deprecationHandler&&a.deprecationHandler(null,b),d&&(v(b+"\nArguments: "+Array.prototype.slice.call(arguments).join(", ")+"\n"+(new Error).stack),d=!1),c.apply(this,arguments)},c)}function x(b,c){null!=a.deprecationHandler&&a.deprecationHandler(b,c),qd[b]||(v(c),qd[b]=!0)}function y(a){return a instanceof Function||"[object Function]"===Object.prototype.toString.call(a)}function z(a){var b,c;for(c in a)b=a[c],y(b)?this[c]=b:this["_"+c]=b;this._config=a,
+// Lenient ordinal parsing accepts just a number in addition to
+// number + (possibly) stuff coming from _ordinalParseLenient.
+        this._ordinalParseLenient=new RegExp(this._ordinalParse.source+"|"+/\d{1,2}/.source)}function A(a,b){var c,e=i({},a);for(c in b)h(b,c)&&(d(a[c])&&d(b[c])?(e[c]={},i(e[c],a[c]),i(e[c],b[c])):null!=b[c]?e[c]=b[c]:delete e[c]);for(c in a)h(a,c)&&!h(b,c)&&d(a[c])&&(
+// make sure changes to properties don't modify parent config
+        e[c]=i({},e[c]));return e}function B(a){null!=a&&this.set(a)}function C(a,b,c){var d=this._calendar[a]||this._calendar.sameElse;return y(d)?d.call(b,c):d}function D(a){var b=this._longDateFormat[a],c=this._longDateFormat[a.toUpperCase()];return b||!c?b:(this._longDateFormat[a]=c.replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a])}function E(){return this._invalidDate}function F(a){return this._ordinal.replace("%d",a)}function G(a,b,c,d){var e=this._relativeTime[c];return y(e)?e(a,b,c,d):e.replace(/%d/i,a)}function H(a,b){var c=this._relativeTime[a>0?"future":"past"];return y(c)?c(b):c.replace(/%s/i,b)}function I(a,b){var c=a.toLowerCase();zd[c]=zd[c+"s"]=zd[b]=a}function J(a){return"string"==typeof a?zd[a]||zd[a.toLowerCase()]:void 0}function K(a){var b,c,d={};for(c in a)h(a,c)&&(b=J(c),b&&(d[b]=a[c]));return d}function L(a,b){Ad[a]=b}function M(a){var b=[];for(var c in a)b.push({unit:c,priority:Ad[c]});return b.sort(function(a,b){return a.priority-b.priority}),b}function N(b,c){return function(d){return null!=d?(P(this,b,d),a.updateOffset(this,c),this):O(this,b)}}function O(a,b){return a.isValid()?a._d["get"+(a._isUTC?"UTC":"")+b]():NaN}function P(a,b,c){a.isValid()&&a._d["set"+(a._isUTC?"UTC":"")+b](c)}
+// MOMENTS
+    function Q(a){return a=J(a),y(this[a])?this[a]():this}function R(a,b){if("object"==typeof a){a=K(a);for(var c=M(a),d=0;d<c.length;d++)this[c[d].unit](a[c[d].unit])}else if(a=J(a),y(this[a]))return this[a](b);return this}function S(a,b,c){var d=""+Math.abs(a),e=b-d.length,f=a>=0;return(f?c?"+":"":"-")+Math.pow(10,Math.max(0,e)).toString().substr(1)+d}
+// token:    'M'
+// padded:   ['MM', 2]
+// ordinal:  'Mo'
+// callback: function () { this.month() + 1 }
+    function T(a,b,c,d){var e=d;"string"==typeof d&&(e=function(){return this[d]()}),a&&(Ed[a]=e),b&&(Ed[b[0]]=function(){return S(e.apply(this,arguments),b[1],b[2])}),c&&(Ed[c]=function(){return this.localeData().ordinal(e.apply(this,arguments),a)})}function U(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function V(a){var b,c,d=a.match(Bd);for(b=0,c=d.length;c>b;b++)Ed[d[b]]?d[b]=Ed[d[b]]:d[b]=U(d[b]);return function(b){var e,f="";for(e=0;c>e;e++)f+=d[e]instanceof Function?d[e].call(b,a):d[e];return f}}
+// format date using native date object
+    function W(a,b){return a.isValid()?(b=X(b,a.localeData()),Dd[b]=Dd[b]||V(b),Dd[b](a)):a.localeData().invalidDate()}function X(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Cd.lastIndex=0;d>=0&&Cd.test(a);)a=a.replace(Cd,c),Cd.lastIndex=0,d-=1;return a}function Y(a,b,c){Wd[a]=y(b)?b:function(a,d){return a&&c?c:b}}function Z(a,b){return h(Wd,a)?Wd[a](b._strict,b._locale):new RegExp($(a))}
+// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
+    function $(a){return _(a.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e}))}function _(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function aa(a,b){var c,d=b;for("string"==typeof a&&(a=[a]),"number"==typeof b&&(d=function(a,c){c[b]=t(a)}),c=0;c<a.length;c++)Xd[a[c]]=d}function ba(a,b){aa(a,function(a,c,d,e){d._w=d._w||{},b(a,d._w,d,e)})}function ca(a,b,c){null!=b&&h(Xd,a)&&Xd[a](b,c._a,c,a)}function da(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function ea(a,b){return c(this._months)?this._months[a.month()]:this._months[(this._months.isFormat||fe).test(b)?"format":"standalone"][a.month()]}function fa(a,b){return c(this._monthsShort)?this._monthsShort[a.month()]:this._monthsShort[fe.test(b)?"format":"standalone"][a.month()]}function ga(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._monthsParse)for(
+// this is not used
+        this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],d=0;12>d;++d)f=j([2e3,d]),this._shortMonthsParse[d]=this.monthsShort(f,"").toLocaleLowerCase(),this._longMonthsParse[d]=this.months(f,"").toLocaleLowerCase();return c?"MMM"===b?(e=sd.call(this._shortMonthsParse,g),-1!==e?e:null):(e=sd.call(this._longMonthsParse,g),-1!==e?e:null):"MMM"===b?(e=sd.call(this._shortMonthsParse,g),-1!==e?e:(e=sd.call(this._longMonthsParse,g),-1!==e?e:null)):(e=sd.call(this._longMonthsParse,g),-1!==e?e:(e=sd.call(this._shortMonthsParse,g),-1!==e?e:null))}function ha(a,b,c){var d,e,f;if(this._monthsParseExact)return ga.call(this,a,b,c);
+// TODO: add sorting
+// Sorting makes sure if one month (or abbr) is a prefix of another
+// see sorting in computeMonthsParse
+        for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),d=0;12>d;d++){
+// test the regex
+            if(e=j([2e3,d]),c&&!this._longMonthsParse[d]&&(this._longMonthsParse[d]=new RegExp("^"+this.months(e,"").replace(".","")+"$","i"),this._shortMonthsParse[d]=new RegExp("^"+this.monthsShort(e,"").replace(".","")+"$","i")),c||this._monthsParse[d]||(f="^"+this.months(e,"")+"|^"+this.monthsShort(e,""),this._monthsParse[d]=new RegExp(f.replace(".",""),"i")),c&&"MMMM"===b&&this._longMonthsParse[d].test(a))return d;if(c&&"MMM"===b&&this._shortMonthsParse[d].test(a))return d;if(!c&&this._monthsParse[d].test(a))return d}}
+// MOMENTS
+    function ia(a,b){var c;if(!a.isValid())
+// No op
+        return a;if("string"==typeof b)if(/^\d+$/.test(b))b=t(b);else
+// TODO: Another silent failure?
+    if(b=a.localeData().monthsParse(b),"number"!=typeof b)return a;return c=Math.min(a.date(),da(a.year(),b)),a._d["set"+(a._isUTC?"UTC":"")+"Month"](b,c),a}function ja(b){return null!=b?(ia(this,b),a.updateOffset(this,!0),this):O(this,"Month")}function ka(){return da(this.year(),this.month())}function la(a){return this._monthsParseExact?(h(this,"_monthsRegex")||na.call(this),a?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,"_monthsShortRegex")||(this._monthsShortRegex=ie),this._monthsShortStrictRegex&&a?this._monthsShortStrictRegex:this._monthsShortRegex)}function ma(a){return this._monthsParseExact?(h(this,"_monthsRegex")||na.call(this),a?this._monthsStrictRegex:this._monthsRegex):(h(this,"_monthsRegex")||(this._monthsRegex=je),this._monthsStrictRegex&&a?this._monthsStrictRegex:this._monthsRegex)}function na(){function a(a,b){return b.length-a.length}var b,c,d=[],e=[],f=[];for(b=0;12>b;b++)c=j([2e3,b]),d.push(this.monthsShort(c,"")),e.push(this.months(c,"")),f.push(this.months(c,"")),f.push(this.monthsShort(c,""));for(
+// Sorting makes sure if one month (or abbr) is a prefix of another it
+// will match the longer piece.
+        d.sort(a),e.sort(a),f.sort(a),b=0;12>b;b++)d[b]=_(d[b]),e[b]=_(e[b]);for(b=0;24>b;b++)f[b]=_(f[b]);this._monthsRegex=new RegExp("^("+f.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+e.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+d.join("|")+")","i")}
+// HELPERS
+    function oa(a){return pa(a)?366:365}function pa(a){return a%4===0&&a%100!==0||a%400===0}function qa(){return pa(this.year())}function ra(a,b,c,d,e,f,g){
+//can't just apply() to create a date:
+//http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
+        var h=new Date(a,b,c,d,e,f,g);
+//the date constructor remaps years 0-99 to 1900-1999
+        return 100>a&&a>=0&&isFinite(h.getFullYear())&&h.setFullYear(a),h}function sa(a){var b=new Date(Date.UTC.apply(null,arguments));
+//the Date.UTC function remaps years 0-99 to 1900-1999
+        return 100>a&&a>=0&&isFinite(b.getUTCFullYear())&&b.setUTCFullYear(a),b}
+// start-of-first-week - start-of-year
+    function ta(a,b,c){var// first-week day -- which january is always in the first week (4 for iso, 1 for other)
+        d=7+b-c,
+// first-week day local weekday -- which local weekday is fwd
+        e=(7+sa(a,0,d).getUTCDay()-b)%7;return-e+d-1}
+//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
+    function ua(a,b,c,d,e){var f,g,h=(7+c-d)%7,i=ta(a,d,e),j=1+7*(b-1)+h+i;return 0>=j?(f=a-1,g=oa(f)+j):j>oa(a)?(f=a+1,g=j-oa(a)):(f=a,g=j),{year:f,dayOfYear:g}}function va(a,b,c){var d,e,f=ta(a.year(),b,c),g=Math.floor((a.dayOfYear()-f-1)/7)+1;return 1>g?(e=a.year()-1,d=g+wa(e,b,c)):g>wa(a.year(),b,c)?(d=g-wa(a.year(),b,c),e=a.year()+1):(e=a.year(),d=g),{week:d,year:e}}function wa(a,b,c){var d=ta(a,b,c),e=ta(a+1,b,c);return(oa(a)-d+e)/7}
+// HELPERS
+// LOCALES
+    function xa(a){return va(a,this._week.dow,this._week.doy).week}function ya(){return this._week.dow}function za(){return this._week.doy}
+// MOMENTS
+    function Aa(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")}function Ba(a){var b=va(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")}
+// HELPERS
+    function Ca(a,b){return"string"!=typeof a?a:isNaN(a)?(a=b.weekdaysParse(a),"number"==typeof a?a:null):parseInt(a,10)}function Da(a,b){return"string"==typeof a?b.weekdaysParse(a)%7||7:isNaN(a)?null:a}function Ea(a,b){return c(this._weekdays)?this._weekdays[a.day()]:this._weekdays[this._weekdays.isFormat.test(b)?"format":"standalone"][a.day()]}function Fa(a){return this._weekdaysShort[a.day()]}function Ga(a){return this._weekdaysMin[a.day()]}function Ha(a,b,c){var d,e,f,g=a.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],d=0;7>d;++d)f=j([2e3,1]).day(d),this._minWeekdaysParse[d]=this.weekdaysMin(f,"").toLocaleLowerCase(),this._shortWeekdaysParse[d]=this.weekdaysShort(f,"").toLocaleLowerCase(),this._weekdaysParse[d]=this.weekdays(f,"").toLocaleLowerCase();return c?"dddd"===b?(e=sd.call(this._weekdaysParse,g),-1!==e?e:null):"ddd"===b?(e=sd.call(this._shortWeekdaysParse,g),-1!==e?e:null):(e=sd.call(this._minWeekdaysParse,g),-1!==e?e:null):"dddd"===b?(e=sd.call(this._weekdaysParse,g),-1!==e?e:(e=sd.call(this._shortWeekdaysParse,g),-1!==e?e:(e=sd.call(this._minWeekdaysParse,g),-1!==e?e:null))):"ddd"===b?(e=sd.call(this._shortWeekdaysParse,g),-1!==e?e:(e=sd.call(this._weekdaysParse,g),-1!==e?e:(e=sd.call(this._minWeekdaysParse,g),-1!==e?e:null))):(e=sd.call(this._minWeekdaysParse,g),-1!==e?e:(e=sd.call(this._weekdaysParse,g),-1!==e?e:(e=sd.call(this._shortWeekdaysParse,g),-1!==e?e:null)))}function Ia(a,b,c){var d,e,f;if(this._weekdaysParseExact)return Ha.call(this,a,b,c);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),d=0;7>d;d++){
+// test the regex
+        if(e=j([2e3,1]).day(d),c&&!this._fullWeekdaysParse[d]&&(this._fullWeekdaysParse[d]=new RegExp("^"+this.weekdays(e,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[d]=new RegExp("^"+this.weekdaysShort(e,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[d]=new RegExp("^"+this.weekdaysMin(e,"").replace(".",".?")+"$","i")),this._weekdaysParse[d]||(f="^"+this.weekdays(e,"")+"|^"+this.weekdaysShort(e,"")+"|^"+this.weekdaysMin(e,""),this._weekdaysParse[d]=new RegExp(f.replace(".",""),"i")),c&&"dddd"===b&&this._fullWeekdaysParse[d].test(a))return d;if(c&&"ddd"===b&&this._shortWeekdaysParse[d].test(a))return d;if(c&&"dd"===b&&this._minWeekdaysParse[d].test(a))return d;if(!c&&this._weekdaysParse[d].test(a))return d}}
+// MOMENTS
+    function Ja(a){if(!this.isValid())return null!=a?this:NaN;var b=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=a?(a=Ca(a,this.localeData()),this.add(a-b,"d")):b}function Ka(a){if(!this.isValid())return null!=a?this:NaN;var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")}function La(a){if(!this.isValid())return null!=a?this:NaN;
+// behaves the same as moment#day except
+// as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
+// as a setter, sunday should belong to the previous week.
+        if(null!=a){var b=Da(a,this.localeData());return this.day(this.day()%7?b:b-7)}return this.day()||7}function Ma(a){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Pa.call(this),a?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,"_weekdaysRegex")||(this._weekdaysRegex=pe),this._weekdaysStrictRegex&&a?this._weekdaysStrictRegex:this._weekdaysRegex)}function Na(a){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Pa.call(this),a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=qe),this._weekdaysShortStrictRegex&&a?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)}function Oa(a){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Pa.call(this),a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=re),this._weekdaysMinStrictRegex&&a?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)}function Pa(){function a(a,b){return b.length-a.length}var b,c,d,e,f,g=[],h=[],i=[],k=[];for(b=0;7>b;b++)c=j([2e3,1]).day(b),d=this.weekdaysMin(c,""),e=this.weekdaysShort(c,""),f=this.weekdays(c,""),g.push(d),h.push(e),i.push(f),k.push(d),k.push(e),k.push(f);for(
+// Sorting makes sure if one weekday (or abbr) is a prefix of another it
+// will match the longer piece.
+        g.sort(a),h.sort(a),i.sort(a),k.sort(a),b=0;7>b;b++)h[b]=_(h[b]),i[b]=_(i[b]),k[b]=_(k[b]);this._weekdaysRegex=new RegExp("^("+k.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+h.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+g.join("|")+")","i")}
+// FORMATTING
+    function Qa(){return this.hours()%12||12}function Ra(){return this.hours()||24}function Sa(a,b){T(a,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),b)})}
+// PARSING
+    function Ta(a,b){return b._meridiemParse}
+// LOCALES
+    function Ua(a){
+// IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
+// Using charAt should be more compatible.
+        return"p"===(a+"").toLowerCase().charAt(0)}function Va(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"}function Wa(a){return a?a.toLowerCase().replace("_","-"):a}
+// pick the locale from the array
+// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
+// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
+    function Xa(a){for(var b,c,d,e,f=0;f<a.length;){for(e=Wa(a[f]).split("-"),b=e.length,c=Wa(a[f+1]),c=c?c.split("-"):null;b>0;){if(d=Ya(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&u(e,c,!0)>=b-1)
+//the next array item is better than a shallower substring of this one
+        break;b--}f++}return null}function Ya(a){var b=null;
+// TODO: Find a better way to register and load all the locales in Node
+        if(!we[a]&&"undefined"!=typeof module&&module&&module.exports)try{b=se._abbr,require("./locale/"+a),
+// because defineLocale currently also sets the global locale, we
+// want to undo that for lazy loaded locales
+            Za(b)}catch(c){}return we[a]}
+// This function will load locale and then set the global locale.  If
+// no arguments are passed in, it will simply return the current global
+// locale key.
+    function Za(a,b){var c;
+// moment.duration._locale = moment._locale = data;
+        return a&&(c=o(b)?ab(a):$a(a,b),c&&(se=c)),se._abbr}function $a(a,b){if(null!==b){var c=ve;
+// treat as if there is no base config
+// backwards compat for now: also set the locale
+        return b.abbr=a,null!=we[a]?(x("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),c=we[a]._config):null!=b.parentLocale&&(null!=we[b.parentLocale]?c=we[b.parentLocale]._config:x("parentLocaleUndefined","specified parentLocale is not defined yet. See http://momentjs.com/guides/#/warnings/parent-locale/")),we[a]=new B(A(c,b)),Za(a),we[a]}
+// useful for testing
+        return delete we[a],null}function _a(a,b){if(null!=b){var c,d=ve;
+// MERGE
+        null!=we[a]&&(d=we[a]._config),b=A(d,b),c=new B(b),c.parentLocale=we[a],we[a]=c,
+// backwards compat for now: also set the locale
+            Za(a)}else
+// pass null for config to unupdate, useful for tests
+        null!=we[a]&&(null!=we[a].parentLocale?we[a]=we[a].parentLocale:null!=we[a]&&delete we[a]);return we[a]}
+// returns locale data
+    function ab(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return se;if(!c(a)){if(b=Ya(a))return b;a=[a]}return Xa(a)}function bb(){return rd(we)}function cb(a){var b,c=a._a;return c&&-2===l(a).overflow&&(b=c[Zd]<0||c[Zd]>11?Zd:c[$d]<1||c[$d]>da(c[Yd],c[Zd])?$d:c[_d]<0||c[_d]>24||24===c[_d]&&(0!==c[ae]||0!==c[be]||0!==c[ce])?_d:c[ae]<0||c[ae]>59?ae:c[be]<0||c[be]>59?be:c[ce]<0||c[ce]>999?ce:-1,l(a)._overflowDayOfYear&&(Yd>b||b>$d)&&(b=$d),l(a)._overflowWeeks&&-1===b&&(b=de),l(a)._overflowWeekday&&-1===b&&(b=ee),l(a).overflow=b),a}
+// date from iso format
+    function db(a){var b,c,d,e,f,g,h=a._i,i=xe.exec(h)||ye.exec(h);if(i){for(l(a).iso=!0,b=0,c=Ae.length;c>b;b++)if(Ae[b][1].exec(i[1])){e=Ae[b][0],d=Ae[b][2]!==!1;break}if(null==e)return void(a._isValid=!1);if(i[3]){for(b=0,c=Be.length;c>b;b++)if(Be[b][1].exec(i[3])){
+// match[2] should be 'T' or space
+        f=(i[2]||" ")+Be[b][0];break}if(null==f)return void(a._isValid=!1)}if(!d&&null!=f)return void(a._isValid=!1);if(i[4]){if(!ze.exec(i[4]))return void(a._isValid=!1);g="Z"}a._f=e+(f||"")+(g||""),jb(a)}else a._isValid=!1}
+// date from iso format or fallback
+    function eb(b){var c=Ce.exec(b._i);return null!==c?void(b._d=new Date(+c[1])):(db(b),void(b._isValid===!1&&(delete b._isValid,a.createFromInputFallback(b))))}
+// Pick the first defined of two or three arguments.
+    function fb(a,b,c){return null!=a?a:null!=b?b:c}function gb(b){
+// hooks is actually the exported moment object
+        var c=new Date(a.now());return b._useUTC?[c.getUTCFullYear(),c.getUTCMonth(),c.getUTCDate()]:[c.getFullYear(),c.getMonth(),c.getDate()]}
+// convert an array to a date.
+// the array should mirror the parameters below
+// note: all values past the year are optional and will default to the lowest possible value.
+// [year, month, day , hour, minute, second, millisecond]
+    function hb(a){var b,c,d,e,f=[];if(!a._d){
+// Default to current date.
+// * if no year, month, day of month are given, default to today
+// * if day of month is given, default month and year
+// * if month is given, default only year
+// * if year is given, don't default anything
+        for(d=gb(a),a._w&&null==a._a[$d]&&null==a._a[Zd]&&ib(a),a._dayOfYear&&(e=fb(a._a[Yd],d[Yd]),a._dayOfYear>oa(e)&&(l(a)._overflowDayOfYear=!0),c=sa(e,0,a._dayOfYear),a._a[Zd]=c.getUTCMonth(),a._a[$d]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=f[b]=d[b];
+// Zero out whatever was not defaulted, including time
+        for(;7>b;b++)a._a[b]=f[b]=null==a._a[b]?2===b?1:0:a._a[b];
+// Check for 24:00:00.000
+        24===a._a[_d]&&0===a._a[ae]&&0===a._a[be]&&0===a._a[ce]&&(a._nextDay=!0,a._a[_d]=0),a._d=(a._useUTC?sa:ra).apply(null,f),
+// Apply timezone offset from input. The actual utcOffset can be changed
+// with parseZone.
+        null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()-a._tzm),a._nextDay&&(a._a[_d]=24)}}function ib(a){var b,c,d,e,f,g,h,i;b=a._w,null!=b.GG||null!=b.W||null!=b.E?(f=1,g=4,c=fb(b.GG,a._a[Yd],va(rb(),1,4).year),d=fb(b.W,1),e=fb(b.E,1),(1>e||e>7)&&(i=!0)):(f=a._locale._week.dow,g=a._locale._week.doy,c=fb(b.gg,a._a[Yd],va(rb(),f,g).year),d=fb(b.w,1),null!=b.d?(e=b.d,(0>e||e>6)&&(i=!0)):null!=b.e?(e=b.e+f,(b.e<0||b.e>6)&&(i=!0)):e=f),1>d||d>wa(c,f,g)?l(a)._overflowWeeks=!0:null!=i?l(a)._overflowWeekday=!0:(h=ua(c,d,e,f,g),a._a[Yd]=h.year,a._dayOfYear=h.dayOfYear)}
+// date from string and format string
+    function jb(b){
+// TODO: Move this to another part of the creation flow to prevent circular deps
+        if(b._f===a.ISO_8601)return void db(b);b._a=[],l(b).empty=!0;
+// This array is used to make a Date, either with `new Date` or `Date.UTC`
+        var c,d,e,f,g,h=""+b._i,i=h.length,j=0;for(e=X(b._f,b._locale).match(Bd)||[],c=0;c<e.length;c++)f=e[c],d=(h.match(Z(f,b))||[])[0],d&&(g=h.substr(0,h.indexOf(d)),g.length>0&&l(b).unusedInput.push(g),h=h.slice(h.indexOf(d)+d.length),j+=d.length),Ed[f]?(d?l(b).empty=!1:l(b).unusedTokens.push(f),ca(f,d,b)):b._strict&&!d&&l(b).unusedTokens.push(f);
+// add remaining unparsed input length to the string
+        l(b).charsLeftOver=i-j,h.length>0&&l(b).unusedInput.push(h),
+// clear _12h flag if hour is <= 12
+        b._a[_d]<=12&&l(b).bigHour===!0&&b._a[_d]>0&&(l(b).bigHour=void 0),l(b).parsedDateParts=b._a.slice(0),l(b).meridiem=b._meridiem,
+// handle meridiem
+            b._a[_d]=kb(b._locale,b._a[_d],b._meridiem),hb(b),cb(b)}function kb(a,b,c){var d;
+// Fallback
+        return null==c?b:null!=a.meridiemHour?a.meridiemHour(b,c):null!=a.isPM?(d=a.isPM(c),d&&12>b&&(b+=12),d||12!==b||(b=0),b):b}
+// date from string and array of format strings
+    function lb(a){var b,c,d,e,f;if(0===a._f.length)return l(a).invalidFormat=!0,void(a._d=new Date(NaN));for(e=0;e<a._f.length;e++)f=0,b=p({},a),null!=a._useUTC&&(b._useUTC=a._useUTC),b._f=a._f[e],jb(b),m(b)&&(f+=l(b).charsLeftOver,f+=10*l(b).unusedTokens.length,l(b).score=f,(null==d||d>f)&&(d=f,c=b));i(a,c||b)}function mb(a){if(!a._d){var b=K(a._i);a._a=g([b.year,b.month,b.day||b.date,b.hour,b.minute,b.second,b.millisecond],function(a){return a&&parseInt(a,10)}),hb(a)}}function nb(a){var b=new q(cb(ob(a)));
+// Adding is smart enough around DST
+        return b._nextDay&&(b.add(1,"d"),b._nextDay=void 0),b}function ob(a){var b=a._i,d=a._f;return a._locale=a._locale||ab(a._l),null===b||void 0===d&&""===b?n({nullInput:!0}):("string"==typeof b&&(a._i=b=a._locale.preparse(b)),r(b)?new q(cb(b)):(c(d)?lb(a):f(b)?a._d=b:d?jb(a):pb(a),m(a)||(a._d=null),a))}function pb(b){var d=b._i;void 0===d?b._d=new Date(a.now()):f(d)?b._d=new Date(d.valueOf()):"string"==typeof d?eb(b):c(d)?(b._a=g(d.slice(0),function(a){return parseInt(a,10)}),hb(b)):"object"==typeof d?mb(b):"number"==typeof d?
+// from milliseconds
+        b._d=new Date(d):a.createFromInputFallback(b)}function qb(a,b,f,g,h){var i={};
+// object construction must be done this way.
+// https://github.com/moment/moment/issues/1423
+        return"boolean"==typeof f&&(g=f,f=void 0),(d(a)&&e(a)||c(a)&&0===a.length)&&(a=void 0),i._isAMomentObject=!0,i._useUTC=i._isUTC=h,i._l=f,i._i=a,i._f=b,i._strict=g,nb(i)}function rb(a,b,c,d){return qb(a,b,c,d,!1)}
+// Pick a moment m from moments so that m[fn](other) is true for all
+// other. This relies on the function fn to be transitive.
+//
+// moments should either be an array of moment objects or an array, whose
+// first element is an array of moment objects.
+    function sb(a,b){var d,e;if(1===b.length&&c(b[0])&&(b=b[0]),!b.length)return rb();for(d=b[0],e=1;e<b.length;++e)b[e].isValid()&&!b[e][a](d)||(d=b[e]);return d}
+// TODO: Use [].sort instead?
+    function tb(){var a=[].slice.call(arguments,0);return sb("isBefore",a)}function ub(){var a=[].slice.call(arguments,0);return sb("isAfter",a)}function vb(a){var b=K(a),c=b.year||0,d=b.quarter||0,e=b.month||0,f=b.week||0,g=b.day||0,h=b.hour||0,i=b.minute||0,j=b.second||0,k=b.millisecond||0;
+// representation for dateAddRemove
+        this._milliseconds=+k+1e3*j+// 1000
+            6e4*i+// 1000 * 60
+            1e3*h*60*60,//using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
+// Because of dateAddRemove treats 24 hours as different from a
+// day when working around DST, we need to store them separately
+            this._days=+g+7*f,
+// It is impossible translate months into days without knowing
+// which months you are are talking about, so we have to store
+// it separately.
+            this._months=+e+3*d+12*c,this._data={},this._locale=ab(),this._bubble()}function wb(a){return a instanceof vb}
+// FORMATTING
+    function xb(a,b){T(a,0,0,function(){var a=this.utcOffset(),c="+";return 0>a&&(a=-a,c="-"),c+S(~~(a/60),2)+b+S(~~a%60,2)})}function yb(a,b){var c=(b||"").match(a)||[],d=c[c.length-1]||[],e=(d+"").match(Ge)||["-",0,0],f=+(60*e[1])+t(e[2]);return"+"===e[0]?f:-f}
+// Return a moment from input, that is local/utc/zone equivalent to model.
+    function zb(b,c){var d,e;
+// Use low-level api, because this fn is low-level api.
+        return c._isUTC?(d=c.clone(),e=(r(b)||f(b)?b.valueOf():rb(b).valueOf())-d.valueOf(),d._d.setTime(d._d.valueOf()+e),a.updateOffset(d,!1),d):rb(b).local()}function Ab(a){
+// On Firefox.24 Date#getTimezoneOffset returns a floating point.
+// https://github.com/moment/moment/pull/1871
+        return 15*-Math.round(a._d.getTimezoneOffset()/15)}
+// MOMENTS
+// keepLocalTime = true means only change the timezone, without
+// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
+// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
+// +0200, so we adjust the time as needed, to be valid.
+//
+// Keeping the time actually adds/subtracts (one hour)
+// from the actual represented time. That is why we call updateOffset
+// a second time. In case it wants us to change the offset again
+// _changeInProgress == true case, then we have to adjust, because
+// there is no such time in the given timezone.
+    function Bb(b,c){var d,e=this._offset||0;return this.isValid()?null!=b?("string"==typeof b?b=yb(Td,b):Math.abs(b)<16&&(b=60*b),!this._isUTC&&c&&(d=Ab(this)),this._offset=b,this._isUTC=!0,null!=d&&this.add(d,"m"),e!==b&&(!c||this._changeInProgress?Sb(this,Mb(b-e,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,a.updateOffset(this,!0),this._changeInProgress=null)),this):this._isUTC?e:Ab(this):null!=b?this:NaN}function Cb(a,b){return null!=a?("string"!=typeof a&&(a=-a),this.utcOffset(a,b),this):-this.utcOffset()}function Db(a){return this.utcOffset(0,a)}function Eb(a){return this._isUTC&&(this.utcOffset(0,a),this._isUTC=!1,a&&this.subtract(Ab(this),"m")),this}function Fb(){return this._tzm?this.utcOffset(this._tzm):"string"==typeof this._i&&this.utcOffset(yb(Sd,this._i)),this}function Gb(a){return this.isValid()?(a=a?rb(a).utcOffset():0,(this.utcOffset()-a)%60===0):!1}function Hb(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()}function Ib(){if(!o(this._isDSTShifted))return this._isDSTShifted;var a={};if(p(a,this),a=ob(a),a._a){var b=a._isUTC?j(a._a):rb(a._a);this._isDSTShifted=this.isValid()&&u(a._a,b.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted}function Jb(){return this.isValid()?!this._isUTC:!1}function Kb(){return this.isValid()?this._isUTC:!1}function Lb(){return this.isValid()?this._isUTC&&0===this._offset:!1}function Mb(a,b){var c,d,e,f=a,
+// matching against regexp is expensive, do it on demand
+        g=null;// checks for null or undefined
+        return wb(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(f={},b?f[b]=a:f.milliseconds=a):(g=He.exec(a))?(c="-"===g[1]?-1:1,f={y:0,d:t(g[$d])*c,h:t(g[_d])*c,m:t(g[ae])*c,s:t(g[be])*c,ms:t(g[ce])*c}):(g=Ie.exec(a))?(c="-"===g[1]?-1:1,f={y:Nb(g[2],c),M:Nb(g[3],c),w:Nb(g[4],c),d:Nb(g[5],c),h:Nb(g[6],c),m:Nb(g[7],c),s:Nb(g[8],c)}):null==f?f={}:"object"==typeof f&&("from"in f||"to"in f)&&(e=Pb(rb(f.from),rb(f.to)),f={},f.ms=e.milliseconds,f.M=e.months),d=new vb(f),wb(a)&&h(a,"_locale")&&(d._locale=a._locale),d}function Nb(a,b){
+// We'd normally use ~~inp for this, but unfortunately it also
+// converts floats to ints.
+// inp may be undefined, so careful calling replace on it.
+        var c=a&&parseFloat(a.replace(",","."));
+// apply sign while we're at it
+        return(isNaN(c)?0:c)*b}function Ob(a,b){var c={milliseconds:0,months:0};return c.months=b.month()-a.month()+12*(b.year()-a.year()),a.clone().add(c.months,"M").isAfter(b)&&--c.months,c.milliseconds=+b-+a.clone().add(c.months,"M"),c}function Pb(a,b){var c;return a.isValid()&&b.isValid()?(b=zb(b,a),a.isBefore(b)?c=Ob(a,b):(c=Ob(b,a),c.milliseconds=-c.milliseconds,c.months=-c.months),c):{milliseconds:0,months:0}}function Qb(a){return 0>a?-1*Math.round(-1*a):Math.round(a)}
+// TODO: remove 'name' arg after deprecation is removed
+    function Rb(a,b){return function(c,d){var e,f;
+//invert the arguments, but complain about it
+        return null===d||isNaN(+d)||(x(b,"moment()."+b+"(period, number) is deprecated. Please use moment()."+b+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),f=c,c=d,d=f),c="string"==typeof c?+c:c,e=Mb(c,d),Sb(this,e,a),this}}function Sb(b,c,d,e){var f=c._milliseconds,g=Qb(c._days),h=Qb(c._months);b.isValid()&&(e=null==e?!0:e,f&&b._d.setTime(b._d.valueOf()+f*d),g&&P(b,"Date",O(b,"Date")+g*d),h&&ia(b,O(b,"Month")+h*d),e&&a.updateOffset(b,g||h))}function Tb(a,b){var c=a.diff(b,"days",!0);return-6>c?"sameElse":-1>c?"lastWeek":0>c?"lastDay":1>c?"sameDay":2>c?"nextDay":7>c?"nextWeek":"sameElse"}function Ub(b,c){
+// We want to compare the start of today, vs this.
+// Getting start-of-today depends on whether we're local/utc/offset or not.
+        var d=b||rb(),e=zb(d,this).startOf("day"),f=a.calendarFormat(this,e)||"sameElse",g=c&&(y(c[f])?c[f].call(this,d):c[f]);return this.format(g||this.localeData().calendar(f,this,rb(d)))}function Vb(){return new q(this)}function Wb(a,b){var c=r(a)?a:rb(a);return this.isValid()&&c.isValid()?(b=J(o(b)?"millisecond":b),"millisecond"===b?this.valueOf()>c.valueOf():c.valueOf()<this.clone().startOf(b).valueOf()):!1}function Xb(a,b){var c=r(a)?a:rb(a);return this.isValid()&&c.isValid()?(b=J(o(b)?"millisecond":b),"millisecond"===b?this.valueOf()<c.valueOf():this.clone().endOf(b).valueOf()<c.valueOf()):!1}function Yb(a,b,c,d){return d=d||"()",("("===d[0]?this.isAfter(a,c):!this.isBefore(a,c))&&(")"===d[1]?this.isBefore(b,c):!this.isAfter(b,c))}function Zb(a,b){var c,d=r(a)?a:rb(a);return this.isValid()&&d.isValid()?(b=J(b||"millisecond"),"millisecond"===b?this.valueOf()===d.valueOf():(c=d.valueOf(),this.clone().startOf(b).valueOf()<=c&&c<=this.clone().endOf(b).valueOf())):!1}function $b(a,b){return this.isSame(a,b)||this.isAfter(a,b)}function _b(a,b){return this.isSame(a,b)||this.isBefore(a,b)}function ac(a,b,c){var d,e,f,g;// 1000
+// 1000 * 60
+// 1000 * 60 * 60
+// 1000 * 60 * 60 * 24, negate dst
+// 1000 * 60 * 60 * 24 * 7, negate dst
+        return this.isValid()?(d=zb(a,this),d.isValid()?(e=6e4*(d.utcOffset()-this.utcOffset()),b=J(b),"year"===b||"month"===b||"quarter"===b?(g=bc(this,d),"quarter"===b?g/=3:"year"===b&&(g/=12)):(f=this-d,g="second"===b?f/1e3:"minute"===b?f/6e4:"hour"===b?f/36e5:"day"===b?(f-e)/864e5:"week"===b?(f-e)/6048e5:f),c?g:s(g)):NaN):NaN}function bc(a,b){
+// difference in months
+        var c,d,e=12*(b.year()-a.year())+(b.month()-a.month()),
+// b is in (anchor - 1 month, anchor + 1 month)
+            f=a.clone().add(e,"months");
+//check for negative zero, return zero if negative zero
+// linear across the month
+// linear across the month
+        return 0>b-f?(c=a.clone().add(e-1,"months"),d=(b-f)/(f-c)):(c=a.clone().add(e+1,"months"),d=(b-f)/(c-f)),-(e+d)||0}function cc(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")}function dc(){var a=this.clone().utc();return 0<a.year()&&a.year()<=9999?y(Date.prototype.toISOString)?this.toDate().toISOString():W(a,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):W(a,"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]")}function ec(b){b||(b=this.isUtc()?a.defaultFormatUtc:a.defaultFormat);var c=W(this,b);return this.localeData().postformat(c)}function fc(a,b){return this.isValid()&&(r(a)&&a.isValid()||rb(a).isValid())?Mb({to:this,from:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function gc(a){return this.from(rb(),a)}function hc(a,b){return this.isValid()&&(r(a)&&a.isValid()||rb(a).isValid())?Mb({from:this,to:a}).locale(this.locale()).humanize(!b):this.localeData().invalidDate()}function ic(a){return this.to(rb(),a)}
+// If passed a locale key, it will set the locale for this
+// instance.  Otherwise, it will return the locale configuration
+// variables for this instance.
+    function jc(a){var b;return void 0===a?this._locale._abbr:(b=ab(a),null!=b&&(this._locale=b),this)}function kc(){return this._locale}function lc(a){
+// the following switch intentionally omits break keywords
+// to utilize falling through the cases.
+        switch(a=J(a)){case"year":this.month(0);/* falls through */
+            case"quarter":case"month":this.date(1);/* falls through */
+            case"week":case"isoWeek":case"day":case"date":this.hours(0);/* falls through */
+            case"hour":this.minutes(0);/* falls through */
+            case"minute":this.seconds(0);/* falls through */
+            case"second":this.milliseconds(0)}
+// weeks are a special case
+// quarters are also special
+        return"week"===a&&this.weekday(0),"isoWeek"===a&&this.isoWeekday(1),"quarter"===a&&this.month(3*Math.floor(this.month()/3)),this}function mc(a){
+// 'date' is an alias for 'day', so it should be considered as such.
+        return a=J(a),void 0===a||"millisecond"===a?this:("date"===a&&(a="day"),this.startOf(a).add(1,"isoWeek"===a?"week":a).subtract(1,"ms"))}function nc(){return this._d.valueOf()-6e4*(this._offset||0)}function oc(){return Math.floor(this.valueOf()/1e3)}function pc(){return new Date(this.valueOf())}function qc(){var a=this;return[a.year(),a.month(),a.date(),a.hour(),a.minute(),a.second(),a.millisecond()]}function rc(){var a=this;return{years:a.year(),months:a.month(),date:a.date(),hours:a.hours(),minutes:a.minutes(),seconds:a.seconds(),milliseconds:a.milliseconds()}}function sc(){
+// new Date(NaN).toJSON() === null
+        return this.isValid()?this.toISOString():null}function tc(){return m(this)}function uc(){return i({},l(this))}function vc(){return l(this).overflow}function wc(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}}function xc(a,b){T(0,[a,a.length],0,b)}
+// MOMENTS
+    function yc(a){return Cc.call(this,a,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)}function zc(a){return Cc.call(this,a,this.isoWeek(),this.isoWeekday(),1,4)}function Ac(){return wa(this.year(),1,4)}function Bc(){var a=this.localeData()._week;return wa(this.year(),a.dow,a.doy)}function Cc(a,b,c,d,e){var f;return null==a?va(this,d,e).year:(f=wa(a,d,e),b>f&&(b=f),Dc.call(this,a,b,c,d,e))}function Dc(a,b,c,d,e){var f=ua(a,b,c,d,e),g=sa(f.year,0,f.dayOfYear);return this.year(g.getUTCFullYear()),this.month(g.getUTCMonth()),this.date(g.getUTCDate()),this}
+// MOMENTS
+    function Ec(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)}
+// HELPERS
+// MOMENTS
+    function Fc(a){var b=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")}function Gc(a,b){b[ce]=t(1e3*("0."+a))}
+// MOMENTS
+    function Hc(){return this._isUTC?"UTC":""}function Ic(){return this._isUTC?"Coordinated Universal Time":""}function Jc(a){return rb(1e3*a)}function Kc(){return rb.apply(null,arguments).parseZone()}function Lc(a){return a}function Mc(a,b,c,d){var e=ab(),f=j().set(d,b);return e[c](f,a)}function Nc(a,b,c){if("number"==typeof a&&(b=a,a=void 0),a=a||"",null!=b)return Mc(a,b,c,"month");var d,e=[];for(d=0;12>d;d++)e[d]=Mc(a,d,c,"month");return e}
+// ()
+// (5)
+// (fmt, 5)
+// (fmt)
+// (true)
+// (true, 5)
+// (true, fmt, 5)
+// (true, fmt)
+    function Oc(a,b,c,d){"boolean"==typeof a?("number"==typeof b&&(c=b,b=void 0),b=b||""):(b=a,c=b,a=!1,"number"==typeof b&&(c=b,b=void 0),b=b||"");var e=ab(),f=a?e._week.dow:0;if(null!=c)return Mc(b,(c+f)%7,d,"day");var g,h=[];for(g=0;7>g;g++)h[g]=Mc(b,(g+f)%7,d,"day");return h}function Pc(a,b){return Nc(a,b,"months")}function Qc(a,b){return Nc(a,b,"monthsShort")}function Rc(a,b,c){return Oc(a,b,c,"weekdays")}function Sc(a,b,c){return Oc(a,b,c,"weekdaysShort")}function Tc(a,b,c){return Oc(a,b,c,"weekdaysMin")}function Uc(){var a=this._data;return this._milliseconds=Ue(this._milliseconds),this._days=Ue(this._days),this._months=Ue(this._months),a.milliseconds=Ue(a.milliseconds),a.seconds=Ue(a.seconds),a.minutes=Ue(a.minutes),a.hours=Ue(a.hours),a.months=Ue(a.months),a.years=Ue(a.years),this}function Vc(a,b,c,d){var e=Mb(b,c);return a._milliseconds+=d*e._milliseconds,a._days+=d*e._days,a._months+=d*e._months,a._bubble()}
+// supports only 2.0-style add(1, 's') or add(duration)
+    function Wc(a,b){return Vc(this,a,b,1)}
+// supports only 2.0-style subtract(1, 's') or subtract(duration)
+    function Xc(a,b){return Vc(this,a,b,-1)}function Yc(a){return 0>a?Math.floor(a):Math.ceil(a)}function Zc(){var a,b,c,d,e,f=this._milliseconds,g=this._days,h=this._months,i=this._data;
+// if we have a mix of positive and negative values, bubble down first
+// check: https://github.com/moment/moment/issues/2166
+// The following code bubbles up values, see the tests for
+// examples of what that means.
+// convert days to months
+// 12 months -> 1 year
+        return f>=0&&g>=0&&h>=0||0>=f&&0>=g&&0>=h||(f+=864e5*Yc(_c(h)+g),g=0,h=0),i.milliseconds=f%1e3,a=s(f/1e3),i.seconds=a%60,b=s(a/60),i.minutes=b%60,c=s(b/60),i.hours=c%24,g+=s(c/24),e=s($c(g)),h+=e,g-=Yc(_c(e)),d=s(h/12),h%=12,i.days=g,i.months=h,i.years=d,this}function $c(a){
+// 400 years have 146097 days (taking into account leap year rules)
+// 400 years have 12 months === 4800
+        return 4800*a/146097}function _c(a){
+// the reverse of daysToMonths
+        return 146097*a/4800}function ad(a){var b,c,d=this._milliseconds;if(a=J(a),"month"===a||"year"===a)return b=this._days+d/864e5,c=this._months+$c(b),"month"===a?c:c/12;switch(b=this._days+Math.round(_c(this._months)),a){case"week":return b/7+d/6048e5;case"day":return b+d/864e5;case"hour":return 24*b+d/36e5;case"minute":return 1440*b+d/6e4;case"second":return 86400*b+d/1e3;
+// Math.floor prevents floating point math errors here
+        case"millisecond":return Math.floor(864e5*b)+d;default:throw new Error("Unknown unit "+a)}}
+// TODO: Use this.as('ms')?
+    function bd(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*t(this._months/12)}function cd(a){return function(){return this.as(a)}}function dd(a){return a=J(a),this[a+"s"]()}function ed(a){return function(){return this._data[a]}}function fd(){return s(this.days()/7)}
+// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
+    function gd(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function hd(a,b,c){var d=Mb(a).abs(),e=jf(d.as("s")),f=jf(d.as("m")),g=jf(d.as("h")),h=jf(d.as("d")),i=jf(d.as("M")),j=jf(d.as("y")),k=e<kf.s&&["s",e]||1>=f&&["m"]||f<kf.m&&["mm",f]||1>=g&&["h"]||g<kf.h&&["hh",g]||1>=h&&["d"]||h<kf.d&&["dd",h]||1>=i&&["M"]||i<kf.M&&["MM",i]||1>=j&&["y"]||["yy",j];return k[2]=b,k[3]=+a>0,k[4]=c,gd.apply(null,k)}
+// This function allows you to set the rounding function for relative time strings
+    function id(a){return void 0===a?jf:"function"==typeof a?(jf=a,!0):!1}
+// This function allows you to set a threshold for relative time strings
+    function jd(a,b){return void 0===kf[a]?!1:void 0===b?kf[a]:(kf[a]=b,!0)}function kd(a){var b=this.localeData(),c=hd(this,!a,b);return a&&(c=b.pastFuture(+this,c)),b.postformat(c)}function ld(){
+// for ISO strings we do not use the normal bubbling rules:
+//  * milliseconds bubble up until they become hours
+//  * days do not bubble at all
+//  * months bubble up until they become years
+// This is because there is no context-free conversion between hours and days
+// (think of clock changes)
+// and also not between days and months (28-31 days per month)
+        var a,b,c,d=lf(this._milliseconds)/1e3,e=lf(this._days),f=lf(this._months);a=s(d/60),b=s(a/60),d%=60,a%=60,c=s(f/12),f%=12;
+// inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
+        var g=c,h=f,i=e,j=b,k=a,l=d,m=this.asSeconds();return m?(0>m?"-":"")+"P"+(g?g+"Y":"")+(h?h+"M":"")+(i?i+"D":"")+(j||k||l?"T":"")+(j?j+"H":"")+(k?k+"M":"")+(l?l+"S":""):"P0D"}var md,nd;nd=Array.prototype.some?Array.prototype.some:function(a){for(var b=Object(this),c=b.length>>>0,d=0;c>d;d++)if(d in b&&a.call(this,b[d],d,b))return!0;return!1};
+// Plugins that add properties should also add the key here (null value),
+// so we can properly clone ourselves.
+    var od=a.momentProperties=[],pd=!1,qd={};a.suppressDeprecationWarnings=!1,a.deprecationHandler=null;var rd;rd=Object.keys?Object.keys:function(a){var b,c=[];for(b in a)h(a,b)&&c.push(b);return c};var sd,td={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},ud={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},vd="Invalid date",wd="%d",xd=/\d{1,2}/,yd={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},zd={},Ad={},Bd=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,Cd=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Dd={},Ed={},Fd=/\d/,Gd=/\d\d/,Hd=/\d{3}/,Id=/\d{4}/,Jd=/[+-]?\d{6}/,Kd=/\d\d?/,Ld=/\d\d\d\d?/,Md=/\d\d\d\d\d\d?/,Nd=/\d{1,3}/,Od=/\d{1,4}/,Pd=/[+-]?\d{1,6}/,Qd=/\d+/,Rd=/[+-]?\d+/,Sd=/Z|[+-]\d\d:?\d\d/gi,Td=/Z|[+-]\d\d(?::?\d\d)?/gi,Ud=/[+-]?\d+(\.\d{1,3})?/,Vd=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Wd={},Xd={},Yd=0,Zd=1,$d=2,_d=3,ae=4,be=5,ce=6,de=7,ee=8;sd=Array.prototype.indexOf?Array.prototype.indexOf:function(a){
+// I know
+        var b;for(b=0;b<this.length;++b)if(this[b]===a)return b;return-1},T("M",["MM",2],"Mo",function(){return this.month()+1}),T("MMM",0,0,function(a){return this.localeData().monthsShort(this,a)}),T("MMMM",0,0,function(a){return this.localeData().months(this,a)}),I("month","M"),L("month",8),Y("M",Kd),Y("MM",Kd,Gd),Y("MMM",function(a,b){return b.monthsShortRegex(a)}),Y("MMMM",function(a,b){return b.monthsRegex(a)}),aa(["M","MM"],function(a,b){b[Zd]=t(a)-1}),aa(["MMM","MMMM"],function(a,b,c,d){var e=c._locale.monthsParse(a,d,c._strict);null!=e?b[Zd]=e:l(c).invalidMonth=a});
+// LOCALES
+    var fe=/D[oD]?(\[[^\[\]]*\]|\s+)+MMMM?/,ge="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),he="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),ie=Vd,je=Vd;
+// FORMATTING
+    T("Y",0,0,function(){var a=this.year();return 9999>=a?""+a:"+"+a}),T(0,["YY",2],0,function(){return this.year()%100}),T(0,["YYYY",4],0,"year"),T(0,["YYYYY",5],0,"year"),T(0,["YYYYYY",6,!0],0,"year"),
+// ALIASES
+        I("year","y"),
+// PRIORITIES
+        L("year",1),
+// PARSING
+        Y("Y",Rd),Y("YY",Kd,Gd),Y("YYYY",Od,Id),Y("YYYYY",Pd,Jd),Y("YYYYYY",Pd,Jd),aa(["YYYYY","YYYYYY"],Yd),aa("YYYY",function(b,c){c[Yd]=2===b.length?a.parseTwoDigitYear(b):t(b)}),aa("YY",function(b,c){c[Yd]=a.parseTwoDigitYear(b)}),aa("Y",function(a,b){b[Yd]=parseInt(a,10)}),
+// HOOKS
+        a.parseTwoDigitYear=function(a){return t(a)+(t(a)>68?1900:2e3)};
+// MOMENTS
+    var ke=N("FullYear",!0);
+// FORMATTING
+    T("w",["ww",2],"wo","week"),T("W",["WW",2],"Wo","isoWeek"),
+// ALIASES
+        I("week","w"),I("isoWeek","W"),
+// PRIORITIES
+        L("week",5),L("isoWeek",5),
+// PARSING
+        Y("w",Kd),Y("ww",Kd,Gd),Y("W",Kd),Y("WW",Kd,Gd),ba(["w","ww","W","WW"],function(a,b,c,d){b[d.substr(0,1)]=t(a)});var le={dow:0,// Sunday is the first day of the week.
+        doy:6};
+// FORMATTING
+    T("d",0,"do","day"),T("dd",0,0,function(a){return this.localeData().weekdaysMin(this,a)}),T("ddd",0,0,function(a){return this.localeData().weekdaysShort(this,a)}),T("dddd",0,0,function(a){return this.localeData().weekdays(this,a)}),T("e",0,0,"weekday"),T("E",0,0,"isoWeekday"),
+// ALIASES
+        I("day","d"),I("weekday","e"),I("isoWeekday","E"),
+// PRIORITY
+        L("day",11),L("weekday",11),L("isoWeekday",11),
+// PARSING
+        Y("d",Kd),Y("e",Kd),Y("E",Kd),Y("dd",function(a,b){return b.weekdaysMinRegex(a)}),Y("ddd",function(a,b){return b.weekdaysShortRegex(a)}),Y("dddd",function(a,b){return b.weekdaysRegex(a)}),ba(["dd","ddd","dddd"],function(a,b,c,d){var e=c._locale.weekdaysParse(a,d,c._strict);
+// if we didn't get a weekday name, mark the date as invalid
+        null!=e?b.d=e:l(c).invalidWeekday=a}),ba(["d","e","E"],function(a,b,c,d){b[d]=t(a)});
+// LOCALES
+    var me="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),ne="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),oe="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),pe=Vd,qe=Vd,re=Vd;T("H",["HH",2],0,"hour"),T("h",["hh",2],0,Qa),T("k",["kk",2],0,Ra),T("hmm",0,0,function(){return""+Qa.apply(this)+S(this.minutes(),2)}),T("hmmss",0,0,function(){return""+Qa.apply(this)+S(this.minutes(),2)+S(this.seconds(),2)}),T("Hmm",0,0,function(){return""+this.hours()+S(this.minutes(),2)}),T("Hmmss",0,0,function(){return""+this.hours()+S(this.minutes(),2)+S(this.seconds(),2)}),Sa("a",!0),Sa("A",!1),
+// ALIASES
+        I("hour","h"),
+// PRIORITY
+        L("hour",13),Y("a",Ta),Y("A",Ta),Y("H",Kd),Y("h",Kd),Y("HH",Kd,Gd),Y("hh",Kd,Gd),Y("hmm",Ld),Y("hmmss",Md),Y("Hmm",Ld),Y("Hmmss",Md),aa(["H","HH"],_d),aa(["a","A"],function(a,b,c){c._isPm=c._locale.isPM(a),c._meridiem=a}),aa(["h","hh"],function(a,b,c){b[_d]=t(a),l(c).bigHour=!0}),aa("hmm",function(a,b,c){var d=a.length-2;b[_d]=t(a.substr(0,d)),b[ae]=t(a.substr(d)),l(c).bigHour=!0}),aa("hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[_d]=t(a.substr(0,d)),b[ae]=t(a.substr(d,2)),b[be]=t(a.substr(e)),l(c).bigHour=!0}),aa("Hmm",function(a,b,c){var d=a.length-2;b[_d]=t(a.substr(0,d)),b[ae]=t(a.substr(d))}),aa("Hmmss",function(a,b,c){var d=a.length-4,e=a.length-2;b[_d]=t(a.substr(0,d)),b[ae]=t(a.substr(d,2)),b[be]=t(a.substr(e))});var se,te=/[ap]\.?m?\.?/i,ue=N("Hours",!0),ve={calendar:td,longDateFormat:ud,invalidDate:vd,ordinal:wd,ordinalParse:xd,relativeTime:yd,months:ge,monthsShort:he,week:le,weekdays:me,weekdaysMin:oe,weekdaysShort:ne,meridiemParse:te},we={},xe=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/,ye=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?/,ze=/Z|[+-]\d\d(?::?\d\d)?/,Ae=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],
+// YYYYMM is NOT allowed by the standard
+        ["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],Be=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Ce=/^\/?Date\((\-?\d+)/i;a.createFromInputFallback=w("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(a){a._d=new Date(a._i+(a._useUTC?" UTC":""))}),
+// constant that refers to the ISO standard
+        a.ISO_8601=function(){};var De=w("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=rb.apply(null,arguments);return this.isValid()&&a.isValid()?this>a?this:a:n()}),Ee=w("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var a=rb.apply(null,arguments);return this.isValid()&&a.isValid()?a>this?this:a:n()}),Fe=function(){return Date.now?Date.now():+new Date};xb("Z",":"),xb("ZZ",""),
+// PARSING
+        Y("Z",Td),Y("ZZ",Td),aa(["Z","ZZ"],function(a,b,c){c._useUTC=!0,c._tzm=yb(Td,a)});
+// HELPERS
+// timezone chunker
+// '+10:00' > ['10',  '00']
+// '-1530'  > ['-15', '30']
+    var Ge=/([\+\-]|\d\d)/gi;
+// HOOKS
+// This function will be called whenever a moment is mutated.
+// It is intended to keep the offset in sync with the timezone.
+    a.updateOffset=function(){};
+// ASP.NET json date format regex
+    var He=/^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?\d*)?$/,Ie=/^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;Mb.fn=vb.prototype;var Je=Rb(1,"add"),Ke=Rb(-1,"subtract");a.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",a.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Le=w("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(a){return void 0===a?this.localeData():this.locale(a)});
+// FORMATTING
+    T(0,["gg",2],0,function(){return this.weekYear()%100}),T(0,["GG",2],0,function(){return this.isoWeekYear()%100}),xc("gggg","weekYear"),xc("ggggg","weekYear"),xc("GGGG","isoWeekYear"),xc("GGGGG","isoWeekYear"),
+// ALIASES
+        I("weekYear","gg"),I("isoWeekYear","GG"),
+// PRIORITY
+        L("weekYear",1),L("isoWeekYear",1),
+// PARSING
+        Y("G",Rd),Y("g",Rd),Y("GG",Kd,Gd),Y("gg",Kd,Gd),Y("GGGG",Od,Id),Y("gggg",Od,Id),Y("GGGGG",Pd,Jd),Y("ggggg",Pd,Jd),ba(["gggg","ggggg","GGGG","GGGGG"],function(a,b,c,d){b[d.substr(0,2)]=t(a)}),ba(["gg","GG"],function(b,c,d,e){c[e]=a.parseTwoDigitYear(b)}),
+// FORMATTING
+        T("Q",0,"Qo","quarter"),
+// ALIASES
+        I("quarter","Q"),
+// PRIORITY
+        L("quarter",7),
+// PARSING
+        Y("Q",Fd),aa("Q",function(a,b){b[Zd]=3*(t(a)-1)}),
+// FORMATTING
+        T("D",["DD",2],"Do","date"),
+// ALIASES
+        I("date","D"),
+// PRIOROITY
+        L("date",9),
+// PARSING
+        Y("D",Kd),Y("DD",Kd,Gd),Y("Do",function(a,b){return a?b._ordinalParse:b._ordinalParseLenient}),aa(["D","DD"],$d),aa("Do",function(a,b){b[$d]=t(a.match(Kd)[0],10)});
+// MOMENTS
+    var Me=N("Date",!0);
+// FORMATTING
+    T("DDD",["DDDD",3],"DDDo","dayOfYear"),
+// ALIASES
+        I("dayOfYear","DDD"),
+// PRIORITY
+        L("dayOfYear",4),
+// PARSING
+        Y("DDD",Nd),Y("DDDD",Hd),aa(["DDD","DDDD"],function(a,b,c){c._dayOfYear=t(a)}),
+// FORMATTING
+        T("m",["mm",2],0,"minute"),
+// ALIASES
+        I("minute","m"),
+// PRIORITY
+        L("minute",14),
+// PARSING
+        Y("m",Kd),Y("mm",Kd,Gd),aa(["m","mm"],ae);
+// MOMENTS
+    var Ne=N("Minutes",!1);
+// FORMATTING
+    T("s",["ss",2],0,"second"),
+// ALIASES
+        I("second","s"),
+// PRIORITY
+        L("second",15),
+// PARSING
+        Y("s",Kd),Y("ss",Kd,Gd),aa(["s","ss"],be);
+// MOMENTS
+    var Oe=N("Seconds",!1);
+// FORMATTING
+    T("S",0,0,function(){return~~(this.millisecond()/100)}),T(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),T(0,["SSS",3],0,"millisecond"),T(0,["SSSS",4],0,function(){return 10*this.millisecond()}),T(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),T(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),T(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),T(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),T(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),
+// ALIASES
+        I("millisecond","ms"),
+// PRIORITY
+        L("millisecond",16),
+// PARSING
+        Y("S",Nd,Fd),Y("SS",Nd,Gd),Y("SSS",Nd,Hd);var Pe;for(Pe="SSSS";Pe.length<=9;Pe+="S")Y(Pe,Qd);for(Pe="S";Pe.length<=9;Pe+="S")aa(Pe,Gc);
+// MOMENTS
+    var Qe=N("Milliseconds",!1);
+// FORMATTING
+    T("z",0,0,"zoneAbbr"),T("zz",0,0,"zoneName");var Re=q.prototype;Re.add=Je,Re.calendar=Ub,Re.clone=Vb,Re.diff=ac,Re.endOf=mc,Re.format=ec,Re.from=fc,Re.fromNow=gc,Re.to=hc,Re.toNow=ic,Re.get=Q,Re.invalidAt=vc,Re.isAfter=Wb,Re.isBefore=Xb,Re.isBetween=Yb,Re.isSame=Zb,Re.isSameOrAfter=$b,Re.isSameOrBefore=_b,Re.isValid=tc,Re.lang=Le,Re.locale=jc,Re.localeData=kc,Re.max=Ee,Re.min=De,Re.parsingFlags=uc,Re.set=R,Re.startOf=lc,Re.subtract=Ke,Re.toArray=qc,Re.toObject=rc,Re.toDate=pc,Re.toISOString=dc,Re.toJSON=sc,Re.toString=cc,Re.unix=oc,Re.valueOf=nc,Re.creationData=wc,
+// Year
+        Re.year=ke,Re.isLeapYear=qa,
+// Week Year
+        Re.weekYear=yc,Re.isoWeekYear=zc,
+// Quarter
+        Re.quarter=Re.quarters=Ec,
+// Month
+        Re.month=ja,Re.daysInMonth=ka,
+// Week
+        Re.week=Re.weeks=Aa,Re.isoWeek=Re.isoWeeks=Ba,Re.weeksInYear=Bc,Re.isoWeeksInYear=Ac,
+// Day
+        Re.date=Me,Re.day=Re.days=Ja,Re.weekday=Ka,Re.isoWeekday=La,Re.dayOfYear=Fc,
+// Hour
+        Re.hour=Re.hours=ue,
+// Minute
+        Re.minute=Re.minutes=Ne,
+// Second
+        Re.second=Re.seconds=Oe,
+// Millisecond
+        Re.millisecond=Re.milliseconds=Qe,
+// Offset
+        Re.utcOffset=Bb,Re.utc=Db,Re.local=Eb,Re.parseZone=Fb,Re.hasAlignedHourOffset=Gb,Re.isDST=Hb,Re.isLocal=Jb,Re.isUtcOffset=Kb,Re.isUtc=Lb,Re.isUTC=Lb,
+// Timezone
+        Re.zoneAbbr=Hc,Re.zoneName=Ic,
+// Deprecations
+        Re.dates=w("dates accessor is deprecated. Use date instead.",Me),Re.months=w("months accessor is deprecated. Use month instead",ja),Re.years=w("years accessor is deprecated. Use year instead",ke),Re.zone=w("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",Cb),Re.isDSTShifted=w("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",Ib);var Se=Re,Te=B.prototype;Te.calendar=C,Te.longDateFormat=D,Te.invalidDate=E,Te.ordinal=F,Te.preparse=Lc,Te.postformat=Lc,Te.relativeTime=G,Te.pastFuture=H,Te.set=z,
+// Month
+        Te.months=ea,Te.monthsShort=fa,Te.monthsParse=ha,Te.monthsRegex=ma,Te.monthsShortRegex=la,
+// Week
+        Te.week=xa,Te.firstDayOfYear=za,Te.firstDayOfWeek=ya,
+// Day of Week
+        Te.weekdays=Ea,Te.weekdaysMin=Ga,Te.weekdaysShort=Fa,Te.weekdaysParse=Ia,Te.weekdaysRegex=Ma,Te.weekdaysShortRegex=Na,Te.weekdaysMinRegex=Oa,
+// Hours
+        Te.isPM=Ua,Te.meridiem=Va,Za("en",{ordinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(a){var b=a%10,c=1===t(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),
+// Side effect imports
+        a.lang=w("moment.lang is deprecated. Use moment.locale instead.",Za),a.langData=w("moment.langData is deprecated. Use moment.localeData instead.",ab);var Ue=Math.abs,Ve=cd("ms"),We=cd("s"),Xe=cd("m"),Ye=cd("h"),Ze=cd("d"),$e=cd("w"),_e=cd("M"),af=cd("y"),bf=ed("milliseconds"),cf=ed("seconds"),df=ed("minutes"),ef=ed("hours"),ff=ed("days"),gf=ed("months"),hf=ed("years"),jf=Math.round,kf={s:45,// seconds to minute
+        m:45,// minutes to hour
+        h:22,// hours to day
+        d:26,// days to month
+        M:11},lf=Math.abs,mf=vb.prototype;mf.abs=Uc,mf.add=Wc,mf.subtract=Xc,mf.as=ad,mf.asMilliseconds=Ve,mf.asSeconds=We,mf.asMinutes=Xe,mf.asHours=Ye,mf.asDays=Ze,mf.asWeeks=$e,mf.asMonths=_e,mf.asYears=af,mf.valueOf=bd,mf._bubble=Zc,mf.get=dd,mf.milliseconds=bf,mf.seconds=cf,mf.minutes=df,mf.hours=ef,mf.days=ff,mf.weeks=fd,mf.months=gf,mf.years=hf,mf.humanize=kd,mf.toISOString=ld,mf.toString=ld,mf.toJSON=ld,mf.locale=jc,mf.localeData=kc,
+// Deprecations
+        mf.toIsoString=w("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",ld),mf.lang=Le,
+// Side effect imports
+// FORMATTING
+        T("X",0,0,"unix"),T("x",0,0,"valueOf"),
+// PARSING
+        Y("x",Rd),Y("X",Ud),aa("X",function(a,b,c){c._d=new Date(1e3*parseFloat(a,10))}),aa("x",function(a,b,c){c._d=new Date(t(a))}),
+// Side effect imports
+        a.version="2.14.1",b(rb),a.fn=Se,a.min=tb,a.max=ub,a.now=Fe,a.utc=j,a.unix=Jc,a.months=Pc,a.isDate=f,a.locale=Za,a.invalid=n,a.duration=Mb,a.isMoment=r,a.weekdays=Rc,a.parseZone=Kc,a.localeData=ab,a.isDuration=wb,a.monthsShort=Qc,a.weekdaysMin=Tc,a.defineLocale=$a,a.updateLocale=_a,a.locales=bb,a.weekdaysShort=Sc,a.normalizeUnits=J,a.relativeTimeRounding=id,a.relativeTimeThreshold=jd,a.calendarFormat=Tb,a.prototype=Se;var nf=a;return nf});

File diff suppressed because it is too large
+ 0 - 0
dashboard/assets/javascripts/rickshaw-1.4.3.min.js


+ 258 - 0
dashboard/assets/stylesheets/application.scss

@@ -0,0 +1,258 @@
+/*
+  //=require_directory .
+  //=require_tree ../../widgets
+*/
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #222;
+$text-color: #fff;
+
+$background-warning-color-1: #e82711;
+$background-warning-color-2: #9b2d23;
+$text-warning-color: #fff;
+
+$background-danger-color-1: #eeae32;
+$background-danger-color-2: #ff9618;
+$text-danger-color: #fff;
+
+@-webkit-keyframes status-warning-background {
+  0%   { background-color: $background-warning-color-1; }
+  50%  { background-color: $background-warning-color-2; }
+  100% { background-color: $background-warning-color-1; }
+}
+@-webkit-keyframes status-danger-background {
+  0%   { background-color: $background-danger-color-1; }
+  50%  { background-color: $background-danger-color-2; }
+  100% { background-color: $background-danger-color-1; }
+}
+@mixin animation($animation-name, $duration, $function, $animation-iteration-count:""){
+  -webkit-animation:  $animation-name $duration $function #{$animation-iteration-count};
+  -moz-animation:     $animation-name $duration $function #{$animation-iteration-count};
+  -ms-animation:      $animation-name $duration $function #{$animation-iteration-count};
+}
+
+// ----------------------------------------------------------------------------
+// Base styles
+// ----------------------------------------------------------------------------
+html {
+  font-size: 100%;
+  -webkit-text-size-adjust: 100%;
+  -ms-text-size-adjust: 100%;
+}
+
+body {
+  margin: 0;
+  background-color: $background-color;
+  font-size: 20px;
+  color: $text-color;
+  font-family: 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+
+b, strong {
+  font-weight: bold;
+}
+
+a {
+  text-decoration: none;
+  color: inherit;
+}
+
+img {
+  border: 0;
+  -ms-interpolation-mode: bicubic;
+  vertical-align: middle;
+}
+
+img, object {
+  max-width: 100%;
+}
+
+iframe {
+  max-width: 100%;
+}
+
+table {
+  border-collapse: collapse;
+  border-spacing: 0;
+  width: 100%;
+}
+
+td {
+  vertical-align: middle;
+}
+
+ul, ol {
+  padding: 0;
+  margin: 0;
+}
+
+h1, h2, h3, h4, h5, p {
+  padding: 0;
+  margin: 0;
+}
+h1 {
+  margin-bottom: 12px;
+  text-align: center;
+  font-size: 30px;
+  font-weight: 400;
+}
+h2 {
+  text-transform: uppercase;
+  font-size: 76px;
+  font-weight: 700;
+  color: $text-color;
+}
+h3 {
+  font-size: 25px;
+  font-weight: 600;
+  color: $text-color;
+}
+
+// ----------------------------------------------------------------------------
+// Base widget styles
+// ----------------------------------------------------------------------------
+.gridster {
+  margin: 0px auto;
+}
+
+.icon-background {
+  pointer-events: none;
+  width: 100%!important;
+  height: 100%;
+  position: absolute;
+  left: 0;
+  top: 0;
+  opacity: 0.1;
+  font-size: 275px;
+  text-align: center;
+  margin-top: 82px;
+}
+
+.list-nostyle {
+  list-style: none;
+}
+
+.gridster ul {
+  list-style: none;
+}
+
+.gs_w {
+  width: 100%;
+  display: table;
+  cursor: pointer;
+}
+
+.widget {
+  text-align: center;
+  width: inherit;
+  height: inherit;
+  display: table-cell;
+  vertical-align: middle;
+}
+
+.widget.status-warning {
+  background-color: $background-warning-color-1;
+  @include animation(status-warning-background, 2s, ease, infinite);
+
+  .icon-warning-sign {
+    display: inline-block;
+  }
+
+  .title, .more-info {
+    color: $text-warning-color;
+  }
+}
+
+.widget.status-danger {
+  color: $text-danger-color;
+  background-color: $background-danger-color-1;
+  @include animation(status-danger-background, 2s, ease, infinite);
+
+  .icon-warning-sign {
+    display: inline-block;
+  }
+
+  .title, .more-info {
+    color: $text-danger-color;
+  }
+}
+
+.more-info {
+  font-size: 15px;
+  position: absolute;
+  bottom: 32px;
+  left: 0;
+  right: 0;
+}
+
+.updated-at {
+  font-size: 15px;
+  position: absolute;
+  bottom: 12px;
+  left: 0;
+  right: 0;
+}
+
+#save-gridster {
+  display: none;
+  position: fixed;
+  top: 0;
+  margin: 0px auto;
+  left: 50%;
+  z-index: 1000;
+  background: black;
+  width: 190px;
+  text-align: center;
+  border: 1px solid white;
+  border-top: 0px;
+  margin-left: -95px;
+  padding: 15px;
+}
+
+#save-gridster:hover {
+  padding-top: 25px;
+}
+
+#saving-instructions {
+  display: none;
+  padding: 10px;
+  width: 500px;
+  height: 122px;
+  z-index: 1000;
+  background: white;
+  top: 100px;
+  color: black;
+  font-size: 15px;
+  padding-bottom: 4px;
+
+  textarea {
+    white-space: nowrap;
+    width: 494px;
+    height: 80px;
+  }
+}
+
+#lean_overlay {
+    position: fixed;
+    z-index:100;
+    top: 0px;
+    left: 0px;
+    height:100%;
+    width:100%;
+    background: #000;
+    display: none;
+}
+
+#container {
+  padding-top: 5px;
+}
+
+
+// ----------------------------------------------------------------------------
+// Clearfix
+// ----------------------------------------------------------------------------
+.clearfix:before, .clearfix:after { content: "\0020"; display: block; height: 0; overflow: hidden; }
+.clearfix:after { clear: both; }
+.clearfix { zoom: 1; }
+

+ 298 - 0
dashboard/assets/stylesheets/climacons-font.css

@@ -0,0 +1,298 @@
+@font-face {
+	font-family: 'Climacons-Font';
+	src:url('climacons-webfont.eot');
+	src:url('climacons-webfont.eot?#iefix') format('embedded-opentype'),
+		url('climacons-webfont.svg#Climacons-Font') format('svg'),
+		url('climacons-webfont.woff') format('woff'),
+		url('climacons-webfont.ttf') format('truetype');
+	font-weight: normal;
+	font-style: normal;
+}
+.climacon:before{
+	font-family: 'Climacons-Font';
+	speak: none;
+	font-style: normal;
+	font-weight: normal;
+	line-height: 1;
+	-webkit-font-smoothing: antialiased;
+}
+.climacon.cloud:before {
+	content: "\e000";
+}
+.climacon.cloud.sun:before {
+	content: "\e001";
+}
+.climacon.cloud.moon:before {
+	content: "\e002";
+}
+.climacon.rain:before,
+.climacon.rain.cloud:before {
+	content: "\e003";
+}
+.climacon.rain.sun:before,
+.climacon.rain.cloud.sun:before {
+	content: "\e004";
+}
+.climacon.rain.moon:before,
+.climacon.rain.cloud.moon:before {
+	content: "\e005";
+}
+.climacon.showers:before,
+.climacon.showers.cloud:before {
+	content: "\e006";
+}
+.climacon.showers.sun:before,
+.climacon.showers.cloud.sun:before {
+	content: "\e007";
+}
+.climacon.showers.moon:before,
+.climacon.showers.cloud.moon:before {
+	content: "\e008";
+}
+.climacon.downpour:before,
+.climacon.downpour.cloud:before {
+	content: "\e009";
+}
+.climacon.downpour.sun:before,
+.climacon.downpour.cloud.sun:before {
+	content: "\e00a";
+}
+.climacon.downpour.moon:before,
+.climacon.downpour.cloud.moon:before {
+	content: "\e00b";
+}
+.climacon.drizzle:before,
+.climacon.drizzle.cloud:before {
+	content: "\e00c";
+}
+.climacon.drizzle.sun:before,
+.climacon.drizzle.cloud.sun:before {
+	content: "\e00d";
+}
+.climacon.drizzle.moon:before,
+.climacon.drizzle.cloud.moon:before {
+	content: "\e00e";
+}
+.climacon.sleet:before,
+.climacon.sleet.cloud:before {
+	content: "\e00f";
+}
+.climacon.sleet.sun:before,
+.climacon.sleet.cloud.sun:before {
+	content: "\e010";
+}
+.climacon.sleet.moon:before,
+.climacon.sleet.cloud.moon:before {
+	content: "\e011";
+}
+.climacon.hail:before,
+.climacon.hail.cloud:before {
+	content: "\e012";
+}
+.climacon.hail.sun:before,
+.climacon.hail.cloud.sun:before {
+	content: "\e013";
+}
+.climacon.hail.moon:before,
+.climacon.hail.cloud.moon:before {
+	content: "\e014";
+}
+.climacon.flurries:before,
+.climacon.flurries.cloud:before {
+	content: "\e015";
+}
+.climacon.flurries.sun:before,
+.climacon.flurries.cloud.sun:before {
+	content: "\e016";
+}
+.climacon.flurries.moon:before,
+.climacon.flurries.cloud.moon:before {
+	content: "\e017";
+}
+.climacon.snow:before,
+.climacon.snow.cloud:before {
+	content: "\e018";
+}
+.climacon.snow.sun:before,
+.climacon.snow.cloud.sun:before {
+	content: "\e019";
+}
+.climacon.snow.moon:before,
+.climacon.snow.cloud.moon:before {
+	content: "\e01a";
+}
+.climacon.fog:before,
+.climacon.fog.cloud:before {
+	content: "\e01b";
+}
+.climacon.fog.sun:before,
+.climacon.fog.cloud.sun:before {
+	content: "\e01c";
+}
+.climacon.fog.moon:before,
+.climacon.fog.cloud.moon:before {
+	content: "\e01d";
+}
+.climacon.haze:before {
+	content: "\e01e";
+}
+.climacon.haze.sun:before {
+	content: "\e01f";
+}
+.climacon.haze.moon:before {
+	content: "\e020";
+}
+.climacon.wind:before {
+	content: "\e021";
+}
+.climacon.wind.cloud:before {
+	content: "\e022";
+}
+.climacon.wind.sun:before,
+.climacon.wind.cloud.sun:before {
+	content: "\e023";
+}
+.climacon.wind.moon:before,
+.climacon.wind.cloud.moon:before {
+	content: "\e024";
+}
+.climacon.lightning:before,
+.climacon.lightning.cloud:before {
+	content: "\e025";
+}
+.climacon.lightning.sun:before,
+.climacon.lightning.cloud.sun:before {
+	content: "\e026";
+}
+.climacon.lightning.moon:before,
+.climacon.lightning.cloud.moon:before {
+	content: "\e027";
+}
+.climacon.sun:before {
+	content: "\e028";
+}
+.climacon.sun.set:before,
+.climacon.sunset:before {
+	content: "\e029";
+}
+.climacon.sun.rise:before,
+.climacon.sunrise:before {
+	content: "\e02a";
+}
+.climacon.sun.low:before,
+.climacon.sun-low:before,
+.climacon.low-sun:before {
+	content: "\e02b";
+}
+.climacon.sun.lower:before,
+.climacon.sun-lower:before,
+.climacon.lower-sun:before {
+	content: "\e02c";
+}
+.climacon.moon:before {
+	content: "\e02d";
+}
+.climacon.moon.new:before {
+	content: "\e02e";
+}
+.climacon.moon.waxing.crescent:before,
+.climacon.moon.first-crescent:before {
+	content: "\e02f";
+}
+.climacon.moon.waxing.quarter:before,
+.climacon.moon.first-quarter:before,
+.climacon.moon.waxing.half:before,
+.climacon.moon.first-half:before{
+	content: "\e030";
+}
+.climacon.moon.waxing.gibbous:before,
+.climacon.moon.first-gibbous:before,
+.climacon.moon.waxing.three-quarter:before,
+.climacon.moon.first-three-quarter:before {
+	content: "\e031";
+}
+.climacon.moon.full:before {
+	content: "\e032";
+}
+.climacon.moon.waning.gibbous:before,
+.climacon.moon.last-gibbous:before,
+.climacon.moon.waning.three-quarter:before,
+.climacon.moon.last-three-quarter:before {
+	content: "\e033";
+}
+.climacon.moon.waning.quarter:before,
+.climacon.moon.last-quarter:before,
+.climacon.moon.waning.half:before,
+.climacon.moon.last-half:before {
+	content: "\e034";
+}
+.climacon.moon.waning.crescent:before,
+.climacon.moon.last-crescent:before {
+	content: "\e035";
+}
+.climacon.snowflake:before {
+	content: "\e036";
+}
+.climacon.tornado:before {
+	content: "\e037";
+}
+.climacon.thermometer.empty:before,
+.climacon.thermometer:before {
+	content: "\e038";
+}
+.climacon.thermometer.low:before {
+	content: "\e039";
+}
+.climacon.thermometer.medium-low:before {
+	content: "\e03a";
+}
+.climacon.thermometer.medium-high:before {
+	content: "\e03b";
+}
+.climacon.thermometer.high:before {
+	content: "\e03c";
+}
+.climacon.thermometer.full:before {
+	content: "\e03d";
+}
+.climacon.celcius:before,
+.climacon.celsius:before {
+	content: "\e03e";
+}
+.climacon.farenheit:before,
+.climacon.fahrenheit:before {
+	content: "\e03f";
+}
+.climacon.compass:before {
+	content: "\e040";
+}
+.climacon.compass.north:before {
+	content: "\e041";
+}
+.climacon.compass.east:before {
+	content: "\e042";
+}
+.climacon.compass.south:before {
+	content: "\e043";
+}
+.climacon.compass.west:before {
+	content: "\e044";
+}
+.climacon.umbrella:before {
+	content: "\e045";
+}
+.climacon.sunglasses:before {
+	content: "\e046";
+}
+.climacon.cloud.cycle:before,
+.climacon.cloud.refresh:before {
+	content: "\e047";
+}
+.climacon.cloud.down:before,
+.climacon.cloud.download:before {
+	content: "\e048";
+}
+.climacon.cloud.up:before,
+.climacon.cloud.upload:before {
+	content: "\e049";
+}

+ 2086 - 0
dashboard/assets/stylesheets/font-awesome.css

@@ -0,0 +1,2086 @@
+/*!
+ *  Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome
+ *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */
+/* FONT PATH
+ * -------------------------- */
+@font-face {
+  font-family: 'FontAwesome';
+  src: url('../assets/fontawesome-webfont.eot?v=4.5.0');
+  src: url('../assets/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'), url('../assets/fontawesome-webfont.woff2?v=4.5.0') format('woff2'), url('../assets/fontawesome-webfont.woff?v=4.5.0') format('woff'), url('../assets/fontawesome-webfont.ttf?v=4.5.0') format('truetype'), url('../assets/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+.fa {
+  display: inline-block;
+  font: normal normal normal 14px/1 FontAwesome;
+  font-size: inherit;
+  text-rendering: auto;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+/* makes the font 33% larger relative to the icon container */
+.fa-lg {
+  font-size: 1.33333333em;
+  line-height: 0.75em;
+  vertical-align: -15%;
+}
+.fa-2x {
+  font-size: 2em;
+}
+.fa-3x {
+  font-size: 3em;
+}
+.fa-4x {
+  font-size: 4em;
+}
+.fa-5x {
+  font-size: 5em;
+}
+.fa-fw {
+  width: 1.28571429em;
+  text-align: center;
+}
+.fa-ul {
+  padding-left: 0;
+  margin-left: 2.14285714em;
+  list-style-type: none;
+}
+.fa-ul > li {
+  position: relative;
+}
+.fa-li {
+  position: absolute;
+  left: -2.14285714em;
+  width: 2.14285714em;
+  top: 0.14285714em;
+  text-align: center;
+}
+.fa-li.fa-lg {
+  left: -1.85714286em;
+}
+.fa-border {
+  padding: .2em .25em .15em;
+  border: solid 0.08em #eeeeee;
+  border-radius: .1em;
+}
+.fa-pull-left {
+  float: left;
+}
+.fa-pull-right {
+  float: right;
+}
+.fa.fa-pull-left {
+  margin-right: .3em;
+}
+.fa.fa-pull-right {
+  margin-left: .3em;
+}
+/* Deprecated as of 4.4.0 */
+.pull-right {
+  float: right;
+}
+.pull-left {
+  float: left;
+}
+.fa.pull-left {
+  margin-right: .3em;
+}
+.fa.pull-right {
+  margin-left: .3em;
+}
+.fa-spin {
+  -webkit-animation: fa-spin 2s infinite linear;
+  animation: fa-spin 2s infinite linear;
+}
+.fa-pulse {
+  -webkit-animation: fa-spin 1s infinite steps(8);
+  animation: fa-spin 1s infinite steps(8);
+}
+@-webkit-keyframes fa-spin {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  100% {
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+@keyframes fa-spin {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  100% {
+    -webkit-transform: rotate(359deg);
+    transform: rotate(359deg);
+  }
+}
+.fa-rotate-90 {
+  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
+  -webkit-transform: rotate(90deg);
+  -ms-transform: rotate(90deg);
+  transform: rotate(90deg);
+}
+.fa-rotate-180 {
+  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
+  -webkit-transform: rotate(180deg);
+  -ms-transform: rotate(180deg);
+  transform: rotate(180deg);
+}
+.fa-rotate-270 {
+  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
+  -webkit-transform: rotate(270deg);
+  -ms-transform: rotate(270deg);
+  transform: rotate(270deg);
+}
+.fa-flip-horizontal {
+  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
+  -webkit-transform: scale(-1, 1);
+  -ms-transform: scale(-1, 1);
+  transform: scale(-1, 1);
+}
+.fa-flip-vertical {
+  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
+  -webkit-transform: scale(1, -1);
+  -ms-transform: scale(1, -1);
+  transform: scale(1, -1);
+}
+:root .fa-rotate-90,
+:root .fa-rotate-180,
+:root .fa-rotate-270,
+:root .fa-flip-horizontal,
+:root .fa-flip-vertical {
+  filter: none;
+}
+.fa-stack {
+  position: relative;
+  display: inline-block;
+  width: 2em;
+  height: 2em;
+  line-height: 2em;
+  vertical-align: middle;
+}
+.fa-stack-1x,
+.fa-stack-2x {
+  position: absolute;
+  left: 0;
+  width: 100%;
+  text-align: center;
+}
+.fa-stack-1x {
+  line-height: inherit;
+}
+.fa-stack-2x {
+  font-size: 2em;
+}
+.fa-inverse {
+  color: #ffffff;
+}
+/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
+   readers do not read off random characters that represent icons */
+.fa-glass:before {
+  content: "\f000";
+}
+.fa-music:before {
+  content: "\f001";
+}
+.fa-search:before {
+  content: "\f002";
+}
+.fa-envelope-o:before {
+  content: "\f003";
+}
+.fa-heart:before {
+  content: "\f004";
+}
+.fa-star:before {
+  content: "\f005";
+}
+.fa-star-o:before {
+  content: "\f006";
+}
+.fa-user:before {
+  content: "\f007";
+}
+.fa-film:before {
+  content: "\f008";
+}
+.fa-th-large:before {
+  content: "\f009";
+}
+.fa-th:before {
+  content: "\f00a";
+}
+.fa-th-list:before {
+  content: "\f00b";
+}
+.fa-check:before {
+  content: "\f00c";
+}
+.fa-remove:before,
+.fa-close:before,
+.fa-times:before {
+  content: "\f00d";
+}
+.fa-search-plus:before {
+  content: "\f00e";
+}
+.fa-search-minus:before {
+  content: "\f010";
+}
+.fa-power-off:before {
+  content: "\f011";
+}
+.fa-signal:before {
+  content: "\f012";
+}
+.fa-gear:before,
+.fa-cog:before {
+  content: "\f013";
+}
+.fa-trash-o:before {
+  content: "\f014";
+}
+.fa-home:before {
+  content: "\f015";
+}
+.fa-file-o:before {
+  content: "\f016";
+}
+.fa-clock-o:before {
+  content: "\f017";
+}
+.fa-road:before {
+  content: "\f018";
+}
+.fa-download:before {
+  content: "\f019";
+}
+.fa-arrow-circle-o-down:before {
+  content: "\f01a";
+}
+.fa-arrow-circle-o-up:before {
+  content: "\f01b";
+}
+.fa-inbox:before {
+  content: "\f01c";
+}
+.fa-play-circle-o:before {
+  content: "\f01d";
+}
+.fa-rotate-right:before,
+.fa-repeat:before {
+  content: "\f01e";
+}
+.fa-refresh:before {
+  content: "\f021";
+}
+.fa-list-alt:before {
+  content: "\f022";
+}
+.fa-lock:before {
+  content: "\f023";
+}
+.fa-flag:before {
+  content: "\f024";
+}
+.fa-headphones:before {
+  content: "\f025";
+}
+.fa-volume-off:before {
+  content: "\f026";
+}
+.fa-volume-down:before {
+  content: "\f027";
+}
+.fa-volume-up:before {
+  content: "\f028";
+}
+.fa-qrcode:before {
+  content: "\f029";
+}
+.fa-barcode:before {
+  content: "\f02a";
+}
+.fa-tag:before {
+  content: "\f02b";
+}
+.fa-tags:before {
+  content: "\f02c";
+}
+.fa-book:before {
+  content: "\f02d";
+}
+.fa-bookmark:before {
+  content: "\f02e";
+}
+.fa-print:before {
+  content: "\f02f";
+}
+.fa-camera:before {
+  content: "\f030";
+}
+.fa-font:before {
+  content: "\f031";
+}
+.fa-bold:before {
+  content: "\f032";
+}
+.fa-italic:before {
+  content: "\f033";
+}
+.fa-text-height:before {
+  content: "\f034";
+}
+.fa-text-width:before {
+  content: "\f035";
+}
+.fa-align-left:before {
+  content: "\f036";
+}
+.fa-align-center:before {
+  content: "\f037";
+}
+.fa-align-right:before {
+  content: "\f038";
+}
+.fa-align-justify:before {
+  content: "\f039";
+}
+.fa-list:before {
+  content: "\f03a";
+}
+.fa-dedent:before,
+.fa-outdent:before {
+  content: "\f03b";
+}
+.fa-indent:before {
+  content: "\f03c";
+}
+.fa-video-camera:before {
+  content: "\f03d";
+}
+.fa-photo:before,
+.fa-image:before,
+.fa-picture-o:before {
+  content: "\f03e";
+}
+.fa-pencil:before {
+  content: "\f040";
+}
+.fa-map-marker:before {
+  content: "\f041";
+}
+.fa-adjust:before {
+  content: "\f042";
+}
+.fa-tint:before {
+  content: "\f043";
+}
+.fa-edit:before,
+.fa-pencil-square-o:before {
+  content: "\f044";
+}
+.fa-share-square-o:before {
+  content: "\f045";
+}
+.fa-check-square-o:before {
+  content: "\f046";
+}
+.fa-arrows:before {
+  content: "\f047";
+}
+.fa-step-backward:before {
+  content: "\f048";
+}
+.fa-fast-backward:before {
+  content: "\f049";
+}
+.fa-backward:before {
+  content: "\f04a";
+}
+.fa-play:before {
+  content: "\f04b";
+}
+.fa-pause:before {
+  content: "\f04c";
+}
+.fa-stop:before {
+  content: "\f04d";
+}
+.fa-forward:before {
+  content: "\f04e";
+}
+.fa-fast-forward:before {
+  content: "\f050";
+}
+.fa-step-forward:before {
+  content: "\f051";
+}
+.fa-eject:before {
+  content: "\f052";
+}
+.fa-chevron-left:before {
+  content: "\f053";
+}
+.fa-chevron-right:before {
+  content: "\f054";
+}
+.fa-plus-circle:before {
+  content: "\f055";
+}
+.fa-minus-circle:before {
+  content: "\f056";
+}
+.fa-times-circle:before {
+  content: "\f057";
+}
+.fa-check-circle:before {
+  content: "\f058";
+}
+.fa-question-circle:before {
+  content: "\f059";
+}
+.fa-info-circle:before {
+  content: "\f05a";
+}
+.fa-crosshairs:before {
+  content: "\f05b";
+}
+.fa-times-circle-o:before {
+  content: "\f05c";
+}
+.fa-check-circle-o:before {
+  content: "\f05d";
+}
+.fa-ban:before {
+  content: "\f05e";
+}
+.fa-arrow-left:before {
+  content: "\f060";
+}
+.fa-arrow-right:before {
+  content: "\f061";
+}
+.fa-arrow-up:before {
+  content: "\f062";
+}
+.fa-arrow-down:before {
+  content: "\f063";
+}
+.fa-mail-forward:before,
+.fa-share:before {
+  content: "\f064";
+}
+.fa-expand:before {
+  content: "\f065";
+}
+.fa-compress:before {
+  content: "\f066";
+}
+.fa-plus:before {
+  content: "\f067";
+}
+.fa-minus:before {
+  content: "\f068";
+}
+.fa-asterisk:before {
+  content: "\f069";
+}
+.fa-exclamation-circle:before {
+  content: "\f06a";
+}
+.fa-gift:before {
+  content: "\f06b";
+}
+.fa-leaf:before {
+  content: "\f06c";
+}
+.fa-fire:before {
+  content: "\f06d";
+}
+.fa-eye:before {
+  content: "\f06e";
+}
+.fa-eye-slash:before {
+  content: "\f070";
+}
+.fa-warning:before,
+.fa-exclamation-triangle:before {
+  content: "\f071";
+}
+.fa-plane:before {
+  content: "\f072";
+}
+.fa-calendar:before {
+  content: "\f073";
+}
+.fa-random:before {
+  content: "\f074";
+}
+.fa-comment:before {
+  content: "\f075";
+}
+.fa-magnet:before {
+  content: "\f076";
+}
+.fa-chevron-up:before {
+  content: "\f077";
+}
+.fa-chevron-down:before {
+  content: "\f078";
+}
+.fa-retweet:before {
+  content: "\f079";
+}
+.fa-shopping-cart:before {
+  content: "\f07a";
+}
+.fa-folder:before {
+  content: "\f07b";
+}
+.fa-folder-open:before {
+  content: "\f07c";
+}
+.fa-arrows-v:before {
+  content: "\f07d";
+}
+.fa-arrows-h:before {
+  content: "\f07e";
+}
+.fa-bar-chart-o:before,
+.fa-bar-chart:before {
+  content: "\f080";
+}
+.fa-twitter-square:before {
+  content: "\f081";
+}
+.fa-facebook-square:before {
+  content: "\f082";
+}
+.fa-camera-retro:before {
+  content: "\f083";
+}
+.fa-key:before {
+  content: "\f084";
+}
+.fa-gears:before,
+.fa-cogs:before {
+  content: "\f085";
+}
+.fa-comments:before {
+  content: "\f086";
+}
+.fa-thumbs-o-up:before {
+  content: "\f087";
+}
+.fa-thumbs-o-down:before {
+  content: "\f088";
+}
+.fa-star-half:before {
+  content: "\f089";
+}
+.fa-heart-o:before {
+  content: "\f08a";
+}
+.fa-sign-out:before {
+  content: "\f08b";
+}
+.fa-linkedin-square:before {
+  content: "\f08c";
+}
+.fa-thumb-tack:before {
+  content: "\f08d";
+}
+.fa-external-link:before {
+  content: "\f08e";
+}
+.fa-sign-in:before {
+  content: "\f090";
+}
+.fa-trophy:before {
+  content: "\f091";
+}
+.fa-github-square:before {
+  content: "\f092";
+}
+.fa-upload:before {
+  content: "\f093";
+}
+.fa-lemon-o:before {
+  content: "\f094";
+}
+.fa-phone:before {
+  content: "\f095";
+}
+.fa-square-o:before {
+  content: "\f096";
+}
+.fa-bookmark-o:before {
+  content: "\f097";
+}
+.fa-phone-square:before {
+  content: "\f098";
+}
+.fa-twitter:before {
+  content: "\f099";
+}
+.fa-facebook-f:before,
+.fa-facebook:before {
+  content: "\f09a";
+}
+.fa-github:before {
+  content: "\f09b";
+}
+.fa-unlock:before {
+  content: "\f09c";
+}
+.fa-credit-card:before {
+  content: "\f09d";
+}
+.fa-feed:before,
+.fa-rss:before {
+  content: "\f09e";
+}
+.fa-hdd-o:before {
+  content: "\f0a0";
+}
+.fa-bullhorn:before {
+  content: "\f0a1";
+}
+.fa-bell:before {
+  content: "\f0f3";
+}
+.fa-certificate:before {
+  content: "\f0a3";
+}
+.fa-hand-o-right:before {
+  content: "\f0a4";
+}
+.fa-hand-o-left:before {
+  content: "\f0a5";
+}
+.fa-hand-o-up:before {
+  content: "\f0a6";
+}
+.fa-hand-o-down:before {
+  content: "\f0a7";
+}
+.fa-arrow-circle-left:before {
+  content: "\f0a8";
+}
+.fa-arrow-circle-right:before {
+  content: "\f0a9";
+}
+.fa-arrow-circle-up:before {
+  content: "\f0aa";
+}
+.fa-arrow-circle-down:before {
+  content: "\f0ab";
+}
+.fa-globe:before {
+  content: "\f0ac";
+}
+.fa-wrench:before {
+  content: "\f0ad";
+}
+.fa-tasks:before {
+  content: "\f0ae";
+}
+.fa-filter:before {
+  content: "\f0b0";
+}
+.fa-briefcase:before {
+  content: "\f0b1";
+}
+.fa-arrows-alt:before {
+  content: "\f0b2";
+}
+.fa-group:before,
+.fa-users:before {
+  content: "\f0c0";
+}
+.fa-chain:before,
+.fa-link:before {
+  content: "\f0c1";
+}
+.fa-cloud:before {
+  content: "\f0c2";
+}
+.fa-flask:before {
+  content: "\f0c3";
+}
+.fa-cut:before,
+.fa-scissors:before {
+  content: "\f0c4";
+}
+.fa-copy:before,
+.fa-files-o:before {
+  content: "\f0c5";
+}
+.fa-paperclip:before {
+  content: "\f0c6";
+}
+.fa-save:before,
+.fa-floppy-o:before {
+  content: "\f0c7";
+}
+.fa-square:before {
+  content: "\f0c8";
+}
+.fa-navicon:before,
+.fa-reorder:before,
+.fa-bars:before {
+  content: "\f0c9";
+}
+.fa-list-ul:before {
+  content: "\f0ca";
+}
+.fa-list-ol:before {
+  content: "\f0cb";
+}
+.fa-strikethrough:before {
+  content: "\f0cc";
+}
+.fa-underline:before {
+  content: "\f0cd";
+}
+.fa-table:before {
+  content: "\f0ce";
+}
+.fa-magic:before {
+  content: "\f0d0";
+}
+.fa-truck:before {
+  content: "\f0d1";
+}
+.fa-pinterest:before {
+  content: "\f0d2";
+}
+.fa-pinterest-square:before {
+  content: "\f0d3";
+}
+.fa-google-plus-square:before {
+  content: "\f0d4";
+}
+.fa-google-plus:before {
+  content: "\f0d5";
+}
+.fa-money:before {
+  content: "\f0d6";
+}
+.fa-caret-down:before {
+  content: "\f0d7";
+}
+.fa-caret-up:before {
+  content: "\f0d8";
+}
+.fa-caret-left:before {
+  content: "\f0d9";
+}
+.fa-caret-right:before {
+  content: "\f0da";
+}
+.fa-columns:before {
+  content: "\f0db";
+}
+.fa-unsorted:before,
+.fa-sort:before {
+  content: "\f0dc";
+}
+.fa-sort-down:before,
+.fa-sort-desc:before {
+  content: "\f0dd";
+}
+.fa-sort-up:before,
+.fa-sort-asc:before {
+  content: "\f0de";
+}
+.fa-envelope:before {
+  content: "\f0e0";
+}
+.fa-linkedin:before {
+  content: "\f0e1";
+}
+.fa-rotate-left:before,
+.fa-undo:before {
+  content: "\f0e2";
+}
+.fa-legal:before,
+.fa-gavel:before {
+  content: "\f0e3";
+}
+.fa-dashboard:before,
+.fa-tachometer:before {
+  content: "\f0e4";
+}
+.fa-comment-o:before {
+  content: "\f0e5";
+}
+.fa-comments-o:before {
+  content: "\f0e6";
+}
+.fa-flash:before,
+.fa-bolt:before {
+  content: "\f0e7";
+}
+.fa-sitemap:before {
+  content: "\f0e8";
+}
+.fa-umbrella:before {
+  content: "\f0e9";
+}
+.fa-paste:before,
+.fa-clipboard:before {
+  content: "\f0ea";
+}
+.fa-lightbulb-o:before {
+  content: "\f0eb";
+}
+.fa-exchange:before {
+  content: "\f0ec";
+}
+.fa-cloud-download:before {
+  content: "\f0ed";
+}
+.fa-cloud-upload:before {
+  content: "\f0ee";
+}
+.fa-user-md:before {
+  content: "\f0f0";
+}
+.fa-stethoscope:before {
+  content: "\f0f1";
+}
+.fa-suitcase:before {
+  content: "\f0f2";
+}
+.fa-bell-o:before {
+  content: "\f0a2";
+}
+.fa-coffee:before {
+  content: "\f0f4";
+}
+.fa-cutlery:before {
+  content: "\f0f5";
+}
+.fa-file-text-o:before {
+  content: "\f0f6";
+}
+.fa-building-o:before {
+  content: "\f0f7";
+}
+.fa-hospital-o:before {
+  content: "\f0f8";
+}
+.fa-ambulance:before {
+  content: "\f0f9";
+}
+.fa-medkit:before {
+  content: "\f0fa";
+}
+.fa-fighter-jet:before {
+  content: "\f0fb";
+}
+.fa-beer:before {
+  content: "\f0fc";
+}
+.fa-h-square:before {
+  content: "\f0fd";
+}
+.fa-plus-square:before {
+  content: "\f0fe";
+}
+.fa-angle-double-left:before {
+  content: "\f100";
+}
+.fa-angle-double-right:before {
+  content: "\f101";
+}
+.fa-angle-double-up:before {
+  content: "\f102";
+}
+.fa-angle-double-down:before {
+  content: "\f103";
+}
+.fa-angle-left:before {
+  content: "\f104";
+}
+.fa-angle-right:before {
+  content: "\f105";
+}
+.fa-angle-up:before {
+  content: "\f106";
+}
+.fa-angle-down:before {
+  content: "\f107";
+}
+.fa-desktop:before {
+  content: "\f108";
+}
+.fa-laptop:before {
+  content: "\f109";
+}
+.fa-tablet:before {
+  content: "\f10a";
+}
+.fa-mobile-phone:before,
+.fa-mobile:before {
+  content: "\f10b";
+}
+.fa-circle-o:before {
+  content: "\f10c";
+}
+.fa-quote-left:before {
+  content: "\f10d";
+}
+.fa-quote-right:before {
+  content: "\f10e";
+}
+.fa-spinner:before {
+  content: "\f110";
+}
+.fa-circle:before {
+  content: "\f111";
+}
+.fa-mail-reply:before,
+.fa-reply:before {
+  content: "\f112";
+}
+.fa-github-alt:before {
+  content: "\f113";
+}
+.fa-folder-o:before {
+  content: "\f114";
+}
+.fa-folder-open-o:before {
+  content: "\f115";
+}
+.fa-smile-o:before {
+  content: "\f118";
+}
+.fa-frown-o:before {
+  content: "\f119";
+}
+.fa-meh-o:before {
+  content: "\f11a";
+}
+.fa-gamepad:before {
+  content: "\f11b";
+}
+.fa-keyboard-o:before {
+  content: "\f11c";
+}
+.fa-flag-o:before {
+  content: "\f11d";
+}
+.fa-flag-checkered:before {
+  content: "\f11e";
+}
+.fa-terminal:before {
+  content: "\f120";
+}
+.fa-code:before {
+  content: "\f121";
+}
+.fa-mail-reply-all:before,
+.fa-reply-all:before {
+  content: "\f122";
+}
+.fa-star-half-empty:before,
+.fa-star-half-full:before,
+.fa-star-half-o:before {
+  content: "\f123";
+}
+.fa-location-arrow:before {
+  content: "\f124";
+}
+.fa-crop:before {
+  content: "\f125";
+}
+.fa-code-fork:before {
+  content: "\f126";
+}
+.fa-unlink:before,
+.fa-chain-broken:before {
+  content: "\f127";
+}
+.fa-question:before {
+  content: "\f128";
+}
+.fa-info:before {
+  content: "\f129";
+}
+.fa-exclamation:before {
+  content: "\f12a";
+}
+.fa-superscript:before {
+  content: "\f12b";
+}
+.fa-subscript:before {
+  content: "\f12c";
+}
+.fa-eraser:before {
+  content: "\f12d";
+}
+.fa-puzzle-piece:before {
+  content: "\f12e";
+}
+.fa-microphone:before {
+  content: "\f130";
+}
+.fa-microphone-slash:before {
+  content: "\f131";
+}
+.fa-shield:before {
+  content: "\f132";
+}
+.fa-calendar-o:before {
+  content: "\f133";
+}
+.fa-fire-extinguisher:before {
+  content: "\f134";
+}
+.fa-rocket:before {
+  content: "\f135";
+}
+.fa-maxcdn:before {
+  content: "\f136";
+}
+.fa-chevron-circle-left:before {
+  content: "\f137";
+}
+.fa-chevron-circle-right:before {
+  content: "\f138";
+}
+.fa-chevron-circle-up:before {
+  content: "\f139";
+}
+.fa-chevron-circle-down:before {
+  content: "\f13a";
+}
+.fa-html5:before {
+  content: "\f13b";
+}
+.fa-css3:before {
+  content: "\f13c";
+}
+.fa-anchor:before {
+  content: "\f13d";
+}
+.fa-unlock-alt:before {
+  content: "\f13e";
+}
+.fa-bullseye:before {
+  content: "\f140";
+}
+.fa-ellipsis-h:before {
+  content: "\f141";
+}
+.fa-ellipsis-v:before {
+  content: "\f142";
+}
+.fa-rss-square:before {
+  content: "\f143";
+}
+.fa-play-circle:before {
+  content: "\f144";
+}
+.fa-ticket:before {
+  content: "\f145";
+}
+.fa-minus-square:before {
+  content: "\f146";
+}
+.fa-minus-square-o:before {
+  content: "\f147";
+}
+.fa-level-up:before {
+  content: "\f148";
+}
+.fa-level-down:before {
+  content: "\f149";
+}
+.fa-check-square:before {
+  content: "\f14a";
+}
+.fa-pencil-square:before {
+  content: "\f14b";
+}
+.fa-external-link-square:before {
+  content: "\f14c";
+}
+.fa-share-square:before {
+  content: "\f14d";
+}
+.fa-compass:before {
+  content: "\f14e";
+}
+.fa-toggle-down:before,
+.fa-caret-square-o-down:before {
+  content: "\f150";
+}
+.fa-toggle-up:before,
+.fa-caret-square-o-up:before {
+  content: "\f151";
+}
+.fa-toggle-right:before,
+.fa-caret-square-o-right:before {
+  content: "\f152";
+}
+.fa-euro:before,
+.fa-eur:before {
+  content: "\f153";
+}
+.fa-gbp:before {
+  content: "\f154";
+}
+.fa-dollar:before,
+.fa-usd:before {
+  content: "\f155";
+}
+.fa-rupee:before,
+.fa-inr:before {
+  content: "\f156";
+}
+.fa-cny:before,
+.fa-rmb:before,
+.fa-yen:before,
+.fa-jpy:before {
+  content: "\f157";
+}
+.fa-ruble:before,
+.fa-rouble:before,
+.fa-rub:before {
+  content: "\f158";
+}
+.fa-won:before,
+.fa-krw:before {
+  content: "\f159";
+}
+.fa-bitcoin:before,
+.fa-btc:before {
+  content: "\f15a";
+}
+.fa-file:before {
+  content: "\f15b";
+}
+.fa-file-text:before {
+  content: "\f15c";
+}
+.fa-sort-alpha-asc:before {
+  content: "\f15d";
+}
+.fa-sort-alpha-desc:before {
+  content: "\f15e";
+}
+.fa-sort-amount-asc:before {
+  content: "\f160";
+}
+.fa-sort-amount-desc:before {
+  content: "\f161";
+}
+.fa-sort-numeric-asc:before {
+  content: "\f162";
+}
+.fa-sort-numeric-desc:before {
+  content: "\f163";
+}
+.fa-thumbs-up:before {
+  content: "\f164";
+}
+.fa-thumbs-down:before {
+  content: "\f165";
+}
+.fa-youtube-square:before {
+  content: "\f166";
+}
+.fa-youtube:before {
+  content: "\f167";
+}
+.fa-xing:before {
+  content: "\f168";
+}
+.fa-xing-square:before {
+  content: "\f169";
+}
+.fa-youtube-play:before {
+  content: "\f16a";
+}
+.fa-dropbox:before {
+  content: "\f16b";
+}
+.fa-stack-overflow:before {
+  content: "\f16c";
+}
+.fa-instagram:before {
+  content: "\f16d";
+}
+.fa-flickr:before {
+  content: "\f16e";
+}
+.fa-adn:before {
+  content: "\f170";
+}
+.fa-bitbucket:before {
+  content: "\f171";
+}
+.fa-bitbucket-square:before {
+  content: "\f172";
+}
+.fa-tumblr:before {
+  content: "\f173";
+}
+.fa-tumblr-square:before {
+  content: "\f174";
+}
+.fa-long-arrow-down:before {
+  content: "\f175";
+}
+.fa-long-arrow-up:before {
+  content: "\f176";
+}
+.fa-long-arrow-left:before {
+  content: "\f177";
+}
+.fa-long-arrow-right:before {
+  content: "\f178";
+}
+.fa-apple:before {
+  content: "\f179";
+}
+.fa-windows:before {
+  content: "\f17a";
+}
+.fa-android:before {
+  content: "\f17b";
+}
+.fa-linux:before {
+  content: "\f17c";
+}
+.fa-dribbble:before {
+  content: "\f17d";
+}
+.fa-skype:before {
+  content: "\f17e";
+}
+.fa-foursquare:before {
+  content: "\f180";
+}
+.fa-trello:before {
+  content: "\f181";
+}
+.fa-female:before {
+  content: "\f182";
+}
+.fa-male:before {
+  content: "\f183";
+}
+.fa-gittip:before,
+.fa-gratipay:before {
+  content: "\f184";
+}
+.fa-sun-o:before {
+  content: "\f185";
+}
+.fa-moon-o:before {
+  content: "\f186";
+}
+.fa-archive:before {
+  content: "\f187";
+}
+.fa-bug:before {
+  content: "\f188";
+}
+.fa-vk:before {
+  content: "\f189";
+}
+.fa-weibo:before {
+  content: "\f18a";
+}
+.fa-renren:before {
+  content: "\f18b";
+}
+.fa-pagelines:before {
+  content: "\f18c";
+}
+.fa-stack-exchange:before {
+  content: "\f18d";
+}
+.fa-arrow-circle-o-right:before {
+  content: "\f18e";
+}
+.fa-arrow-circle-o-left:before {
+  content: "\f190";
+}
+.fa-toggle-left:before,
+.fa-caret-square-o-left:before {
+  content: "\f191";
+}
+.fa-dot-circle-o:before {
+  content: "\f192";
+}
+.fa-wheelchair:before {
+  content: "\f193";
+}
+.fa-vimeo-square:before {
+  content: "\f194";
+}
+.fa-turkish-lira:before,
+.fa-try:before {
+  content: "\f195";
+}
+.fa-plus-square-o:before {
+  content: "\f196";
+}
+.fa-space-shuttle:before {
+  content: "\f197";
+}
+.fa-slack:before {
+  content: "\f198";
+}
+.fa-envelope-square:before {
+  content: "\f199";
+}
+.fa-wordpress:before {
+  content: "\f19a";
+}
+.fa-openid:before {
+  content: "\f19b";
+}
+.fa-institution:before,
+.fa-bank:before,
+.fa-university:before {
+  content: "\f19c";
+}
+.fa-mortar-board:before,
+.fa-graduation-cap:before {
+  content: "\f19d";
+}
+.fa-yahoo:before {
+  content: "\f19e";
+}
+.fa-google:before {
+  content: "\f1a0";
+}
+.fa-reddit:before {
+  content: "\f1a1";
+}
+.fa-reddit-square:before {
+  content: "\f1a2";
+}
+.fa-stumbleupon-circle:before {
+  content: "\f1a3";
+}
+.fa-stumbleupon:before {
+  content: "\f1a4";
+}
+.fa-delicious:before {
+  content: "\f1a5";
+}
+.fa-digg:before {
+  content: "\f1a6";
+}
+.fa-pied-piper:before {
+  content: "\f1a7";
+}
+.fa-pied-piper-alt:before {
+  content: "\f1a8";
+}
+.fa-drupal:before {
+  content: "\f1a9";
+}
+.fa-joomla:before {
+  content: "\f1aa";
+}
+.fa-language:before {
+  content: "\f1ab";
+}
+.fa-fax:before {
+  content: "\f1ac";
+}
+.fa-building:before {
+  content: "\f1ad";
+}
+.fa-child:before {
+  content: "\f1ae";
+}
+.fa-paw:before {
+  content: "\f1b0";
+}
+.fa-spoon:before {
+  content: "\f1b1";
+}
+.fa-cube:before {
+  content: "\f1b2";
+}
+.fa-cubes:before {
+  content: "\f1b3";
+}
+.fa-behance:before {
+  content: "\f1b4";
+}
+.fa-behance-square:before {
+  content: "\f1b5";
+}
+.fa-steam:before {
+  content: "\f1b6";
+}
+.fa-steam-square:before {
+  content: "\f1b7";
+}
+.fa-recycle:before {
+  content: "\f1b8";
+}
+.fa-automobile:before,
+.fa-car:before {
+  content: "\f1b9";
+}
+.fa-cab:before,
+.fa-taxi:before {
+  content: "\f1ba";
+}
+.fa-tree:before {
+  content: "\f1bb";
+}
+.fa-spotify:before {
+  content: "\f1bc";
+}
+.fa-deviantart:before {
+  content: "\f1bd";
+}
+.fa-soundcloud:before {
+  content: "\f1be";
+}
+.fa-database:before {
+  content: "\f1c0";
+}
+.fa-file-pdf-o:before {
+  content: "\f1c1";
+}
+.fa-file-word-o:before {
+  content: "\f1c2";
+}
+.fa-file-excel-o:before {
+  content: "\f1c3";
+}
+.fa-file-powerpoint-o:before {
+  content: "\f1c4";
+}
+.fa-file-photo-o:before,
+.fa-file-picture-o:before,
+.fa-file-image-o:before {
+  content: "\f1c5";
+}
+.fa-file-zip-o:before,
+.fa-file-archive-o:before {
+  content: "\f1c6";
+}
+.fa-file-sound-o:before,
+.fa-file-audio-o:before {
+  content: "\f1c7";
+}
+.fa-file-movie-o:before,
+.fa-file-video-o:before {
+  content: "\f1c8";
+}
+.fa-file-code-o:before {
+  content: "\f1c9";
+}
+.fa-vine:before {
+  content: "\f1ca";
+}
+.fa-codepen:before {
+  content: "\f1cb";
+}
+.fa-jsfiddle:before {
+  content: "\f1cc";
+}
+.fa-life-bouy:before,
+.fa-life-buoy:before,
+.fa-life-saver:before,
+.fa-support:before,
+.fa-life-ring:before {
+  content: "\f1cd";
+}
+.fa-circle-o-notch:before {
+  content: "\f1ce";
+}
+.fa-ra:before,
+.fa-rebel:before {
+  content: "\f1d0";
+}
+.fa-ge:before,
+.fa-empire:before {
+  content: "\f1d1";
+}
+.fa-git-square:before {
+  content: "\f1d2";
+}
+.fa-git:before {
+  content: "\f1d3";
+}
+.fa-y-combinator-square:before,
+.fa-yc-square:before,
+.fa-hacker-news:before {
+  content: "\f1d4";
+}
+.fa-tencent-weibo:before {
+  content: "\f1d5";
+}
+.fa-qq:before {
+  content: "\f1d6";
+}
+.fa-wechat:before,
+.fa-weixin:before {
+  content: "\f1d7";
+}
+.fa-send:before,
+.fa-paper-plane:before {
+  content: "\f1d8";
+}
+.fa-send-o:before,
+.fa-paper-plane-o:before {
+  content: "\f1d9";
+}
+.fa-history:before {
+  content: "\f1da";
+}
+.fa-circle-thin:before {
+  content: "\f1db";
+}
+.fa-header:before {
+  content: "\f1dc";
+}
+.fa-paragraph:before {
+  content: "\f1dd";
+}
+.fa-sliders:before {
+  content: "\f1de";
+}
+.fa-share-alt:before {
+  content: "\f1e0";
+}
+.fa-share-alt-square:before {
+  content: "\f1e1";
+}
+.fa-bomb:before {
+  content: "\f1e2";
+}
+.fa-soccer-ball-o:before,
+.fa-futbol-o:before {
+  content: "\f1e3";
+}
+.fa-tty:before {
+  content: "\f1e4";
+}
+.fa-binoculars:before {
+  content: "\f1e5";
+}
+.fa-plug:before {
+  content: "\f1e6";
+}
+.fa-slideshare:before {
+  content: "\f1e7";
+}
+.fa-twitch:before {
+  content: "\f1e8";
+}
+.fa-yelp:before {
+  content: "\f1e9";
+}
+.fa-newspaper-o:before {
+  content: "\f1ea";
+}
+.fa-wifi:before {
+  content: "\f1eb";
+}
+.fa-calculator:before {
+  content: "\f1ec";
+}
+.fa-paypal:before {
+  content: "\f1ed";
+}
+.fa-google-wallet:before {
+  content: "\f1ee";
+}
+.fa-cc-visa:before {
+  content: "\f1f0";
+}
+.fa-cc-mastercard:before {
+  content: "\f1f1";
+}
+.fa-cc-discover:before {
+  content: "\f1f2";
+}
+.fa-cc-amex:before {
+  content: "\f1f3";
+}
+.fa-cc-paypal:before {
+  content: "\f1f4";
+}
+.fa-cc-stripe:before {
+  content: "\f1f5";
+}
+.fa-bell-slash:before {
+  content: "\f1f6";
+}
+.fa-bell-slash-o:before {
+  content: "\f1f7";
+}
+.fa-trash:before {
+  content: "\f1f8";
+}
+.fa-copyright:before {
+  content: "\f1f9";
+}
+.fa-at:before {
+  content: "\f1fa";
+}
+.fa-eyedropper:before {
+  content: "\f1fb";
+}
+.fa-paint-brush:before {
+  content: "\f1fc";
+}
+.fa-birthday-cake:before {
+  content: "\f1fd";
+}
+.fa-area-chart:before {
+  content: "\f1fe";
+}
+.fa-pie-chart:before {
+  content: "\f200";
+}
+.fa-line-chart:before {
+  content: "\f201";
+}
+.fa-lastfm:before {
+  content: "\f202";
+}
+.fa-lastfm-square:before {
+  content: "\f203";
+}
+.fa-toggle-off:before {
+  content: "\f204";
+}
+.fa-toggle-on:before {
+  content: "\f205";
+}
+.fa-bicycle:before {
+  content: "\f206";
+}
+.fa-bus:before {
+  content: "\f207";
+}
+.fa-ioxhost:before {
+  content: "\f208";
+}
+.fa-angellist:before {
+  content: "\f209";
+}
+.fa-cc:before {
+  content: "\f20a";
+}
+.fa-shekel:before,
+.fa-sheqel:before,
+.fa-ils:before {
+  content: "\f20b";
+}
+.fa-meanpath:before {
+  content: "\f20c";
+}
+.fa-buysellads:before {
+  content: "\f20d";
+}
+.fa-connectdevelop:before {
+  content: "\f20e";
+}
+.fa-dashcube:before {
+  content: "\f210";
+}
+.fa-forumbee:before {
+  content: "\f211";
+}
+.fa-leanpub:before {
+  content: "\f212";
+}
+.fa-sellsy:before {
+  content: "\f213";
+}
+.fa-shirtsinbulk:before {
+  content: "\f214";
+}
+.fa-simplybuilt:before {
+  content: "\f215";
+}
+.fa-skyatlas:before {
+  content: "\f216";
+}
+.fa-cart-plus:before {
+  content: "\f217";
+}
+.fa-cart-arrow-down:before {
+  content: "\f218";
+}
+.fa-diamond:before {
+  content: "\f219";
+}
+.fa-ship:before {
+  content: "\f21a";
+}
+.fa-user-secret:before {
+  content: "\f21b";
+}
+.fa-motorcycle:before {
+  content: "\f21c";
+}
+.fa-street-view:before {
+  content: "\f21d";
+}
+.fa-heartbeat:before {
+  content: "\f21e";
+}
+.fa-venus:before {
+  content: "\f221";
+}
+.fa-mars:before {
+  content: "\f222";
+}
+.fa-mercury:before {
+  content: "\f223";
+}
+.fa-intersex:before,
+.fa-transgender:before {
+  content: "\f224";
+}
+.fa-transgender-alt:before {
+  content: "\f225";
+}
+.fa-venus-double:before {
+  content: "\f226";
+}
+.fa-mars-double:before {
+  content: "\f227";
+}
+.fa-venus-mars:before {
+  content: "\f228";
+}
+.fa-mars-stroke:before {
+  content: "\f229";
+}
+.fa-mars-stroke-v:before {
+  content: "\f22a";
+}
+.fa-mars-stroke-h:before {
+  content: "\f22b";
+}
+.fa-neuter:before {
+  content: "\f22c";
+}
+.fa-genderless:before {
+  content: "\f22d";
+}
+.fa-facebook-official:before {
+  content: "\f230";
+}
+.fa-pinterest-p:before {
+  content: "\f231";
+}
+.fa-whatsapp:before {
+  content: "\f232";
+}
+.fa-server:before {
+  content: "\f233";
+}
+.fa-user-plus:before {
+  content: "\f234";
+}
+.fa-user-times:before {
+  content: "\f235";
+}
+.fa-hotel:before,
+.fa-bed:before {
+  content: "\f236";
+}
+.fa-viacoin:before {
+  content: "\f237";
+}
+.fa-train:before {
+  content: "\f238";
+}
+.fa-subway:before {
+  content: "\f239";
+}
+.fa-medium:before {
+  content: "\f23a";
+}
+.fa-yc:before,
+.fa-y-combinator:before {
+  content: "\f23b";
+}
+.fa-optin-monster:before {
+  content: "\f23c";
+}
+.fa-opencart:before {
+  content: "\f23d";
+}
+.fa-expeditedssl:before {
+  content: "\f23e";
+}
+.fa-battery-4:before,
+.fa-battery-full:before {
+  content: "\f240";
+}
+.fa-battery-3:before,
+.fa-battery-three-quarters:before {
+  content: "\f241";
+}
+.fa-battery-2:before,
+.fa-battery-half:before {
+  content: "\f242";
+}
+.fa-battery-1:before,
+.fa-battery-quarter:before {
+  content: "\f243";
+}
+.fa-battery-0:before,
+.fa-battery-empty:before {
+  content: "\f244";
+}
+.fa-mouse-pointer:before {
+  content: "\f245";
+}
+.fa-i-cursor:before {
+  content: "\f246";
+}
+.fa-object-group:before {
+  content: "\f247";
+}
+.fa-object-ungroup:before {
+  content: "\f248";
+}
+.fa-sticky-note:before {
+  content: "\f249";
+}
+.fa-sticky-note-o:before {
+  content: "\f24a";
+}
+.fa-cc-jcb:before {
+  content: "\f24b";
+}
+.fa-cc-diners-club:before {
+  content: "\f24c";
+}
+.fa-clone:before {
+  content: "\f24d";
+}
+.fa-balance-scale:before {
+  content: "\f24e";
+}
+.fa-hourglass-o:before {
+  content: "\f250";
+}
+.fa-hourglass-1:before,
+.fa-hourglass-start:before {
+  content: "\f251";
+}
+.fa-hourglass-2:before,
+.fa-hourglass-half:before {
+  content: "\f252";
+}
+.fa-hourglass-3:before,
+.fa-hourglass-end:before {
+  content: "\f253";
+}
+.fa-hourglass:before {
+  content: "\f254";
+}
+.fa-hand-grab-o:before,
+.fa-hand-rock-o:before {
+  content: "\f255";
+}
+.fa-hand-stop-o:before,
+.fa-hand-paper-o:before {
+  content: "\f256";
+}
+.fa-hand-scissors-o:before {
+  content: "\f257";
+}
+.fa-hand-lizard-o:before {
+  content: "\f258";
+}
+.fa-hand-spock-o:before {
+  content: "\f259";
+}
+.fa-hand-pointer-o:before {
+  content: "\f25a";
+}
+.fa-hand-peace-o:before {
+  content: "\f25b";
+}
+.fa-trademark:before {
+  content: "\f25c";
+}
+.fa-registered:before {
+  content: "\f25d";
+}
+.fa-creative-commons:before {
+  content: "\f25e";
+}
+.fa-gg:before {
+  content: "\f260";
+}
+.fa-gg-circle:before {
+  content: "\f261";
+}
+.fa-tripadvisor:before {
+  content: "\f262";
+}
+.fa-odnoklassniki:before {
+  content: "\f263";
+}
+.fa-odnoklassniki-square:before {
+  content: "\f264";
+}
+.fa-get-pocket:before {
+  content: "\f265";
+}
+.fa-wikipedia-w:before {
+  content: "\f266";
+}
+.fa-safari:before {
+  content: "\f267";
+}
+.fa-chrome:before {
+  content: "\f268";
+}
+.fa-firefox:before {
+  content: "\f269";
+}
+.fa-opera:before {
+  content: "\f26a";
+}
+.fa-internet-explorer:before {
+  content: "\f26b";
+}
+.fa-tv:before,
+.fa-television:before {
+  content: "\f26c";
+}
+.fa-contao:before {
+  content: "\f26d";
+}
+.fa-500px:before {
+  content: "\f26e";
+}
+.fa-amazon:before {
+  content: "\f270";
+}
+.fa-calendar-plus-o:before {
+  content: "\f271";
+}
+.fa-calendar-minus-o:before {
+  content: "\f272";
+}
+.fa-calendar-times-o:before {
+  content: "\f273";
+}
+.fa-calendar-check-o:before {
+  content: "\f274";
+}
+.fa-industry:before {
+  content: "\f275";
+}
+.fa-map-pin:before {
+  content: "\f276";
+}
+.fa-map-signs:before {
+  content: "\f277";
+}
+.fa-map-o:before {
+  content: "\f278";
+}
+.fa-map:before {
+  content: "\f279";
+}
+.fa-commenting:before {
+  content: "\f27a";
+}
+.fa-commenting-o:before {
+  content: "\f27b";
+}
+.fa-houzz:before {
+  content: "\f27c";
+}
+.fa-vimeo:before {
+  content: "\f27d";
+}
+.fa-black-tie:before {
+  content: "\f27e";
+}
+.fa-fonticons:before {
+  content: "\f280";
+}
+.fa-reddit-alien:before {
+  content: "\f281";
+}
+.fa-edge:before {
+  content: "\f282";
+}
+.fa-credit-card-alt:before {
+  content: "\f283";
+}
+.fa-codiepie:before {
+  content: "\f284";
+}
+.fa-modx:before {
+  content: "\f285";
+}
+.fa-fort-awesome:before {
+  content: "\f286";
+}
+.fa-usb:before {
+  content: "\f287";
+}
+.fa-product-hunt:before {
+  content: "\f288";
+}
+.fa-mixcloud:before {
+  content: "\f289";
+}
+.fa-scribd:before {
+  content: "\f28a";
+}
+.fa-pause-circle:before {
+  content: "\f28b";
+}
+.fa-pause-circle-o:before {
+  content: "\f28c";
+}
+.fa-stop-circle:before {
+  content: "\f28d";
+}
+.fa-stop-circle-o:before {
+  content: "\f28e";
+}
+.fa-shopping-bag:before {
+  content: "\f290";
+}
+.fa-shopping-basket:before {
+  content: "\f291";
+}
+.fa-hashtag:before {
+  content: "\f292";
+}
+.fa-bluetooth:before {
+  content: "\f293";
+}
+.fa-bluetooth-b:before {
+  content: "\f294";
+}
+.fa-percent:before {
+  content: "\f295";
+}

File diff suppressed because it is too large
+ 1 - 0
dashboard/assets/stylesheets/jquery.gridster.min.css


+ 28 - 0
dashboard/config.ru

@@ -0,0 +1,28 @@
+require 'dashing'
+require 'sinatra/cross_origin'
+
+configure do
+ # set :auth_token, 'YOUR_EclecticLabs_AUTH_TOKEN'
+
+  enable :cross_origin
+  set :allow_origin, :any
+  set :allow_methods, [:get, :post, :options]
+  set :allow_credentials, true
+  set :max_age, "1728000"
+  set :expose_headers, ['Content-Type']
+  set :protection, :except => :frame_options
+
+
+  helpers do
+    def protected!
+      # Put any authentication code you want in here.
+      # This method is run before accessing any resource.
+    end
+  end
+end
+
+map Sinatra::Application.assets_prefix do
+  run Sinatra::Application.sprockets
+end
+
+run Sinatra::Application

+ 24 - 0
dashboard/dashboards/layout.erb

@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8"/>
+  <meta name="description" content="">
+  <meta name="viewport" content="width=device-width">
+  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+
+  <title>EclecticLabs | Trafficker </title>
+
+  <script type="text/javascript" src="/assets/application.js"></script>
+  <link rel="stylesheet" href="/assets/application.css">
+
+  <link href='//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700' rel='stylesheet' type='text/css'>
+  <link rel="icon" href="/assets/favicon.ico">
+
+</head>
+  <body>
+    <div id="container">
+      <%= yield %>
+    </div>
+
+  </body>
+</html>

+ 61 - 0
dashboard/dashboards/main.erb

@@ -0,0 +1,61 @@
+<% content_for :title do %> EclecticLabs | Trafficker <% end %>
+
+<div class="gridster">
+  <ul>
+
+
+    <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
+      <div data-id="clients" data-view="List" data-unordered="true" data-title="Clients" data-moreinfo="# of active users"></div>
+    </li>
+
+    <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
+      <div data-id="connections" data-view="Number" data-title="Queries" data-moreinfo="# of API calls" data-prefix="+"></div>
+    </li>
+
+    <li data-row="1" data-col="1" data-sizex="2" data-sizey="1">
+      <div data-id="traffic" data-view="Graph" data-title="Traffic" style="background-color:#ff9618"></div>
+    </li>
+
+
+    <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
+      <div data-id="load" data-view="Meter" data-title="Memory" data-height="200" data-width="200" data-min="0" data-max="100" data-suffix="%"></div>
+    </li>
+
+
+
+    <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
+      <div data-view="Gauge" data-metric="stats.gauges.kong.Blog.latency"
+           data-title="Latency" data-graphite_host="http://qaapi.eclecticLabs.io:8081" data-unit="ms" data-colors="0:#96bf48,100:#ff9618,500:#d26771"></div>
+    </li>
+
+    <li data-row="1" data-col="1" data-sizex="2" data-sizey="1">
+      <div data-view="Timer" data-metric="stats.gauges.kong.ElasticSearch.request.size"
+           data-title="Requests" data-graphite_host="http://qaapi.eclecticLabs.io:8081"
+           data-unit="Kb"></div>
+    </li>
+
+
+    <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
+      <div data-id="weather" data-view="Weather"></div>
+    </li>
+
+    <li data-row="1" data-col="1" data-sizex="2" data-sizey="1">
+      <div data-id="mta" data-view="Mta"></div>
+    </li>
+
+    <li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
+      <div data-id="news" data-view="News"></div>
+    </li>
+
+    <% if ENV['GRAPHITE_ROCKS'] %>
+
+    <li data-row="1" data-col="1" data-sizex="4" data-sizey="1">
+      <div data-view="Graphite" data-image="http://qaapi.eclecticLabs.io:8081/render?from=-2hours&amp;width=500&amp;height=400&amp;target=stats.gauges.kong.Blog.latency&amp;_uniq=0.11581272282637656" data-interval="30000"></div>
+    </li>
+
+
+     <% end %>
+
+
+  </ul>
+</div>

+ 68 - 0
dashboard/jobs/daily_xkcd.rb

@@ -0,0 +1,68 @@
+require 'net/http'
+require 'json'
+require 'date'
+
+XKCD_URI = 'http://xkcd.com'
+DATE_FORMAT_STR = '%B %-d, %Y'
+
+$displayed_xkcd = nil
+$prev_displayed_xkcd = nil
+
+# Get's the nth xkcd entry, unless
+# nil is passed, in which case current
+def get_nth_xkcd(n)
+   uri = URI.join(XKCD_URI, n.to_s + '/', 'info.0.json')
+   response = Net::HTTP.get(uri)
+   JSON.parse(response)
+end
+
+# Get's the current, featured xkcd entry
+def get_current_xkcd
+    get_nth_xkcd(nil)
+end
+
+# Get's a random xkcd
+def get_random_xkcd
+	curr_id = get_current_xkcd['num']
+	random_id = nil
+
+	# 404 is reserved for Not found
+	while true do
+		random_id = rand(curr_id)
+		break if random_id != 404 
+	end	
+	get_nth_xkcd(random_id)
+end
+
+# Check if provided xkcd was published yesterday
+def published_yesterday_and_unseen(xkcd_date)
+	xkcd_date == Date.today.prev_day and ($prev_displayed_xkcd.nil? or not $prev_displayed_xkcd['num'] == xkcd['num'])
+end
+
+# Basic logic:
+#  - if an xkcd was published today, display it.
+#  - if an xkcd was published yesterday, and we didn't
+# 	 show it yesterday, display it. 
+#  - otherwise, display a random xkcd.
+SCHEDULER.every '1d', :first_in => 0 do |job|
+	$prev_displayed_xkcd = $displayed_xkcd
+
+	xkcd = get_current_xkcd
+	xkcd_date = Date.new(
+		xkcd['year'].to_i,
+		xkcd['month'].to_i,
+		xkcd['day'].to_i
+	)
+	if xkcd_date == Date.today or published_yesterday_and_unseen(xkcd_date)
+		$displayed_xkcd = xkcd
+	else
+		$displayed_xkcd = get_random_xkcd
+		xkcd_date = Date.new(
+			$displayed_xkcd['year'].to_i,
+			$displayed_xkcd['month'].to_i,
+			$displayed_xkcd['day'].to_i
+		)
+	end
+	$displayed_xkcd['datestr'] = xkcd_date.strftime(DATE_FORMAT_STR)
+	send_event('xkcd-of-the-day', $displayed_xkcd)
+end

+ 107 - 0
dashboard/jobs/jira_list_current_sprint_issues.rb

@@ -0,0 +1,107 @@
+=begin
+
+require 'jira'
+require 'net/http'
+require 'json'
+
+# Settings to configure:
+# PROJECT: the project path/name
+# RAPID_VIEW_ID: id for the rapid view board
+# JIRA_CONFIG: credentials to access JIRA
+# ISSUE_LISTS: a widget per entry for different statuses (see JIRA_STATUSES) 
+PROJECT = "EclecticLabs"
+RAPID_VIEW_ID = 0
+JIRA_CONFIG = {
+  :username     => 'trafficker',
+  :password     => 'password',
+  :site         => "https://eclecticLabs.atlassian.net",
+  :auth_type    => :basic,
+  :context_path => ''
+}
+
+ISSUE_LISTS = [
+  {:widget_id => 'jira_open_issues', :status_id => 1},   # Lists all open issues
+  {:widget_id => 'jira_in_prog_issues', :status_id => 3} # Lists all issues in progress
+]
+
+# Constants (do not change)
+JIRA_URI = URI.parse(JIRA_CONFIG[:site])
+JIRA_ANON_AVATAR_ID = 10123
+JIRA_STATUSES = {
+  1 => "Open",
+  3 => "In Progress",
+  4 => "Reopened",
+  5 => "Resolved",
+  6 => "Closed"
+}
+
+# gets the view for a given view id
+def get_view_for_viewid(view_id)
+  http = create_http
+  request = create_request("/rest/greenhopper/1.0/rapidviews/list")
+  response = http.request(request)
+  views = JSON.parse(response.body)['views']
+  views.each do |view|
+    if view['id'] == view_id
+      return view
+    end
+  end
+end
+
+# gets the active sprint for the view
+def get_active_sprint_for_view(view_id)
+  http = create_http
+  request = create_request("/rest/greenhopper/1.0/sprintquery/#{view_id}")
+  response = http.request(request)
+  sprints = JSON.parse(response.body)['sprints']
+  sprints.each do |sprint|
+    if sprint['state'] == 'ACTIVE'
+      return sprint
+    end
+  end
+end
+
+# create HTTP
+def create_http
+  http = Net::HTTP.new(JIRA_URI.host, JIRA_URI.port)
+  if ('https' == JIRA_URI.scheme)
+    http.use_ssl     = true
+    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+  end
+  return http
+end
+
+# create HTTP request for given path
+def create_request(path)
+  request = Net::HTTP::Get.new(JIRA_URI.path + path)
+  if JIRA_CONFIG[:username]
+    request.basic_auth(JIRA_CONFIG[:username], JIRA_CONFIG[:password])
+  end
+  return request
+end
+
+ISSUE_LISTS.each do |list_config| 
+  SCHEDULER.every '5m', :first_in => 0 do |job|    
+    issues = []
+    status_id = list_config[:status_id]
+    client = JIRA::Client.new(JIRA_CONFIG)
+    client.Issue.jql("PROJECT = \"#{PROJECT}\" AND STATUS = \"#{status_id}\" AND SPRINT in openSprints()").each { |issue|
+        assigneeAvatarUrl = issue.assignee.nil? ? URI.join(JIRA_URI.to_s, "secure/useravatar?avatarId=#{JIRA_ANON_AVATAR_ID}") : issue.assignee.avatarUrls["48x48"]
+        assigneeName = issue.assignee.nil? ? "unassigned" : issue.assignee.name
+
+        issues.push({
+         id: issue.key,
+         title: issue.summary,
+         assigneeName: assigneeName,
+         assigneeAvatarUrl: assigneeAvatarUrl
+        })
+    }
+
+    issue_type = JIRA_STATUSES[status_id]
+    active_sprint = get_active_sprint_for_view(RAPID_VIEW_ID)
+    sprint_name = active_sprint['name']
+    send_event(list_config[:widget_id], { header: "#{sprint_name} Issues", issue_type: issue_type, issues: issues})
+  end
+end
+
+=end

+ 27 - 0
dashboard/jobs/main.rb

@@ -0,0 +1,27 @@
+current_connections = 0
+current_load = 0
+points = []
+(1..10).each do |i|
+  points << { x: i, y: rand(50) }
+end
+
+clients = ['NYPD', 'BPD', 'FBI', 'NSA', 'CIA']
+client_counts = Hash.new({ value: 0 })
+last_x = points.last[:x]
+
+SCHEDULER.every '5s' do
+  random_client = clients.sample
+  client_counts[random_client] = { label: random_client, value: (client_counts[random_client][:value] + 1) % 30 }
+  last_connections = current_connections
+  last_load  = current_load
+  current_connections = rand(100)
+  current_load    = rand(200000)
+  points.shift
+  last_x += 1
+  points << { x: last_x, y: rand(50) }
+  send_event('connections', { current: current_connections, last: last_connections })
+  send_event('traffic', { current: current_load, last: last_load })
+  send_event('load',   { value: rand(100) })
+  send_event('traffic', points: points)
+  send_event('clients', { items: client_counts.values })
+end

+ 34 - 0
dashboard/jobs/mta.rb

@@ -0,0 +1,34 @@
+require 'nokogiri'
+require 'httparty'
+
+MTA_URL = 'http://www.mta.info/status/serviceStatus.txt'
+LINES = %W[123 456 7 ACE L S BDFM NQR JZ G]
+
+def status_as_class description
+  case description.downcase
+    when 'good service' then 'online'
+    when 'delays' then 'delays'
+    when 'service change' then 'delays'
+    else 'offline'
+  end
+end
+
+def line_status line, in_lines, feed
+  line_status = feed
+                    .css("line:contains(#{in_lines})")
+                    .find {|line| line.css('name').text.chomp == in_lines}
+  status = line_status.css('status').text.chomp
+  {name: line, status: status_as_class(status)}
+end
+
+SCHEDULER.every '5m', first_in: 0 do
+  feed = Nokogiri::XML.parse HTTParty.get(MTA_URL)
+
+  line_statuses = LINES.collect do |lines|
+    lines.split(//).map do |line|
+      line_status(line, lines, feed)
+    end
+  end.flatten
+
+  send_event('mta', {items: line_statuses})
+end

+ 34 - 0
dashboard/jobs/news.rb

@@ -0,0 +1,34 @@
+require 'nokogiri'
+require 'net/http'
+class BbcNews
+  def initialize()
+    @http = Net::HTTP.new('feeds.bbci.co.uk')
+  end
+
+  def latest_headlines()
+    response = @http.request(Net::HTTP::Get.new("http://feeds.bbci.co.uk/news/world/us_and_canada/rss.xml"))
+    doc = Nokogiri::XML(response.body)
+    news_headlines = [];
+    doc.xpath('//channel/item').each do |news_item|
+      news_headline = NewsHeadlineBuilder.BuildFrom(news_item)
+      news_headlines.push(news_headline)
+    end
+    news_headlines
+  end
+end
+
+class NewsHeadlineBuilder
+  def self.BuildFrom(news_item)
+    {
+        title: news_item.xpath('title').text,
+        description: news_item.xpath('description').text,
+    }
+  end
+end
+
+@BBC_News = BbcNews.new()
+
+SCHEDULER.every '15m', :first_in => 0 do |job|
+  headlines = @BBC_News.latest_headlines
+  send_event('news', { :headlines => headlines})
+end

+ 48 - 0
dashboard/jobs/slack_presence.rb

@@ -0,0 +1,48 @@
+require 'slack'
+
+options = {
+    :api_token => ''
+}
+
+slack_client = Slack::Client.new token: options[:api_token]
+
+status_map = {
+    'active' => 'Active',
+    'away' => 'Away',
+    'dnd' => 'Do Not Disturb',
+    '' => 'Offline'
+}
+
+SCHEDULER.every '30s' do
+
+
+  users_response = slack_client.users_list(presence: 1)
+  dnd_response = slack_client.dnd_teamInfo
+
+  slack_members = users_response["members"]
+  slack_dnd = dnd_response["users"]
+
+  slack_members.each do |u|
+
+    target = "slack-presence-#{u['name']}"
+
+    data = {
+        :fullname => u['real_name'],
+        :img => u['profile']['image_72'],
+        :presence_class => u['presence']
+    }
+
+    if slack_dnd[u['id']] && slack_dnd[u['id']]['dnd_enabled']
+      now = Time.now.to_i
+      dnd_data = slack_dnd[u['id']]
+      if now >= dnd_data['next_dnd_start_ts'] && now <= dnd_data['next_dnd_end_ts']
+        data[:presence_class] = 'dnd'
+      end
+    end
+
+    data[:presence_name] = status_map[data[:presence_class]]
+
+    send_event(target, data)
+  end
+
+end

+ 68 - 0
dashboard/jobs/weather.rb

@@ -0,0 +1,68 @@
+require 'net/http'
+
+# you can find CITY_ID here http://bulk.openweathermap.org/sample/city.list.json.gz
+CITY_ID = 5128581
+
+# options: metric / imperial
+UNITS   = 'imperial'
+
+# create free account on open weather map to get API key
+API_KEY = ''
+
+SCHEDULER.every '20s', :first_in => 0 do |job|
+
+  http = Net::HTTP.new('api.openweathermap.org')
+  response = http.request(Net::HTTP::Get.new("/data/2.5/weather?id=#{CITY_ID}&units=#{UNITS}&appid=#{API_KEY}"))
+
+  next unless '200'.eql? response.code
+
+  weather_data  = JSON.parse(response.body)
+  detailed_info = weather_data['weather'].first
+  current_temp  = weather_data['main']['temp'].to_f.round
+
+  send_event('weather', { :temp => "#{current_temp} &deg;#{temperature_units}",
+                          :condition => detailed_info['main'],
+                          :title => "#{weather_data['name']} Weather",
+                          :color => color_temperature(current_temp),
+                          :climacon => climacon_class(detailed_info['id'])})
+end
+
+
+def temperature_units
+  'metric'.eql?(UNITS) ? 'C' : 'K'
+end
+
+def color_temperature(temp_celsius)
+  case temp_celsius.to_i
+    when 30..100
+      '#FF3300'
+    when 25..29
+      '#FF6000'
+    when 19..24
+      '#FF9D00'
+    when 5..18
+      '#18A9FF'
+    else
+      '#0065FF'
+  end
+end
+
+# fun times ;) legend: http://openweathermap.org/weather-conditions
+def climacon_class(weather_code)
+  case weather_code.to_s
+    when /800/
+      'sun'
+    when /80./
+      'cloud'
+    when /2.*/
+      'lightning'
+    when /3.*/
+      'drizzle'
+    when /5.*/
+      'rain'
+    when /6.*/
+      'snow'
+    else
+      'sun'
+  end
+end

+ 48 - 0
dashboard/lib/graphite.rb

@@ -0,0 +1,48 @@
+require "rest-client"
+require "json"
+require "date"
+
+class Graphite
+  # Pass in the url of graphite server
+  def initialize(url="http://stats:3030")
+    @url = url
+  end
+
+  def get_value(datapoint)
+    value = datapoint[0] || 0
+    return value.round(2)
+  end
+
+
+  def query(name, since=nil)
+    since ||= '-2min'
+    url = "#{@url}/render?format=json&target=#{name}&from=#{since}"
+    response = RestClient.get url
+    result = JSON.parse(response.body, :symbolize_names => true)
+    return result.first
+  end
+
+
+  def points(name, since=nil)
+    stats = query name, since
+    datapoints = stats[:datapoints]
+
+    points = []
+    count = 1
+
+    (datapoints.select { |el| not el[0].nil? }).each do|item|
+      points << { x: count, y: get_value(item)}
+      count += 1
+    end
+
+    return points
+  end
+
+
+  def value(name, since=nil)
+    stats = query name, since
+    last = (stats[:datapoints].select { |el| not el[0].nil? }).last
+
+    return get_value(last)
+  end
+end

+ 26 - 0
dashboard/public/404.html

@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>EclecticLabs | Trafficker</title>
+  <style>
+    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
+    div.dialog {
+      width: 25em;
+      padding: 0 4em;
+      margin: 4em auto 0 auto;
+      border: 1px solid #ccc;
+      border-right-color: #999;
+      border-bottom-color: #999;
+    }
+    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
+  </style>
+</head>
+
+<body>
+  <!-- This file lives in public/404.html -->
+  <div class="dialog">
+    <h1>LOL WUT?</h1>
+    <p>You probably mistyped the address or the page moved or something...</p>
+  </div>
+</body>
+</html>

BIN
dashboard/public/favicon.ico


+ 18 - 0
dashboard/widgets/clock/clock.coffee

@@ -0,0 +1,18 @@
+class Dashing.Clock extends Dashing.Widget
+
+  ready: ->
+    setInterval(@startTime, 500)
+
+  startTime: =>
+    today = new Date()
+
+    h = today.getHours()
+    m = today.getMinutes()
+    s = today.getSeconds()
+    m = @formatTime(m)
+    s = @formatTime(s)
+    @set('time', h + ":" + m + ":" + s)
+    @set('date', today.toDateString())
+
+  formatTime: (i) ->
+    if i < 10 then "0" + i else i

+ 2 - 0
dashboard/widgets/clock/clock.html

@@ -0,0 +1,2 @@
+<h1 data-bind="date"></h1>
+<h2 data-bind="time"></h2>

+ 13 - 0
dashboard/widgets/clock/clock.scss

@@ -0,0 +1,13 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color:  #dc5945;
+
+// ----------------------------------------------------------------------------
+// Widget-clock styles
+// ----------------------------------------------------------------------------
+.widget-clock {
+
+  background-color: $background-color;
+
+}

+ 24 - 0
dashboard/widgets/comments/comments.coffee

@@ -0,0 +1,24 @@
+class Dashing.Comments extends Dashing.Widget
+
+  @accessor 'quote', ->
+    "“#{@get('current_comment')?.body}”"
+
+  ready: ->
+    @currentIndex = 0
+    @commentElem = $(@node).find('.comment-container')
+    @nextComment()
+    @startCarousel()
+
+  onData: (data) ->
+    @currentIndex = 0
+
+  startCarousel: ->
+    setInterval(@nextComment, 8000)
+
+  nextComment: =>
+    comments = @get('comments')
+    if comments
+      @commentElem.fadeOut =>
+        @currentIndex = (@currentIndex + 1) % comments.length
+        @set 'current_comment', comments[@currentIndex]
+        @commentElem.fadeIn()

+ 7 - 0
dashboard/widgets/comments/comments.html

@@ -0,0 +1,7 @@
+<h1 class="title" data-bind="title"></h1>
+<div class="comment-container">
+  <h3><img data-bind-src='current_comment.avatar'/><span data-bind='current_comment.name' class="name"></span></h3>
+  <p class="comment" data-bind='quote'></p>
+</div>
+
+<p class="more-info" data-bind="moreinfo"></p>

+ 33 - 0
dashboard/widgets/comments/comments.scss

@@ -0,0 +1,33 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color:  #eb9c3c;
+
+$title-color:       rgba(255, 255, 255, 0.7);
+$moreinfo-color:    rgba(255, 255, 255, 0.7);
+
+// ----------------------------------------------------------------------------
+// Widget-comment styles
+// ----------------------------------------------------------------------------
+.widget-comments {
+
+  background-color: $background-color;
+
+  .title {
+    color: $title-color;
+    margin-bottom: 15px;
+  }
+
+  .name {
+    padding-left: 5px;
+  }
+
+  .comment-container {
+    display: none;
+  }
+
+  .more-info {
+    color: $moreinfo-color;
+  }
+
+}

+ 9 - 0
dashboard/widgets/daily_xkcd/daily_xkcd.coffee

@@ -0,0 +1,9 @@
+class Dashing.DailyXKCD extends Dashing.Widget
+
+ ready: ->
+   # This is fired when the widget is done being rendered
+
+ onData: (data) ->
+   # Handle incoming data
+   # You can access the html node of this widget with `@node`
+   # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in.

+ 7 - 0
dashboard/widgets/daily_xkcd/daily_xkcd.html

@@ -0,0 +1,7 @@
+<h1 class="header" data-bind="'xkcd #' | append num | append ': ' | append title"></h1>
+
+<img class="xkcd-image" data-bind-src="img"/>
+<p class="date" data-bind="'Published: ' | append datestr"></p>
+<p class="caption" data-bind="alt"></p>
+
+<p class="updated-at" data-bind="updatedAtMessage"></p>

+ 60 - 0
dashboard/widgets/daily_xkcd/daily_xkcd.scss

@@ -0,0 +1,60 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color:  white;
+
+$header-color:     rgba(0, 0, 0, 0.7);
+
+$title-color:      rgba(0, 0, 0, 0.7);
+
+$caption-color:    rgba(0, 0, 0, 0.7);
+
+$published-color:  rgba(0, 0, 0, 0.7);
+
+$updated-at-color: rgba(0, 0, 0, 0.3);
+
+// ----------------------------------------------------------------------------
+// Widget-daily-xkcd styles
+// ----------------------------------------------------------------------------
+.widget-daily-xkcd {
+  background-color: $background-color;
+  vertical-align: top !important;
+  padding: 8px 12px !important;
+
+  .wrapper {
+    max-height: 100%;
+  }
+
+  .header {
+    color: $header-color;
+
+    font-size: large;
+    font-variant: small-caps;
+  }
+
+  .xkcd-image {
+    
+  }
+
+  .date {
+    color: $published-color;
+    margin-bottom: 0.5vh;
+    font-size: small;
+    text-align: left;
+  }
+
+  .caption {
+    color: $caption-color;
+    font-size: small;
+    text-align: justify;
+    margin-bottom: 0.5vh;
+  }
+
+  .updated-at {
+    color: $updated-at-color;
+    position: relative;
+    padding: 0;
+    margin: 0;
+    bottom:0;
+  }
+}

+ 17 - 0
dashboard/widgets/fullscreen/fullscreen.coffee

@@ -0,0 +1,17 @@
+class Dashing.Fullscreen extends Dashing.Widget
+
+  ready: ->
+    $(document).keypress =>
+      if event.charCode is 102
+        @requestFullscreen()
+
+  requestFullscreen: ->
+    elem = $('body')[0]
+    if elem.requestFullscreen
+      elem.requestFullscreen()
+    else if elem.msRequestFullscreen
+      elem.msRequestFullscreen()
+    else if elem.mozRequestFullScreen
+      elem.mozRequestFullScreen()
+    else if elem.webkitRequestFullscreen
+      elem.webkitRequestFullscreen()

+ 1 - 0
dashboard/widgets/fullscreen/fullscreen.html

@@ -0,0 +1 @@
+<sup></sup>

+ 3 - 0
dashboard/widgets/fullscreen/fullscreen.scss

@@ -0,0 +1,3 @@
+.widget-fullscreen {
+  display: none !important;
+}

+ 107 - 0
dashboard/widgets/gauge/coffeelint.json

@@ -0,0 +1,107 @@
+{
+    "arrow_spacing": {
+        "level": "ignore"
+    },
+    "camel_case_classes": {
+        "level": "error"
+    },
+    "coffeescript_error": {
+        "level": "error"
+    },
+    "colon_assignment_spacing": {
+        "level": "ignore",
+        "spacing": {
+            "left": 0,
+            "right": 0
+        }
+    },
+    "cyclomatic_complexity": {
+        "value": 10,
+        "level": "ignore"
+    },
+    "duplicate_key": {
+        "level": "error"
+    },
+    "empty_constructor_needs_parens": {
+        "level": "ignore"
+    },
+    "indentation": {
+        "value": 2,
+        "level": "error"
+    },
+    "line_endings": {
+        "level": "ignore",
+        "value": "unix"
+    },
+    "max_line_length": {
+        "value": 120,
+        "level": "warn",
+        "limitComments": true
+    },
+    "missing_fat_arrows": {
+        "level": "ignore"
+    },
+    "newlines_after_classes": {
+        "value": 3,
+        "level": "ignore"
+    },
+    "no_backticks": {
+        "level": "error"
+    },
+    "no_debugger": {
+        "level": "warn"
+    },
+    "no_empty_functions": {
+        "level": "ignore"
+    },
+    "no_empty_param_list": {
+        "level": "ignore"
+    },
+    "no_implicit_braces": {
+        "level": "ignore",
+        "strict": true
+    },
+    "no_implicit_parens": {
+        "strict": true,
+        "level": "ignore"
+    },
+    "no_interpolation_in_single_quotes": {
+        "level": "ignore"
+    },
+    "no_plusplus": {
+        "level": "ignore"
+    },
+    "no_stand_alone_at": {
+        "level": "ignore"
+    },
+    "no_tabs": {
+        "level": "error"
+    },
+    "no_throwing_strings": {
+        "level": "error"
+    },
+    "no_trailing_semicolons": {
+        "level": "error"
+    },
+    "no_trailing_whitespace": {
+        "level": "error",
+        "allowed_in_comments": false,
+        "allowed_in_empty_lines": true
+    },
+    "no_unnecessary_double_quotes": {
+        "level": "ignore"
+    },
+    "no_unnecessary_fat_arrows": {
+        "level": "warn"
+    },
+    "non_empty_constructor_needs_parens": {
+        "level": "ignore"
+    },
+    "prefer_english_operator": {
+        "level": "ignore",
+        "doubleNotLevel": "ignore"
+    },
+    "space_operators": {
+        "level": "ignore"
+    }
+}

+ 200 - 0
dashboard/widgets/gauge/gauge.coffee

@@ -0,0 +1,200 @@
+class Dashing.Gauge extends Dashing.Widget
+
+  displayError:(msg) ->
+    $(@node).find(".error").show()
+    $(@node).find(".error").html(msg)
+  displayMissingDependency:(name,url) ->
+    error_html = "<h1>Missing #{name}</h1><p>Download <a href='#{url}'>#{name}</a> and place it in the <span class='highlighted'>assets/javascripts</span> folder"
+    @displayError(error_html)
+
+  ready: ->
+
+    @displayMissingDependency("moment.js","http://momentjs.com/downloads/moment.min.js") if (!window.moment)
+    @displayMissingDependency("lodash.js","https://raw.githubusercontent.com/lodash/lodash/2.4.1/dist/lodash.min.js") if (!window._)
+    @displayMissingDependency("jQuery Sparkline","http://omnipotent.net/jquery.sparkline/#s-about") if (!$.fn.sparkline)
+
+    if @get('debug')
+      @debug = (@get('debug'))
+    else
+      @debug = false
+
+    # use the data-from property in the widget tag to indicate the time range (Default: 7d)
+    if typeof @get('from') isnt "undefined"
+      @from = (@get('from'))
+    else
+      @from = "7d"
+
+    # use the data-unit property in the widget tag to indicate the unit to display (Default:ms)
+    if typeof @get('unit') isnt "undefined"
+      @unit = (@get('unit'))
+    else
+      @unit = "ms"
+
+    # use the graphite_host property in the widget tag to indicate the graphite host (Default:our P2 graphite host)
+    if @get('graphite_host')
+      @graphite_host = (@get('graphite_host'))
+    else
+      @graphite_host = "http://graphite"
+
+    $n = $(@node)
+
+    # The widget looks at 24 hours worth of data in 10 minutes increment and compares it to the same day a week ago
+    targets = ["#{@get('metric')}"]
+    if @timeshift = (@get('timeshift'))
+      targets.push "timeShift(#{@get('metric')}, '#{@get('timeshift')}')"
+
+    @encoded_target = _.reduce(targets, (memo,target,key) ->
+      memo += "&target=#{target}"
+    ,"")
+
+    if @get('interval')
+      interval = parseInt(@get('interval'))
+    else
+      interval = 60000
+
+    self = this
+
+    console.dir self if @debug
+
+    setInterval ->
+      self.updateGraph()
+    , interval
+    @updateGraph()
+
+    setInterval ->
+      self.updateSparkline()
+    , interval*100
+
+    @updateSparkline()
+
+  updateGraph: ->
+
+    graph_data_url = "#{@graphite_host}/render?format=json#{@encoded_target}"
+    console.log graph_data_url if @debug
+    $.getJSON graph_data_url,
+      from: '-' + @from
+      until: 'now',
+      renderResults.bind(@)
+
+  updateSparkline: ->
+    metric = @get('metric')
+    target = "&target=#{metric}"
+    graph_data_url = "#{@graphite_host}/render?format=json#{target}"
+
+    $.getJSON graph_data_url,
+      from: '-' + @from
+      until: 'now',
+      renderSparkline.bind(@)
+
+  renderSparkline = (data) ->
+    console.dir(data) if @debug
+    dataset = removeTimestampFromTuple(data[0].datapoints)
+
+    if dataset.length>1
+      $(@node).find(".sparkline-chart").sparkline(dataset, {
+        type: 'line',
+        chartRangeMin: 0,
+        drawNormalOnTop: true,
+        normalRangeMax: 3000,
+        width:'10em',
+        normalRangeColor: '#336699'})
+      $(@node).find(".sparkline-label").text("#{@from} ")
+    else
+      $(@node).find(".sparkline").hide()
+
+  renderResults = (data) ->
+    console.log data if @debug
+    dataLatest = 0
+    for point in data[0].datapoints.reverse()
+      unless point[0] == null
+        dataLatest = point[0]
+        break
+
+    change_rate = 0
+
+    $(@node).find(".change-rate i").removeClass("icon-arrow-up").removeClass("icon-arrow-down")
+
+    if isNaN change_rate
+      change_rate = ""
+      $(@node).find(".change-rate").css("font-size","1em")
+      $(@node).find(".change-rate").css("line-height","40px")
+
+    else if change_rate>0
+      $(@node).find(".change-rate").css("color","red")
+      change_rate=change_rate+"%"
+      $(@node).find(".change-rate i").addClass("icon-arrow-up")
+
+    else if change_rate==0
+      $(@node).find(".change-rate").css("color","white")
+      change_rate=""
+      # change_rate="no change"
+      $(@node).find(".change-rate").css("font-size","1em")
+      $(@node).find(".change-rate").css("line-height","40px")
+    else
+      $(@node).find(".change-rate").css("color","green")
+      change_rate=change_rate+"%"
+      $(@node).find(".change-rate i").addClass("icon-arrow-down")
+
+    unit = @unit
+    if isNaN dataLatest
+      $(@node).find(".value").text("N/A").fadeOut().fadeIn()
+    else
+      value = dataLatest.toLocaleString()
+      sizeClass = getSizeClassForValue(value)
+      $(@node).find(".value").html("#{value}<span style='font-size:.3em;'>#{unit}</span>").fadeOut().fadeIn()
+      colors = _.map(@get('colors').split(","), (elem) -> elem.split(":"))
+      color = getColorFromValue(colors, dataLatest)
+      $(@node).fadeOut().css('background-color', color).fadeIn()
+      $(@node).find(".value").addClass(sizeClass)
+
+    $(@node).find(".change-rate span").text("#{change_rate}")
+    $(@node).find(".change-rate span").fadeOut().fadeIn()
+    $(@node).find(".updated-at").text(moment().format('MMMM Do YYYY, h:mmA')).fadeOut().fadeIn()
+
+    return
+
+  findLargestIndexSmallerThanValue = (arr, val) ->
+    _.findLastIndex(arr, (elem) -> elem[0] <= val)
+
+  getColorFromValue = (colors, val) ->
+    index = findLargestIndexSmallerThanValue(colors, val)
+    if colors.length == index + 1
+      getColorFromTupel(colors[index])
+    else
+      color1 = getColorFromTupel(colors[index])
+      color2 = getColorFromTupel(colors[index+1])
+      value1 = getValueFromTupel(colors[index])
+      value2 = getValueFromTupel(colors[index+1])
+      ratio = (val - value1) / (value2 - value1)
+      getGradientColor(color1, color2, ratio)
+
+  getSizeClassForValue = (val) ->
+    "length#{val.length}"
+
+  hex = (x) ->
+    y = x.toString(16)
+    y.length == 1 ? '0' + y : y
+
+  h = (x) ->
+    ('0' + x.toString(16)).substr(-2)
+
+  getGradientColor = (color1, color2, ratio) ->
+    r = Math.ceil(parseInt(color1.substr(1,2), 16) * (1-ratio) + parseInt(color2.substr(1,2), 16) * ratio)
+    g = Math.ceil(parseInt(color1.substr(3,2), 16) * (1-ratio) + parseInt(color2.substr(3,2), 16) * ratio)
+    b = Math.ceil(parseInt(color1.substr(5,2), 16) * (1-ratio) + parseInt(color2.substr(5,2), 16) * ratio)
+    "#" + h(r) + h(g) + h(b)
+
+  getColorFromTupel = (arr) ->
+    arr[1]
+  getValueFromTupel = (arr) ->
+    parseInt(arr[0],10)
+  removeTimestampFromTuple = (arr) ->
+    _.map(arr, (num) -> num[0])
+  roundUpArrayValues = (arr) ->
+    _.map(arr, (num) -> Math.floor(num))
+
+  array_values_average = (arr) ->
+    _.reduce(arr, (memo, num) ->
+      memo + num
+    , 0) / arr.length
+

+ 10 - 0
dashboard/widgets/gauge/gauge.html

@@ -0,0 +1,10 @@
+<div class="error">ERROR</div>
+<h1 class="title" data-bind="title"></h1>
+<h2 class="value"></h2>
+<p class="change-rate">
+  <i class="trend icon-arrow-up"></i><span></span>
+</p>
+<br/>
+<div class="sparkline"><span class="sparkline-label"></span><span class="sparkline-chart"></span></div>
+<p class="more-info"></p>
+<p class="updated-at" ></p>

+ 89 - 0
dashboard/widgets/gauge/gauge.scss

@@ -0,0 +1,89 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color:  #47bbb3;
+$value-color:       #fff;
+$error-color: red;
+
+$title-color:       rgba(255, 255, 255, 0.7);
+$moreinfo-color:    rgba(255, 255, 255, 0.7);
+
+// ----------------------------------------------------------------------------
+// Widget-number styles
+// ----------------------------------------------------------------------------
+.widget-gauge {
+
+  background-color: $background-color;
+
+  .sparkline-label
+  {
+    font-size:.6em;
+  }
+  .sparkline
+  {
+    vertical-align: baseline;
+  }
+  .title {
+    color: $title-color;
+  }
+
+  .error {
+    background-color: $error-color;
+    color: white;
+    padding:1em;
+    border-radius: 25px;
+    display:none;
+    .highlighted
+    {
+      background-color: #444444;
+      padding:0 4px 0 4px;
+    }
+    a
+    {
+      text-decoration: underline;
+    }
+
+  }
+  .value {
+    color: $value-color;
+    font-size: 4em;
+    text-transform: lowercase;
+
+    &.length5 {
+      font-size: 4em;
+    }
+    &.length6 {
+      font-size: 3.3em;
+    }
+    &.length7 {
+      font-size: 2.8em;
+    }
+    &.length9 {
+      font-size: 2.4em;
+    }
+    &.length10 {
+      font-size: 2em;
+    }
+  }
+
+  &.small {
+    .value {
+      font-size: 2em;
+    }
+  }
+
+  .change-rate {
+    font-weight: 500;
+    font-size: 30px;
+    color: $value-color;
+  }
+
+  .more-info {
+    color: $moreinfo-color;
+  }
+
+  .updated-at {
+    color: rgba(0, 0, 0, 0.3);
+  }
+
+}

+ 37 - 0
dashboard/widgets/graph/graph.coffee

@@ -0,0 +1,37 @@
+class Dashing.Graph extends Dashing.Widget
+
+  @accessor 'current', ->
+    return @get('displayedValue') if @get('displayedValue')
+    points = @get('points')
+    if points
+      points[points.length - 1].y
+
+  ready: ->
+    container = $(@node).parent()
+    # Gross hacks. Let's fix this.
+    width = (Dashing.widget_base_dimensions[0] * container.data("sizex")) + Dashing.widget_margins[0] * 2 * (container.data("sizex") - 1)
+    height = (Dashing.widget_base_dimensions[1] * container.data("sizey"))
+    @graph = new Rickshaw.Graph(
+      element: @node
+      width: width
+      height: height
+      renderer: @get("graphtype")
+      series: [
+        {
+        color: "#fff",
+        data: [{x:0, y:0}]
+        }
+      ]
+      padding: {top: 0.02, left: 0.02, right: 0.02, bottom: 0.02}
+    )
+
+    @graph.series[0].data = @get('points') if @get('points')
+
+    x_axis = new Rickshaw.Graph.Axis.Time(graph: @graph)
+    y_axis = new Rickshaw.Graph.Axis.Y(graph: @graph, tickFormat: Rickshaw.Fixtures.Number.formatKMBT)
+    @graph.render()
+
+  onData: (data) ->
+    if @graph
+      @graph.series[0].data = data.points
+      @graph.render()

+ 5 - 0
dashboard/widgets/graph/graph.html

@@ -0,0 +1,5 @@
+<h1 class="title" data-bind="title"></h1>
+
+<h2 class="value" data-bind="current | prettyNumber | prepend prefix | append suffix"></h2>
+
+<p class="more-info" data-bind="moreinfo"></p>

+ 65 - 0
dashboard/widgets/graph/graph.scss

@@ -0,0 +1,65 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color:  #dc5945;
+
+$title-color:       rgba(255, 255, 255, 0.7);
+$moreinfo-color:    rgba(255, 255, 255, 0.3);
+$tick-color:        rgba(0, 0, 0, 0.4);
+
+
+// ----------------------------------------------------------------------------
+// Widget-graph styles
+// ----------------------------------------------------------------------------
+.widget-graph {
+
+  background-color: $background-color;
+  position: relative;
+
+
+  svg {
+    position: absolute;
+    opacity: 0.4;
+    fill-opacity: 0.4;
+    left: 0px;
+    top: 0px;
+  }
+
+  .title, .value {
+    position: relative;
+    z-index: 99;
+  }
+
+  .title {
+    color: $title-color;
+  }
+
+  .more-info {
+    color: $moreinfo-color;
+    font-weight: 600;
+    font-size: 20px;
+    margin-top: 0;
+  }
+
+  .x_tick {
+    position: absolute;
+    bottom: 0;
+    .title {
+      font-size: 20px;
+      color: $tick-color;
+      opacity: 0.5;
+      padding-bottom: 3px;
+    }
+  }
+
+  .y_ticks {
+    font-size: 20px;
+    fill: $tick-color;
+    fill-opacity: 1;
+  }
+
+  .domain {
+    display: none;
+  }
+
+}

+ 36 - 0
dashboard/widgets/graphite/graphite.coffee

@@ -0,0 +1,36 @@
+class Dashing.Graphite extends Dashing.Widget
+
+  ready: ->
+    if @get('interval')
+      interval = parseInt(@get('interval'))
+    else
+      interval = 30000
+    self = this
+    setInterval ->
+      self.updateGraph()
+    , interval
+    @updateGraph()
+
+  updateGraph: ->
+    url = @get('image')
+    $n = $(@node)
+    width = $n.width()
+    height = $n.height()
+
+    url = @updateUrl url, 'width', width
+    url = @updateUrl url, 'height', height
+    url = @updateUrl url, '_uniq', new Date().getTime()
+
+    $n.find('img').attr 'src', url
+
+  updateUrl: (url, param, value) ->
+    if url.indexOf(param) >= 0
+      regexp = new RegExp '([\?&])' + param + '(=[^&]*)?&?'
+      url = url.replace regexp, '$1'
+
+    repl = param + '=' + value
+
+    if url.indexOf('?') < 0
+      url + '?' + repl
+    else
+      url + '&' + repl

+ 1 - 0
dashboard/widgets/graphite/graphite.html

@@ -0,0 +1 @@
+<img data-bind-width="width" data-bind-height="height" />

+ 3 - 0
dashboard/widgets/graphite/graphite.scss

@@ -0,0 +1,3 @@
+.widget.widget-graphite {
+  padding: 0;
+}

+ 9 - 0
dashboard/widgets/iframe/iframe.coffee

@@ -0,0 +1,9 @@
+class Dashing.Iframe extends Dashing.Widget
+
+  ready: ->
+    # This is fired when the widget is done being rendered
+
+  onData: (data) ->
+    # Handle incoming data
+    # You can access the html node of this widget with `@node`
+    # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in.

+ 1 - 0
dashboard/widgets/iframe/iframe.html

@@ -0,0 +1 @@
+<iframe data-bind-src="url" frameborder=0></iframe>

+ 8 - 0
dashboard/widgets/iframe/iframe.scss

@@ -0,0 +1,8 @@
+.widget-iframe {
+  padding: 3px 0px 0px 0px !important;
+
+  iframe {
+    width: 100%;
+    height: 100%;
+  }
+}

+ 9 - 0
dashboard/widgets/image/image.coffee

@@ -0,0 +1,9 @@
+class Dashing.Image extends Dashing.Widget
+
+  ready: ->
+    # This is fired when the widget is done being rendered
+
+  onData: (data) ->
+    # Handle incoming data
+    # You can access the html node of this widget with `@node`
+    # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in.

+ 1 - 0
dashboard/widgets/image/image.html

@@ -0,0 +1 @@
+<img data-bind-src="image | prepend '/assets'" data-bind-width="width"/>

+ 13 - 0
dashboard/widgets/image/image.scss

@@ -0,0 +1,13 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color:  #4b4b4b;
+
+// ----------------------------------------------------------------------------
+// Widget-image styles
+// ----------------------------------------------------------------------------
+.widget-image {
+
+  background-color: $background-color;
+
+}

+ 9 - 0
dashboard/widgets/jira_list_current_sprint_issues/jira_list_current_sprint_issues.coffee

@@ -0,0 +1,9 @@
+class Dashing.JiraListCurrentSprintIssues extends Dashing.Widget
+
+ ready: ->
+   # This is fired when the widget is done being rendered
+
+ onData: (data) ->
+   # Handle incoming data
+   # You can access the html node of this widget with `@node`
+   # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in.

+ 17 - 0
dashboard/widgets/jira_list_current_sprint_issues/jira_list_current_sprint_issues.html

@@ -0,0 +1,17 @@
+<h1 class="header" data-bind="header"></h1>
+<h2 class="status-title" data-bind="issue_type"></h2>
+
+<div class="list">
+	<div data-foreach-issue="issues" class="wrapper">
+		<div class="left">
+			<img class="assignee-avatar" data-bind-src="issue.assigneeAvatarUrl" alt="Assignee Avatar" src="">
+			<div class="assignee-name" data-bind="issue.assigneeName"></div>
+		</div>
+		<div class="right">
+			<div class="issue-title" data-bind="issue.title"></div>
+			<div class="issue-id" data-bind="issue.id"></div>
+		</div>
+		<div style="clear: both; padding-bottom: 1vh;"></div>
+	</div>
+</div>
+<p class="updated-at" data-bind="updatedAtMessage"></p>

+ 71 - 0
dashboard/widgets/jira_list_current_sprint_issues/jira_list_current_sprint_issues.scss

@@ -0,0 +1,71 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color:  teal;
+$value-color:       #fff;
+
+$title-color:       rgba(255, 255, 255, 0.7);
+$label-color:       rgba(255, 255, 255, 0.7);
+$updated-at-color: rgba(212, 212, 212, 0.76);
+
+// ----------------------------------------------------------------------------
+// Widget-jira-list-current-sprint-issues styles
+// ----------------------------------------------------------------------------
+.widget-jira-list-current-sprint-issues {
+
+  background-color: $background-color;
+  vertical-align: top !important;
+
+  .header {
+    color: $label-color;
+    font-size: xx-large;
+    margin-bottom: 0;
+  }
+
+  .status-title {
+    font-size: x-large;
+    padding-bottom: 1vh;
+    color: $label-color;
+  }
+
+  .wrapper {
+    width: 100%;
+    height: auto;
+  }
+
+  .left {
+    display: inline;
+    float: left;
+    width: 4vw;
+    text-align: left;
+    overflow: hidden;
+  }
+
+  .right {
+    vertical-align: top;
+    overflow: hidden;
+    padding-left: 0.5vw;
+  }
+
+  .issue-title {
+    font-size: medium;
+    text-align: left;
+  }
+
+  .issue-id {
+    font-size: small;
+  }
+
+  .assignee-name {
+    font-size: small;
+  }
+
+  .list {
+    list-style: none;
+  }
+
+  .updated-at {
+    color: $updated-at-color;
+  }
+
+}

+ 6 - 0
dashboard/widgets/list/list.coffee

@@ -0,0 +1,6 @@
+class Dashing.List extends Dashing.Widget
+  ready: ->
+    if @get('unordered')
+      $(@node).find('ol').remove()
+    else
+      $(@node).find('ul').remove()

+ 18 - 0
dashboard/widgets/list/list.html

@@ -0,0 +1,18 @@
+<h1 class="title" data-bind="title"></h1>
+
+<ol>
+  <li data-foreach-item="items">
+    <span class="label" data-bind="item.label"></span>
+    <span class="value" data-bind="item.value"></span>
+  </li>
+</ol>
+
+<ul class="list-nostyle">
+  <li data-foreach-item="items">
+    <span class="label" data-bind="item.label"></span>
+    <span class="value" data-bind="item.value"></span>
+  </li>
+</ul>
+
+<p class="more-info" data-bind="moreinfo"></p>
+<p class="updated-at" data-bind="updatedAtMessage"></p>

+ 60 - 0
dashboard/widgets/list/list.scss

@@ -0,0 +1,60 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color:  #12b0c5;
+$value-color:       #fff;
+
+$title-color:       rgba(255, 255, 255, 0.7);
+$label-color:       rgba(255, 255, 255, 0.7);
+$moreinfo-color:    rgba(255, 255, 255, 0.7);
+
+// ----------------------------------------------------------------------------
+// Widget-list styles
+// ----------------------------------------------------------------------------
+.widget-list {
+
+  background-color: $background-color;
+  vertical-align: top;
+
+  .title {
+    color: $title-color;
+  }
+
+  ol, ul {
+    margin: 0 15px;
+    text-align: left;
+    color: $label-color;
+  }
+
+  ol {
+    list-style-position: inside;
+  }
+
+  li {
+    margin-bottom: 5px;
+  }
+
+  .list-nostyle {
+    list-style: none;
+  }
+
+  .label {
+    color: $label-color;
+  }
+
+  .value {
+    float: right;
+    margin-left: 12px;
+    font-weight: 600;
+    color: $value-color;
+  }
+
+  .updated-at {
+    color: rgba(0, 0, 0, 0.3);
+  }
+
+  .more-info {
+    color: $moreinfo-color;
+  }
+
+}

+ 14 - 0
dashboard/widgets/meter/meter.coffee

@@ -0,0 +1,14 @@
+class Dashing.Meter extends Dashing.Widget
+
+  @accessor 'value', Dashing.AnimatedValue
+
+  constructor: ->
+    super
+    @observe 'value', (value) ->
+      $(@node).find(".meter").val(value).trigger('change')
+
+  ready: ->
+    meter = $(@node).find(".meter")
+    meter.attr("data-bgcolor", meter.css("background-color"))
+    meter.attr("data-fgcolor", meter.css("color"))
+    meter.knob()

+ 7 - 0
dashboard/widgets/meter/meter.html

@@ -0,0 +1,7 @@
+<h1 class="title" data-bind="title"></h1>
+
+<input class="meter" data-angleOffset=-125 data-angleArc=250 data-bind-data-height="height | default 200" data-bind-data-width="width | default 200" data-readOnly=true data-bind-value="value | shortenedNumber | prepend prefix | append suffix" data-bind-data-min="min" data-bind-data-max="max">
+
+<p class="more-info" data-bind="moreinfo"></p>
+
+<p class="updated-at" data-bind="updatedAtMessage"></p>

+ 35 - 0
dashboard/widgets/meter/meter.scss

@@ -0,0 +1,35 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color:  #9c4274;
+
+$title-color:       rgba(255, 255, 255, 0.7);
+$moreinfo-color:    rgba(255, 255, 255, 0.3);
+
+$meter-background:  darken($background-color, 15%);
+
+// ----------------------------------------------------------------------------
+// Widget-meter styles
+// ----------------------------------------------------------------------------
+.widget-meter {
+
+  background-color: $background-color;
+
+  input.meter {
+    background-color: $meter-background;
+    color: #fff;
+  }
+
+  .title {
+    color: $title-color;
+  }
+
+  .more-info {
+    color: $moreinfo-color;
+  }
+
+  .updated-at {
+    color: rgba(0, 0, 0, 0.3);
+  }
+
+}

+ 1 - 0
dashboard/widgets/mta/mta.coffee

@@ -0,0 +1 @@
+class Dashing.Mta extends Dashing.Widget

+ 7 - 0
dashboard/widgets/mta/mta.html

@@ -0,0 +1,7 @@
+<h1 class="title">Subway Status</h1>
+<ol>
+  <li data-foreach-item="items" data-bind-class="item.name | prepend 'line-' | append ' ' | append item.status">
+    <span data-bind="item.name"></span>
+  </li>
+</ol>
+<p class="updated-at" data-bind="updatedAtMessage"></p>

+ 70 - 0
dashboard/widgets/mta/mta.sass

@@ -0,0 +1,70 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color: #3b5cac
+
+// ----------------------------------------------------------------------------
+// Widget-comment styles
+// ----------------------------------------------------------------------------
+.widget-mta
+  background-color: $background-color
+
+  .line-1, .line-2, .line-3
+    background-color: rgb(239, 106, 76)
+
+  .line-4, .line-5, .line-6
+    background-color: rgb(82, 175, 90)
+
+  .line-7
+    background-color: rgb(183, 130, 211)
+
+  .line-A, .line-C, .line-E
+    background-color: rgb(61, 155, 227)
+
+  .line-L
+    background-color: rgb(153, 153, 153)
+
+  .line-S
+    background-color: rgb(119, 119, 119)
+
+  .line-B, .line-D, .line-F, .line-M
+    background-color: rgb(245, 164, 59)
+
+  .line-N, .line-Q, .line-R
+    background-color: rgb(251, 207, 51)
+    color: black
+
+  .line-J, .line-Z
+    background-color: rgb(172, 123, 43)
+
+  .line-G
+    background-color: rgb(198, 212, 38)
+
+  li
+    margin: 0 12px 12px 0
+    list-style: none
+    background-color: white
+    border-radius: 20px
+    padding: 4px
+    font-size: 110%
+    width: 32px
+    height: 32px
+    display: inline-block
+    font-weight: bold
+
+    &.delays
+      animation: delays 5s infinite
+
+    &.offline
+      opacity: 0.2
+
+@keyframes delays
+  25%
+    opacity: 0.2
+    transition: opacity ease-in
+  50%
+    opacity: 1
+    transition: opacity ease-in
+  75%
+    opacity: 0.2
+    transition: opacity ease-in

+ 21 - 0
dashboard/widgets/news/news.coffee

@@ -0,0 +1,21 @@
+class Dashing.News extends Dashing.Widget
+
+  ready: ->
+    @currentIndex = 0
+    @headlineElem = $(@node).find('.headline-container')
+    @nextComment()
+    @startCarousel()
+
+  onData: (data) ->
+    @currentIndex = 0
+
+  startCarousel: ->
+    setInterval(@nextComment, 8000)
+
+  nextComment: =>
+    headlines = @get('headlines')
+    if headlines
+      @headlineElem.fadeOut =>
+        @currentIndex = (@currentIndex + 1) % headlines.length
+        @set 'current_headline', headlines[@currentIndex]
+        @headlineElem.fadeIn()

+ 12 - 0
dashboard/widgets/news/news.html

@@ -0,0 +1,12 @@
+<div class="heading">
+    <image src="http://static.bbci.co.uk/frameworks/barlesque/2.35.2/orb/4/img/bbc-blocks-dark.png"></image>
+    <span> News</span>
+</div>
+<div class="headline-container">
+    <h1 data-bind='current_headline.title' class="title"></h1>
+    <div class="headline">
+        <div>
+            <p class="description" data-bind='current_headline.description'></p>
+        </div>
+    </div>
+</div>

+ 24 - 0
dashboard/widgets/number/number.coffee

@@ -0,0 +1,24 @@
+class Dashing.Number extends Dashing.Widget
+  @accessor 'current', Dashing.AnimatedValue
+
+  @accessor 'difference', ->
+    if @get('last')
+      last = parseInt(@get('last'))
+      current = parseInt(@get('current'))
+      if last != 0
+        diff = Math.abs(Math.round((current - last) / last * 100))
+        "#{diff}%"
+    else
+      ""
+
+  @accessor 'arrow', ->
+    if @get('last')
+      if parseInt(@get('current')) > parseInt(@get('last')) then 'fa fa-arrow-up' else 'fa fa-arrow-down'
+
+  onData: (data) ->
+    if data.status
+      # clear existing "status-*" classes
+      $(@get('node')).attr 'class', (i,c) ->
+        c.replace /\bstatus-\S+/g, ''
+      # add new class
+      $(@get('node')).addClass "status-#{data.status}"

+ 11 - 0
dashboard/widgets/number/number.html

@@ -0,0 +1,11 @@
+<h1 class="title" data-bind="title"></h1>
+
+<h2 class="value" data-bind="current | shortenedNumber | prepend prefix | append suffix"></h2>
+
+<p class="change-rate">
+  <i data-bind-class="arrow"></i><span data-bind="difference"></span>
+</p>
+
+<p class="more-info" data-bind="moreinfo"></p>
+
+<p class="updated-at" data-bind="updatedAtMessage"></p>

+ 39 - 0
dashboard/widgets/number/number.scss

@@ -0,0 +1,39 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color:  #47bbb3;
+$value-color:       #fff;
+
+$title-color:       rgba(255, 255, 255, 0.7);
+$moreinfo-color:    rgba(255, 255, 255, 0.7);
+
+// ----------------------------------------------------------------------------
+// Widget-number styles
+// ----------------------------------------------------------------------------
+.widget-number {
+
+  background-color: $background-color;
+
+  .title {
+    color: $title-color;
+  }
+
+  .value {
+    color: $value-color;
+  }
+
+  .change-rate {
+    font-weight: 500;
+    font-size: 30px;
+    color: $value-color;
+  }
+
+  .more-info {
+    color: $moreinfo-color;
+  }
+
+  .updated-at {
+    color: rgba(0, 0, 0, 0.3);
+  }
+
+}

+ 9 - 0
dashboard/widgets/outboard/outboard.coffee

@@ -0,0 +1,9 @@
+class Dashing.Outboard extends Dashing.Widget
+
+  ready: ->
+    # This is fired when the widget is done being rendered
+
+  onData: (data) ->
+    # Handle incoming data
+    # You can access the html node of this widget with `@node`
+    # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in.

+ 13 - 0
dashboard/widgets/outboard/outboard.html

@@ -0,0 +1,13 @@
+<h1 class="title" data-bind="title"></h1>
+
+<div class="content">
+  <ul>
+    <li data-foreach-value="values" data-bind-class="value.status">
+      <i data-bind-class="value.icon"></i>
+      <span class="name" data-bind="value.name"></span>
+      <span class="comment" data-bind="value.comment"></span>
+    </li>
+  </ul>
+</div>
+
+<p class="updated-at" data-bind="updatedAtMessage"></p>

+ 56 - 0
dashboard/widgets/outboard/outboard.scss

@@ -0,0 +1,56 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color:  rgba(135, 135, 135, 0.7);
+$title-color:       rgba(255, 255, 255, 0.7);
+$in-color: rgba(255, 255, 255, 1.0);
+$out-color: rgba(175, 175, 175, 1.0);
+$updated-at-color: rgba(255, 255, 255, 0.7);
+
+.widget-outboard {
+  background-color: $background-color;
+
+  .content {
+    text-align: left;
+  }
+
+  .title {
+    color: $title-color;
+    position: absolute;
+    top: 18px;
+    left: 0;
+    right: 0;
+  }
+
+  .updated-at {
+    color: $updated-at-color;
+  }
+
+  &.large h3 {
+    font-size: 65px;
+  }
+
+  li {
+    font-size: 15px;
+    margin-top: 8px;
+    text-indent: 10px;
+    font-weight: normal;
+  }
+
+  .name {
+    font-weight: bold;
+  }
+
+  .comment {
+    font-style: italic;
+  }
+
+  .in {
+    color: $in-color;
+  }
+
+  .out {
+    color: $out-color;
+  }
+
+}

+ 9 - 0
dashboard/widgets/slack_presence/slack_presence.coffee

@@ -0,0 +1,9 @@
+class Dashing.SlackPresence extends Dashing.Widget
+
+  ready: ->
+    super
+
+  onData: (data) ->
+    node = $(@node)
+    node.removeClass('status-active status-away status-dnd status-offline')
+    node.addClass "status-#{data.presence_class}"

+ 12 - 0
dashboard/widgets/slack_presence/slack_presence.html

@@ -0,0 +1,12 @@
+
+<div class="userinfo">
+    <div  data-bind-class="presence_class">
+        <span class="label fullname" data-bind="fullname"></span>
+
+        <img data-bind-src="img" />
+        <span class="label presence_name" data-bind="presence_name"></span>
+
+        <span class="label small presence_message" data-bind="presence_message"></span>
+    </div>
+</div>
+

+ 65 - 0
dashboard/widgets/slack_presence/slack_presence.scss

@@ -0,0 +1,65 @@
+// ----------------------------------------------------------------------------
+// Sass declarations
+// ----------------------------------------------------------------------------
+$background-color-active: rgb(40, 185, 61);
+$background-color-away: rgb(27, 91, 204);
+$background-color-dnd: rgb(249, 46, 25);
+$background-color-offline: rgb(146, 146, 146);
+
+$label-color:       rgba(255, 255, 255, 0.7);
+$label-small-color: rgba(222, 222, 222, 0.70);
+
+// ----------------------------------------------------------------------------
+// Widget-slack_presence styles
+// ----------------------------------------------------------------------------
+.widget-slack-presence {
+
+  background-color: $background-color-offline;
+  vertical-align: top;
+
+  .userinfo {
+    width: 250px;
+    height: 250px;
+    margin: 0 auto;
+
+    img {
+      border-radius: 10px;
+      margin: 5px;
+    }
+
+    .presence_message {
+      margin-top: 15px;
+    }
+
+  }
+
+  .label {
+    text-align: center;
+    display: block;
+    color: $label-color;
+    font-size: 24px;
+    word-wrap: break-word;
+
+    &.small {
+      font-size: 18px;
+      color: $label-small-color;
+    }
+  }
+
+
+  &.status-active{
+    background-color: $background-color-active;
+  }
+
+  &.status-away {
+    background-color: $background-color-away;
+  }
+
+  &.status-dnd {
+    background-color: $background-color-dnd;
+  }
+
+  &.status-offline {
+    background-color: $background-color-offline;
+  }
+}

+ 1 - 0
dashboard/widgets/text/text.coffee

@@ -0,0 +1 @@
+class Dashing.Text extends Dashing.Widget

Some files were not shown because too many files changed in this diff