aboutsummaryrefslogtreecommitdiffhomepage
path: root/public/bower_components/morris.js
diff options
context:
space:
mode:
authorMarvin Borner2018-07-13 19:06:45 +0200
committerMarvin Borner2018-07-13 19:06:45 +0200
commit6fcfb7c04d32e1c8b26a312295bf7ac3ec2d2ad7 (patch)
treedbc87ef16fa01d5d99116de283592b8fe5e02944 /public/bower_components/morris.js
parentdfd839f27146df0ad0494e11734fc7d310c70ebf (diff)
Fixed many permissions and began admin interface
Diffstat (limited to 'public/bower_components/morris.js')
-rw-r--r--public/bower_components/morris.js/.bower.json30
-rw-r--r--public/bower_components/morris.js/.gitignore6
-rw-r--r--public/bower_components/morris.js/.travis.yml19
-rw-r--r--public/bower_components/morris.js/Gruntfile.js90
-rw-r--r--public/bower_components/morris.js/README.md234
-rw-r--r--public/bower_components/morris.js/bower.json20
-rw-r--r--public/bower_components/morris.js/bower.travis.json20
-rw-r--r--public/bower_components/morris.js/examples/_template.html18
-rw-r--r--public/bower_components/morris.js/examples/area-as-line.html31
-rw-r--r--public/bower_components/morris.js/examples/area.html34
-rw-r--r--public/bower_components/morris.js/examples/bar-colors.html44
-rw-r--r--public/bower_components/morris.js/examples/bar-no-axes.html31
-rw-r--r--public/bower_components/morris.js/examples/bar.html32
-rw-r--r--public/bower_components/morris.js/examples/days.html37
-rw-r--r--public/bower_components/morris.js/examples/decimal-custom-hover.html37
-rw-r--r--public/bower_components/morris.js/examples/diagonal-xlabels-bar.html38
-rw-r--r--public/bower_components/morris.js/examples/diagonal-xlabels.html38
-rw-r--r--public/bower_components/morris.js/examples/donut-colors.html38
-rw-r--r--public/bower_components/morris.js/examples/donut-formatter.html27
-rw-r--r--public/bower_components/morris.js/examples/donut.html29
-rw-r--r--public/bower_components/morris.js/examples/dst.html30
-rw-r--r--public/bower_components/morris.js/examples/events.html57
-rw-r--r--public/bower_components/morris.js/examples/goals.html33
-rw-r--r--public/bower_components/morris.js/examples/lib/example.css13
-rw-r--r--public/bower_components/morris.js/examples/lib/example.js4
-rw-r--r--public/bower_components/morris.js/examples/months-no-smooth.html38
-rw-r--r--public/bower_components/morris.js/examples/negative.html36
-rw-r--r--public/bower_components/morris.js/examples/no-grid.html38
-rw-r--r--public/bower_components/morris.js/examples/non-continuous.html42
-rw-r--r--public/bower_components/morris.js/examples/non-date.html37
-rw-r--r--public/bower_components/morris.js/examples/quarters.html54
-rw-r--r--public/bower_components/morris.js/examples/resize.html42
-rw-r--r--public/bower_components/morris.js/examples/stacked_bars.html31
-rw-r--r--public/bower_components/morris.js/examples/timestamps.html38
-rw-r--r--public/bower_components/morris.js/examples/updating.html49
-rw-r--r--public/bower_components/morris.js/examples/weeks.html53
-rw-r--r--public/bower_components/morris.js/examples/years.html37
-rw-r--r--public/bower_components/morris.js/less/morris.core.less27
-rw-r--r--public/bower_components/morris.js/lib/morris.area.coffee66
-rw-r--r--public/bower_components/morris.js/lib/morris.bar.coffee208
-rw-r--r--public/bower_components/morris.js/lib/morris.coffee43
-rw-r--r--public/bower_components/morris.js/lib/morris.donut.coffee213
-rw-r--r--public/bower_components/morris.js/lib/morris.grid.coffee499
-rw-r--r--public/bower_components/morris.js/lib/morris.hover.coffee44
-rw-r--r--public/bower_components/morris.js/lib/morris.line.coffee405
-rw-r--r--public/bower_components/morris.js/morris.css2
-rw-r--r--public/bower_components/morris.js/morris.js1892
-rw-r--r--public/bower_components/morris.js/morris.min.js7
-rw-r--r--public/bower_components/morris.js/package.json36
-rw-r--r--public/bower_components/morris.js/spec/lib/area/area_spec.coffee60
-rw-r--r--public/bower_components/morris.js/spec/lib/bar/bar_spec.coffee127
-rw-r--r--public/bower_components/morris.js/spec/lib/bar/colours.coffee36
-rw-r--r--public/bower_components/morris.js/spec/lib/commas_spec.coffee38
-rw-r--r--public/bower_components/morris.js/spec/lib/donut/donut_spec.coffee76
-rw-r--r--public/bower_components/morris.js/spec/lib/grid/auto_grid_lines_spec.coffee25
-rw-r--r--public/bower_components/morris.js/spec/lib/grid/set_data_spec.coffee208
-rw-r--r--public/bower_components/morris.js/spec/lib/grid/y_label_format_spec.coffee15
-rw-r--r--public/bower_components/morris.js/spec/lib/hover_spec.coffee64
-rw-r--r--public/bower_components/morris.js/spec/lib/label_series_spec.coffee186
-rw-r--r--public/bower_components/morris.js/spec/lib/line/line_spec.coffee211
-rw-r--r--public/bower_components/morris.js/spec/lib/pad_spec.coffee17
-rw-r--r--public/bower_components/morris.js/spec/lib/parse_time_spec.coffee35
-rw-r--r--public/bower_components/morris.js/spec/specs.html34
-rw-r--r--public/bower_components/morris.js/spec/support/placeholder.coffee6
-rw-r--r--public/bower_components/morris.js/spec/viz/examples.js56
-rw-r--r--public/bower_components/morris.js/spec/viz/exemplary/area0.pngbin0 -> 18402 bytes
-rw-r--r--public/bower_components/morris.js/spec/viz/exemplary/bar0.pngbin0 -> 6936 bytes
-rw-r--r--public/bower_components/morris.js/spec/viz/exemplary/line0.pngbin0 -> 22812 bytes
-rw-r--r--public/bower_components/morris.js/spec/viz/exemplary/stacked_bar0.pngbin0 -> 5859 bytes
-rwxr-xr-xpublic/bower_components/morris.js/spec/viz/run.sh32
-rw-r--r--public/bower_components/morris.js/spec/viz/test.html34
-rw-r--r--public/bower_components/morris.js/spec/viz/visual_specs.js66
72 files changed, 6253 insertions, 0 deletions
diff --git a/public/bower_components/morris.js/.bower.json b/public/bower_components/morris.js/.bower.json
new file mode 100644
index 0000000..33a9fc4
--- /dev/null
+++ b/public/bower_components/morris.js/.bower.json
@@ -0,0 +1,30 @@
+{
+ "name": "morris.js",
+ "version": "0.5.1",
+ "main": [
+ "./morris.js",
+ "./morris.css"
+ ],
+ "dependencies": {
+ "jquery": ">= 2.1.0",
+ "raphael": ">= 2.0",
+ "mocha": "~1.17.1"
+ },
+ "devDependencies": {
+ "mocha": "~1.17.1",
+ "chai": "~1.9.0",
+ "chai-jquery": "~1.2.1",
+ "sinon": "http://sinonjs.org/releases/sinon-1.8.1.js",
+ "sinon-chai": "~2.5.0"
+ },
+ "homepage": "https://github.com/oesmith/morris.js",
+ "_release": "0.5.1",
+ "_resolution": {
+ "type": "version",
+ "tag": "0.5.1",
+ "commit": "d5cf1410eda7055eaf2c7d218d4cb24ec5ed55c8"
+ },
+ "_source": "https://github.com/oesmith/morris.js.git",
+ "_target": "^0.5.1",
+ "_originalSource": "morris.js"
+} \ No newline at end of file
diff --git a/public/bower_components/morris.js/.gitignore b/public/bower_components/morris.js/.gitignore
new file mode 100644
index 0000000..7f83be5
--- /dev/null
+++ b/public/bower_components/morris.js/.gitignore
@@ -0,0 +1,6 @@
+build/
+node_modules/
+spec/viz/output/
+spec/viz/diff/
+bower_components
+.idea
diff --git a/public/bower_components/morris.js/.travis.yml b/public/bower_components/morris.js/.travis.yml
new file mode 100644
index 0000000..d3d2e53
--- /dev/null
+++ b/public/bower_components/morris.js/.travis.yml
@@ -0,0 +1,19 @@
+language: node_js
+node_js:
+ - 0.10
+before_script:
+ - "npm install -g grunt-cli"
+ - "npm install"
+ - "cp -f bower.travis.json bower.json"
+ - 'sed -i -e "s/JQUERY/$JQUERY/" bower.json'
+ - 'sed -i -e "s/RAPHAEL/$RAPHAEL/" bower.json'
+ - "bower install"
+env:
+ - JQUERY="~> 1.8.0" RAPHAEL="~> 2.0.0"
+ - JQUERY="~> 1.9.0" RAPHAEL="~> 2.0.0"
+ - JQUERY="~> 2.0.0" RAPHAEL="~> 2.0.0"
+ - JQUERY="~> 2.1.0" RAPHAEL="~> 2.0.0"
+ - JQUERY="~> 1.8.0" RAPHAEL="~> 2.1.0"
+ - JQUERY="~> 1.9.0" RAPHAEL="~> 2.1.0"
+ - JQUERY="~> 2.0.0" RAPHAEL="~> 2.1.0"
+ - JQUERY="~> 2.1.0" RAPHAEL="~> 2.1.0"
diff --git a/public/bower_components/morris.js/Gruntfile.js b/public/bower_components/morris.js/Gruntfile.js
new file mode 100644
index 0000000..20eed2b
--- /dev/null
+++ b/public/bower_components/morris.js/Gruntfile.js
@@ -0,0 +1,90 @@
+module.exports = function (grunt) {
+ require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
+
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+ coffee: {
+ lib: {
+ options: { bare: false },
+ files: {
+ 'morris.js': ['build/morris.coffee']
+ }
+ },
+ spec: {
+ options: { bare: true },
+ files: {
+ 'build/spec.js': ['build/spec.coffee']
+ }
+ },
+ },
+ concat: {
+ 'build/morris.coffee': {
+ options: {
+ banner: "### @license\n"+
+ "<%= pkg.name %> v<%= pkg.version %>\n"+
+ "Copyright <%= (new Date()).getFullYear() %> <%= pkg.author.name %> All rights reserved.\n" +
+ "Licensed under the <%= pkg.license %> License.\n" +
+ "###\n",
+ },
+ src: [
+ 'lib/morris.coffee',
+ 'lib/morris.grid.coffee',
+ 'lib/morris.hover.coffee',
+ 'lib/morris.line.coffee',
+ 'lib/morris.area.coffee',
+ 'lib/morris.bar.coffee',
+ 'lib/morris.donut.coffee'
+ ],
+ dest: 'build/morris.coffee'
+ },
+ 'build/spec.coffee': ['spec/support/**/*.coffee', 'spec/lib/**/*.coffee']
+ },
+ less: {
+ all: {
+ src: 'less/*.less',
+ dest: 'morris.css',
+ options: {
+ compress: true
+ }
+ }
+ },
+ uglify: {
+ build: {
+ options: {
+ preserveComments: 'some'
+ },
+ files: {
+ 'morris.min.js': 'morris.js'
+ }
+ }
+ },
+ mocha: {
+ index: ['spec/specs.html'],
+ options: {run: true}
+ },
+ watch: {
+ all: {
+ files: ['lib/**/*.coffee', 'spec/lib/**/*.coffee', 'spec/support/**/*.coffee', 'less/**/*.less'],
+ tasks: 'default'
+ },
+ dev: {
+ files: 'lib/*.coffee' ,
+ tasks: ['concat:build/morris.coffee', 'coffee:lib']
+ }
+ },
+ shell: {
+ visual_spec: {
+ command: './run.sh',
+ options: {
+ stdout: true,
+ failOnError: true,
+ execOptions: {
+ cwd: 'spec/viz'
+ }
+ }
+ }
+ }
+ });
+
+ grunt.registerTask('default', ['concat', 'coffee', 'less', 'uglify', 'mocha', 'shell:visual_spec']);
+};
diff --git a/public/bower_components/morris.js/README.md b/public/bower_components/morris.js/README.md
new file mode 100644
index 0000000..6873d7b
--- /dev/null
+++ b/public/bower_components/morris.js/README.md
@@ -0,0 +1,234 @@
+# Morris.js - pretty time-series line graphs
+
+[![Build Status](https://secure.travis-ci.org/morrisjs/morris.js.png?branch=master)](http://travis-ci.org/morrisjs/morris.js)
+
+Morris.js is the library that powers the graphs on http://howmanyleft.co.uk/.
+It's a very simple API for drawing line, bar, area and donut charts.
+
+Cheers!
+
+\- Olly (olly@oesmith.co.uk)
+
+## Contributors wanted
+
+I'm unfortunately not able to actively support Morris.js any more. I keep an eye
+on the issues, but I rarely have the time to fix bugs or review pull requests.
+
+If you're interested in actively contributing to Morris.js, please contact me on
+the email address above.
+
+## Requirements
+
+- [jQuery](http://jquery.com/) (>= 1.7 recommended, but it'll probably work with
+ older versions)
+- [Raphael.js](http://raphaeljs.com/) (>= 2.0)
+
+## Usage
+
+See [the website](http://morrisjs.github.com/morris.js/).
+
+## Development
+
+Very daring.
+
+Fork, hack, possibly even add some tests, then send a pull request :)
+
+Remember that Morris.js is a coffeescript project. Please make your changes in
+the `.coffee` files, not in the compiled javascript files in the root directory
+of the project.
+
+### Developer quick-start
+
+You'll need [node.js](https://nodejs.org). I recommend using
+[nvm](https://github.com/creationix/nvm) for installing node in
+development environments.
+
+With node installed, install [grunt](https://github.com/cowboy/grunt) using
+`npm install -g grunt-cli`, and then the rest of the test/build dependencies
+with `npm install` in the morris.js project folder.
+
+Once you're all set up, you can compile, minify and run the tests using `grunt`.
+
+Note: I'm experimenting with using perceptual diffs to catch rendering
+regressions. Due to font rendering differences between platforms, the pdiff
+tests currently *only* pass on OS X.
+
+## Changelog
+
+### 0.5.1 - 15th June 2014
+
+- Fix touch event handling.
+- Fix stacked=false in bar chart [#275](https://github.com/morrisjs/morris.js/issues/275)
+- Configurable vertical segments [#297](https://github.com/morrisjs/morris.js/issues/297)
+- Deprecate continuousLine option.
+
+### 0.5.0 - 19th March 2014
+
+- Update grunt dependency [#288](https://github.com/morrisjs/morris.js/issues/228)
+- Donut segment color config in data objects [#281](https://github.com/morrisjs/morris.js/issues/281)
+- Customisable line widths and point drawing [#272](https://github.com/morrisjs/morris.js/issues/272)
+- Bugfix for @options.smooth [#266](https://github.com/morrisjs/morris.js/issues/266)
+- Option to disable axes individually [#253](https://github.com/morrisjs/morris.js/issues/253)
+- Range selection [#252](https://github.com/morrisjs/morris.js/issues/252)
+- Week format for x-labels [#250](https://github.com/morrisjs/morris.js/issues/250)
+- Update developer quickstart instructions [#243](https://github.com/morrisjs/morris.js/issues/243)
+- Experimenting with perceptual diffs.
+- Add original data row to hover callback [#264](https://github.com/morrisjs/morris.js/issues/264)
+- setData method for donut charts [#211](https://github.com/morrisjs/morris.js/issues/211)
+- Automatic resizing [#111](https://github.com/morrisjs/morris.js/issues/111)
+- Fix travis builds [#298](https://github.com/morrisjs/morris.js/issues/298)
+- Option for rounded corners on bar charts [#305](https://github.com/morrisjs/morris.js/issues/305)
+- Option to set padding for X axis labels [#306](https://github.com/morrisjs/morris.js/issues/306)
+- Use local javascript for examples.
+- Events on non-time series [#314](https://github.com/morrisjs/morris.js/issues/314)
+
+### 0.4.3 - 12th May 2013
+
+- Fix flickering hover box [#186](https://github.com/morrisjs/morris.js/issues/186)
+- xLabelAngle option (diagonal labels!!) [#239](https://github.com/morrisjs/morris.js/issues/239)
+- Fix area chart fill bug [#190](https://github.com/morrisjs/morris.js/issues/190)
+- Make event handlers chainable
+- gridTextFamily and gridTextWeight options
+- Fix hovers with setData [#213](https://github.com/morrisjs/morris.js/issues/213)
+- Fix hideHover behaviour [#236](https://github.com/morrisjs/morris.js/issues/236)
+
+### 0.4.2 - 14th April 2013
+
+- Fix DST handling [#191](https://github.com/morrisjs/morris.js/issues/191)
+- Parse data values from strings in Morris.Donut [#189](https://github.com/morrisjs/morris.js/issues/189)
+- Non-cumulative area charts [#199](https://github.com/morrisjs/morris.js/issues/199)
+- Round Y-axis labels to significant numbers [#162](https://github.com/morrisjs/morris.js/162)
+- Customising default hover content [#179](https://github.com/morrisjs/morris.js/179)
+
+### 0.4.1 - 8th February 2013
+
+- Fix goal and event rendering. [#181](https://github.com/morrisjs/morris.js/issues/181)
+- Don't break when empty data is passed to setData [#142](https://github.com/morrisjs/morris.js/issues/142)
+- labelColor option for donuts [#159](https://github.com/morrisjs/morris.js/issues/159)
+
+### 0.4.0 - 26th January 2013
+
+- Goals and events [#103](https://github.com/morrisjs/morris.js/issues/103).
+- Bower package manager metadata.
+- More flexible formatters [#107](https://github.com/morrisjs/morris.js/issues/107).
+- Color callbacks.
+- Decade intervals for time-axis labels.
+- Non-continous line tweaks [#116](https://github.com/morrisjs/morris.js/issues/116).
+- Stacked bars [#120](https://github.com/morrisjs/morris.js/issues/120).
+- HTML hover [#134](https://github.com/morrisjs/morris.js/issues/134).
+- yLabelFormat [#139](https://github.com/morrisjs/morris.js/issues/139).
+- Disable axes [#114](https://github.com/morrisjs/morris.js/issues/114).
+
+### 0.3.3 - 1st November 2012
+
+- **Bar charts!** [#101](https://github.com/morrisjs/morris.js/issues/101).
+
+### 0.3.2 - 28th October 2012
+
+- **Area charts!** [#47](https://github.com/morrisjs/morris.js/issues/47).
+- Some major refactoring and test suite improvements.
+- Set smooth parameter per series [#91](https://github.com/morrisjs/morris.js/issues/91).
+- Custom dateFormat for string x-values [#90](https://github.com/morrisjs/morris.js/issues/90).
+
+### 0.3.1 - 13th October 2012
+
+- Add `formatter` option for customising value labels in donuts [#75](https://github.com/morrisjs/morris.js/issues/75).
+- Cycle `lineColors` on line charts to avoid running out of colours [#78](https://github.com/morrisjs/morris.js/issues/78).
+- Add method to select donut segments. [#79](https://github.com/morrisjs/morris.js/issues/79).
+- Don't go negative on yMin when all y values are zero. [#80](https://github.com/morrisjs/morris.js/issues/80).
+- Don't sort data when parseTime is false [#83](https://github.com/morrisjs/morris.js/issues/83).
+- Customise styling for points. [#87](https://github.com/morrisjs/morris.js/issues/87).
+
+### 0.3.0 - 15th September 2012
+
+- Donut charts!
+- Bugfix: ymin/ymax bug [#71](https://github.com/morrisjs/morris.js/issues/71).
+- Bugfix: infinite loop when data indicates horizontal line [#66](https://github.com/morrisjs/morris.js/issues/66).
+
+### 0.2.10 - 26th June 2012
+
+- Support for decimal labels on y-axis [#58](https://github.com/morrisjs/morris.js/issues/58).
+- Better axis label clipping [#63](https://github.com/morrisjs/morris.js/issues/63).
+- Redraw graphs with updated data using `setData` method [#64](https://github.com/morrisjs/morris.js/issues/64).
+- Bugfix: series with zero or one non-null values [#65](https://github.com/morrisjs/morris.js/issues/65).
+
+### 0.2.9 - 15th May 2012
+
+- Bugfix: Fix zero-value regression
+- Bugfix: Don't modify user-supplied data
+
+### 0.2.8 - 10th May 2012
+
+- Customising x-axis labels with `xLabelFormat` option
+- Only use timezones when timezone info is specified
+- Fix old IE bugs (mostly in examples!)
+- Added `preunits` and `postunits` options
+- Better non-continuous series data support
+
+### 0.2.7 - 2nd April 2012
+
+- Added `xLabels` option
+- Refactored x-axis labelling
+- Better ISO date support
+- Fix bug with single value in non time-series graphs
+
+### 0.2.6 - 18th March 2012
+
+- Partial series support (see `null` y-values in `examples/quarters.html`)
+- `parseTime` option bugfix for non-time-series data
+
+### 0.2.5 - 15th March 2012
+
+- Raw millisecond timestamp support (with `dateFormat` option)
+- YYYY-MM-DD HH:MM[:SS[.SSS]] date support
+- Decimal number labels
+
+### 0.2.4 - 8th March 2012
+
+- Negative y-values support
+- `ymin` option
+- `units` options
+
+### 0.2.3 - 6th Mar 2012
+
+- jQuery no-conflict compatibility
+- Support ISO week-number dates
+- Optionally hide hover on mouseout (`hideHover`)
+- Optionally skip parsing dates, treating X values as an equally-spaced series (`parseTime`)
+
+### 0.2.2 - 29th Feb 2012
+
+- Bugfix: mouseover error when options.data.length == 2
+- Automatically sort options.data
+
+### 0.2.1 - 28th Feb 2012
+
+- Accept a DOM element *or* an ID in `options.element`
+- Add `smooth` option
+- Bugfix: clone `@default`
+- Add `ymax` option
+
+## License
+
+Copyright (c) 2012-2014, Olly Smith
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. 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.
+
+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.
diff --git a/public/bower_components/morris.js/bower.json b/public/bower_components/morris.js/bower.json
new file mode 100644
index 0000000..a2dbfea
--- /dev/null
+++ b/public/bower_components/morris.js/bower.json
@@ -0,0 +1,20 @@
+{
+ "name": "morris.js",
+ "version": "0.5.0",
+ "main": [
+ "./morris.js",
+ "./morris.css"
+ ],
+ "dependencies": {
+ "jquery": ">= 2.1.0",
+ "raphael": ">= 2.0",
+ "mocha": "~1.17.1"
+ },
+ "devDependencies": {
+ "mocha": "~1.17.1",
+ "chai": "~1.9.0",
+ "chai-jquery": "~1.2.1",
+ "sinon": "http://sinonjs.org/releases/sinon-1.8.1.js",
+ "sinon-chai": "~2.5.0"
+ }
+}
diff --git a/public/bower_components/morris.js/bower.travis.json b/public/bower_components/morris.js/bower.travis.json
new file mode 100644
index 0000000..2a86ece
--- /dev/null
+++ b/public/bower_components/morris.js/bower.travis.json
@@ -0,0 +1,20 @@
+{
+ "name": "morris.js",
+ "version": "0.5.0",
+ "main": [
+ "./morris.js",
+ "./morris.css"
+ ],
+ "dependencies": {
+ "jquery": "JQUERY",
+ "raphael": "RAPHAEL",
+ "mocha": "~1.17.1"
+ },
+ "devDependencies": {
+ "mocha": "~1.17.1",
+ "chai": "~1.9.0",
+ "chai-jquery": "~1.2.1",
+ "sinon": "http://sinonjs.org/releases/sinon-1.8.1.js",
+ "sinon-chai": "~2.5.0"
+ }
+}
diff --git a/public/bower_components/morris.js/examples/_template.html b/public/bower_components/morris.js/examples/_template.html
new file mode 100644
index 0000000..625c45b
--- /dev/null
+++ b/public/bower_components/morris.js/examples/_template.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+</head>
+<body>
+<h1>Title</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+// Insert code here:
+// it'll get eval()-ed and prettyprinted.
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/area-as-line.html b/public/bower_components/morris.js/examples/area-as-line.html
new file mode 100644
index 0000000..1e198d2
--- /dev/null
+++ b/public/bower_components/morris.js/examples/area-as-line.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Area charts behaving like line charts</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+// Use Morris.Area instead of Morris.Line
+Morris.Area({
+ element: 'graph',
+ behaveLikeLine: true,
+ data: [
+ {x: '2011 Q1', y: 3, z: 3},
+ {x: '2011 Q2', y: 2, z: 1},
+ {x: '2011 Q3', y: 2, z: 4},
+ {x: '2011 Q4', y: 3, z: 3}
+ ],
+ xkey: 'x',
+ ykeys: ['y', 'z'],
+ labels: ['Y', 'Z']
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/area.html b/public/bower_components/morris.js/examples/area.html
new file mode 100644
index 0000000..4af1dc8
--- /dev/null
+++ b/public/bower_components/morris.js/examples/area.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Area charts</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+// Use Morris.Area instead of Morris.Line
+Morris.Area({
+ element: 'graph',
+ data: [
+ {x: '2010 Q4', y: 3, z: 7},
+ {x: '2011 Q1', y: 3, z: 4},
+ {x: '2011 Q2', y: null, z: 1},
+ {x: '2011 Q3', y: 2, z: 5},
+ {x: '2011 Q4', y: 8, z: 2},
+ {x: '2012 Q1', y: 4, z: 4}
+ ],
+ xkey: 'x',
+ ykeys: ['y', 'z'],
+ labels: ['Y', 'Z']
+}).on('click', function(i, row){
+ console.log(i, row);
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/bar-colors.html b/public/bower_components/morris.js/examples/bar-colors.html
new file mode 100644
index 0000000..f53d1a2
--- /dev/null
+++ b/public/bower_components/morris.js/examples/bar-colors.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Bar charts</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+// Use Morris.Bar
+Morris.Bar({
+ element: 'graph',
+ data: [
+ {x: '2011 Q1', y: 0},
+ {x: '2011 Q2', y: 1},
+ {x: '2011 Q3', y: 2},
+ {x: '2011 Q4', y: 3},
+ {x: '2012 Q1', y: 4},
+ {x: '2012 Q2', y: 5},
+ {x: '2012 Q3', y: 6},
+ {x: '2012 Q4', y: 7},
+ {x: '2013 Q1', y: 8}
+ ],
+ xkey: 'x',
+ ykeys: ['y'],
+ labels: ['Y'],
+ barColors: function (row, series, type) {
+ if (type === 'bar') {
+ var red = Math.ceil(255 * row.y / this.ymax);
+ return 'rgb(' + red + ',0,0)';
+ }
+ else {
+ return '#000';
+ }
+ }
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/bar-no-axes.html b/public/bower_components/morris.js/examples/bar-no-axes.html
new file mode 100644
index 0000000..a5c0220
--- /dev/null
+++ b/public/bower_components/morris.js/examples/bar-no-axes.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Bar charts</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+// Use Morris.Bar
+Morris.Bar({
+ element: 'graph',
+ axes: false,
+ data: [
+ {x: '2011 Q1', y: 3, z: 2, a: 3},
+ {x: '2011 Q2', y: 2, z: null, a: 1},
+ {x: '2011 Q3', y: 0, z: 2, a: 4},
+ {x: '2011 Q4', y: 2, z: 4, a: 3}
+ ],
+ xkey: 'x',
+ ykeys: ['y', 'z', 'a'],
+ labels: ['Y', 'Z', 'A']
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/bar.html b/public/bower_components/morris.js/examples/bar.html
new file mode 100644
index 0000000..1076718
--- /dev/null
+++ b/public/bower_components/morris.js/examples/bar.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Bar charts</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+// Use Morris.Bar
+Morris.Bar({
+ element: 'graph',
+ data: [
+ {x: '2011 Q1', y: 3, z: 2, a: 3},
+ {x: '2011 Q2', y: 2, z: null, a: 1},
+ {x: '2011 Q3', y: 0, z: 2, a: 4},
+ {x: '2011 Q4', y: 2, z: 4, a: 3}
+ ],
+ xkey: 'x',
+ ykeys: ['y', 'z', 'a'],
+ labels: ['Y', 'Z', 'A']
+}).on('click', function(i, row){
+ console.log(i, row);
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/days.html b/public/bower_components/morris.js/examples/days.html
new file mode 100644
index 0000000..9bc0e5e
--- /dev/null
+++ b/public/bower_components/morris.js/examples/days.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Formatting Dates YYYY-MM-DD</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+/* data stolen from http://howmanyleft.co.uk/vehicle/jaguar_'e'_type */
+var day_data = [
+ {"period": "2012-10-01", "licensed": 3407, "sorned": 660},
+ {"period": "2012-09-30", "licensed": 3351, "sorned": 629},
+ {"period": "2012-09-29", "licensed": 3269, "sorned": 618},
+ {"period": "2012-09-20", "licensed": 3246, "sorned": 661},
+ {"period": "2012-09-19", "licensed": 3257, "sorned": 667},
+ {"period": "2012-09-18", "licensed": 3248, "sorned": 627},
+ {"period": "2012-09-17", "licensed": 3171, "sorned": 660},
+ {"period": "2012-09-16", "licensed": 3171, "sorned": 676},
+ {"period": "2012-09-15", "licensed": 3201, "sorned": 656},
+ {"period": "2012-09-10", "licensed": 3215, "sorned": 622}
+];
+Morris.Line({
+ element: 'graph',
+ data: day_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned'],
+ labels: ['Licensed', 'SORN']
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/decimal-custom-hover.html b/public/bower_components/morris.js/examples/decimal-custom-hover.html
new file mode 100644
index 0000000..b168aa7
--- /dev/null
+++ b/public/bower_components/morris.js/examples/decimal-custom-hover.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Decimal Data</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+var decimal_data = [];
+for (var x = 0; x <= 360; x += 10) {
+ decimal_data.push({
+ x: x,
+ y: 1.5 + 1.5 * Math.sin(Math.PI * x / 180).toFixed(4)
+ });
+}
+window.m = Morris.Line({
+ element: 'graph',
+ data: decimal_data,
+ xkey: 'x',
+ ykeys: ['y'],
+ labels: ['sin(x)'],
+ parseTime: false,
+ hoverCallback: function (index, options, default_content, row) {
+ return default_content.replace("sin(x)", "1.5 + 1.5 sin(" + row.x + ")");
+ },
+ xLabelMargin: 10,
+ integerYLabels: true
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/diagonal-xlabels-bar.html b/public/bower_components/morris.js/examples/diagonal-xlabels-bar.html
new file mode 100644
index 0000000..8f51aa5
--- /dev/null
+++ b/public/bower_components/morris.js/examples/diagonal-xlabels-bar.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Displaying X Labels Diagonally (Bar Chart)</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+/* data stolen from http://howmanyleft.co.uk/vehicle/jaguar_'e'_type */
+var day_data = [
+ {"period": "2012-10-01", "licensed": 3407, "sorned": 660},
+ {"period": "2012-09-30", "licensed": 3351, "sorned": 629},
+ {"period": "2012-09-29", "licensed": 3269, "sorned": 618},
+ {"period": "2012-09-20", "licensed": 3246, "sorned": 661},
+ {"period": "2012-09-19", "licensed": 3257, "sorned": 667},
+ {"period": "2012-09-18", "licensed": 3248, "sorned": 627},
+ {"period": "2012-09-17", "licensed": 3171, "sorned": 660},
+ {"period": "2012-09-16", "licensed": 3171, "sorned": 676},
+ {"period": "2012-09-15", "licensed": 3201, "sorned": 656},
+ {"period": "2012-09-10", "licensed": 3215, "sorned": 622}
+];
+Morris.Bar({
+ element: 'graph',
+ data: day_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned'],
+ labels: ['Licensed', 'SORN'],
+ xLabelAngle: 60
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/diagonal-xlabels.html b/public/bower_components/morris.js/examples/diagonal-xlabels.html
new file mode 100644
index 0000000..96b6f8b
--- /dev/null
+++ b/public/bower_components/morris.js/examples/diagonal-xlabels.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Displaying X Labels Diagonally</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+/* data stolen from http://howmanyleft.co.uk/vehicle/jaguar_'e'_type */
+var day_data = [
+ {"period": "2012-10-30", "licensed": 3407, "sorned": 660},
+ {"period": "2012-09-30", "licensed": 3351, "sorned": 629},
+ {"period": "2012-09-29", "licensed": 3269, "sorned": 618},
+ {"period": "2012-09-20", "licensed": 3246, "sorned": 661},
+ {"period": "2012-09-19", "licensed": 3257, "sorned": 667},
+ {"period": "2012-09-18", "licensed": 3248, "sorned": 627},
+ {"period": "2012-09-17", "licensed": 3171, "sorned": 660},
+ {"period": "2012-09-16", "licensed": 3171, "sorned": 676},
+ {"period": "2012-09-15", "licensed": 3201, "sorned": 656},
+ {"period": "2012-09-10", "licensed": 3215, "sorned": 622}
+];
+Morris.Line({
+ element: 'graph',
+ data: day_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned'],
+ labels: ['Licensed', 'SORN'],
+ xLabelAngle: 60
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/donut-colors.html b/public/bower_components/morris.js/examples/donut-colors.html
new file mode 100644
index 0000000..9d6743e
--- /dev/null
+++ b/public/bower_components/morris.js/examples/donut-colors.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+ <style>
+ body { background:#ccc; }
+ </style>
+</head>
+<body>
+<h1>Donut Chart</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+Morris.Donut({
+ element: 'graph',
+ data: [
+ {value: 70, label: 'foo'},
+ {value: 15, label: 'bar'},
+ {value: 10, label: 'baz'},
+ {value: 5, label: 'A really really long label'}
+ ],
+ backgroundColor: '#ccc',
+ labelColor: '#060',
+ colors: [
+ '#0BA462',
+ '#39B580',
+ '#67C69D',
+ '#95D7BB'
+ ],
+ formatter: function (x) { return x + "%"}
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/donut-formatter.html b/public/bower_components/morris.js/examples/donut-formatter.html
new file mode 100644
index 0000000..47dbbe8
--- /dev/null
+++ b/public/bower_components/morris.js/examples/donut-formatter.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Donut Chart</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+Morris.Donut({
+ element: 'graph',
+ data: [
+ {value: 70, label: 'foo', formatted: 'at least 70%' },
+ {value: 15, label: 'bar', formatted: 'approx. 15%' },
+ {value: 10, label: 'baz', formatted: 'approx. 10%' },
+ {value: 5, label: 'A really really long label', formatted: 'at most 5%' }
+ ],
+ formatter: function (x, data) { return data.formatted; }
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/donut.html b/public/bower_components/morris.js/examples/donut.html
new file mode 100644
index 0000000..f69a8f4
--- /dev/null
+++ b/public/bower_components/morris.js/examples/donut.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Donut Chart</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+Morris.Donut({
+ element: 'graph',
+ data: [
+ {value: 70, label: 'foo'},
+ {value: 15, label: 'bar'},
+ {value: 10, label: 'baz'},
+ {value: 5, label: 'A really really long label'}
+ ],
+ formatter: function (x) { return x + "%"}
+}).on('click', function(i, row){
+ console.log(i, row);
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/dst.html b/public/bower_components/morris.js/examples/dst.html
new file mode 100644
index 0000000..b81bb24
--- /dev/null
+++ b/public/bower_components/morris.js/examples/dst.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Daylight-savings time</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+// This crosses a DST boundary in the UK.
+Morris.Area({
+ element: 'graph',
+ data: [
+ {x: '2013-03-30 22:00:00', y: 3, z: 3},
+ {x: '2013-03-31 00:00:00', y: 2, z: 0},
+ {x: '2013-03-31 02:00:00', y: 0, z: 2},
+ {x: '2013-03-31 04:00:00', y: 4, z: 4}
+ ],
+ xkey: 'x',
+ ykeys: ['y', 'z'],
+ labels: ['Y', 'Z']
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/events.html b/public/bower_components/morris.js/examples/events.html
new file mode 100644
index 0000000..a383b78
--- /dev/null
+++ b/public/bower_components/morris.js/examples/events.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Time Events</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+var week_data = [
+ {"period": "2011 W27", "licensed": 3407, "sorned": 660},
+ {"period": "2011 W26", "licensed": 3351, "sorned": 629},
+ {"period": "2011 W25", "licensed": 3269, "sorned": 618},
+ {"period": "2011 W24", "licensed": 3246, "sorned": 661},
+ {"period": "2011 W23", "licensed": 3257, "sorned": 667},
+ {"period": "2011 W22", "licensed": 3248, "sorned": 627},
+ {"period": "2011 W21", "licensed": 3171, "sorned": 660},
+ {"period": "2011 W20", "licensed": 3171, "sorned": 676},
+ {"period": "2011 W19", "licensed": 3201, "sorned": 656},
+ {"period": "2011 W18", "licensed": 3215, "sorned": 622},
+ {"period": "2011 W17", "licensed": 3148, "sorned": 632},
+ {"period": "2011 W16", "licensed": 3155, "sorned": 681},
+ {"period": "2011 W15", "licensed": 3190, "sorned": 667},
+ {"period": "2011 W14", "licensed": 3226, "sorned": 620},
+ {"period": "2011 W13", "licensed": 3245, "sorned": null},
+ {"period": "2011 W12", "licensed": 3289, "sorned": null},
+ {"period": "2011 W11", "licensed": 3263, "sorned": null},
+ {"period": "2011 W10", "licensed": 3189, "sorned": null},
+ {"period": "2011 W09", "licensed": 3079, "sorned": null},
+ {"period": "2011 W08", "licensed": 3085, "sorned": null},
+ {"period": "2011 W07", "licensed": 3055, "sorned": null},
+ {"period": "2011 W06", "licensed": 3063, "sorned": null},
+ {"period": "2011 W05", "licensed": 2943, "sorned": null},
+ {"period": "2011 W04", "licensed": 2806, "sorned": null},
+ {"period": "2011 W03", "licensed": 2674, "sorned": null},
+ {"period": "2011 W02", "licensed": 1702, "sorned": null},
+ {"period": "2011 W01", "licensed": 1732, "sorned": null}
+];
+Morris.Line({
+ element: 'graph',
+ data: week_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned'],
+ labels: ['Licensed', 'SORN'],
+ events: [
+ '2011-04',
+ '2011-08'
+ ]
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/goals.html b/public/bower_components/morris.js/examples/goals.html
new file mode 100644
index 0000000..303ed74
--- /dev/null
+++ b/public/bower_components/morris.js/examples/goals.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Value Goals</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+var decimal_data = [];
+for (var x = 0; x <= 360; x += 10) {
+ decimal_data.push({
+ x: x,
+ y: Math.sin(Math.PI * x / 180).toFixed(4)
+ });
+}
+window.m = Morris.Line({
+ element: 'graph',
+ data: decimal_data,
+ xkey: 'x',
+ ykeys: ['y'],
+ labels: ['sin(x)'],
+ parseTime: false,
+ goals: [-1, 0, 1]
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/lib/example.css b/public/bower_components/morris.js/examples/lib/example.css
new file mode 100644
index 0000000..4805c5e
--- /dev/null
+++ b/public/bower_components/morris.js/examples/lib/example.css
@@ -0,0 +1,13 @@
+body {
+ width: 800px;
+ margin: 0 auto;
+}
+#graph {
+ width: 800px;
+ height: 250px;
+ margin: 20px auto 0 auto;
+}
+pre {
+ height: 250px;
+ overflow: auto;
+}
diff --git a/public/bower_components/morris.js/examples/lib/example.js b/public/bower_components/morris.js/examples/lib/example.js
new file mode 100644
index 0000000..80b19f4
--- /dev/null
+++ b/public/bower_components/morris.js/examples/lib/example.js
@@ -0,0 +1,4 @@
+$(function () {
+ eval($('#code').text());
+ prettyPrint();
+}); \ No newline at end of file
diff --git a/public/bower_components/morris.js/examples/months-no-smooth.html b/public/bower_components/morris.js/examples/months-no-smooth.html
new file mode 100644
index 0000000..04b4441
--- /dev/null
+++ b/public/bower_components/morris.js/examples/months-no-smooth.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Formatting Dates with YYYY-MM</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+/* data stolen from http://howmanyleft.co.uk/vehicle/jaguar_'e'_type */
+var month_data = [
+ {"period": "2012-10", "licensed": 3407, "sorned": 660},
+ {"period": "2011-08", "licensed": 3351, "sorned": 629},
+ {"period": "2011-03", "licensed": 3269, "sorned": 618},
+ {"period": "2010-08", "licensed": 3246, "sorned": 661},
+ {"period": "2010-05", "licensed": 3257, "sorned": 667},
+ {"period": "2010-03", "licensed": 3248, "sorned": 627},
+ {"period": "2010-01", "licensed": 3171, "sorned": 660},
+ {"period": "2009-12", "licensed": 3171, "sorned": 676},
+ {"period": "2009-10", "licensed": 3201, "sorned": 656},
+ {"period": "2009-09", "licensed": 3215, "sorned": 622}
+];
+Morris.Line({
+ element: 'graph',
+ data: month_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned'],
+ labels: ['Licensed', 'SORN'],
+ smooth: false
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/negative.html b/public/bower_components/morris.js/examples/negative.html
new file mode 100644
index 0000000..8027c7e
--- /dev/null
+++ b/public/bower_components/morris.js/examples/negative.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Negative values</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+var neg_data = [
+ {"period": "2011-08-12", "a": 100},
+ {"period": "2011-03-03", "a": 75},
+ {"period": "2010-08-08", "a": 50},
+ {"period": "2010-05-10", "a": 25},
+ {"period": "2010-03-14", "a": 0},
+ {"period": "2010-01-10", "a": -25},
+ {"period": "2009-12-10", "a": -50},
+ {"period": "2009-10-07", "a": -75},
+ {"period": "2009-09-25", "a": -100}
+];
+Morris.Line({
+ element: 'graph',
+ data: neg_data,
+ xkey: 'period',
+ ykeys: ['a'],
+ labels: ['Series A'],
+ units: '%'
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/no-grid.html b/public/bower_components/morris.js/examples/no-grid.html
new file mode 100644
index 0000000..1527d8b
--- /dev/null
+++ b/public/bower_components/morris.js/examples/no-grid.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Formatting Dates YYYY-MM-DD</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+/* data stolen from http://howmanyleft.co.uk/vehicle/jaguar_'e'_type */
+var day_data = [
+ {"period": "2012-10-01", "licensed": 3407, "sorned": 660},
+ {"period": "2012-09-30", "licensed": 3351, "sorned": 629},
+ {"period": "2012-09-29", "licensed": 3269, "sorned": 618},
+ {"period": "2012-09-20", "licensed": 3246, "sorned": 661},
+ {"period": "2012-09-19", "licensed": 3257, "sorned": 667},
+ {"period": "2012-09-18", "licensed": 3248, "sorned": 627},
+ {"period": "2012-09-17", "licensed": 3171, "sorned": 660},
+ {"period": "2012-09-16", "licensed": 3171, "sorned": 676},
+ {"period": "2012-09-15", "licensed": 3201, "sorned": 656},
+ {"period": "2012-09-10", "licensed": 3215, "sorned": 622}
+];
+Morris.Line({
+ element: 'graph',
+ grid: false,
+ data: day_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned'],
+ labels: ['Licensed', 'SORN']
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/non-continuous.html b/public/bower_components/morris.js/examples/non-continuous.html
new file mode 100644
index 0000000..921049d
--- /dev/null
+++ b/public/bower_components/morris.js/examples/non-continuous.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Non-continuous data</h1>
+<p>Null series values will break the line when rendering, missing values will be skipped</p>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+/* data stolen from http://howmanyleft.co.uk/vehicle/jaguar_'e'_type */
+var day_data = [
+ {"period": "2012-10-01", "licensed": 3407},
+ {"period": "2012-09-30", "sorned": 0},
+ {"period": "2012-09-29", "sorned": 618},
+ {"period": "2012-09-20", "licensed": 3246, "sorned": 661},
+ {"period": "2012-09-19", "licensed": 3257, "sorned": null},
+ {"period": "2012-09-18", "licensed": 3248, "other": 1000},
+ {"period": "2012-09-17", "sorned": 0},
+ {"period": "2012-09-16", "sorned": 0},
+ {"period": "2012-09-15", "licensed": 3201, "sorned": 656},
+ {"period": "2012-09-10", "licensed": 3215}
+];
+Morris.Line({
+ element: 'graph',
+ data: day_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned', 'other'],
+ labels: ['Licensed', 'SORN', 'Other'],
+ /* custom label formatting with `xLabelFormat` */
+ xLabelFormat: function(d) { return (d.getMonth()+1)+'/'+d.getDate()+'/'+d.getFullYear(); },
+ /* setting `xLabels` is recommended when using xLabelFormat */
+ xLabels: 'day'
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/non-date.html b/public/bower_components/morris.js/examples/non-date.html
new file mode 100644
index 0000000..21a1e20
--- /dev/null
+++ b/public/bower_components/morris.js/examples/non-date.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Formatting Non-date Arbitrary X-axis</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+var day_data = [
+ {"elapsed": "I", "value": 34},
+ {"elapsed": "II", "value": 24},
+ {"elapsed": "III", "value": 3},
+ {"elapsed": "IV", "value": 12},
+ {"elapsed": "V", "value": 13},
+ {"elapsed": "VI", "value": 22},
+ {"elapsed": "VII", "value": 5},
+ {"elapsed": "VIII", "value": 26},
+ {"elapsed": "IX", "value": 12},
+ {"elapsed": "X", "value": 19}
+];
+Morris.Line({
+ element: 'graph',
+ data: day_data,
+ xkey: 'elapsed',
+ ykeys: ['value'],
+ labels: ['value'],
+ parseTime: false
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/quarters.html b/public/bower_components/morris.js/examples/quarters.html
new file mode 100644
index 0000000..bcc0f2d
--- /dev/null
+++ b/public/bower_components/morris.js/examples/quarters.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Formatting Dates with Quarters</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+/* data stolen from http://howmanyleft.co.uk/vehicle/jaguar_e_type */
+var quarter_data = [
+ {"period": "2011 Q3", "licensed": 3407, "sorned": 660},
+ {"period": "2011 Q2", "licensed": 3351, "sorned": 629},
+ {"period": "2011 Q1", "licensed": 3269, "sorned": 618},
+ {"period": "2010 Q4", "licensed": 3246, "sorned": 661},
+ {"period": "2010 Q3", "licensed": 3257, "sorned": 667},
+ {"period": "2010 Q2", "licensed": 3248, "sorned": 627},
+ {"period": "2010 Q1", "licensed": 3171, "sorned": 660},
+ {"period": "2009 Q4", "licensed": 3171, "sorned": 676},
+ {"period": "2009 Q3", "licensed": 3201, "sorned": 656},
+ {"period": "2009 Q2", "licensed": 3215, "sorned": 622},
+ {"period": "2009 Q1", "licensed": 3148, "sorned": 632},
+ {"period": "2008 Q4", "licensed": 3155, "sorned": 681},
+ {"period": "2008 Q3", "licensed": 3190, "sorned": 667},
+ {"period": "2007 Q4", "licensed": 3226, "sorned": 620},
+ {"period": "2006 Q4", "licensed": 3245, "sorned": null},
+ {"period": "2005 Q4", "licensed": 3289, "sorned": null},
+ {"period": "2004 Q4", "licensed": 3263, "sorned": null},
+ {"period": "2003 Q4", "licensed": 3189, "sorned": null},
+ {"period": "2002 Q4", "licensed": 3079, "sorned": null},
+ {"period": "2001 Q4", "licensed": 3085, "sorned": null},
+ {"period": "2000 Q4", "licensed": 3055, "sorned": null},
+ {"period": "1999 Q4", "licensed": 3063, "sorned": null},
+ {"period": "1998 Q4", "licensed": 2943, "sorned": null},
+ {"period": "1997 Q4", "licensed": 2806, "sorned": null},
+ {"period": "1996 Q4", "licensed": 2674, "sorned": null},
+ {"period": "1995 Q4", "licensed": 1702, "sorned": null},
+ {"period": "1994 Q4", "licensed": 1732, "sorned": null}
+];
+Morris.Line({
+ element: 'graph',
+ data: quarter_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned'],
+ labels: ['Licensed', 'SORN']
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/resize.html b/public/bower_components/morris.js/examples/resize.html
new file mode 100644
index 0000000..ef82471
--- /dev/null
+++ b/public/bower_components/morris.js/examples/resize.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+ <style>
+ body { width: 100%; }
+ #graph { width: 100%; }
+ </style>
+</head>
+<body>
+<h1>Formatting Dates YYYY-MM-DD</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+/* data stolen from http://howmanyleft.co.uk/vehicle/jaguar_'e'_type */
+var day_data = [
+ {"period": "2012-10-01", "licensed": 3407, "sorned": 660},
+ {"period": "2012-09-30", "licensed": 3351, "sorned": 629},
+ {"period": "2012-09-29", "licensed": 3269, "sorned": 618},
+ {"period": "2012-09-20", "licensed": 3246, "sorned": 661},
+ {"period": "2012-09-19", "licensed": 3257, "sorned": 667},
+ {"period": "2012-09-18", "licensed": 3248, "sorned": 627},
+ {"period": "2012-09-17", "licensed": 3171, "sorned": 660},
+ {"period": "2012-09-16", "licensed": 3171, "sorned": 676},
+ {"period": "2012-09-15", "licensed": 3201, "sorned": 656},
+ {"period": "2012-09-10", "licensed": 3215, "sorned": 622}
+];
+Morris.Line({
+ element: 'graph',
+ data: day_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned'],
+ labels: ['Licensed', 'SORN'],
+ resize: true
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/stacked_bars.html b/public/bower_components/morris.js/examples/stacked_bars.html
new file mode 100644
index 0000000..81568e6
--- /dev/null
+++ b/public/bower_components/morris.js/examples/stacked_bars.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Stacked Bars chart</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+// Use Morris.Bar
+Morris.Bar({
+ element: 'graph',
+ data: [
+ {x: '2011 Q1', y: 3, z: 2, a: 3},
+ {x: '2011 Q2', y: 2, z: null, a: 1},
+ {x: '2011 Q3', y: 0, z: 2, a: 4},
+ {x: '2011 Q4', y: 2, z: 4, a: 3}
+ ],
+ xkey: 'x',
+ ykeys: ['y', 'z', 'a'],
+ labels: ['Y', 'Z', 'A'],
+ stacked: true
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/timestamps.html b/public/bower_components/morris.js/examples/timestamps.html
new file mode 100644
index 0000000..efddbe9
--- /dev/null
+++ b/public/bower_components/morris.js/examples/timestamps.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Timestamps</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+/* data stolen from http://howmanyleft.co.uk/vehicle/jaguar_'e'_type */
+var timestamp_data = [
+ {"period": 1349046000000, "licensed": 3407, "sorned": 660},
+ {"period": 1313103600000, "licensed": 3351, "sorned": 629},
+ {"period": 1299110400000, "licensed": 3269, "sorned": 618},
+ {"period": 1281222000000, "licensed": 3246, "sorned": 661},
+ {"period": 1273446000000, "licensed": 3257, "sorned": 667},
+ {"period": 1268524800000, "licensed": 3248, "sorned": 627},
+ {"period": 1263081600000, "licensed": 3171, "sorned": 660},
+ {"period": 1260403200000, "licensed": 3171, "sorned": 676},
+ {"period": 1254870000000, "licensed": 3201, "sorned": 656},
+ {"period": 1253833200000, "licensed": 3215, "sorned": 622}
+];
+Morris.Line({
+ element: 'graph',
+ data: timestamp_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned'],
+ labels: ['Licensed', 'SORN'],
+ dateFormat: function (x) { return new Date(x).toDateString(); }
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/updating.html b/public/bower_components/morris.js/examples/updating.html
new file mode 100644
index 0000000..6267f3f
--- /dev/null
+++ b/public/bower_components/morris.js/examples/updating.html
@@ -0,0 +1,49 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Updating data</h1>
+<div id="graph"></div>
+<div id="reloadStatus">
+<pre id="code" class="prettyprint linenums">
+
+var nReloads = 0;
+function data(offset) {
+ var ret = [];
+ for (var x = 0; x <= 360; x += 10) {
+ var v = (offset + x) % 360;
+ ret.push({
+ x: x,
+ y: Math.sin(Math.PI * v / 180).toFixed(4),
+ z: Math.cos(Math.PI * v / 180).toFixed(4)
+ });
+ }
+ return ret;
+}
+var graph = Morris.Line({
+ element: 'graph',
+ data: data(0),
+ xkey: 'x',
+ ykeys: ['y', 'z'],
+ labels: ['sin()', 'cos()'],
+ parseTime: false,
+ ymin: -1.0,
+ ymax: 1.0,
+ hideHover: true
+});
+function update() {
+ nReloads++;
+ graph.setData(data(5 * nReloads));
+ $('#reloadStatus').text(nReloads + ' reloads');
+}
+setInterval(update, 100);
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/weeks.html b/public/bower_components/morris.js/examples/weeks.html
new file mode 100644
index 0000000..bcb06bd
--- /dev/null
+++ b/public/bower_components/morris.js/examples/weeks.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Formatting Dates With Weeks</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+var week_data = [
+ {"period": "2011 W27", "licensed": 3407, "sorned": 660},
+ {"period": "2011 W26", "licensed": 3351, "sorned": 629},
+ {"period": "2011 W25", "licensed": 3269, "sorned": 618},
+ {"period": "2011 W24", "licensed": 3246, "sorned": 661},
+ {"period": "2011 W23", "licensed": 3257, "sorned": 667},
+ {"period": "2011 W22", "licensed": 3248, "sorned": 627},
+ {"period": "2011 W21", "licensed": 3171, "sorned": 660},
+ {"period": "2011 W20", "licensed": 3171, "sorned": 676},
+ {"period": "2011 W19", "licensed": 3201, "sorned": 656},
+ {"period": "2011 W18", "licensed": 3215, "sorned": 622},
+ {"period": "2011 W17", "licensed": 3148, "sorned": 632},
+ {"period": "2011 W16", "licensed": 3155, "sorned": 681},
+ {"period": "2011 W15", "licensed": 3190, "sorned": 667},
+ {"period": "2011 W14", "licensed": 3226, "sorned": 620},
+ {"period": "2011 W13", "licensed": 3245, "sorned": null},
+ {"period": "2011 W12", "licensed": 3289, "sorned": null},
+ {"period": "2011 W11", "licensed": 3263, "sorned": null},
+ {"period": "2011 W10", "licensed": 3189, "sorned": null},
+ {"period": "2011 W09", "licensed": 3079, "sorned": null},
+ {"period": "2011 W08", "licensed": 3085, "sorned": null},
+ {"period": "2011 W07", "licensed": 3055, "sorned": null},
+ {"period": "2011 W06", "licensed": 3063, "sorned": null},
+ {"period": "2011 W05", "licensed": 2943, "sorned": null},
+ {"period": "2011 W04", "licensed": 2806, "sorned": null},
+ {"period": "2011 W03", "licensed": 2674, "sorned": null},
+ {"period": "2011 W02", "licensed": 1702, "sorned": null},
+ {"period": "2011 W01", "licensed": 1732, "sorned": null}
+];
+Morris.Line({
+ element: 'graph',
+ data: week_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned'],
+ labels: ['Licensed', 'SORN']
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/examples/years.html b/public/bower_components/morris.js/examples/years.html
new file mode 100644
index 0000000..761d8ed
--- /dev/null
+++ b/public/bower_components/morris.js/examples/years.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<head>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.2/raphael-min.js"></script>
+ <script src="../morris.js"></script>
+ <script src="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.js"></script>
+ <script src="lib/example.js"></script>
+ <link rel="stylesheet" href="lib/example.css">
+ <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/prettify/r224/prettify.min.css">
+ <link rel="stylesheet" href="../morris.css">
+</head>
+<body>
+<h1>Formatting Dates YYYY</h1>
+<div id="graph"></div>
+<pre id="code" class="prettyprint linenums">
+/* data stolen from http://howmanyleft.co.uk/vehicle/jaguar_'e'_type */
+var year_data = [
+ {"period": "2012", "licensed": 3407, "sorned": 660},
+ {"period": "2011", "licensed": 3351, "sorned": 629},
+ {"period": "2010", "licensed": 3269, "sorned": 618},
+ {"period": "2009", "licensed": 3246, "sorned": 661},
+ {"period": "2008", "licensed": 3257, "sorned": 667},
+ {"period": "2007", "licensed": 3248, "sorned": 627},
+ {"period": "2006", "licensed": 3171, "sorned": 660},
+ {"period": "2005", "licensed": 3171, "sorned": 676},
+ {"period": "2004", "licensed": 3201, "sorned": 656},
+ {"period": "2003", "licensed": 3215, "sorned": 622}
+];
+Morris.Line({
+ element: 'graph',
+ data: year_data,
+ xkey: 'period',
+ ykeys: ['licensed', 'sorned'],
+ labels: ['Licensed', 'SORN']
+});
+</pre>
+</body>
diff --git a/public/bower_components/morris.js/less/morris.core.less b/public/bower_components/morris.js/less/morris.core.less
new file mode 100644
index 0000000..1d27211
--- /dev/null
+++ b/public/bower_components/morris.js/less/morris.core.less
@@ -0,0 +1,27 @@
+.morris-hover {
+ position: absolute;
+ z-index: 1000;
+
+ &.morris-default-style {
+ border-radius: 10px;
+ padding: 6px;
+ color: #666;
+ background: rgba(255, 255, 255, 0.8);
+ border: solid 2px rgba(230, 230, 230, 0.8);
+
+ font-family: sans-serif;
+ font-size: 12px;
+ text-align: center;
+
+ .morris-hover-row-label {
+ font-weight: bold;
+ margin: 0.25em 0;
+ }
+
+ .morris-hover-point {
+ white-space: nowrap;
+ margin: 0.1em 0;
+ }
+ }
+
+}
diff --git a/public/bower_components/morris.js/lib/morris.area.coffee b/public/bower_components/morris.js/lib/morris.area.coffee
new file mode 100644
index 0000000..86f8595
--- /dev/null
+++ b/public/bower_components/morris.js/lib/morris.area.coffee
@@ -0,0 +1,66 @@
+class Morris.Area extends Morris.Line
+ # Initialise
+ #
+ areaDefaults =
+ fillOpacity: 'auto'
+ behaveLikeLine: false
+
+ constructor: (options) ->
+ return new Morris.Area(options) unless (@ instanceof Morris.Area)
+ areaOptions = $.extend {}, areaDefaults, options
+
+ @cumulative = not areaOptions.behaveLikeLine
+
+ if areaOptions.fillOpacity is 'auto'
+ areaOptions.fillOpacity = if areaOptions.behaveLikeLine then .8 else 1
+
+ super(areaOptions)
+
+ # calculate series data point coordinates
+ #
+ # @private
+ calcPoints: ->
+ for row in @data
+ row._x = @transX(row.x)
+ total = 0
+ row._y = for y in row.y
+ if @options.behaveLikeLine
+ @transY(y)
+ else
+ total += (y || 0)
+ @transY(total)
+ row._ymax = Math.max row._y...
+
+ # draw the data series
+ #
+ # @private
+ drawSeries: ->
+ @seriesPoints = []
+ if @options.behaveLikeLine
+ range = [0..@options.ykeys.length-1]
+ else
+ range = [@options.ykeys.length-1..0]
+
+ for i in range
+ @_drawFillFor i
+ @_drawLineFor i
+ @_drawPointFor i
+
+ _drawFillFor: (index) ->
+ path = @paths[index]
+ if path isnt null
+ path = path + "L#{@transX(@xmax)},#{@bottom}L#{@transX(@xmin)},#{@bottom}Z"
+ @drawFilledPath path, @fillForSeries(index)
+
+ fillForSeries: (i) ->
+ color = Raphael.rgb2hsl @colorFor(@data[i], i, 'line')
+ Raphael.hsl(
+ color.h,
+ if @options.behaveLikeLine then color.s * 0.9 else color.s * 0.75,
+ Math.min(0.98, if @options.behaveLikeLine then color.l * 1.2 else color.l * 1.25))
+
+ drawFilledPath: (path, fill) ->
+ @raphael.path(path)
+ .attr('fill', fill)
+ .attr('fill-opacity', @options.fillOpacity)
+ .attr('stroke', 'none')
diff --git a/public/bower_components/morris.js/lib/morris.bar.coffee b/public/bower_components/morris.js/lib/morris.bar.coffee
new file mode 100644
index 0000000..86fb32b
--- /dev/null
+++ b/public/bower_components/morris.js/lib/morris.bar.coffee
@@ -0,0 +1,208 @@
+class Morris.Bar extends Morris.Grid
+ constructor: (options) ->
+ return new Morris.Bar(options) unless (@ instanceof Morris.Bar)
+ super($.extend {}, options, parseTime: false)
+
+ init: ->
+ @cumulative = @options.stacked
+
+ if @options.hideHover isnt 'always'
+ @hover = new Morris.Hover(parent: @el)
+ @on('hovermove', @onHoverMove)
+ @on('hoverout', @onHoverOut)
+ @on('gridclick', @onGridClick)
+
+ # Default configuration
+ #
+ defaults:
+ barSizeRatio: 0.75
+ barGap: 3
+ barColors: [
+ '#0b62a4'
+ '#7a92a3'
+ '#4da74d'
+ '#afd8f8'
+ '#edc240'
+ '#cb4b4b'
+ '#9440ed'
+ ],
+ barOpacity: 1.0
+ barRadius: [0, 0, 0, 0]
+ xLabelMargin: 50
+
+ # Do any size-related calculations
+ #
+ # @private
+ calc: ->
+ @calcBars()
+ if @options.hideHover is false
+ @hover.update(@hoverContentForRow(@data.length - 1)...)
+
+ # calculate series data bars coordinates and sizes
+ #
+ # @private
+ calcBars: ->
+ for row, idx in @data
+ row._x = @left + @width * (idx + 0.5) / @data.length
+ row._y = for y in row.y
+ if y? then @transY(y) else null
+
+ # Draws the bar chart.
+ #
+ draw: ->
+ @drawXAxis() if @options.axes in [true, 'both', 'x']
+ @drawSeries()
+
+ # draw the x-axis labels
+ #
+ # @private
+ drawXAxis: ->
+ # draw x axis labels
+ ypos = @bottom + (@options.xAxisLabelTopPadding || @options.padding / 2)
+ prevLabelMargin = null
+ prevAngleMargin = null
+ for i in [0...@data.length]
+ row = @data[@data.length - 1 - i]
+ label = @drawXAxisLabel(row._x, ypos, row.label)
+ textBox = label.getBBox()
+ label.transform("r#{-@options.xLabelAngle}")
+ labelBox = label.getBBox()
+ label.transform("t0,#{labelBox.height / 2}...")
+ if @options.xLabelAngle != 0
+ offset = -0.5 * textBox.width *
+ Math.cos(@options.xLabelAngle * Math.PI / 180.0)
+ label.transform("t#{offset},0...")
+ # try to avoid overlaps
+ if (not prevLabelMargin? or
+ prevLabelMargin >= labelBox.x + labelBox.width or
+ prevAngleMargin? and prevAngleMargin >= labelBox.x) and
+ labelBox.x >= 0 and (labelBox.x + labelBox.width) < @el.width()
+ if @options.xLabelAngle != 0
+ margin = 1.25 * @options.gridTextSize /
+ Math.sin(@options.xLabelAngle * Math.PI / 180.0)
+ prevAngleMargin = labelBox.x - margin
+ prevLabelMargin = labelBox.x - @options.xLabelMargin
+ else
+ label.remove()
+
+ # draw the data series
+ #
+ # @private
+ drawSeries: ->
+ groupWidth = @width / @options.data.length
+ numBars = if @options.stacked then 1 else @options.ykeys.length
+ barWidth = (groupWidth * @options.barSizeRatio - @options.barGap * (numBars - 1)) / numBars
+ barWidth = Math.min(barWidth, @options.barSize) if @options.barSize
+ spaceLeft = groupWidth - barWidth * numBars - @options.barGap * (numBars - 1)
+ leftPadding = spaceLeft / 2
+ zeroPos = if @ymin <= 0 and @ymax >= 0 then @transY(0) else null
+ @bars = for row, idx in @data
+ lastTop = 0
+ for ypos, sidx in row._y
+ if ypos != null
+ if zeroPos
+ top = Math.min(ypos, zeroPos)
+ bottom = Math.max(ypos, zeroPos)
+ else
+ top = ypos
+ bottom = @bottom
+
+ left = @left + idx * groupWidth + leftPadding
+ left += sidx * (barWidth + @options.barGap) unless @options.stacked
+ size = bottom - top
+
+ if @options.verticalGridCondition and @options.verticalGridCondition(row.x)
+ @drawBar(@left + idx * groupWidth, @top, groupWidth, Math.abs(@top - @bottom), @options.verticalGridColor, @options.verticalGridOpacity, @options.barRadius)
+
+ top -= lastTop if @options.stacked
+ @drawBar(left, top, barWidth, size, @colorFor(row, sidx, 'bar'),
+ @options.barOpacity, @options.barRadius)
+
+ lastTop += size
+ else
+ null
+
+ # @private
+ #
+ # @param row [Object] row data
+ # @param sidx [Number] series index
+ # @param type [String] "bar", "hover" or "label"
+ colorFor: (row, sidx, type) ->
+ if typeof @options.barColors is 'function'
+ r = { x: row.x, y: row.y[sidx], label: row.label }
+ s = { index: sidx, key: @options.ykeys[sidx], label: @options.labels[sidx] }
+ @options.barColors.call(@, r, s, type)
+ else
+ @options.barColors[sidx % @options.barColors.length]
+
+ # hit test - returns the index of the row at the given x-coordinate
+ #
+ hitTest: (x) ->
+ return null if @data.length == 0
+ x = Math.max(Math.min(x, @right), @left)
+ Math.min(@data.length - 1,
+ Math.floor((x - @left) / (@width / @data.length)))
+
+ # click on grid event handler
+ #
+ # @private
+ onGridClick: (x, y) =>
+ index = @hitTest(x)
+ @fire 'click', index, @data[index].src, x, y
+
+ # hover movement event handler
+ #
+ # @private
+ onHoverMove: (x, y) =>
+ index = @hitTest(x)
+ @hover.update(@hoverContentForRow(index)...)
+
+ # hover out event handler
+ #
+ # @private
+ onHoverOut: =>
+ if @options.hideHover isnt false
+ @hover.hide()
+
+ # hover content for a point
+ #
+ # @private
+ hoverContentForRow: (index) ->
+ row = @data[index]
+ content = "<div class='morris-hover-row-label'>#{row.label}</div>"
+ for y, j in row.y
+ content += """
+ <div class='morris-hover-point' style='color: #{@colorFor(row, j, 'label')}'>
+ #{@options.labels[j]}:
+ #{@yLabelFormat(y)}
+ </div>
+ """
+ if typeof @options.hoverCallback is 'function'
+ content = @options.hoverCallback(index, @options, content, row.src)
+ x = @left + (index + 0.5) * @width / @data.length
+ [content, x]
+
+ drawXAxisLabel: (xPos, yPos, text) ->
+ label = @raphael.text(xPos, yPos, text)
+ .attr('font-size', @options.gridTextSize)
+ .attr('font-family', @options.gridTextFamily)
+ .attr('font-weight', @options.gridTextWeight)
+ .attr('fill', @options.gridTextColor)
+
+ drawBar: (xPos, yPos, width, height, barColor, opacity, radiusArray) ->
+ maxRadius = Math.max(radiusArray...)
+ if maxRadius == 0 or maxRadius > height
+ path = @raphael.rect(xPos, yPos, width, height)
+ else
+ path = @raphael.path @roundedRect(xPos, yPos, width, height, radiusArray)
+ path
+ .attr('fill', barColor)
+ .attr('fill-opacity', opacity)
+ .attr('stroke', 'none')
+
+ roundedRect: (x, y, w, h, r = [0,0,0,0]) ->
+ [ "M", x, r[0] + y, "Q", x, y, x + r[0], y,
+ "L", x + w - r[1], y, "Q", x + w, y, x + w, y + r[1],
+ "L", x + w, y + h - r[2], "Q", x + w, y + h, x + w - r[2], y + h,
+ "L", x + r[3], y + h, "Q", x, y + h, x, y + h - r[3], "Z" ]
+
diff --git a/public/bower_components/morris.js/lib/morris.coffee b/public/bower_components/morris.js/lib/morris.coffee
new file mode 100644
index 0000000..f2cd2df
--- /dev/null
+++ b/public/bower_components/morris.js/lib/morris.coffee
@@ -0,0 +1,43 @@
+Morris = window.Morris = {}
+
+$ = jQuery
+
+# Very simple event-emitter class.
+#
+# @private
+class Morris.EventEmitter
+ on: (name, handler) ->
+ unless @handlers?
+ @handlers = {}
+ unless @handlers[name]?
+ @handlers[name] = []
+ @handlers[name].push(handler)
+ @
+
+ fire: (name, args...) ->
+ if @handlers? and @handlers[name]?
+ for handler in @handlers[name]
+ handler(args...)
+
+# Make long numbers prettier by inserting commas.
+#
+# @example
+# Morris.commas(1234567) -> '1,234,567'
+Morris.commas = (num) ->
+ if num?
+ ret = if num < 0 then "-" else ""
+ absnum = Math.abs(num)
+ intnum = Math.floor(absnum).toFixed(0)
+ ret += intnum.replace(/(?=(?:\d{3})+$)(?!^)/g, ',')
+ strabsnum = absnum.toString()
+ if strabsnum.length > intnum.length
+ ret += strabsnum.slice(intnum.length)
+ ret
+ else
+ '-'
+
+# Zero-pad numbers to two characters wide.
+#
+# @example
+# Morris.pad2(1) -> '01'
+Morris.pad2 = (number) -> (if number < 10 then '0' else '') + number
diff --git a/public/bower_components/morris.js/lib/morris.donut.coffee b/public/bower_components/morris.js/lib/morris.donut.coffee
new file mode 100644
index 0000000..e40314d
--- /dev/null
+++ b/public/bower_components/morris.js/lib/morris.donut.coffee
@@ -0,0 +1,213 @@
+# Donut charts.
+#
+# @example
+# Morris.Donut({
+# el: $('#donut-container'),
+# data: [
+# { label: 'yin', value: 50 },
+# { label: 'yang', value: 50 }
+# ]
+# });
+class Morris.Donut extends Morris.EventEmitter
+ defaults:
+ colors: [
+ '#0B62A4'
+ '#3980B5'
+ '#679DC6'
+ '#95BBD7'
+ '#B0CCE1'
+ '#095791'
+ '#095085'
+ '#083E67'
+ '#052C48'
+ '#042135'
+ ],
+ backgroundColor: '#FFFFFF',
+ labelColor: '#000000',
+ formatter: Morris.commas
+ resize: false
+
+ # Create and render a donut chart.
+ #
+ constructor: (options) ->
+ return new Morris.Donut(options) unless (@ instanceof Morris.Donut)
+ @options = $.extend {}, @defaults, options
+
+ if typeof options.element is 'string'
+ @el = $ document.getElementById(options.element)
+ else
+ @el = $ options.element
+
+ if @el == null || @el.length == 0
+ throw new Error("Graph placeholder not found.")
+
+ # bail if there's no data
+ if options.data is undefined or options.data.length is 0
+ return
+
+ @raphael = new Raphael(@el[0])
+
+ if @options.resize
+ $(window).bind 'resize', (evt) =>
+ if @timeoutId?
+ window.clearTimeout @timeoutId
+ @timeoutId = window.setTimeout @resizeHandler, 100
+
+ @setData options.data
+
+ # Clear and redraw the chart.
+ redraw: ->
+ @raphael.clear()
+
+ cx = @el.width() / 2
+ cy = @el.height() / 2
+ w = (Math.min(cx, cy) - 10) / 3
+
+ total = 0
+ total += value for value in @values
+
+ min = 5 / (2 * w)
+ C = 1.9999 * Math.PI - min * @data.length
+
+ last = 0
+ idx = 0
+ @segments = []
+ for value, i in @values
+ next = last + min + C * (value / total)
+ seg = new Morris.DonutSegment(
+ cx, cy, w*2, w, last, next,
+ @data[i].color || @options.colors[idx % @options.colors.length],
+ @options.backgroundColor, idx, @raphael)
+ seg.render()
+ @segments.push seg
+ seg.on 'hover', @select
+ seg.on 'click', @click
+ last = next
+ idx += 1
+
+ @text1 = @drawEmptyDonutLabel(cx, cy - 10, @options.labelColor, 15, 800)
+ @text2 = @drawEmptyDonutLabel(cx, cy + 10, @options.labelColor, 14)
+
+ max_value = Math.max @values...
+ idx = 0
+ for value in @values
+ if value == max_value
+ @select idx
+ break
+ idx += 1
+
+ setData: (data) ->
+ @data = data
+ @values = (parseFloat(row.value) for row in @data)
+ @redraw()
+
+ # @private
+ click: (idx) =>
+ @fire 'click', idx, @data[idx]
+
+ # Select the segment at the given index.
+ select: (idx) =>
+ s.deselect() for s in @segments
+ segment = @segments[idx]
+ segment.select()
+ row = @data[idx]
+ @setLabels(row.label, @options.formatter(row.value, row))
+
+
+
+ # @private
+ setLabels: (label1, label2) ->
+ inner = (Math.min(@el.width() / 2, @el.height() / 2) - 10) * 2 / 3
+ maxWidth = 1.8 * inner
+ maxHeightTop = inner / 2
+ maxHeightBottom = inner / 3
+ @text1.attr(text: label1, transform: '')
+ text1bbox = @text1.getBBox()
+ text1scale = Math.min(maxWidth / text1bbox.width, maxHeightTop / text1bbox.height)
+ @text1.attr(transform: "S#{text1scale},#{text1scale},#{text1bbox.x + text1bbox.width / 2},#{text1bbox.y + text1bbox.height}")
+ @text2.attr(text: label2, transform: '')
+ text2bbox = @text2.getBBox()
+ text2scale = Math.min(maxWidth / text2bbox.width, maxHeightBottom / text2bbox.height)
+ @text2.attr(transform: "S#{text2scale},#{text2scale},#{text2bbox.x + text2bbox.width / 2},#{text2bbox.y}")
+
+ drawEmptyDonutLabel: (xPos, yPos, color, fontSize, fontWeight) ->
+ text = @raphael.text(xPos, yPos, '')
+ .attr('font-size', fontSize)
+ .attr('fill', color)
+ text.attr('font-weight', fontWeight) if fontWeight?
+ return text
+
+ resizeHandler: =>
+ @timeoutId = null
+ @raphael.setSize @el.width(), @el.height()
+ @redraw()
+
+
+# A segment within a donut chart.
+#
+# @private
+class Morris.DonutSegment extends Morris.EventEmitter
+ constructor: (@cx, @cy, @inner, @outer, p0, p1, @color, @backgroundColor, @index, @raphael) ->
+ @sin_p0 = Math.sin(p0)
+ @cos_p0 = Math.cos(p0)
+ @sin_p1 = Math.sin(p1)
+ @cos_p1 = Math.cos(p1)
+ @is_long = if (p1 - p0) > Math.PI then 1 else 0
+ @path = @calcSegment(@inner + 3, @inner + @outer - 5)
+ @selectedPath = @calcSegment(@inner + 3, @inner + @outer)
+ @hilight = @calcArc(@inner)
+
+ calcArcPoints: (r) ->
+ return [
+ @cx + r * @sin_p0,
+ @cy + r * @cos_p0,
+ @cx + r * @sin_p1,
+ @cy + r * @cos_p1]
+
+ calcSegment: (r1, r2) ->
+ [ix0, iy0, ix1, iy1] = @calcArcPoints(r1)
+ [ox0, oy0, ox1, oy1] = @calcArcPoints(r2)
+ return (
+ "M#{ix0},#{iy0}" +
+ "A#{r1},#{r1},0,#{@is_long},0,#{ix1},#{iy1}" +
+ "L#{ox1},#{oy1}" +
+ "A#{r2},#{r2},0,#{@is_long},1,#{ox0},#{oy0}" +
+ "Z")
+
+ calcArc: (r) ->
+ [ix0, iy0, ix1, iy1] = @calcArcPoints(r)
+ return (
+ "M#{ix0},#{iy0}" +
+ "A#{r},#{r},0,#{@is_long},0,#{ix1},#{iy1}")
+
+ render: ->
+ @arc = @drawDonutArc(@hilight, @color)
+ @seg = @drawDonutSegment(
+ @path,
+ @color,
+ @backgroundColor,
+ => @fire('hover', @index),
+ => @fire('click', @index)
+ )
+
+ drawDonutArc: (path, color) ->
+ @raphael.path(path)
+ .attr(stroke: color, 'stroke-width': 2, opacity: 0)
+
+ drawDonutSegment: (path, fillColor, strokeColor, hoverFunction, clickFunction) ->
+ @raphael.path(path)
+ .attr(fill: fillColor, stroke: strokeColor, 'stroke-width': 3)
+ .hover(hoverFunction)
+ .click(clickFunction)
+
+ select: =>
+ unless @selected
+ @seg.animate(path: @selectedPath, 150, '<>')
+ @arc.animate(opacity: 1, 150, '<>')
+ @selected = true
+
+ deselect: =>
+ if @selected
+ @seg.animate(path: @path, 150, '<>')
+ @arc.animate(opacity: 0, 150, '<>')
+ @selected = false
diff --git a/public/bower_components/morris.js/lib/morris.grid.coffee b/public/bower_components/morris.js/lib/morris.grid.coffee
new file mode 100644
index 0000000..db0882c
--- /dev/null
+++ b/public/bower_components/morris.js/lib/morris.grid.coffee
@@ -0,0 +1,499 @@
+class Morris.Grid extends Morris.EventEmitter
+ # A generic pair of axes for line/area/bar charts.
+ #
+ # Draws grid lines and axis labels.
+ #
+ constructor: (options) ->
+ # find the container to draw the graph in
+ if typeof options.element is 'string'
+ @el = $ document.getElementById(options.element)
+ else
+ @el = $ options.element
+ if not @el? or @el.length == 0
+ throw new Error("Graph container element not found")
+
+ if @el.css('position') == 'static'
+ @el.css('position', 'relative')
+
+ @options = $.extend {}, @gridDefaults, (@defaults || {}), options
+
+ # backwards compatibility for units -> postUnits
+ if typeof @options.units is 'string'
+ @options.postUnits = options.units
+
+ # the raphael drawing instance
+ @raphael = new Raphael(@el[0])
+
+ # some redraw stuff
+ @elementWidth = null
+ @elementHeight = null
+ @dirty = false
+
+ # range selection
+ @selectFrom = null
+
+ # more stuff
+ @init() if @init
+
+ # load data
+ @setData @options.data
+
+ # hover
+ @el.bind 'mousemove', (evt) =>
+ offset = @el.offset()
+ x = evt.pageX - offset.left
+ if @selectFrom
+ left = @data[@hitTest(Math.min(x, @selectFrom))]._x
+ right = @data[@hitTest(Math.max(x, @selectFrom))]._x
+ width = right - left
+ @selectionRect.attr({ x: left, width: width })
+ else
+ @fire 'hovermove', x, evt.pageY - offset.top
+
+ @el.bind 'mouseleave', (evt) =>
+ if @selectFrom
+ @selectionRect.hide()
+ @selectFrom = null
+ @fire 'hoverout'
+
+ @el.bind 'touchstart touchmove touchend', (evt) =>
+ touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0]
+ offset = @el.offset()
+ @fire 'hovermove', touch.pageX - offset.left, touch.pageY - offset.top
+
+ @el.bind 'click', (evt) =>
+ offset = @el.offset()
+ @fire 'gridclick', evt.pageX - offset.left, evt.pageY - offset.top
+
+ if @options.rangeSelect
+ @selectionRect = @raphael.rect(0, 0, 0, @el.innerHeight())
+ .attr({ fill: @options.rangeSelectColor, stroke: false })
+ .toBack()
+ .hide()
+
+ @el.bind 'mousedown', (evt) =>
+ offset = @el.offset()
+ @startRange evt.pageX - offset.left
+
+ @el.bind 'mouseup', (evt) =>
+ offset = @el.offset()
+ @endRange evt.pageX - offset.left
+ @fire 'hovermove', evt.pageX - offset.left, evt.pageY - offset.top
+
+ if @options.resize
+ $(window).bind 'resize', (evt) =>
+ if @timeoutId?
+ window.clearTimeout @timeoutId
+ @timeoutId = window.setTimeout @resizeHandler, 100
+
+ # Disable tap highlight on iOS.
+ @el.css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)')
+
+ @postInit() if @postInit
+
+ # Default options
+ #
+ gridDefaults:
+ dateFormat: null
+ axes: true
+ grid: true
+ gridLineColor: '#aaa'
+ gridStrokeWidth: 0.5
+ gridTextColor: '#888'
+ gridTextSize: 12
+ gridTextFamily: 'sans-serif'
+ gridTextWeight: 'normal'
+ hideHover: false
+ yLabelFormat: null
+ xLabelAngle: 0
+ numLines: 5
+ padding: 25
+ parseTime: true
+ postUnits: ''
+ preUnits: ''
+ ymax: 'auto'
+ ymin: 'auto 0'
+ goals: []
+ goalStrokeWidth: 1.0
+ goalLineColors: [
+ '#666633'
+ '#999966'
+ '#cc6666'
+ '#663333'
+ ]
+ events: []
+ eventStrokeWidth: 1.0
+ eventLineColors: [
+ '#005a04'
+ '#ccffbb'
+ '#3a5f0b'
+ '#005502'
+ ]
+ rangeSelect: null
+ rangeSelectColor: '#eef'
+ resize: false
+
+ # Update the data series and redraw the chart.
+ #
+ setData: (data, redraw = true) ->
+ @options.data = data
+
+ if !data? or data.length == 0
+ @data = []
+ @raphael.clear()
+ @hover.hide() if @hover?
+ return
+
+ ymax = if @cumulative then 0 else null
+ ymin = if @cumulative then 0 else null
+
+ if @options.goals.length > 0
+ minGoal = Math.min @options.goals...
+ maxGoal = Math.max @options.goals...
+ ymin = if ymin? then Math.min(ymin, minGoal) else minGoal
+ ymax = if ymax? then Math.max(ymax, maxGoal) else maxGoal
+
+ @data = for row, index in data
+ ret = {src: row}
+
+ ret.label = row[@options.xkey]
+ if @options.parseTime
+ ret.x = Morris.parseDate(ret.label)
+ if @options.dateFormat
+ ret.label = @options.dateFormat ret.x
+ else if typeof ret.label is 'number'
+ ret.label = new Date(ret.label).toString()
+ else
+ ret.x = index
+ if @options.xLabelFormat
+ ret.label = @options.xLabelFormat ret
+ total = 0
+ ret.y = for ykey, idx in @options.ykeys
+ yval = row[ykey]
+ yval = parseFloat(yval) if typeof yval is 'string'
+ yval = null if yval? and typeof yval isnt 'number'
+ if yval?
+ if @cumulative
+ total += yval
+ else
+ if ymax?
+ ymax = Math.max(yval, ymax)
+ ymin = Math.min(yval, ymin)
+ else
+ ymax = ymin = yval
+ if @cumulative and total?
+ ymax = Math.max(total, ymax)
+ ymin = Math.min(total, ymin)
+ yval
+ ret
+
+ if @options.parseTime
+ @data = @data.sort (a, b) -> (a.x > b.x) - (b.x > a.x)
+
+ # calculate horizontal range of the graph
+ @xmin = @data[0].x
+ @xmax = @data[@data.length - 1].x
+
+ @events = []
+ if @options.events.length > 0
+ if @options.parseTime
+ @events = (Morris.parseDate(e) for e in @options.events)
+ else
+ @events = @options.events
+ @xmax = Math.max(@xmax, Math.max(@events...))
+ @xmin = Math.min(@xmin, Math.min(@events...))
+
+ if @xmin is @xmax
+ @xmin -= 1
+ @xmax += 1
+
+ @ymin = @yboundary('min', ymin)
+ @ymax = @yboundary('max', ymax)
+
+ if @ymin is @ymax
+ @ymin -= 1 if ymin
+ @ymax += 1
+
+ if @options.axes in [true, 'both', 'y'] or @options.grid is true
+ if (@options.ymax == @gridDefaults.ymax and
+ @options.ymin == @gridDefaults.ymin)
+ # calculate 'magic' grid placement
+ @grid = @autoGridLines(@ymin, @ymax, @options.numLines)
+ @ymin = Math.min(@ymin, @grid[0])
+ @ymax = Math.max(@ymax, @grid[@grid.length - 1])
+ else
+ step = (@ymax - @ymin) / (@options.numLines - 1)
+ @grid = (y for y in [@ymin..@ymax] by step)
+
+ @dirty = true
+ @redraw() if redraw
+
+ yboundary: (boundaryType, currentValue) ->
+ boundaryOption = @options["y#{boundaryType}"]
+ if typeof boundaryOption is 'string'
+ if boundaryOption[0..3] is 'auto'
+ if boundaryOption.length > 5
+ suggestedValue = parseInt(boundaryOption[5..], 10)
+ return suggestedValue unless currentValue?
+ Math[boundaryType](currentValue, suggestedValue)
+ else
+ if currentValue? then currentValue else 0
+ else
+ parseInt(boundaryOption, 10)
+ else
+ boundaryOption
+
+ autoGridLines: (ymin, ymax, nlines) ->
+ span = ymax - ymin
+ ymag = Math.floor(Math.log(span) / Math.log(10))
+ unit = Math.pow(10, ymag)
+
+ # calculate initial grid min and max values
+ gmin = Math.floor(ymin / unit) * unit
+ gmax = Math.ceil(ymax / unit) * unit
+ step = (gmax - gmin) / (nlines - 1)
+ if unit == 1 and step > 1 and Math.ceil(step) != step
+ step = Math.ceil(step)
+ gmax = gmin + step * (nlines - 1)
+
+ # ensure zero is plotted where the range includes zero
+ if gmin < 0 and gmax > 0
+ gmin = Math.floor(ymin / step) * step
+ gmax = Math.ceil(ymax / step) * step
+
+ # special case for decimal numbers
+ if step < 1
+ smag = Math.floor(Math.log(step) / Math.log(10))
+ grid = for y in [gmin..gmax] by step
+ parseFloat(y.toFixed(1 - smag))
+ else
+ grid = (y for y in [gmin..gmax] by step)
+ grid
+
+ _calc: ->
+ w = @el.width()
+ h = @el.height()
+
+ if @elementWidth != w or @elementHeight != h or @dirty
+ @elementWidth = w
+ @elementHeight = h
+ @dirty = false
+ # recalculate grid dimensions
+ @left = @options.padding
+ @right = @elementWidth - @options.padding
+ @top = @options.padding
+ @bottom = @elementHeight - @options.padding
+ if @options.axes in [true, 'both', 'y']
+ yLabelWidths = for gridLine in @grid
+ @measureText(@yAxisFormat(gridLine)).width
+ @left += Math.max(yLabelWidths...)
+ if @options.axes in [true, 'both', 'x']
+ bottomOffsets = for i in [0...@data.length]
+ @measureText(@data[i].text, -@options.xLabelAngle).height
+ @bottom -= Math.max(bottomOffsets...)
+ @width = Math.max(1, @right - @left)
+ @height = Math.max(1, @bottom - @top)
+ @dx = @width / (@xmax - @xmin)
+ @dy = @height / (@ymax - @ymin)
+ @calc() if @calc
+
+ # Quick translation helpers
+ #
+ transY: (y) -> @bottom - (y - @ymin) * @dy
+ transX: (x) ->
+ if @data.length == 1
+ (@left + @right) / 2
+ else
+ @left + (x - @xmin) * @dx
+
+ # Draw it!
+ #
+ # If you need to re-size your charts, call this method after changing the
+ # size of the container element.
+ redraw: ->
+ @raphael.clear()
+ @_calc()
+ @drawGrid()
+ @drawGoals()
+ @drawEvents()
+ @draw() if @draw
+
+ # @private
+ #
+ measureText: (text, angle = 0) ->
+ tt = @raphael.text(100, 100, text)
+ .attr('font-size', @options.gridTextSize)
+ .attr('font-family', @options.gridTextFamily)
+ .attr('font-weight', @options.gridTextWeight)
+ .rotate(angle)
+ ret = tt.getBBox()
+ tt.remove()
+ ret
+
+ # @private
+ #
+ yAxisFormat: (label) -> @yLabelFormat(label)
+
+ # @private
+ #
+ yLabelFormat: (label) ->
+ if typeof @options.yLabelFormat is 'function'
+ @options.yLabelFormat(label)
+ else
+ "#{@options.preUnits}#{Morris.commas(label)}#{@options.postUnits}"
+
+ # draw y axis labels, horizontal lines
+ #
+ drawGrid: ->
+ return if @options.grid is false and @options.axes not in [true, 'both', 'y']
+ for lineY in @grid
+ y = @transY(lineY)
+ if @options.axes in [true, 'both', 'y']
+ @drawYAxisLabel(@left - @options.padding / 2, y, @yAxisFormat(lineY))
+ if @options.grid
+ @drawGridLine("M#{@left},#{y}H#{@left + @width}")
+
+ # draw goals horizontal lines
+ #
+ drawGoals: ->
+ for goal, i in @options.goals
+ color = @options.goalLineColors[i % @options.goalLineColors.length]
+ @drawGoal(goal, color)
+
+ # draw events vertical lines
+ drawEvents: ->
+ for event, i in @events
+ color = @options.eventLineColors[i % @options.eventLineColors.length]
+ @drawEvent(event, color)
+
+ drawGoal: (goal, color) ->
+ @raphael.path("M#{@left},#{@transY(goal)}H#{@right}")
+ .attr('stroke', color)
+ .attr('stroke-width', @options.goalStrokeWidth)
+
+ drawEvent: (event, color) ->
+ @raphael.path("M#{@transX(event)},#{@bottom}V#{@top}")
+ .attr('stroke', color)
+ .attr('stroke-width', @options.eventStrokeWidth)
+
+ drawYAxisLabel: (xPos, yPos, text) ->
+ @raphael.text(xPos, yPos, text)
+ .attr('font-size', @options.gridTextSize)
+ .attr('font-family', @options.gridTextFamily)
+ .attr('font-weight', @options.gridTextWeight)
+ .attr('fill', @options.gridTextColor)
+ .attr('text-anchor', 'end')
+
+ drawGridLine: (path) ->
+ @raphael.path(path)
+ .attr('stroke', @options.gridLineColor)
+ .attr('stroke-width', @options.gridStrokeWidth)
+
+ # Range selection
+ #
+ startRange: (x) ->
+ @hover.hide()
+ @selectFrom = x
+ @selectionRect.attr({ x: x, width: 0 }).show()
+
+ endRange: (x) ->
+ if @selectFrom
+ start = Math.min(@selectFrom, x)
+ end = Math.max(@selectFrom, x)
+ @options.rangeSelect.call @el,
+ start: @data[@hitTest(start)].x
+ end: @data[@hitTest(end)].x
+ @selectFrom = null
+
+ resizeHandler: =>
+ @timeoutId = null
+ @raphael.setSize @el.width(), @el.height()
+ @redraw()
+
+# Parse a date into a javascript timestamp
+#
+#
+Morris.parseDate = (date) ->
+ if typeof date is 'number'
+ return date
+ m = date.match /^(\d+) Q(\d)$/
+ n = date.match /^(\d+)-(\d+)$/
+ o = date.match /^(\d+)-(\d+)-(\d+)$/
+ p = date.match /^(\d+) W(\d+)$/
+ q = date.match /^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+)(Z|([+-])(\d\d):?(\d\d))?$/
+ r = date.match /^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+):(\d+(\.\d+)?)(Z|([+-])(\d\d):?(\d\d))?$/
+ if m
+ new Date(
+ parseInt(m[1], 10),
+ parseInt(m[2], 10) * 3 - 1,
+ 1).getTime()
+ else if n
+ new Date(
+ parseInt(n[1], 10),
+ parseInt(n[2], 10) - 1,
+ 1).getTime()
+ else if o
+ new Date(
+ parseInt(o[1], 10),
+ parseInt(o[2], 10) - 1,
+ parseInt(o[3], 10)).getTime()
+ else if p
+ # calculate number of weeks in year given
+ ret = new Date(parseInt(p[1], 10), 0, 1);
+ # first thursday in year (ISO 8601 standard)
+ if ret.getDay() isnt 4
+ ret.setMonth(0, 1 + ((4 - ret.getDay()) + 7) % 7);
+ # add weeks
+ ret.getTime() + parseInt(p[2], 10) * 604800000
+ else if q
+ if not q[6]
+ # no timezone info, use local
+ new Date(
+ parseInt(q[1], 10),
+ parseInt(q[2], 10) - 1,
+ parseInt(q[3], 10),
+ parseInt(q[4], 10),
+ parseInt(q[5], 10)).getTime()
+ else
+ # timezone info supplied, use UTC
+ offsetmins = 0
+ if q[6] != 'Z'
+ offsetmins = parseInt(q[8], 10) * 60 + parseInt(q[9], 10)
+ offsetmins = 0 - offsetmins if q[7] == '+'
+ Date.UTC(
+ parseInt(q[1], 10),
+ parseInt(q[2], 10) - 1,
+ parseInt(q[3], 10),
+ parseInt(q[4], 10),
+ parseInt(q[5], 10) + offsetmins)
+ else if r
+ secs = parseFloat(r[6])
+ isecs = Math.floor(secs)
+ msecs = Math.round((secs - isecs) * 1000)
+ if not r[8]
+ # no timezone info, use local
+ new Date(
+ parseInt(r[1], 10),
+ parseInt(r[2], 10) - 1,
+ parseInt(r[3], 10),
+ parseInt(r[4], 10),
+ parseInt(r[5], 10),
+ isecs,
+ msecs).getTime()
+ else
+ # timezone info supplied, use UTC
+ offsetmins = 0
+ if r[8] != 'Z'
+ offsetmins = parseInt(r[10], 10) * 60 + parseInt(r[11], 10)
+ offsetmins = 0 - offsetmins if r[9] == '+'
+ Date.UTC(
+ parseInt(r[1], 10),
+ parseInt(r[2], 10) - 1,
+ parseInt(r[3], 10),
+ parseInt(r[4], 10),
+ parseInt(r[5], 10) + offsetmins,
+ isecs,
+ msecs)
+ else
+ new Date(parseInt(date, 10), 0, 1).getTime()
+
diff --git a/public/bower_components/morris.js/lib/morris.hover.coffee b/public/bower_components/morris.js/lib/morris.hover.coffee
new file mode 100644
index 0000000..530cb08
--- /dev/null
+++ b/public/bower_components/morris.js/lib/morris.hover.coffee
@@ -0,0 +1,44 @@
+class Morris.Hover
+ # Displays contextual information in a floating HTML div.
+
+ @defaults:
+ class: 'morris-hover morris-default-style'
+
+ constructor: (options = {}) ->
+ @options = $.extend {}, Morris.Hover.defaults, options
+ @el = $ "<div class='#{@options.class}'></div>"
+ @el.hide()
+ @options.parent.append(@el)
+
+ update: (html, x, y) ->
+ if not html
+ @hide()
+ else
+ @html(html)
+ @show()
+ @moveTo(x, y)
+
+ html: (content) ->
+ @el.html(content)
+
+ moveTo: (x, y) ->
+ parentWidth = @options.parent.innerWidth()
+ parentHeight = @options.parent.innerHeight()
+ hoverWidth = @el.outerWidth()
+ hoverHeight = @el.outerHeight()
+ left = Math.min(Math.max(0, x - hoverWidth / 2), parentWidth - hoverWidth)
+ if y?
+ top = y - hoverHeight - 10
+ if top < 0
+ top = y + 10
+ if top + hoverHeight > parentHeight
+ top = parentHeight / 2 - hoverHeight / 2
+ else
+ top = parentHeight / 2 - hoverHeight / 2
+ @el.css(left: left + "px", top: parseInt(top) + "px")
+
+ show: ->
+ @el.show()
+
+ hide: ->
+ @el.hide()
diff --git a/public/bower_components/morris.js/lib/morris.line.coffee b/public/bower_components/morris.js/lib/morris.line.coffee
new file mode 100644
index 0000000..ea5e7dd
--- /dev/null
+++ b/public/bower_components/morris.js/lib/morris.line.coffee
@@ -0,0 +1,405 @@
+class Morris.Line extends Morris.Grid
+ # Initialise the graph.
+ #
+ constructor: (options) ->
+ return new Morris.Line(options) unless (@ instanceof Morris.Line)
+ super(options)
+
+ init: ->
+ # Some instance variables for later
+ if @options.hideHover isnt 'always'
+ @hover = new Morris.Hover(parent: @el)
+ @on('hovermove', @onHoverMove)
+ @on('hoverout', @onHoverOut)
+ @on('gridclick', @onGridClick)
+
+ # Default configuration
+ #
+ defaults:
+ lineWidth: 3
+ pointSize: 4
+ lineColors: [
+ '#0b62a4'
+ '#7A92A3'
+ '#4da74d'
+ '#afd8f8'
+ '#edc240'
+ '#cb4b4b'
+ '#9440ed'
+ ]
+ pointStrokeWidths: [1]
+ pointStrokeColors: ['#ffffff']
+ pointFillColors: []
+ smooth: true
+ xLabels: 'auto'
+ xLabelFormat: null
+ xLabelMargin: 24
+ hideHover: false
+
+ # Do any size-related calculations
+ #
+ # @private
+ calc: ->
+ @calcPoints()
+ @generatePaths()
+
+ # calculate series data point coordinates
+ #
+ # @private
+ calcPoints: ->
+ for row in @data
+ row._x = @transX(row.x)
+ row._y = for y in row.y
+ if y? then @transY(y) else y
+ row._ymax = Math.min [@bottom].concat(y for y in row._y when y?)...
+
+ # hit test - returns the index of the row at the given x-coordinate
+ #
+ hitTest: (x) ->
+ return null if @data.length == 0
+ # TODO better search algo
+ for r, index in @data.slice(1)
+ break if x < (r._x + @data[index]._x) / 2
+ index
+
+ # click on grid event handler
+ #
+ # @private
+ onGridClick: (x, y) =>
+ index = @hitTest(x)
+ @fire 'click', index, @data[index].src, x, y
+
+ # hover movement event handler
+ #
+ # @private
+ onHoverMove: (x, y) =>
+ index = @hitTest(x)
+ @displayHoverForRow(index)
+
+ # hover out event handler
+ #
+ # @private
+ onHoverOut: =>
+ if @options.hideHover isnt false
+ @displayHoverForRow(null)
+
+ # display a hover popup over the given row
+ #
+ # @private
+ displayHoverForRow: (index) ->
+ if index?
+ @hover.update(@hoverContentForRow(index)...)
+ @hilight(index)
+ else
+ @hover.hide()
+ @hilight()
+
+ # hover content for a point
+ #
+ # @private
+ hoverContentForRow: (index) ->
+ row = @data[index]
+ content = "<div class='morris-hover-row-label'>#{row.label}</div>"
+ for y, j in row.y
+ content += """
+ <div class='morris-hover-point' style='color: #{@colorFor(row, j, 'label')}'>
+ #{@options.labels[j]}:
+ #{@yLabelFormat(y)}
+ </div>
+ """
+ if typeof @options.hoverCallback is 'function'
+ content = @options.hoverCallback(index, @options, content, row.src)
+ [content, row._x, row._ymax]
+
+
+ # generate paths for series lines
+ #
+ # @private
+ generatePaths: ->
+ @paths = for i in [0...@options.ykeys.length]
+ smooth = if typeof @options.smooth is "boolean" then @options.smooth else @options.ykeys[i] in @options.smooth
+ coords = ({x: r._x, y: r._y[i]} for r in @data when r._y[i] isnt undefined)
+
+ if coords.length > 1
+ Morris.Line.createPath coords, smooth, @bottom
+ else
+ null
+
+ # Draws the line chart.
+ #
+ draw: ->
+ @drawXAxis() if @options.axes in [true, 'both', 'x']
+ @drawSeries()
+ if @options.hideHover is false
+ @displayHoverForRow(@data.length - 1)
+
+ # draw the x-axis labels
+ #
+ # @private
+ drawXAxis: ->
+ # draw x axis labels
+ ypos = @bottom + @options.padding / 2
+ prevLabelMargin = null
+ prevAngleMargin = null
+ drawLabel = (labelText, xpos) =>
+ label = @drawXAxisLabel(@transX(xpos), ypos, labelText)
+ textBox = label.getBBox()
+ label.transform("r#{-@options.xLabelAngle}")
+ labelBox = label.getBBox()
+ label.transform("t0,#{labelBox.height / 2}...")
+ if @options.xLabelAngle != 0
+ offset = -0.5 * textBox.width *
+ Math.cos(@options.xLabelAngle * Math.PI / 180.0)
+ label.transform("t#{offset},0...")
+ # try to avoid overlaps
+ labelBox = label.getBBox()
+ if (not prevLabelMargin? or
+ prevLabelMargin >= labelBox.x + labelBox.width or
+ prevAngleMargin? and prevAngleMargin >= labelBox.x) and
+ labelBox.x >= 0 and (labelBox.x + labelBox.width) < @el.width()
+ if @options.xLabelAngle != 0
+ margin = 1.25 * @options.gridTextSize /
+ Math.sin(@options.xLabelAngle * Math.PI / 180.0)
+ prevAngleMargin = labelBox.x - margin
+ prevLabelMargin = labelBox.x - @options.xLabelMargin
+ else
+ label.remove()
+ if @options.parseTime
+ if @data.length == 1 and @options.xLabels == 'auto'
+ # where there's only one value in the series, we can't make a
+ # sensible guess for an x labelling scheme, so just use the original
+ # column label
+ labels = [[@data[0].label, @data[0].x]]
+ else
+ labels = Morris.labelSeries(@xmin, @xmax, @width, @options.xLabels, @options.xLabelFormat)
+ else
+ labels = ([row.label, row.x] for row in @data)
+ labels.reverse()
+ for l in labels
+ drawLabel(l[0], l[1])
+
+ # draw the data series
+ #
+ # @private
+ drawSeries: ->
+ @seriesPoints = []
+ for i in [@options.ykeys.length-1..0]
+ @_drawLineFor i
+ for i in [@options.ykeys.length-1..0]
+ @_drawPointFor i
+
+ _drawPointFor: (index) ->
+ @seriesPoints[index] = []
+ for row in @data
+ circle = null
+ if row._y[index]?
+ circle = @drawLinePoint(row._x, row._y[index], @colorFor(row, index, 'point'), index)
+ @seriesPoints[index].push(circle)
+
+ _drawLineFor: (index) ->
+ path = @paths[index]
+ if path isnt null
+ @drawLinePath path, @colorFor(null, index, 'line'), index
+
+ # create a path for a data series
+ #
+ # @private
+ @createPath: (coords, smooth, bottom) ->
+ path = ""
+ grads = Morris.Line.gradients(coords) if smooth
+
+ prevCoord = {y: null}
+ for coord, i in coords
+ if coord.y?
+ if prevCoord.y?
+ if smooth
+ g = grads[i]
+ lg = grads[i - 1]
+ ix = (coord.x - prevCoord.x) / 4
+ x1 = prevCoord.x + ix
+ y1 = Math.min(bottom, prevCoord.y + ix * lg)
+ x2 = coord.x - ix
+ y2 = Math.min(bottom, coord.y - ix * g)
+ path += "C#{x1},#{y1},#{x2},#{y2},#{coord.x},#{coord.y}"
+ else
+ path += "L#{coord.x},#{coord.y}"
+ else
+ if not smooth or grads[i]?
+ path += "M#{coord.x},#{coord.y}"
+ prevCoord = coord
+ return path
+
+ # calculate a gradient at each point for a series of points
+ #
+ # @private
+ @gradients: (coords) ->
+ grad = (a, b) -> (a.y - b.y) / (a.x - b.x)
+ for coord, i in coords
+ if coord.y?
+ nextCoord = coords[i + 1] or {y: null}
+ prevCoord = coords[i - 1] or {y: null}
+ if prevCoord.y? and nextCoord.y?
+ grad(prevCoord, nextCoord)
+ else if prevCoord.y?
+ grad(prevCoord, coord)
+ else if nextCoord.y?
+ grad(coord, nextCoord)
+ else
+ null
+ else
+ null
+
+ # @private
+ hilight: (index) =>
+ if @prevHilight isnt null and @prevHilight isnt index
+ for i in [0..@seriesPoints.length-1]
+ if @seriesPoints[i][@prevHilight]
+ @seriesPoints[i][@prevHilight].animate @pointShrinkSeries(i)
+ if index isnt null and @prevHilight isnt index
+ for i in [0..@seriesPoints.length-1]
+ if @seriesPoints[i][index]
+ @seriesPoints[i][index].animate @pointGrowSeries(i)
+ @prevHilight = index
+
+ colorFor: (row, sidx, type) ->
+ if typeof @options.lineColors is 'function'
+ @options.lineColors.call(@, row, sidx, type)
+ else if type is 'point'
+ @options.pointFillColors[sidx % @options.pointFillColors.length] || @options.lineColors[sidx % @options.lineColors.length]
+ else
+ @options.lineColors[sidx % @options.lineColors.length]
+
+ drawXAxisLabel: (xPos, yPos, text) ->
+ @raphael.text(xPos, yPos, text)
+ .attr('font-size', @options.gridTextSize)
+ .attr('font-family', @options.gridTextFamily)
+ .attr('font-weight', @options.gridTextWeight)
+ .attr('fill', @options.gridTextColor)
+
+ drawLinePath: (path, lineColor, lineIndex) ->
+ @raphael.path(path)
+ .attr('stroke', lineColor)
+ .attr('stroke-width', @lineWidthForSeries(lineIndex))
+
+ drawLinePoint: (xPos, yPos, pointColor, lineIndex) ->
+ @raphael.circle(xPos, yPos, @pointSizeForSeries(lineIndex))
+ .attr('fill', pointColor)
+ .attr('stroke-width', @pointStrokeWidthForSeries(lineIndex))
+ .attr('stroke', @pointStrokeColorForSeries(lineIndex))
+
+ # @private
+ pointStrokeWidthForSeries: (index) ->
+ @options.pointStrokeWidths[index % @options.pointStrokeWidths.length]
+
+ # @private
+ pointStrokeColorForSeries: (index) ->
+ @options.pointStrokeColors[index % @options.pointStrokeColors.length]
+
+ # @private
+ lineWidthForSeries: (index) ->
+ if (@options.lineWidth instanceof Array)
+ @options.lineWidth[index % @options.lineWidth.length]
+ else
+ @options.lineWidth
+
+ # @private
+ pointSizeForSeries: (index) ->
+ if (@options.pointSize instanceof Array)
+ @options.pointSize[index % @options.pointSize.length]
+ else
+ @options.pointSize
+
+ # @private
+ pointGrowSeries: (index) ->
+ Raphael.animation r: @pointSizeForSeries(index) + 3, 25, 'linear'
+
+ # @private
+ pointShrinkSeries: (index) ->
+ Raphael.animation r: @pointSizeForSeries(index), 25, 'linear'
+
+# generate a series of label, timestamp pairs for x-axis labels
+#
+# @private
+Morris.labelSeries = (dmin, dmax, pxwidth, specName, xLabelFormat) ->
+ ddensity = 200 * (dmax - dmin) / pxwidth # seconds per `margin` pixels
+ d0 = new Date(dmin)
+ spec = Morris.LABEL_SPECS[specName]
+ # if the spec doesn't exist, search for the closest one in the list
+ if spec is undefined
+ for name in Morris.AUTO_LABEL_ORDER
+ s = Morris.LABEL_SPECS[name]
+ if ddensity >= s.span
+ spec = s
+ break
+ # if we run out of options, use second-intervals
+ if spec is undefined
+ spec = Morris.LABEL_SPECS["second"]
+ # check if there's a user-defined formatting function
+ if xLabelFormat
+ spec = $.extend({}, spec, {fmt: xLabelFormat})
+ # calculate labels
+ d = spec.start(d0)
+ ret = []
+ while (t = d.getTime()) <= dmax
+ if t >= dmin
+ ret.push [spec.fmt(d), t]
+ spec.incr(d)
+ return ret
+
+# @private
+minutesSpecHelper = (interval) ->
+ span: interval * 60 * 1000
+ start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours())
+ fmt: (d) -> "#{Morris.pad2(d.getHours())}:#{Morris.pad2(d.getMinutes())}"
+ incr: (d) -> d.setUTCMinutes(d.getUTCMinutes() + interval)
+
+# @private
+secondsSpecHelper = (interval) ->
+ span: interval * 1000
+ start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes())
+ fmt: (d) -> "#{Morris.pad2(d.getHours())}:#{Morris.pad2(d.getMinutes())}:#{Morris.pad2(d.getSeconds())}"
+ incr: (d) -> d.setUTCSeconds(d.getUTCSeconds() + interval)
+
+Morris.LABEL_SPECS =
+ "decade":
+ span: 172800000000 # 10 * 365 * 24 * 60 * 60 * 1000
+ start: (d) -> new Date(d.getFullYear() - d.getFullYear() % 10, 0, 1)
+ fmt: (d) -> "#{d.getFullYear()}"
+ incr: (d) -> d.setFullYear(d.getFullYear() + 10)
+ "year":
+ span: 17280000000 # 365 * 24 * 60 * 60 * 1000
+ start: (d) -> new Date(d.getFullYear(), 0, 1)
+ fmt: (d) -> "#{d.getFullYear()}"
+ incr: (d) -> d.setFullYear(d.getFullYear() + 1)
+ "month":
+ span: 2419200000 # 28 * 24 * 60 * 60 * 1000
+ start: (d) -> new Date(d.getFullYear(), d.getMonth(), 1)
+ fmt: (d) -> "#{d.getFullYear()}-#{Morris.pad2(d.getMonth() + 1)}"
+ incr: (d) -> d.setMonth(d.getMonth() + 1)
+ "week":
+ span: 604800000 # 7 * 24 * 60 * 60 * 1000
+ start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate())
+ fmt: (d) -> "#{d.getFullYear()}-#{Morris.pad2(d.getMonth() + 1)}-#{Morris.pad2(d.getDate())}"
+ incr: (d) -> d.setDate(d.getDate() + 7)
+ "day":
+ span: 86400000 # 24 * 60 * 60 * 1000
+ start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate())
+ fmt: (d) -> "#{d.getFullYear()}-#{Morris.pad2(d.getMonth() + 1)}-#{Morris.pad2(d.getDate())}"
+ incr: (d) -> d.setDate(d.getDate() + 1)
+ "hour": minutesSpecHelper(60)
+ "30min": minutesSpecHelper(30)
+ "15min": minutesSpecHelper(15)
+ "10min": minutesSpecHelper(10)
+ "5min": minutesSpecHelper(5)
+ "minute": minutesSpecHelper(1)
+ "30sec": secondsSpecHelper(30)
+ "15sec": secondsSpecHelper(15)
+ "10sec": secondsSpecHelper(10)
+ "5sec": secondsSpecHelper(5)
+ "second": secondsSpecHelper(1)
+
+Morris.AUTO_LABEL_ORDER = [
+ "decade", "year", "month", "week", "day", "hour",
+ "30min", "15min", "10min", "5min", "minute",
+ "30sec", "15sec", "10sec", "5sec", "second"
+]
diff --git a/public/bower_components/morris.js/morris.css b/public/bower_components/morris.js/morris.css
new file mode 100644
index 0000000..209f091
--- /dev/null
+++ b/public/bower_components/morris.js/morris.css
@@ -0,0 +1,2 @@
+.morris-hover{position:absolute;z-index:1000}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255,255,255,0.8);border:solid 2px rgba(230,230,230,0.8);font-family:sans-serif;font-size:12px;text-align:center}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0}
+.morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0}
diff --git a/public/bower_components/morris.js/morris.js b/public/bower_components/morris.js/morris.js
new file mode 100644
index 0000000..3a1a830
--- /dev/null
+++ b/public/bower_components/morris.js/morris.js
@@ -0,0 +1,1892 @@
+/* @license
+morris.js v0.5.0
+Copyright 2014 Olly Smith All rights reserved.
+Licensed under the BSD-2-Clause License.
+*/
+
+
+(function() {
+ var $, Morris, minutesSpecHelper, secondsSpecHelper,
+ __slice = [].slice,
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
+ __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
+
+ Morris = window.Morris = {};
+
+ $ = jQuery;
+
+ Morris.EventEmitter = (function() {
+ function EventEmitter() {}
+
+ EventEmitter.prototype.on = function(name, handler) {
+ if (this.handlers == null) {
+ this.handlers = {};
+ }
+ if (this.handlers[name] == null) {
+ this.handlers[name] = [];
+ }
+ this.handlers[name].push(handler);
+ return this;
+ };
+
+ EventEmitter.prototype.fire = function() {
+ var args, handler, name, _i, _len, _ref, _results;
+ name = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
+ if ((this.handlers != null) && (this.handlers[name] != null)) {
+ _ref = this.handlers[name];
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ handler = _ref[_i];
+ _results.push(handler.apply(null, args));
+ }
+ return _results;
+ }
+ };
+
+ return EventEmitter;
+
+ })();
+
+ Morris.commas = function(num) {
+ var absnum, intnum, ret, strabsnum;
+ if (num != null) {
+ ret = num < 0 ? "-" : "";
+ absnum = Math.abs(num);
+ intnum = Math.floor(absnum).toFixed(0);
+ ret += intnum.replace(/(?=(?:\d{3})+$)(?!^)/g, ',');
+ strabsnum = absnum.toString();
+ if (strabsnum.length > intnum.length) {
+ ret += strabsnum.slice(intnum.length);
+ }
+ return ret;
+ } else {
+ return '-';
+ }
+ };
+
+ Morris.pad2 = function(number) {
+ return (number < 10 ? '0' : '') + number;
+ };
+
+ Morris.Grid = (function(_super) {
+ __extends(Grid, _super);
+
+ function Grid(options) {
+ this.resizeHandler = __bind(this.resizeHandler, this);
+ var _this = this;
+ if (typeof options.element === 'string') {
+ this.el = $(document.getElementById(options.element));
+ } else {
+ this.el = $(options.element);
+ }
+ if ((this.el == null) || this.el.length === 0) {
+ throw new Error("Graph container element not found");
+ }
+ if (this.el.css('position') === 'static') {
+ this.el.css('position', 'relative');
+ }
+ this.options = $.extend({}, this.gridDefaults, this.defaults || {}, options);
+ if (typeof this.options.units === 'string') {
+ this.options.postUnits = options.units;
+ }
+ this.raphael = new Raphael(this.el[0]);
+ this.elementWidth = null;
+ this.elementHeight = null;
+ this.dirty = false;
+ this.selectFrom = null;
+ if (this.init) {
+ this.init();
+ }
+ this.setData(this.options.data);
+ this.el.bind('mousemove', function(evt) {
+ var left, offset, right, width, x;
+ offset = _this.el.offset();
+ x = evt.pageX - offset.left;
+ if (_this.selectFrom) {
+ left = _this.data[_this.hitTest(Math.min(x, _this.selectFrom))]._x;
+ right = _this.data[_this.hitTest(Math.max(x, _this.selectFrom))]._x;
+ width = right - left;
+ return _this.selectionRect.attr({
+ x: left,
+ width: width
+ });
+ } else {
+ return _this.fire('hovermove', x, evt.pageY - offset.top);
+ }
+ });
+ this.el.bind('mouseleave', function(evt) {
+ if (_this.selectFrom) {
+ _this.selectionRect.hide();
+ _this.selectFrom = null;
+ }
+ return _this.fire('hoverout');
+ });
+ this.el.bind('touchstart touchmove touchend', function(evt) {
+ var offset, touch;
+ touch = evt.originalEvent.touches[0] || evt.originalEvent.changedTouches[0];
+ offset = _this.el.offset();
+ return _this.fire('hovermove', touch.pageX - offset.left, touch.pageY - offset.top);
+ });
+ this.el.bind('click', function(evt) {
+ var offset;
+ offset = _this.el.offset();
+ return _this.fire('gridclick', evt.pageX - offset.left, evt.pageY - offset.top);
+ });
+ if (this.options.rangeSelect) {
+ this.selectionRect = this.raphael.rect(0, 0, 0, this.el.innerHeight()).attr({
+ fill: this.options.rangeSelectColor,
+ stroke: false
+ }).toBack().hide();
+ this.el.bind('mousedown', function(evt) {
+ var offset;
+ offset = _this.el.offset();
+ return _this.startRange(evt.pageX - offset.left);
+ });
+ this.el.bind('mouseup', function(evt) {
+ var offset;
+ offset = _this.el.offset();
+ _this.endRange(evt.pageX - offset.left);
+ return _this.fire('hovermove', evt.pageX - offset.left, evt.pageY - offset.top);
+ });
+ }
+ if (this.options.resize) {
+ $(window).bind('resize', function(evt) {
+ if (_this.timeoutId != null) {
+ window.clearTimeout(_this.timeoutId);
+ }
+ return _this.timeoutId = window.setTimeout(_this.resizeHandler, 100);
+ });
+ }
+ this.el.css('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
+ if (this.postInit) {
+ this.postInit();
+ }
+ }
+
+ Grid.prototype.gridDefaults = {
+ dateFormat: null,
+ axes: true,
+ grid: true,
+ gridLineColor: '#aaa',
+ gridStrokeWidth: 0.5,
+ gridTextColor: '#888',
+ gridTextSize: 12,
+ gridTextFamily: 'sans-serif',
+ gridTextWeight: 'normal',
+ hideHover: false,
+ yLabelFormat: null,
+ xLabelAngle: 0,
+ numLines: 5,
+ padding: 25,
+ parseTime: true,
+ postUnits: '',
+ preUnits: '',
+ ymax: 'auto',
+ ymin: 'auto 0',
+ goals: [],
+ goalStrokeWidth: 1.0,
+ goalLineColors: ['#666633', '#999966', '#cc6666', '#663333'],
+ events: [],
+ eventStrokeWidth: 1.0,
+ eventLineColors: ['#005a04', '#ccffbb', '#3a5f0b', '#005502'],
+ rangeSelect: null,
+ rangeSelectColor: '#eef',
+ resize: false
+ };
+
+ Grid.prototype.setData = function(data, redraw) {
+ var e, idx, index, maxGoal, minGoal, ret, row, step, total, y, ykey, ymax, ymin, yval, _ref;
+ if (redraw == null) {
+ redraw = true;
+ }
+ this.options.data = data;
+ if ((data == null) || data.length === 0) {
+ this.data = [];
+ this.raphael.clear();
+ if (this.hover != null) {
+ this.hover.hide();
+ }
+ return;
+ }
+ ymax = this.cumulative ? 0 : null;
+ ymin = this.cumulative ? 0 : null;
+ if (this.options.goals.length > 0) {
+ minGoal = Math.min.apply(Math, this.options.goals);
+ maxGoal = Math.max.apply(Math, this.options.goals);
+ ymin = ymin != null ? Math.min(ymin, minGoal) : minGoal;
+ ymax = ymax != null ? Math.max(ymax, maxGoal) : maxGoal;
+ }
+ this.data = (function() {
+ var _i, _len, _results;
+ _results = [];
+ for (index = _i = 0, _len = data.length; _i < _len; index = ++_i) {
+ row = data[index];
+ ret = {
+ src: row
+ };
+ ret.label = row[this.options.xkey];
+ if (this.options.parseTime) {
+ ret.x = Morris.parseDate(ret.label);
+ if (this.options.dateFormat) {
+ ret.label = this.options.dateFormat(ret.x);
+ } else if (typeof ret.label === 'number') {
+ ret.label = new Date(ret.label).toString();
+ }
+ } else {
+ ret.x = index;
+ if (this.options.xLabelFormat) {
+ ret.label = this.options.xLabelFormat(ret);
+ }
+ }
+ total = 0;
+ ret.y = (function() {
+ var _j, _len1, _ref, _results1;
+ _ref = this.options.ykeys;
+ _results1 = [];
+ for (idx = _j = 0, _len1 = _ref.length; _j < _len1; idx = ++_j) {
+ ykey = _ref[idx];
+ yval = row[ykey];
+ if (typeof yval === 'string') {
+ yval = parseFloat(yval);
+ }
+ if ((yval != null) && typeof yval !== 'number') {
+ yval = null;
+ }
+ if (yval != null) {
+ if (this.cumulative) {
+ total += yval;
+ } else {
+ if (ymax != null) {
+ ymax = Math.max(yval, ymax);
+ ymin = Math.min(yval, ymin);
+ } else {
+ ymax = ymin = yval;
+ }
+ }
+ }
+ if (this.cumulative && (total != null)) {
+ ymax = Math.max(total, ymax);
+ ymin = Math.min(total, ymin);
+ }
+ _results1.push(yval);
+ }
+ return _results1;
+ }).call(this);
+ _results.push(ret);
+ }
+ return _results;
+ }).call(this);
+ if (this.options.parseTime) {
+ this.data = this.data.sort(function(a, b) {
+ return (a.x > b.x) - (b.x > a.x);
+ });
+ }
+ this.xmin = this.data[0].x;
+ this.xmax = this.data[this.data.length - 1].x;
+ this.events = [];
+ if (this.options.events.length > 0) {
+ if (this.options.parseTime) {
+ this.events = (function() {
+ var _i, _len, _ref, _results;
+ _ref = this.options.events;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ e = _ref[_i];
+ _results.push(Morris.parseDate(e));
+ }
+ return _results;
+ }).call(this);
+ } else {
+ this.events = this.options.events;
+ }
+ this.xmax = Math.max(this.xmax, Math.max.apply(Math, this.events));
+ this.xmin = Math.min(this.xmin, Math.min.apply(Math, this.events));
+ }
+ if (this.xmin === this.xmax) {
+ this.xmin -= 1;
+ this.xmax += 1;
+ }
+ this.ymin = this.yboundary('min', ymin);
+ this.ymax = this.yboundary('max', ymax);
+ if (this.ymin === this.ymax) {
+ if (ymin) {
+ this.ymin -= 1;
+ }
+ this.ymax += 1;
+ }
+ if (((_ref = this.options.axes) === true || _ref === 'both' || _ref === 'y') || this.options.grid === true) {
+ if (this.options.ymax === this.gridDefaults.ymax && this.options.ymin === this.gridDefaults.ymin) {
+ this.grid = this.autoGridLines(this.ymin, this.ymax, this.options.numLines);
+ this.ymin = Math.min(this.ymin, this.grid[0]);
+ this.ymax = Math.max(this.ymax, this.grid[this.grid.length - 1]);
+ } else {
+ step = (this.ymax - this.ymin) / (this.options.numLines - 1);
+ this.grid = (function() {
+ var _i, _ref1, _ref2, _results;
+ _results = [];
+ for (y = _i = _ref1 = this.ymin, _ref2 = this.ymax; step > 0 ? _i <= _ref2 : _i >= _ref2; y = _i += step) {
+ _results.push(y);
+ }
+ return _results;
+ }).call(this);
+ }
+ }
+ this.dirty = true;
+ if (redraw) {
+ return this.redraw();
+ }
+ };
+
+ Grid.prototype.yboundary = function(boundaryType, currentValue) {
+ var boundaryOption, suggestedValue;
+ boundaryOption = this.options["y" + boundaryType];
+ if (typeof boundaryOption === 'string') {
+ if (boundaryOption.slice(0, 4) === 'auto') {
+ if (boundaryOption.length > 5) {
+ suggestedValue = parseInt(boundaryOption.slice(5), 10);
+ if (currentValue == null) {
+ return suggestedValue;
+ }
+ return Math[boundaryType](currentValue, suggestedValue);
+ } else {
+ if (currentValue != null) {
+ return currentValue;
+ } else {
+ return 0;
+ }
+ }
+ } else {
+ return parseInt(boundaryOption, 10);
+ }
+ } else {
+ return boundaryOption;
+ }
+ };
+
+ Grid.prototype.autoGridLines = function(ymin, ymax, nlines) {
+ var gmax, gmin, grid, smag, span, step, unit, y, ymag;
+ span = ymax - ymin;
+ ymag = Math.floor(Math.log(span) / Math.log(10));
+ unit = Math.pow(10, ymag);
+ gmin = Math.floor(ymin / unit) * unit;
+ gmax = Math.ceil(ymax / unit) * unit;
+ step = (gmax - gmin) / (nlines - 1);
+ if (unit === 1 && step > 1 && Math.ceil(step) !== step) {
+ step = Math.ceil(step);
+ gmax = gmin + step * (nlines - 1);
+ }
+ if (gmin < 0 && gmax > 0) {
+ gmin = Math.floor(ymin / step) * step;
+ gmax = Math.ceil(ymax / step) * step;
+ }
+ if (step < 1) {
+ smag = Math.floor(Math.log(step) / Math.log(10));
+ grid = (function() {
+ var _i, _results;
+ _results = [];
+ for (y = _i = gmin; step > 0 ? _i <= gmax : _i >= gmax; y = _i += step) {
+ _results.push(parseFloat(y.toFixed(1 - smag)));
+ }
+ return _results;
+ })();
+ } else {
+ grid = (function() {
+ var _i, _results;
+ _results = [];
+ for (y = _i = gmin; step > 0 ? _i <= gmax : _i >= gmax; y = _i += step) {
+ _results.push(y);
+ }
+ return _results;
+ })();
+ }
+ return grid;
+ };
+
+ Grid.prototype._calc = function() {
+ var bottomOffsets, gridLine, h, i, w, yLabelWidths, _ref, _ref1;
+ w = this.el.width();
+ h = this.el.height();
+ if (this.elementWidth !== w || this.elementHeight !== h || this.dirty) {
+ this.elementWidth = w;
+ this.elementHeight = h;
+ this.dirty = false;
+ this.left = this.options.padding;
+ this.right = this.elementWidth - this.options.padding;
+ this.top = this.options.padding;
+ this.bottom = this.elementHeight - this.options.padding;
+ if ((_ref = this.options.axes) === true || _ref === 'both' || _ref === 'y') {
+ yLabelWidths = (function() {
+ var _i, _len, _ref1, _results;
+ _ref1 = this.grid;
+ _results = [];
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
+ gridLine = _ref1[_i];
+ _results.push(this.measureText(this.yAxisFormat(gridLine)).width);
+ }
+ return _results;
+ }).call(this);
+ this.left += Math.max.apply(Math, yLabelWidths);
+ }
+ if ((_ref1 = this.options.axes) === true || _ref1 === 'both' || _ref1 === 'x') {
+ bottomOffsets = (function() {
+ var _i, _ref2, _results;
+ _results = [];
+ for (i = _i = 0, _ref2 = this.data.length; 0 <= _ref2 ? _i < _ref2 : _i > _ref2; i = 0 <= _ref2 ? ++_i : --_i) {
+ _results.push(this.measureText(this.data[i].text, -this.options.xLabelAngle).height);
+ }
+ return _results;
+ }).call(this);
+ this.bottom -= Math.max.apply(Math, bottomOffsets);
+ }
+ this.width = Math.max(1, this.right - this.left);
+ this.height = Math.max(1, this.bottom - this.top);
+ this.dx = this.width / (this.xmax - this.xmin);
+ this.dy = this.height / (this.ymax - this.ymin);
+ if (this.calc) {
+ return this.calc();
+ }
+ }
+ };
+
+ Grid.prototype.transY = function(y) {
+ return this.bottom - (y - this.ymin) * this.dy;
+ };
+
+ Grid.prototype.transX = function(x) {
+ if (this.data.length === 1) {
+ return (this.left + this.right) / 2;
+ } else {
+ return this.left + (x - this.xmin) * this.dx;
+ }
+ };
+
+ Grid.prototype.redraw = function() {
+ this.raphael.clear();
+ this._calc();
+ this.drawGrid();
+ this.drawGoals();
+ this.drawEvents();
+ if (this.draw) {
+ return this.draw();
+ }
+ };
+
+ Grid.prototype.measureText = function(text, angle) {
+ var ret, tt;
+ if (angle == null) {
+ angle = 0;
+ }
+ tt = this.raphael.text(100, 100, text).attr('font-size', this.options.gridTextSize).attr('font-family', this.options.gridTextFamily).attr('font-weight', this.options.gridTextWeight).rotate(angle);
+ ret = tt.getBBox();
+ tt.remove();
+ return ret;
+ };
+
+ Grid.prototype.yAxisFormat = function(label) {
+ return this.yLabelFormat(label);
+ };
+
+ Grid.prototype.yLabelFormat = function(label) {
+ if (typeof this.options.yLabelFormat === 'function') {
+ return this.options.yLabelFormat(label);
+ } else {
+ return "" + this.options.preUnits + (Morris.commas(label)) + this.options.postUnits;
+ }
+ };
+
+ Grid.prototype.drawGrid = function() {
+ var lineY, y, _i, _len, _ref, _ref1, _ref2, _results;
+ if (this.options.grid === false && ((_ref = this.options.axes) !== true && _ref !== 'both' && _ref !== 'y')) {
+ return;
+ }
+ _ref1 = this.grid;
+ _results = [];
+ for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
+ lineY = _ref1[_i];
+ y = this.transY(lineY);
+ if ((_ref2 = this.options.axes) === true || _ref2 === 'both' || _ref2 === 'y') {
+ this.drawYAxisLabel(this.left - this.options.padding / 2, y, this.yAxisFormat(lineY));
+ }
+ if (this.options.grid) {
+ _results.push(this.drawGridLine("M" + this.left + "," + y + "H" + (this.left + this.width)));
+ } else {
+ _results.push(void 0);
+ }
+ }
+ return _results;
+ };
+
+ Grid.prototype.drawGoals = function() {
+ var color, goal, i, _i, _len, _ref, _results;
+ _ref = this.options.goals;
+ _results = [];
+ for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
+ goal = _ref[i];
+ color = this.options.goalLineColors[i % this.options.goalLineColors.length];
+ _results.push(this.drawGoal(goal, color));
+ }
+ return _results;
+ };
+
+ Grid.prototype.drawEvents = function() {
+ var color, event, i, _i, _len, _ref, _results;
+ _ref = this.events;
+ _results = [];
+ for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
+ event = _ref[i];
+ color = this.options.eventLineColors[i % this.options.eventLineColors.length];
+ _results.push(this.drawEvent(event, color));
+ }
+ return _results;
+ };
+
+ Grid.prototype.drawGoal = function(goal, color) {
+ return this.raphael.path("M" + this.left + "," + (this.transY(goal)) + "H" + this.right).attr('stroke', color).attr('stroke-width', this.options.goalStrokeWidth);
+ };
+
+ Grid.prototype.drawEvent = function(event, color) {
+ return this.raphael.path("M" + (this.transX(event)) + "," + this.bottom + "V" + this.top).attr('stroke', color).attr('stroke-width', this.options.eventStrokeWidth);
+ };
+
+ Grid.prototype.drawYAxisLabel = function(xPos, yPos, text) {
+ return this.raphael.text(xPos, yPos, text).attr('font-size', this.options.gridTextSize).attr('font-family', this.options.gridTextFamily).attr('font-weight', this.options.gridTextWeight).attr('fill', this.options.gridTextColor).attr('text-anchor', 'end');
+ };
+
+ Grid.prototype.drawGridLine = function(path) {
+ return this.raphael.path(path).attr('stroke', this.options.gridLineColor).attr('stroke-width', this.options.gridStrokeWidth);
+ };
+
+ Grid.prototype.startRange = function(x) {
+ this.hover.hide();
+ this.selectFrom = x;
+ return this.selectionRect.attr({
+ x: x,
+ width: 0
+ }).show();
+ };
+
+ Grid.prototype.endRange = function(x) {
+ var end, start;
+ if (this.selectFrom) {
+ start = Math.min(this.selectFrom, x);
+ end = Math.max(this.selectFrom, x);
+ this.options.rangeSelect.call(this.el, {
+ start: this.data[this.hitTest(start)].x,
+ end: this.data[this.hitTest(end)].x
+ });
+ return this.selectFrom = null;
+ }
+ };
+
+ Grid.prototype.resizeHandler = function() {
+ this.timeoutId = null;
+ this.raphael.setSize(this.el.width(), this.el.height());
+ return this.redraw();
+ };
+
+ return Grid;
+
+ })(Morris.EventEmitter);
+
+ Morris.parseDate = function(date) {
+ var isecs, m, msecs, n, o, offsetmins, p, q, r, ret, secs;
+ if (typeof date === 'number') {
+ return date;
+ }
+ m = date.match(/^(\d+) Q(\d)$/);
+ n = date.match(/^(\d+)-(\d+)$/);
+ o = date.match(/^(\d+)-(\d+)-(\d+)$/);
+ p = date.match(/^(\d+) W(\d+)$/);
+ q = date.match(/^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+)(Z|([+-])(\d\d):?(\d\d))?$/);
+ r = date.match(/^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+):(\d+(\.\d+)?)(Z|([+-])(\d\d):?(\d\d))?$/);
+ if (m) {
+ return new Date(parseInt(m[1], 10), parseInt(m[2], 10) * 3 - 1, 1).getTime();
+ } else if (n) {
+ return new Date(parseInt(n[1], 10), parseInt(n[2], 10) - 1, 1).getTime();
+ } else if (o) {
+ return new Date(parseInt(o[1], 10), parseInt(o[2], 10) - 1, parseInt(o[3], 10)).getTime();
+ } else if (p) {
+ ret = new Date(parseInt(p[1], 10), 0, 1);
+ if (ret.getDay() !== 4) {
+ ret.setMonth(0, 1 + ((4 - ret.getDay()) + 7) % 7);
+ }
+ return ret.getTime() + parseInt(p[2], 10) * 604800000;
+ } else if (q) {
+ if (!q[6]) {
+ return new Date(parseInt(q[1], 10), parseInt(q[2], 10) - 1, parseInt(q[3], 10), parseInt(q[4], 10), parseInt(q[5], 10)).getTime();
+ } else {
+ offsetmins = 0;
+ if (q[6] !== 'Z') {
+ offsetmins = parseInt(q[8], 10) * 60 + parseInt(q[9], 10);
+ if (q[7] === '+') {
+ offsetmins = 0 - offsetmins;
+ }
+ }
+ return Date.UTC(parseInt(q[1], 10), parseInt(q[2], 10) - 1, parseInt(q[3], 10), parseInt(q[4], 10), parseInt(q[5], 10) + offsetmins);
+ }
+ } else if (r) {
+ secs = parseFloat(r[6]);
+ isecs = Math.floor(secs);
+ msecs = Math.round((secs - isecs) * 1000);
+ if (!r[8]) {
+ return new Date(parseInt(r[1], 10), parseInt(r[2], 10) - 1, parseInt(r[3], 10), parseInt(r[4], 10), parseInt(r[5], 10), isecs, msecs).getTime();
+ } else {
+ offsetmins = 0;
+ if (r[8] !== 'Z') {
+ offsetmins = parseInt(r[10], 10) * 60 + parseInt(r[11], 10);
+ if (r[9] === '+') {
+ offsetmins = 0 - offsetmins;
+ }
+ }
+ return Date.UTC(parseInt(r[1], 10), parseInt(r[2], 10) - 1, parseInt(r[3], 10), parseInt(r[4], 10), parseInt(r[5], 10) + offsetmins, isecs, msecs);
+ }
+ } else {
+ return new Date(parseInt(date, 10), 0, 1).getTime();
+ }
+ };
+
+ Morris.Hover = (function() {
+ Hover.defaults = {
+ "class": 'morris-hover morris-default-style'
+ };
+
+ function Hover(options) {
+ if (options == null) {
+ options = {};
+ }
+ this.options = $.extend({}, Morris.Hover.defaults, options);
+ this.el = $("<div class='" + this.options["class"] + "'></div>");
+ this.el.hide();
+ this.options.parent.append(this.el);
+ }
+
+ Hover.prototype.update = function(html, x, y) {
+ if (!html) {
+ return this.hide();
+ } else {
+ this.html(html);
+ this.show();
+ return this.moveTo(x, y);
+ }
+ };
+
+ Hover.prototype.html = function(content) {
+ return this.el.html(content);
+ };
+
+ Hover.prototype.moveTo = function(x, y) {
+ var hoverHeight, hoverWidth, left, parentHeight, parentWidth, top;
+ parentWidth = this.options.parent.innerWidth();
+ parentHeight = this.options.parent.innerHeight();
+ hoverWidth = this.el.outerWidth();
+ hoverHeight = this.el.outerHeight();
+ left = Math.min(Math.max(0, x - hoverWidth / 2), parentWidth - hoverWidth);
+ if (y != null) {
+ top = y - hoverHeight - 10;
+ if (top < 0) {
+ top = y + 10;
+ if (top + hoverHeight > parentHeight) {
+ top = parentHeight / 2 - hoverHeight / 2;
+ }
+ }
+ } else {
+ top = parentHeight / 2 - hoverHeight / 2;
+ }
+ return this.el.css({
+ left: left + "px",
+ top: parseInt(top) + "px"
+ });
+ };
+
+ Hover.prototype.show = function() {
+ return this.el.show();
+ };
+
+ Hover.prototype.hide = function() {
+ return this.el.hide();
+ };
+
+ return Hover;
+
+ })();
+
+ Morris.Line = (function(_super) {
+ __extends(Line, _super);
+
+ function Line(options) {
+ this.hilight = __bind(this.hilight, this);
+ this.onHoverOut = __bind(this.onHoverOut, this);
+ this.onHoverMove = __bind(this.onHoverMove, this);
+ this.onGridClick = __bind(this.onGridClick, this);
+ if (!(this instanceof Morris.Line)) {
+ return new Morris.Line(options);
+ }
+ Line.__super__.constructor.call(this, options);
+ }
+
+ Line.prototype.init = function() {
+ if (this.options.hideHover !== 'always') {
+ this.hover = new Morris.Hover({
+ parent: this.el
+ });
+ this.on('hovermove', this.onHoverMove);
+ this.on('hoverout', this.onHoverOut);
+ return this.on('gridclick', this.onGridClick);
+ }
+ };
+
+ Line.prototype.defaults = {
+ lineWidth: 3,
+ pointSize: 4,
+ lineColors: ['#0b62a4', '#7A92A3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'],
+ pointStrokeWidths: [1],
+ pointStrokeColors: ['#ffffff'],
+ pointFillColors: [],
+ smooth: true,
+ xLabels: 'auto',
+ xLabelFormat: null,
+ xLabelMargin: 24,
+ hideHover: false
+ };
+
+ Line.prototype.calc = function() {
+ this.calcPoints();
+ return this.generatePaths();
+ };
+
+ Line.prototype.calcPoints = function() {
+ var row, y, _i, _len, _ref, _results;
+ _ref = this.data;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ row = _ref[_i];
+ row._x = this.transX(row.x);
+ row._y = (function() {
+ var _j, _len1, _ref1, _results1;
+ _ref1 = row.y;
+ _results1 = [];
+ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
+ y = _ref1[_j];
+ if (y != null) {
+ _results1.push(this.transY(y));
+ } else {
+ _results1.push(y);
+ }
+ }
+ return _results1;
+ }).call(this);
+ _results.push(row._ymax = Math.min.apply(Math, [this.bottom].concat((function() {
+ var _j, _len1, _ref1, _results1;
+ _ref1 = row._y;
+ _results1 = [];
+ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
+ y = _ref1[_j];
+ if (y != null) {
+ _results1.push(y);
+ }
+ }
+ return _results1;
+ })())));
+ }
+ return _results;
+ };
+
+ Line.prototype.hitTest = function(x) {
+ var index, r, _i, _len, _ref;
+ if (this.data.length === 0) {
+ return null;
+ }
+ _ref = this.data.slice(1);
+ for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) {
+ r = _ref[index];
+ if (x < (r._x + this.data[index]._x) / 2) {
+ break;
+ }
+ }
+ return index;
+ };
+
+ Line.prototype.onGridClick = function(x, y) {
+ var index;
+ index = this.hitTest(x);
+ return this.fire('click', index, this.data[index].src, x, y);
+ };
+
+ Line.prototype.onHoverMove = function(x, y) {
+ var index;
+ index = this.hitTest(x);
+ return this.displayHoverForRow(index);
+ };
+
+ Line.prototype.onHoverOut = function() {
+ if (this.options.hideHover !== false) {
+ return this.displayHoverForRow(null);
+ }
+ };
+
+ Line.prototype.displayHoverForRow = function(index) {
+ var _ref;
+ if (index != null) {
+ (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(index));
+ return this.hilight(index);
+ } else {
+ this.hover.hide();
+ return this.hilight();
+ }
+ };
+
+ Line.prototype.hoverContentForRow = function(index) {
+ var content, j, row, y, _i, _len, _ref;
+ row = this.data[index];
+ content = "<div class='morris-hover-row-label'>" + row.label + "</div>";
+ _ref = row.y;
+ for (j = _i = 0, _len = _ref.length; _i < _len; j = ++_i) {
+ y = _ref[j];
+ content += "<div class='morris-hover-point' style='color: " + (this.colorFor(row, j, 'label')) + "'>\n " + this.options.labels[j] + ":\n " + (this.yLabelFormat(y)) + "\n</div>";
+ }
+ if (typeof this.options.hoverCallback === 'function') {
+ content = this.options.hoverCallback(index, this.options, content, row.src);
+ }
+ return [content, row._x, row._ymax];
+ };
+
+ Line.prototype.generatePaths = function() {
+ var coords, i, r, smooth;
+ return this.paths = (function() {
+ var _i, _ref, _ref1, _results;
+ _results = [];
+ for (i = _i = 0, _ref = this.options.ykeys.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
+ smooth = typeof this.options.smooth === "boolean" ? this.options.smooth : (_ref1 = this.options.ykeys[i], __indexOf.call(this.options.smooth, _ref1) >= 0);
+ coords = (function() {
+ var _j, _len, _ref2, _results1;
+ _ref2 = this.data;
+ _results1 = [];
+ for (_j = 0, _len = _ref2.length; _j < _len; _j++) {
+ r = _ref2[_j];
+ if (r._y[i] !== void 0) {
+ _results1.push({
+ x: r._x,
+ y: r._y[i]
+ });
+ }
+ }
+ return _results1;
+ }).call(this);
+ if (coords.length > 1) {
+ _results.push(Morris.Line.createPath(coords, smooth, this.bottom));
+ } else {
+ _results.push(null);
+ }
+ }
+ return _results;
+ }).call(this);
+ };
+
+ Line.prototype.draw = function() {
+ var _ref;
+ if ((_ref = this.options.axes) === true || _ref === 'both' || _ref === 'x') {
+ this.drawXAxis();
+ }
+ this.drawSeries();
+ if (this.options.hideHover === false) {
+ return this.displayHoverForRow(this.data.length - 1);
+ }
+ };
+
+ Line.prototype.drawXAxis = function() {
+ var drawLabel, l, labels, prevAngleMargin, prevLabelMargin, row, ypos, _i, _len, _results,
+ _this = this;
+ ypos = this.bottom + this.options.padding / 2;
+ prevLabelMargin = null;
+ prevAngleMargin = null;
+ drawLabel = function(labelText, xpos) {
+ var label, labelBox, margin, offset, textBox;
+ label = _this.drawXAxisLabel(_this.transX(xpos), ypos, labelText);
+ textBox = label.getBBox();
+ label.transform("r" + (-_this.options.xLabelAngle));
+ labelBox = label.getBBox();
+ label.transform("t0," + (labelBox.height / 2) + "...");
+ if (_this.options.xLabelAngle !== 0) {
+ offset = -0.5 * textBox.width * Math.cos(_this.options.xLabelAngle * Math.PI / 180.0);
+ label.transform("t" + offset + ",0...");
+ }
+ labelBox = label.getBBox();
+ if (((prevLabelMargin == null) || prevLabelMargin >= labelBox.x + labelBox.width || (prevAngleMargin != null) && prevAngleMargin >= labelBox.x) && labelBox.x >= 0 && (labelBox.x + labelBox.width) < _this.el.width()) {
+ if (_this.options.xLabelAngle !== 0) {
+ margin = 1.25 * _this.options.gridTextSize / Math.sin(_this.options.xLabelAngle * Math.PI / 180.0);
+ prevAngleMargin = labelBox.x - margin;
+ }
+ return prevLabelMargin = labelBox.x - _this.options.xLabelMargin;
+ } else {
+ return label.remove();
+ }
+ };
+ if (this.options.parseTime) {
+ if (this.data.length === 1 && this.options.xLabels === 'auto') {
+ labels = [[this.data[0].label, this.data[0].x]];
+ } else {
+ labels = Morris.labelSeries(this.xmin, this.xmax, this.width, this.options.xLabels, this.options.xLabelFormat);
+ }
+ } else {
+ labels = (function() {
+ var _i, _len, _ref, _results;
+ _ref = this.data;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ row = _ref[_i];
+ _results.push([row.label, row.x]);
+ }
+ return _results;
+ }).call(this);
+ }
+ labels.reverse();
+ _results = [];
+ for (_i = 0, _len = labels.length; _i < _len; _i++) {
+ l = labels[_i];
+ _results.push(drawLabel(l[0], l[1]));
+ }
+ return _results;
+ };
+
+ Line.prototype.drawSeries = function() {
+ var i, _i, _j, _ref, _ref1, _results;
+ this.seriesPoints = [];
+ for (i = _i = _ref = this.options.ykeys.length - 1; _ref <= 0 ? _i <= 0 : _i >= 0; i = _ref <= 0 ? ++_i : --_i) {
+ this._drawLineFor(i);
+ }
+ _results = [];
+ for (i = _j = _ref1 = this.options.ykeys.length - 1; _ref1 <= 0 ? _j <= 0 : _j >= 0; i = _ref1 <= 0 ? ++_j : --_j) {
+ _results.push(this._drawPointFor(i));
+ }
+ return _results;
+ };
+
+ Line.prototype._drawPointFor = function(index) {
+ var circle, row, _i, _len, _ref, _results;
+ this.seriesPoints[index] = [];
+ _ref = this.data;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ row = _ref[_i];
+ circle = null;
+ if (row._y[index] != null) {
+ circle = this.drawLinePoint(row._x, row._y[index], this.colorFor(row, index, 'point'), index);
+ }
+ _results.push(this.seriesPoints[index].push(circle));
+ }
+ return _results;
+ };
+
+ Line.prototype._drawLineFor = function(index) {
+ var path;
+ path = this.paths[index];
+ if (path !== null) {
+ return this.drawLinePath(path, this.colorFor(null, index, 'line'), index);
+ }
+ };
+
+ Line.createPath = function(coords, smooth, bottom) {
+ var coord, g, grads, i, ix, lg, path, prevCoord, x1, x2, y1, y2, _i, _len;
+ path = "";
+ if (smooth) {
+ grads = Morris.Line.gradients(coords);
+ }
+ prevCoord = {
+ y: null
+ };
+ for (i = _i = 0, _len = coords.length; _i < _len; i = ++_i) {
+ coord = coords[i];
+ if (coord.y != null) {
+ if (prevCoord.y != null) {
+ if (smooth) {
+ g = grads[i];
+ lg = grads[i - 1];
+ ix = (coord.x - prevCoord.x) / 4;
+ x1 = prevCoord.x + ix;
+ y1 = Math.min(bottom, prevCoord.y + ix * lg);
+ x2 = coord.x - ix;
+ y2 = Math.min(bottom, coord.y - ix * g);
+ path += "C" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + coord.x + "," + coord.y;
+ } else {
+ path += "L" + coord.x + "," + coord.y;
+ }
+ } else {
+ if (!smooth || (grads[i] != null)) {
+ path += "M" + coord.x + "," + coord.y;
+ }
+ }
+ }
+ prevCoord = coord;
+ }
+ return path;
+ };
+
+ Line.gradients = function(coords) {
+ var coord, grad, i, nextCoord, prevCoord, _i, _len, _results;
+ grad = function(a, b) {
+ return (a.y - b.y) / (a.x - b.x);
+ };
+ _results = [];
+ for (i = _i = 0, _len = coords.length; _i < _len; i = ++_i) {
+ coord = coords[i];
+ if (coord.y != null) {
+ nextCoord = coords[i + 1] || {
+ y: null
+ };
+ prevCoord = coords[i - 1] || {
+ y: null
+ };
+ if ((prevCoord.y != null) && (nextCoord.y != null)) {
+ _results.push(grad(prevCoord, nextCoord));
+ } else if (prevCoord.y != null) {
+ _results.push(grad(prevCoord, coord));
+ } else if (nextCoord.y != null) {
+ _results.push(grad(coord, nextCoord));
+ } else {
+ _results.push(null);
+ }
+ } else {
+ _results.push(null);
+ }
+ }
+ return _results;
+ };
+
+ Line.prototype.hilight = function(index) {
+ var i, _i, _j, _ref, _ref1;
+ if (this.prevHilight !== null && this.prevHilight !== index) {
+ for (i = _i = 0, _ref = this.seriesPoints.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
+ if (this.seriesPoints[i][this.prevHilight]) {
+ this.seriesPoints[i][this.prevHilight].animate(this.pointShrinkSeries(i));
+ }
+ }
+ }
+ if (index !== null && this.prevHilight !== index) {
+ for (i = _j = 0, _ref1 = this.seriesPoints.length - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
+ if (this.seriesPoints[i][index]) {
+ this.seriesPoints[i][index].animate(this.pointGrowSeries(i));
+ }
+ }
+ }
+ return this.prevHilight = index;
+ };
+
+ Line.prototype.colorFor = function(row, sidx, type) {
+ if (typeof this.options.lineColors === 'function') {
+ return this.options.lineColors.call(this, row, sidx, type);
+ } else if (type === 'point') {
+ return this.options.pointFillColors[sidx % this.options.pointFillColors.length] || this.options.lineColors[sidx % this.options.lineColors.length];
+ } else {
+ return this.options.lineColors[sidx % this.options.lineColors.length];
+ }
+ };
+
+ Line.prototype.drawXAxisLabel = function(xPos, yPos, text) {
+ return this.raphael.text(xPos, yPos, text).attr('font-size', this.options.gridTextSize).attr('font-family', this.options.gridTextFamily).attr('font-weight', this.options.gridTextWeight).attr('fill', this.options.gridTextColor);
+ };
+
+ Line.prototype.drawLinePath = function(path, lineColor, lineIndex) {
+ return this.raphael.path(path).attr('stroke', lineColor).attr('stroke-width', this.lineWidthForSeries(lineIndex));
+ };
+
+ Line.prototype.drawLinePoint = function(xPos, yPos, pointColor, lineIndex) {
+ return this.raphael.circle(xPos, yPos, this.pointSizeForSeries(lineIndex)).attr('fill', pointColor).attr('stroke-width', this.pointStrokeWidthForSeries(lineIndex)).attr('stroke', this.pointStrokeColorForSeries(lineIndex));
+ };
+
+ Line.prototype.pointStrokeWidthForSeries = function(index) {
+ return this.options.pointStrokeWidths[index % this.options.pointStrokeWidths.length];
+ };
+
+ Line.prototype.pointStrokeColorForSeries = function(index) {
+ return this.options.pointStrokeColors[index % this.options.pointStrokeColors.length];
+ };
+
+ Line.prototype.lineWidthForSeries = function(index) {
+ if (this.options.lineWidth instanceof Array) {
+ return this.options.lineWidth[index % this.options.lineWidth.length];
+ } else {
+ return this.options.lineWidth;
+ }
+ };
+
+ Line.prototype.pointSizeForSeries = function(index) {
+ if (this.options.pointSize instanceof Array) {
+ return this.options.pointSize[index % this.options.pointSize.length];
+ } else {
+ return this.options.pointSize;
+ }
+ };
+
+ Line.prototype.pointGrowSeries = function(index) {
+ return Raphael.animation({
+ r: this.pointSizeForSeries(index) + 3
+ }, 25, 'linear');
+ };
+
+ Line.prototype.pointShrinkSeries = function(index) {
+ return Raphael.animation({
+ r: this.pointSizeForSeries(index)
+ }, 25, 'linear');
+ };
+
+ return Line;
+
+ })(Morris.Grid);
+
+ Morris.labelSeries = function(dmin, dmax, pxwidth, specName, xLabelFormat) {
+ var d, d0, ddensity, name, ret, s, spec, t, _i, _len, _ref;
+ ddensity = 200 * (dmax - dmin) / pxwidth;
+ d0 = new Date(dmin);
+ spec = Morris.LABEL_SPECS[specName];
+ if (spec === void 0) {
+ _ref = Morris.AUTO_LABEL_ORDER;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ name = _ref[_i];
+ s = Morris.LABEL_SPECS[name];
+ if (ddensity >= s.span) {
+ spec = s;
+ break;
+ }
+ }
+ }
+ if (spec === void 0) {
+ spec = Morris.LABEL_SPECS["second"];
+ }
+ if (xLabelFormat) {
+ spec = $.extend({}, spec, {
+ fmt: xLabelFormat
+ });
+ }
+ d = spec.start(d0);
+ ret = [];
+ while ((t = d.getTime()) <= dmax) {
+ if (t >= dmin) {
+ ret.push([spec.fmt(d), t]);
+ }
+ spec.incr(d);
+ }
+ return ret;
+ };
+
+ minutesSpecHelper = function(interval) {
+ return {
+ span: interval * 60 * 1000,
+ start: function(d) {
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours());
+ },
+ fmt: function(d) {
+ return "" + (Morris.pad2(d.getHours())) + ":" + (Morris.pad2(d.getMinutes()));
+ },
+ incr: function(d) {
+ return d.setUTCMinutes(d.getUTCMinutes() + interval);
+ }
+ };
+ };
+
+ secondsSpecHelper = function(interval) {
+ return {
+ span: interval * 1000,
+ start: function(d) {
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes());
+ },
+ fmt: function(d) {
+ return "" + (Morris.pad2(d.getHours())) + ":" + (Morris.pad2(d.getMinutes())) + ":" + (Morris.pad2(d.getSeconds()));
+ },
+ incr: function(d) {
+ return d.setUTCSeconds(d.getUTCSeconds() + interval);
+ }
+ };
+ };
+
+ Morris.LABEL_SPECS = {
+ "decade": {
+ span: 172800000000,
+ start: function(d) {
+ return new Date(d.getFullYear() - d.getFullYear() % 10, 0, 1);
+ },
+ fmt: function(d) {
+ return "" + (d.getFullYear());
+ },
+ incr: function(d) {
+ return d.setFullYear(d.getFullYear() + 10);
+ }
+ },
+ "year": {
+ span: 17280000000,
+ start: function(d) {
+ return new Date(d.getFullYear(), 0, 1);
+ },
+ fmt: function(d) {
+ return "" + (d.getFullYear());
+ },
+ incr: function(d) {
+ return d.setFullYear(d.getFullYear() + 1);
+ }
+ },
+ "month": {
+ span: 2419200000,
+ start: function(d) {
+ return new Date(d.getFullYear(), d.getMonth(), 1);
+ },
+ fmt: function(d) {
+ return "" + (d.getFullYear()) + "-" + (Morris.pad2(d.getMonth() + 1));
+ },
+ incr: function(d) {
+ return d.setMonth(d.getMonth() + 1);
+ }
+ },
+ "week": {
+ span: 604800000,
+ start: function(d) {
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate());
+ },
+ fmt: function(d) {
+ return "" + (d.getFullYear()) + "-" + (Morris.pad2(d.getMonth() + 1)) + "-" + (Morris.pad2(d.getDate()));
+ },
+ incr: function(d) {
+ return d.setDate(d.getDate() + 7);
+ }
+ },
+ "day": {
+ span: 86400000,
+ start: function(d) {
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate());
+ },
+ fmt: function(d) {
+ return "" + (d.getFullYear()) + "-" + (Morris.pad2(d.getMonth() + 1)) + "-" + (Morris.pad2(d.getDate()));
+ },
+ incr: function(d) {
+ return d.setDate(d.getDate() + 1);
+ }
+ },
+ "hour": minutesSpecHelper(60),
+ "30min": minutesSpecHelper(30),
+ "15min": minutesSpecHelper(15),
+ "10min": minutesSpecHelper(10),
+ "5min": minutesSpecHelper(5),
+ "minute": minutesSpecHelper(1),
+ "30sec": secondsSpecHelper(30),
+ "15sec": secondsSpecHelper(15),
+ "10sec": secondsSpecHelper(10),
+ "5sec": secondsSpecHelper(5),
+ "second": secondsSpecHelper(1)
+ };
+
+ Morris.AUTO_LABEL_ORDER = ["decade", "year", "month", "week", "day", "hour", "30min", "15min", "10min", "5min", "minute", "30sec", "15sec", "10sec", "5sec", "second"];
+
+ Morris.Area = (function(_super) {
+ var areaDefaults;
+
+ __extends(Area, _super);
+
+ areaDefaults = {
+ fillOpacity: 'auto',
+ behaveLikeLine: false
+ };
+
+ function Area(options) {
+ var areaOptions;
+ if (!(this instanceof Morris.Area)) {
+ return new Morris.Area(options);
+ }
+ areaOptions = $.extend({}, areaDefaults, options);
+ this.cumulative = !areaOptions.behaveLikeLine;
+ if (areaOptions.fillOpacity === 'auto') {
+ areaOptions.fillOpacity = areaOptions.behaveLikeLine ? .8 : 1;
+ }
+ Area.__super__.constructor.call(this, areaOptions);
+ }
+
+ Area.prototype.calcPoints = function() {
+ var row, total, y, _i, _len, _ref, _results;
+ _ref = this.data;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ row = _ref[_i];
+ row._x = this.transX(row.x);
+ total = 0;
+ row._y = (function() {
+ var _j, _len1, _ref1, _results1;
+ _ref1 = row.y;
+ _results1 = [];
+ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
+ y = _ref1[_j];
+ if (this.options.behaveLikeLine) {
+ _results1.push(this.transY(y));
+ } else {
+ total += y || 0;
+ _results1.push(this.transY(total));
+ }
+ }
+ return _results1;
+ }).call(this);
+ _results.push(row._ymax = Math.max.apply(Math, row._y));
+ }
+ return _results;
+ };
+
+ Area.prototype.drawSeries = function() {
+ var i, range, _i, _j, _k, _len, _ref, _ref1, _results, _results1, _results2;
+ this.seriesPoints = [];
+ if (this.options.behaveLikeLine) {
+ range = (function() {
+ _results = [];
+ for (var _i = 0, _ref = this.options.ykeys.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; 0 <= _ref ? _i++ : _i--){ _results.push(_i); }
+ return _results;
+ }).apply(this);
+ } else {
+ range = (function() {
+ _results1 = [];
+ for (var _j = _ref1 = this.options.ykeys.length - 1; _ref1 <= 0 ? _j <= 0 : _j >= 0; _ref1 <= 0 ? _j++ : _j--){ _results1.push(_j); }
+ return _results1;
+ }).apply(this);
+ }
+ _results2 = [];
+ for (_k = 0, _len = range.length; _k < _len; _k++) {
+ i = range[_k];
+ this._drawFillFor(i);
+ this._drawLineFor(i);
+ _results2.push(this._drawPointFor(i));
+ }
+ return _results2;
+ };
+
+ Area.prototype._drawFillFor = function(index) {
+ var path;
+ path = this.paths[index];
+ if (path !== null) {
+ path = path + ("L" + (this.transX(this.xmax)) + "," + this.bottom + "L" + (this.transX(this.xmin)) + "," + this.bottom + "Z");
+ return this.drawFilledPath(path, this.fillForSeries(index));
+ }
+ };
+
+ Area.prototype.fillForSeries = function(i) {
+ var color;
+ color = Raphael.rgb2hsl(this.colorFor(this.data[i], i, 'line'));
+ return Raphael.hsl(color.h, this.options.behaveLikeLine ? color.s * 0.9 : color.s * 0.75, Math.min(0.98, this.options.behaveLikeLine ? color.l * 1.2 : color.l * 1.25));
+ };
+
+ Area.prototype.drawFilledPath = function(path, fill) {
+ return this.raphael.path(path).attr('fill', fill).attr('fill-opacity', this.options.fillOpacity).attr('stroke', 'none');
+ };
+
+ return Area;
+
+ })(Morris.Line);
+
+ Morris.Bar = (function(_super) {
+ __extends(Bar, _super);
+
+ function Bar(options) {
+ this.onHoverOut = __bind(this.onHoverOut, this);
+ this.onHoverMove = __bind(this.onHoverMove, this);
+ this.onGridClick = __bind(this.onGridClick, this);
+ if (!(this instanceof Morris.Bar)) {
+ return new Morris.Bar(options);
+ }
+ Bar.__super__.constructor.call(this, $.extend({}, options, {
+ parseTime: false
+ }));
+ }
+
+ Bar.prototype.init = function() {
+ this.cumulative = this.options.stacked;
+ if (this.options.hideHover !== 'always') {
+ this.hover = new Morris.Hover({
+ parent: this.el
+ });
+ this.on('hovermove', this.onHoverMove);
+ this.on('hoverout', this.onHoverOut);
+ return this.on('gridclick', this.onGridClick);
+ }
+ };
+
+ Bar.prototype.defaults = {
+ barSizeRatio: 0.75,
+ barGap: 3,
+ barColors: ['#0b62a4', '#7a92a3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'],
+ barOpacity: 1.0,
+ barRadius: [0, 0, 0, 0],
+ xLabelMargin: 50
+ };
+
+ Bar.prototype.calc = function() {
+ var _ref;
+ this.calcBars();
+ if (this.options.hideHover === false) {
+ return (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(this.data.length - 1));
+ }
+ };
+
+ Bar.prototype.calcBars = function() {
+ var idx, row, y, _i, _len, _ref, _results;
+ _ref = this.data;
+ _results = [];
+ for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
+ row = _ref[idx];
+ row._x = this.left + this.width * (idx + 0.5) / this.data.length;
+ _results.push(row._y = (function() {
+ var _j, _len1, _ref1, _results1;
+ _ref1 = row.y;
+ _results1 = [];
+ for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
+ y = _ref1[_j];
+ if (y != null) {
+ _results1.push(this.transY(y));
+ } else {
+ _results1.push(null);
+ }
+ }
+ return _results1;
+ }).call(this));
+ }
+ return _results;
+ };
+
+ Bar.prototype.draw = function() {
+ var _ref;
+ if ((_ref = this.options.axes) === true || _ref === 'both' || _ref === 'x') {
+ this.drawXAxis();
+ }
+ return this.drawSeries();
+ };
+
+ Bar.prototype.drawXAxis = function() {
+ var i, label, labelBox, margin, offset, prevAngleMargin, prevLabelMargin, row, textBox, ypos, _i, _ref, _results;
+ ypos = this.bottom + (this.options.xAxisLabelTopPadding || this.options.padding / 2);
+ prevLabelMargin = null;
+ prevAngleMargin = null;
+ _results = [];
+ for (i = _i = 0, _ref = this.data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
+ row = this.data[this.data.length - 1 - i];
+ label = this.drawXAxisLabel(row._x, ypos, row.label);
+ textBox = label.getBBox();
+ label.transform("r" + (-this.options.xLabelAngle));
+ labelBox = label.getBBox();
+ label.transform("t0," + (labelBox.height / 2) + "...");
+ if (this.options.xLabelAngle !== 0) {
+ offset = -0.5 * textBox.width * Math.cos(this.options.xLabelAngle * Math.PI / 180.0);
+ label.transform("t" + offset + ",0...");
+ }
+ if (((prevLabelMargin == null) || prevLabelMargin >= labelBox.x + labelBox.width || (prevAngleMargin != null) && prevAngleMargin >= labelBox.x) && labelBox.x >= 0 && (labelBox.x + labelBox.width) < this.el.width()) {
+ if (this.options.xLabelAngle !== 0) {
+ margin = 1.25 * this.options.gridTextSize / Math.sin(this.options.xLabelAngle * Math.PI / 180.0);
+ prevAngleMargin = labelBox.x - margin;
+ }
+ _results.push(prevLabelMargin = labelBox.x - this.options.xLabelMargin);
+ } else {
+ _results.push(label.remove());
+ }
+ }
+ return _results;
+ };
+
+ Bar.prototype.drawSeries = function() {
+ var barWidth, bottom, groupWidth, idx, lastTop, left, leftPadding, numBars, row, sidx, size, spaceLeft, top, ypos, zeroPos;
+ groupWidth = this.width / this.options.data.length;
+ numBars = this.options.stacked ? 1 : this.options.ykeys.length;
+ barWidth = (groupWidth * this.options.barSizeRatio - this.options.barGap * (numBars - 1)) / numBars;
+ if (this.options.barSize) {
+ barWidth = Math.min(barWidth, this.options.barSize);
+ }
+ spaceLeft = groupWidth - barWidth * numBars - this.options.barGap * (numBars - 1);
+ leftPadding = spaceLeft / 2;
+ zeroPos = this.ymin <= 0 && this.ymax >= 0 ? this.transY(0) : null;
+ return this.bars = (function() {
+ var _i, _len, _ref, _results;
+ _ref = this.data;
+ _results = [];
+ for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
+ row = _ref[idx];
+ lastTop = 0;
+ _results.push((function() {
+ var _j, _len1, _ref1, _results1;
+ _ref1 = row._y;
+ _results1 = [];
+ for (sidx = _j = 0, _len1 = _ref1.length; _j < _len1; sidx = ++_j) {
+ ypos = _ref1[sidx];
+ if (ypos !== null) {
+ if (zeroPos) {
+ top = Math.min(ypos, zeroPos);
+ bottom = Math.max(ypos, zeroPos);
+ } else {
+ top = ypos;
+ bottom = this.bottom;
+ }
+ left = this.left + idx * groupWidth + leftPadding;
+ if (!this.options.stacked) {
+ left += sidx * (barWidth + this.options.barGap);
+ }
+ size = bottom - top;
+ if (this.options.verticalGridCondition && this.options.verticalGridCondition(row.x)) {
+ this.drawBar(this.left + idx * groupWidth, this.top, groupWidth, Math.abs(this.top - this.bottom), this.options.verticalGridColor, this.options.verticalGridOpacity, this.options.barRadius);
+ }
+ if (this.options.stacked) {
+ top -= lastTop;
+ }
+ this.drawBar(left, top, barWidth, size, this.colorFor(row, sidx, 'bar'), this.options.barOpacity, this.options.barRadius);
+ _results1.push(lastTop += size);
+ } else {
+ _results1.push(null);
+ }
+ }
+ return _results1;
+ }).call(this));
+ }
+ return _results;
+ }).call(this);
+ };
+
+ Bar.prototype.colorFor = function(row, sidx, type) {
+ var r, s;
+ if (typeof this.options.barColors === 'function') {
+ r = {
+ x: row.x,
+ y: row.y[sidx],
+ label: row.label
+ };
+ s = {
+ index: sidx,
+ key: this.options.ykeys[sidx],
+ label: this.options.labels[sidx]
+ };
+ return this.options.barColors.call(this, r, s, type);
+ } else {
+ return this.options.barColors[sidx % this.options.barColors.length];
+ }
+ };
+
+ Bar.prototype.hitTest = function(x) {
+ if (this.data.length === 0) {
+ return null;
+ }
+ x = Math.max(Math.min(x, this.right), this.left);
+ return Math.min(this.data.length - 1, Math.floor((x - this.left) / (this.width / this.data.length)));
+ };
+
+ Bar.prototype.onGridClick = function(x, y) {
+ var index;
+ index = this.hitTest(x);
+ return this.fire('click', index, this.data[index].src, x, y);
+ };
+
+ Bar.prototype.onHoverMove = function(x, y) {
+ var index, _ref;
+ index = this.hitTest(x);
+ return (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(index));
+ };
+
+ Bar.prototype.onHoverOut = function() {
+ if (this.options.hideHover !== false) {
+ return this.hover.hide();
+ }
+ };
+
+ Bar.prototype.hoverContentForRow = function(index) {
+ var content, j, row, x, y, _i, _len, _ref;
+ row = this.data[index];
+ content = "<div class='morris-hover-row-label'>" + row.label + "</div>";
+ _ref = row.y;
+ for (j = _i = 0, _len = _ref.length; _i < _len; j = ++_i) {
+ y = _ref[j];
+ content += "<div class='morris-hover-point' style='color: " + (this.colorFor(row, j, 'label')) + "'>\n " + this.options.labels[j] + ":\n " + (this.yLabelFormat(y)) + "\n</div>";
+ }
+ if (typeof this.options.hoverCallback === 'function') {
+ content = this.options.hoverCallback(index, this.options, content, row.src);
+ }
+ x = this.left + (index + 0.5) * this.width / this.data.length;
+ return [content, x];
+ };
+
+ Bar.prototype.drawXAxisLabel = function(xPos, yPos, text) {
+ var label;
+ return label = this.raphael.text(xPos, yPos, text).attr('font-size', this.options.gridTextSize).attr('font-family', this.options.gridTextFamily).attr('font-weight', this.options.gridTextWeight).attr('fill', this.options.gridTextColor);
+ };
+
+ Bar.prototype.drawBar = function(xPos, yPos, width, height, barColor, opacity, radiusArray) {
+ var maxRadius, path;
+ maxRadius = Math.max.apply(Math, radiusArray);
+ if (maxRadius === 0 || maxRadius > height) {
+ path = this.raphael.rect(xPos, yPos, width, height);
+ } else {
+ path = this.raphael.path(this.roundedRect(xPos, yPos, width, height, radiusArray));
+ }
+ return path.attr('fill', barColor).attr('fill-opacity', opacity).attr('stroke', 'none');
+ };
+
+ Bar.prototype.roundedRect = function(x, y, w, h, r) {
+ if (r == null) {
+ r = [0, 0, 0, 0];
+ }
+ return ["M", x, r[0] + y, "Q", x, y, x + r[0], y, "L", x + w - r[1], y, "Q", x + w, y, x + w, y + r[1], "L", x + w, y + h - r[2], "Q", x + w, y + h, x + w - r[2], y + h, "L", x + r[3], y + h, "Q", x, y + h, x, y + h - r[3], "Z"];
+ };
+
+ return Bar;
+
+ })(Morris.Grid);
+
+ Morris.Donut = (function(_super) {
+ __extends(Donut, _super);
+
+ Donut.prototype.defaults = {
+ colors: ['#0B62A4', '#3980B5', '#679DC6', '#95BBD7', '#B0CCE1', '#095791', '#095085', '#083E67', '#052C48', '#042135'],
+ backgroundColor: '#FFFFFF',
+ labelColor: '#000000',
+ formatter: Morris.commas,
+ resize: false
+ };
+
+ function Donut(options) {
+ this.resizeHandler = __bind(this.resizeHandler, this);
+ this.select = __bind(this.select, this);
+ this.click = __bind(this.click, this);
+ var _this = this;
+ if (!(this instanceof Morris.Donut)) {
+ return new Morris.Donut(options);
+ }
+ this.options = $.extend({}, this.defaults, options);
+ if (typeof options.element === 'string') {
+ this.el = $(document.getElementById(options.element));
+ } else {
+ this.el = $(options.element);
+ }
+ if (this.el === null || this.el.length === 0) {
+ throw new Error("Graph placeholder not found.");
+ }
+ if (options.data === void 0 || options.data.length === 0) {
+ return;
+ }
+ this.raphael = new Raphael(this.el[0]);
+ if (this.options.resize) {
+ $(window).bind('resize', function(evt) {
+ if (_this.timeoutId != null) {
+ window.clearTimeout(_this.timeoutId);
+ }
+ return _this.timeoutId = window.setTimeout(_this.resizeHandler, 100);
+ });
+ }
+ this.setData(options.data);
+ }
+
+ Donut.prototype.redraw = function() {
+ var C, cx, cy, i, idx, last, max_value, min, next, seg, total, value, w, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _results;
+ this.raphael.clear();
+ cx = this.el.width() / 2;
+ cy = this.el.height() / 2;
+ w = (Math.min(cx, cy) - 10) / 3;
+ total = 0;
+ _ref = this.values;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ value = _ref[_i];
+ total += value;
+ }
+ min = 5 / (2 * w);
+ C = 1.9999 * Math.PI - min * this.data.length;
+ last = 0;
+ idx = 0;
+ this.segments = [];
+ _ref1 = this.values;
+ for (i = _j = 0, _len1 = _ref1.length; _j < _len1; i = ++_j) {
+ value = _ref1[i];
+ next = last + min + C * (value / total);
+ seg = new Morris.DonutSegment(cx, cy, w * 2, w, last, next, this.data[i].color || this.options.colors[idx % this.options.colors.length], this.options.backgroundColor, idx, this.raphael);
+ seg.render();
+ this.segments.push(seg);
+ seg.on('hover', this.select);
+ seg.on('click', this.click);
+ last = next;
+ idx += 1;
+ }
+ this.text1 = this.drawEmptyDonutLabel(cx, cy - 10, this.options.labelColor, 15, 800);
+ this.text2 = this.drawEmptyDonutLabel(cx, cy + 10, this.options.labelColor, 14);
+ max_value = Math.max.apply(Math, this.values);
+ idx = 0;
+ _ref2 = this.values;
+ _results = [];
+ for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
+ value = _ref2[_k];
+ if (value === max_value) {
+ this.select(idx);
+ break;
+ }
+ _results.push(idx += 1);
+ }
+ return _results;
+ };
+
+ Donut.prototype.setData = function(data) {
+ var row;
+ this.data = data;
+ this.values = (function() {
+ var _i, _len, _ref, _results;
+ _ref = this.data;
+ _results = [];
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ row = _ref[_i];
+ _results.push(parseFloat(row.value));
+ }
+ return _results;
+ }).call(this);
+ return this.redraw();
+ };
+
+ Donut.prototype.click = function(idx) {
+ return this.fire('click', idx, this.data[idx]);
+ };
+
+ Donut.prototype.select = function(idx) {
+ var row, s, segment, _i, _len, _ref;
+ _ref = this.segments;
+ for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+ s = _ref[_i];
+ s.deselect();
+ }
+ segment = this.segments[idx];
+ segment.select();
+ row = this.data[idx];
+ return this.setLabels(row.label, this.options.formatter(row.value, row));
+ };
+
+ Donut.prototype.setLabels = function(label1, label2) {
+ var inner, maxHeightBottom, maxHeightTop, maxWidth, text1bbox, text1scale, text2bbox, text2scale;
+ inner = (Math.min(this.el.width() / 2, this.el.height() / 2) - 10) * 2 / 3;
+ maxWidth = 1.8 * inner;
+ maxHeightTop = inner / 2;
+ maxHeightBottom = inner / 3;
+ this.text1.attr({
+ text: label1,
+ transform: ''
+ });
+ text1bbox = this.text1.getBBox();
+ text1scale = Math.min(maxWidth / text1bbox.width, maxHeightTop / text1bbox.height);
+ this.text1.attr({
+ transform: "S" + text1scale + "," + text1scale + "," + (text1bbox.x + text1bbox.width / 2) + "," + (text1bbox.y + text1bbox.height)
+ });
+ this.text2.attr({
+ text: label2,
+ transform: ''
+ });
+ text2bbox = this.text2.getBBox();
+ text2scale = Math.min(maxWidth / text2bbox.width, maxHeightBottom / text2bbox.height);
+ return this.text2.attr({
+ transform: "S" + text2scale + "," + text2scale + "," + (text2bbox.x + text2bbox.width / 2) + "," + text2bbox.y
+ });
+ };
+
+ Donut.prototype.drawEmptyDonutLabel = function(xPos, yPos, color, fontSize, fontWeight) {
+ var text;
+ text = this.raphael.text(xPos, yPos, '').attr('font-size', fontSize).attr('fill', color);
+ if (fontWeight != null) {
+ text.attr('font-weight', fontWeight);
+ }
+ return text;
+ };
+
+ Donut.prototype.resizeHandler = function() {
+ this.timeoutId = null;
+ this.raphael.setSize(this.el.width(), this.el.height());
+ return this.redraw();
+ };
+
+ return Donut;
+
+ })(Morris.EventEmitter);
+
+ Morris.DonutSegment = (function(_super) {
+ __extends(DonutSegment, _super);
+
+ function DonutSegment(cx, cy, inner, outer, p0, p1, color, backgroundColor, index, raphael) {
+ this.cx = cx;
+ this.cy = cy;
+ this.inner = inner;
+ this.outer = outer;
+ this.color = color;
+ this.backgroundColor = backgroundColor;
+ this.index = index;
+ this.raphael = raphael;
+ this.deselect = __bind(this.deselect, this);
+ this.select = __bind(this.select, this);
+ this.sin_p0 = Math.sin(p0);
+ this.cos_p0 = Math.cos(p0);
+ this.sin_p1 = Math.sin(p1);
+ this.cos_p1 = Math.cos(p1);
+ this.is_long = (p1 - p0) > Math.PI ? 1 : 0;
+ this.path = this.calcSegment(this.inner + 3, this.inner + this.outer - 5);
+ this.selectedPath = this.calcSegment(this.inner + 3, this.inner + this.outer);
+ this.hilight = this.calcArc(this.inner);
+ }
+
+ DonutSegment.prototype.calcArcPoints = function(r) {
+ return [this.cx + r * this.sin_p0, this.cy + r * this.cos_p0, this.cx + r * this.sin_p1, this.cy + r * this.cos_p1];
+ };
+
+ DonutSegment.prototype.calcSegment = function(r1, r2) {
+ var ix0, ix1, iy0, iy1, ox0, ox1, oy0, oy1, _ref, _ref1;
+ _ref = this.calcArcPoints(r1), ix0 = _ref[0], iy0 = _ref[1], ix1 = _ref[2], iy1 = _ref[3];
+ _ref1 = this.calcArcPoints(r2), ox0 = _ref1[0], oy0 = _ref1[1], ox1 = _ref1[2], oy1 = _ref1[3];
+ return ("M" + ix0 + "," + iy0) + ("A" + r1 + "," + r1 + ",0," + this.is_long + ",0," + ix1 + "," + iy1) + ("L" + ox1 + "," + oy1) + ("A" + r2 + "," + r2 + ",0," + this.is_long + ",1," + ox0 + "," + oy0) + "Z";
+ };
+
+ DonutSegment.prototype.calcArc = function(r) {
+ var ix0, ix1, iy0, iy1, _ref;
+ _ref = this.calcArcPoints(r), ix0 = _ref[0], iy0 = _ref[1], ix1 = _ref[2], iy1 = _ref[3];
+ return ("M" + ix0 + "," + iy0) + ("A" + r + "," + r + ",0," + this.is_long + ",0," + ix1 + "," + iy1);
+ };
+
+ DonutSegment.prototype.render = function() {
+ var _this = this;
+ this.arc = this.drawDonutArc(this.hilight, this.color);
+ return this.seg = this.drawDonutSegment(this.path, this.color, this.backgroundColor, function() {
+ return _this.fire('hover', _this.index);
+ }, function() {
+ return _this.fire('click', _this.index);
+ });
+ };
+
+ DonutSegment.prototype.drawDonutArc = function(path, color) {
+ return this.raphael.path(path).attr({
+ stroke: color,
+ 'stroke-width': 2,
+ opacity: 0
+ });
+ };
+
+ DonutSegment.prototype.drawDonutSegment = function(path, fillColor, strokeColor, hoverFunction, clickFunction) {
+ return this.raphael.path(path).attr({
+ fill: fillColor,
+ stroke: strokeColor,
+ 'stroke-width': 3
+ }).hover(hoverFunction).click(clickFunction);
+ };
+
+ DonutSegment.prototype.select = function() {
+ if (!this.selected) {
+ this.seg.animate({
+ path: this.selectedPath
+ }, 150, '<>');
+ this.arc.animate({
+ opacity: 1
+ }, 150, '<>');
+ return this.selected = true;
+ }
+ };
+
+ DonutSegment.prototype.deselect = function() {
+ if (this.selected) {
+ this.seg.animate({
+ path: this.path
+ }, 150, '<>');
+ this.arc.animate({
+ opacity: 0
+ }, 150, '<>');
+ return this.selected = false;
+ }
+ };
+
+ return DonutSegment;
+
+ })(Morris.EventEmitter);
+
+}).call(this);
diff --git a/public/bower_components/morris.js/morris.min.js b/public/bower_components/morris.js/morris.min.js
new file mode 100644
index 0000000..c3bda2f
--- /dev/null
+++ b/public/bower_components/morris.js/morris.min.js
@@ -0,0 +1,7 @@
+/* @license
+morris.js v0.5.0
+Copyright 2014 Olly Smith All rights reserved.
+Licensed under the BSD-2-Clause License.
+*/
+(function(){var a,b,c,d,e=[].slice,f=function(a,b){return function(){return a.apply(b,arguments)}},g={}.hasOwnProperty,h=function(a,b){function c(){this.constructor=a}for(var d in b)g.call(b,d)&&(a[d]=b[d]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},i=[].indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(b in this&&this[b]===a)return b;return-1};b=window.Morris={},a=jQuery,b.EventEmitter=function(){function a(){}return a.prototype.on=function(a,b){return null==this.handlers&&(this.handlers={}),null==this.handlers[a]&&(this.handlers[a]=[]),this.handlers[a].push(b),this},a.prototype.fire=function(){var a,b,c,d,f,g,h;if(c=arguments[0],a=2<=arguments.length?e.call(arguments,1):[],null!=this.handlers&&null!=this.handlers[c]){for(g=this.handlers[c],h=[],d=0,f=g.length;f>d;d++)b=g[d],h.push(b.apply(null,a));return h}},a}(),b.commas=function(a){var b,c,d,e;return null!=a?(d=0>a?"-":"",b=Math.abs(a),c=Math.floor(b).toFixed(0),d+=c.replace(/(?=(?:\d{3})+$)(?!^)/g,","),e=b.toString(),e.length>c.length&&(d+=e.slice(c.length)),d):"-"},b.pad2=function(a){return(10>a?"0":"")+a},b.Grid=function(c){function d(b){this.resizeHandler=f(this.resizeHandler,this);var c=this;if(this.el="string"==typeof b.element?a(document.getElementById(b.element)):a(b.element),null==this.el||0===this.el.length)throw new Error("Graph container element not found");"static"===this.el.css("position")&&this.el.css("position","relative"),this.options=a.extend({},this.gridDefaults,this.defaults||{},b),"string"==typeof this.options.units&&(this.options.postUnits=b.units),this.raphael=new Raphael(this.el[0]),this.elementWidth=null,this.elementHeight=null,this.dirty=!1,this.selectFrom=null,this.init&&this.init(),this.setData(this.options.data),this.el.bind("mousemove",function(a){var b,d,e,f,g;return d=c.el.offset(),g=a.pageX-d.left,c.selectFrom?(b=c.data[c.hitTest(Math.min(g,c.selectFrom))]._x,e=c.data[c.hitTest(Math.max(g,c.selectFrom))]._x,f=e-b,c.selectionRect.attr({x:b,width:f})):c.fire("hovermove",g,a.pageY-d.top)}),this.el.bind("mouseleave",function(){return c.selectFrom&&(c.selectionRect.hide(),c.selectFrom=null),c.fire("hoverout")}),this.el.bind("touchstart touchmove touchend",function(a){var b,d;return d=a.originalEvent.touches[0]||a.originalEvent.changedTouches[0],b=c.el.offset(),c.fire("hovermove",d.pageX-b.left,d.pageY-b.top)}),this.el.bind("click",function(a){var b;return b=c.el.offset(),c.fire("gridclick",a.pageX-b.left,a.pageY-b.top)}),this.options.rangeSelect&&(this.selectionRect=this.raphael.rect(0,0,0,this.el.innerHeight()).attr({fill:this.options.rangeSelectColor,stroke:!1}).toBack().hide(),this.el.bind("mousedown",function(a){var b;return b=c.el.offset(),c.startRange(a.pageX-b.left)}),this.el.bind("mouseup",function(a){var b;return b=c.el.offset(),c.endRange(a.pageX-b.left),c.fire("hovermove",a.pageX-b.left,a.pageY-b.top)})),this.options.resize&&a(window).bind("resize",function(){return null!=c.timeoutId&&window.clearTimeout(c.timeoutId),c.timeoutId=window.setTimeout(c.resizeHandler,100)}),this.el.css("-webkit-tap-highlight-color","rgba(0,0,0,0)"),this.postInit&&this.postInit()}return h(d,c),d.prototype.gridDefaults={dateFormat:null,axes:!0,grid:!0,gridLineColor:"#aaa",gridStrokeWidth:.5,gridTextColor:"#888",gridTextSize:12,gridTextFamily:"sans-serif",gridTextWeight:"normal",hideHover:!1,yLabelFormat:null,xLabelAngle:0,numLines:5,padding:25,parseTime:!0,postUnits:"",preUnits:"",ymax:"auto",ymin:"auto 0",goals:[],goalStrokeWidth:1,goalLineColors:["#666633","#999966","#cc6666","#663333"],events:[],eventStrokeWidth:1,eventLineColors:["#005a04","#ccffbb","#3a5f0b","#005502"],rangeSelect:null,rangeSelectColor:"#eef",resize:!1},d.prototype.setData=function(a,c){var d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;return null==c&&(c=!0),this.options.data=a,null==a||0===a.length?(this.data=[],this.raphael.clear(),null!=this.hover&&this.hover.hide(),void 0):(o=this.cumulative?0:null,p=this.cumulative?0:null,this.options.goals.length>0&&(h=Math.min.apply(Math,this.options.goals),g=Math.max.apply(Math,this.options.goals),p=null!=p?Math.min(p,h):h,o=null!=o?Math.max(o,g):g),this.data=function(){var c,d,g;for(g=[],f=c=0,d=a.length;d>c;f=++c)j=a[f],i={src:j},i.label=j[this.options.xkey],this.options.parseTime?(i.x=b.parseDate(i.label),this.options.dateFormat?i.label=this.options.dateFormat(i.x):"number"==typeof i.label&&(i.label=new Date(i.label).toString())):(i.x=f,this.options.xLabelFormat&&(i.label=this.options.xLabelFormat(i))),l=0,i.y=function(){var a,b,c,d;for(c=this.options.ykeys,d=[],e=a=0,b=c.length;b>a;e=++a)n=c[e],q=j[n],"string"==typeof q&&(q=parseFloat(q)),null!=q&&"number"!=typeof q&&(q=null),null!=q&&(this.cumulative?l+=q:null!=o?(o=Math.max(q,o),p=Math.min(q,p)):o=p=q),this.cumulative&&null!=l&&(o=Math.max(l,o),p=Math.min(l,p)),d.push(q);return d}.call(this),g.push(i);return g}.call(this),this.options.parseTime&&(this.data=this.data.sort(function(a,b){return(a.x>b.x)-(b.x>a.x)})),this.xmin=this.data[0].x,this.xmax=this.data[this.data.length-1].x,this.events=[],this.options.events.length>0&&(this.events=this.options.parseTime?function(){var a,c,e,f;for(e=this.options.events,f=[],a=0,c=e.length;c>a;a++)d=e[a],f.push(b.parseDate(d));return f}.call(this):this.options.events,this.xmax=Math.max(this.xmax,Math.max.apply(Math,this.events)),this.xmin=Math.min(this.xmin,Math.min.apply(Math,this.events))),this.xmin===this.xmax&&(this.xmin-=1,this.xmax+=1),this.ymin=this.yboundary("min",p),this.ymax=this.yboundary("max",o),this.ymin===this.ymax&&(p&&(this.ymin-=1),this.ymax+=1),((r=this.options.axes)===!0||"both"===r||"y"===r||this.options.grid===!0)&&(this.options.ymax===this.gridDefaults.ymax&&this.options.ymin===this.gridDefaults.ymin?(this.grid=this.autoGridLines(this.ymin,this.ymax,this.options.numLines),this.ymin=Math.min(this.ymin,this.grid[0]),this.ymax=Math.max(this.ymax,this.grid[this.grid.length-1])):(k=(this.ymax-this.ymin)/(this.options.numLines-1),this.grid=function(){var a,b,c,d;for(d=[],m=a=b=this.ymin,c=this.ymax;k>0?c>=a:a>=c;m=a+=k)d.push(m);return d}.call(this))),this.dirty=!0,c?this.redraw():void 0)},d.prototype.yboundary=function(a,b){var c,d;return c=this.options["y"+a],"string"==typeof c?"auto"===c.slice(0,4)?c.length>5?(d=parseInt(c.slice(5),10),null==b?d:Math[a](b,d)):null!=b?b:0:parseInt(c,10):c},d.prototype.autoGridLines=function(a,b,c){var d,e,f,g,h,i,j,k,l;return h=b-a,l=Math.floor(Math.log(h)/Math.log(10)),j=Math.pow(10,l),e=Math.floor(a/j)*j,d=Math.ceil(b/j)*j,i=(d-e)/(c-1),1===j&&i>1&&Math.ceil(i)!==i&&(i=Math.ceil(i),d=e+i*(c-1)),0>e&&d>0&&(e=Math.floor(a/i)*i,d=Math.ceil(b/i)*i),1>i?(g=Math.floor(Math.log(i)/Math.log(10)),f=function(){var a,b;for(b=[],k=a=e;i>0?d>=a:a>=d;k=a+=i)b.push(parseFloat(k.toFixed(1-g)));return b}()):f=function(){var a,b;for(b=[],k=a=e;i>0?d>=a:a>=d;k=a+=i)b.push(k);return b}(),f},d.prototype._calc=function(){var a,b,c,d,e,f,g,h;return e=this.el.width(),c=this.el.height(),(this.elementWidth!==e||this.elementHeight!==c||this.dirty)&&(this.elementWidth=e,this.elementHeight=c,this.dirty=!1,this.left=this.options.padding,this.right=this.elementWidth-this.options.padding,this.top=this.options.padding,this.bottom=this.elementHeight-this.options.padding,((g=this.options.axes)===!0||"both"===g||"y"===g)&&(f=function(){var a,c,d,e;for(d=this.grid,e=[],a=0,c=d.length;c>a;a++)b=d[a],e.push(this.measureText(this.yAxisFormat(b)).width);return e}.call(this),this.left+=Math.max.apply(Math,f)),((h=this.options.axes)===!0||"both"===h||"x"===h)&&(a=function(){var a,b,c;for(c=[],d=a=0,b=this.data.length;b>=0?b>a:a>b;d=b>=0?++a:--a)c.push(this.measureText(this.data[d].text,-this.options.xLabelAngle).height);return c}.call(this),this.bottom-=Math.max.apply(Math,a)),this.width=Math.max(1,this.right-this.left),this.height=Math.max(1,this.bottom-this.top),this.dx=this.width/(this.xmax-this.xmin),this.dy=this.height/(this.ymax-this.ymin),this.calc)?this.calc():void 0},d.prototype.transY=function(a){return this.bottom-(a-this.ymin)*this.dy},d.prototype.transX=function(a){return 1===this.data.length?(this.left+this.right)/2:this.left+(a-this.xmin)*this.dx},d.prototype.redraw=function(){return this.raphael.clear(),this._calc(),this.drawGrid(),this.drawGoals(),this.drawEvents(),this.draw?this.draw():void 0},d.prototype.measureText=function(a,b){var c,d;return null==b&&(b=0),d=this.raphael.text(100,100,a).attr("font-size",this.options.gridTextSize).attr("font-family",this.options.gridTextFamily).attr("font-weight",this.options.gridTextWeight).rotate(b),c=d.getBBox(),d.remove(),c},d.prototype.yAxisFormat=function(a){return this.yLabelFormat(a)},d.prototype.yLabelFormat=function(a){return"function"==typeof this.options.yLabelFormat?this.options.yLabelFormat(a):""+this.options.preUnits+b.commas(a)+this.options.postUnits},d.prototype.drawGrid=function(){var a,b,c,d,e,f,g,h;if(this.options.grid!==!1||(e=this.options.axes)===!0||"both"===e||"y"===e){for(f=this.grid,h=[],c=0,d=f.length;d>c;c++)a=f[c],b=this.transY(a),((g=this.options.axes)===!0||"both"===g||"y"===g)&&this.drawYAxisLabel(this.left-this.options.padding/2,b,this.yAxisFormat(a)),this.options.grid?h.push(this.drawGridLine("M"+this.left+","+b+"H"+(this.left+this.width))):h.push(void 0);return h}},d.prototype.drawGoals=function(){var a,b,c,d,e,f,g;for(f=this.options.goals,g=[],c=d=0,e=f.length;e>d;c=++d)b=f[c],a=this.options.goalLineColors[c%this.options.goalLineColors.length],g.push(this.drawGoal(b,a));return g},d.prototype.drawEvents=function(){var a,b,c,d,e,f,g;for(f=this.events,g=[],c=d=0,e=f.length;e>d;c=++d)b=f[c],a=this.options.eventLineColors[c%this.options.eventLineColors.length],g.push(this.drawEvent(b,a));return g},d.prototype.drawGoal=function(a,b){return this.raphael.path("M"+this.left+","+this.transY(a)+"H"+this.right).attr("stroke",b).attr("stroke-width",this.options.goalStrokeWidth)},d.prototype.drawEvent=function(a,b){return this.raphael.path("M"+this.transX(a)+","+this.bottom+"V"+this.top).attr("stroke",b).attr("stroke-width",this.options.eventStrokeWidth)},d.prototype.drawYAxisLabel=function(a,b,c){return this.raphael.text(a,b,c).attr("font-size",this.options.gridTextSize).attr("font-family",this.options.gridTextFamily).attr("font-weight",this.options.gridTextWeight).attr("fill",this.options.gridTextColor).attr("text-anchor","end")},d.prototype.drawGridLine=function(a){return this.raphael.path(a).attr("stroke",this.options.gridLineColor).attr("stroke-width",this.options.gridStrokeWidth)},d.prototype.startRange=function(a){return this.hover.hide(),this.selectFrom=a,this.selectionRect.attr({x:a,width:0}).show()},d.prototype.endRange=function(a){var b,c;return this.selectFrom?(c=Math.min(this.selectFrom,a),b=Math.max(this.selectFrom,a),this.options.rangeSelect.call(this.el,{start:this.data[this.hitTest(c)].x,end:this.data[this.hitTest(b)].x}),this.selectFrom=null):void 0},d.prototype.resizeHandler=function(){return this.timeoutId=null,this.raphael.setSize(this.el.width(),this.el.height()),this.redraw()},d}(b.EventEmitter),b.parseDate=function(a){var b,c,d,e,f,g,h,i,j,k,l;return"number"==typeof a?a:(c=a.match(/^(\d+) Q(\d)$/),e=a.match(/^(\d+)-(\d+)$/),f=a.match(/^(\d+)-(\d+)-(\d+)$/),h=a.match(/^(\d+) W(\d+)$/),i=a.match(/^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+)(Z|([+-])(\d\d):?(\d\d))?$/),j=a.match(/^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+):(\d+(\.\d+)?)(Z|([+-])(\d\d):?(\d\d))?$/),c?new Date(parseInt(c[1],10),3*parseInt(c[2],10)-1,1).getTime():e?new Date(parseInt(e[1],10),parseInt(e[2],10)-1,1).getTime():f?new Date(parseInt(f[1],10),parseInt(f[2],10)-1,parseInt(f[3],10)).getTime():h?(k=new Date(parseInt(h[1],10),0,1),4!==k.getDay()&&k.setMonth(0,1+(4-k.getDay()+7)%7),k.getTime()+6048e5*parseInt(h[2],10)):i?i[6]?(g=0,"Z"!==i[6]&&(g=60*parseInt(i[8],10)+parseInt(i[9],10),"+"===i[7]&&(g=0-g)),Date.UTC(parseInt(i[1],10),parseInt(i[2],10)-1,parseInt(i[3],10),parseInt(i[4],10),parseInt(i[5],10)+g)):new Date(parseInt(i[1],10),parseInt(i[2],10)-1,parseInt(i[3],10),parseInt(i[4],10),parseInt(i[5],10)).getTime():j?(l=parseFloat(j[6]),b=Math.floor(l),d=Math.round(1e3*(l-b)),j[8]?(g=0,"Z"!==j[8]&&(g=60*parseInt(j[10],10)+parseInt(j[11],10),"+"===j[9]&&(g=0-g)),Date.UTC(parseInt(j[1],10),parseInt(j[2],10)-1,parseInt(j[3],10),parseInt(j[4],10),parseInt(j[5],10)+g,b,d)):new Date(parseInt(j[1],10),parseInt(j[2],10)-1,parseInt(j[3],10),parseInt(j[4],10),parseInt(j[5],10),b,d).getTime()):new Date(parseInt(a,10),0,1).getTime())},b.Hover=function(){function c(c){null==c&&(c={}),this.options=a.extend({},b.Hover.defaults,c),this.el=a("<div class='"+this.options["class"]+"'></div>"),this.el.hide(),this.options.parent.append(this.el)}return c.defaults={"class":"morris-hover morris-default-style"},c.prototype.update=function(a,b,c){return a?(this.html(a),this.show(),this.moveTo(b,c)):this.hide()},c.prototype.html=function(a){return this.el.html(a)},c.prototype.moveTo=function(a,b){var c,d,e,f,g,h;return g=this.options.parent.innerWidth(),f=this.options.parent.innerHeight(),d=this.el.outerWidth(),c=this.el.outerHeight(),e=Math.min(Math.max(0,a-d/2),g-d),null!=b?(h=b-c-10,0>h&&(h=b+10,h+c>f&&(h=f/2-c/2))):h=f/2-c/2,this.el.css({left:e+"px",top:parseInt(h)+"px"})},c.prototype.show=function(){return this.el.show()},c.prototype.hide=function(){return this.el.hide()},c}(),b.Line=function(a){function c(a){return this.hilight=f(this.hilight,this),this.onHoverOut=f(this.onHoverOut,this),this.onHoverMove=f(this.onHoverMove,this),this.onGridClick=f(this.onGridClick,this),this instanceof b.Line?(c.__super__.constructor.call(this,a),void 0):new b.Line(a)}return h(c,a),c.prototype.init=function(){return"always"!==this.options.hideHover?(this.hover=new b.Hover({parent:this.el}),this.on("hovermove",this.onHoverMove),this.on("hoverout",this.onHoverOut),this.on("gridclick",this.onGridClick)):void 0},c.prototype.defaults={lineWidth:3,pointSize:4,lineColors:["#0b62a4","#7A92A3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],pointStrokeWidths:[1],pointStrokeColors:["#ffffff"],pointFillColors:[],smooth:!0,xLabels:"auto",xLabelFormat:null,xLabelMargin:24,hideHover:!1},c.prototype.calc=function(){return this.calcPoints(),this.generatePaths()},c.prototype.calcPoints=function(){var a,b,c,d,e,f;for(e=this.data,f=[],c=0,d=e.length;d>c;c++)a=e[c],a._x=this.transX(a.x),a._y=function(){var c,d,e,f;for(e=a.y,f=[],c=0,d=e.length;d>c;c++)b=e[c],null!=b?f.push(this.transY(b)):f.push(b);return f}.call(this),f.push(a._ymax=Math.min.apply(Math,[this.bottom].concat(function(){var c,d,e,f;for(e=a._y,f=[],c=0,d=e.length;d>c;c++)b=e[c],null!=b&&f.push(b);return f}())));return f},c.prototype.hitTest=function(a){var b,c,d,e,f;if(0===this.data.length)return null;for(f=this.data.slice(1),b=d=0,e=f.length;e>d&&(c=f[b],!(a<(c._x+this.data[b]._x)/2));b=++d);return b},c.prototype.onGridClick=function(a,b){var c;return c=this.hitTest(a),this.fire("click",c,this.data[c].src,a,b)},c.prototype.onHoverMove=function(a){var b;return b=this.hitTest(a),this.displayHoverForRow(b)},c.prototype.onHoverOut=function(){return this.options.hideHover!==!1?this.displayHoverForRow(null):void 0},c.prototype.displayHoverForRow=function(a){var b;return null!=a?((b=this.hover).update.apply(b,this.hoverContentForRow(a)),this.hilight(a)):(this.hover.hide(),this.hilight())},c.prototype.hoverContentForRow=function(a){var b,c,d,e,f,g,h;for(d=this.data[a],b="<div class='morris-hover-row-label'>"+d.label+"</div>",h=d.y,c=f=0,g=h.length;g>f;c=++f)e=h[c],b+="<div class='morris-hover-point' style='color: "+this.colorFor(d,c,"label")+"'>\n "+this.options.labels[c]+":\n "+this.yLabelFormat(e)+"\n</div>";return"function"==typeof this.options.hoverCallback&&(b=this.options.hoverCallback(a,this.options,b,d.src)),[b,d._x,d._ymax]},c.prototype.generatePaths=function(){var a,c,d,e;return this.paths=function(){var f,g,h,j;for(j=[],c=f=0,g=this.options.ykeys.length;g>=0?g>f:f>g;c=g>=0?++f:--f)e="boolean"==typeof this.options.smooth?this.options.smooth:(h=this.options.ykeys[c],i.call(this.options.smooth,h)>=0),a=function(){var a,b,e,f;for(e=this.data,f=[],a=0,b=e.length;b>a;a++)d=e[a],void 0!==d._y[c]&&f.push({x:d._x,y:d._y[c]});return f}.call(this),a.length>1?j.push(b.Line.createPath(a,e,this.bottom)):j.push(null);return j}.call(this)},c.prototype.draw=function(){var a;return((a=this.options.axes)===!0||"both"===a||"x"===a)&&this.drawXAxis(),this.drawSeries(),this.options.hideHover===!1?this.displayHoverForRow(this.data.length-1):void 0},c.prototype.drawXAxis=function(){var a,c,d,e,f,g,h,i,j,k,l=this;for(h=this.bottom+this.options.padding/2,f=null,e=null,a=function(a,b){var c,d,g,i,j;return c=l.drawXAxisLabel(l.transX(b),h,a),j=c.getBBox(),c.transform("r"+-l.options.xLabelAngle),d=c.getBBox(),c.transform("t0,"+d.height/2+"..."),0!==l.options.xLabelAngle&&(i=-.5*j.width*Math.cos(l.options.xLabelAngle*Math.PI/180),c.transform("t"+i+",0...")),d=c.getBBox(),(null==f||f>=d.x+d.width||null!=e&&e>=d.x)&&d.x>=0&&d.x+d.width<l.el.width()?(0!==l.options.xLabelAngle&&(g=1.25*l.options.gridTextSize/Math.sin(l.options.xLabelAngle*Math.PI/180),e=d.x-g),f=d.x-l.options.xLabelMargin):c.remove()},d=this.options.parseTime?1===this.data.length&&"auto"===this.options.xLabels?[[this.data[0].label,this.data[0].x]]:b.labelSeries(this.xmin,this.xmax,this.width,this.options.xLabels,this.options.xLabelFormat):function(){var a,b,c,d;for(c=this.data,d=[],a=0,b=c.length;b>a;a++)g=c[a],d.push([g.label,g.x]);return d}.call(this),d.reverse(),k=[],i=0,j=d.length;j>i;i++)c=d[i],k.push(a(c[0],c[1]));return k},c.prototype.drawSeries=function(){var a,b,c,d,e,f;for(this.seriesPoints=[],a=b=d=this.options.ykeys.length-1;0>=d?0>=b:b>=0;a=0>=d?++b:--b)this._drawLineFor(a);for(f=[],a=c=e=this.options.ykeys.length-1;0>=e?0>=c:c>=0;a=0>=e?++c:--c)f.push(this._drawPointFor(a));return f},c.prototype._drawPointFor=function(a){var b,c,d,e,f,g;for(this.seriesPoints[a]=[],f=this.data,g=[],d=0,e=f.length;e>d;d++)c=f[d],b=null,null!=c._y[a]&&(b=this.drawLinePoint(c._x,c._y[a],this.colorFor(c,a,"point"),a)),g.push(this.seriesPoints[a].push(b));return g},c.prototype._drawLineFor=function(a){var b;return b=this.paths[a],null!==b?this.drawLinePath(b,this.colorFor(null,a,"line"),a):void 0},c.createPath=function(a,c,d){var e,f,g,h,i,j,k,l,m,n,o,p,q,r;for(k="",c&&(g=b.Line.gradients(a)),l={y:null},h=q=0,r=a.length;r>q;h=++q)e=a[h],null!=e.y&&(null!=l.y?c?(f=g[h],j=g[h-1],i=(e.x-l.x)/4,m=l.x+i,o=Math.min(d,l.y+i*j),n=e.x-i,p=Math.min(d,e.y-i*f),k+="C"+m+","+o+","+n+","+p+","+e.x+","+e.y):k+="L"+e.x+","+e.y:c&&null==g[h]||(k+="M"+e.x+","+e.y)),l=e;return k},c.gradients=function(a){var b,c,d,e,f,g,h,i;for(c=function(a,b){return(a.y-b.y)/(a.x-b.x)},i=[],d=g=0,h=a.length;h>g;d=++g)b=a[d],null!=b.y?(e=a[d+1]||{y:null},f=a[d-1]||{y:null},null!=f.y&&null!=e.y?i.push(c(f,e)):null!=f.y?i.push(c(f,b)):null!=e.y?i.push(c(b,e)):i.push(null)):i.push(null);return i},c.prototype.hilight=function(a){var b,c,d,e,f;if(null!==this.prevHilight&&this.prevHilight!==a)for(b=c=0,e=this.seriesPoints.length-1;e>=0?e>=c:c>=e;b=e>=0?++c:--c)this.seriesPoints[b][this.prevHilight]&&this.seriesPoints[b][this.prevHilight].animate(this.pointShrinkSeries(b));if(null!==a&&this.prevHilight!==a)for(b=d=0,f=this.seriesPoints.length-1;f>=0?f>=d:d>=f;b=f>=0?++d:--d)this.seriesPoints[b][a]&&this.seriesPoints[b][a].animate(this.pointGrowSeries(b));return this.prevHilight=a},c.prototype.colorFor=function(a,b,c){return"function"==typeof this.options.lineColors?this.options.lineColors.call(this,a,b,c):"point"===c?this.options.pointFillColors[b%this.options.pointFillColors.length]||this.options.lineColors[b%this.options.lineColors.length]:this.options.lineColors[b%this.options.lineColors.length]},c.prototype.drawXAxisLabel=function(a,b,c){return this.raphael.text(a,b,c).attr("font-size",this.options.gridTextSize).attr("font-family",this.options.gridTextFamily).attr("font-weight",this.options.gridTextWeight).attr("fill",this.options.gridTextColor)},c.prototype.drawLinePath=function(a,b,c){return this.raphael.path(a).attr("stroke",b).attr("stroke-width",this.lineWidthForSeries(c))},c.prototype.drawLinePoint=function(a,b,c,d){return this.raphael.circle(a,b,this.pointSizeForSeries(d)).attr("fill",c).attr("stroke-width",this.pointStrokeWidthForSeries(d)).attr("stroke",this.pointStrokeColorForSeries(d))},c.prototype.pointStrokeWidthForSeries=function(a){return this.options.pointStrokeWidths[a%this.options.pointStrokeWidths.length]},c.prototype.pointStrokeColorForSeries=function(a){return this.options.pointStrokeColors[a%this.options.pointStrokeColors.length]},c.prototype.lineWidthForSeries=function(a){return this.options.lineWidth instanceof Array?this.options.lineWidth[a%this.options.lineWidth.length]:this.options.lineWidth},c.prototype.pointSizeForSeries=function(a){return this.options.pointSize instanceof Array?this.options.pointSize[a%this.options.pointSize.length]:this.options.pointSize},c.prototype.pointGrowSeries=function(a){return Raphael.animation({r:this.pointSizeForSeries(a)+3},25,"linear")},c.prototype.pointShrinkSeries=function(a){return Raphael.animation({r:this.pointSizeForSeries(a)},25,"linear")},c}(b.Grid),b.labelSeries=function(c,d,e,f,g){var h,i,j,k,l,m,n,o,p,q,r;if(j=200*(d-c)/e,i=new Date(c),n=b.LABEL_SPECS[f],void 0===n)for(r=b.AUTO_LABEL_ORDER,p=0,q=r.length;q>p;p++)if(k=r[p],m=b.LABEL_SPECS[k],j>=m.span){n=m;break}for(void 0===n&&(n=b.LABEL_SPECS.second),g&&(n=a.extend({},n,{fmt:g})),h=n.start(i),l=[];(o=h.getTime())<=d;)o>=c&&l.push([n.fmt(h),o]),n.incr(h);return l},c=function(a){return{span:60*a*1e3,start:function(a){return new Date(a.getFullYear(),a.getMonth(),a.getDate(),a.getHours())},fmt:function(a){return""+b.pad2(a.getHours())+":"+b.pad2(a.getMinutes())},incr:function(b){return b.setUTCMinutes(b.getUTCMinutes()+a)}}},d=function(a){return{span:1e3*a,start:function(a){return new Date(a.getFullYear(),a.getMonth(),a.getDate(),a.getHours(),a.getMinutes())},fmt:function(a){return""+b.pad2(a.getHours())+":"+b.pad2(a.getMinutes())+":"+b.pad2(a.getSeconds())},incr:function(b){return b.setUTCSeconds(b.getUTCSeconds()+a)}}},b.LABEL_SPECS={decade:{span:1728e8,start:function(a){return new Date(a.getFullYear()-a.getFullYear()%10,0,1)},fmt:function(a){return""+a.getFullYear()},incr:function(a){return a.setFullYear(a.getFullYear()+10)}},year:{span:1728e7,start:function(a){return new Date(a.getFullYear(),0,1)},fmt:function(a){return""+a.getFullYear()},incr:function(a){return a.setFullYear(a.getFullYear()+1)}},month:{span:24192e5,start:function(a){return new Date(a.getFullYear(),a.getMonth(),1)},fmt:function(a){return""+a.getFullYear()+"-"+b.pad2(a.getMonth()+1)},incr:function(a){return a.setMonth(a.getMonth()+1)}},week:{span:6048e5,start:function(a){return new Date(a.getFullYear(),a.getMonth(),a.getDate())},fmt:function(a){return""+a.getFullYear()+"-"+b.pad2(a.getMonth()+1)+"-"+b.pad2(a.getDate())},incr:function(a){return a.setDate(a.getDate()+7)}},day:{span:864e5,start:function(a){return new Date(a.getFullYear(),a.getMonth(),a.getDate())},fmt:function(a){return""+a.getFullYear()+"-"+b.pad2(a.getMonth()+1)+"-"+b.pad2(a.getDate())},incr:function(a){return a.setDate(a.getDate()+1)}},hour:c(60),"30min":c(30),"15min":c(15),"10min":c(10),"5min":c(5),minute:c(1),"30sec":d(30),"15sec":d(15),"10sec":d(10),"5sec":d(5),second:d(1)},b.AUTO_LABEL_ORDER=["decade","year","month","week","day","hour","30min","15min","10min","5min","minute","30sec","15sec","10sec","5sec","second"],b.Area=function(c){function d(c){var f;return this instanceof b.Area?(f=a.extend({},e,c),this.cumulative=!f.behaveLikeLine,"auto"===f.fillOpacity&&(f.fillOpacity=f.behaveLikeLine?.8:1),d.__super__.constructor.call(this,f),void 0):new b.Area(c)}var e;return h(d,c),e={fillOpacity:"auto",behaveLikeLine:!1},d.prototype.calcPoints=function(){var a,b,c,d,e,f,g;for(f=this.data,g=[],d=0,e=f.length;e>d;d++)a=f[d],a._x=this.transX(a.x),b=0,a._y=function(){var d,e,f,g;for(f=a.y,g=[],d=0,e=f.length;e>d;d++)c=f[d],this.options.behaveLikeLine?g.push(this.transY(c)):(b+=c||0,g.push(this.transY(b)));return g}.call(this),g.push(a._ymax=Math.max.apply(Math,a._y));return g},d.prototype.drawSeries=function(){var a,b,c,d,e,f,g,h;for(this.seriesPoints=[],b=this.options.behaveLikeLine?function(){f=[];for(var a=0,b=this.options.ykeys.length-1;b>=0?b>=a:a>=b;b>=0?a++:a--)f.push(a);return f}.apply(this):function(){g=[];for(var a=e=this.options.ykeys.length-1;0>=e?0>=a:a>=0;0>=e?a++:a--)g.push(a);return g}.apply(this),h=[],c=0,d=b.length;d>c;c++)a=b[c],this._drawFillFor(a),this._drawLineFor(a),h.push(this._drawPointFor(a));return h},d.prototype._drawFillFor=function(a){var b;return b=this.paths[a],null!==b?(b+="L"+this.transX(this.xmax)+","+this.bottom+"L"+this.transX(this.xmin)+","+this.bottom+"Z",this.drawFilledPath(b,this.fillForSeries(a))):void 0},d.prototype.fillForSeries=function(a){var b;return b=Raphael.rgb2hsl(this.colorFor(this.data[a],a,"line")),Raphael.hsl(b.h,this.options.behaveLikeLine?.9*b.s:.75*b.s,Math.min(.98,this.options.behaveLikeLine?1.2*b.l:1.25*b.l))},d.prototype.drawFilledPath=function(a,b){return this.raphael.path(a).attr("fill",b).attr("fill-opacity",this.options.fillOpacity).attr("stroke","none")},d}(b.Line),b.Bar=function(c){function d(c){return this.onHoverOut=f(this.onHoverOut,this),this.onHoverMove=f(this.onHoverMove,this),this.onGridClick=f(this.onGridClick,this),this instanceof b.Bar?(d.__super__.constructor.call(this,a.extend({},c,{parseTime:!1})),void 0):new b.Bar(c)}return h(d,c),d.prototype.init=function(){return this.cumulative=this.options.stacked,"always"!==this.options.hideHover?(this.hover=new b.Hover({parent:this.el}),this.on("hovermove",this.onHoverMove),this.on("hoverout",this.onHoverOut),this.on("gridclick",this.onGridClick)):void 0},d.prototype.defaults={barSizeRatio:.75,barGap:3,barColors:["#0b62a4","#7a92a3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],barOpacity:1,barRadius:[0,0,0,0],xLabelMargin:50},d.prototype.calc=function(){var a;return this.calcBars(),this.options.hideHover===!1?(a=this.hover).update.apply(a,this.hoverContentForRow(this.data.length-1)):void 0},d.prototype.calcBars=function(){var a,b,c,d,e,f,g;for(f=this.data,g=[],a=d=0,e=f.length;e>d;a=++d)b=f[a],b._x=this.left+this.width*(a+.5)/this.data.length,g.push(b._y=function(){var a,d,e,f;for(e=b.y,f=[],a=0,d=e.length;d>a;a++)c=e[a],null!=c?f.push(this.transY(c)):f.push(null);return f}.call(this));return g},d.prototype.draw=function(){var a;return((a=this.options.axes)===!0||"both"===a||"x"===a)&&this.drawXAxis(),this.drawSeries()},d.prototype.drawXAxis=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m;for(j=this.bottom+(this.options.xAxisLabelTopPadding||this.options.padding/2),g=null,f=null,m=[],a=k=0,l=this.data.length;l>=0?l>k:k>l;a=l>=0?++k:--k)h=this.data[this.data.length-1-a],b=this.drawXAxisLabel(h._x,j,h.label),i=b.getBBox(),b.transform("r"+-this.options.xLabelAngle),c=b.getBBox(),b.transform("t0,"+c.height/2+"..."),0!==this.options.xLabelAngle&&(e=-.5*i.width*Math.cos(this.options.xLabelAngle*Math.PI/180),b.transform("t"+e+",0...")),(null==g||g>=c.x+c.width||null!=f&&f>=c.x)&&c.x>=0&&c.x+c.width<this.el.width()?(0!==this.options.xLabelAngle&&(d=1.25*this.options.gridTextSize/Math.sin(this.options.xLabelAngle*Math.PI/180),f=c.x-d),m.push(g=c.x-this.options.xLabelMargin)):m.push(b.remove());return m},d.prototype.drawSeries=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m,n,o;return c=this.width/this.options.data.length,h=this.options.stacked?1:this.options.ykeys.length,a=(c*this.options.barSizeRatio-this.options.barGap*(h-1))/h,this.options.barSize&&(a=Math.min(a,this.options.barSize)),l=c-a*h-this.options.barGap*(h-1),g=l/2,o=this.ymin<=0&&this.ymax>=0?this.transY(0):null,this.bars=function(){var h,l,p,q;for(p=this.data,q=[],d=h=0,l=p.length;l>h;d=++h)i=p[d],e=0,q.push(function(){var h,l,p,q;for(p=i._y,q=[],j=h=0,l=p.length;l>h;j=++h)n=p[j],null!==n?(o?(m=Math.min(n,o),b=Math.max(n,o)):(m=n,b=this.bottom),f=this.left+d*c+g,this.options.stacked||(f+=j*(a+this.options.barGap)),k=b-m,this.options.verticalGridCondition&&this.options.verticalGridCondition(i.x)&&this.drawBar(this.left+d*c,this.top,c,Math.abs(this.top-this.bottom),this.options.verticalGridColor,this.options.verticalGridOpacity,this.options.barRadius),this.options.stacked&&(m-=e),this.drawBar(f,m,a,k,this.colorFor(i,j,"bar"),this.options.barOpacity,this.options.barRadius),q.push(e+=k)):q.push(null);return q}.call(this));return q}.call(this)},d.prototype.colorFor=function(a,b,c){var d,e;return"function"==typeof this.options.barColors?(d={x:a.x,y:a.y[b],label:a.label},e={index:b,key:this.options.ykeys[b],label:this.options.labels[b]},this.options.barColors.call(this,d,e,c)):this.options.barColors[b%this.options.barColors.length]},d.prototype.hitTest=function(a){return 0===this.data.length?null:(a=Math.max(Math.min(a,this.right),this.left),Math.min(this.data.length-1,Math.floor((a-this.left)/(this.width/this.data.length))))},d.prototype.onGridClick=function(a,b){var c;return c=this.hitTest(a),this.fire("click",c,this.data[c].src,a,b)},d.prototype.onHoverMove=function(a){var b,c;return b=this.hitTest(a),(c=this.hover).update.apply(c,this.hoverContentForRow(b))},d.prototype.onHoverOut=function(){return this.options.hideHover!==!1?this.hover.hide():void 0},d.prototype.hoverContentForRow=function(a){var b,c,d,e,f,g,h,i;for(d=this.data[a],b="<div class='morris-hover-row-label'>"+d.label+"</div>",i=d.y,c=g=0,h=i.length;h>g;c=++g)f=i[c],b+="<div class='morris-hover-point' style='color: "+this.colorFor(d,c,"label")+"'>\n "+this.options.labels[c]+":\n "+this.yLabelFormat(f)+"\n</div>";return"function"==typeof this.options.hoverCallback&&(b=this.options.hoverCallback(a,this.options,b,d.src)),e=this.left+(a+.5)*this.width/this.data.length,[b,e]},d.prototype.drawXAxisLabel=function(a,b,c){var d;return d=this.raphael.text(a,b,c).attr("font-size",this.options.gridTextSize).attr("font-family",this.options.gridTextFamily).attr("font-weight",this.options.gridTextWeight).attr("fill",this.options.gridTextColor)},d.prototype.drawBar=function(a,b,c,d,e,f,g){var h,i;return h=Math.max.apply(Math,g),i=0===h||h>d?this.raphael.rect(a,b,c,d):this.raphael.path(this.roundedRect(a,b,c,d,g)),i.attr("fill",e).attr("fill-opacity",f).attr("stroke","none")},d.prototype.roundedRect=function(a,b,c,d,e){return null==e&&(e=[0,0,0,0]),["M",a,e[0]+b,"Q",a,b,a+e[0],b,"L",a+c-e[1],b,"Q",a+c,b,a+c,b+e[1],"L",a+c,b+d-e[2],"Q",a+c,b+d,a+c-e[2],b+d,"L",a+e[3],b+d,"Q",a,b+d,a,b+d-e[3],"Z"]},d}(b.Grid),b.Donut=function(c){function d(c){this.resizeHandler=f(this.resizeHandler,this),this.select=f(this.select,this),this.click=f(this.click,this);var d=this;if(!(this instanceof b.Donut))return new b.Donut(c);if(this.options=a.extend({},this.defaults,c),this.el="string"==typeof c.element?a(document.getElementById(c.element)):a(c.element),null===this.el||0===this.el.length)throw new Error("Graph placeholder not found.");void 0!==c.data&&0!==c.data.length&&(this.raphael=new Raphael(this.el[0]),this.options.resize&&a(window).bind("resize",function(){return null!=d.timeoutId&&window.clearTimeout(d.timeoutId),d.timeoutId=window.setTimeout(d.resizeHandler,100)}),this.setData(c.data))}return h(d,c),d.prototype.defaults={colors:["#0B62A4","#3980B5","#679DC6","#95BBD7","#B0CCE1","#095791","#095085","#083E67","#052C48","#042135"],backgroundColor:"#FFFFFF",labelColor:"#000000",formatter:b.commas,resize:!1},d.prototype.redraw=function(){var a,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x;for(this.raphael.clear(),c=this.el.width()/2,d=this.el.height()/2,n=(Math.min(c,d)-10)/3,l=0,u=this.values,o=0,r=u.length;r>o;o++)m=u[o],l+=m;for(i=5/(2*n),a=1.9999*Math.PI-i*this.data.length,g=0,f=0,this.segments=[],v=this.values,e=p=0,s=v.length;s>p;e=++p)m=v[e],j=g+i+a*(m/l),k=new b.DonutSegment(c,d,2*n,n,g,j,this.data[e].color||this.options.colors[f%this.options.colors.length],this.options.backgroundColor,f,this.raphael),k.render(),this.segments.push(k),k.on("hover",this.select),k.on("click",this.click),g=j,f+=1;for(this.text1=this.drawEmptyDonutLabel(c,d-10,this.options.labelColor,15,800),this.text2=this.drawEmptyDonutLabel(c,d+10,this.options.labelColor,14),h=Math.max.apply(Math,this.values),f=0,w=this.values,x=[],q=0,t=w.length;t>q;q++){if(m=w[q],m===h){this.select(f);
+break}x.push(f+=1)}return x},d.prototype.setData=function(a){var b;return this.data=a,this.values=function(){var a,c,d,e;for(d=this.data,e=[],a=0,c=d.length;c>a;a++)b=d[a],e.push(parseFloat(b.value));return e}.call(this),this.redraw()},d.prototype.click=function(a){return this.fire("click",a,this.data[a])},d.prototype.select=function(a){var b,c,d,e,f,g;for(g=this.segments,e=0,f=g.length;f>e;e++)c=g[e],c.deselect();return d=this.segments[a],d.select(),b=this.data[a],this.setLabels(b.label,this.options.formatter(b.value,b))},d.prototype.setLabels=function(a,b){var c,d,e,f,g,h,i,j;return c=2*(Math.min(this.el.width()/2,this.el.height()/2)-10)/3,f=1.8*c,e=c/2,d=c/3,this.text1.attr({text:a,transform:""}),g=this.text1.getBBox(),h=Math.min(f/g.width,e/g.height),this.text1.attr({transform:"S"+h+","+h+","+(g.x+g.width/2)+","+(g.y+g.height)}),this.text2.attr({text:b,transform:""}),i=this.text2.getBBox(),j=Math.min(f/i.width,d/i.height),this.text2.attr({transform:"S"+j+","+j+","+(i.x+i.width/2)+","+i.y})},d.prototype.drawEmptyDonutLabel=function(a,b,c,d,e){var f;return f=this.raphael.text(a,b,"").attr("font-size",d).attr("fill",c),null!=e&&f.attr("font-weight",e),f},d.prototype.resizeHandler=function(){return this.timeoutId=null,this.raphael.setSize(this.el.width(),this.el.height()),this.redraw()},d}(b.EventEmitter),b.DonutSegment=function(a){function b(a,b,c,d,e,g,h,i,j,k){this.cx=a,this.cy=b,this.inner=c,this.outer=d,this.color=h,this.backgroundColor=i,this.index=j,this.raphael=k,this.deselect=f(this.deselect,this),this.select=f(this.select,this),this.sin_p0=Math.sin(e),this.cos_p0=Math.cos(e),this.sin_p1=Math.sin(g),this.cos_p1=Math.cos(g),this.is_long=g-e>Math.PI?1:0,this.path=this.calcSegment(this.inner+3,this.inner+this.outer-5),this.selectedPath=this.calcSegment(this.inner+3,this.inner+this.outer),this.hilight=this.calcArc(this.inner)}return h(b,a),b.prototype.calcArcPoints=function(a){return[this.cx+a*this.sin_p0,this.cy+a*this.cos_p0,this.cx+a*this.sin_p1,this.cy+a*this.cos_p1]},b.prototype.calcSegment=function(a,b){var c,d,e,f,g,h,i,j,k,l;return k=this.calcArcPoints(a),c=k[0],e=k[1],d=k[2],f=k[3],l=this.calcArcPoints(b),g=l[0],i=l[1],h=l[2],j=l[3],"M"+c+","+e+("A"+a+","+a+",0,"+this.is_long+",0,"+d+","+f)+("L"+h+","+j)+("A"+b+","+b+",0,"+this.is_long+",1,"+g+","+i)+"Z"},b.prototype.calcArc=function(a){var b,c,d,e,f;return f=this.calcArcPoints(a),b=f[0],d=f[1],c=f[2],e=f[3],"M"+b+","+d+("A"+a+","+a+",0,"+this.is_long+",0,"+c+","+e)},b.prototype.render=function(){var a=this;return this.arc=this.drawDonutArc(this.hilight,this.color),this.seg=this.drawDonutSegment(this.path,this.color,this.backgroundColor,function(){return a.fire("hover",a.index)},function(){return a.fire("click",a.index)})},b.prototype.drawDonutArc=function(a,b){return this.raphael.path(a).attr({stroke:b,"stroke-width":2,opacity:0})},b.prototype.drawDonutSegment=function(a,b,c,d,e){return this.raphael.path(a).attr({fill:b,stroke:c,"stroke-width":3}).hover(d).click(e)},b.prototype.select=function(){return this.selected?void 0:(this.seg.animate({path:this.selectedPath},150,"<>"),this.arc.animate({opacity:1},150,"<>"),this.selected=!0)},b.prototype.deselect=function(){return this.selected?(this.seg.animate({path:this.path},150,"<>"),this.arc.animate({opacity:0},150,"<>"),this.selected=!1):void 0},b}(b.EventEmitter)}).call(this); \ No newline at end of file
diff --git a/public/bower_components/morris.js/package.json b/public/bower_components/morris.js/package.json
new file mode 100644
index 0000000..529c2b1
--- /dev/null
+++ b/public/bower_components/morris.js/package.json
@@ -0,0 +1,36 @@
+{
+ "name": "morris.js",
+ "version": "0.5.0",
+ "homepage": "http://morrisjs.github.com/morris.js",
+ "license": "BSD-2-Clause",
+ "description": "Easy, pretty charts",
+ "author": {
+ "name": "Olly Smith",
+ "email": "olly@oesmith.co.uk"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/morrisjs/morris.js.git"
+ },
+ "bugs": {
+ "url": "https://github.com/morrisjs/morris.js/issues"
+ },
+ "devDependencies": {
+ "matchdep": "~0.1.2",
+ "grunt": "~0.4.1",
+ "grunt-mocha": "~0.4.10",
+ "grunt-contrib-concat": "~0.3.0",
+ "grunt-contrib-coffee": "~0.7.0",
+ "grunt-contrib-uglify": "~0.2.4",
+ "grunt-contrib-less": "~0.7.0",
+ "grunt-contrib-watch": "~0.5.3",
+ "grunt-shell": "~0.5.0",
+ "bower": "~1.2.8"
+ },
+ "scripts": {
+ "test": "grunt concat coffee mocha"
+ },
+ "engines": {
+ "node": ">=0.8 <0.11"
+ }
+}
diff --git a/public/bower_components/morris.js/spec/lib/area/area_spec.coffee b/public/bower_components/morris.js/spec/lib/area/area_spec.coffee
new file mode 100644
index 0000000..fa36b2e
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/area/area_spec.coffee
@@ -0,0 +1,60 @@
+describe 'Morris.Area', ->
+
+ describe 'svg structure', ->
+ defaults =
+ element: 'graph'
+ data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
+ lineColors: [ '#0b62a4', '#7a92a3']
+ gridLineColor: '#aaa'
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['Y']
+
+ it 'should contain a line path for each line', ->
+ chart = Morris.Area $.extend {}, defaults
+ $('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
+
+ it 'should contain a path with stroke-width 0 for each line', ->
+ chart = Morris.Area $.extend {}, defaults
+ $('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
+
+ it 'should contain 5 grid lines', ->
+ chart = Morris.Area $.extend {}, defaults
+ $('#graph').find("path[stroke='#aaaaaa']").size().should.equal 5
+
+ it 'should contain 9 text elements', ->
+ chart = Morris.Area $.extend {}, defaults
+ $('#graph').find("text").size().should.equal 9
+
+ describe 'svg attributes', ->
+ defaults =
+ element: 'graph'
+ data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['Y']
+ lineColors: [ '#0b62a4', '#7a92a3']
+ lineWidth: 3
+ pointWidths: [5]
+ pointStrokeColors: ['#ffffff']
+ gridLineColor: '#aaa'
+ gridStrokeWidth: 0.5
+ gridTextColor: '#888'
+ gridTextSize: 12
+
+ it 'should not be cumulative if behaveLikeLine', ->
+ chart = Morris.Area $.extend {}, defaults, behaveLikeLine: true
+ chart.cumulative.should.equal false
+
+ it 'should have a line with transparent fill if behaveLikeLine', ->
+ chart = Morris.Area $.extend {}, defaults, behaveLikeLine: true
+ $('#graph').find("path[fill-opacity='0.8']").size().should.equal 1
+
+ it 'should not have a line with transparent fill', ->
+ chart = Morris.Area $.extend {}, defaults
+ $('#graph').find("path[fill-opacity='0.8']").size().should.equal 0
+
+ it 'should have a line with the fill of a modified line color', ->
+ chart = Morris.Area $.extend {}, defaults
+ $('#graph').find("path[fill='#0b62a4']").size().should.equal 0
+ $('#graph').find("path[fill='#7a92a3']").size().should.equal 0
diff --git a/public/bower_components/morris.js/spec/lib/bar/bar_spec.coffee b/public/bower_components/morris.js/spec/lib/bar/bar_spec.coffee
new file mode 100644
index 0000000..c647c53
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/bar/bar_spec.coffee
@@ -0,0 +1,127 @@
+describe 'Morris.Bar', ->
+ describe 'when using vertical grid', ->
+ defaults =
+ element: 'graph'
+ data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
+ xkey: 'x'
+ ykeys: ['y', 'z']
+ labels: ['Y', 'Z']
+ barColors: [ '#0b62a4', '#7a92a3']
+ gridLineColor: '#aaa'
+ gridStrokeWidth: 0.5
+ gridTextColor: '#888'
+ gridTextSize: 12
+ verticalGridCondition: (index) -> index % 2
+ verticalGridColor: '#888888'
+ verticalGridOpacity: '0.2'
+
+ describe 'svg structure', ->
+ it 'should contain extra rectangles for vertical grid', ->
+ $('#graph').css('height', '250px').css('width', '800px')
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("rect").size().should.equal 6
+
+ describe 'svg attributes', ->
+ it 'should have to bars with verticalGrid.color', ->
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("rect[fill='#{defaults.verticalGridColor}']").size().should.equal 2
+ it 'should have to bars with verticalGrid.color', ->
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("rect[fill-opacity='#{defaults.verticalGridOpacity}']").size().should.equal 2
+
+ describe 'svg structure', ->
+ defaults =
+ element: 'graph'
+ data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
+ xkey: 'x'
+ ykeys: ['y', 'z']
+ labels: ['Y', 'Z']
+
+ it 'should contain a rect for each bar', ->
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("rect").size().should.equal 4
+
+ it 'should contain 5 grid lines', ->
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("path").size().should.equal 5
+
+ it 'should contain 7 text elements', ->
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("text").size().should.equal 7
+
+ describe 'svg attributes', ->
+ defaults =
+ element: 'graph'
+ data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
+ xkey: 'x'
+ ykeys: ['y', 'z']
+ labels: ['Y', 'Z']
+ barColors: [ '#0b62a4', '#7a92a3']
+ gridLineColor: '#aaa'
+ gridStrokeWidth: 0.5
+ gridTextColor: '#888'
+ gridTextSize: 12
+
+ it 'should have a bar with the first default color', ->
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("rect[fill='#0b62a4']").size().should.equal 2
+
+ it 'should have a bar with no stroke', ->
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("rect[stroke='none']").size().should.equal 4
+
+ it 'should have text with configured fill color', ->
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("text[fill='#888888']").size().should.equal 7
+
+ it 'should have text with configured font size', ->
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("text[font-size='12px']").size().should.equal 7
+
+ describe 'when setting bar radius', ->
+ describe 'svg structure', ->
+ defaults =
+ element: 'graph'
+ data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
+ xkey: 'x'
+ ykeys: ['y', 'z']
+ labels: ['Y', 'Z']
+ barRadius: [5, 5, 0, 0]
+
+ it 'should contain a path for each bar', ->
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("path").size().should.equal 9
+
+ it 'should use rects if radius is too big', ->
+ delete defaults.barStyle
+ chart = Morris.Bar $.extend {}, defaults,
+ barRadius: [300, 300, 0, 0]
+ $('#graph').find("rect").size().should.equal 4
+
+ describe 'barSize option', ->
+ describe 'svg attributes', ->
+ defaults =
+ element: 'graph'
+ barSize: 20
+ data: [
+ {x: '2011 Q1', y: 3, z: 2, a: 3}
+ {x: '2011 Q2', y: 2, z: null, a: 1}
+ {x: '2011 Q3', y: 0, z: 2, a: 4}
+ {x: '2011 Q4', y: 2, z: 4, a: 3}
+ ],
+ xkey: 'x'
+ ykeys: ['y', 'z', 'a']
+ labels: ['Y', 'Z', 'A']
+
+ it 'should calc the width if too narrow for barSize', ->
+ $('#graph').width('200px')
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("rect").filter((i) ->
+ parseFloat($(@).attr('width'), 10) < 10
+ ).size().should.equal 11
+
+ it 'should set width to @options.barSize if possible', ->
+ chart = Morris.Bar $.extend {}, defaults
+ $('#graph').find("rect[width='#{defaults.barSize}']").size().should.equal 11
+
+
diff --git a/public/bower_components/morris.js/spec/lib/bar/colours.coffee b/public/bower_components/morris.js/spec/lib/bar/colours.coffee
new file mode 100644
index 0000000..ebe5469
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/bar/colours.coffee
@@ -0,0 +1,36 @@
+describe 'Morris.Bar#colorFor', ->
+
+ defaults =
+ element: 'graph'
+ data: [{x: 'foo', y: 2, z: 3}, {x: 'bar', y: 4, z: 6}]
+ xkey: 'x'
+ ykeys: ['y', 'z']
+ labels: ['Y', 'Z']
+
+ it 'should fetch colours from an array', ->
+ chart = Morris.Bar $.extend {}, defaults, barColors: ['#f00', '#0f0', '#00f']
+ chart.colorFor(chart.data[0], 0, 'bar').should.equal '#f00'
+ chart.colorFor(chart.data[0], 0, 'hover').should.equal '#f00'
+ chart.colorFor(chart.data[0], 1, 'bar').should.equal '#0f0'
+ chart.colorFor(chart.data[0], 1, 'hover').should.equal '#0f0'
+ chart.colorFor(chart.data[0], 2, 'bar').should.equal '#00f'
+ chart.colorFor(chart.data[0], 2, 'hover').should.equal '#00f'
+ chart.colorFor(chart.data[0], 3, 'bar').should.equal '#f00'
+ chart.colorFor(chart.data[0], 4, 'hover').should.equal '#0f0'
+
+ it 'should defer to a callback', ->
+ stub = sinon.stub().returns '#f00'
+ chart = Morris.Bar $.extend {}, defaults, barColors: stub
+ stub.reset()
+
+ chart.colorFor(chart.data[0], 0, 'bar')
+ stub.should.have.been.calledWith(
+ {x:0, y:2, label:'foo'},
+ {index:0, key:'y', label:'Y'},
+ 'bar')
+
+ chart.colorFor(chart.data[0], 1, 'hover')
+ stub.should.have.been.calledWith(
+ {x:0, y:3, label:'foo'},
+ {index:1, key:'z', label:'Z'},
+ 'hover')
diff --git a/public/bower_components/morris.js/spec/lib/commas_spec.coffee b/public/bower_components/morris.js/spec/lib/commas_spec.coffee
new file mode 100644
index 0000000..9c14887
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/commas_spec.coffee
@@ -0,0 +1,38 @@
+describe '#commas', ->
+
+ it 'should insert commas into long numbers', ->
+ # zero
+ Morris.commas(0).should.equal("0")
+
+ # positive integers
+ Morris.commas(1).should.equal("1")
+ Morris.commas(12).should.equal("12")
+ Morris.commas(123).should.equal("123")
+ Morris.commas(1234).should.equal("1,234")
+ Morris.commas(12345).should.equal("12,345")
+ Morris.commas(123456).should.equal("123,456")
+ Morris.commas(1234567).should.equal("1,234,567")
+
+ # negative integers
+ Morris.commas(-1).should.equal("-1")
+ Morris.commas(-12).should.equal("-12")
+ Morris.commas(-123).should.equal("-123")
+ Morris.commas(-1234).should.equal("-1,234")
+ Morris.commas(-12345).should.equal("-12,345")
+ Morris.commas(-123456).should.equal("-123,456")
+ Morris.commas(-1234567).should.equal("-1,234,567")
+
+ # positive decimals
+ Morris.commas(1.2).should.equal("1.2")
+ Morris.commas(12.34).should.equal("12.34")
+ Morris.commas(123.456).should.equal("123.456")
+ Morris.commas(1234.56).should.equal("1,234.56")
+
+ # negative decimals
+ Morris.commas(-1.2).should.equal("-1.2")
+ Morris.commas(-12.34).should.equal("-12.34")
+ Morris.commas(-123.456).should.equal("-123.456")
+ Morris.commas(-1234.56).should.equal("-1,234.56")
+
+ # null
+ Morris.commas(null).should.equal('-')
diff --git a/public/bower_components/morris.js/spec/lib/donut/donut_spec.coffee b/public/bower_components/morris.js/spec/lib/donut/donut_spec.coffee
new file mode 100644
index 0000000..d8313f9
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/donut/donut_spec.coffee
@@ -0,0 +1,76 @@
+describe 'Morris.Donut', ->
+
+ describe 'svg structure', ->
+ defaults =
+ element: 'graph'
+ data: [ {label: 'Jam', value: 25 },
+ {label: 'Frosted', value: 40 },
+ {label: 'Custard', value: 25 },
+ {label: 'Sugar', value: 10 } ]
+ formatter: (y) -> "#{y}%"
+
+ it 'should contain 2 paths for each segment', ->
+ chart = Morris.Donut $.extend {}, defaults
+ $('#graph').find("path").size().should.equal 8
+
+ it 'should contain 2 text elements for the label', ->
+ chart = Morris.Donut $.extend {}, defaults
+ $('#graph').find("text").size().should.equal 2
+
+ describe 'svg attributes', ->
+ defaults =
+ element: 'graph'
+ data: [ {label: 'Jam', value: 25 },
+ {label: 'Frosted', value: 40 },
+ {label: 'Custard', value: 25 },
+ {label: 'Sugar', value: 10 } ]
+ formatter: (y) -> "#{y}%"
+ colors: [ '#0B62A4', '#3980B5', '#679DC6', '#95BBD7']
+
+ it 'should have a label with font size 15', ->
+ chart = Morris.Donut $.extend {}, defaults
+ $('#graph').find("text[font-size='15px']").size().should.equal 1
+
+ it 'should have a label with font size 14', ->
+ chart = Morris.Donut $.extend {}, defaults
+ $('#graph').find("text[font-size='14px']").size().should.equal 1
+
+ it 'should have a label with font-weight 800', ->
+ chart = Morris.Donut $.extend {}, defaults
+ $('#graph').find("text[font-weight='800']").size().should.equal 1
+
+ it 'should have 1 paths with fill of first color', ->
+ chart = Morris.Donut $.extend {}, defaults
+ $('#graph').find("path[fill='#0b62a4']").size().should.equal 1
+
+ it 'should have 1 paths with stroke of first color', ->
+ chart = Morris.Donut $.extend {}, defaults
+ $('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
+
+ it 'should have a path with white stroke', ->
+ chart = Morris.Donut $.extend {}, defaults
+ $('#graph').find("path[stroke='#ffffff']").size().should.equal 4
+
+ it 'should have a path with stroke-width 3', ->
+ chart = Morris.Donut $.extend {}, defaults
+ $('#graph').find("path[stroke-width='3']").size().should.equal 4
+
+ it 'should have a path with stroke-width 2', ->
+ chart = Morris.Donut $.extend {}, defaults
+ $('#graph').find("path[stroke-width='2']").size().should.equal 4
+
+ describe 'setData', ->
+ defaults =
+ element: 'graph'
+ data: [ {label: 'One', value: 25 }, {label: "Two", value: 30} ]
+ colors: ['#ff0000', '#00ff00', '#0000ff']
+
+ it 'should update the chart', ->
+ chart = Morris.Donut $.extend {}, defaults
+ $('#graph').find("path[stroke='#0000ff']").size().should.equal 0
+ chart.setData [
+ { label: 'One', value: 25 }
+ { label: 'Two', value: 30 }
+ { label: 'Three', value: 35 }
+ ]
+ $('#graph').find("path[stroke='#0000ff']").size().should.equal 1
diff --git a/public/bower_components/morris.js/spec/lib/grid/auto_grid_lines_spec.coffee b/public/bower_components/morris.js/spec/lib/grid/auto_grid_lines_spec.coffee
new file mode 100644
index 0000000..2312fe2
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/grid/auto_grid_lines_spec.coffee
@@ -0,0 +1,25 @@
+describe 'Morris.Grid#autoGridLines', ->
+
+ beforeEach ->
+ @subject = Morris.Grid.prototype.autoGridLines
+
+ it 'should draw at fixed intervals', ->
+ @subject(0, 4, 5).should.deep.equal [0, 1, 2, 3, 4]
+ @subject(0, 400, 5).should.deep.equal [0, 100, 200, 300, 400]
+
+ it 'should pick intervals that show significant numbers', ->
+ @subject(102, 499, 5).should.deep.equal [100, 200, 300, 400, 500]
+
+ it 'should draw zero when it falls within [ymin..ymax]', ->
+ @subject(-100, 300, 5).should.deep.equal [-100, 0, 100, 200, 300]
+ @subject(-50, 350, 5).should.deep.equal [-125, 0, 125, 250, 375]
+ @subject(-400, 400, 5).should.deep.equal [-400, -200, 0, 200, 400]
+ @subject(100, 500, 5).should.deep.equal [100, 200, 300, 400, 500]
+ @subject(-500, -100, 5).should.deep.equal [-500, -400, -300, -200, -100]
+
+ it 'should generate decimal labels to 2 significant figures', ->
+ @subject(0, 1, 5).should.deep.equal [0, 0.25, 0.5, 0.75, 1]
+ @subject(0.1, 0.5, 5).should.deep.equal [0.1, 0.2, 0.3, 0.4, 0.5]
+
+ it 'should use integer intervals for intervals larger than 1', ->
+ @subject(0, 9, 5).should.deep.equal [0, 3, 6, 9, 12]
diff --git a/public/bower_components/morris.js/spec/lib/grid/set_data_spec.coffee b/public/bower_components/morris.js/spec/lib/grid/set_data_spec.coffee
new file mode 100644
index 0000000..50450d7
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/grid/set_data_spec.coffee
@@ -0,0 +1,208 @@
+describe 'Morris.Grid#setData', ->
+
+ it 'should not alter user-supplied data', ->
+ my_data = [{x: 1, y: 1}, {x: 2, y: 2}]
+ expected_data = [{x: 1, y: 1}, {x: 2, y: 2}]
+ Morris.Line
+ element: 'graph'
+ data: my_data
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['dontcare']
+ my_data.should.deep.equal expected_data
+
+ describe 'ymin/ymax', ->
+ beforeEach ->
+ @defaults =
+ element: 'graph'
+ xkey: 'x'
+ ykeys: ['y', 'z']
+ labels: ['y', 'z']
+
+ it 'should use a user-specified minimum and maximum value', ->
+ line = Morris.Line $.extend @defaults,
+ data: [{x: 1, y: 1}]
+ ymin: 10
+ ymax: 20
+ line.ymin.should.equal 10
+ line.ymax.should.equal 20
+
+ describe 'auto', ->
+
+ it 'should automatically calculate the minimum and maximum value', ->
+ line = Morris.Line $.extend @defaults,
+ data: [{x: 1, y: 10}, {x: 2, y: 15}, {x: 3, y: null}, {x: 4}]
+ ymin: 'auto'
+ ymax: 'auto'
+ line.ymin.should.equal 10
+ line.ymax.should.equal 15
+
+ it 'should automatically calculate the minimum and maximum value given no y data', ->
+ line = Morris.Line $.extend @defaults,
+ data: [{x: 1}, {x: 2}, {x: 3}, {x: 4}]
+ ymin: 'auto'
+ ymax: 'auto'
+ line.ymin.should.equal 0
+ line.ymax.should.equal 1
+
+ describe 'auto [n]', ->
+
+ it 'should automatically calculate the minimum and maximum value', ->
+ line = Morris.Line $.extend @defaults,
+ data: [{x: 1, y: 10}, {x: 2, y: 15}, {x: 3, y: null}, {x: 4}]
+ ymin: 'auto 11'
+ ymax: 'auto 13'
+ line.ymin.should.equal 10
+ line.ymax.should.equal 15
+
+ it 'should automatically calculate the minimum and maximum value given no data', ->
+ line = Morris.Line $.extend @defaults,
+ data: [{x: 1}, {x: 2}, {x: 3}, {x: 4}]
+ ymin: 'auto 11'
+ ymax: 'auto 13'
+ line.ymin.should.equal 11
+ line.ymax.should.equal 13
+
+ it 'should use a user-specified minimum and maximum value', ->
+ line = Morris.Line $.extend @defaults,
+ data: [{x: 1, y: 10}, {x: 2, y: 15}, {x: 3, y: null}, {x: 4}]
+ ymin: 'auto 5'
+ ymax: 'auto 20'
+ line.ymin.should.equal 5
+ line.ymax.should.equal 20
+
+ it 'should use a user-specified minimum and maximum value given no data', ->
+ line = Morris.Line $.extend @defaults,
+ data: [{x: 1}, {x: 2}, {x: 3}, {x: 4}]
+ ymin: 'auto 5'
+ ymax: 'auto 20'
+ line.ymin.should.equal 5
+ line.ymax.should.equal 20
+
+ describe 'xmin/xmax', ->
+
+ it 'should calculate the horizontal range', ->
+ line = Morris.Line
+ element: 'graph'
+ data: [{x: 2, y: 2}, {x: 1, y: 1}, {x: 4, y: 4}, {x: 3, y: 3}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['y']
+ line.xmin.should == 1
+ line.xmax.should == 4
+
+ it "should pad the range if there's only one data point", ->
+ line = Morris.Line
+ element: 'graph'
+ data: [{x: 2, y: 2}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['y']
+ line.xmin.should == 1
+ line.xmax.should == 3
+
+ describe 'sorting', ->
+
+ it 'should sort data when parseTime is true', ->
+ line = Morris.Line
+ element: 'graph'
+ data: [
+ {x: '2012 Q1', y: 2},
+ {x: '2012 Q3', y: 1},
+ {x: '2012 Q4', y: 4},
+ {x: '2012 Q2', y: 3}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['y']
+ line.data.map((row) -> row.label).should.deep.equal ['2012 Q1', '2012 Q2', '2012 Q3', '2012 Q4']
+
+ it 'should not sort data when parseTime is false', ->
+ line = Morris.Line
+ element: 'graph'
+ data: [{x: 1, y: 2}, {x: 4, y: 1}, {x: 3, y: 4}, {x: 2, y: 3}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['y']
+ parseTime: false
+ line.data.map((row) -> row.label).should.deep.equal [1, 4, 3, 2]
+
+ describe 'timestamp data', ->
+
+ it 'should generate default labels for timestamp x-values', ->
+ d = [
+ new Date 2012, 0, 1
+ new Date 2012, 0, 2
+ new Date 2012, 0, 3
+ new Date 2012, 0, 4
+ ]
+ line = Morris.Line
+ element: 'graph'
+ data: [
+ {x: d[0].getTime(), y: 2},
+ {x: d[1].getTime(), y: 1},
+ {x: d[2].getTime(), y: 4},
+ {x: d[3].getTime(), y: 3}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['y']
+ line.data.map((row) -> row.label).should.deep.equal d.map((t) -> t.toString())
+
+ it 'should use a user-supplied formatter for labels', ->
+ line = Morris.Line
+ element: 'graph'
+ data: [
+ {x: new Date(2012, 0, 1).getTime(), y: 2},
+ {x: new Date(2012, 0, 2).getTime(), y: 1},
+ {x: new Date(2012, 0, 3).getTime(), y: 4},
+ {x: new Date(2012, 0, 4).getTime(), y: 3}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['y']
+ dateFormat: (ts) ->
+ date = new Date(ts)
+ "#{date.getFullYear()}-#{date.getMonth()+1}-#{date.getDate()}"
+ line.data.map((row) -> row.label).should.deep.equal ['2012-1-1', '2012-1-2', '2012-1-3', '2012-1-4']
+
+ it 'should parse y-values in strings', ->
+ line = Morris.Line
+ element: 'graph'
+ data: [{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['y']
+ line.ymin.should == 12
+ line.ymax.should == 16
+ line.data.map((row) -> row.y).should.deep.equal [[13.5], [12], [16], [14]]
+
+ it 'should clear the chart when empty data is supplied', ->
+ line = Morris.Line
+ element: 'graph',
+ data: [{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['y']
+ line.data.length.should.equal 4
+ line.setData([])
+ line.data.length.should.equal 0
+ line.setData([{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}])
+ line.data.length.should.equal 4
+
+ it 'should be able to add data if the chart is initialised with empty data', ->
+ line = Morris.Line
+ element: 'graph',
+ data: []
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['y']
+ line.data.length.should.equal 0
+ line.setData([{x: 2, y: '12'}, {x: 1, y: '13.5'}, {x: 4, y: '14'}, {x: 3, y: '16'}])
+ line.data.length.should.equal 4
+
+ it 'should automatically choose significant numbers for y-labels', ->
+ line = Morris.Line
+ element: 'graph',
+ data: [{x: 1, y: 0}, {x: 2, y: 3600}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['y']
+ line.grid.should == [0, 1000, 2000, 3000, 4000]
diff --git a/public/bower_components/morris.js/spec/lib/grid/y_label_format_spec.coffee b/public/bower_components/morris.js/spec/lib/grid/y_label_format_spec.coffee
new file mode 100644
index 0000000..e074230
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/grid/y_label_format_spec.coffee
@@ -0,0 +1,15 @@
+describe 'Morris.Grid#yLabelFormat', ->
+
+ it 'should use custom formatter for y labels', ->
+ formatter = (label) ->
+ flabel = parseFloat(label) / 1000
+ "#{flabel.toFixed(1)}k"
+ line = Morris.Line
+ element: 'graph'
+ data: [{x: 1, y: 1500}, {x: 2, y: 2500}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['dontcare']
+ preUnits: "$"
+ yLabelFormat: formatter
+ line.yLabelFormat(1500).should.equal "1.5k"
diff --git a/public/bower_components/morris.js/spec/lib/hover_spec.coffee b/public/bower_components/morris.js/spec/lib/hover_spec.coffee
new file mode 100644
index 0000000..868f1a3
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/hover_spec.coffee
@@ -0,0 +1,64 @@
+describe "Morris.Hover", ->
+
+ describe "with dummy content", ->
+
+ beforeEach ->
+ parent = $('<div style="width:200px;height:180px"></div>')
+ .appendTo($('#test'))
+ @hover = new Morris.Hover(parent: parent)
+ @element = $('#test .morris-hover')
+
+ it "should initialise a hidden, empty popup", ->
+ @element.should.exist
+ @element.should.be.hidden
+ @element.should.be.empty
+
+ describe "#show", ->
+ it "should show the popup", ->
+ @hover.show()
+ @element.should.be.visible
+
+ describe "#hide", ->
+ it "should hide the popup", ->
+ @hover.show()
+ @hover.hide()
+ @element.should.be.hidden
+
+ describe "#html", ->
+ it "should replace the contents of the element", ->
+ @hover.html('<div>Foobarbaz</div>')
+ @element.should.have.html('<div>Foobarbaz</div>')
+
+ describe "#moveTo", ->
+ beforeEach ->
+ @hover.html('<div style="width:84px;height:84px"></div>')
+
+ it "should place the popup directly above the given point", ->
+ @hover.moveTo(100, 150)
+ @element.should.have.css('left', '50px')
+ @element.should.have.css('top', '40px')
+
+ it "should place the popup below the given point if it does not fit above", ->
+ @hover.moveTo(100, 50)
+ @element.should.have.css('left', '50px')
+ @element.should.have.css('top', '60px')
+
+ it "should center the popup vertically if it will not fit above or below", ->
+ @hover.moveTo(100, 100)
+ @element.should.have.css('left', '50px')
+ @element.should.have.css('top', '40px')
+
+ it "should center the popup vertically if no y value is supplied", ->
+ @hover.moveTo(100)
+ @element.should.have.css('left', '50px')
+ @element.should.have.css('top', '40px')
+
+ describe "#update", ->
+ it "should update content, show and reposition the popup", ->
+ hover = new Morris.Hover(parent: $('#test'))
+ html = "<div style='width:84px;height:84px'>Hello, Everyone!</div>"
+ hover.update(html, 150, 200)
+ el = $('#test .morris-hover')
+ el.should.have.css('left', '100px')
+ el.should.have.css('top', '90px')
+ el.should.have.text('Hello, Everyone!')
diff --git a/public/bower_components/morris.js/spec/lib/label_series_spec.coffee b/public/bower_components/morris.js/spec/lib/label_series_spec.coffee
new file mode 100644
index 0000000..2794e73
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/label_series_spec.coffee
@@ -0,0 +1,186 @@
+describe '#labelSeries', ->
+
+ it 'should generate decade intervals', ->
+ Morris.labelSeries(
+ new Date(1952, 0, 1).getTime(),
+ new Date(2012, 0, 1).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["1960", new Date(1960, 0, 1).getTime()],
+ ["1970", new Date(1970, 0, 1).getTime()],
+ ["1980", new Date(1980, 0, 1).getTime()],
+ ["1990", new Date(1990, 0, 1).getTime()],
+ ["2000", new Date(2000, 0, 1).getTime()],
+ ["2010", new Date(2010, 0, 1).getTime()]
+ ])
+ Morris.labelSeries(
+ new Date(1952, 3, 1).getTime(),
+ new Date(2012, 3, 1).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["1960", new Date(1960, 0, 1).getTime()],
+ ["1970", new Date(1970, 0, 1).getTime()],
+ ["1980", new Date(1980, 0, 1).getTime()],
+ ["1990", new Date(1990, 0, 1).getTime()],
+ ["2000", new Date(2000, 0, 1).getTime()],
+ ["2010", new Date(2010, 0, 1).getTime()]
+ ])
+
+ it 'should generate year intervals', ->
+ Morris.labelSeries(
+ new Date(2007, 0, 1).getTime(),
+ new Date(2012, 0, 1).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["2007", new Date(2007, 0, 1).getTime()],
+ ["2008", new Date(2008, 0, 1).getTime()],
+ ["2009", new Date(2009, 0, 1).getTime()],
+ ["2010", new Date(2010, 0, 1).getTime()],
+ ["2011", new Date(2011, 0, 1).getTime()],
+ ["2012", new Date(2012, 0, 1).getTime()]
+ ])
+ Morris.labelSeries(
+ new Date(2007, 3, 1).getTime(),
+ new Date(2012, 3, 1).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["2008", new Date(2008, 0, 1).getTime()],
+ ["2009", new Date(2009, 0, 1).getTime()],
+ ["2010", new Date(2010, 0, 1).getTime()],
+ ["2011", new Date(2011, 0, 1).getTime()],
+ ["2012", new Date(2012, 0, 1).getTime()]
+ ])
+
+ it 'should generate month intervals', ->
+ Morris.labelSeries(
+ new Date(2012, 0, 1).getTime(),
+ new Date(2012, 5, 1).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["2012-01", new Date(2012, 0, 1).getTime()],
+ ["2012-02", new Date(2012, 1, 1).getTime()],
+ ["2012-03", new Date(2012, 2, 1).getTime()],
+ ["2012-04", new Date(2012, 3, 1).getTime()],
+ ["2012-05", new Date(2012, 4, 1).getTime()],
+ ["2012-06", new Date(2012, 5, 1).getTime()]
+ ])
+
+ it 'should generate week intervals', ->
+ Morris.labelSeries(
+ new Date(2012, 0, 1).getTime(),
+ new Date(2012, 1, 10).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["2012-01-01", new Date(2012, 0, 1).getTime()],
+ ["2012-01-08", new Date(2012, 0, 8).getTime()],
+ ["2012-01-15", new Date(2012, 0, 15).getTime()],
+ ["2012-01-22", new Date(2012, 0, 22).getTime()],
+ ["2012-01-29", new Date(2012, 0, 29).getTime()],
+ ["2012-02-05", new Date(2012, 1, 5).getTime()]
+ ])
+
+ it 'should generate day intervals', ->
+ Morris.labelSeries(
+ new Date(2012, 0, 1).getTime(),
+ new Date(2012, 0, 6).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["2012-01-01", new Date(2012, 0, 1).getTime()],
+ ["2012-01-02", new Date(2012, 0, 2).getTime()],
+ ["2012-01-03", new Date(2012, 0, 3).getTime()],
+ ["2012-01-04", new Date(2012, 0, 4).getTime()],
+ ["2012-01-05", new Date(2012, 0, 5).getTime()],
+ ["2012-01-06", new Date(2012, 0, 6).getTime()]
+ ])
+
+ it 'should generate hour intervals', ->
+ Morris.labelSeries(
+ new Date(2012, 0, 1, 0).getTime(),
+ new Date(2012, 0, 1, 5).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["00:00", new Date(2012, 0, 1, 0).getTime()],
+ ["01:00", new Date(2012, 0, 1, 1).getTime()],
+ ["02:00", new Date(2012, 0, 1, 2).getTime()],
+ ["03:00", new Date(2012, 0, 1, 3).getTime()],
+ ["04:00", new Date(2012, 0, 1, 4).getTime()],
+ ["05:00", new Date(2012, 0, 1, 5).getTime()]
+ ])
+
+ it 'should generate half-hour intervals', ->
+ Morris.labelSeries(
+ new Date(2012, 0, 1, 0, 0).getTime(),
+ new Date(2012, 0, 1, 2, 30).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["00:00", new Date(2012, 0, 1, 0, 0).getTime()],
+ ["00:30", new Date(2012, 0, 1, 0, 30).getTime()],
+ ["01:00", new Date(2012, 0, 1, 1, 0).getTime()],
+ ["01:30", new Date(2012, 0, 1, 1, 30).getTime()],
+ ["02:00", new Date(2012, 0, 1, 2, 0).getTime()],
+ ["02:30", new Date(2012, 0, 1, 2, 30).getTime()]
+ ])
+ Morris.labelSeries(
+ new Date(2012, 4, 12, 0, 0).getTime(),
+ new Date(2012, 4, 12, 2, 30).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["00:00", new Date(2012, 4, 12, 0, 0).getTime()],
+ ["00:30", new Date(2012, 4, 12, 0, 30).getTime()],
+ ["01:00", new Date(2012, 4, 12, 1, 0).getTime()],
+ ["01:30", new Date(2012, 4, 12, 1, 30).getTime()],
+ ["02:00", new Date(2012, 4, 12, 2, 0).getTime()],
+ ["02:30", new Date(2012, 4, 12, 2, 30).getTime()]
+ ])
+
+ it 'should generate fifteen-minute intervals', ->
+ Morris.labelSeries(
+ new Date(2012, 0, 1, 0, 0).getTime(),
+ new Date(2012, 0, 1, 1, 15).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["00:00", new Date(2012, 0, 1, 0, 0).getTime()],
+ ["00:15", new Date(2012, 0, 1, 0, 15).getTime()],
+ ["00:30", new Date(2012, 0, 1, 0, 30).getTime()],
+ ["00:45", new Date(2012, 0, 1, 0, 45).getTime()],
+ ["01:00", new Date(2012, 0, 1, 1, 0).getTime()],
+ ["01:15", new Date(2012, 0, 1, 1, 15).getTime()]
+ ])
+ Morris.labelSeries(
+ new Date(2012, 4, 12, 0, 0).getTime(),
+ new Date(2012, 4, 12, 1, 15).getTime(),
+ 1000
+ ).should.deep.equal([
+ ["00:00", new Date(2012, 4, 12, 0, 0).getTime()],
+ ["00:15", new Date(2012, 4, 12, 0, 15).getTime()],
+ ["00:30", new Date(2012, 4, 12, 0, 30).getTime()],
+ ["00:45", new Date(2012, 4, 12, 0, 45).getTime()],
+ ["01:00", new Date(2012, 4, 12, 1, 0).getTime()],
+ ["01:15", new Date(2012, 4, 12, 1, 15).getTime()]
+ ])
+
+ it 'should override automatic intervals', ->
+ Morris.labelSeries(
+ new Date(2011, 11, 12).getTime(),
+ new Date(2012, 0, 12).getTime(),
+ 1000,
+ "year"
+ ).should.deep.equal([
+ ["2012", new Date(2012, 0, 1).getTime()]
+ ])
+
+ it 'should apply custom formatters', ->
+ Morris.labelSeries(
+ new Date(2012, 0, 1).getTime(),
+ new Date(2012, 0, 6).getTime(),
+ 1000,
+ "day",
+ (d) -> "#{d.getMonth()+1}/#{d.getDate()}/#{d.getFullYear()}"
+ ).should.deep.equal([
+ ["1/1/2012", new Date(2012, 0, 1).getTime()],
+ ["1/2/2012", new Date(2012, 0, 2).getTime()],
+ ["1/3/2012", new Date(2012, 0, 3).getTime()],
+ ["1/4/2012", new Date(2012, 0, 4).getTime()],
+ ["1/5/2012", new Date(2012, 0, 5).getTime()],
+ ["1/6/2012", new Date(2012, 0, 6).getTime()]
+ ])
diff --git a/public/bower_components/morris.js/spec/lib/line/line_spec.coffee b/public/bower_components/morris.js/spec/lib/line/line_spec.coffee
new file mode 100644
index 0000000..42e52e8
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/line/line_spec.coffee
@@ -0,0 +1,211 @@
+describe 'Morris.Line', ->
+
+ it 'should raise an error when the placeholder element is not found', ->
+ my_data = [{x: 1, y: 1}, {x: 2, y: 2}]
+ fn = ->
+ Morris.Line(
+ element: "thisplacedoesnotexist"
+ data: my_data
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['dontcare']
+ )
+ fn.should.throw(/Graph container element not found/)
+
+ it 'should make point styles customizable', ->
+ my_data = [{x: 1, y: 1}, {x: 2, y: 2}]
+ red = '#ff0000'
+ blue = '#0000ff'
+ chart = Morris.Line
+ element: 'graph'
+ data: my_data
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['dontcare']
+ pointStrokeColors: [red, blue]
+ pointStrokeWidths: [1, 2]
+ pointFillColors: [null, red]
+ chart.pointStrokeWidthForSeries(0).should.equal 1
+ chart.pointStrokeColorForSeries(0).should.equal red
+ chart.pointStrokeWidthForSeries(1).should.equal 2
+ chart.pointStrokeColorForSeries(1).should.equal blue
+ chart.colorFor(chart.data[0], 0, 'point').should.equal chart.colorFor(chart.data[0], 0, 'line')
+ chart.colorFor(chart.data[1], 1, 'point').should.equal red
+
+ describe 'generating column labels', ->
+
+ it 'should use user-supplied x value strings by default', ->
+ chart = Morris.Line
+ element: 'graph'
+ data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['dontcare']
+ chart.data.map((x) -> x.label).should == ['2012 Q1', '2012 Q2']
+
+ it 'should use a default format for timestamp x-values', ->
+ d1 = new Date(2012, 0, 1)
+ d2 = new Date(2012, 0, 2)
+ chart = Morris.Line
+ element: 'graph'
+ data: [{x: d1.getTime(), y: 1}, {x: d2.getTime(), y: 1}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['dontcare']
+ chart.data.map((x) -> x.label).should == [d2.toString(), d1.toString()]
+
+ it 'should use user-defined formatters', ->
+ d = new Date(2012, 0, 1)
+ chart = Morris.Line
+ element: 'graph'
+ data: [{x: d.getTime(), y: 1}, {x: '2012-01-02', y: 1}]
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['dontcare']
+ dateFormat: (d) ->
+ x = new Date(d)
+ "#{x.getYear()}/#{x.getMonth()+1}/#{x.getDay()}"
+ chart.data.map((x) -> x.label).should == ['2012/1/1', '2012/1/2']
+
+ describe 'rendering lines', ->
+ beforeEach ->
+ @defaults =
+ element: 'graph'
+ data: [{x:0, y:1, z:0}, {x:1, y:0, z:1}, {x:2, y:1, z:0}, {x:3, y:0, z:1}, {x:4, y:1, z:0}]
+ xkey: 'x'
+ ykeys: ['y', 'z']
+ labels: ['y', 'z']
+ lineColors: ['#abcdef', '#fedcba']
+ smooth: true
+
+ shouldHavePath = (regex, color = '#abcdef') ->
+ # Matches an SVG path element within the rendered chart.
+ #
+ # Sneakily uses line colors to differentiate between paths within
+ # the chart.
+ $('#graph').find("path[stroke='#{color}']").attr('d').should.match regex
+
+ it 'should generate smooth lines when options.smooth is true', ->
+ Morris.Line @defaults
+ shouldHavePath /M[\d\.]+,[\d\.]+(C[\d\.]+(,[\d\.]+){5}){4}/
+
+ it 'should generate jagged lines when options.smooth is false', ->
+ Morris.Line $.extend(@defaults, smooth: false)
+ shouldHavePath /M[\d\.]+,[\d\.]+(L[\d\.]+,[\d\.]+){4}/
+
+ it 'should generate smooth/jagged lines according to the value for each series when options.smooth is an array', ->
+ Morris.Line $.extend(@defaults, smooth: ['y'])
+ shouldHavePath /M[\d\.]+,[\d\.]+(C[\d\.]+(,[\d\.]+){5}){4}/, '#abcdef'
+ shouldHavePath /M[\d\.]+,[\d\.]+(L[\d\.]+,[\d\.]+){4}/, '#fedcba'
+
+ it 'should ignore undefined values', ->
+ @defaults.data[2].y = undefined
+ Morris.Line @defaults
+ shouldHavePath /M[\d\.]+,[\d\.]+(C[\d\.]+(,[\d\.]+){5}){3}/
+
+ it 'should break the line at null values', ->
+ @defaults.data[2].y = null
+ Morris.Line @defaults
+ shouldHavePath /(M[\d\.]+,[\d\.]+C[\d\.]+(,[\d\.]+){5}){2}/
+
+ it 'should make line width customizable', ->
+ chart = Morris.Line $.extend(@defaults, lineWidth: [1, 2])
+ chart.lineWidthForSeries(0).should.equal 1
+ chart.lineWidthForSeries(1).should.equal 2
+
+ describe '#createPath', ->
+
+ it 'should generate a smooth line', ->
+ testData = [{x: 0, y: 10}, {x: 10, y: 0}, {x: 20, y: 10}]
+ path = Morris.Line.createPath(testData, true, 20)
+ path.should.equal 'M0,10C2.5,7.5,7.5,0,10,0C12.5,0,17.5,7.5,20,10'
+
+ it 'should generate a jagged line', ->
+ testData = [{x: 0, y: 10}, {x: 10, y: 0}, {x: 20, y: 10}]
+ path = Morris.Line.createPath(testData, false, 20)
+ path.should.equal 'M0,10L10,0L20,10'
+
+ it 'should prevent paths from descending below the bottom of the chart', ->
+ testData = [{x: 0, y: 20}, {x: 10, y: 30}, {x: 20, y: 10}]
+ path = Morris.Line.createPath(testData, true, 30)
+ path.should.equal 'M0,20C2.5,22.5,7.5,30,10,30C12.5,28.75,17.5,15,20,10'
+
+ it 'should break the line at null values', ->
+ testData = [{x: 0, y: 10}, {x: 10, y: 0}, {x: 20, y: null}, {x: 30, y: 10}, {x: 40, y: 0}]
+ path = Morris.Line.createPath(testData, true, 20)
+ path.should.equal 'M0,10C2.5,7.5,7.5,2.5,10,0M30,10C32.5,7.5,37.5,2.5,40,0'
+
+ it 'should ignore leading and trailing null values', ->
+ testData = [{x: 0, y: null}, {x: 10, y: 10}, {x: 20, y: 0}, {x: 30, y: 10}, {x: 40, y: null}]
+ path = Morris.Line.createPath(testData, true, 20)
+ path.should.equal 'M10,10C12.5,7.5,17.5,0,20,0C22.5,0,27.5,7.5,30,10'
+
+ describe 'svg structure', ->
+ defaults =
+ element: 'graph'
+ data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
+ lineColors: [ '#0b62a4', '#7a92a3']
+ xkey: 'x'
+ ykeys: ['y']
+ labels: ['dontcare']
+
+ it 'should contain a path that represents the line', ->
+ chart = Morris.Line $.extend {}, defaults
+ $('#graph').find("path[stroke='#0b62a4']").size().should.equal 1
+
+ it 'should contain a circle for each data point', ->
+ chart = Morris.Line $.extend {}, defaults
+ $('#graph').find("circle").size().should.equal 2
+
+ it 'should contain 5 grid lines', ->
+ chart = Morris.Line $.extend {}, defaults
+ $('#graph').find("path[stroke='#aaaaaa']").size().should.equal 5
+
+ it 'should contain 9 text elements', ->
+ chart = Morris.Line $.extend {}, defaults
+ $('#graph').find("text").size().should.equal 9
+
+ describe 'svg attributes', ->
+ defaults =
+ element: 'graph'
+ data: [{x: '2012 Q1', y: 1}, {x: '2012 Q2', y: 1}]
+ xkey: 'x'
+ ykeys: ['y', 'z']
+ labels: ['Y', 'Z']
+ lineColors: [ '#0b62a4', '#7a92a3']
+ lineWidth: 3
+ pointStrokeWidths: [5]
+ pointStrokeColors: ['#ffffff']
+ gridLineColor: '#aaa'
+ gridStrokeWidth: 0.5
+ gridTextColor: '#888'
+ gridTextSize: 12
+ pointSize: [5]
+
+ it 'should have circles with configured fill color', ->
+ chart = Morris.Line $.extend {}, defaults
+ $('#graph').find("circle[fill='#0b62a4']").size().should.equal 2
+
+ it 'should have circles with configured stroke width', ->
+ chart = Morris.Line $.extend {}, defaults
+ $('#graph').find("circle[stroke-width='5']").size().should.equal 2
+
+ it 'should have circles with configured stroke color', ->
+ chart = Morris.Line $.extend {}, defaults
+ $('#graph').find("circle[stroke='#ffffff']").size().should.equal 2
+
+ it 'should have line with configured line width', ->
+ chart = Morris.Line $.extend {}, defaults
+ $('#graph').find("path[stroke-width='3']").size().should.equal 1
+
+ it 'should have text with configured font size', ->
+ chart = Morris.Line $.extend {}, defaults
+ $('#graph').find("text[font-size='12px']").size().should.equal 9
+
+ it 'should have text with configured font size', ->
+ chart = Morris.Line $.extend {}, defaults
+ $('#graph').find("text[fill='#888888']").size().should.equal 9
+
+ it 'should have circle with configured size', ->
+ chart = Morris.Line $.extend {}, defaults
+ $('#graph').find("circle[r='5']").size().should.equal 2
diff --git a/public/bower_components/morris.js/spec/lib/pad_spec.coffee b/public/bower_components/morris.js/spec/lib/pad_spec.coffee
new file mode 100644
index 0000000..f81a592
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/pad_spec.coffee
@@ -0,0 +1,17 @@
+describe '#pad', ->
+
+ it 'should pad numbers', ->
+ Morris.pad2(0).should.equal("00")
+ Morris.pad2(1).should.equal("01")
+ Morris.pad2(2).should.equal("02")
+ Morris.pad2(3).should.equal("03")
+ Morris.pad2(4).should.equal("04")
+ Morris.pad2(5).should.equal("05")
+ Morris.pad2(6).should.equal("06")
+ Morris.pad2(7).should.equal("07")
+ Morris.pad2(8).should.equal("08")
+ Morris.pad2(9).should.equal("09")
+ Morris.pad2(10).should.equal("10")
+ Morris.pad2(12).should.equal("12")
+ Morris.pad2(34).should.equal("34")
+ Morris.pad2(123).should.equal("123") \ No newline at end of file
diff --git a/public/bower_components/morris.js/spec/lib/parse_time_spec.coffee b/public/bower_components/morris.js/spec/lib/parse_time_spec.coffee
new file mode 100644
index 0000000..199aa9d
--- /dev/null
+++ b/public/bower_components/morris.js/spec/lib/parse_time_spec.coffee
@@ -0,0 +1,35 @@
+describe '#parseTime', ->
+
+ it 'should parse years', ->
+ Morris.parseDate('2012').should.equal(new Date(2012, 0, 1).getTime())
+
+ it 'should parse quarters', ->
+ Morris.parseDate('2012 Q1').should.equal(new Date(2012, 2, 1).getTime())
+
+ it 'should parse months', ->
+ Morris.parseDate('2012-09').should.equal(new Date(2012, 8, 1).getTime())
+ Morris.parseDate('2012-10').should.equal(new Date(2012, 9, 1).getTime())
+
+ it 'should parse dates', ->
+ Morris.parseDate('2012-09-15').should.equal(new Date(2012, 8, 15).getTime())
+ Morris.parseDate('2012-10-15').should.equal(new Date(2012, 9, 15).getTime())
+
+ it 'should parse times', ->
+ Morris.parseDate("2012-10-15 12:34").should.equal(new Date(2012, 9, 15, 12, 34).getTime())
+ Morris.parseDate("2012-10-15T12:34").should.equal(new Date(2012, 9, 15, 12, 34).getTime())
+ Morris.parseDate("2012-10-15 12:34:55").should.equal(new Date(2012, 9, 15, 12, 34, 55).getTime())
+ Morris.parseDate("2012-10-15T12:34:55").should.equal(new Date(2012, 9, 15, 12, 34, 55).getTime())
+
+ it 'should parse times with timezones', ->
+ Morris.parseDate("2012-10-15T12:34+0100").should.equal(Date.UTC(2012, 9, 15, 11, 34))
+ Morris.parseDate("2012-10-15T12:34+02:00").should.equal(Date.UTC(2012, 9, 15, 10, 34))
+ Morris.parseDate("2012-10-15T12:34-0100").should.equal(Date.UTC(2012, 9, 15, 13, 34))
+ Morris.parseDate("2012-10-15T12:34-02:00").should.equal(Date.UTC(2012, 9, 15, 14, 34))
+ Morris.parseDate("2012-10-15T12:34:55Z").should.equal(Date.UTC(2012, 9, 15, 12, 34, 55))
+ Morris.parseDate("2012-10-15T12:34:55+0600").should.equal(Date.UTC(2012, 9, 15, 6, 34, 55))
+ Morris.parseDate("2012-10-15T12:34:55+04:00").should.equal(Date.UTC(2012, 9, 15, 8, 34, 55))
+ Morris.parseDate("2012-10-15T12:34:55-0600").should.equal(Date.UTC(2012, 9, 15, 18, 34, 55))
+
+ it 'should pass-through timestamps', ->
+ Morris.parseDate(new Date(2012, 9, 15, 12, 34, 55, 123).getTime())
+ .should.equal(new Date(2012, 9, 15, 12, 34, 55, 123).getTime()) \ No newline at end of file
diff --git a/public/bower_components/morris.js/spec/specs.html b/public/bower_components/morris.js/spec/specs.html
new file mode 100644
index 0000000..28ec092
--- /dev/null
+++ b/public/bower_components/morris.js/spec/specs.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<head>
+ <meta charset="utf-8">
+ <title>morris.js tests</title>
+ <link rel="stylesheet" href="../bower_components/mocha/mocha.css" type="text/css" media="screen" />
+ <link rel="stylesheet" href="../morris.css" type="text/css" media="screen" />
+ <!-- jQuery packaging changed for 2.1.0, so try to load both paths, one will work. -->
+ <script src="../bower_components/jquery/dist/jquery.js"></script>
+ <script src="../bower_components/jquery/jquery.js"></script>
+ <script type="text/javascript" src="../bower_components/raphael/raphael-min.js"></script>
+</head>
+<body>
+ <div id="mocha"></div>
+
+ <script type="text/javascript" src="../bower_components/mocha/mocha.js"></script>
+ <script type="text/javascript" src="../bower_components/chai/chai.js"></script>
+ <script type="text/javascript" src="../bower_components/chai-jquery/chai-jquery.js"></script>
+ <script type="text/javascript" src="../bower_components/sinon/index.js"></script>
+ <script type="text/javascript" src="../bower_components/sinon-chai/lib/sinon-chai.js"></script>
+ <script>
+ mocha.setup('bdd');
+ should = chai.should();
+ </script>
+
+ <script type="text/javascript" src="../morris.js"></script>
+ <script type="text/javascript" src="../build/spec.js"></script>
+ <div id="test" style="width: 400px; height: 200px;"></div>
+ <script>
+ if (navigator.userAgent.indexOf('PhantomJS') < 0) {
+ mocha.run();
+ }
+ </script>
+</body>
+</html>
diff --git a/public/bower_components/morris.js/spec/support/placeholder.coffee b/public/bower_components/morris.js/spec/support/placeholder.coffee
new file mode 100644
index 0000000..f715035
--- /dev/null
+++ b/public/bower_components/morris.js/spec/support/placeholder.coffee
@@ -0,0 +1,6 @@
+beforeEach ->
+ placeholder = $('<div id="graph" style="width: 600px; height: 400px"></div>')
+ $('#test').append(placeholder)
+
+afterEach ->
+ $('#test').empty()
diff --git a/public/bower_components/morris.js/spec/viz/examples.js b/public/bower_components/morris.js/spec/viz/examples.js
new file mode 100644
index 0000000..0e97de3
--- /dev/null
+++ b/public/bower_components/morris.js/spec/viz/examples.js
@@ -0,0 +1,56 @@
+var webpage = require("webpage"),
+ fs = require("fs");
+
+var html_path = fs.absolute("test.html");
+var examples = [];
+
+function run_example(example_index) {
+ if (example_index >= examples.length) {
+ phantom.exit(0);
+ return;
+ }
+
+ var example = examples[example_index];
+ var snapshot_index = 0;
+ var page = webpage.create();
+
+ page.viewportSize = { width: 500, height: 300 };
+ page.clipRect = { width: 500, height: 300 };
+ page.onAlert = function (msg) {
+ var e = JSON.parse(msg);
+ if (e.fn == "snapshot") {
+ page.render("output/" + example.name + snapshot_index + ".png");
+ snapshot_index += 1;
+ } else if (e.fn == "mousemove") {
+ page.sendEvent("mousemove", e.x, e.y);
+ }
+ };
+
+ page.open(html_path, function (status) {
+ if (status == "fail") {
+ console.log("Failed to load test page: " + example.name);
+ phantom.exit(1);
+ } else {
+ page.evaluate(example.runner);
+ }
+ page.close();
+ run_example(example_index + 1);
+ });
+}
+
+exports.def = function (name, runner) {
+ examples.push({ name: name, runner: runner });
+};
+
+exports.run = function () {
+ if (fs.isDirectory("output")) {
+ fs.list("output").forEach(function (path) {
+ if (path != "." && path != "..") {
+ fs.remove("output/" + path);
+ }
+ });
+ } else {
+ fs.makeDirectory("output");
+ }
+ run_example(0);
+};
diff --git a/public/bower_components/morris.js/spec/viz/exemplary/area0.png b/public/bower_components/morris.js/spec/viz/exemplary/area0.png
new file mode 100644
index 0000000..67e5f4a
--- /dev/null
+++ b/public/bower_components/morris.js/spec/viz/exemplary/area0.png
Binary files differ
diff --git a/public/bower_components/morris.js/spec/viz/exemplary/bar0.png b/public/bower_components/morris.js/spec/viz/exemplary/bar0.png
new file mode 100644
index 0000000..03f12e0
--- /dev/null
+++ b/public/bower_components/morris.js/spec/viz/exemplary/bar0.png
Binary files differ
diff --git a/public/bower_components/morris.js/spec/viz/exemplary/line0.png b/public/bower_components/morris.js/spec/viz/exemplary/line0.png
new file mode 100644
index 0000000..70f814c
--- /dev/null
+++ b/public/bower_components/morris.js/spec/viz/exemplary/line0.png
Binary files differ
diff --git a/public/bower_components/morris.js/spec/viz/exemplary/stacked_bar0.png b/public/bower_components/morris.js/spec/viz/exemplary/stacked_bar0.png
new file mode 100644
index 0000000..238e05f
--- /dev/null
+++ b/public/bower_components/morris.js/spec/viz/exemplary/stacked_bar0.png
Binary files differ
diff --git a/public/bower_components/morris.js/spec/viz/run.sh b/public/bower_components/morris.js/spec/viz/run.sh
new file mode 100755
index 0000000..ae671d4
--- /dev/null
+++ b/public/bower_components/morris.js/spec/viz/run.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# visual_specs.js creates output in output/XXX.png
+phantomjs visual_specs.js
+
+# clear out old diffs
+mkdir -p diff
+rm -f diff/*
+
+# generate diffs
+PASS=1
+for i in exemplary/*.png
+do
+ FN=`basename $i`
+ perceptualdiff $i output/$FN -output diff/$FN
+ if [ $? -eq 0 ]
+ then
+ echo "OK: $FN"
+ else
+ echo "FAIL: $FN"
+ PASS=0
+ fi
+done
+
+# pass / fail
+if [ $PASS -eq 1 ]
+then
+ echo "Success."
+else
+ echo "Failed."
+ exit 1
+fi
diff --git a/public/bower_components/morris.js/spec/viz/test.html b/public/bower_components/morris.js/spec/viz/test.html
new file mode 100644
index 0000000..28db054
--- /dev/null
+++ b/public/bower_components/morris.js/spec/viz/test.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<head>
+ <!-- jQuery packaging changed for 2.1.0, so try to load both paths, one will work. -->
+ <script src="../../bower_components/jquery/dist/jquery.js"></script>
+ <script src="../../bower_components/jquery/jquery.js"></script>
+ <script src="../../bower_components/raphael/raphael-min.js"></script>
+ <script src="../../morris.js"></script>
+ <link rel="stylesheet" href="../../morris.css">
+ <style>
+ body {
+ padding: 0;
+ margin: 0;
+ background-color: white;
+ }
+ #chart {
+ width: 500px;
+ height: 300px;
+ }
+ </style>
+ <script>
+ function bridge(e) {
+ window.alert(JSON.stringify(e));
+ }
+ window.snapshot = function () {
+ bridge({ fn: "snapshot" });
+ };
+ window.mousemove = function (x, y) {
+ bridge({ fn: "mousemove", x: x, y: y });
+ };
+ </script>
+</head>
+<body>
+<div id="chart"></div>
+</body>
diff --git a/public/bower_components/morris.js/spec/viz/visual_specs.js b/public/bower_components/morris.js/spec/viz/visual_specs.js
new file mode 100644
index 0000000..20dc666
--- /dev/null
+++ b/public/bower_components/morris.js/spec/viz/visual_specs.js
@@ -0,0 +1,66 @@
+var examples = require('./examples');
+
+examples.def('line', function () {
+ Morris.Line({
+ element: 'chart',
+ data: [
+ { x: 0, y: 10, z: 30 }, { x: 1, y: 20, z: 20 },
+ { x: 2, y: 30, z: 10 }, { x: 3, y: 30, z: 10 },
+ { x: 4, y: 20, z: 20 }, { x: 5, y: 10, z: 30 }
+ ],
+ xkey: 'x',
+ ykeys: ['y', 'z'],
+ labels: ['y', 'z'],
+ parseTime: false
+ });
+ window.snapshot();
+});
+
+examples.def('area', function () {
+ Morris.Area({
+ element: 'chart',
+ data: [
+ { x: 0, y: 1, z: 1 }, { x: 1, y: 2, z: 1 },
+ { x: 2, y: 3, z: 1 }, { x: 3, y: 3, z: 1 },
+ { x: 4, y: 2, z: 1 }, { x: 5, y: 1, z: 1 }
+ ],
+ xkey: 'x',
+ ykeys: ['y', 'z'],
+ labels: ['y', 'z'],
+ parseTime: false
+ });
+ window.snapshot();
+});
+
+examples.def('bar', function () {
+ Morris.Bar({
+ element: 'chart',
+ data: [
+ { x: 0, y: 1, z: 3 }, { x: 1, y: 2, z: 2 },
+ { x: 2, y: 3, z: 1 }, { x: 3, y: 3, z: 1 },
+ { x: 4, y: 2, z: 2 }, { x: 5, y: 1, z: 3 }
+ ],
+ xkey: 'x',
+ ykeys: ['y', 'z'],
+ labels: ['y', 'z']
+ });
+ window.snapshot();
+});
+
+examples.def('stacked_bar', function () {
+ Morris.Bar({
+ element: 'chart',
+ data: [
+ { x: 0, y: 1, z: 1 }, { x: 1, y: 2, z: 1 },
+ { x: 2, y: 3, z: 1 }, { x: 3, y: 3, z: 1 },
+ { x: 4, y: 2, z: 1 }, { x: 5, y: 1, z: 1 }
+ ],
+ xkey: 'x',
+ ykeys: ['y', 'z'],
+ labels: ['y', 'z'],
+ stacked: true
+ });
+ window.snapshot();
+});
+
+examples.run();