mirror of
https://github.com/morrisjs/morris.js.git
synced 2024-11-10 21:36:34 +01:00
Support for negative numbers.
This commit is contained in:
parent
c4e48a275c
commit
4fb43d5d37
34
examples/negative.html
Normal file
34
examples/negative.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!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>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']
|
||||
});
|
||||
</pre>
|
||||
</body>
|
@ -37,6 +37,7 @@ class Morris.Line
|
||||
'#9440ed'
|
||||
]
|
||||
ymax: 'auto'
|
||||
ymin: 'auto 0'
|
||||
marginTop: 25
|
||||
marginRight: 25
|
||||
marginBottom: 30
|
||||
@ -87,12 +88,18 @@ class Morris.Line
|
||||
|
||||
# Compute the vertical range of the graph if desired
|
||||
if typeof @options.ymax is 'string' and @options.ymax[0..3] is 'auto'
|
||||
# use Array.concat to flatten arrays and find the max y value
|
||||
ymax = Math.max.apply null, Array.prototype.concat.apply([], @series)
|
||||
if @options.ymax.length > 5
|
||||
@options.ymax = Math.max parseInt(@options.ymax[5..], 10), ymax
|
||||
else
|
||||
@options.ymax = ymax
|
||||
# use Array.concat to flatten arrays and find the max y value
|
||||
ymax = Math.max.apply null, Array.prototype.concat.apply([], @series)
|
||||
if @options.ymax.length > 5
|
||||
@options.ymax = Math.max parseInt(@options.ymax[5..], 10), ymax
|
||||
else
|
||||
@options.ymax = ymax
|
||||
if typeof @options.ymin is 'string' and @options.ymin[0..3] is 'auto'
|
||||
ymin = Math.min.apply null, Array.prototype.concat.apply([], @series)
|
||||
if @options.ymin.length > 5
|
||||
@options.ymin = Math.min parseInt(@options.ymin[5..], 10), ymin
|
||||
else
|
||||
@options.ymin = ymin
|
||||
|
||||
# Clear and redraw the graph
|
||||
#
|
||||
@ -104,11 +111,14 @@ class Morris.Line
|
||||
@r = new Raphael(@el[0])
|
||||
|
||||
# calculate grid dimensions
|
||||
left = @measureText(@options.ymax, @options.gridTextSize).width + @options.marginLeft
|
||||
maxYLabelWidth = Math.max(
|
||||
@measureText(@options.ymin, @options.gridTextSize).width,
|
||||
@measureText(@options.ymax, @options.gridTextSize).width)
|
||||
left = maxYLabelWidth + @options.marginLeft
|
||||
width = @el.width() - left - @options.marginRight
|
||||
height = @el.height() - @options.marginTop - @options.marginBottom
|
||||
dx = width / (@xmax - @xmin)
|
||||
dy = height / @options.ymax
|
||||
dy = height / (@options.ymax - @options.ymin)
|
||||
|
||||
# quick translation helpers
|
||||
transX = (x) =>
|
||||
@ -117,13 +127,15 @@ class Morris.Line
|
||||
else
|
||||
left + (x - @xmin) * dx
|
||||
transY = (y) =>
|
||||
return @options.marginTop + height - y * dy
|
||||
return @options.marginTop + height - (y - @options.ymin) * dy
|
||||
|
||||
# draw y axis labels, horizontal lines
|
||||
lineInterval = height / (@options.numLines - 1)
|
||||
for i in [0..@options.numLines-1]
|
||||
y = @options.marginTop + i * lineInterval
|
||||
v = Math.round((@options.numLines - 1 - i) * @options.ymax / (@options.numLines - 1))
|
||||
yInterval = (@options.ymax - @options.ymin) / (@options.numLines - 1)
|
||||
firstY = Math.ceil(@options.ymin / yInterval) * yInterval
|
||||
lastY = Math.floor(@options.ymax / yInterval) * yInterval
|
||||
for lineY in [firstY..lastY] by yInterval
|
||||
v = Math.floor(lineY)
|
||||
y = transY(v)
|
||||
@r.text(left - @options.marginLeft/2, y, v)
|
||||
.attr('font-size', @options.gridTextSize)
|
||||
.attr('fill', @options.gridTextColor)
|
||||
@ -334,7 +346,7 @@ class Morris.Line
|
||||
# eg: commas(1234567) -> '1,234,567'
|
||||
#
|
||||
commas: (num) ->
|
||||
Math.max(0, num).toFixed(0).replace(/(?=(?:\d{3})+$)(?!^)/g, ',')
|
||||
num.toFixed(0).replace(/(?=(?:\d{3})+$)(?!^)/g, ',')
|
||||
|
||||
window.Morris = Morris
|
||||
# vim: set et ts=2 sw=2 sts=2
|
||||
|
82
morris.js
82
morris.js
@ -26,6 +26,7 @@
|
||||
pointSize: 4,
|
||||
lineColors: ['#0b62a4', '#7A92A3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'],
|
||||
ymax: 'auto',
|
||||
ymin: 'auto 0',
|
||||
marginTop: 25,
|
||||
marginRight: 25,
|
||||
marginBottom: 30,
|
||||
@ -50,7 +51,7 @@
|
||||
};
|
||||
|
||||
Line.prototype.precalc = function() {
|
||||
var ykey, ymax, _i, _j, _len, _ref, _ref2, _results,
|
||||
var ykey, ymax, ymin, _i, _j, _len, _ref, _ref2, _results,
|
||||
_this = this;
|
||||
this.options.data.sort(function(a, b) {
|
||||
return (a[_this.options.xkey] < b[_this.options.xkey]) - (b[_this.options.xkey] < a[_this.options.xkey]);
|
||||
@ -87,23 +88,32 @@
|
||||
if (typeof this.options.ymax === 'string' && this.options.ymax.slice(0, 4) === 'auto') {
|
||||
ymax = Math.max.apply(null, Array.prototype.concat.apply([], this.series));
|
||||
if (this.options.ymax.length > 5) {
|
||||
return this.options.ymax = Math.max(parseInt(this.options.ymax.slice(5), 10), ymax);
|
||||
this.options.ymax = Math.max(parseInt(this.options.ymax.slice(5), 10), ymax);
|
||||
} else {
|
||||
return this.options.ymax = ymax;
|
||||
this.options.ymax = ymax;
|
||||
}
|
||||
}
|
||||
if (typeof this.options.ymin === 'string' && this.options.ymin.slice(0, 4) === 'auto') {
|
||||
ymin = Math.min.apply(null, Array.prototype.concat.apply([], this.series));
|
||||
if (this.options.ymin.length > 5) {
|
||||
return this.options.ymin = Math.min(parseInt(this.options.ymin.slice(5), 10), ymin);
|
||||
} else {
|
||||
return this.options.ymin = ymin;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Line.prototype.redraw = function() {
|
||||
var c, circle, columns, coords, dx, dy, height, hideHover, hilight, hover, hoverHeight, hoverMargins, hoverSet, i, label, labelBox, labelText, left, lineInterval, path, pointGrow, pointShrink, prevHilight, prevLabelMargin, s, seriesCoords, seriesPoints, touchHandler, transX, transY, updateHilight, updateHover, v, width, x, xLabel, xLabelMargin, y, yLabel, yLabels, _i, _j, _len, _len2, _ref, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8,
|
||||
var c, circle, columns, coords, dx, dy, firstY, height, hideHover, hilight, hover, hoverHeight, hoverMargins, hoverSet, i, label, labelBox, labelText, lastY, left, lineY, maxYLabelWidth, path, pointGrow, pointShrink, prevHilight, prevLabelMargin, s, seriesCoords, seriesPoints, touchHandler, transX, transY, updateHilight, updateHover, v, width, x, xLabel, xLabelMargin, y, yInterval, yLabel, yLabels, _i, _j, _len, _len2, _ref, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7,
|
||||
_this = this;
|
||||
this.el.empty();
|
||||
this.r = new Raphael(this.el[0]);
|
||||
left = this.measureText(this.options.ymax, this.options.gridTextSize).width + this.options.marginLeft;
|
||||
maxYLabelWidth = Math.max(this.measureText(this.options.ymin, this.options.gridTextSize).width, this.measureText(this.options.ymax, this.options.gridTextSize).width);
|
||||
left = maxYLabelWidth + this.options.marginLeft;
|
||||
width = this.el.width() - left - this.options.marginRight;
|
||||
height = this.el.height() - this.options.marginTop - this.options.marginBottom;
|
||||
dx = width / (this.xmax - this.xmin);
|
||||
dy = height / this.options.ymax;
|
||||
dy = height / (this.options.ymax - this.options.ymin);
|
||||
transX = function(x) {
|
||||
if (_this.xvals.length === 1) {
|
||||
return left + width / 2;
|
||||
@ -112,18 +122,20 @@
|
||||
}
|
||||
};
|
||||
transY = function(y) {
|
||||
return _this.options.marginTop + height - y * dy;
|
||||
return _this.options.marginTop + height - (y - _this.options.ymin) * dy;
|
||||
};
|
||||
lineInterval = height / (this.options.numLines - 1);
|
||||
for (i = 0, _ref = this.options.numLines - 1; 0 <= _ref ? i <= _ref : i >= _ref; 0 <= _ref ? i++ : i--) {
|
||||
y = this.options.marginTop + i * lineInterval;
|
||||
v = Math.round((this.options.numLines - 1 - i) * this.options.ymax / (this.options.numLines - 1));
|
||||
yInterval = (this.options.ymax - this.options.ymin) / (this.options.numLines - 1);
|
||||
firstY = Math.ceil(this.options.ymin / yInterval) * yInterval;
|
||||
lastY = Math.floor(this.options.ymax / yInterval) * yInterval;
|
||||
for (lineY = firstY; firstY <= lastY ? lineY <= lastY : lineY >= lastY; lineY += yInterval) {
|
||||
v = Math.floor(lineY);
|
||||
y = transY(v);
|
||||
this.r.text(left - this.options.marginLeft / 2, y, v).attr('font-size', this.options.gridTextSize).attr('fill', this.options.gridTextColor).attr('text-anchor', 'end');
|
||||
this.r.path("M" + left + "," + y + 'H' + (left + width)).attr('stroke', this.options.gridLineColor).attr('stroke-width', this.options.gridStrokeWidth);
|
||||
}
|
||||
prevLabelMargin = null;
|
||||
xLabelMargin = 50;
|
||||
for (i = _ref2 = Math.ceil(this.xmin), _ref3 = Math.floor(this.xmax); _ref2 <= _ref3 ? i <= _ref3 : i >= _ref3; _ref2 <= _ref3 ? i++ : i--) {
|
||||
for (i = _ref = Math.ceil(this.xmin), _ref2 = Math.floor(this.xmax); _ref <= _ref2 ? i <= _ref2 : i >= _ref2; _ref <= _ref2 ? i++ : i--) {
|
||||
labelText = this.options.parseTime ? i : this.columnLabels[this.columnLabels.length - i - 1];
|
||||
label = this.r.text(transX(i), this.options.marginTop + height + this.options.marginBottom / 2, labelText).attr('font-size', this.options.gridTextSize).attr('fill', this.options.gridTextColor);
|
||||
labelBox = label.getBBox();
|
||||
@ -134,19 +146,19 @@
|
||||
}
|
||||
}
|
||||
columns = (function() {
|
||||
var _i, _len, _ref4, _results;
|
||||
_ref4 = this.xvals;
|
||||
var _i, _len, _ref3, _results;
|
||||
_ref3 = this.xvals;
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
|
||||
x = _ref4[_i];
|
||||
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
|
||||
x = _ref3[_i];
|
||||
_results.push(transX(x));
|
||||
}
|
||||
return _results;
|
||||
}).call(this);
|
||||
seriesCoords = [];
|
||||
_ref4 = this.series;
|
||||
for (_i = 0, _len = _ref4.length; _i < _len; _i++) {
|
||||
s = _ref4[_i];
|
||||
_ref3 = this.series;
|
||||
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
|
||||
s = _ref3[_i];
|
||||
seriesCoords.push($.map(s, function(y, i) {
|
||||
return {
|
||||
x: columns[i],
|
||||
@ -154,7 +166,7 @@
|
||||
};
|
||||
}));
|
||||
}
|
||||
for (i = _ref5 = seriesCoords.length - 1; _ref5 <= 0 ? i <= 0 : i >= 0; _ref5 <= 0 ? i++ : i--) {
|
||||
for (i = _ref4 = seriesCoords.length - 1; _ref4 <= 0 ? i <= 0 : i >= 0; _ref4 <= 0 ? i++ : i--) {
|
||||
coords = seriesCoords[i];
|
||||
if (coords.length > 1) {
|
||||
path = this.createPath(coords, this.options.marginTop, left, this.options.marginTop + height, left + width);
|
||||
@ -162,17 +174,17 @@
|
||||
}
|
||||
}
|
||||
seriesPoints = (function() {
|
||||
var _ref6, _results;
|
||||
var _ref5, _results;
|
||||
_results = [];
|
||||
for (i = 0, _ref6 = seriesCoords.length - 1; 0 <= _ref6 ? i <= _ref6 : i >= _ref6; 0 <= _ref6 ? i++ : i--) {
|
||||
for (i = 0, _ref5 = seriesCoords.length - 1; 0 <= _ref5 ? i <= _ref5 : i >= _ref5; 0 <= _ref5 ? i++ : i--) {
|
||||
_results.push([]);
|
||||
}
|
||||
return _results;
|
||||
})();
|
||||
for (i = _ref6 = seriesCoords.length - 1; _ref6 <= 0 ? i <= 0 : i >= 0; _ref6 <= 0 ? i++ : i--) {
|
||||
_ref7 = seriesCoords[i];
|
||||
for (_j = 0, _len2 = _ref7.length; _j < _len2; _j++) {
|
||||
c = _ref7[_j];
|
||||
for (i = _ref5 = seriesCoords.length - 1; _ref5 <= 0 ? i <= 0 : i >= 0; _ref5 <= 0 ? i++ : i--) {
|
||||
_ref6 = seriesCoords[i];
|
||||
for (_j = 0, _len2 = _ref6.length; _j < _len2; _j++) {
|
||||
c = _ref6[_j];
|
||||
circle = this.r.circle(c.x, c.y, this.options.pointSize).attr('fill', this.options.lineColors[i]).attr('stroke-width', 1).attr('stroke', '#ffffff');
|
||||
seriesPoints[i].push(circle);
|
||||
}
|
||||
@ -184,16 +196,16 @@
|
||||
hoverSet.push(hover);
|
||||
hoverSet.push(xLabel);
|
||||
yLabels = [];
|
||||
for (i = 0, _ref8 = this.series.length - 1; 0 <= _ref8 ? i <= _ref8 : i >= _ref8; 0 <= _ref8 ? i++ : i--) {
|
||||
for (i = 0, _ref7 = this.series.length - 1; 0 <= _ref7 ? i <= _ref7 : i >= _ref7; 0 <= _ref7 ? i++ : i--) {
|
||||
yLabel = this.r.text(0, this.options.hoverFontSize * 1.5 * (i + 1.5) - hoverHeight / 2, '').attr('fill', this.options.lineColors[i]).attr('font-size', this.options.hoverFontSize);
|
||||
yLabels.push(yLabel);
|
||||
hoverSet.push(yLabel);
|
||||
}
|
||||
updateHover = function(index) {
|
||||
var i, maxLabelWidth, xloc, yloc, _ref9;
|
||||
var i, maxLabelWidth, xloc, yloc, _ref8;
|
||||
hoverSet.show();
|
||||
xLabel.attr('text', _this.columnLabels[index]);
|
||||
for (i = 0, _ref9 = _this.series.length - 1; 0 <= _ref9 ? i <= _ref9 : i >= _ref9; 0 <= _ref9 ? i++ : i--) {
|
||||
for (i = 0, _ref8 = _this.series.length - 1; 0 <= _ref8 ? i <= _ref8 : i >= _ref8; 0 <= _ref8 ? i++ : i--) {
|
||||
yLabels[i].attr('text', "" + _this.seriesLabels[i] + ": " + (_this.commas(_this.series[i][index])));
|
||||
}
|
||||
maxLabelWidth = Math.max.apply(null, $.map(yLabels, function(l) {
|
||||
@ -230,14 +242,14 @@
|
||||
r: this.options.pointSize
|
||||
}, 25, 'linear');
|
||||
hilight = function(index) {
|
||||
var i, _ref10, _ref9;
|
||||
var i, _ref8, _ref9;
|
||||
if (prevHilight !== null && prevHilight !== index) {
|
||||
for (i = 0, _ref9 = seriesPoints.length - 1; 0 <= _ref9 ? i <= _ref9 : i >= _ref9; 0 <= _ref9 ? i++ : i--) {
|
||||
for (i = 0, _ref8 = seriesPoints.length - 1; 0 <= _ref8 ? i <= _ref8 : i >= _ref8; 0 <= _ref8 ? i++ : i--) {
|
||||
seriesPoints[i][prevHilight].animate(pointShrink);
|
||||
}
|
||||
}
|
||||
if (index !== null && prevHilight !== index) {
|
||||
for (i = 0, _ref10 = seriesPoints.length - 1; 0 <= _ref10 ? i <= _ref10 : i >= _ref10; 0 <= _ref10 ? i++ : i--) {
|
||||
for (i = 0, _ref9 = seriesPoints.length - 1; 0 <= _ref9 ? i <= _ref9 : i >= _ref9; 0 <= _ref9 ? i++ : i--) {
|
||||
seriesPoints[i][index].animate(pointGrow);
|
||||
}
|
||||
updateHover(index);
|
||||
@ -246,10 +258,10 @@
|
||||
if (index === null) return hideHover();
|
||||
};
|
||||
updateHilight = function(x) {
|
||||
var hoverIndex, _ref9, _results;
|
||||
var hoverIndex, _ref8, _results;
|
||||
x -= _this.el.offset().left;
|
||||
_results = [];
|
||||
for (hoverIndex = _ref9 = hoverMargins.length; _ref9 <= 0 ? hoverIndex <= 0 : hoverIndex >= 0; _ref9 <= 0 ? hoverIndex++ : hoverIndex--) {
|
||||
for (hoverIndex = _ref8 = hoverMargins.length; _ref8 <= 0 ? hoverIndex <= 0 : hoverIndex >= 0; _ref8 <= 0 ? hoverIndex++ : hoverIndex--) {
|
||||
if (hoverIndex === 0 || hoverMargins[hoverIndex - 1] > x) {
|
||||
hilight(hoverIndex);
|
||||
break;
|
||||
@ -362,7 +374,7 @@
|
||||
};
|
||||
|
||||
Line.prototype.commas = function(num) {
|
||||
return Math.max(0, num).toFixed(0).replace(/(?=(?:\d{3})+$)(?!^)/g, ',');
|
||||
return num.toFixed(0).replace(/(?=(?:\d{3})+$)(?!^)/g, ',');
|
||||
};
|
||||
|
||||
return Line;
|
||||
|
2
morris.min.js
vendored
2
morris.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user