mirror of
https://github.com/cwinfo/powerdns-admin.git
synced 2025-06-14 20:16:05 +00:00
Initial commit
This commit is contained in:
234
app/static/global/plugins/morris/README.md
Normal file
234
app/static/global/plugins/morris/README.md
Normal file
@ -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.
|
18
app/static/global/plugins/morris/examples/_template.html
Normal file
18
app/static/global/plugins/morris/examples/_template.html
Normal file
@ -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>
|
31
app/static/global/plugins/morris/examples/area-as-line.html
Normal file
31
app/static/global/plugins/morris/examples/area-as-line.html
Normal file
@ -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>
|
34
app/static/global/plugins/morris/examples/area.html
Normal file
34
app/static/global/plugins/morris/examples/area.html
Normal file
@ -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>
|
44
app/static/global/plugins/morris/examples/bar-colors.html
Normal file
44
app/static/global/plugins/morris/examples/bar-colors.html
Normal file
@ -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>
|
31
app/static/global/plugins/morris/examples/bar-no-axes.html
Normal file
31
app/static/global/plugins/morris/examples/bar-no-axes.html
Normal file
@ -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>
|
32
app/static/global/plugins/morris/examples/bar.html
Normal file
32
app/static/global/plugins/morris/examples/bar.html
Normal file
@ -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>
|
37
app/static/global/plugins/morris/examples/days.html
Normal file
37
app/static/global/plugins/morris/examples/days.html
Normal file
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
38
app/static/global/plugins/morris/examples/donut-colors.html
Normal file
38
app/static/global/plugins/morris/examples/donut-colors.html
Normal file
@ -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>
|
@ -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>
|
29
app/static/global/plugins/morris/examples/donut.html
Normal file
29
app/static/global/plugins/morris/examples/donut.html
Normal file
@ -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>
|
30
app/static/global/plugins/morris/examples/dst.html
Normal file
30
app/static/global/plugins/morris/examples/dst.html
Normal file
@ -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>
|
57
app/static/global/plugins/morris/examples/events.html
Normal file
57
app/static/global/plugins/morris/examples/events.html
Normal file
@ -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>
|
33
app/static/global/plugins/morris/examples/goals.html
Normal file
33
app/static/global/plugins/morris/examples/goals.html
Normal file
@ -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>
|
13
app/static/global/plugins/morris/examples/lib/example.css
Normal file
13
app/static/global/plugins/morris/examples/lib/example.css
Normal file
@ -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;
|
||||
}
|
4
app/static/global/plugins/morris/examples/lib/example.js
Normal file
4
app/static/global/plugins/morris/examples/lib/example.js
Normal file
@ -0,0 +1,4 @@
|
||||
$(function () {
|
||||
eval($('#code').text());
|
||||
prettyPrint();
|
||||
});
|
@ -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>
|
36
app/static/global/plugins/morris/examples/negative.html
Normal file
36
app/static/global/plugins/morris/examples/negative.html
Normal file
@ -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>
|
38
app/static/global/plugins/morris/examples/no-grid.html
Normal file
38
app/static/global/plugins/morris/examples/no-grid.html
Normal file
@ -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>
|
@ -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>
|
37
app/static/global/plugins/morris/examples/non-date.html
Normal file
37
app/static/global/plugins/morris/examples/non-date.html
Normal file
@ -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>
|
54
app/static/global/plugins/morris/examples/quarters.html
Normal file
54
app/static/global/plugins/morris/examples/quarters.html
Normal file
@ -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>
|
42
app/static/global/plugins/morris/examples/resize.html
Normal file
42
app/static/global/plugins/morris/examples/resize.html
Normal file
@ -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>
|
31
app/static/global/plugins/morris/examples/stacked_bars.html
Normal file
31
app/static/global/plugins/morris/examples/stacked_bars.html
Normal file
@ -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>
|
38
app/static/global/plugins/morris/examples/timestamps.html
Normal file
38
app/static/global/plugins/morris/examples/timestamps.html
Normal file
@ -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>
|
49
app/static/global/plugins/morris/examples/updating.html
Normal file
49
app/static/global/plugins/morris/examples/updating.html
Normal file
@ -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>
|
53
app/static/global/plugins/morris/examples/weeks.html
Normal file
53
app/static/global/plugins/morris/examples/weeks.html
Normal file
@ -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>
|
37
app/static/global/plugins/morris/examples/years.html
Normal file
37
app/static/global/plugins/morris/examples/years.html
Normal file
@ -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>
|
27
app/static/global/plugins/morris/less/morris.core.less
Normal file
27
app/static/global/plugins/morris/less/morris.core.less
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
66
app/static/global/plugins/morris/lib/morris.area.coffee
Normal file
66
app/static/global/plugins/morris/lib/morris.area.coffee
Normal file
@ -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')
|
208
app/static/global/plugins/morris/lib/morris.bar.coffee
Normal file
208
app/static/global/plugins/morris/lib/morris.bar.coffee
Normal file
@ -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" ]
|
||||
|
43
app/static/global/plugins/morris/lib/morris.coffee
Normal file
43
app/static/global/plugins/morris/lib/morris.coffee
Normal file
@ -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
|
213
app/static/global/plugins/morris/lib/morris.donut.coffee
Normal file
213
app/static/global/plugins/morris/lib/morris.donut.coffee
Normal file
@ -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
|
499
app/static/global/plugins/morris/lib/morris.grid.coffee
Normal file
499
app/static/global/plugins/morris/lib/morris.grid.coffee
Normal file
@ -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()
|
||||
|
44
app/static/global/plugins/morris/lib/morris.hover.coffee
Normal file
44
app/static/global/plugins/morris/lib/morris.hover.coffee
Normal file
@ -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()
|
405
app/static/global/plugins/morris/lib/morris.line.coffee
Normal file
405
app/static/global/plugins/morris/lib/morris.line.coffee
Normal file
@ -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"
|
||||
]
|
2
app/static/global/plugins/morris/morris.css
Normal file
2
app/static/global/plugins/morris/morris.css
Normal file
@ -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}
|
1892
app/static/global/plugins/morris/morris.js
Normal file
1892
app/static/global/plugins/morris/morris.js
Normal file
File diff suppressed because it is too large
Load Diff
7
app/static/global/plugins/morris/morris.min.js
vendored
Normal file
7
app/static/global/plugins/morris/morris.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
11
app/static/global/plugins/morris/raphael-min.js
vendored
Normal file
11
app/static/global/plugins/morris/raphael-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -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
|
127
app/static/global/plugins/morris/spec/lib/bar/bar_spec.coffee
Normal file
127
app/static/global/plugins/morris/spec/lib/bar/bar_spec.coffee
Normal file
@ -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
|
||||
|
||||
|
36
app/static/global/plugins/morris/spec/lib/bar/colours.coffee
Normal file
36
app/static/global/plugins/morris/spec/lib/bar/colours.coffee
Normal file
@ -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')
|
38
app/static/global/plugins/morris/spec/lib/commas_spec.coffee
Normal file
38
app/static/global/plugins/morris/spec/lib/commas_spec.coffee
Normal file
@ -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('-')
|
@ -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
|
@ -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]
|
@ -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]
|
@ -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"
|
64
app/static/global/plugins/morris/spec/lib/hover_spec.coffee
Normal file
64
app/static/global/plugins/morris/spec/lib/hover_spec.coffee
Normal file
@ -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!')
|
@ -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()]
|
||||
])
|
211
app/static/global/plugins/morris/spec/lib/line/line_spec.coffee
Normal file
211
app/static/global/plugins/morris/spec/lib/line/line_spec.coffee
Normal file
@ -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
|
17
app/static/global/plugins/morris/spec/lib/pad_spec.coffee
Normal file
17
app/static/global/plugins/morris/spec/lib/pad_spec.coffee
Normal file
@ -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")
|
@ -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())
|
34
app/static/global/plugins/morris/spec/specs.html
Normal file
34
app/static/global/plugins/morris/spec/specs.html
Normal file
@ -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>
|
@ -0,0 +1,6 @@
|
||||
beforeEach ->
|
||||
placeholder = $('<div id="graph" style="width: 600px; height: 400px"></div>')
|
||||
$('#test').append(placeholder)
|
||||
|
||||
afterEach ->
|
||||
$('#test').empty()
|
56
app/static/global/plugins/morris/spec/viz/examples.js
Normal file
56
app/static/global/plugins/morris/spec/viz/examples.js
Normal file
@ -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);
|
||||
};
|
BIN
app/static/global/plugins/morris/spec/viz/exemplary/area0.png
Normal file
BIN
app/static/global/plugins/morris/spec/viz/exemplary/area0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
BIN
app/static/global/plugins/morris/spec/viz/exemplary/bar0.png
Normal file
BIN
app/static/global/plugins/morris/spec/viz/exemplary/bar0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.8 KiB |
BIN
app/static/global/plugins/morris/spec/viz/exemplary/line0.png
Normal file
BIN
app/static/global/plugins/morris/spec/viz/exemplary/line0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
32
app/static/global/plugins/morris/spec/viz/run.sh
Normal file
32
app/static/global/plugins/morris/spec/viz/run.sh
Normal file
@ -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
|
34
app/static/global/plugins/morris/spec/viz/test.html
Normal file
34
app/static/global/plugins/morris/spec/viz/test.html
Normal file
@ -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>
|
66
app/static/global/plugins/morris/spec/viz/visual_specs.js
Normal file
66
app/static/global/plugins/morris/spec/viz/visual_specs.js
Normal file
@ -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();
|
Reference in New Issue
Block a user