| 21627 |
kshitij.so |
1 |
class Morris.Bar extends Morris.Grid
|
|
|
2 |
constructor: (options) ->
|
|
|
3 |
return new Morris.Bar(options) unless (@ instanceof Morris.Bar)
|
|
|
4 |
super($.extend {}, options, parseTime: false)
|
|
|
5 |
|
|
|
6 |
init: ->
|
|
|
7 |
@cumulative = @options.stacked
|
|
|
8 |
|
|
|
9 |
if @options.hideHover isnt 'always'
|
|
|
10 |
@hover = new Morris.Hover(parent: @el)
|
|
|
11 |
@on('hovermove', @onHoverMove)
|
|
|
12 |
@on('hoverout', @onHoverOut)
|
|
|
13 |
@on('gridclick', @onGridClick)
|
|
|
14 |
|
|
|
15 |
# Default configuration
|
|
|
16 |
#
|
|
|
17 |
defaults:
|
|
|
18 |
barSizeRatio: 0.75
|
|
|
19 |
barGap: 3
|
|
|
20 |
barColors: [
|
|
|
21 |
'#0b62a4'
|
|
|
22 |
'#7a92a3'
|
|
|
23 |
'#4da74d'
|
|
|
24 |
'#afd8f8'
|
|
|
25 |
'#edc240'
|
|
|
26 |
'#cb4b4b'
|
|
|
27 |
'#9440ed'
|
|
|
28 |
]
|
|
|
29 |
xLabelMargin: 50
|
|
|
30 |
|
|
|
31 |
# Do any size-related calculations
|
|
|
32 |
#
|
|
|
33 |
# @private
|
|
|
34 |
calc: ->
|
|
|
35 |
@calcBars()
|
|
|
36 |
if @options.hideHover is false
|
|
|
37 |
@hover.update(@hoverContentForRow(@data.length - 1)...)
|
|
|
38 |
|
|
|
39 |
# calculate series data bars coordinates and sizes
|
|
|
40 |
#
|
|
|
41 |
# @private
|
|
|
42 |
calcBars: ->
|
|
|
43 |
for row, idx in @data
|
|
|
44 |
row._x = @left + @width * (idx + 0.5) / @data.length
|
|
|
45 |
row._y = for y in row.y
|
|
|
46 |
if y? then @transY(y) else null
|
|
|
47 |
|
|
|
48 |
# Draws the bar chart.
|
|
|
49 |
#
|
|
|
50 |
draw: ->
|
|
|
51 |
@drawXAxis() if @options.axes
|
|
|
52 |
@drawSeries()
|
|
|
53 |
|
|
|
54 |
# draw the x-axis labels
|
|
|
55 |
#
|
|
|
56 |
# @private
|
|
|
57 |
drawXAxis: ->
|
|
|
58 |
# draw x axis labels
|
|
|
59 |
ypos = @bottom + @options.padding / 2
|
|
|
60 |
prevLabelMargin = null
|
|
|
61 |
prevAngleMargin = null
|
|
|
62 |
for i in [0...@data.length]
|
|
|
63 |
row = @data[@data.length - 1 - i]
|
|
|
64 |
label = @drawXAxisLabel(row._x, ypos, row.label)
|
|
|
65 |
textBox = label.getBBox()
|
|
|
66 |
label.transform("r#{-@options.xLabelAngle}")
|
|
|
67 |
labelBox = label.getBBox()
|
|
|
68 |
label.transform("t0,#{labelBox.height / 2}...")
|
|
|
69 |
if @options.xLabelAngle != 0
|
|
|
70 |
offset = -0.5 * textBox.width *
|
|
|
71 |
Math.cos(@options.xLabelAngle * Math.PI / 180.0)
|
|
|
72 |
label.transform("t#{offset},0...")
|
|
|
73 |
# try to avoid overlaps
|
|
|
74 |
if (not prevLabelMargin? or
|
|
|
75 |
prevLabelMargin >= labelBox.x + labelBox.width or
|
|
|
76 |
prevAngleMargin? and prevAngleMargin >= labelBox.x) and
|
|
|
77 |
labelBox.x >= 0 and (labelBox.x + labelBox.width) < @el.width()
|
|
|
78 |
if @options.xLabelAngle != 0
|
|
|
79 |
margin = 1.25 * @options.gridTextSize /
|
|
|
80 |
Math.sin(@options.xLabelAngle * Math.PI / 180.0)
|
|
|
81 |
prevAngleMargin = labelBox.x - margin
|
|
|
82 |
prevLabelMargin = labelBox.x - @options.xLabelMargin
|
|
|
83 |
else
|
|
|
84 |
label.remove()
|
|
|
85 |
|
|
|
86 |
# draw the data series
|
|
|
87 |
#
|
|
|
88 |
# @private
|
|
|
89 |
drawSeries: ->
|
|
|
90 |
groupWidth = @width / @options.data.length
|
|
|
91 |
numBars = if @options.stacked? then 1 else @options.ykeys.length
|
|
|
92 |
barWidth = (groupWidth * @options.barSizeRatio - @options.barGap * (numBars - 1)) / numBars
|
|
|
93 |
leftPadding = groupWidth * (1 - @options.barSizeRatio) / 2
|
|
|
94 |
zeroPos = if @ymin <= 0 and @ymax >= 0 then @transY(0) else null
|
|
|
95 |
@bars = for row, idx in @data
|
|
|
96 |
lastTop = 0
|
|
|
97 |
for ypos, sidx in row._y
|
|
|
98 |
if ypos != null
|
|
|
99 |
if zeroPos
|
|
|
100 |
top = Math.min(ypos, zeroPos)
|
|
|
101 |
bottom = Math.max(ypos, zeroPos)
|
|
|
102 |
else
|
|
|
103 |
top = ypos
|
|
|
104 |
bottom = @bottom
|
|
|
105 |
|
|
|
106 |
left = @left + idx * groupWidth + leftPadding
|
|
|
107 |
left += sidx * (barWidth + @options.barGap) unless @options.stacked
|
|
|
108 |
size = bottom - top
|
|
|
109 |
|
|
|
110 |
top -= lastTop if @options.stacked
|
|
|
111 |
@drawBar(left, top, barWidth, size, @colorFor(row, sidx, 'bar'))
|
|
|
112 |
|
|
|
113 |
lastTop += size
|
|
|
114 |
else
|
|
|
115 |
null
|
|
|
116 |
|
|
|
117 |
# @private
|
|
|
118 |
#
|
|
|
119 |
# @param row [Object] row data
|
|
|
120 |
# @param sidx [Number] series index
|
|
|
121 |
# @param type [String] "bar", "hover" or "label"
|
|
|
122 |
colorFor: (row, sidx, type) ->
|
|
|
123 |
if typeof @options.barColors is 'function'
|
|
|
124 |
r = { x: row.x, y: row.y[sidx], label: row.label }
|
|
|
125 |
s = { index: sidx, key: @options.ykeys[sidx], label: @options.labels[sidx] }
|
|
|
126 |
@options.barColors.call(@, r, s, type)
|
|
|
127 |
else
|
|
|
128 |
@options.barColors[sidx % @options.barColors.length]
|
|
|
129 |
|
|
|
130 |
# hit test - returns the index of the row beneath the given coordinate
|
|
|
131 |
#
|
|
|
132 |
hitTest: (x, y) ->
|
|
|
133 |
return null if @data.length == 0
|
|
|
134 |
x = Math.max(Math.min(x, @right), @left)
|
|
|
135 |
Math.min(@data.length - 1,
|
|
|
136 |
Math.floor((x - @left) / (@width / @data.length)))
|
|
|
137 |
|
|
|
138 |
# click on grid event handler
|
|
|
139 |
#
|
|
|
140 |
# @private
|
|
|
141 |
onGridClick: (x, y) =>
|
|
|
142 |
index = @hitTest(x, y)
|
|
|
143 |
@fire 'click', index, @options.data[index], x, y
|
|
|
144 |
|
|
|
145 |
# hover movement event handler
|
|
|
146 |
#
|
|
|
147 |
# @private
|
|
|
148 |
onHoverMove: (x, y) =>
|
|
|
149 |
index = @hitTest(x, y)
|
|
|
150 |
@hover.update(@hoverContentForRow(index)...)
|
|
|
151 |
|
|
|
152 |
# hover out event handler
|
|
|
153 |
#
|
|
|
154 |
# @private
|
|
|
155 |
onHoverOut: =>
|
|
|
156 |
if @options.hideHover isnt false
|
|
|
157 |
@hover.hide()
|
|
|
158 |
|
|
|
159 |
# hover content for a point
|
|
|
160 |
#
|
|
|
161 |
# @private
|
|
|
162 |
hoverContentForRow: (index) ->
|
|
|
163 |
row = @data[index]
|
|
|
164 |
content = "<div class='morris-hover-row-label'>#{row.label}</div>"
|
|
|
165 |
for y, j in row.y
|
|
|
166 |
content += """
|
|
|
167 |
<div class='morris-hover-point' style='color: #{@colorFor(row, j, 'label')}'>
|
|
|
168 |
#{@options.labels[j]}:
|
|
|
169 |
#{@yLabelFormat(y)}
|
|
|
170 |
</div>
|
|
|
171 |
"""
|
|
|
172 |
if typeof @options.hoverCallback is 'function'
|
|
|
173 |
content = @options.hoverCallback(index, @options, content)
|
|
|
174 |
x = @left + (index + 0.5) * @width / @data.length
|
|
|
175 |
[content, x]
|
|
|
176 |
|
|
|
177 |
drawXAxisLabel: (xPos, yPos, text) ->
|
|
|
178 |
label = @raphael.text(xPos, yPos, text)
|
|
|
179 |
.attr('font-size', @options.gridTextSize)
|
|
|
180 |
.attr('font-family', @options.gridTextFamily)
|
|
|
181 |
.attr('font-weight', @options.gridTextWeight)
|
|
|
182 |
.attr('fill', @options.gridTextColor)
|
|
|
183 |
|
|
|
184 |
drawBar: (xPos, yPos, width, height, barColor) ->
|
|
|
185 |
@raphael.rect(xPos, yPos, width, height)
|
|
|
186 |
.attr('fill', barColor)
|
|
|
187 |
.attr('stroke-width', 0)
|