Back-to-basics bar chart.

This commit is contained in:
Olly Smith 2012-10-31 21:58:26 +00:00
parent 5ee5ffe824
commit e4f4594fab
4 changed files with 114 additions and 443 deletions

View File

@ -17,13 +17,13 @@ Morris.Bar({
element: 'graph', element: 'graph',
data: [ data: [
{x: '2011 Q1', y: 3, z: 2, a: 3}, {x: '2011 Q1', y: 3, z: 2, a: 3},
{x: '2011 Q2', y: 2, z: 1, a: 1}, {x: '2011 Q2', y: 2, z: null, a: 1},
{x: '2011 Q3', y: 0, z: 2, a: 4}, {x: '2011 Q3', y: 0, z: 2, a: 4},
{x: '2011 Q4', y: 4, z: 4, a: 3} {x: '2011 Q4', y: 2, z: 4, a: 3}
], ],
xkey: 'x', xkey: 'x',
ykeys: ['y', 'z', 'a'], ykeys: ['y', 'z', 'a'],
labels: ['Y', 'Z', 'A'] labels: ['Y', 'Z', 'A']
}); });
</pre> </pre>
</body> </body>

View File

@ -3,13 +3,11 @@ class Morris.Bar extends Morris.Grid
# #
constructor: (options) -> constructor: (options) ->
return new Morris.Bar(options) unless (@ instanceof Morris.Bar) return new Morris.Bar(options) unless (@ instanceof Morris.Bar)
super(options) super($.extend {}, options, parseTime: false)
# setup event handlers
#
init: -> init: ->
# Some instance variables for later
@barFace = Raphael.animation opacity: @options.barHoverOpacity, 25, 'linear'
@barDeface = Raphael.animation opacity: 1.0, 25, 'linear'
# data hilight events
@prevHilight = null @prevHilight = null
@el.mousemove (evt) => @el.mousemove (evt) =>
@updateHilight evt.pageX @updateHilight evt.pageX
@ -23,15 +21,13 @@ class Morris.Bar extends Morris.Grid
@el.bind 'touchstart', touchHandler @el.bind 'touchstart', touchHandler
@el.bind 'touchmove', touchHandler @el.bind 'touchmove', touchHandler
@el.bind 'touchend', touchHandler @el.bind 'touchend', touchHandler
# Default configuration # Default configuration
# #
defaults: defaults:
barSizeRatio: 0.5 barSizeRatio: 0.75
barGap: 3 barGap: 3
barStrokeWidths: [0] barColors: [
barStrokeColors: ['#ffffff']
barFillColors: [
'#0b62a4' '#0b62a4'
'#7a92a3' '#7a92a3'
'#4da74d' '#4da74d'
@ -40,7 +36,6 @@ class Morris.Bar extends Morris.Grid
'#cb4b4b' '#cb4b4b'
'#9440ed' '#9440ed'
] ]
barHoverOpacity: 0.95
hoverPaddingX: 10 hoverPaddingX: 10
hoverPaddingY: 5 hoverPaddingY: 5
hoverMargin: 10 hoverMargin: 10
@ -51,65 +46,30 @@ class Morris.Bar extends Morris.Grid
hoverLabelColor: '#444' hoverLabelColor: '#444'
hoverFontSize: 12 hoverFontSize: 12
hideHover: false hideHover: false
xLabels: 'auto'
xLabelFormat: null
# Override padding
#
# @private
overridePadding: ->
maxYLabelWidth = Math.max(
@measureText(@yAxisFormat(@ymin), @options.gridTextSize).width,
@measureText(@yAxisFormat(@ymax), @options.gridTextSize).width)
@left = maxYLabelWidth + @paddingLeft
@right = @elementWidth - @paddingRight
@width = @right - @left
xgap = @width / @data.length
@barsoffset = @options.barSizeRatio * xgap / 2.0;
@barwidth = (@options.barSizeRatio * xgap - ( @options.ykeys.length - 1 ) * @options.barGap ) / @options.ykeys.length
@halfBarsWidth = Math.round( @options.ykeys.length / 2 ) * ( @barwidth + @options.barGap )
@paddingLeft += @halfBarsWidth
@paddingRight += @halfBarsWidth
# Do any size-related calculations # Do any size-related calculations
# #
# @private # @private
calc: -> calc: ->
@calcBars() @calcBars()
@generateBars()
@calcHoverMargins() @calcHoverMargins()
# calculate series data bars coordinates and sizes # calculate series data bars coordinates and sizes
# #
# @private # @private
calcBars: -> calcBars: ->
for row in @data for row, idx in @data
row._x = @transX(row.x) row._x = @left + @width * (idx + 0.5) / @data.length
row._y = for y in row.y row._y = for y in row.y
if y is null if y is null then null else @transY(y)
null
else
@transY(y)
# calculate hover margins # calculate hover margins
# #
# @private # @private
calcHoverMargins: -> calcHoverMargins: ->
@hoverMargins = $.map @data.slice(1), (r, i) => (r._x + @data[i]._x) / 2 @hoverMargins = for i in [1...@data.length]
@left + i * @width / @data.length
# generate bars for series
#
# @private
generateBars: ->
@bars = for i in [0..@options.ykeys.length]
coords = ({x: r._x - @barsoffset + i * (@options.barGap + @barwidth) , y: r._y[i], v: r.y[i] } for r in @data when r._y[i] isnt null)
if coords.length > 1
@createBars i, coords, @barwidth
else
null
# Draws the bar chart. # Draws the bar chart.
# #
draw: -> draw: ->
@ -117,7 +77,7 @@ class Morris.Bar extends Morris.Grid
@drawSeries() @drawSeries()
@drawHover() @drawHover()
@hilight(if @options.hideHover then null else @data.length - 1) @hilight(if @options.hideHover then null else @data.length - 1)
# draw the x-axis labels # draw the x-axis labels
# #
# @private # @private
@ -126,8 +86,9 @@ class Morris.Bar extends Morris.Grid
ypos = @bottom + @options.gridTextSize * 1.25 ypos = @bottom + @options.gridTextSize * 1.25
xLabelMargin = 50 # make this an option? xLabelMargin = 50 # make this an option?
prevLabelMargin = null prevLabelMargin = null
drawLabel = (labelText, xpos) => for i in [0...@data.length]
label = @r.text(@transX(xpos), ypos, labelText) row = @data[@data.length - 1 - i]
label = @r.text(row._x, ypos, row.label)
.attr('font-size', @options.gridTextSize) .attr('font-size', @options.gridTextSize)
.attr('fill', @options.gridTextColor) .attr('fill', @options.gridTextColor)
labelBox = label.getBBox() labelBox = label.getBBox()
@ -138,54 +99,32 @@ class Morris.Bar extends Morris.Grid
prevLabelMargin = labelBox.x - xLabelMargin prevLabelMargin = labelBox.x - xLabelMargin
else else
label.remove() 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 # draw the data series
# #
# @private # @private
drawSeries: -> drawSeries: ->
@seriesBars = ([] for i in [0...@options.ykeys.length]) groupWidth = @width / @options.data.length
for i in [@options.ykeys.length-1..0] numBars = @options.ykeys.length
bars = @bars[i] barWidth = (groupWidth * @options.barSizeRatio - @options.barGap * (numBars - 1)) / numBars
if bars.length > 0 leftPadding = groupWidth * (1 - @options.barSizeRatio) / 2
for bar in bars zeroPos = if @ymin <= 0 and @ymax >= 0 then @transY(0) else null
if bar isnt null @bars = for row, idx in @data
rect = @r.rect(bar.x, bar.y, bar.width, bar.height) for ypos, sidx in row._y
.attr('fill', bar.fill) if ypos != null
.attr('stroke', @strokeForSeries(i)) if zeroPos
.attr('stroke-width', @strokeWidthForSeries(i)) top = Math.min(ypos, zeroPos)
bottom = Math.max(ypos, zeroPos)
else else
rect = null top = ypos
@seriesBars[i].push(rect) bottom = @bottom
left = @left + idx * groupWidth + leftPadding + sidx * (barWidth + @options.barGap)
# create bars for a data series @r.rect(left, top, barWidth, bottom - top)
# .attr('fill', @options.barColors[sidx % @options.barColors.length])
# @private .attr('stroke-width', 0)
createBars: (index, coords, barwidth) -> else
bars = [] null
for coord in coords
bars.push(
x: coord.x
y: coord.y
width: barwidth
height: @bottom - coord.y
fill: @colorForSeriesAndValue(index, coord.v)
)
return bars
# draw the hover tooltip # draw the hover tooltip
# #
# @private # @private
@ -206,8 +145,7 @@ class Morris.Bar extends Morris.Grid
@hoverSet.push(@xLabel) @hoverSet.push(@xLabel)
@yLabels = [] @yLabels = []
for i in [0...@options.ykeys.length] for i in [0...@options.ykeys.length]
idx = if @cumulative then (@options.ykeys.length - i - 1) else i yLabel = @r.text(0, @options.hoverFontSize * 1.5 * (i + 1.5) - @hoverHeight / 2, '')
yLabel = @r.text(0, @options.hoverFontSize * 1.5 * (idx + 1.5) - @hoverHeight / 2, '')
.attr('font-size', @options.hoverFontSize) .attr('font-size', @options.hoverFontSize)
@yLabels.push(yLabel) @yLabels.push(yLabel)
@hoverSet.push(yLabel) @hoverSet.push(yLabel)
@ -218,7 +156,7 @@ class Morris.Bar extends Morris.Grid
row = @data[index] row = @data[index]
@xLabel.attr('text', row.label) @xLabel.attr('text', row.label)
for y, i in row.y for y, i in row.y
@yLabels[i].attr('fill', @hoverColorForSeriesAndValue(i, y)) @yLabels[i].attr('fill', @options.barColors[i % @options.barColors.length])
@yLabels[i].attr('text', "#{@options.labels[i]}: #{@yLabelFormat(y)}") @yLabels[i].attr('text', "#{@options.labels[i]}: #{@yLabelFormat(y)}")
# recalculate hover box width # recalculate hover box width
maxLabelWidth = Math.max.apply null, $.map @yLabels, (l) -> maxLabelWidth = Math.max.apply null, $.map @yLabels, (l) ->
@ -227,89 +165,26 @@ class Morris.Bar extends Morris.Grid
@hover.attr 'width', maxLabelWidth + @options.hoverPaddingX * 2 @hover.attr 'width', maxLabelWidth + @options.hoverPaddingX * 2
@hover.attr 'x', -@options.hoverPaddingX - maxLabelWidth / 2 @hover.attr 'x', -@options.hoverPaddingX - maxLabelWidth / 2
# move to y pos # move to y pos
yloc = Math.min.apply null, (y for y in row._y when y isnt null).concat(@bottom) yloc = (@bottom + @top) / 2
if yloc > @hoverHeight + @options.hoverPaddingY * 2 + @options.hoverMargin + @top
yloc = yloc - @hoverHeight / 2 - @options.hoverPaddingY - @options.hoverMargin
else
yloc = yloc + @hoverHeight / 2 + @options.hoverPaddingY + @options.hoverMargin
yloc = Math.max @top + @hoverHeight / 2 + @options.hoverPaddingY, yloc
yloc = Math.min @bottom - @hoverHeight / 2 - @options.hoverPaddingY, yloc
xloc = Math.min @right - maxLabelWidth / 2 - @options.hoverPaddingX, @data[index]._x xloc = Math.min @right - maxLabelWidth / 2 - @options.hoverPaddingX, @data[index]._x
xloc = Math.max @left + maxLabelWidth / 2 + @options.hoverPaddingX, xloc xloc = Math.max @left + maxLabelWidth / 2 + @options.hoverPaddingX, xloc
@hoverSet.attr 'transform', "t#{xloc},#{yloc}" @hoverSet.attr 'transform', "t#{xloc},#{yloc}"
# @private # @private
hideHover: -> hideHover: ->
@hoverSet.hide() @hoverSet.hide()
# @private # @private
hilight: (index) => hilight: (index) =>
if @prevHilight isnt null and @prevHilight isnt index
for i in [0..@seriesBars.length-1]
if @seriesBars[i][@prevHilight]
@seriesBars[i][@prevHilight].animate @barDeface
if index isnt null and @prevHilight isnt index if index isnt null and @prevHilight isnt index
for i in [0..@seriesBars.length-1]
if @seriesBars[i][index]
@seriesBars[i][index].animate @barFace
@updateHover index @updateHover index
@prevHilight = index @prevHilight = index
if index is null if index is null
@hideHover() @hideHover()
# @private # @private
updateHilight: (x) => updateHilight: (x) =>
x -= @el.offset().left x -= @el.offset().left
for hoverIndex in [0...@hoverMargins.length] for hoverIndex in [0...@hoverMargins.length]
break if @hoverMargins[hoverIndex] > x break if @hoverMargins[hoverIndex] > x
@hilight hoverIndex @hilight hoverIndex
# @private
strokeWidthForSeries: (index) ->
@options.barStrokeWidths[index % @options.barStrokeWidths.length]
# @private
strokeForSeries: (index) ->
@options.barStrokeColors[index % @options.barStrokeColors.length]
# @private
hoverColorForSeriesAndValue: (index, value) =>
colorOrGradient = @colorForSeriesAndValue index, value
if typeof colorOrGradient is 'string'
return colorOrGradient.split('-').pop()
return colorOrGradient
# @private
colorForSeriesAndValue: (index, value) =>
color = @options.barFillColors[index % @options.barFillColors.length]
if color.indexOf(' ') is -1
return color
color = color.split(/\s/)
colorAt = (top, bottom, relPos) ->
chan = (a, b) -> a + Math.round((b-a)*relPos)
newColor =
r: chan(top.r, bottom.r)
g: chan(top.g, bottom.g)
b: chan(top.b, bottom.b)
return Raphael.color("rgb(#{newColor.r},#{newColor.g},#{newColor.b})")
position = 1.0 - (value - @ymin) / (@ymax - @ymin)
top = Raphael.color(color[0])
bottom = Raphael.color(color[1])
if color.length is 3
bottom = Raphael.color(color[2])
middle = Raphael.color(color[1])
if position > 0.5
start = colorAt(middle, bottom, 2 * (position - 0.5))
return "90-#{bottom.hex}-#{start.hex}"
else
start = colorAt(top, middle, position * 2)
middlepos = 100 - Math.round(100 * (0.5 - position) / (1.0 - position))
return "90-#{bottom.hex}-#{middle.hex}:#{middlepos}-#{start.hex}"
start = colorAt(top, bottom, position)
return "90-#{bottom.hex}-#{start.hex}"

330
morris.js
View File

@ -228,7 +228,7 @@
}; };
Grid.prototype._calc = function() { Grid.prototype._calc = function() {
var h, maxYLabelWidth, padding, w; var h, maxYLabelWidth, w;
w = this.el.width(); w = this.el.width();
h = this.el.height(); h = this.el.height();
if (this.elementWidth !== w || this.elementHeight !== h || this.dirty) { if (this.elementWidth !== w || this.elementHeight !== h || this.dirty) {
@ -236,38 +236,11 @@
this.elementHeight = h; this.elementHeight = h;
this.dirty = false; this.dirty = false;
maxYLabelWidth = Math.max(this.measureText(this.yAxisFormat(this.ymin), this.options.gridTextSize).width, this.measureText(this.yAxisFormat(this.ymax), this.options.gridTextSize).width); maxYLabelWidth = Math.max(this.measureText(this.yAxisFormat(this.ymin), this.options.gridTextSize).width, this.measureText(this.yAxisFormat(this.ymax), this.options.gridTextSize).width);
if (typeof this.options.padding === 'string') { this.left = maxYLabelWidth + this.options.padding;
padding = this.options.padding.split(/\s+/); this.right = this.elementWidth - this.options.padding;
if (padding.length === 2) { this.top = this.options.padding;
this.paddingLeft = this.paddingRight = padding[1]; this.bottom = this.elementHeight - this.options.padding - 1.5 * this.options.gridTextSize;
this.paddingTop = this.paddingBottom = padding[0];
} else if (padding.length === 3) {
this.paddingLeft = this.paddingRight = padding[1];
this.paddingTop = padding[0];
this.paddingBottom = padding[2];
} else if (padding.length === 4) {
this.paddingTop = padding[0];
this.paddingRight = padding[1];
this.paddingBottom = padding[2];
this.paddingLeft = padding[3];
}
} else {
this.paddingTop = this.paddingBottom = this.options.padding;
this.paddingLeft = this.paddingRight = this.options.padding;
}
this.gridPaddingRight = this.paddingRight;
this.gridPaddingLeft = this.paddingLeft;
if (this.overridePadding) {
this.overridePadding();
}
this.left = maxYLabelWidth + this.paddingLeft;
this.gridLeft = maxYLabelWidth + this.gridPaddingLeft;
this.right = this.elementWidth - this.paddingRight;
this.gridRight = this.elementWidth - this.gridPaddingRight;
this.top = this.paddingTop;
this.bottom = this.elementHeight - this.paddingBottom - 1.5 * this.options.gridTextSize;
this.width = this.right - this.left; this.width = this.right - this.left;
this.gridWidth = this.gridRight - this.gridLeft;
this.height = this.bottom - this.top; this.height = this.bottom - this.top;
this.dx = this.width / (this.xmax - this.xmin); this.dx = this.width / (this.xmax - this.xmin);
this.dy = this.height / (this.ymax - this.ymin); this.dy = this.height / (this.ymax - this.ymin);
@ -306,8 +279,8 @@
for (lineY = _i = firstY, _ref = this.yInterval; firstY <= lastY ? _i <= lastY : _i >= lastY; lineY = _i += _ref) { for (lineY = _i = firstY, _ref = this.yInterval; firstY <= lastY ? _i <= lastY : _i >= lastY; lineY = _i += _ref) {
v = parseFloat(lineY.toFixed(this.precision)); v = parseFloat(lineY.toFixed(this.precision));
y = this.transY(v); y = this.transY(v);
this.r.text(this.gridLeft - this.gridPaddingLeft / 2, y, this.yAxisFormat(v)).attr('font-size', this.options.gridTextSize).attr('fill', this.options.gridTextColor).attr('text-anchor', 'end'); this.r.text(this.left - this.options.padding / 2, y, this.yAxisFormat(v)).attr('font-size', this.options.gridTextSize).attr('fill', this.options.gridTextColor).attr('text-anchor', 'end');
_results.push(this.r.path("M" + this.gridLeft + "," + y + "H" + (this.gridLeft + this.gridWidth)).attr('stroke', this.options.gridLineColor).attr('stroke-width', this.options.gridStrokeWidth)); _results.push(this.r.path("M" + this.left + "," + y + "H" + (this.left + this.width)).attr('stroke', this.options.gridLineColor).attr('stroke-width', this.options.gridStrokeWidth));
} }
return _results; return _results;
}; };
@ -953,10 +926,6 @@
__extends(Bar, _super); __extends(Bar, _super);
function Bar(options) { function Bar(options) {
this.colorForSeriesAndValue = __bind(this.colorForSeriesAndValue, this);
this.hoverColorForSeriesAndValue = __bind(this.hoverColorForSeriesAndValue, this);
this.updateHilight = __bind(this.updateHilight, this); this.updateHilight = __bind(this.updateHilight, this);
this.hilight = __bind(this.hilight, this); this.hilight = __bind(this.hilight, this);
@ -965,18 +934,14 @@
if (!(this instanceof Morris.Bar)) { if (!(this instanceof Morris.Bar)) {
return new Morris.Bar(options); return new Morris.Bar(options);
} }
Bar.__super__.constructor.call(this, options); Bar.__super__.constructor.call(this, $.extend({}, options, {
parseTime: false
}));
} }
Bar.prototype.init = function() { Bar.prototype.init = function() {
var touchHandler, var touchHandler,
_this = this; _this = this;
this.barFace = Raphael.animation({
opacity: this.options.barHoverOpacity
}, 25, 'linear');
this.barDeface = Raphael.animation({
opacity: 1.0
}, 25, 'linear');
this.prevHilight = null; this.prevHilight = null;
this.el.mousemove(function(evt) { this.el.mousemove(function(evt) {
return _this.updateHilight(evt.pageX); return _this.updateHilight(evt.pageX);
@ -998,12 +963,9 @@
}; };
Bar.prototype.defaults = { Bar.prototype.defaults = {
barSizeRatio: 0.5, barSizeRatio: 0.75,
barGap: 3, barGap: 3,
barStrokeWidths: [0], barColors: ['#0b62a4', '#7a92a3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'],
barStrokeColors: ['#ffffff'],
barFillColors: ['#0b62a4', '#7a92a3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'],
barHoverOpacity: 0.95,
hoverPaddingX: 10, hoverPaddingX: 10,
hoverPaddingY: 5, hoverPaddingY: 5,
hoverMargin: 10, hoverMargin: 10,
@ -1013,38 +975,21 @@
hoverOpacity: 0.95, hoverOpacity: 0.95,
hoverLabelColor: '#444', hoverLabelColor: '#444',
hoverFontSize: 12, hoverFontSize: 12,
hideHover: false, hideHover: false
xLabels: 'auto',
xLabelFormat: null
};
Bar.prototype.overridePadding = function() {
var maxYLabelWidth, xgap;
maxYLabelWidth = Math.max(this.measureText(this.yAxisFormat(this.ymin), this.options.gridTextSize).width, this.measureText(this.yAxisFormat(this.ymax), this.options.gridTextSize).width);
this.left = maxYLabelWidth + this.paddingLeft;
this.right = this.elementWidth - this.paddingRight;
this.width = this.right - this.left;
xgap = this.width / this.data.length;
this.barsoffset = this.options.barSizeRatio * xgap / 2.0;
this.barwidth = (this.options.barSizeRatio * xgap - (this.options.ykeys.length - 1) * this.options.barGap) / this.options.ykeys.length;
this.halfBarsWidth = Math.round(this.options.ykeys.length / 2) * (this.barwidth + this.options.barGap);
this.paddingLeft += this.halfBarsWidth;
return this.paddingRight += this.halfBarsWidth;
}; };
Bar.prototype.calc = function() { Bar.prototype.calc = function() {
this.calcBars(); this.calcBars();
this.generateBars();
return this.calcHoverMargins(); return this.calcHoverMargins();
}; };
Bar.prototype.calcBars = function() { Bar.prototype.calcBars = function() {
var row, y, _i, _len, _ref, _results; var idx, row, y, _i, _len, _ref, _results;
_ref = this.data; _ref = this.data;
_results = []; _results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) { for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
row = _ref[_i]; row = _ref[idx];
row._x = this.transX(row.x); row._x = this.left + this.width * (idx + 0.5) / this.data.length;
_results.push(row._y = (function() { _results.push(row._y = (function() {
var _j, _len1, _ref1, _results1; var _j, _len1, _ref1, _results1;
_ref1 = row.y; _ref1 = row.y;
@ -1064,39 +1009,12 @@
}; };
Bar.prototype.calcHoverMargins = function() { Bar.prototype.calcHoverMargins = function() {
var _this = this; var i;
return this.hoverMargins = $.map(this.data.slice(1), function(r, i) { return this.hoverMargins = (function() {
return (r._x + _this.data[i]._x) / 2;
});
};
Bar.prototype.generateBars = function() {
var coords, i, r;
return this.bars = (function() {
var _i, _ref, _results; var _i, _ref, _results;
_results = []; _results = [];
for (i = _i = 0, _ref = this.options.ykeys.length; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { for (i = _i = 1, _ref = this.data.length; 1 <= _ref ? _i < _ref : _i > _ref; i = 1 <= _ref ? ++_i : --_i) {
coords = (function() { _results.push(this.left + i * this.width / this.data.length);
var _j, _len, _ref1, _results1;
_ref1 = this.data;
_results1 = [];
for (_j = 0, _len = _ref1.length; _j < _len; _j++) {
r = _ref1[_j];
if (r._y[i] !== null) {
_results1.push({
x: r._x - this.barsoffset + i * (this.options.barGap + this.barwidth),
y: r._y[i],
v: r.y[i]
});
}
}
return _results1;
}).call(this);
if (coords.length > 1) {
_results.push(this.createBars(i, coords, this.barwidth));
} else {
_results.push(null);
}
} }
return _results; return _results;
}).call(this); }).call(this);
@ -1110,101 +1028,66 @@
}; };
Bar.prototype.drawXAxis = function() { Bar.prototype.drawXAxis = function() {
var drawLabel, l, labels, prevLabelMargin, row, xLabelMargin, ypos, _i, _len, _results, var i, label, labelBox, prevLabelMargin, row, xLabelMargin, ypos, _i, _ref, _results;
_this = this;
ypos = this.bottom + this.options.gridTextSize * 1.25; ypos = this.bottom + this.options.gridTextSize * 1.25;
xLabelMargin = 50; xLabelMargin = 50;
prevLabelMargin = null; prevLabelMargin = null;
drawLabel = function(labelText, xpos) {
var label, labelBox;
label = _this.r.text(_this.transX(xpos), ypos, labelText).attr('font-size', _this.options.gridTextSize).attr('fill', _this.options.gridTextColor);
labelBox = label.getBBox();
if ((prevLabelMargin === null || prevLabelMargin >= labelBox.x + labelBox.width) && labelBox.x >= 0 && (labelBox.x + labelBox.width) < _this.el.width()) {
return prevLabelMargin = labelBox.x - xLabelMargin;
} else {
return label.remove();
}
};
if (this.options.parseTime) {
if (this.data.length === 1 && this.options.xLabels === 'auto') {
labels = [[this.data[0].label, this.data[0].x]];
} else {
labels = Morris.labelSeries(this.xmin, this.xmax, this.width, this.options.xLabels, this.options.xLabelFormat);
}
} else {
labels = (function() {
var _i, _len, _ref, _results;
_ref = this.data;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
row = _ref[_i];
_results.push([row.label, row.x]);
}
return _results;
}).call(this);
}
labels.reverse();
_results = []; _results = [];
for (_i = 0, _len = labels.length; _i < _len; _i++) { for (i = _i = 0, _ref = this.data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
l = labels[_i]; row = this.data[this.data.length - 1 - i];
_results.push(drawLabel(l[0], l[1])); label = this.r.text(row._x, ypos, row.label).attr('font-size', this.options.gridTextSize).attr('fill', this.options.gridTextColor);
labelBox = label.getBBox();
if ((prevLabelMargin === null || prevLabelMargin >= labelBox.x + labelBox.width) && labelBox.x >= 0 && (labelBox.x + labelBox.width) < this.el.width()) {
_results.push(prevLabelMargin = labelBox.x - xLabelMargin);
} else {
_results.push(label.remove());
}
} }
return _results; return _results;
}; };
Bar.prototype.drawSeries = function() { Bar.prototype.drawSeries = function() {
var bar, bars, i, rect, _i, _ref, _results; var barWidth, bottom, groupWidth, idx, left, leftPadding, numBars, row, sidx, top, ypos, zeroPos;
this.seriesBars = (function() { groupWidth = this.width / this.options.data.length;
var _i, _ref, _results; numBars = this.options.ykeys.length;
barWidth = (groupWidth * this.options.barSizeRatio - this.options.barGap * (numBars - 1)) / numBars;
leftPadding = groupWidth * (1 - this.options.barSizeRatio) / 2;
zeroPos = this.ymin <= 0 && this.ymax >= 0 ? this.transY(0) : null;
return this.bars = (function() {
var _i, _len, _ref, _results;
_ref = this.data;
_results = []; _results = [];
for (i = _i = 0, _ref = this.options.ykeys.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { for (idx = _i = 0, _len = _ref.length; _i < _len; idx = ++_i) {
_results.push([]); row = _ref[idx];
}
return _results;
}).call(this);
_results = [];
for (i = _i = _ref = this.options.ykeys.length - 1; _ref <= 0 ? _i <= 0 : _i >= 0; i = _ref <= 0 ? ++_i : --_i) {
bars = this.bars[i];
if (bars.length > 0) {
_results.push((function() { _results.push((function() {
var _j, _len, _results1; var _j, _len1, _ref1, _results1;
_ref1 = row._y;
_results1 = []; _results1 = [];
for (_j = 0, _len = bars.length; _j < _len; _j++) { for (sidx = _j = 0, _len1 = _ref1.length; _j < _len1; sidx = ++_j) {
bar = bars[_j]; ypos = _ref1[sidx];
if (bar !== null) { if (ypos !== null) {
rect = this.r.rect(bar.x, bar.y, bar.width, bar.height).attr('fill', bar.fill).attr('stroke', this.strokeForSeries(i)).attr('stroke-width', this.strokeWidthForSeries(i)); if (zeroPos) {
top = Math.min(ypos, zeroPos);
bottom = Math.max(ypos, zeroPos);
} else {
top = ypos;
bottom = this.bottom;
}
left = this.left + idx * groupWidth + leftPadding + sidx * (barWidth + this.options.barGap);
_results1.push(this.r.rect(left, top, barWidth, bottom - top).attr('fill', this.options.barColors[sidx % this.options.barColors.length]).attr('stroke-width', 0));
} else { } else {
rect = null; _results1.push(null);
} }
_results1.push(this.seriesBars[i].push(rect));
} }
return _results1; return _results1;
}).call(this)); }).call(this));
} else {
_results.push(void 0);
} }
} return _results;
return _results; }).call(this);
};
Bar.prototype.createBars = function(index, coords, barwidth) {
var bars, coord, _i, _len;
bars = [];
for (_i = 0, _len = coords.length; _i < _len; _i++) {
coord = coords[_i];
bars.push({
x: coord.x,
y: coord.y,
width: barwidth,
height: this.bottom - coord.y,
fill: this.colorForSeriesAndValue(index, coord.v)
});
}
return bars;
}; };
Bar.prototype.drawHover = function() { Bar.prototype.drawHover = function() {
var i, idx, yLabel, _i, _ref, _results; var i, yLabel, _i, _ref, _results;
this.hoverHeight = this.options.hoverFontSize * 1.5 * (this.options.ykeys.length + 1); this.hoverHeight = this.options.hoverFontSize * 1.5 * (this.options.ykeys.length + 1);
this.hover = this.r.rect(-10, -this.hoverHeight / 2 - this.options.hoverPaddingY, 20, this.hoverHeight + this.options.hoverPaddingY * 2, 10).attr('fill', this.options.hoverFillColor).attr('stroke', this.options.hoverBorderColor).attr('stroke-width', this.options.hoverBorderWidth).attr('opacity', this.options.hoverOpacity); this.hover = this.r.rect(-10, -this.hoverHeight / 2 - this.options.hoverPaddingY, 20, this.hoverHeight + this.options.hoverPaddingY * 2, 10).attr('fill', this.options.hoverFillColor).attr('stroke', this.options.hoverBorderColor).attr('stroke-width', this.options.hoverBorderWidth).attr('opacity', this.options.hoverOpacity);
this.xLabel = this.r.text(0, (this.options.hoverFontSize * 0.75) - this.hoverHeight / 2, '').attr('fill', this.options.hoverLabelColor).attr('font-weight', 'bold').attr('font-size', this.options.hoverFontSize); this.xLabel = this.r.text(0, (this.options.hoverFontSize * 0.75) - this.hoverHeight / 2, '').attr('fill', this.options.hoverLabelColor).attr('font-weight', 'bold').attr('font-size', this.options.hoverFontSize);
@ -1214,8 +1097,7 @@
this.yLabels = []; this.yLabels = [];
_results = []; _results = [];
for (i = _i = 0, _ref = this.options.ykeys.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { for (i = _i = 0, _ref = this.options.ykeys.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
idx = this.cumulative ? this.options.ykeys.length - i - 1 : i; yLabel = this.r.text(0, this.options.hoverFontSize * 1.5 * (i + 1.5) - this.hoverHeight / 2, '').attr('font-size', this.options.hoverFontSize);
yLabel = this.r.text(0, this.options.hoverFontSize * 1.5 * (idx + 1.5) - this.hoverHeight / 2, '').attr('font-size', this.options.hoverFontSize);
this.yLabels.push(yLabel); this.yLabels.push(yLabel);
_results.push(this.hoverSet.push(yLabel)); _results.push(this.hoverSet.push(yLabel));
} }
@ -1230,7 +1112,7 @@
_ref = row.y; _ref = row.y;
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
y = _ref[i]; y = _ref[i];
this.yLabels[i].attr('fill', this.hoverColorForSeriesAndValue(i, y)); this.yLabels[i].attr('fill', this.options.barColors[i % this.options.barColors.length]);
this.yLabels[i].attr('text', "" + this.options.labels[i] + ": " + (this.yLabelFormat(y))); this.yLabels[i].attr('text', "" + this.options.labels[i] + ": " + (this.yLabelFormat(y)));
} }
maxLabelWidth = Math.max.apply(null, $.map(this.yLabels, function(l) { maxLabelWidth = Math.max.apply(null, $.map(this.yLabels, function(l) {
@ -1239,25 +1121,7 @@
maxLabelWidth = Math.max(maxLabelWidth, this.xLabel.getBBox().width); maxLabelWidth = Math.max(maxLabelWidth, this.xLabel.getBBox().width);
this.hover.attr('width', maxLabelWidth + this.options.hoverPaddingX * 2); this.hover.attr('width', maxLabelWidth + this.options.hoverPaddingX * 2);
this.hover.attr('x', -this.options.hoverPaddingX - maxLabelWidth / 2); this.hover.attr('x', -this.options.hoverPaddingX - maxLabelWidth / 2);
yloc = Math.min.apply(null, ((function() { yloc = (this.bottom + this.top) / 2;
var _j, _len1, _ref1, _results;
_ref1 = row._y;
_results = [];
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
y = _ref1[_j];
if (y !== null) {
_results.push(y);
}
}
return _results;
})()).concat(this.bottom));
if (yloc > this.hoverHeight + this.options.hoverPaddingY * 2 + this.options.hoverMargin + this.top) {
yloc = yloc - this.hoverHeight / 2 - this.options.hoverPaddingY - this.options.hoverMargin;
} else {
yloc = yloc + this.hoverHeight / 2 + this.options.hoverPaddingY + this.options.hoverMargin;
}
yloc = Math.max(this.top + this.hoverHeight / 2 + this.options.hoverPaddingY, yloc);
yloc = Math.min(this.bottom - this.hoverHeight / 2 - this.options.hoverPaddingY, yloc);
xloc = Math.min(this.right - maxLabelWidth / 2 - this.options.hoverPaddingX, this.data[index]._x); xloc = Math.min(this.right - maxLabelWidth / 2 - this.options.hoverPaddingX, this.data[index]._x);
xloc = Math.max(this.left + maxLabelWidth / 2 + this.options.hoverPaddingX, xloc); xloc = Math.max(this.left + maxLabelWidth / 2 + this.options.hoverPaddingX, xloc);
return this.hoverSet.attr('transform', "t" + xloc + "," + yloc); return this.hoverSet.attr('transform', "t" + xloc + "," + yloc);
@ -1268,20 +1132,7 @@
}; };
Bar.prototype.hilight = function(index) { Bar.prototype.hilight = function(index) {
var i, _i, _j, _ref, _ref1;
if (this.prevHilight !== null && this.prevHilight !== index) {
for (i = _i = 0, _ref = this.seriesBars.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) {
if (this.seriesBars[i][this.prevHilight]) {
this.seriesBars[i][this.prevHilight].animate(this.barDeface);
}
}
}
if (index !== null && this.prevHilight !== index) { if (index !== null && this.prevHilight !== index) {
for (i = _j = 0, _ref1 = this.seriesBars.length - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
if (this.seriesBars[i][index]) {
this.seriesBars[i][index].animate(this.barFace);
}
}
this.updateHover(index); this.updateHover(index);
} }
this.prevHilight = index; this.prevHilight = index;
@ -1301,61 +1152,6 @@
return this.hilight(hoverIndex); return this.hilight(hoverIndex);
}; };
Bar.prototype.strokeWidthForSeries = function(index) {
return this.options.barStrokeWidths[index % this.options.barStrokeWidths.length];
};
Bar.prototype.strokeForSeries = function(index) {
return this.options.barStrokeColors[index % this.options.barStrokeColors.length];
};
Bar.prototype.hoverColorForSeriesAndValue = function(index, value) {
var colorOrGradient;
colorOrGradient = this.colorForSeriesAndValue(index, value);
if (typeof colorOrGradient === 'string') {
return colorOrGradient.split('-').pop();
}
return colorOrGradient;
};
Bar.prototype.colorForSeriesAndValue = function(index, value) {
var bottom, color, colorAt, middle, middlepos, position, start, top;
color = this.options.barFillColors[index % this.options.barFillColors.length];
if (color.indexOf(' ') === -1) {
return color;
}
color = color.split(/\s/);
colorAt = function(top, bottom, relPos) {
var chan, newColor;
chan = function(a, b) {
return a + Math.round((b - a) * relPos);
};
newColor = {
r: chan(top.r, bottom.r),
g: chan(top.g, bottom.g),
b: chan(top.b, bottom.b)
};
return Raphael.color("rgb(" + newColor.r + "," + newColor.g + "," + newColor.b + ")");
};
position = 1.0 - (value - this.ymin) / (this.ymax - this.ymin);
top = Raphael.color(color[0]);
bottom = Raphael.color(color[1]);
if (color.length === 3) {
bottom = Raphael.color(color[2]);
middle = Raphael.color(color[1]);
if (position > 0.5) {
start = colorAt(middle, bottom, 2 * (position - 0.5));
return "90-" + bottom.hex + "-" + start.hex;
} else {
start = colorAt(top, middle, position * 2);
middlepos = 100 - Math.round(100 * (0.5 - position) / (1.0 - position));
return "90-" + bottom.hex + "-" + middle.hex + ":" + middlepos + "-" + start.hex;
}
}
start = colorAt(top, bottom, position);
return "90-" + bottom.hex + "-" + start.hex;
};
return Bar; return Bar;
})(Morris.Grid); })(Morris.Grid);

2
morris.min.js vendored

File diff suppressed because one or more lines are too long