From d5b8071dbb1fc13697121a227210484a5b305eb7 Mon Sep 17 00:00:00 2001 From: Olly Smith Date: Sun, 21 Oct 2012 22:33:49 +0100 Subject: [PATCH] WIP Refactor grid drawing into a base class. --- examples/days.html | 8 +- lib/morris.grid.coffee | 292 ++++++++++++++++ lib/morris.line.coffee | 340 ++---------------- morris.js | 777 +++++++++++++++++++++-------------------- morris.min.js | 2 +- 5 files changed, 733 insertions(+), 686 deletions(-) create mode 100644 lib/morris.grid.coffee diff --git a/examples/days.html b/examples/days.html index f1c5815..ebfff30 100644 --- a/examples/days.html +++ b/examples/days.html @@ -25,16 +25,12 @@ var day_data = [ {"period": "2012-09-15", "licensed": 3201, "sorned": 656}, {"period": "2012-09-10", "licensed": 3215, "sorned": 622} ]; -Morris.Line({ +line = Morris.Line({ element: 'graph', data: day_data, xkey: 'period', ykeys: ['licensed', 'sorned'], - labels: ['Licensed', 'SORN'], - /* 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' + labels: ['Licensed', 'SORN'] }); diff --git a/lib/morris.grid.coffee b/lib/morris.grid.coffee new file mode 100644 index 0000000..d811d83 --- /dev/null +++ b/lib/morris.grid.coffee @@ -0,0 +1,292 @@ +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 @el is null or @el.length == 0 + throw new Error("Graph container element not found") + + @options = $.extend {}, @gridDefaults, (@defaults || {}), options + + # bail if there's no data + if @options.data is undefined or @options.data.length is 0 + return + + # backwards compatibility for units -> postUnits + if typeof @options.units is 'string' + @options.postUnits = options.units + + # the raphael drawing instance + @r = new Raphael(@el[0]) + + # some redraw stuff + @elementWidth = null + @elementHeight = null + @dirty = false + + # more stuff + @init() if @init + + # load data + @setData(@options.data) + + # Default options + # + gridDefaults: + dateFormat: null + gridLineColor: '#aaa' + gridStrokeWidth: 0.5 + gridTextColor: '#888' + gridTextSize: 12 + numLines: 5 + padding: 25 + parseTime: true + postUnits: '' + preUnits: '' + ymax: 'auto' + ymin: 'auto 0' + + # Update the data series and redraw the chart. + # + setData: (data, redraw = true) -> + # shallow copy data + @options.data = data.slice() + if @parseTime + @options.data = @options.data.sort (a, b) => + (a[@options.xkey] < b[@options.xkey]) - (b[@options.xkey] < a[@options.xkey]) + + # extract series data + @series = [] + for ykey in @options.ykeys + seriesData = [] + for d in @options.data + y = d[ykey] + seriesData.push switch typeof y + when 'number' then y + when 'string' then parseFloat y + else null + @series.push seriesData + + # extract labels / x values + @columnLabels = $.map @options.data, (d) => d[@options.xkey] + if @options.parseTime + @xvals = $.map @columnLabels, (x) -> Morris.parseDate x + if @options.dateFormat + @columnLabels = $.map @xvals, (d) => @options.dateFormat d + else + @columnLabels = $.map @columnLabels, (d) => + # default formatter for numeric timestamp labels + if typeof d is 'number' then new Date(d).toString() else d + else + @xvals = [0...@columnLabels.length] + + # calculate horizontal range of the graph + @xmin = Math.min.apply null, @xvals + @xmax = Math.max.apply null, @xvals + if @xmin is @xmax + @xmin -= 1 + @xmax += 1 + + # 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 + @ymax = Math.max parseInt(@options.ymax[5..], 10), ymax + else + @ymax = ymax + else if typeof @options.ymax is 'string' + @ymax = parseInt(@options.ymax, 10) + else + @ymax = @options.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 + @ymin = Math.min parseInt(@options.ymin[5..], 10), ymin + else + @ymin = ymin + else if typeof @options.ymin is 'string' + @ymin = parseInt(@options.ymin, 10) + else + @ymin = @options.ymin + if @ymin is @ymax + if @ymin is not 0 then @ymin -= 1 + @ymax += 1 + + @yInterval = (@ymax - @ymin) / (@options.numLines - 1) + if @yInterval > 0 and @yInterval < 1 + @precision = -Math.floor(Math.log(@yInterval) / Math.log(10)) + else + @precision = 0 + + @dirty = true + @redraw() if redraw + + _calc: -> + w = @el.width() + h = @el.height() + + if @elementWidth != w or @elementHeight != h or @dirty + @elementWidth = w + @elementHeight = h + @dirty = false + # calculate grid dimensions + maxYLabelWidth = Math.max( + @measureText(@yAxisFormat(@ymin), @options.gridTextSize).width, + @measureText(@yAxisFormat(@ymax), @options.gridTextSize).width) + @left = maxYLabelWidth + @options.padding + @right = @elementWidth - @options.padding + @top = @options.padding + @bottom = @elementHeight - @options.padding - 1.5 * @options.gridTextSize + @width = @right - @left + @height = @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 @xvals.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: -> + @r.clear() + @_calc() + @drawGrid() + @draw() if @draw + + # draw y axis labels, horizontal lines + # + drawGrid: -> + firstY = @ymin + lastY = @ymax + for lineY in [firstY..lastY] by @yInterval + v = parseFloat(lineY.toFixed(@precision)) + y = @transY(v) + @r.text(@left - @options.padding / 2, y, @yAxisFormat(v)) + .attr('font-size', @options.gridTextSize) + .attr('fill', @options.gridTextColor) + .attr('text-anchor', 'end') + @r.path("M#{@left},#{y}H#{@left + @width}") + .attr('stroke', @options.gridLineColor) + .attr('stroke-width', @options.gridStrokeWidth) + + # @private + # + measureText: (text, fontSize = 12) -> + tt = @r.text(100, 100, text).attr('font-size', fontSize) + ret = tt.getBBox() + tt.remove() + ret + + # @private + # + yAxisFormat: (label) -> @yLabelFormat(label) + + # @private + # + yLabelFormat: (label) -> + "#{@options.preUnits}#{Morris.commas(label)}#{@options.postUnits}" + + +# 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() + diff --git a/lib/morris.line.coffee b/lib/morris.line.coffee index aea1506..1c7d27a 100644 --- a/lib/morris.line.coffee +++ b/lib/morris.line.coffee @@ -1,35 +1,14 @@ -class Morris.Line +class Morris.Line extends Morris.Grid # Initialise the graph. # constructor: (options) -> - if not (this instanceof Morris.Line) - return new Morris.Line(options) - if typeof options.element is 'string' - @el = $ document.getElementById(options.element) - else - @el = $ options.element - - if @el == null || @el.length == 0 - throw new Error("Graph placeholder not found.") - - @options = $.extend {}, @defaults, options - # backwards compatibility for units -> postUnits - if typeof @options.units is 'string' - @options.postUnits = options.units - # bail if there's no data - if @options.data is undefined or @options.data.length is 0 - return - @el.addClass 'graph-initialised' - - # the raphael drawing instance - @r = new Raphael(@el[0]) + 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' - @elementWidth = null - @elementHeight = null - @dirty = false # column hilight events @prevHilight = null @el.mousemove (evt) => @@ -45,9 +24,6 @@ class Morris.Line @el.bind 'touchmove', touchHandler @el.bind 'touchend', touchHandler - @seriesLabels = @options.labels - @setData(@options.data) - # Default configuration # defaults: @@ -65,17 +41,6 @@ class Morris.Line pointWidths: [1] pointStrokeColors: ['#ffffff'] pointFillColors: [] - ymax: 'auto' - ymin: 'auto 0' - marginTop: 25 - marginRight: 25 - marginBottom: 30 - marginLeft: 25 - numLines: 5 - gridLineColor: '#aaa' - gridTextColor: '#888' - gridTextSize: 12 - gridStrokeWidth: 0.5 hoverPaddingX: 10 hoverPaddingY: 5 hoverMargin: 10 @@ -87,176 +52,41 @@ class Morris.Line hoverFontSize: 12 smooth: true hideHover: false - parseTime: true - preUnits: '' - postUnits: '' - dateFormat: null xLabels: 'auto' xLabelFormat: null - # Update the data series and redraw the chart. - # - setData: (data, redraw = true) -> - # shallow copy & sort data (if required) - @options.data = data.slice(0) - if @options.parseTime - @options.data.sort (a, b) => - (a[@options.xkey] < b[@options.xkey]) - (b[@options.xkey] < a[@options.xkey]) - else - @options.data.reverse() - - # extract series data - @series = [] - for ykey in @options.ykeys - series_data = [] - for d in @options.data - series_data.push switch typeof d[ykey] - when 'number' then d[ykey] - when 'string' then parseFloat(d[ykey]) - else null - @series.push(series_data) - - # extract labels - @columnLabels = $.map @options.data, (d) => d[@options.xkey] - - # translate x labels into nominal dates - if @options.parseTime - @xvals = $.map @columnLabels, (x) -> Morris.parseDate x - else - @xvals = [(@columnLabels.length-1)..0] - - # format column labels - if @options.parseTime - if @options.dateFormat - @columnLabels = $.map @xvals, (d) => @options.dateFormat(d) - else - @columnLabels = $.map @columnLabels, (d) => - # default formatter for numeric timestamp labels - if typeof d is 'number' then new Date(d).toString() else d - - # calculate horizontal range of the graph - @xmin = Math.min.apply null, @xvals - @xmax = Math.max.apply null, @xvals - if @xmin is @xmax - @xmin -= 1 - @xmax += 1 - - # 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 - @ymax = Math.max parseInt(@options.ymax[5..], 10), ymax - else - @ymax = ymax - else if typeof @options.ymax is 'string' - @ymax = parseInt(@options.ymax, 10) - else - @ymax = @options.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 - @ymin = Math.min parseInt(@options.ymin[5..], 10), ymin - else - @ymin = ymin - else if typeof @options.ymin is 'string' - @ymin = parseInt(@options.ymin, 10) - else - @ymin = @options.ymin - if @ymin is @ymax - if @ymin is not 0 then @ymin -= 1 - @ymax += 1 - - @yInterval = (@ymax - @ymin) / (@options.numLines - 1) - if @yInterval > 0 and @yInterval < 1 - @precision = -Math.floor(Math.log(@yInterval) / Math.log(10)) - else - @precision = 0 - - @dirty = true - @redraw() if redraw - # Do any size-related calculations # # @private calc: -> - w = @el.width() - h = @el.height() + # calculate series data point coordinates + @columns = (@transX(x) for x in @xvals) + @seriesCoords = [] + for s in @series + scoords = [] + $.each s, (i, y) => + if y == null + scoords.push(null) + else + scoords.push(x: @columns[i], y: @transY(y)) + @seriesCoords.push(scoords) + # calculate hover margins + @hoverMargins = $.map @columns.slice(1), (x, i) => (x + @columns[i]) / 2 - if @elementWidth != w or @elementHeight != h or @dirty - @elementWidth = w - @elementHeight = h - @dirty = false - # calculate grid dimensions - @maxYLabelWidth = Math.max( - @measureText(@yAxisFormat(@ymin), @options.gridTextSize).width, - @measureText(@yAxisFormat(@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 / (@ymax - @ymin) - # calculate series data point coordinates - @columns = (@transX(x) for x in @xvals) - @seriesCoords = [] - for s in @series - scoords = [] - $.each s, (i, y) => - if y == null - scoords.push(null) - else - scoords.push(x: @columns[i], y: @transY(y)) - @seriesCoords.push(scoords) - # calculate hover margins - @hoverMargins = $.map @columns.slice(1), (x, i) => (x + @columns[i]) / 2 - - # quick translation helpers + # Draws the line chart. # - # @private - transX: (x) => - if @xvals.length is 1 - @left + @width / 2 - else - @left + (x - @xmin) * @dx - - # @private - transY: (y) => - return @options.marginTop + @height - (y - @ymin) * @dy - - # 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: -> - @r.clear() - @calc() - @drawGrid() + draw: -> + @drawXAxis() @drawSeries() @drawHover() @hilight(if @options.hideHover then null else 0) - # draw the grid, and axes labels + # draw the x-axis labels # # @private - drawGrid: -> - # draw y axis labels, horizontal lines - firstY = @ymin - lastY = @ymax - - - for lineY in [firstY..lastY] by @yInterval - v = parseFloat(lineY.toFixed(@precision)) - y = @transY(v) - @r.text(@left - @options.marginLeft/2, y, @yAxisFormat(v)) - .attr('font-size', @options.gridTextSize) - .attr('fill', @options.gridTextColor) - .attr('text-anchor', 'end') - @r.path("M#{@left},#{y}H#{@left + @width}") - .attr('stroke', @options.gridLineColor) - .attr('stroke-width', @options.gridStrokeWidth) - - ## draw x axis labels - ypos = @options.marginTop + @height + @options.marginBottom / 2 + drawXAxis: -> + # draw x axis labels + ypos = @bottom + @options.gridTextSize * 1.25 xLabelMargin = 50 # make this an option? prevLabelMargin = null drawLabel = (labelText, xpos) => @@ -290,11 +120,11 @@ class Morris.Line # @private drawSeries: -> for i in [@seriesCoords.length-1..0] - coords = $.map(@seriesCoords[i], (c) -> c) + coords = @seriesCoords[i] smooth = @options.smooth is true or $.inArray(@options.ykeys[i], @options.smooth) > -1 if coords.length > 1 - path = @createPath coords, @options.marginTop + @height, smooth + path = @createPath coords, @bottom, smooth @r.path(path) .attr('stroke', @colorForSeries(i)) .attr('stroke-width', @options.lineWidth) @@ -313,7 +143,7 @@ class Morris.Line # create a path for a data series # # @private - createPath: (coords, bottom, smooth) -> + createPath: (coords, smooth) -> path = "" if smooth grads = @gradients coords @@ -327,9 +157,9 @@ class Morris.Line lg = grads[i - 1] ix = (c.x - lc.x) / 4 x1 = lc.x + ix - y1 = Math.min(bottom, lc.y + ix * lg) + y1 = Math.min(@bottom, lc.y + ix * lg) x2 = c.x - ix - y2 = Math.min(bottom, c.y - ix * g) + y2 = Math.min(@bottom, c.y - ix * g) path += "C#{x1},#{y1},#{x2},#{y2},#{c.x},#{c.y}" else path = "M" + $.map(coords, (c) -> "#{c.x},#{c.y}").join("L") @@ -378,7 +208,7 @@ class Morris.Line @hoverSet.show() @xLabel.attr('text', @columnLabels[index]) for i in [0..@series.length-1] - @yLabels[i].attr('text', "#{@seriesLabels[i]}: #{@yLabelFormat(@series[i][index])}") + @yLabels[i].attr('text', "#{@options.labels[i]}: #{@yLabelFormat(@series[i][index])}") # recalculate hover box width maxLabelWidth = Math.max.apply null, $.map @yLabels, (l) -> l.getBBox().width @@ -388,13 +218,13 @@ class Morris.Line # move to y pos yloc = Math.min.apply null, $.map @series, (s) => @transY s[index] - if yloc > @hoverHeight + @options.hoverPaddingY * 2 + @options.hoverMargin + @options.marginTop + 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 @options.marginTop + @hoverHeight / 2 + @options.hoverPaddingY, yloc - yloc = Math.min @options.marginTop + @height - @hoverHeight / 2 - @options.hoverPaddingY, yloc - xloc = Math.min @left + @width - maxLabelWidth / 2 - @options.hoverPaddingX, @columns[index] + 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, @columns[index] xloc = Math.max @left + maxLabelWidth / 2 + @options.hoverPaddingX, xloc @hoverSet.attr 'transform', "t#{xloc},#{yloc}" @@ -425,21 +255,6 @@ class Morris.Line @hilight hoverIndex break - # @private - measureText: (text, fontSize = 12) -> - tt = @r.text(100, 100, text).attr('font-size', fontSize) - ret = tt.getBBox() - tt.remove() - return ret - - # @private - yAxisFormat: (label) -> - @yLabelFormat(label) - - # @private - yLabelFormat: (label) -> - "#{@options.preUnits}#{Morris.commas(label)}#{@options.postUnits}" - # @private colorForSeries: (index) -> @options.lineColors[index % @options.lineColors.length] @@ -457,93 +272,6 @@ class Morris.Line @options.pointFillColors[index % @options.pointFillColors.length] -# 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() - # generate a series of label, timestamp pairs for x-axis labels # # @private diff --git a/morris.js b/morris.js index 2ac9f19..0822f54 100644 --- a/morris.js +++ b/morris.js @@ -283,143 +283,84 @@ })(Morris.EventEmitter); - Morris.Line = (function() { + Morris.Grid = (function(_super) { - function Line(options) { - this.updateHilight = __bind(this.updateHilight, this); + __extends(Grid, _super); - this.hilight = __bind(this.hilight, this); - - this.updateHover = __bind(this.updateHover, this); - - this.transY = __bind(this.transY, this); - - this.transX = __bind(this.transX, this); - - var touchHandler, - _this = this; - if (!(this instanceof Morris.Line)) { - return new Morris.Line(options); - } + function Grid(options) { if (typeof options.element === 'string') { this.el = $(document.getElementById(options.element)); } else { this.el = $(options.element); } if (this.el === null || this.el.length === 0) { - throw new Error("Graph placeholder not found."); - } - this.options = $.extend({}, this.defaults, options); - if (typeof this.options.units === 'string') { - this.options.postUnits = options.units; + throw new Error("Graph container element not found"); } + this.options = $.extend({}, this.gridDefaults, this.defaults || {}, options); if (this.options.data === void 0 || this.options.data.length === 0) { return; } - this.el.addClass('graph-initialised'); + if (typeof this.options.units === 'string') { + this.options.postUnits = options.units; + } this.r = new Raphael(this.el[0]); - this.pointGrow = Raphael.animation({ - r: this.options.pointSize + 3 - }, 25, 'linear'); - this.pointShrink = Raphael.animation({ - r: this.options.pointSize - }, 25, 'linear'); this.elementWidth = null; this.elementHeight = null; this.dirty = false; - this.prevHilight = null; - this.el.mousemove(function(evt) { - return _this.updateHilight(evt.pageX); - }); - if (this.options.hideHover) { - this.el.mouseout(function(evt) { - return _this.hilight(null); - }); + if (this.init) { + this.init(); } - touchHandler = function(evt) { - var touch; - touch = evt.originalEvent.touches[0] || evt.originalEvent.changedTouches[0]; - _this.updateHilight(touch.pageX); - return touch; - }; - this.el.bind('touchstart', touchHandler); - this.el.bind('touchmove', touchHandler); - this.el.bind('touchend', touchHandler); - this.seriesLabels = this.options.labels; this.setData(this.options.data); } - Line.prototype.defaults = { - lineWidth: 3, - pointSize: 4, - lineColors: ['#0b62a4', '#7A92A3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'], - pointWidths: [1], - pointStrokeColors: ['#ffffff'], - pointFillColors: [], - ymax: 'auto', - ymin: 'auto 0', - marginTop: 25, - marginRight: 25, - marginBottom: 30, - marginLeft: 25, - numLines: 5, + Grid.prototype.gridDefaults = { + dateFormat: null, gridLineColor: '#aaa', + gridStrokeWidth: 0.5, gridTextColor: '#888', gridTextSize: 12, - gridStrokeWidth: 0.5, - hoverPaddingX: 10, - hoverPaddingY: 5, - hoverMargin: 10, - hoverFillColor: '#fff', - hoverBorderColor: '#ccc', - hoverBorderWidth: 2, - hoverOpacity: 0.95, - hoverLabelColor: '#444', - hoverFontSize: 12, - smooth: true, - hideHover: false, + numLines: 5, + padding: 25, parseTime: true, - preUnits: '', postUnits: '', - dateFormat: null, - xLabels: 'auto', - xLabelFormat: null + preUnits: '', + ymax: 'auto', + ymin: 'auto 0' }; - Line.prototype.setData = function(data, redraw) { - var d, series_data, ykey, ymax, ymin, _i, _j, _k, _len, _len1, _ref, _ref1, _ref2, _results, + Grid.prototype.setData = function(data, redraw) { + var d, seriesData, y, ykey, ymax, ymin, _i, _j, _k, _len, _len1, _ref, _ref1, _ref2, _results, _this = this; if (redraw == null) { redraw = true; } - this.options.data = data.slice(0); - if (this.options.parseTime) { - this.options.data.sort(function(a, b) { + this.options.data = data.slice(); + if (this.parseTime) { + this.options.data = this.options.data.sort(function(a, b) { return (a[_this.options.xkey] < b[_this.options.xkey]) - (b[_this.options.xkey] < a[_this.options.xkey]); }); - } else { - this.options.data.reverse(); } this.series = []; _ref = this.options.ykeys; for (_i = 0, _len = _ref.length; _i < _len; _i++) { ykey = _ref[_i]; - series_data = []; + seriesData = []; _ref1 = this.options.data; for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { d = _ref1[_j]; - series_data.push((function() { - switch (typeof d[ykey]) { + y = d[ykey]; + seriesData.push((function() { + switch (typeof y) { case 'number': - return d[ykey]; + return y; case 'string': - return parseFloat(d[ykey]); + return parseFloat(y); default: return null; } })()); } - this.series.push(series_data); + this.series.push(seriesData); } this.columnLabels = $.map(this.options.data, function(d) { return d[_this.options.xkey]; @@ -428,14 +369,6 @@ this.xvals = $.map(this.columnLabels, function(x) { return Morris.parseDate(x); }); - } else { - this.xvals = (function() { - _results = []; - for (var _k = _ref2 = this.columnLabels.length - 1; _ref2 <= 0 ? _k <= 0 : _k >= 0; _ref2 <= 0 ? _k++ : _k--){ _results.push(_k); } - return _results; - }).apply(this); - } - if (this.options.parseTime) { if (this.options.dateFormat) { this.columnLabels = $.map(this.xvals, function(d) { return _this.options.dateFormat(d); @@ -449,6 +382,12 @@ } }); } + } else { + this.xvals = (function() { + _results = []; + for (var _k = 0, _ref2 = this.columnLabels.length; 0 <= _ref2 ? _k < _ref2 : _k > _ref2; 0 <= _ref2 ? _k++ : _k--){ _results.push(_k); } + return _results; + }).apply(this); } this.xmin = Math.min.apply(null, this.xvals); this.xmax = Math.max.apply(null, this.xvals); @@ -498,293 +437,65 @@ } }; - Line.prototype.calc = function() { - var h, s, scoords, w, x, _i, _len, _ref, - _this = this; + Grid.prototype._calc = function() { + var h, maxYLabelWidth, w; w = this.el.width(); h = this.el.height(); if (this.elementWidth !== w || this.elementHeight !== h || this.dirty) { this.elementWidth = w; this.elementHeight = h; this.dirty = false; - this.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 = this.maxYLabelWidth + this.options.marginLeft; - this.width = this.el.width() - this.left - this.options.marginRight; - this.height = this.el.height() - this.options.marginTop - this.options.marginBottom; + 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.options.padding; + this.right = this.elementWidth - this.options.padding; + this.top = this.options.padding; + this.bottom = this.elementHeight - this.options.padding - 1.5 * this.options.gridTextSize; + this.width = this.right - this.left; + this.height = this.bottom - this.top; this.dx = this.width / (this.xmax - this.xmin); this.dy = this.height / (this.ymax - this.ymin); - this.columns = (function() { - var _i, _len, _ref, _results; - _ref = this.xvals; - _results = []; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - x = _ref[_i]; - _results.push(this.transX(x)); - } - return _results; - }).call(this); - this.seriesCoords = []; - _ref = this.series; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - s = _ref[_i]; - scoords = []; - $.each(s, function(i, y) { - if (y === null) { - return scoords.push(null); - } else { - return scoords.push({ - x: _this.columns[i], - y: _this.transY(y) - }); - } - }); - this.seriesCoords.push(scoords); + if (this.calc) { + return this.calc(); } - return this.hoverMargins = $.map(this.columns.slice(1), function(x, i) { - return (x + _this.columns[i]) / 2; - }); } }; - Line.prototype.transX = function(x) { + Grid.prototype.transY = function(y) { + return this.bottom - (y - this.ymin) * this.dy; + }; + + Grid.prototype.transX = function(x) { if (this.xvals.length === 1) { - return this.left + this.width / 2; + return (this.left + this.right) / 2; } else { return this.left + (x - this.xmin) * this.dx; } }; - Line.prototype.transY = function(y) { - return this.options.marginTop + this.height - (y - this.ymin) * this.dy; - }; - - Line.prototype.redraw = function() { + Grid.prototype.redraw = function() { this.r.clear(); - this.calc(); + this._calc(); this.drawGrid(); - this.drawSeries(); - this.drawHover(); - return this.hilight(this.options.hideHover ? null : 0); + if (this.draw) { + return this.draw(); + } }; - Line.prototype.drawGrid = function() { - var drawLabel, firstY, i, l, labelText, lastY, lineY, prevLabelMargin, v, xLabelMargin, y, ypos, _i, _j, _k, _len, _ref, _ref1, _ref2, _results, _results1, - _this = this; + Grid.prototype.drawGrid = function() { + var firstY, lastY, lineY, v, y, _i, _ref, _results; firstY = this.ymin; lastY = this.ymax; + _results = []; for (lineY = _i = firstY, _ref = this.yInterval; firstY <= lastY ? _i <= lastY : _i >= lastY; lineY = _i += _ref) { v = parseFloat(lineY.toFixed(this.precision)); y = this.transY(v); - this.r.text(this.left - this.options.marginLeft / 2, y, this.yAxisFormat(v)).attr('font-size', this.options.gridTextSize).attr('fill', this.options.gridTextColor).attr('text-anchor', 'end'); - this.r.path("M" + this.left + "," + y + "H" + (this.left + this.width)).attr('stroke', this.options.gridLineColor).attr('stroke-width', this.options.gridStrokeWidth); - } - ypos = this.options.marginTop + this.height + this.options.marginBottom / 2; - xLabelMargin = 50; - 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.x >= 0 && (labelBox.x + labelBox.width) < _this.el.width()) { - return prevLabelMargin = labelBox.x + labelBox.width + xLabelMargin; - } else { - return label.remove(); - } - }; - if (this.options.parseTime) { - if (this.columnLabels.length === 1 && this.options.xLabels === 'auto') { - return drawLabel(this.columnLabels[0], this.xvals[0]); - } else { - _ref1 = Morris.labelSeries(this.xmin, this.xmax, this.width, this.options.xLabels, this.options.xLabelFormat); - _results = []; - for (_j = 0, _len = _ref1.length; _j < _len; _j++) { - l = _ref1[_j]; - _results.push(drawLabel(l[0], l[1])); - } - return _results; - } - } else { - _results1 = []; - for (i = _k = 0, _ref2 = this.columnLabels.length; 0 <= _ref2 ? _k <= _ref2 : _k >= _ref2; i = 0 <= _ref2 ? ++_k : --_k) { - labelText = this.columnLabels[this.columnLabels.length - i - 1]; - _results1.push(drawLabel(labelText, i)); - } - return _results1; - } - }; - - Line.prototype.drawSeries = function() { - var c, circle, coords, i, path, smooth, _i, _j, _ref, _ref1, _results; - for (i = _i = _ref = this.seriesCoords.length - 1; _ref <= 0 ? _i <= 0 : _i >= 0; i = _ref <= 0 ? ++_i : --_i) { - coords = $.map(this.seriesCoords[i], function(c) { - return c; - }); - smooth = this.options.smooth === true || $.inArray(this.options.ykeys[i], this.options.smooth) > -1; - if (coords.length > 1) { - path = this.createPath(coords, this.options.marginTop + this.height, smooth); - this.r.path(path).attr('stroke', this.colorForSeries(i)).attr('stroke-width', this.options.lineWidth); - } - } - this.seriesPoints = (function() { - var _j, _ref1, _results; - _results = []; - for (i = _j = 0, _ref1 = this.seriesCoords.length - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; i = 0 <= _ref1 ? ++_j : --_j) { - _results.push([]); - } - return _results; - }).call(this); - _results = []; - for (i = _j = _ref1 = this.seriesCoords.length - 1; _ref1 <= 0 ? _j <= 0 : _j >= 0; i = _ref1 <= 0 ? ++_j : --_j) { - _results.push((function() { - var _k, _len, _ref2, _results1; - _ref2 = this.seriesCoords[i]; - _results1 = []; - for (_k = 0, _len = _ref2.length; _k < _len; _k++) { - c = _ref2[_k]; - if (c === null) { - circle = null; - } else { - circle = this.r.circle(c.x, c.y, this.options.pointSize).attr('fill', this.pointFillColorForSeries(i) || this.colorForSeries(i)).attr('stroke-width', this.strokeWidthForSeries(i)).attr('stroke', this.strokeForSeries(i)); - } - _results1.push(this.seriesPoints[i].push(circle)); - } - return _results1; - }).call(this)); + 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.left + "," + y + "H" + (this.left + this.width)).attr('stroke', this.options.gridLineColor).attr('stroke-width', this.options.gridStrokeWidth)); } return _results; }; - Line.prototype.createPath = function(coords, bottom, smooth) { - var c, g, grads, i, ix, lc, lg, path, x1, x2, y1, y2, _i, _ref; - path = ""; - if (smooth) { - grads = this.gradients(coords); - for (i = _i = 0, _ref = coords.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { - c = coords[i]; - if (i === 0) { - path += "M" + c.x + "," + c.y; - } else { - g = grads[i]; - lc = coords[i - 1]; - lg = grads[i - 1]; - ix = (c.x - lc.x) / 4; - x1 = lc.x + ix; - y1 = Math.min(bottom, lc.y + ix * lg); - x2 = c.x - ix; - y2 = Math.min(bottom, c.y - ix * g); - path += "C" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + c.x + "," + c.y; - } - } - } else { - path = "M" + $.map(coords, function(c) { - return "" + c.x + "," + c.y; - }).join("L"); - } - return path; - }; - - Line.prototype.gradients = function(coords) { - return $.map(coords, function(c, i) { - if (i === 0) { - return (coords[1].y - c.y) / (coords[1].x - c.x); - } else if (i === (coords.length - 1)) { - return (c.y - coords[i - 1].y) / (c.x - coords[i - 1].x); - } else { - return (coords[i + 1].y - coords[i - 1].y) / (coords[i + 1].x - coords[i - 1].x); - } - }); - }; - - Line.prototype.drawHover = function() { - var i, yLabel, _i, _ref, _results; - this.hoverHeight = this.options.hoverFontSize * 1.5 * (this.series.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.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.hoverSet = this.r.set(); - this.hoverSet.push(this.hover); - this.hoverSet.push(this.xLabel); - this.yLabels = []; - _results = []; - for (i = _i = 0, _ref = this.series.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { - yLabel = this.r.text(0, this.options.hoverFontSize * 1.5 * (i + 1.5) - this.hoverHeight / 2, '').attr('fill', this.colorForSeries(i)).attr('font-size', this.options.hoverFontSize); - this.yLabels.push(yLabel); - _results.push(this.hoverSet.push(yLabel)); - } - return _results; - }; - - Line.prototype.updateHover = function(index) { - var i, maxLabelWidth, xloc, yloc, _i, _ref, - _this = this; - this.hoverSet.show(); - this.xLabel.attr('text', this.columnLabels[index]); - for (i = _i = 0, _ref = this.series.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { - this.yLabels[i].attr('text', "" + this.seriesLabels[i] + ": " + (this.yLabelFormat(this.series[i][index]))); - } - maxLabelWidth = Math.max.apply(null, $.map(this.yLabels, function(l) { - return l.getBBox().width; - })); - maxLabelWidth = Math.max(maxLabelWidth, this.xLabel.getBBox().width); - this.hover.attr('width', maxLabelWidth + this.options.hoverPaddingX * 2); - this.hover.attr('x', -this.options.hoverPaddingX - maxLabelWidth / 2); - yloc = Math.min.apply(null, $.map(this.series, function(s) { - return _this.transY(s[index]); - })); - if (yloc > this.hoverHeight + this.options.hoverPaddingY * 2 + this.options.hoverMargin + this.options.marginTop) { - 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.options.marginTop + this.hoverHeight / 2 + this.options.hoverPaddingY, yloc); - yloc = Math.min(this.options.marginTop + this.height - this.hoverHeight / 2 - this.options.hoverPaddingY, yloc); - xloc = Math.min(this.left + this.width - maxLabelWidth / 2 - this.options.hoverPaddingX, this.columns[index]); - xloc = Math.max(this.left + maxLabelWidth / 2 + this.options.hoverPaddingX, xloc); - return this.hoverSet.attr('transform', "t" + xloc + "," + yloc); - }; - - Line.prototype.hideHover = function() { - return this.hoverSet.hide(); - }; - - Line.prototype.hilight = function(index) { - var i, _i, _j, _ref, _ref1; - if (this.prevHilight !== null && this.prevHilight !== index) { - for (i = _i = 0, _ref = this.seriesPoints.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { - if (this.seriesPoints[i][this.prevHilight]) { - this.seriesPoints[i][this.prevHilight].animate(this.pointShrink); - } - } - } - if (index !== null && this.prevHilight !== index) { - for (i = _j = 0, _ref1 = this.seriesPoints.length - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; i = 0 <= _ref1 ? ++_j : --_j) { - if (this.seriesPoints[i][index]) { - this.seriesPoints[i][index].animate(this.pointGrow); - } - } - this.updateHover(index); - } - this.prevHilight = index; - if (index === null) { - return this.hideHover(); - } - }; - - Line.prototype.updateHilight = function(x) { - var hoverIndex, _i, _ref, _results; - x -= this.el.offset().left; - _results = []; - for (hoverIndex = _i = _ref = this.hoverMargins.length; _ref <= 0 ? _i <= 0 : _i >= 0; hoverIndex = _ref <= 0 ? ++_i : --_i) { - if (hoverIndex === 0 || this.hoverMargins[hoverIndex - 1] > x) { - this.hilight(hoverIndex); - break; - } else { - _results.push(void 0); - } - } - return _results; - }; - - Line.prototype.measureText = function(text, fontSize) { + Grid.prototype.measureText = function(text, fontSize) { var ret, tt; if (fontSize == null) { fontSize = 12; @@ -795,33 +506,17 @@ return ret; }; - Line.prototype.yAxisFormat = function(label) { + Grid.prototype.yAxisFormat = function(label) { return this.yLabelFormat(label); }; - Line.prototype.yLabelFormat = function(label) { + Grid.prototype.yLabelFormat = function(label) { return "" + this.options.preUnits + (Morris.commas(label)) + this.options.postUnits; }; - Line.prototype.colorForSeries = function(index) { - return this.options.lineColors[index % this.options.lineColors.length]; - }; + return Grid; - Line.prototype.strokeWidthForSeries = function(index) { - return this.options.pointWidths[index % this.options.pointWidths.length]; - }; - - Line.prototype.strokeForSeries = function(index) { - return this.options.pointStrokeColors[index % this.options.pointStrokeColors.length]; - }; - - Line.prototype.pointFillColorForSeries = function(index) { - return this.options.pointFillColors[index % this.options.pointFillColors.length]; - }; - - return Line; - - })(); + })(Morris.EventEmitter); Morris.parseDate = function(date) { var isecs, m, msecs, n, o, offsetmins, p, q, r, ret, secs; @@ -880,6 +575,342 @@ } }; + Morris.Line = (function(_super) { + + __extends(Line, _super); + + function Line(options) { + this.updateHilight = __bind(this.updateHilight, this); + + this.hilight = __bind(this.hilight, this); + + this.updateHover = __bind(this.updateHover, this); + if (!(this instanceof Morris.Line)) { + return new Morris.Line(options); + } + Line.__super__.constructor.call(this, options); + } + + Line.prototype.init = function() { + var touchHandler, + _this = this; + this.pointGrow = Raphael.animation({ + r: this.options.pointSize + 3 + }, 25, 'linear'); + this.pointShrink = Raphael.animation({ + r: this.options.pointSize + }, 25, 'linear'); + this.prevHilight = null; + this.el.mousemove(function(evt) { + return _this.updateHilight(evt.pageX); + }); + if (this.options.hideHover) { + this.el.mouseout(function(evt) { + return _this.hilight(null); + }); + } + touchHandler = function(evt) { + var touch; + touch = evt.originalEvent.touches[0] || evt.originalEvent.changedTouches[0]; + _this.updateHilight(touch.pageX); + return touch; + }; + this.el.bind('touchstart', touchHandler); + this.el.bind('touchmove', touchHandler); + return this.el.bind('touchend', touchHandler); + }; + + Line.prototype.defaults = { + lineWidth: 3, + pointSize: 4, + lineColors: ['#0b62a4', '#7A92A3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'], + pointWidths: [1], + pointStrokeColors: ['#ffffff'], + pointFillColors: [], + hoverPaddingX: 10, + hoverPaddingY: 5, + hoverMargin: 10, + hoverFillColor: '#fff', + hoverBorderColor: '#ccc', + hoverBorderWidth: 2, + hoverOpacity: 0.95, + hoverLabelColor: '#444', + hoverFontSize: 12, + smooth: true, + hideHover: false, + xLabels: 'auto', + xLabelFormat: null + }; + + Line.prototype.calc = function() { + var s, scoords, x, _i, _len, _ref, + _this = this; + this.columns = (function() { + var _i, _len, _ref, _results; + _ref = this.xvals; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + x = _ref[_i]; + _results.push(this.transX(x)); + } + return _results; + }).call(this); + this.seriesCoords = []; + _ref = this.series; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + s = _ref[_i]; + scoords = []; + $.each(s, function(i, y) { + if (y === null) { + return scoords.push(null); + } else { + return scoords.push({ + x: _this.columns[i], + y: _this.transY(y) + }); + } + }); + this.seriesCoords.push(scoords); + } + return this.hoverMargins = $.map(this.columns.slice(1), function(x, i) { + return (x + _this.columns[i]) / 2; + }); + }; + + Line.prototype.draw = function() { + this.drawXAxis(); + this.drawSeries(); + this.drawHover(); + return this.hilight(this.options.hideHover ? null : 0); + }; + + Line.prototype.drawXAxis = function() { + var drawLabel, i, l, labelText, prevLabelMargin, xLabelMargin, ypos, _i, _j, _len, _ref, _ref1, _results, _results1, + _this = this; + ypos = this.bottom + this.options.gridTextSize * 1.25; + xLabelMargin = 50; + 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.x >= 0 && (labelBox.x + labelBox.width) < _this.el.width()) { + return prevLabelMargin = labelBox.x + labelBox.width + xLabelMargin; + } else { + return label.remove(); + } + }; + if (this.options.parseTime) { + if (this.columnLabels.length === 1 && this.options.xLabels === 'auto') { + return drawLabel(this.columnLabels[0], this.xvals[0]); + } else { + _ref = Morris.labelSeries(this.xmin, this.xmax, this.width, this.options.xLabels, this.options.xLabelFormat); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + l = _ref[_i]; + _results.push(drawLabel(l[0], l[1])); + } + return _results; + } + } else { + _results1 = []; + for (i = _j = 0, _ref1 = this.columnLabels.length; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; i = 0 <= _ref1 ? ++_j : --_j) { + labelText = this.columnLabels[this.columnLabels.length - i - 1]; + _results1.push(drawLabel(labelText, i)); + } + return _results1; + } + }; + + Line.prototype.drawSeries = function() { + var c, circle, coords, i, path, smooth, _i, _j, _ref, _ref1, _results; + for (i = _i = _ref = this.seriesCoords.length - 1; _ref <= 0 ? _i <= 0 : _i >= 0; i = _ref <= 0 ? ++_i : --_i) { + coords = this.seriesCoords[i]; + smooth = this.options.smooth === true || $.inArray(this.options.ykeys[i], this.options.smooth) > -1; + if (coords.length > 1) { + path = this.createPath(coords, this.bottom, smooth); + this.r.path(path).attr('stroke', this.colorForSeries(i)).attr('stroke-width', this.options.lineWidth); + } + } + this.seriesPoints = (function() { + var _j, _ref1, _results; + _results = []; + for (i = _j = 0, _ref1 = this.seriesCoords.length - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; i = 0 <= _ref1 ? ++_j : --_j) { + _results.push([]); + } + return _results; + }).call(this); + _results = []; + for (i = _j = _ref1 = this.seriesCoords.length - 1; _ref1 <= 0 ? _j <= 0 : _j >= 0; i = _ref1 <= 0 ? ++_j : --_j) { + _results.push((function() { + var _k, _len, _ref2, _results1; + _ref2 = this.seriesCoords[i]; + _results1 = []; + for (_k = 0, _len = _ref2.length; _k < _len; _k++) { + c = _ref2[_k]; + if (c === null) { + circle = null; + } else { + circle = this.r.circle(c.x, c.y, this.options.pointSize).attr('fill', this.pointFillColorForSeries(i) || this.colorForSeries(i)).attr('stroke-width', this.strokeWidthForSeries(i)).attr('stroke', this.strokeForSeries(i)); + } + _results1.push(this.seriesPoints[i].push(circle)); + } + return _results1; + }).call(this)); + } + return _results; + }; + + Line.prototype.createPath = function(coords, smooth) { + var c, g, grads, i, ix, lc, lg, path, x1, x2, y1, y2, _i, _ref; + path = ""; + if (smooth) { + grads = this.gradients(coords); + for (i = _i = 0, _ref = coords.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { + c = coords[i]; + if (i === 0) { + path += "M" + c.x + "," + c.y; + } else { + g = grads[i]; + lc = coords[i - 1]; + lg = grads[i - 1]; + ix = (c.x - lc.x) / 4; + x1 = lc.x + ix; + y1 = Math.min(this.bottom, lc.y + ix * lg); + x2 = c.x - ix; + y2 = Math.min(this.bottom, c.y - ix * g); + path += "C" + x1 + "," + y1 + "," + x2 + "," + y2 + "," + c.x + "," + c.y; + } + } + } else { + path = "M" + $.map(coords, function(c) { + return "" + c.x + "," + c.y; + }).join("L"); + } + return path; + }; + + Line.prototype.gradients = function(coords) { + return $.map(coords, function(c, i) { + if (i === 0) { + return (coords[1].y - c.y) / (coords[1].x - c.x); + } else if (i === (coords.length - 1)) { + return (c.y - coords[i - 1].y) / (c.x - coords[i - 1].x); + } else { + return (coords[i + 1].y - coords[i - 1].y) / (coords[i + 1].x - coords[i - 1].x); + } + }); + }; + + Line.prototype.drawHover = function() { + var i, yLabel, _i, _ref, _results; + this.hoverHeight = this.options.hoverFontSize * 1.5 * (this.series.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.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.hoverSet = this.r.set(); + this.hoverSet.push(this.hover); + this.hoverSet.push(this.xLabel); + this.yLabels = []; + _results = []; + for (i = _i = 0, _ref = this.series.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { + yLabel = this.r.text(0, this.options.hoverFontSize * 1.5 * (i + 1.5) - this.hoverHeight / 2, '').attr('fill', this.colorForSeries(i)).attr('font-size', this.options.hoverFontSize); + this.yLabels.push(yLabel); + _results.push(this.hoverSet.push(yLabel)); + } + return _results; + }; + + Line.prototype.updateHover = function(index) { + var i, maxLabelWidth, xloc, yloc, _i, _ref, + _this = this; + this.hoverSet.show(); + this.xLabel.attr('text', this.columnLabels[index]); + for (i = _i = 0, _ref = this.series.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { + this.yLabels[i].attr('text', "" + this.options.labels[i] + ": " + (this.yLabelFormat(this.series[i][index]))); + } + maxLabelWidth = Math.max.apply(null, $.map(this.yLabels, function(l) { + return l.getBBox().width; + })); + maxLabelWidth = Math.max(maxLabelWidth, this.xLabel.getBBox().width); + this.hover.attr('width', maxLabelWidth + this.options.hoverPaddingX * 2); + this.hover.attr('x', -this.options.hoverPaddingX - maxLabelWidth / 2); + yloc = Math.min.apply(null, $.map(this.series, function(s) { + return _this.transY(s[index]); + })); + 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.columns[index]); + xloc = Math.max(this.left + maxLabelWidth / 2 + this.options.hoverPaddingX, xloc); + return this.hoverSet.attr('transform', "t" + xloc + "," + yloc); + }; + + Line.prototype.hideHover = function() { + return this.hoverSet.hide(); + }; + + Line.prototype.hilight = function(index) { + var i, _i, _j, _ref, _ref1; + if (this.prevHilight !== null && this.prevHilight !== index) { + for (i = _i = 0, _ref = this.seriesPoints.length - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; i = 0 <= _ref ? ++_i : --_i) { + if (this.seriesPoints[i][this.prevHilight]) { + this.seriesPoints[i][this.prevHilight].animate(this.pointShrink); + } + } + } + if (index !== null && this.prevHilight !== index) { + for (i = _j = 0, _ref1 = this.seriesPoints.length - 1; 0 <= _ref1 ? _j <= _ref1 : _j >= _ref1; i = 0 <= _ref1 ? ++_j : --_j) { + if (this.seriesPoints[i][index]) { + this.seriesPoints[i][index].animate(this.pointGrow); + } + } + this.updateHover(index); + } + this.prevHilight = index; + if (index === null) { + return this.hideHover(); + } + }; + + Line.prototype.updateHilight = function(x) { + var hoverIndex, _i, _ref, _results; + x -= this.el.offset().left; + _results = []; + for (hoverIndex = _i = _ref = this.hoverMargins.length; _ref <= 0 ? _i <= 0 : _i >= 0; hoverIndex = _ref <= 0 ? ++_i : --_i) { + if (hoverIndex === 0 || this.hoverMargins[hoverIndex - 1] > x) { + this.hilight(hoverIndex); + break; + } else { + _results.push(void 0); + } + } + return _results; + }; + + Line.prototype.colorForSeries = function(index) { + return this.options.lineColors[index % this.options.lineColors.length]; + }; + + Line.prototype.strokeWidthForSeries = function(index) { + return this.options.pointWidths[index % this.options.pointWidths.length]; + }; + + Line.prototype.strokeForSeries = function(index) { + return this.options.pointStrokeColors[index % this.options.pointStrokeColors.length]; + }; + + Line.prototype.pointFillColorForSeries = function(index) { + return this.options.pointFillColors[index % this.options.pointFillColors.length]; + }; + + return Line; + + })(Morris.Grid); + Morris.labelSeries = function(dmin, dmax, pxwidth, specName, xLabelFormat) { var d, d0, ddensity, name, ret, s, spec, t, _i, _len, _ref; ddensity = 200 * (dmax - dmin) / pxwidth; diff --git a/morris.min.js b/morris.min.js index 5601777..3d5bdd6 100644 --- a/morris.min.js +++ b/morris.min.js @@ -1 +1 @@ -(function(){var e,t,n,r,i=[].slice,s=function(e,t){return function(){return e.apply(t,arguments)}},o={}.hasOwnProperty,u=function(e,t){function r(){this.constructor=e}for(var n in t)o.call(t,n)&&(e[n]=t[n]);return r.prototype=t.prototype,e.prototype=new r,e.__super__=t.prototype,e};t=window.Morris={},e=jQuery,t.EventEmitter=function(){function e(){}return e.prototype.on=function(e,t){return this.handlers==null&&(this.handlers={}),this.handlers[e]==null&&(this.handlers[e]=[]),this.handlers[e].push(t)},e.prototype.fire=function(){var e,t,n,r,s,o,u;n=arguments[0],e=2<=arguments.length?i.call(arguments,1):[];if(this.handlers!=null&&this.handlers[n]!=null){o=this.handlers[n],u=[];for(r=0,s=o.length;rn.length&&(r+=i.slice(n.length)),r)},t.pad2=function(e){return(e<10?"0":"")+e},t.Donut=function(){function n(n){this.select=s(this.select,this);if(!(this instanceof t.Donut))return new t.Donut(n);typeof n.element=="string"?this.el=e(document.getElementById(n.element)):this.el=e(n.element),this.options=e.extend({},this.defaults,n);if(this.el===null||this.el.length===0)throw new Error("Graph placeholder not found.");if(n.data===void 0||n.data.length===0)return;this.data=n.data,this.el.addClass("graph-initialised"),this.redraw()}return n.prototype.defaults={colors:["#0B62A4","#3980B5","#679DC6","#95BBD7","#B0CCE1","#095791","#095085","#083E67","#052C48","#042135"],formatter:t.commas},n.prototype.redraw=function(){var e,n,r,i,s,o,u,a,f,l,c,h,p,d,v,m,g,y,b,w,E,S,x;this.el.empty(),this.r=new Raphael(this.el[0]),n=this.el.width()/2,r=this.el.height()/2,h=(Math.min(n,r)-10)/3,c=0,w=this.data;for(d=0,g=w.length;dMath.PI?1:0,this.path=this.calcSegment(this.inner+3,this.inner+this.outer-5),this.selectedPath=this.calcSegment(this.inner+3,this.inner+this.outer),this.hilight=this.calcArc(this.inner)}return u(t,e),t.prototype.calcArcPoints=function(e){return[this.cx+e*this.sin_p0,this.cy+e*this.cos_p0,this.cx+e*this.sin_p1,this.cy+e*this.cos_p1]},t.prototype.calcSegment=function(e,t){var n,r,i,s,o,u,a,f,l,c;return l=this.calcArcPoints(e),n=l[0],i=l[1],r=l[2],s=l[3],c=this.calcArcPoints(t),o=c[0],a=c[1],u=c[2],f=c[3],"M"+n+","+i+("A"+e+","+e+",0,"+this.long+",0,"+r+","+s)+("L"+u+","+f)+("A"+t+","+t+",0,"+this.long+",1,"+o+","+a)+"Z"},t.prototype.calcArc=function(e){var t,n,r,i,s;return s=this.calcArcPoints(e),t=s[0],r=s[1],n=s[2],i=s[3],"M"+t+","+r+("A"+e+","+e+",0,"+this.long+",0,"+n+","+i)},t.prototype.render=function(e){var t=this;return this.arc=e.path(this.hilight).attr({stroke:this.color,"stroke-width":2,opacity:0}),this.seg=e.path(this.path).attr({fill:this.color,stroke:"white","stroke-width":3}).hover(function(){return t.fire("hover",t)})},t.prototype.select=function(){if(!this.selected)return this.seg.animate({path:this.selectedPath},150,"<>"),this.arc.animate({opacity:1},150,"<>"),this.selected=!0},t.prototype.deselect=function(){if(this.selected)return this.seg.animate({path:this.path},150,"<>"),this.arc.animate({opacity:0},150,"<>"),this.selected=!1},t}(t.EventEmitter),t.Line=function(){function n(n){this.updateHilight=s(this.updateHilight,this),this.hilight=s(this.hilight,this),this.updateHover=s(this.updateHover,this),this.transY=s(this.transY,this),this.transX=s(this.transX,this);var r,i=this;if(!(this instanceof t.Line))return new t.Line(n);typeof n.element=="string"?this.el=e(document.getElementById(n.element)):this.el=e(n.element);if(this.el===null||this.el.length===0)throw new Error("Graph placeholder not found.");this.options=e.extend({},this.defaults,n),typeof this.options.units=="string"&&(this.options.postUnits=n.units);if(this.options.data===void 0||this.options.data.length===0)return;this.el.addClass("graph-initialised"),this.r=new Raphael(this.el[0]),this.pointGrow=Raphael.animation({r:this.options.pointSize+3},25,"linear"),this.pointShrink=Raphael.animation({r:this.options.pointSize},25,"linear"),this.elementWidth=null,this.elementHeight=null,this.dirty=!1,this.prevHilight=null,this.el.mousemove(function(e){return i.updateHilight(e.pageX)}),this.options.hideHover&&this.el.mouseout(function(e){return i.hilight(null)}),r=function(e){var t;return t=e.originalEvent.touches[0]||e.originalEvent.changedTouches[0],i.updateHilight(t.pageX),t},this.el.bind("touchstart",r),this.el.bind("touchmove",r),this.el.bind("touchend",r),this.seriesLabels=this.options.labels,this.setData(this.options.data)}return n.prototype.defaults={lineWidth:3,pointSize:4,lineColors:["#0b62a4","#7A92A3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],pointWidths:[1],pointStrokeColors:["#ffffff"],pointFillColors:[],ymax:"auto",ymin:"auto 0",marginTop:25,marginRight:25,marginBottom:30,marginLeft:25,numLines:5,gridLineColor:"#aaa",gridTextColor:"#888",gridTextSize:12,gridStrokeWidth:.5,hoverPaddingX:10,hoverPaddingY:5,hoverMargin:10,hoverFillColor:"#fff",hoverBorderColor:"#ccc",hoverBorderWidth:2,hoverOpacity:.95,hoverLabelColor:"#444",hoverFontSize:12,smooth:!0,hideHover:!1,parseTime:!0,preUnits:"",postUnits:"",dateFormat:null,xLabels:"auto",xLabelFormat:null},n.prototype.setData=function(n,r){var i,s,o,u,a,f,l,c,h,p,d,v,m,g,y=this;r==null&&(r=!0),this.options.data=n.slice(0),this.options.parseTime?this.options.data.sort(function(e,t){return(e[y.options.xkey]=0;m<=0?e++:e--)g.push(e);return g}.apply(this),this.options.parseTime&&(this.options.dateFormat?this.columnLabels=e.map(this.xvals,function(e){return y.options.dateFormat(e)}):this.columnLabels=e.map(this.columnLabels,function(e){return typeof e=="number"?(new Date(e)).toString():e})),this.xmin=Math.min.apply(null,this.xvals),this.xmax=Math.max.apply(null,this.xvals),this.xmin===this.xmax&&(this.xmin-=1,this.xmax+=1),typeof this.options.ymax=="string"&&this.options.ymax.slice(0,4)==="auto"?(u=Math.max.apply(null,Array.prototype.concat.apply([],this.series)),this.options.ymax.length>5?this.ymax=Math.max(parseInt(this.options.ymax.slice(5),10),u):this.ymax=u):typeof this.options.ymax=="string"?this.ymax=parseInt(this.options.ymax,10):this.ymax=this.options.ymax,typeof this.options.ymin=="string"&&this.options.ymin.slice(0,4)==="auto"?(a=Math.min.apply(null,Array.prototype.concat.apply([],this.series)),this.options.ymin.length>5?this.ymin=Math.min(parseInt(this.options.ymin.slice(5),10),a):this.ymin=a):typeof this.options.ymin=="string"?this.ymin=parseInt(this.options.ymin,10):this.ymin=this.options.ymin,this.ymin===this.ymax&&(this.ymin===!0&&(this.ymin-=1),this.ymax+=1),this.yInterval=(this.ymax-this.ymin)/(this.options.numLines-1),this.yInterval>0&&this.yInterval<1?this.precision=-Math.floor(Math.log(this.yInterval)/Math.log(10)):this.precision=0,this.dirty=!0;if(r)return this.redraw()},n.prototype.calc=function(){var t,n,r,i,s,o,u,a,f=this;i=this.el.width(),t=this.el.height();if(this.elementWidth!==i||this.elementHeight!==t||this.dirty){this.elementWidth=i,this.elementHeight=t,this.dirty=!1,this.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=this.maxYLabelWidth+this.options.marginLeft,this.width=this.el.width()-this.left-this.options.marginRight,this.height=this.el.height()-this.options.marginTop-this.options.marginBottom,this.dx=this.width/(this.xmax-this.xmin),this.dy=this.height/(this.ymax-this.ymin),this.columns=function(){var e,t,n,r;n=this.xvals,r=[];for(e=0,t=n.length;e=o;u=p+=g)f=parseFloat(u.toFixed(this.precision)),c=this.transY(f),this.r.text(this.left-this.options.marginLeft/2,c,this.yAxisFormat(f)).attr("font-size",this.options.gridTextSize).attr("fill",this.options.gridTextColor).attr("text-anchor","end"),this.r.path("M"+this.left+","+c+"H"+(this.left+this.width)).attr("stroke",this.options.gridLineColor).attr("stroke-width",this.options.gridStrokeWidth);h=this.options.marginTop+this.height+this.options.marginBottom/2,l=50,a=null,e=function(e,t){var n,r;return n=S.r.text(S.transX(t),h,e).attr("font-size",S.options.gridTextSize).attr("fill",S.options.gridTextColor),r=n.getBBox(),(a===null||a<=r.x)&&r.x>=0&&r.x+r.width=b;r=0<=b?++v:--v)s=this.columnLabels[this.columnLabels.length-r-1],E.push(e(s,r));return E},n.prototype.drawSeries=function(){var t,n,r,i,s,o,u,a,f,l,c;for(i=u=f=this.seriesCoords.length-1;f<=0?u<=0:u>=0;i=f<=0?++u:--u)r=e.map(this.seriesCoords[i],function(e){return e}),o=this.options.smooth===!0||e.inArray(this.options.ykeys[i],this.options.smooth)>-1,r.length>1&&(s=this.createPath(r,this.options.marginTop+this.height,o),this.r.path(s).attr("stroke",this.colorForSeries(i)).attr("stroke-width",this.options.lineWidth));this.seriesPoints=function(){var e,t,n;n=[];for(i=e=0,t=this.seriesCoords.length-1;0<=t?e<=t:e>=t;i=0<=t?++e:--e)n.push([]);return n}.call(this),c=[];for(i=a=l=this.seriesCoords.length-1;l<=0?a<=0:a>=0;i=l<=0?++a:--a)c.push(function(){var e,r,s,o;s=this.seriesCoords[i],o=[];for(e=0,r=s.length;e=g;u=0<=g?++m:--m)i=t[u],u===0?c+="M"+i.x+","+i.y:(s=o[u],f=t[u-1],l=o[u-1],a=(i.x-f.x)/4,h=f.x+a,d=Math.min(n,f.y+a*l),p=i.x-a,v=Math.min(n,i.y-a*s),c+="C"+h+","+d+","+p+","+v+","+i.x+","+i.y)}else c="M"+e.map(t,function(e){return""+e.x+","+e.y}).join("L");return c},n.prototype.gradients=function(t){return e.map(t,function(e,n){return n===0?(t[1].y-e.y)/(t[1].x-e.x):n===t.length-1?(e.y-t[n-1].y)/(e.x-t[n-1].x):(t[n+1].y-t[n-1].y)/(t[n+1].x-t[n-1].x)})},n.prototype.drawHover=function(){var e,t,n,r,i;this.hoverHeight=this.options.hoverFontSize*1.5*(this.series.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.xLabel=this.r.text(0,this.options.hoverFontSize*.75-this.hoverHeight/2,"").attr("fill",this.options.hoverLabelColor).attr("font-weight","bold").attr("font-size",this.options.hoverFontSize),this.hoverSet=this.r.set(),this.hoverSet.push(this.hover),this.hoverSet.push(this.xLabel),this.yLabels=[],i=[];for(e=n=0,r=this.series.length-1;0<=r?n<=r:n>=r;e=0<=r?++n:--n)t=this.r.text(0,this.options.hoverFontSize*1.5*(e+1.5)-this.hoverHeight/2,"").attr("fill",this.colorForSeries(e)).attr("font-size",this.options.hoverFontSize),this.yLabels.push(t),i.push(this.hoverSet.push(t));return i},n.prototype.updateHover=function(t){var n,r,i,s,o,u,a=this;this.hoverSet.show(),this.xLabel.attr("text",this.columnLabels[t]);for(n=o=0,u=this.series.length-1;0<=u?o<=u:o>=u;n=0<=u?++o:--o)this.yLabels[n].attr("text",""+this.seriesLabels[n]+": "+this.yLabelFormat(this.series[n][t]));return r=Math.max.apply(null,e.map(this.yLabels,function(e){return e.getBBox().width})),r=Math.max(r,this.xLabel.getBBox().width),this.hover.attr("width",r+this.options.hoverPaddingX*2),this.hover.attr("x",-this.options.hoverPaddingX-r/2),s=Math.min.apply(null,e.map(this.series,function(e){return a.transY(e[t])})),s>this.hoverHeight+this.options.hoverPaddingY*2+this.options.hoverMargin+this.options.marginTop?s=s-this.hoverHeight/2-this.options.hoverPaddingY-this.options.hoverMargin:s=s+this.hoverHeight/2+this.options.hoverPaddingY+this.options.hoverMargin,s=Math.max(this.options.marginTop+this.hoverHeight/2+this.options.hoverPaddingY,s),s=Math.min(this.options.marginTop+this.height-this.hoverHeight/2-this.options.hoverPaddingY,s),i=Math.min(this.left+this.width-r/2-this.options.hoverPaddingX,this.columns[t]),i=Math.max(this.left+r/2+this.options.hoverPaddingX,i),this.hoverSet.attr("transform","t"+i+","+s)},n.prototype.hideHover=function(){return this.hoverSet.hide()},n.prototype.hilight=function(e){var t,n,r,i,s;if(this.prevHilight!==null&&this.prevHilight!==e)for(t=n=0,i=this.seriesPoints.length-1;0<=i?n<=i:n>=i;t=0<=i?++n:--n)this.seriesPoints[t][this.prevHilight]&&this.seriesPoints[t][this.prevHilight].animate(this.pointShrink);if(e!==null&&this.prevHilight!==e){for(t=r=0,s=this.seriesPoints.length-1;0<=s?r<=s:r>=s;t=0<=s?++r:--r)this.seriesPoints[t][e]&&this.seriesPoints[t][e].animate(this.pointGrow);this.updateHover(e)}this.prevHilight=e;if(e===null)return this.hideHover()},n.prototype.updateHilight=function(e){var t,n,r,i;e-=this.el.offset().left,i=[];for(t=n=r=this.hoverMargins.length;r<=0?n<=0:n>=0;t=r<=0?++n:--n){if(t===0||this.hoverMargins[t-1]>e){this.hilight(t);break}i.push(void 0)}return i},n.prototype.measureText=function(e,t){var n,r;return t==null&&(t=12),r=this.r.text(100,100,e).attr("font-size",t),n=r.getBBox(),r.remove(),n},n.prototype.yAxisFormat=function(e){return this.yLabelFormat(e)},n.prototype.yLabelFormat=function(e){return""+this.options.preUnits+t.commas(e)+this.options.postUnits},n.prototype.colorForSeries=function(e){return this.options.lineColors[e%this.options.lineColors.length]},n.prototype.strokeWidthForSeries=function(e){return this.options.pointWidths[e%this.options.pointWidths.length]},n.prototype.strokeForSeries=function(e){return this.options.pointStrokeColors[e%this.options.pointStrokeColors.length]},n.prototype.pointFillColorForSeries=function(e){return this.options.pointFillColors[e%this.options.pointFillColors.length]},n}(),t.parseDate=function(e){var t,n,r,i,s,o,u,a,f,l,c;return typeof e=="number"?e:(n=e.match(/^(\d+) Q(\d)$/),i=e.match(/^(\d+)-(\d+)$/),s=e.match(/^(\d+)-(\d+)-(\d+)$/),u=e.match(/^(\d+) W(\d+)$/),a=e.match(/^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+)(Z|([+-])(\d\d):?(\d\d))?$/),f=e.match(/^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+):(\d+(\.\d+)?)(Z|([+-])(\d\d):?(\d\d))?$/),n?(new Date(parseInt(n[1],10),parseInt(n[2],10)*3-1,1)).getTime():i?(new Date(parseInt(i[1],10),parseInt(i[2],10)-1,1)).getTime():s?(new Date(parseInt(s[1],10),parseInt(s[2],10)-1,parseInt(s[3],10))).getTime():u?(l=new Date(parseInt(u[1],10),0,1),l.getDay()!==4&&l.setMonth(0,1+(4-l.getDay()+7)%7),l.getTime()+parseInt(u[2],10)*6048e5):a?a[6]?(o=0,a[6]!=="Z"&&(o=parseInt(a[8],10)*60+parseInt(a[9],10),a[7]==="+"&&(o=0-o)),Date.UTC(parseInt(a[1],10),parseInt(a[2],10)-1,parseInt(a[3],10),parseInt(a[4],10),parseInt(a[5],10)+o)):(new Date(parseInt(a[1],10),parseInt(a[2],10)-1,parseInt(a[3],10),parseInt(a[4],10),parseInt(a[5],10))).getTime():f?(c=parseFloat(f[6]),t=Math.floor(c),r=Math.round((c-t)*1e3),f[8]?(o=0,f[8]!=="Z"&&(o=parseInt(f[10],10)*60+parseInt(f[11],10),f[9]==="+"&&(o=0-o)),Date.UTC(parseInt(f[1],10),parseInt(f[2],10)-1,parseInt(f[3],10),parseInt(f[4],10),parseInt(f[5],10)+o,t,r)):(new Date(parseInt(f[1],10),parseInt(f[2],10)-1,parseInt(f[3],10),parseInt(f[4],10),parseInt(f[5],10),t,r)).getTime()):(new Date(parseInt(e,10),0,1)).getTime())},t.labelSeries=function(n,r,i,s,o){var u,a,f,l,c,h,p,d,v,m,g;f=200*(r-n)/i,a=new Date(n),p=t.LABEL_SPECS[s];if(p===void 0){g=t.AUTO_LABEL_ORDER;for(v=0,m=g.length;v=h.span){p=h;break}}}p===void 0&&(p=t.LABEL_SPECS.second),o&&(p=e.extend({},p,{fmt:o})),u=p.start(a),c=[];while((d=u.getTime())<=r)d>=n&&c.push([p.fmt(u),d]),p.incr(u);return c},n=function(e){return{span:e*60*1e3,start:function(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate(),e.getHours())},fmt:function(e){return""+t.pad2(e.getHours())+":"+t.pad2(e.getMinutes())},incr:function(t){return t.setMinutes(t.getMinutes()+e)}}},r=function(e){return{span:e*1e3,start:function(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate(),e.getHours(),e.getMinutes())},fmt:function(e){return""+t.pad2(e.getHours())+":"+t.pad2(e.getMinutes())+":"+t.pad2(e.getSeconds())},incr:function(t){return t.setSeconds(t.getSeconds()+e)}}},t.LABEL_SPECS={year:{span:1728e7,start:function(e){return new Date(e.getFullYear(),0,1)},fmt:function(e){return""+e.getFullYear()},incr:function(e){return e.setFullYear(e.getFullYear()+1)}},month:{span:24192e5,start:function(e){return new Date(e.getFullYear(),e.getMonth(),1)},fmt:function(e){return""+e.getFullYear()+"-"+t.pad2(e.getMonth()+1)},incr:function(e){return e.setMonth(e.getMonth()+1)}},day:{span:864e5,start:function(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate())},fmt:function(e){return""+e.getFullYear()+"-"+t.pad2(e.getMonth()+1)+"-"+t.pad2(e.getDate())},incr:function(e){return e.setDate(e.getDate()+1)}},hour:n(60),"30min":n(30),"15min":n(15),"10min":n(10),"5min":n(5),minute:n(1),"30sec":r(30),"15sec":r(15),"10sec":r(10),"5sec":r(5),second:r(1)},t.AUTO_LABEL_ORDER=["year","month","day","hour","30min","15min","10min","5min","minute","30sec","15sec","10sec","5sec","second"]}).call(this); \ No newline at end of file +(function(){var e,t,n,r,i=[].slice,s=function(e,t){return function(){return e.apply(t,arguments)}},o={}.hasOwnProperty,u=function(e,t){function r(){this.constructor=e}for(var n in t)o.call(t,n)&&(e[n]=t[n]);return r.prototype=t.prototype,e.prototype=new r,e.__super__=t.prototype,e};t=window.Morris={},e=jQuery,t.EventEmitter=function(){function e(){}return e.prototype.on=function(e,t){return this.handlers==null&&(this.handlers={}),this.handlers[e]==null&&(this.handlers[e]=[]),this.handlers[e].push(t)},e.prototype.fire=function(){var e,t,n,r,s,o,u;n=arguments[0],e=2<=arguments.length?i.call(arguments,1):[];if(this.handlers!=null&&this.handlers[n]!=null){o=this.handlers[n],u=[];for(r=0,s=o.length;rn.length&&(r+=i.slice(n.length)),r)},t.pad2=function(e){return(e<10?"0":"")+e},t.Donut=function(){function n(n){this.select=s(this.select,this);if(!(this instanceof t.Donut))return new t.Donut(n);typeof n.element=="string"?this.el=e(document.getElementById(n.element)):this.el=e(n.element),this.options=e.extend({},this.defaults,n);if(this.el===null||this.el.length===0)throw new Error("Graph placeholder not found.");if(n.data===void 0||n.data.length===0)return;this.data=n.data,this.el.addClass("graph-initialised"),this.redraw()}return n.prototype.defaults={colors:["#0B62A4","#3980B5","#679DC6","#95BBD7","#B0CCE1","#095791","#095085","#083E67","#052C48","#042135"],formatter:t.commas},n.prototype.redraw=function(){var e,n,r,i,s,o,u,a,f,l,c,h,p,d,v,m,g,y,b,w,E,S,x;this.el.empty(),this.r=new Raphael(this.el[0]),n=this.el.width()/2,r=this.el.height()/2,h=(Math.min(n,r)-10)/3,c=0,w=this.data;for(d=0,g=w.length;dMath.PI?1:0,this.path=this.calcSegment(this.inner+3,this.inner+this.outer-5),this.selectedPath=this.calcSegment(this.inner+3,this.inner+this.outer),this.hilight=this.calcArc(this.inner)}return u(t,e),t.prototype.calcArcPoints=function(e){return[this.cx+e*this.sin_p0,this.cy+e*this.cos_p0,this.cx+e*this.sin_p1,this.cy+e*this.cos_p1]},t.prototype.calcSegment=function(e,t){var n,r,i,s,o,u,a,f,l,c;return l=this.calcArcPoints(e),n=l[0],i=l[1],r=l[2],s=l[3],c=this.calcArcPoints(t),o=c[0],a=c[1],u=c[2],f=c[3],"M"+n+","+i+("A"+e+","+e+",0,"+this.long+",0,"+r+","+s)+("L"+u+","+f)+("A"+t+","+t+",0,"+this.long+",1,"+o+","+a)+"Z"},t.prototype.calcArc=function(e){var t,n,r,i,s;return s=this.calcArcPoints(e),t=s[0],r=s[1],n=s[2],i=s[3],"M"+t+","+r+("A"+e+","+e+",0,"+this.long+",0,"+n+","+i)},t.prototype.render=function(e){var t=this;return this.arc=e.path(this.hilight).attr({stroke:this.color,"stroke-width":2,opacity:0}),this.seg=e.path(this.path).attr({fill:this.color,stroke:"white","stroke-width":3}).hover(function(){return t.fire("hover",t)})},t.prototype.select=function(){if(!this.selected)return this.seg.animate({path:this.selectedPath},150,"<>"),this.arc.animate({opacity:1},150,"<>"),this.selected=!0},t.prototype.deselect=function(){if(this.selected)return this.seg.animate({path:this.path},150,"<>"),this.arc.animate({opacity:0},150,"<>"),this.selected=!1},t}(t.EventEmitter),t.Grid=function(n){function r(t){typeof t.element=="string"?this.el=e(document.getElementById(t.element)):this.el=e(t.element);if(this.el===null||this.el.length===0)throw new Error("Graph container element not found");this.options=e.extend({},this.gridDefaults,this.defaults||{},t);if(this.options.data===void 0||this.options.data.length===0)return;typeof this.options.units=="string"&&(this.options.postUnits=t.units),this.r=new Raphael(this.el[0]),this.elementWidth=null,this.elementHeight=null,this.dirty=!1,this.init&&this.init(),this.setData(this.options.data)}return u(r,n),r.prototype.gridDefaults={dateFormat:null,gridLineColor:"#aaa",gridStrokeWidth:.5,gridTextColor:"#888",gridTextSize:12,numLines:5,padding:25,parseTime:!0,postUnits:"",preUnits:"",ymax:"auto",ymin:"auto 0"},r.prototype.setData=function(n,r){var i,s,o,u,a,f,l,c,h,p,d,v,m,g,y,b=this;r==null&&(r=!0),this.options.data=n.slice(),this.parseTime&&(this.options.data=this.options.data.sort(function(e,t){return(e[b.options.xkey]t;0<=t?e++:e--)y.push(e);return y}.apply(this),this.xmin=Math.min.apply(null,this.xvals),this.xmax=Math.max.apply(null,this.xvals),this.xmin===this.xmax&&(this.xmin-=1,this.xmax+=1),typeof this.options.ymax=="string"&&this.options.ymax.slice(0,4)==="auto"?(a=Math.max.apply(null,Array.prototype.concat.apply([],this.series)),this.options.ymax.length>5?this.ymax=Math.max(parseInt(this.options.ymax.slice(5),10),a):this.ymax=a):typeof this.options.ymax=="string"?this.ymax=parseInt(this.options.ymax,10):this.ymax=this.options.ymax,typeof this.options.ymin=="string"&&this.options.ymin.slice(0,4)==="auto"?(f=Math.min.apply(null,Array.prototype.concat.apply([],this.series)),this.options.ymin.length>5?this.ymin=Math.min(parseInt(this.options.ymin.slice(5),10),f):this.ymin=f):typeof this.options.ymin=="string"?this.ymin=parseInt(this.options.ymin,10):this.ymin=this.options.ymin,this.ymin===this.ymax&&(this.ymin===!0&&(this.ymin-=1),this.ymax+=1),this.yInterval=(this.ymax-this.ymin)/(this.options.numLines-1),this.yInterval>0&&this.yInterval<1?this.precision=-Math.floor(Math.log(this.yInterval)/Math.log(10)):this.precision=0,this.dirty=!0;if(r)return this.redraw()},r.prototype._calc=function(){var e,t,n;n=this.el.width(),e=this.el.height();if(this.elementWidth!==n||this.elementHeight!==e||this.dirty){this.elementWidth=n,this.elementHeight=e,this.dirty=!1,t=Math.max(this.measureText(this.yAxisFormat(this.ymin),this.options.gridTextSize).width,this.measureText(this.yAxisFormat(this.ymax),this.options.gridTextSize).width),this.left=t+this.options.padding,this.right=this.elementWidth-this.options.padding,this.top=this.options.padding,this.bottom=this.elementHeight-this.options.padding-1.5*this.options.gridTextSize,this.width=this.right-this.left,this.height=this.bottom-this.top,this.dx=this.width/(this.xmax-this.xmin),this.dy=this.height/(this.ymax-this.ymin);if(this.calc)return this.calc()}},r.prototype.transY=function(e){return this.bottom-(e-this.ymin)*this.dy},r.prototype.transX=function(e){return this.xvals.length===1?(this.left+this.right)/2:this.left+(e-this.xmin)*this.dx},r.prototype.redraw=function(){this.r.clear(),this._calc(),this.drawGrid();if(this.draw)return this.draw()},r.prototype.drawGrid=function(){var e,t,n,r,i,s,o,u;e=this.ymin,t=this.ymax,u=[];for(n=s=e,o=this.yInterval;e<=t?s<=t:s>=t;n=s+=o)r=parseFloat(n.toFixed(this.precision)),i=this.transY(r),this.r.text(this.left-this.options.padding/2,i,this.yAxisFormat(r)).attr("font-size",this.options.gridTextSize).attr("fill",this.options.gridTextColor).attr("text-anchor","end"),u.push(this.r.path("M"+this.left+","+i+"H"+(this.left+this.width)).attr("stroke",this.options.gridLineColor).attr("stroke-width",this.options.gridStrokeWidth));return u},r.prototype.measureText=function(e,t){var n,r;return t==null&&(t=12),r=this.r.text(100,100,e).attr("font-size",t),n=r.getBBox(),r.remove(),n},r.prototype.yAxisFormat=function(e){return this.yLabelFormat(e)},r.prototype.yLabelFormat=function(e){return""+this.options.preUnits+t.commas(e)+this.options.postUnits},r}(t.EventEmitter),t.parseDate=function(e){var t,n,r,i,s,o,u,a,f,l,c;return typeof e=="number"?e:(n=e.match(/^(\d+) Q(\d)$/),i=e.match(/^(\d+)-(\d+)$/),s=e.match(/^(\d+)-(\d+)-(\d+)$/),u=e.match(/^(\d+) W(\d+)$/),a=e.match(/^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+)(Z|([+-])(\d\d):?(\d\d))?$/),f=e.match(/^(\d+)-(\d+)-(\d+)[ T](\d+):(\d+):(\d+(\.\d+)?)(Z|([+-])(\d\d):?(\d\d))?$/),n?(new Date(parseInt(n[1],10),parseInt(n[2],10)*3-1,1)).getTime():i?(new Date(parseInt(i[1],10),parseInt(i[2],10)-1,1)).getTime():s?(new Date(parseInt(s[1],10),parseInt(s[2],10)-1,parseInt(s[3],10))).getTime():u?(l=new Date(parseInt(u[1],10),0,1),l.getDay()!==4&&l.setMonth(0,1+(4-l.getDay()+7)%7),l.getTime()+parseInt(u[2],10)*6048e5):a?a[6]?(o=0,a[6]!=="Z"&&(o=parseInt(a[8],10)*60+parseInt(a[9],10),a[7]==="+"&&(o=0-o)),Date.UTC(parseInt(a[1],10),parseInt(a[2],10)-1,parseInt(a[3],10),parseInt(a[4],10),parseInt(a[5],10)+o)):(new Date(parseInt(a[1],10),parseInt(a[2],10)-1,parseInt(a[3],10),parseInt(a[4],10),parseInt(a[5],10))).getTime():f?(c=parseFloat(f[6]),t=Math.floor(c),r=Math.round((c-t)*1e3),f[8]?(o=0,f[8]!=="Z"&&(o=parseInt(f[10],10)*60+parseInt(f[11],10),f[9]==="+"&&(o=0-o)),Date.UTC(parseInt(f[1],10),parseInt(f[2],10)-1,parseInt(f[3],10),parseInt(f[4],10),parseInt(f[5],10)+o,t,r)):(new Date(parseInt(f[1],10),parseInt(f[2],10)-1,parseInt(f[3],10),parseInt(f[4],10),parseInt(f[5],10),t,r)).getTime()):(new Date(parseInt(e,10),0,1)).getTime())},t.Line=function(n){function r(e){this.updateHilight=s(this.updateHilight,this),this.hilight=s(this.hilight,this),this.updateHover=s(this.updateHover,this);if(!(this instanceof t.Line))return new t.Line(e);r.__super__.constructor.call(this,e)}return u(r,n),r.prototype.init=function(){var e,t=this;return this.pointGrow=Raphael.animation({r:this.options.pointSize+3},25,"linear"),this.pointShrink=Raphael.animation({r:this.options.pointSize},25,"linear"),this.prevHilight=null,this.el.mousemove(function(e){return t.updateHilight(e.pageX)}),this.options.hideHover&&this.el.mouseout(function(e){return t.hilight(null)}),e=function(e){var n;return n=e.originalEvent.touches[0]||e.originalEvent.changedTouches[0],t.updateHilight(n.pageX),n},this.el.bind("touchstart",e),this.el.bind("touchmove",e),this.el.bind("touchend",e)},r.prototype.defaults={lineWidth:3,pointSize:4,lineColors:["#0b62a4","#7A92A3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],pointWidths:[1],pointStrokeColors:["#ffffff"],pointFillColors:[],hoverPaddingX:10,hoverPaddingY:5,hoverMargin:10,hoverFillColor:"#fff",hoverBorderColor:"#ccc",hoverBorderWidth:2,hoverOpacity:.95,hoverLabelColor:"#444",hoverFontSize:12,smooth:!0,hideHover:!1,xLabels:"auto",xLabelFormat:null},r.prototype.calc=function(){var t,n,r,i,s,o,u=this;this.columns=function(){var e,t,n,i;n=this.xvals,i=[];for(e=0,t=n.length;e=0&&r.x+r.width=h;n=0<=h?++f:--f)i=this.columnLabels[this.columnLabels.length-n-1],d.push(e(i,n));return d},r.prototype.drawSeries=function(){var t,n,r,i,s,o,u,a,f,l,c;for(i=u=f=this.seriesCoords.length-1;f<=0?u<=0:u>=0;i=f<=0?++u:--u)r=this.seriesCoords[i],o=this.options.smooth===!0||e.inArray(this.options.ykeys[i],this.options.smooth)>-1,r.length>1&&(s=this.createPath(r,this.bottom,o),this.r.path(s).attr("stroke",this.colorForSeries(i)).attr("stroke-width",this.options.lineWidth));this.seriesPoints=function(){var e,t,n;n=[];for(i=e=0,t=this.seriesCoords.length-1;0<=t?e<=t:e>=t;i=0<=t?++e:--e)n.push([]);return n}.call(this),c=[];for(i=a=l=this.seriesCoords.length-1;l<=0?a<=0:a>=0;i=l<=0?++a:--a)c.push(function(){var e,r,s,o;s=this.seriesCoords[i],o=[];for(e=0,r=s.length;e=m;o=0<=m?++v:--v)r=t[o],o===0?l+="M"+r.x+","+r.y:(i=s[o],a=t[o-1],f=s[o-1],u=(r.x-a.x)/4,c=a.x+u,p=Math.min(this.bottom,a.y+u*f),h=r.x-u,d=Math.min(this.bottom,r.y-u*i),l+="C"+c+","+p+","+h+","+d+","+r.x+","+r.y)}else l="M"+e.map(t,function(e){return""+e.x+","+e.y}).join("L");return l},r.prototype.gradients=function(t){return e.map(t,function(e,n){return n===0?(t[1].y-e.y)/(t[1].x-e.x):n===t.length-1?(e.y-t[n-1].y)/(e.x-t[n-1].x):(t[n+1].y-t[n-1].y)/(t[n+1].x-t[n-1].x)})},r.prototype.drawHover=function(){var e,t,n,r,i;this.hoverHeight=this.options.hoverFontSize*1.5*(this.series.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.xLabel=this.r.text(0,this.options.hoverFontSize*.75-this.hoverHeight/2,"").attr("fill",this.options.hoverLabelColor).attr("font-weight","bold").attr("font-size",this.options.hoverFontSize),this.hoverSet=this.r.set(),this.hoverSet.push(this.hover),this.hoverSet.push(this.xLabel),this.yLabels=[],i=[];for(e=n=0,r=this.series.length-1;0<=r?n<=r:n>=r;e=0<=r?++n:--n)t=this.r.text(0,this.options.hoverFontSize*1.5*(e+1.5)-this.hoverHeight/2,"").attr("fill",this.colorForSeries(e)).attr("font-size",this.options.hoverFontSize),this.yLabels.push(t),i.push(this.hoverSet.push(t));return i},r.prototype.updateHover=function(t){var n,r,i,s,o,u,a=this;this.hoverSet.show(),this.xLabel.attr("text",this.columnLabels[t]);for(n=o=0,u=this.series.length-1;0<=u?o<=u:o>=u;n=0<=u?++o:--o)this.yLabels[n].attr("text",""+this.options.labels[n]+": "+this.yLabelFormat(this.series[n][t]));return r=Math.max.apply(null,e.map(this.yLabels,function(e){return e.getBBox().width})),r=Math.max(r,this.xLabel.getBBox().width),this.hover.attr("width",r+this.options.hoverPaddingX*2),this.hover.attr("x",-this.options.hoverPaddingX-r/2),s=Math.min.apply(null,e.map(this.series,function(e){return a.transY(e[t])})),s>this.hoverHeight+this.options.hoverPaddingY*2+this.options.hoverMargin+this.top?s=s-this.hoverHeight/2-this.options.hoverPaddingY-this.options.hoverMargin:s=s+this.hoverHeight/2+this.options.hoverPaddingY+this.options.hoverMargin,s=Math.max(this.top+this.hoverHeight/2+this.options.hoverPaddingY,s),s=Math.min(this.bottom-this.hoverHeight/2-this.options.hoverPaddingY,s),i=Math.min(this.right-r/2-this.options.hoverPaddingX,this.columns[t]),i=Math.max(this.left+r/2+this.options.hoverPaddingX,i),this.hoverSet.attr("transform","t"+i+","+s)},r.prototype.hideHover=function(){return this.hoverSet.hide()},r.prototype.hilight=function(e){var t,n,r,i,s;if(this.prevHilight!==null&&this.prevHilight!==e)for(t=n=0,i=this.seriesPoints.length-1;0<=i?n<=i:n>=i;t=0<=i?++n:--n)this.seriesPoints[t][this.prevHilight]&&this.seriesPoints[t][this.prevHilight].animate(this.pointShrink);if(e!==null&&this.prevHilight!==e){for(t=r=0,s=this.seriesPoints.length-1;0<=s?r<=s:r>=s;t=0<=s?++r:--r)this.seriesPoints[t][e]&&this.seriesPoints[t][e].animate(this.pointGrow);this.updateHover(e)}this.prevHilight=e;if(e===null)return this.hideHover()},r.prototype.updateHilight=function(e){var t,n,r,i;e-=this.el.offset().left,i=[];for(t=n=r=this.hoverMargins.length;r<=0?n<=0:n>=0;t=r<=0?++n:--n){if(t===0||this.hoverMargins[t-1]>e){this.hilight(t);break}i.push(void 0)}return i},r.prototype.colorForSeries=function(e){return this.options.lineColors[e%this.options.lineColors.length]},r.prototype.strokeWidthForSeries=function(e){return this.options.pointWidths[e%this.options.pointWidths.length]},r.prototype.strokeForSeries=function(e){return this.options.pointStrokeColors[e%this.options.pointStrokeColors.length]},r.prototype.pointFillColorForSeries=function(e){return this.options.pointFillColors[e%this.options.pointFillColors.length]},r}(t.Grid),t.labelSeries=function(n,r,i,s,o){var u,a,f,l,c,h,p,d,v,m,g;f=200*(r-n)/i,a=new Date(n),p=t.LABEL_SPECS[s];if(p===void 0){g=t.AUTO_LABEL_ORDER;for(v=0,m=g.length;v=h.span){p=h;break}}}p===void 0&&(p=t.LABEL_SPECS.second),o&&(p=e.extend({},p,{fmt:o})),u=p.start(a),c=[];while((d=u.getTime())<=r)d>=n&&c.push([p.fmt(u),d]),p.incr(u);return c},n=function(e){return{span:e*60*1e3,start:function(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate(),e.getHours())},fmt:function(e){return""+t.pad2(e.getHours())+":"+t.pad2(e.getMinutes())},incr:function(t){return t.setMinutes(t.getMinutes()+e)}}},r=function(e){return{span:e*1e3,start:function(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate(),e.getHours(),e.getMinutes())},fmt:function(e){return""+t.pad2(e.getHours())+":"+t.pad2(e.getMinutes())+":"+t.pad2(e.getSeconds())},incr:function(t){return t.setSeconds(t.getSeconds()+e)}}},t.LABEL_SPECS={year:{span:1728e7,start:function(e){return new Date(e.getFullYear(),0,1)},fmt:function(e){return""+e.getFullYear()},incr:function(e){return e.setFullYear(e.getFullYear()+1)}},month:{span:24192e5,start:function(e){return new Date(e.getFullYear(),e.getMonth(),1)},fmt:function(e){return""+e.getFullYear()+"-"+t.pad2(e.getMonth()+1)},incr:function(e){return e.setMonth(e.getMonth()+1)}},day:{span:864e5,start:function(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate())},fmt:function(e){return""+e.getFullYear()+"-"+t.pad2(e.getMonth()+1)+"-"+t.pad2(e.getDate())},incr:function(e){return e.setDate(e.getDate()+1)}},hour:n(60),"30min":n(30),"15min":n(15),"10min":n(10),"5min":n(5),minute:n(1),"30sec":r(30),"15sec":r(15),"10sec":r(10),"5sec":r(5),second:r(1)},t.AUTO_LABEL_ORDER=["year","month","day","hour","30min","15min","10min","5min","minute","30sec","15sec","10sec","5sec","second"]}).call(this); \ No newline at end of file