Blame | Last modification | View Log | Download | RSS feed
class Morris.Bar extends Morris.Gridconstructor: (options) ->return new Morris.Bar(options) unless (@ instanceof Morris.Bar)super($.extend {}, options, parseTime: false)init: ->@cumulative = @options.stackedif @options.hideHover isnt 'always'@hover = new Morris.Hover(parent: @el)@on('hovermove', @onHoverMove)@on('hoverout', @onHoverOut)@on('gridclick', @onGridClick)# Default configuration#defaults:barSizeRatio: 0.75barGap: 3barColors: ['#0b62a4''#7a92a3''#4da74d''#afd8f8''#edc240''#cb4b4b''#9440ed'],barOpacity: 1.0barRadius: [0, 0, 0, 0]xLabelMargin: 50# Do any size-related calculations## @privatecalc: ->@calcBars()if @options.hideHover is false@hover.update(@hoverContentForRow(@data.length - 1)...)# calculate series data bars coordinates and sizes## @privatecalcBars: ->for row, idx in @datarow._x = @left + @width * (idx + 0.5) / @data.lengthrow._y = for y in row.yif y? then @transY(y) else null# Draws the bar chart.#draw: ->@drawXAxis() if @options.axes in [true, 'both', 'x']@drawSeries()# draw the x-axis labels## @privatedrawXAxis: -># draw x axis labelsypos = @bottom + (@options.xAxisLabelTopPadding || @options.padding / 2)prevLabelMargin = nullprevAngleMargin = nullfor 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 != 0offset = -0.5 * textBox.width *Math.cos(@options.xLabelAngle * Math.PI / 180.0)label.transform("t#{offset},0...")# try to avoid overlapsif (not prevLabelMargin? orprevLabelMargin >= labelBox.x + labelBox.width orprevAngleMargin? and prevAngleMargin >= labelBox.x) andlabelBox.x >= 0 and (labelBox.x + labelBox.width) < @el.width()if @options.xLabelAngle != 0margin = 1.25 * @options.gridTextSize /Math.sin(@options.xLabelAngle * Math.PI / 180.0)prevAngleMargin = labelBox.x - marginprevLabelMargin = labelBox.x - @options.xLabelMarginelselabel.remove()# draw the data series## @privatedrawSeries: ->groupWidth = @width / @options.data.lengthnumBars = if @options.stacked then 1 else @options.ykeys.lengthbarWidth = (groupWidth * @options.barSizeRatio - @options.barGap * (numBars - 1)) / numBarsbarWidth = Math.min(barWidth, @options.barSize) if @options.barSizespaceLeft = groupWidth - barWidth * numBars - @options.barGap * (numBars - 1)leftPadding = spaceLeft / 2zeroPos = if @ymin <= 0 and @ymax >= 0 then @transY(0) else null@bars = for row, idx in @datalastTop = 0for ypos, sidx in row._yif ypos != nullif zeroPostop = Math.min(ypos, zeroPos)bottom = Math.max(ypos, zeroPos)elsetop = yposbottom = @bottomleft = @left + idx * groupWidth + leftPaddingleft += sidx * (barWidth + @options.barGap) unless @options.stackedsize = bottom - topif @options.verticalGridCondition and @options.verticalGridCondition(row.x)@drawBar(@left + idx * groupWidth, @top, groupWidth, Math.abs(@top - @bottom), @options.verticalGridColor, @options.verticalGridOpacity, @options.barRadius)top -= lastTop if @options.stacked@drawBar(left, top, barWidth, size, @colorFor(row, sidx, 'bar'),@options.barOpacity, @options.barRadius)lastTop += sizeelsenull# @private## @param row [Object] row data# @param sidx [Number] series index# @param type [String] "bar", "hover" or "label"colorFor: (row, sidx, type) ->if typeof @options.barColors is 'function'r = { x: row.x, y: row.y[sidx], label: row.label }s = { index: sidx, key: @options.ykeys[sidx], label: @options.labels[sidx] }@options.barColors.call(@, r, s, type)else@options.barColors[sidx % @options.barColors.length]# hit test - returns the index of the row at the given x-coordinate#hitTest: (x) ->return null if @data.length == 0x = Math.max(Math.min(x, @right), @left)Math.min(@data.length - 1,Math.floor((x - @left) / (@width / @data.length)))# click on grid event handler## @privateonGridClick: (x, y) =>index = @hitTest(x)@fire 'click', index, @data[index].src, x, y# hover movement event handler## @privateonHoverMove: (x, y) =>index = @hitTest(x)@hover.update(@hoverContentForRow(index)...)# hover out event handler## @privateonHoverOut: =>if @options.hideHover isnt false@hover.hide()# hover content for a point## @privatehoverContentForRow: (index) ->row = @data[index]content = "<div class='morris-hover-row-label'>#{row.label}</div>"for y, j in row.ycontent += """<div class='morris-hover-point' style='color: #{@colorFor(row, j, 'label')}'>#{@options.labels[j]}:#{@yLabelFormat(y)}</div>"""if typeof @options.hoverCallback is 'function'content = @options.hoverCallback(index, @options, content, row.src)x = @left + (index + 0.5) * @width / @data.length[content, x]drawXAxisLabel: (xPos, yPos, text) ->label = @raphael.text(xPos, yPos, text).attr('font-size', @options.gridTextSize).attr('font-family', @options.gridTextFamily).attr('font-weight', @options.gridTextWeight).attr('fill', @options.gridTextColor)drawBar: (xPos, yPos, width, height, barColor, opacity, radiusArray) ->maxRadius = Math.max(radiusArray...)if maxRadius == 0 or maxRadius > heightpath = @raphael.rect(xPos, yPos, width, height)elsepath = @raphael.path @roundedRect(xPos, yPos, width, height, radiusArray)path.attr('fill', barColor).attr('fill-opacity', opacity).attr('stroke', 'none')roundedRect: (x, y, w, h, r = [0,0,0,0]) ->[ "M", x, r[0] + y, "Q", x, y, x + r[0], y,"L", x + w - r[1], y, "Q", x + w, y, x + w, y + r[1],"L", x + w, y + h - r[2], "Q", x + w, y + h, x + w - r[2], y + h,"L", x + r[3], y + h, "Q", x, y + h, x, y + h - r[3], "Z" ]