First Initial
This commit is contained in:
6
wwwroot/BackendScript/assets/morris.js-0.4.3/.travis.yml
Normal file
6
wwwroot/BackendScript/assets/morris.js-0.4.3/.travis.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
before_script:
|
||||
- "npm install -g grunt"
|
||||
- "npm install"
|
||||
190
wwwroot/BackendScript/assets/morris.js-0.4.3/README.md
Normal file
190
wwwroot/BackendScript/assets/morris.js-0.4.3/README.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# Morris.js - pretty time-series line graphs
|
||||
|
||||
[](http://travis-ci.org/oesmith/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)
|
||||
|
||||
## 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://oesmith.github.com/morris.js/).
|
||||
|
||||
## Development
|
||||
|
||||
Very daring.
|
||||
|
||||
Fork, hack, possibly even add some tests, then send a pull request :)
|
||||
|
||||
### 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`, 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`.
|
||||
|
||||
## Changelog
|
||||
|
||||
### 0.4.3 - 12th May 2013
|
||||
|
||||
- Fix flickering hover box [#186](https://github.com/oesmith/morris.js/issues/186)
|
||||
- xLabelAngle option (diagonal labels!!) [#239](https://github.com/oesmith/morris.js/issues/239)
|
||||
- Fix area chart fill bug [#190](https://github.com/oesmith/morris.js/issues/190)
|
||||
- Make event handlers chainable
|
||||
- gridTextFamily and gridTextWeight options
|
||||
- Fix hovers with setData [#213](https://github.com/oesmith/morris.js/issues/213)
|
||||
- Fix hideHover behaviour [#236](https://github.com/oesmith/morris.js/issues/236)
|
||||
|
||||
### 0.4.2 - 14th April 2013
|
||||
|
||||
- Fix DST handling [#191](https://github.com/oesmith/morris.js/issues/191)
|
||||
- Parse data values from strings in Morris.Donut [#189](https://github.com/oesmith/morris.js/issues/189)
|
||||
- Non-cumulative area charts [#199](https://github.com/oesmith/morris.js/issues/199)
|
||||
- Round Y-axis labels to significant numbers [#162](https://github.com/oesmith/morris.js/162)
|
||||
- Customising default hover content [#179](https://github.com/oesmith/morris.js/179)
|
||||
|
||||
### 0.4.1 - 8th February 2013
|
||||
|
||||
- Fix goal and event rendering. [#181](https://github.com/oesmith/morris.js/issues/181)
|
||||
- Don't break when empty data is passed to setData [#142](https://github.com/oesmith/morris.js/issues/142)
|
||||
- labelColor option for donuts [#159](https://github.com/oesmith/morris.js/issues/159)
|
||||
|
||||
### 0.4.0 - 26th January 2013
|
||||
|
||||
- Goals and events [#103](https://github.com/oesmith/morris.js/issues/103).
|
||||
- Bower package manager metadata.
|
||||
- More flexible formatters [#107](https://github.com/oesmith/morris.js/issues/107).
|
||||
- Color callbacks.
|
||||
- Decade intervals for time-axis labels.
|
||||
- Non-continous line tweaks [#116](https://github.com/oesmith/morris.js/issues/116).
|
||||
- Stacked bars [#120](https://github.com/oesmith/morris.js/issues/120).
|
||||
- HTML hover [#134](https://github.com/oesmith/morris.js/issues/134).
|
||||
- yLabelFormat [#139](https://github.com/oesmith/morris.js/issues/139).
|
||||
- Disable axes [#114](https://github.com/oesmith/morris.js/issues/114).
|
||||
|
||||
### 0.3.3 - 1st November 2012
|
||||
|
||||
- **Bar charts!** [#101](https://github.com/oesmith/morris.js/issues/101).
|
||||
|
||||
### 0.3.2 - 28th October 2012
|
||||
|
||||
- **Area charts!** [#47](https://github.com/oesmith/morris.js/issues/47).
|
||||
- Some major refactoring and test suite improvements.
|
||||
- Set smooth parameter per series [#91](https://github.com/oesmith/morris.js/issues/91).
|
||||
- Custom dateFormat for string x-values [#90](https://github.com/oesmith/morris.js/issues/90).
|
||||
|
||||
### 0.3.1 - 13th October 2012
|
||||
|
||||
- Add `formatter` option for customising value labels in donuts [#75](https://github.com/oesmith/morris.js/issues/75).
|
||||
- Cycle `lineColors` on line charts to avoid running out of colours [#78](https://github.com/oesmith/morris.js/issues/78).
|
||||
- Add method to select donut segments. [#79](https://github.com/oesmith/morris.js/issues/79).
|
||||
- Don't go negative on yMin when all y values are zero. [#80](https://github.com/oesmith/morris.js/issues/80).
|
||||
- Don't sort data when parseTime is false [#83](https://github.com/oesmith/morris.js/issues/83).
|
||||
- Customise styling for points. [#87](https://github.com/oesmith/morris.js/issues/87).
|
||||
|
||||
### 0.3.0 - 15th September 2012
|
||||
|
||||
- Donut charts!
|
||||
- Bugfix: ymin/ymax bug [#71](https://github.com/oesmith/morris.js/issues/71).
|
||||
- Bugfix: infinite loop when data indicates horizontal line [#66](https://github.com/oesmith/morris.js/issues/66).
|
||||
|
||||
### 0.2.10 - 26th June 2012
|
||||
|
||||
- Support for decimal labels on y-axis [#58](https://github.com/oesmith/morris.js/issues/58).
|
||||
- Better axis label clipping [#63](https://github.com/oesmith/morris.js/issues/63).
|
||||
- Redraw graphs with updated data using `setData` method [#64](https://github.com/oesmith/morris.js/issues/64).
|
||||
- Bugfix: series with zero or one non-null values [#65](https://github.com/oesmith/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, 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.
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "morris.js",
|
||||
"version": "0.4.3",
|
||||
"main": ["./morris.js", "./morris.css"],
|
||||
"dependencies": {
|
||||
"jquery": ">= 1.7.2",
|
||||
"raphael": ">= 2.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,31 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,32 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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: '2011 Q1', y: 3, z: 3},
|
||||
{x: '2011 Q2', y: 2, z: 0},
|
||||
{x: '2011 Q3', y: 2, z: 5},
|
||||
{x: '2011 Q4', y: 4, z: 4}
|
||||
],
|
||||
xkey: 'x',
|
||||
ykeys: ['y', 'z'],
|
||||
labels: ['Y', 'Z']
|
||||
}).on('click', function(i, row){
|
||||
console.log(i, row);
|
||||
});
|
||||
</pre>
|
||||
</body>
|
||||
@@ -0,0 +1,44 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,31 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,32 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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,38 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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) {
|
||||
var row = options.data[index];
|
||||
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://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,38 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,29 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,30 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,57 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
$(function () {
|
||||
eval($('#code').text());
|
||||
prettyPrint();
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
|
||||
@@ -0,0 +1,28 @@
|
||||
var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
|
||||
(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
|
||||
[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
|
||||
f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
|
||||
(j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
|
||||
{b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
|
||||
t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
|
||||
"string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
|
||||
l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
|
||||
q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
|
||||
q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
|
||||
"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
|
||||
a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
|
||||
for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
|
||||
m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
|
||||
a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
|
||||
j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
|
||||
"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
|
||||
H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
|
||||
J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
|
||||
I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
|
||||
["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
|
||||
/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
|
||||
["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
|
||||
hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
|
||||
!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
|
||||
250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
|
||||
PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();
|
||||
@@ -0,0 +1,38 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,36 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,38 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Non-continuous data</h1>
|
||||
<p>Null or missing series values will be skipped when rendering.</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>
|
||||
@@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,54 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,31 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,38 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,49 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,53 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
@@ -0,0 +1,37 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
||||
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
||||
<script src="../morris.js"></script>
|
||||
<script src="lib/prettify.js"></script>
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.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>
|
||||
56
wwwroot/BackendScript/assets/morris.js-0.4.3/grunt.js
Normal file
56
wwwroot/BackendScript/assets/morris.js-0.4.3/grunt.js
Normal file
@@ -0,0 +1,56 @@
|
||||
module.exports = function (grunt) {
|
||||
grunt.initConfig({
|
||||
coffee: {
|
||||
lib: {
|
||||
src: ['build/morris.coffee'],
|
||||
dest: '.',
|
||||
options: { bare: false }
|
||||
},
|
||||
spec: {
|
||||
src: ['build/spec.coffee'],
|
||||
dest: 'build',
|
||||
options: { bare: true }
|
||||
}
|
||||
},
|
||||
concat: {
|
||||
'build/morris.coffee': [
|
||||
'lib/morris.coffee',
|
||||
'lib/morris.grid.coffee',
|
||||
'lib/morris.hover.coffee',
|
||||
'lib/morris.line.coffee',
|
||||
'lib/morris.area.coffee',
|
||||
'lib/morris.bar.coffee',
|
||||
'lib/morris.donut.coffee'
|
||||
],
|
||||
'build/spec.coffee': ['spec/support/**/*.coffee', 'spec/lib/**/*.coffee']
|
||||
},
|
||||
less: {
|
||||
all: {
|
||||
src: 'less/*.less',
|
||||
dest: 'morris.css',
|
||||
options: {
|
||||
compress: true
|
||||
}
|
||||
}
|
||||
},
|
||||
min: {
|
||||
'morris.min.js': 'morris.js'
|
||||
},
|
||||
mocha: {
|
||||
spec: {
|
||||
src: 'spec/specs.html',
|
||||
run: true
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
files: ['lib/**/*.coffee', 'spec/lib/**/*.coffee', 'spec/support/**/*.coffee', 'less/**/*.less'],
|
||||
tasks: 'default'
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-coffee');
|
||||
grunt.loadNpmTasks('grunt-mocha');
|
||||
grunt.loadNpmTasks('grunt-contrib-less');
|
||||
|
||||
grunt.registerTask('default', 'concat coffee less min mocha');
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
.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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.apply Math, 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-width', 0)
|
||||
@@ -0,0 +1,187 @@
|
||||
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'
|
||||
]
|
||||
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
|
||||
@drawSeries()
|
||||
|
||||
# draw the x-axis labels
|
||||
#
|
||||
# @private
|
||||
drawXAxis: ->
|
||||
# draw x axis labels
|
||||
ypos = @bottom + @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
|
||||
leftPadding = groupWidth * (1 - @options.barSizeRatio) / 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
|
||||
|
||||
top -= lastTop if @options.stacked
|
||||
@drawBar(left, top, barWidth, size, @colorFor(row, sidx, 'bar'))
|
||||
|
||||
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 beneath the given coordinate
|
||||
#
|
||||
hitTest: (x, y) ->
|
||||
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, y)
|
||||
@fire 'click', index, @options.data[index], x, y
|
||||
|
||||
# hover movement event handler
|
||||
#
|
||||
# @private
|
||||
onHoverMove: (x, y) =>
|
||||
index = @hitTest(x, y)
|
||||
@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)
|
||||
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) ->
|
||||
@raphael.rect(xPos, yPos, width, height)
|
||||
.attr('fill', barColor)
|
||||
.attr('stroke-width', 0)
|
||||
@@ -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
|
||||
@@ -0,0 +1,201 @@
|
||||
# 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
|
||||
|
||||
# Create and render a donut chart.
|
||||
#
|
||||
constructor: (options) ->
|
||||
if not (this instanceof Morris.Donut)
|
||||
return new Morris.Donut(options)
|
||||
|
||||
if typeof options.element is 'string'
|
||||
@el = $ document.getElementById(options.element)
|
||||
else
|
||||
@el = $ options.element
|
||||
|
||||
@options = $.extend {}, @defaults, options
|
||||
|
||||
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
|
||||
@data = options.data
|
||||
@values = (parseFloat(row.value) for row in @data)
|
||||
|
||||
@redraw()
|
||||
|
||||
# Clear and redraw the chart.
|
||||
#
|
||||
# If you need to re-size your charts, call this method after changing the
|
||||
# size of the container element.
|
||||
redraw: ->
|
||||
@el.empty()
|
||||
|
||||
@raphael = new Raphael(@el[0])
|
||||
|
||||
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,
|
||||
@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.apply(null, value for value in @values)
|
||||
idx = 0
|
||||
for value in @values
|
||||
if value == max_value
|
||||
@select idx
|
||||
break
|
||||
idx += 1
|
||||
|
||||
# @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
|
||||
|
||||
|
||||
# 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
|
||||
@@ -0,0 +1,440 @@
|
||||
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
|
||||
|
||||
# more stuff
|
||||
@init() if @init
|
||||
|
||||
# load data
|
||||
@setData @options.data
|
||||
|
||||
# hover
|
||||
@el.bind 'mousemove', (evt) =>
|
||||
offset = @el.offset()
|
||||
@fire 'hovermove', evt.pageX - offset.left, evt.pageY - offset.top
|
||||
|
||||
@el.bind 'mouseout', (evt) =>
|
||||
@fire 'hoverout'
|
||||
|
||||
@el.bind 'touchstart touchmove touchend', (evt) =>
|
||||
touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0]
|
||||
offset = @el.offset()
|
||||
@fire 'hover', touch.pageX - offset.left, touch.pageY - offset.top
|
||||
touch
|
||||
|
||||
@el.bind 'click', (evt) =>
|
||||
offset = @el.offset()
|
||||
@fire 'gridclick', evt.pageX - offset.left, evt.pageY - offset.top
|
||||
|
||||
@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'
|
||||
]
|
||||
|
||||
# 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.apply(null, @options.goals)
|
||||
maxGoal = Math.max.apply(null, @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 = {}
|
||||
|
||||
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.parseTime and @options.events.length > 0
|
||||
@events = (Morris.parseDate(e) for e in @options.events)
|
||||
@xmax = Math.max(@xmax, Math.max.apply(null, @events))
|
||||
@xmin = Math.min(@xmin, Math.min.apply(null, @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 is true 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
|
||||
yLabelWidths = for gridLine in @grid
|
||||
@measureText(@yAxisFormat(gridLine)).width
|
||||
@left += Math.max(yLabelWidths...)
|
||||
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}"
|
||||
|
||||
updateHover: (x, y) ->
|
||||
hit = @hitTest(x, y)
|
||||
if hit?
|
||||
@hover.update(hit...)
|
||||
|
||||
# draw y axis labels, horizontal lines
|
||||
#
|
||||
drawGrid: ->
|
||||
return if @options.grid is false and @options.axes is false
|
||||
for lineY in @grid
|
||||
y = @transY(lineY)
|
||||
if @options.axes
|
||||
@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)
|
||||
|
||||
# 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()
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
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) ->
|
||||
@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()
|
||||
@@ -0,0 +1,383 @@
|
||||
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
|
||||
@pointGrow = Raphael.animation r: @options.pointSize + 3, 25, 'linear'
|
||||
@pointShrink = Raphael.animation r: @options.pointSize, 25, 'linear'
|
||||
|
||||
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'
|
||||
]
|
||||
pointWidths: [1]
|
||||
pointStrokeColors: ['#ffffff']
|
||||
pointFillColors: []
|
||||
smooth: true
|
||||
xLabels: 'auto'
|
||||
xLabelFormat: null
|
||||
xLabelMargin: 24
|
||||
continuousLine: true
|
||||
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.apply(null, [@bottom].concat(y for y in row._y when y?))
|
||||
|
||||
# hit test - returns the index of the row beneath the given coordinate
|
||||
#
|
||||
hitTest: (x, y) ->
|
||||
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, y)
|
||||
@fire 'click', index, @options.data[index], x, y
|
||||
|
||||
# hover movement event handler
|
||||
#
|
||||
# @private
|
||||
onHoverMove: (x, y) =>
|
||||
index = @hitTest(x, y)
|
||||
@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)
|
||||
[content, row._x, row._ymax]
|
||||
|
||||
|
||||
# generate paths for series lines
|
||||
#
|
||||
# @private
|
||||
generatePaths: ->
|
||||
@paths = for i in [0...@options.ykeys.length]
|
||||
smooth = @options.smooth is true or @options.ykeys[i] in @options.smooth
|
||||
coords = ({x: r._x, y: r._y[i]} for r in @data when r._y[i] isnt undefined)
|
||||
coords = (c for c in coords when c.y isnt null) if @options.continuousLine
|
||||
|
||||
if coords.length > 1
|
||||
Morris.Line.createPath coords, smooth, @bottom
|
||||
else
|
||||
null
|
||||
|
||||
# Draws the line chart.
|
||||
#
|
||||
draw: ->
|
||||
@drawXAxis() if @options.axes
|
||||
@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], @options.pointSize, @colorFor(row, index, 'point'), index)
|
||||
@seriesPoints[index].push(circle)
|
||||
|
||||
_drawLineFor: (index) ->
|
||||
path = @paths[index]
|
||||
if path isnt null
|
||||
@drawLinePath path, @colorFor(null, index, 'line')
|
||||
|
||||
# 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 @pointShrink
|
||||
if index isnt null and @prevHilight isnt index
|
||||
for i in [0..@seriesPoints.length-1]
|
||||
if @seriesPoints[i][index]
|
||||
@seriesPoints[i][index].animate @pointGrow
|
||||
@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) ->
|
||||
@raphael.path(path)
|
||||
.attr('stroke', lineColor)
|
||||
.attr('stroke-width', @options.lineWidth)
|
||||
|
||||
drawLinePoint: (xPos, yPos, size, pointColor, lineIndex) ->
|
||||
@raphael.circle(xPos, yPos, size)
|
||||
.attr('fill', pointColor)
|
||||
.attr('stroke-width', @strokeWidthForSeries(lineIndex))
|
||||
.attr('stroke', @strokeForSeries(lineIndex))
|
||||
|
||||
# @private
|
||||
strokeWidthForSeries: (index) ->
|
||||
@options.pointWidths[index % @options.pointWidths.length]
|
||||
|
||||
# @private
|
||||
strokeForSeries: (index) ->
|
||||
@options.pointStrokeColors[index % @options.pointStrokeColors.length]
|
||||
|
||||
# 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)
|
||||
"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", "day", "hour",
|
||||
"30min", "15min", "10min", "5min", "minute",
|
||||
"30sec", "15sec", "10sec", "5sec", "second"
|
||||
]
|
||||
28
wwwroot/BackendScript/assets/morris.js-0.4.3/morris.css
Normal file
28
wwwroot/BackendScript/assets/morris.js-0.4.3/morris.css
Normal file
@@ -0,0 +1,28 @@
|
||||
.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;
|
||||
}
|
||||
|
||||
|
||||
#hero-graph, #hero-bar, #hero-area, #hero-donut {height: 250px}
|
||||
1767
wwwroot/BackendScript/assets/morris.js-0.4.3/morris.js
Normal file
1767
wwwroot/BackendScript/assets/morris.js-0.4.3/morris.js
Normal file
File diff suppressed because it is too large
Load Diff
1
wwwroot/BackendScript/assets/morris.js-0.4.3/morris.min.js
vendored
Normal file
1
wwwroot/BackendScript/assets/morris.js-0.4.3/morris.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
26
wwwroot/BackendScript/assets/morris.js-0.4.3/package.json
Normal file
26
wwwroot/BackendScript/assets/morris.js-0.4.3/package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "morris.js",
|
||||
"version": "0.4.3",
|
||||
"homepage": "http://oesmith.github.com/morris.js",
|
||||
"description": "Easy, pretty charts",
|
||||
"author": {
|
||||
"name": "Olly Smith",
|
||||
"email": "olly@oesmith.co.uk"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/oesmith/morris.js.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/oesmith/morris.js/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt-coffee": "~> 0.0.6",
|
||||
"grunt-mocha": "~> 0.1.7",
|
||||
"grunt-contrib-less": "~> 0.3.2",
|
||||
"grunt": "~> 0.3.17"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "./node_modules/.bin/grunt coffee mocha"
|
||||
}
|
||||
}
|
||||
8
wwwroot/BackendScript/assets/morris.js-0.4.3/raphael-min.js
vendored
Normal file
8
wwwroot/BackendScript/assets/morris.js-0.4.3/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
|
||||
@@ -0,0 +1,50 @@
|
||||
describe 'Morris.Bar', ->
|
||||
|
||||
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 stroke width 0', ->
|
||||
chart = Morris.Bar $.extend {}, defaults
|
||||
$('#graph').find("rect[stroke-width='0']").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
|
||||
@@ -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')
|
||||
@@ -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,61 @@
|
||||
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 =
|
||||
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
|
||||
@@ -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"
|
||||
@@ -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,172 @@
|
||||
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 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()]
|
||||
])
|
||||
@@ -0,0 +1,207 @@
|
||||
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]
|
||||
pointWidths: [1, 2]
|
||||
pointFillColors: [null, red]
|
||||
chart.strokeWidthForSeries(0).should.equal 1
|
||||
chart.strokeForSeries(0).should.equal red
|
||||
chart.strokeWidthForSeries(1).should.equal 2
|
||||
chart.strokeForSeries(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
|
||||
continuousLine: false
|
||||
|
||||
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 ignore null values when options.continuousLine is true', ->
|
||||
@defaults.data[2].y = null
|
||||
Morris.Line $.extend(@defaults, continuousLine: true)
|
||||
shouldHavePath /M[\d\.]+,[\d\.]+(C[\d\.]+(,[\d\.]+){5}){3}/
|
||||
|
||||
it 'should break the line at null values when options.continuousLine is false', ->
|
||||
@defaults.data[2].y = null
|
||||
Morris.Line @defaults
|
||||
shouldHavePath /(M[\d\.]+,[\d\.]+C[\d\.]+(,[\d\.]+){5}){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
|
||||
pointWidths: [5]
|
||||
pointStrokeColors: ['#ffffff']
|
||||
gridLineColor: '#aaa'
|
||||
gridStrokeWidth: 0.5
|
||||
gridTextColor: '#888'
|
||||
gridTextSize: 12
|
||||
|
||||
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
|
||||
@@ -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())
|
||||
35
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/specs.html
Normal file
35
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/specs.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>morris.js tests</title>
|
||||
<link rel="stylesheet" href="vendor/mocha-1.6.0.css" type="text/css" media="screen" />
|
||||
<link rel="stylesheet" href="../morris.css" type="text/css" media="screen" />
|
||||
<script src="vendor/jquery-1.8.2.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/raphael-2.1.0.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="mocha"></div>
|
||||
|
||||
<script type="text/javascript" src="vendor/mocha-1.6.0.js"></script>
|
||||
<script>
|
||||
mocha.setup('bdd');
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="vendor/chai-1.3.0.js"></script>
|
||||
<script type="text/javascript" src="vendor/chai-jquery-1.1.0.js"></script>
|
||||
<script type="text/javascript" src="vendor/sinon-1.5.0.js"></script>
|
||||
<script type="text/javascript" src="vendor/sinon-chai-2.1.2.js"></script>
|
||||
<script>
|
||||
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()
|
||||
3649
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/chai-1.3.0.js
vendored
Normal file
3649
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/chai-1.3.0.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
232
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/chai-jquery-1.1.0.js
vendored
Normal file
232
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/chai-jquery-1.1.0.js
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
(function (chaiJquery) {
|
||||
// Module systems magic dance.
|
||||
if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
|
||||
// NodeJS
|
||||
module.exports = chaiJquery;
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(function () {
|
||||
return chaiJquery;
|
||||
});
|
||||
} else {
|
||||
// Other environment (usually <script> tag): plug in to global chai instance directly.
|
||||
chai.use(chaiJquery);
|
||||
}
|
||||
}(function (chai, utils) {
|
||||
var inspect = utils.inspect,
|
||||
flag = utils.flag;
|
||||
|
||||
jQuery.fn.inspect = function (depth) {
|
||||
var el = jQuery('<div />').append(this.clone());
|
||||
if (depth) {
|
||||
var children = el.children();
|
||||
while (depth-- > 0)
|
||||
children = children.children();
|
||||
children.html('...');
|
||||
}
|
||||
return el.html();
|
||||
};
|
||||
|
||||
var props = {attr: 'attribute', css: 'CSS property'};
|
||||
for (var prop in props) {
|
||||
(function (prop, description) {
|
||||
chai.Assertion.addMethod(prop, function (name, val) {
|
||||
var actual = flag(this, 'object')[prop](name);
|
||||
|
||||
if (!flag(this, 'negate') || undefined === val) {
|
||||
this.assert(
|
||||
undefined !== actual
|
||||
, 'expected #{this} to have a #{exp} ' + description
|
||||
, 'expected #{this} not to have a #{exp} ' + description
|
||||
, name
|
||||
);
|
||||
}
|
||||
|
||||
if (undefined !== val) {
|
||||
this.assert(
|
||||
val === actual
|
||||
, 'expected #{this} to have a ' + inspect(name) + ' ' + description + ' with the value #{exp}, but the value was #{act}'
|
||||
, 'expected #{this} not to have a ' + inspect(name) + ' ' + description + ' with the value #{act}'
|
||||
, val
|
||||
, actual
|
||||
);
|
||||
}
|
||||
|
||||
flag(this, 'object', actual);
|
||||
});
|
||||
})(prop, props[prop]);
|
||||
}
|
||||
|
||||
chai.Assertion.addMethod('data', function (name, val) {
|
||||
// Work around a chai bug (https://github.com/logicalparadox/chai/issues/16)
|
||||
if (flag(this, 'negate') && undefined !== val && undefined === flag(this, 'object').data(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var assertion = new chai.Assertion(flag(this, 'object').data());
|
||||
if (flag(this, 'negate'))
|
||||
assertion = assertion.not;
|
||||
return assertion.property(name, val);
|
||||
});
|
||||
|
||||
chai.Assertion.addMethod('class', function (className) {
|
||||
this.assert(
|
||||
flag(this, 'object').hasClass(className)
|
||||
, 'expected #{this} to have class #{exp}'
|
||||
, 'expected #{this} not to have class #{exp}'
|
||||
, className
|
||||
);
|
||||
});
|
||||
|
||||
chai.Assertion.addMethod('id', function (id) {
|
||||
this.assert(
|
||||
flag(this, 'object').attr('id') === id
|
||||
, 'expected #{this} to have id #{exp}'
|
||||
, 'expected #{this} not to have id #{exp}'
|
||||
, id
|
||||
);
|
||||
});
|
||||
|
||||
chai.Assertion.addMethod('html', function (html) {
|
||||
this.assert(
|
||||
flag(this, 'object').html() === html
|
||||
, 'expected #{this} to have HTML #{exp}'
|
||||
, 'expected #{this} not to have HTML #{exp}'
|
||||
, html
|
||||
);
|
||||
});
|
||||
|
||||
chai.Assertion.addMethod('text', function (text) {
|
||||
this.assert(
|
||||
flag(this, 'object').text() === text
|
||||
, 'expected #{this} to have text #{exp}'
|
||||
, 'expected #{this} not to have text #{exp}'
|
||||
, text
|
||||
);
|
||||
});
|
||||
|
||||
chai.Assertion.addMethod('value', function (value) {
|
||||
this.assert(
|
||||
flag(this, 'object').val() === value
|
||||
, 'expected #{this} to have value #{exp}'
|
||||
, 'expected #{this} not to have value #{exp}'
|
||||
, value
|
||||
);
|
||||
});
|
||||
|
||||
jQuery.each(['visible', 'hidden', 'selected', 'checked', 'disabled'], function (i, attr) {
|
||||
chai.Assertion.addProperty(attr, function () {
|
||||
this.assert(
|
||||
flag(this, 'object').is(':' + attr)
|
||||
, 'expected #{this} to be ' + attr
|
||||
, 'expected #{this} not to be ' + attr);
|
||||
});
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteProperty('exist', function (_super) {
|
||||
return function () {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
this.assert(
|
||||
obj.length > 0
|
||||
, 'expected ' + inspect(obj.selector) + ' to exist'
|
||||
, 'expected ' + inspect(obj.selector) + ' not to exist');
|
||||
} else {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteProperty('empty', function (_super) {
|
||||
return function () {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
this.assert(
|
||||
obj.is(':empty')
|
||||
, 'expected #{this} to be empty'
|
||||
, 'expected #{this} not to be empty');
|
||||
} else {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteProperty('be', function (_super) {
|
||||
return function () {
|
||||
var be = function (selector) {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
this.assert(
|
||||
obj.is(selector)
|
||||
, 'expected #{this} to be #{exp}'
|
||||
, 'expected #{this} not to be #{exp}'
|
||||
, selector
|
||||
);
|
||||
} else {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
be.__proto__ = this;
|
||||
return be;
|
||||
}
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteMethod('match', function (_super) {
|
||||
return function (selector) {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
this.assert(
|
||||
obj.is(selector)
|
||||
, 'expected #{this} to match #{exp}'
|
||||
, 'expected #{this} not to match #{exp}'
|
||||
, selector
|
||||
);
|
||||
} else {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteProperty('contain', function (_super) {
|
||||
return function () {
|
||||
_super.call(this);
|
||||
var contain = function (text) {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
this.assert(
|
||||
obj.is(':contains(\'' + text + '\')')
|
||||
, 'expected #{this} to contain #{exp}'
|
||||
, 'expected #{this} not to contain #{exp}'
|
||||
, text
|
||||
);
|
||||
} else {
|
||||
Function.prototype.apply.call(_super.call(this), this, arguments);
|
||||
}
|
||||
};
|
||||
contain.__proto__ = this;
|
||||
return contain;
|
||||
}
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteProperty('have', function (_super) {
|
||||
return function () {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
var have = function (selector) {
|
||||
this.assert(
|
||||
// Using find() rather than has() to work around a jQuery bug:
|
||||
// http://bugs.jquery.com/ticket/11706
|
||||
obj.find(selector).length > 0
|
||||
, 'expected #{this} to have #{exp}'
|
||||
, 'expected #{this} not to have #{exp}'
|
||||
, selector
|
||||
);
|
||||
};
|
||||
have.__proto__ = this;
|
||||
return have;
|
||||
} else {
|
||||
_super.call(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
2
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/jquery-1.8.2.min.js
vendored
Normal file
2
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/jquery-1.8.2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
203
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/mocha-1.6.0.css
vendored
Normal file
203
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/mocha-1.6.0.css
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
@charset "UTF-8";
|
||||
body {
|
||||
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
padding: 60px 50px;
|
||||
}
|
||||
|
||||
#mocha ul, #mocha li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#mocha ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
#mocha h1, #mocha h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#mocha h1 {
|
||||
margin-top: 15px;
|
||||
font-size: 1em;
|
||||
font-weight: 200;
|
||||
}
|
||||
|
||||
#mocha h1 a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#mocha h1 a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#mocha .suite .suite h1 {
|
||||
margin-top: 0;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#mocha .suite {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#mocha .test {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#mocha .test:hover h2::after {
|
||||
position: relative;
|
||||
top: 0;
|
||||
right: -10px;
|
||||
content: '(view source)';
|
||||
font-size: 12px;
|
||||
font-family: arial;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#mocha .test.pending:hover h2::after {
|
||||
content: '(pending)';
|
||||
font-family: arial;
|
||||
}
|
||||
|
||||
#mocha .test.pass.medium .duration {
|
||||
background: #C09853;
|
||||
}
|
||||
|
||||
#mocha .test.pass.slow .duration {
|
||||
background: #B94A48;
|
||||
}
|
||||
|
||||
#mocha .test.pass::before {
|
||||
content: '✓';
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
color: #00d6b2;
|
||||
}
|
||||
|
||||
#mocha .test.pass .duration {
|
||||
font-size: 9px;
|
||||
margin-left: 5px;
|
||||
padding: 2px 5px;
|
||||
color: white;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
-ms-border-radius: 5px;
|
||||
-o-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
#mocha .test.pass.fast .duration {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#mocha .test.pending {
|
||||
color: #0b97c4;
|
||||
}
|
||||
|
||||
#mocha .test.pending::before {
|
||||
content: '◦';
|
||||
color: #0b97c4;
|
||||
}
|
||||
|
||||
#mocha .test.fail {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
#mocha .test.fail pre {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#mocha .test.fail::before {
|
||||
content: '✖';
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
#mocha .test pre.error {
|
||||
color: #c00;
|
||||
}
|
||||
|
||||
#mocha .test pre {
|
||||
display: inline-block;
|
||||
font: 12px/1.5 monaco, monospace;
|
||||
margin: 5px;
|
||||
padding: 15px;
|
||||
border: 1px solid #eee;
|
||||
border-bottom-color: #ddd;
|
||||
-webkit-border-radius: 3px;
|
||||
-webkit-box-shadow: 0 1px 3px #eee;
|
||||
}
|
||||
|
||||
#report.pass .test.fail {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#report.fail .test.pass {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#error {
|
||||
color: #c00;
|
||||
font-size: 1.5 em;
|
||||
font-weight: 100;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
#stats {
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
right: 10px;
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
#stats .progress {
|
||||
float: right;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#stats em {
|
||||
color: black;
|
||||
}
|
||||
|
||||
#stats a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
#stats a:hover {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#stats li {
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
list-style: none;
|
||||
padding-top: 11px;
|
||||
}
|
||||
|
||||
code .comment { color: #ddd }
|
||||
code .init { color: #2F6FAD }
|
||||
code .string { color: #5890AD }
|
||||
code .keyword { color: #8A6343 }
|
||||
code .number { color: #2F6FAD }
|
||||
4906
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/mocha-1.6.0.js
vendored
Normal file
4906
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/mocha-1.6.0.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/raphael-2.1.0.min.js
vendored
Normal file
10
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/raphael-2.1.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4142
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/sinon-1.5.0.js
vendored
Normal file
4142
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/sinon-1.5.0.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
95
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/sinon-chai-2.1.2.js
vendored
Normal file
95
wwwroot/BackendScript/assets/morris.js-0.4.3/spec/vendor/sinon-chai-2.1.2.js
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
(function (sinonChai) {
|
||||
"use strict";
|
||||
|
||||
// Module systems magic dance.
|
||||
|
||||
if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
|
||||
// NodeJS
|
||||
module.exports = sinonChai;
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(function () {
|
||||
return sinonChai;
|
||||
});
|
||||
} else {
|
||||
// Other environment (usually <script> tag): plug in to global chai instance directly.
|
||||
chai.use(sinonChai);
|
||||
}
|
||||
}(function sinonChai(chai, utils) {
|
||||
"use strict";
|
||||
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
function isSpy(putativeSpy) {
|
||||
return typeof putativeSpy === "function" &&
|
||||
typeof putativeSpy.getCall === "function" &&
|
||||
typeof putativeSpy.calledWithExactly === "function";
|
||||
}
|
||||
|
||||
function isCall(putativeCall) {
|
||||
return putativeCall && isSpy(putativeCall.proxy);
|
||||
}
|
||||
|
||||
function assertCanWorkWith(assertion) {
|
||||
if (!isSpy(assertion._obj) && !isCall(assertion._obj)) {
|
||||
throw new TypeError(utils.inspect(assertion._obj) + " is not a spy or a call to a spy!");
|
||||
}
|
||||
}
|
||||
|
||||
function getMessages(spy, action, nonNegatedSuffix, always, args) {
|
||||
var verbPhrase = always ? "always have " : "have ";
|
||||
nonNegatedSuffix = nonNegatedSuffix || "";
|
||||
spy = spy.proxy || spy;
|
||||
|
||||
function printfArray(array) {
|
||||
return spy.printf.apply(spy, array);
|
||||
}
|
||||
|
||||
return {
|
||||
affirmative: printfArray(["expected %n to " + verbPhrase + action + nonNegatedSuffix].concat(args)),
|
||||
negative: printfArray(["expected %n to not " + verbPhrase + action].concat(args))
|
||||
};
|
||||
}
|
||||
|
||||
function sinonProperty(name, action, nonNegatedSuffix) {
|
||||
utils.addProperty(chai.Assertion.prototype, name, function () {
|
||||
assertCanWorkWith(this);
|
||||
|
||||
var messages = getMessages(this._obj, action, nonNegatedSuffix, false);
|
||||
this.assert(this._obj[name], messages.affirmative, messages.negative);
|
||||
});
|
||||
}
|
||||
|
||||
function exceptionalSinonMethod(chaiName, sinonName, action, nonNegatedSuffix) {
|
||||
utils.addMethod(chai.Assertion.prototype, chaiName, function () {
|
||||
assertCanWorkWith(this);
|
||||
|
||||
var alwaysSinonMethod = "always" + sinonName[0].toUpperCase() + sinonName.substring(1);
|
||||
var shouldBeAlways = utils.flag(this, "always") && typeof this._obj[alwaysSinonMethod] === "function";
|
||||
var sinonMethod = shouldBeAlways ? alwaysSinonMethod : sinonName;
|
||||
|
||||
var messages = getMessages(this._obj, action, nonNegatedSuffix, shouldBeAlways, slice.call(arguments));
|
||||
this.assert(this._obj[sinonMethod].apply(this._obj, arguments), messages.affirmative, messages.negative);
|
||||
});
|
||||
}
|
||||
|
||||
function sinonMethod(name, action, nonNegatedSuffix) {
|
||||
exceptionalSinonMethod(name, name, action, nonNegatedSuffix);
|
||||
}
|
||||
|
||||
utils.addProperty(chai.Assertion.prototype, "always", function () {
|
||||
utils.flag(this, "always", true);
|
||||
});
|
||||
|
||||
sinonProperty("called", "been called", " at least once, but it was never called");
|
||||
sinonProperty("calledOnce", "been called exactly once", ", but it was called %c%C");
|
||||
sinonProperty("calledTwice", "been called exactly twice", ", but it was called %c%C");
|
||||
sinonProperty("calledThrice", "been called exactly thrice", ", but it was called %c%C");
|
||||
sinonMethod("calledBefore", "been called before %1");
|
||||
sinonMethod("calledAfter", "been called after %1");
|
||||
sinonMethod("calledOn", "been called with %1 as this", ", but it was called with %t instead");
|
||||
sinonMethod("calledWith", "been called with arguments %*", "%C");
|
||||
sinonMethod("calledWithExactly", "been called with exact arguments %*", "%C");
|
||||
sinonMethod("returned", "returned %1");
|
||||
exceptionalSinonMethod("thrown", "threw", "thrown %1");
|
||||
}));
|
||||
Reference in New Issue
Block a user