diff options
author | Marvin Borner | 2018-07-13 19:06:45 +0200 |
---|---|---|
committer | Marvin Borner | 2018-07-13 19:06:45 +0200 |
commit | 6fcfb7c04d32e1c8b26a312295bf7ac3ec2d2ad7 (patch) | |
tree | dbc87ef16fa01d5d99116de283592b8fe5e02944 /public/bower_components/morris.js | |
parent | dfd839f27146df0ad0494e11734fc7d310c70ebf (diff) |
Fixed many permissions and began admin interface
Diffstat (limited to 'public/bower_components/morris.js')
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 + +[](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 Binary files differnew file mode 100644 index 0000000..67e5f4a --- /dev/null +++ b/public/bower_components/morris.js/spec/viz/exemplary/area0.png diff --git a/public/bower_components/morris.js/spec/viz/exemplary/bar0.png b/public/bower_components/morris.js/spec/viz/exemplary/bar0.png Binary files differnew file mode 100644 index 0000000..03f12e0 --- /dev/null +++ b/public/bower_components/morris.js/spec/viz/exemplary/bar0.png diff --git a/public/bower_components/morris.js/spec/viz/exemplary/line0.png b/public/bower_components/morris.js/spec/viz/exemplary/line0.png Binary files differnew file mode 100644 index 0000000..70f814c --- /dev/null +++ b/public/bower_components/morris.js/spec/viz/exemplary/line0.png 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 Binary files differnew file mode 100644 index 0000000..238e05f --- /dev/null +++ b/public/bower_components/morris.js/spec/viz/exemplary/stacked_bar0.png 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(); |