diff --git a/lib/morris.grid.coffee b/lib/morris.grid.coffee index aa80de3..dadaed2 100644 --- a/lib/morris.grid.coffee +++ b/lib/morris.grid.coffee @@ -34,7 +34,7 @@ class Morris.Grid extends Morris.EventEmitter @init() if @init # load data - @setData(@options.data) + @setData @options.data # Default options # @@ -55,62 +55,55 @@ class Morris.Grid extends Morris.EventEmitter # Update the data series and redraw the chart. # setData: (data, redraw = true) -> - # shallow copy data - @options.data = $.map data, (row) => + ymax = null + ymin = null + @data = $.map data, (row, index) => + ret = {} + ret.label = row[@options.xkey] if @options.parseTime - $.extend {'__T': Morris.parseDate(row[@options.xkey])}, row + ret.x = Morris.parseDate(ret.label) + if @options.dateFormat + ret.label = @options.dateFormat ret.x + else if typeof ret.label is 'number' + ret.label = new Date(ret.label).toString() else - $.extend {}, row - if @options.parseTime - @options.data = @options.data.sort (a, b) => - (a['__T'] > b['__T']) - (b['__T'] > a['__T']) + ret.x = index + ret.y = for ykey in @options.ykeys + yval = row[ykey] + yval = parseFloat(yval) if typeof yval is 'string' + yval = null unless typeof yval is 'number' + unless yval is null + if ymax is null + ymax = ymin = yval + else + ymax = Math.max(yval, ymax) + ymin = Math.min(yval, ymin) + yval + ret - # 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, (row) => row[@options.xkey] if @options.parseTime - @xvals = $.map @options.data, (row) -> row['__T'] - 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] + @data = @data.sort (a, b) -> (a.x > b.x) - (b.x > a.x) # calculate horizontal range of the graph - @xmin = Math.min.apply null, @xvals - @xmax = Math.max.apply null, @xvals + @xmin = @data[0].x + @xmax = @data[@data.length - 1].x 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 + if typeof @options.ymax is 'string' + if @options.ymax[0..3] is 'auto' + # use Array.concat to flatten arrays and find the max y value + if @options.ymax.length > 5 + @ymax = Math.max parseInt(@options.ymax[5..], 10), ymax + else + @ymax = ymax else - @ymax = ymax - else if typeof @options.ymax is 'string' - @ymax = parseInt(@options.ymax, 10) + @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 @@ -120,7 +113,7 @@ class Morris.Grid extends Morris.EventEmitter else @ymin = @options.ymin if @ymin is @ymax - if @ymin is not 0 then @ymin -= 1 + if @ymin isnt 0 then @ymin -= 1 @ymax += 1 @yInterval = (@ymax - @ymin) / (@options.numLines - 1) @@ -158,7 +151,7 @@ class Morris.Grid extends Morris.EventEmitter # transY: (y) -> @bottom - (y - @ymin) * @dy transX: (x) -> - if @xvals.length == 1 + if @data.length == 1 (@left + @right) / 2 else @left + (x - @xmin) * @dx diff --git a/lib/morris.line.coffee b/lib/morris.line.coffee index 2609a93..13958ad 100644 --- a/lib/morris.line.coffee +++ b/lib/morris.line.coffee @@ -60,18 +60,15 @@ class Morris.Line extends Morris.Grid # @private calc: -> # 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) + for row in @data + row._x = @transX(row.x) + row._y = for y in row.y + if y is null + null + else + @transY(y) # calculate hover margins - @hoverMargins = $.map @columns.slice(1), (x, i) => (x + @columns[i]) / 2 + @hoverMargins = $.map @data.slice(1), (r, i) => (r._x + @data[i]._x) / 2 # Draws the line chart. # @@ -79,7 +76,7 @@ class Morris.Line extends Morris.Grid @drawXAxis() @drawSeries() @drawHover() - @hilight(if @options.hideHover then null else @options.data.length - 1) + @hilight(if @options.hideHover then null else @data.length - 1) # draw the x-axis labels # @@ -102,25 +99,24 @@ class Morris.Line extends Morris.Grid else label.remove() if @options.parseTime - if @columnLabels.length == 1 and @options.xLabels == 'auto' + if @data.length == 1 and @options.xLabels == 'auto' # where there's only one value in the series, we can't make a # sensible guess for an x labelling scheme, so just use the original # column label - drawLabel(@columnLabels[0], @xvals[0]) + drawLabel(@data[0].label, @data[0].x) else for l in Morris.labelSeries(@xmin, @xmax, @width, @options.xLabels, @options.xLabelFormat) drawLabel(l[0], l[1]) else - for i in [0...@columnLabels.length] - labelText = @columnLabels[i] - drawLabel(labelText, i) + for row in @data + drawLabel(row.label, row.x) # draw the data series # # @private drawSeries: -> - for i in [@seriesCoords.length-1..0] - coords = $.map @seriesCoords[i], (c) -> c + for i in [@options.ykeys.length-1..0] + coords = ({x: r._x, y: r._y[i]} for r in @data when r._y[i] isnt null) smooth = @options.smooth is true or $.inArray(@options.ykeys[i], @options.smooth) > -1 if coords.length > 1 @@ -128,13 +124,13 @@ class Morris.Line extends Morris.Grid @r.path(path) .attr('stroke', @colorForSeries(i)) .attr('stroke-width', @options.lineWidth) - @seriesPoints = ([] for i in [0..@seriesCoords.length-1]) - for i in [@seriesCoords.length-1..0] - for c in @seriesCoords[i] - if c == null + @seriesPoints = ([] for i in [0...@options.ykeys.length]) + for i in [@options.ykeys.length-1..0] + for row in @data + if row._y[i] == null circle = null else - circle = @r.circle(c.x, c.y, @options.pointSize) + circle = @r.circle(row._x, row._y[i], @options.pointSize) .attr('fill', @pointFillColorForSeries(i) || @colorForSeries(i)) .attr('stroke-width', @strokeWidthForSeries(i)) .attr('stroke', @strokeForSeries(i)) @@ -182,7 +178,7 @@ class Morris.Line extends Morris.Grid # @private drawHover: -> # hover labels - @hoverHeight = @options.hoverFontSize * 1.5 * (@series.length + 1) + @hoverHeight = @options.hoverFontSize * 1.5 * (@options.ykeys.length + 1) @hover = @r.rect(-10, -@hoverHeight / 2 - @options.hoverPaddingY, 20, @hoverHeight + @options.hoverPaddingY * 2, 10) .attr('fill', @options.hoverFillColor) .attr('stroke', @options.hoverBorderColor) @@ -196,7 +192,7 @@ class Morris.Line extends Morris.Grid @hoverSet.push(@hover) @hoverSet.push(@xLabel) @yLabels = [] - for i in [0..@series.length-1] + for i in [0...@data.length] yLabel = @r.text(0, @options.hoverFontSize * 1.5 * (i + 1.5) - @hoverHeight / 2, '') .attr('fill', @colorForSeries(i)) .attr('font-size', @options.hoverFontSize) @@ -206,9 +202,10 @@ class Morris.Line extends Morris.Grid # @private updateHover: (index) => @hoverSet.show() - @xLabel.attr('text', @columnLabels[index]) - for i in [0..@series.length-1] - @yLabels[i].attr('text', "#{@options.labels[i]}: #{@yLabelFormat(@series[i][index])}") + row = @data[index] + @xLabel.attr('text', row.label) + for y, i in row.y + @yLabels[i].attr('text', "#{@options.labels[i]}: #{@yLabelFormat(y)}") # recalculate hover box width maxLabelWidth = Math.max.apply null, $.map @yLabels, (l) -> l.getBBox().width @@ -216,15 +213,14 @@ class Morris.Line extends Morris.Grid @hover.attr 'width', maxLabelWidth + @options.hoverPaddingX * 2 @hover.attr 'x', -@options.hoverPaddingX - maxLabelWidth / 2 # move to y pos - yloc = Math.min.apply null, $.map @series, (s) => - @transY s[index] + yloc = Math.min.apply null, row.y if yloc > @hoverHeight + @options.hoverPaddingY * 2 + @options.hoverMargin + @top yloc = yloc - @hoverHeight / 2 - @options.hoverPaddingY - @options.hoverMargin else yloc = yloc + @hoverHeight / 2 + @options.hoverPaddingY + @options.hoverMargin yloc = Math.max @top + @hoverHeight / 2 + @options.hoverPaddingY, yloc yloc = Math.min @bottom - @hoverHeight / 2 - @options.hoverPaddingY, yloc - xloc = Math.min @right - maxLabelWidth / 2 - @options.hoverPaddingX, @columns[index] + xloc = Math.min @right - maxLabelWidth / 2 - @options.hoverPaddingX, @data[index]._x xloc = Math.max @left + maxLabelWidth / 2 + @options.hoverPaddingX, xloc @hoverSet.attr 'transform', "t#{xloc},#{yloc}" diff --git a/morris.js b/morris.js index 1b5d18a..3528da1 100644 --- a/morris.js +++ b/morris.js @@ -62,6 +62,16 @@ return (number < 10 ? '0' : '') + number; }; + Morris.Data = (function() { + + function Data() {} + + Data.prototype.initialize = function(options) {}; + + return Data; + + })(); + Morris.Donut = (function() { Donut.prototype.defaults = { @@ -329,94 +339,79 @@ }; Grid.prototype.setData = function(data, redraw) { - var d, seriesData, y, ykey, ymax, ymin, _i, _j, _k, _len, _len1, _ref, _ref1, _ref2, _results, + var ymax, ymin, _this = this; if (redraw == null) { redraw = true; } - this.options.data = $.map(data, function(row) { + ymax = null; + ymin = null; + this.data = $.map(data, function(row, index) { + var ret, ykey, yval; + ret = {}; + ret.label = row[_this.options.xkey]; if (_this.options.parseTime) { - return $.extend({ - '__T': Morris.parseDate(row[_this.options.xkey]) - }, row); + ret.x = Morris.parseDate(ret.label); + if (_this.options.dateFormat) { + ret.label = _this.options.dateFormat(ret.x); + } else if (typeof ret.label === 'number') { + ret.label = new Date(ret.label).toString(); + } } else { - return $.extend({}, row); + ret.x = index; } - }); - if (this.options.parseTime) { - this.options.data = this.options.data.sort(function(a, b) { - return (a['__T'] > b['__T']) - (b['__T'] > a['__T']); - }); - } - this.series = []; - _ref = this.options.ykeys; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - ykey = _ref[_i]; - seriesData = []; - _ref1 = this.options.data; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - d = _ref1[_j]; - y = d[ykey]; - seriesData.push((function() { - switch (typeof y) { - case 'number': - return y; - case 'string': - return parseFloat(y); - default: - return null; - } - })()); - } - this.series.push(seriesData); - } - this.columnLabels = $.map(this.options.data, function(row) { - return row[_this.options.xkey]; - }); - if (this.options.parseTime) { - this.xvals = $.map(this.options.data, function(row) { - return row['__T']; - }); - if (this.options.dateFormat) { - this.columnLabels = $.map(this.xvals, function(d) { - return _this.options.dateFormat(d); - }); - } else { - this.columnLabels = $.map(this.columnLabels, function(d) { - if (typeof d === 'number') { - return new Date(d).toString(); - } else { - return d; - } - }); - } - } else { - this.xvals = (function() { + ret.y = (function() { + var _i, _len, _ref, _results; + _ref = this.options.ykeys; _results = []; - for (var _k = 0, _ref2 = this.columnLabels.length; 0 <= _ref2 ? _k < _ref2 : _k > _ref2; 0 <= _ref2 ? _k++ : _k--){ _results.push(_k); } + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + ykey = _ref[_i]; + yval = row[ykey]; + if (typeof yval === 'string') { + yval = parseFloat(yval); + } + if (typeof yval !== 'number') { + yval = null; + } + if (yval !== null) { + if (ymax === null) { + ymax = ymin = yval; + } else { + ymax = Math.max(yval, ymax); + ymin = Math.min(yval, ymin); + } + } + _results.push(yval); + } return _results; - }).apply(this); + }).call(_this); + return ret; + }); + if (this.options.parseTime) { + this.data = this.data.sort(function(a, b) { + return (a.x > b.x) - (b.x > a.x); + }); } - this.xmin = Math.min.apply(null, this.xvals); - this.xmax = Math.max.apply(null, this.xvals); + this.xmin = this.data[0].x; + this.xmax = this.data[this.data.length - 1].x; if (this.xmin === this.xmax) { this.xmin -= 1; this.xmax += 1; } - if (typeof this.options.ymax === 'string' && this.options.ymax.slice(0, 4) === 'auto') { - ymax = Math.max.apply(null, Array.prototype.concat.apply([], this.series)); - if (this.options.ymax.length > 5) { - this.ymax = Math.max(parseInt(this.options.ymax.slice(5), 10), ymax); + if (typeof this.options.ymax === 'string') { + if (this.options.ymax.slice(0, 4) === 'auto') { + if (this.options.ymax.length > 5) { + this.ymax = Math.max(parseInt(this.options.ymax.slice(5), 10), ymax); + } else { + this.ymax = ymax; + } } else { - this.ymax = ymax; + this.ymax = parseInt(this.options.ymax, 10); } - } else if (typeof this.options.ymax === 'string') { - this.ymax = parseInt(this.options.ymax, 10); } else { this.ymax = this.options.ymax; } if (typeof this.options.ymin === 'string' && this.options.ymin.slice(0, 4) === 'auto') { - ymin = Math.min.apply(null, Array.prototype.concat.apply([], this.series)); if (this.options.ymin.length > 5) { this.ymin = Math.min(parseInt(this.options.ymin.slice(5), 10), ymin); } else { @@ -428,7 +423,7 @@ this.ymin = this.options.ymin; } if (this.ymin === this.ymax) { - if (this.ymin === !0) { + if (this.ymin !== 0) { this.ymin -= 1; } this.ymax += 1; @@ -473,7 +468,7 @@ }; Grid.prototype.transX = function(x) { - if (this.xvals.length === 1) { + if (this.data.length === 1) { return (this.left + this.right) / 2; } else { return this.left + (x - this.xmin) * this.dx; @@ -651,37 +646,29 @@ }; Line.prototype.calc = function() { - var s, scoords, x, _i, _len, _ref, + var row, y, _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; + _ref = this.data; 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) - }); + row = _ref[_i]; + row._x = this.transX(row.x); + row._y = (function() { + var _j, _len1, _ref1, _results; + _ref1 = row.y; + _results = []; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + y = _ref1[_j]; + if (y === null) { + _results.push(null); + } else { + _results.push(this.transY(y)); + } } - }); - this.seriesCoords.push(scoords); + return _results; + }).call(this); } - return this.hoverMargins = $.map(this.columns.slice(1), function(x, i) { - return (x + _this.columns[i]) / 2; + return this.hoverMargins = $.map(this.data.slice(1), function(r, i) { + return (r._x + _this.data[i]._x) / 2; }); }; @@ -689,11 +676,11 @@ this.drawXAxis(); this.drawSeries(); this.drawHover(); - return this.hilight(this.options.hideHover ? null : this.options.data.length - 1); + return this.hilight(this.options.hideHover ? null : this.data.length - 1); }; Line.prototype.drawXAxis = function() { - var drawLabel, i, l, labelText, prevLabelMargin, xLabelMargin, ypos, _i, _j, _len, _ref, _ref1, _results, _results1, + var drawLabel, l, prevLabelMargin, row, xLabelMargin, ypos, _i, _j, _len, _len1, _ref, _ref1, _results, _results1, _this = this; ypos = this.bottom + this.options.gridTextSize * 1.25; xLabelMargin = 50; @@ -709,8 +696,8 @@ } }; if (this.options.parseTime) { - if (this.columnLabels.length === 1 && this.options.xLabels === 'auto') { - return drawLabel(this.columnLabels[0], this.xvals[0]); + if (this.data.length === 1 && this.options.xLabels === 'auto') { + return drawLabel(this.data[0].label, this.data[0].x); } else { _ref = Morris.labelSeries(this.xmin, this.xmax, this.width, this.options.xLabels, this.options.xLabelFormat); _results = []; @@ -721,21 +708,34 @@ return _results; } } else { + _ref1 = this.data; _results1 = []; - for (i = _j = 0, _ref1 = this.columnLabels.length; 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) { - labelText = this.columnLabels[i]; - _results1.push(drawLabel(labelText, i)); + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + row = _ref1[_j]; + _results1.push(drawLabel(row.label, row.x)); } 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; - }); + var circle, coords, i, path, r, row, smooth, _i, _j, _ref, _ref1, _results; + for (i = _i = _ref = this.options.ykeys.length - 1; _ref <= 0 ? _i <= 0 : _i >= 0; i = _ref <= 0 ? ++_i : --_i) { + coords = (function() { + var _j, _len, _ref1, _results; + _ref1 = this.data; + _results = []; + for (_j = 0, _len = _ref1.length; _j < _len; _j++) { + r = _ref1[_j]; + if (r._y[i] !== null) { + _results.push({ + x: r._x, + y: r._y[i] + }); + } + } + return _results; + }).call(this); 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); @@ -745,23 +745,23 @@ 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) { + for (i = _j = 0, _ref1 = this.options.ykeys.length; 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) { + for (i = _j = _ref1 = this.options.ykeys.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]; + _ref2 = this.data; _results1 = []; for (_k = 0, _len = _ref2.length; _k < _len; _k++) { - c = _ref2[_k]; - if (c === null) { + row = _ref2[_k]; + if (row._y[i] === 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)); + circle = this.r.circle(row._x, row._y[i], 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)); } @@ -814,7 +814,7 @@ Line.prototype.drawHover = function() { var i, yLabel, _i, _ref, _results; - this.hoverHeight = this.options.hoverFontSize * 1.5 * (this.series.length + 1); + this.hoverHeight = this.options.hoverFontSize * 1.5 * (this.options.ykeys.length + 1); this.hover = this.r.rect(-10, -this.hoverHeight / 2 - this.options.hoverPaddingY, 20, this.hoverHeight + this.options.hoverPaddingY * 2, 10).attr('fill', this.options.hoverFillColor).attr('stroke', this.options.hoverBorderColor).attr('stroke-width', this.options.hoverBorderWidth).attr('opacity', this.options.hoverOpacity); this.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(); @@ -822,7 +822,7 @@ 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) { + for (i = _i = 0, _ref = this.data.length; 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)); @@ -831,12 +831,14 @@ }; Line.prototype.updateHover = function(index) { - var i, maxLabelWidth, xloc, yloc, _i, _ref, - _this = this; + var i, maxLabelWidth, row, xloc, y, yloc, _i, _len, _ref; 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]))); + row = this.data[index]; + this.xLabel.attr('text', row.label); + _ref = row.y; + for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { + y = _ref[i]; + this.yLabels[i].attr('text', "" + this.options.labels[i] + ": " + (this.yLabelFormat(y))); } maxLabelWidth = Math.max.apply(null, $.map(this.yLabels, function(l) { return l.getBBox().width; @@ -844,9 +846,7 @@ 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]); - })); + yloc = Math.min.apply(null, row.y); 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 { @@ -854,7 +854,7 @@ } 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.min(this.right - maxLabelWidth / 2 - this.options.hoverPaddingX, this.data[index]._x); xloc = Math.max(this.left + maxLabelWidth / 2 + this.options.hoverPaddingX, xloc); return this.hoverSet.attr('transform', "t" + xloc + "," + yloc); }; diff --git a/morris.min.js b/morris.min.js index 6df6d16..1a14199 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.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=e.map(n,function(n){return b.options.parseTime?e.extend({__T:t.parseDate(n[b.options.xkey])},n):e.extend({},n)}),this.options.parseTime&&(this.options.data=this.options.data.sort(function(e,t){return(e.__T>t.__T)-(t.__T>e.__T)})),this.series=[],v=this.options.ykeys;for(l=0,p=v.length;lt;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.widthh;n=0<=h?++f:--f)i=this.columnLabels[n],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=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.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;e-=this.el.offset().left;for(t=n=0,r=this.hoverMargins.length;0<=r?nr;t=0<=r?++n:--n)if(this.hoverMargins[t]>e)break;return this.hilight(t)},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 +(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.Data=function(){function e(){}return e.prototype.initialize=function(e){},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=this;r==null&&(r=!0),i=null,s=null,this.data=e.map(n,function(e,n){var r,u,a;return r={},r.label=e[o.options.xkey],o.options.parseTime?(r.x=t.parseDate(r.label),o.options.dateFormat?r.label=o.options.dateFormat(r.x):typeof r.label=="number"&&(r.label=(new Date(r.label)).toString())):r.x=n,r.y=function(){var t,n,r,o;r=this.options.ykeys,o=[];for(t=0,n=r.length;tt.x)-(t.x>e.x)})),this.xmin=this.data[0].x,this.xmax=this.data[this.data.length-1].x,this.xmin===this.xmax&&(this.xmin-=1,this.xmax+=1),typeof this.options.ymax=="string"?this.options.ymax.slice(0,4)==="auto"?this.options.ymax.length>5?this.ymax=Math.max(parseInt(this.options.ymax.slice(5),10),i):this.ymax=i:this.ymax=parseInt(this.options.ymax,10):this.ymax=this.options.ymax,typeof this.options.ymin=="string"&&this.options.ymin.slice(0,4)==="auto"?this.options.ymin.length>5?this.ymin=Math.min(parseInt(this.options.ymin.slice(5),10),s):this.ymin=s: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.data.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=this;s=this.data;for(r=0,i=s.length;r=0&&i.x+i.width=0;r=l<=0?++a:--a)n=function(){var e,t,n,i;n=this.data,i=[];for(e=0,t=n.length;e-1,n.length>1&&(i=this.createPath(n,this.bottom,u),this.r.path(i).attr("stroke",this.colorForSeries(r)).attr("stroke-width",this.options.lineWidth));this.seriesPoints=function(){var e,t,n;n=[];for(r=e=0,t=this.options.ykeys.length;0<=t?et;r=0<=t?++e:--e)n.push([]);return n}.call(this),h=[];for(r=f=c=this.options.ykeys.length-1;c<=0?f<=0:f>=0;r=c<=0?++f:--f)h.push(function(){var e,n,i,s;i=this.data,s=[];for(e=0,n=i.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.options.ykeys.length+1),this.hover=this.r.rect(-10,-this.hoverHeight/2-this.options.hoverPaddingY,20,this.hoverHeight+this.options.hoverPaddingY*2,10).attr("fill",this.options.hoverFillColor).attr("stroke",this.options.hoverBorderColor).attr("stroke-width",this.options.hoverBorderWidth).attr("opacity",this.options.hoverOpacity),this.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.data.length;0<=r?nr;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,f,l;this.hoverSet.show(),i=this.data[t],this.xLabel.attr("text",i.label),l=i.y;for(n=a=0,f=l.length;athis.hoverHeight+this.options.hoverPaddingY*2+this.options.hoverMargin+this.top?u=u-this.hoverHeight/2-this.options.hoverPaddingY-this.options.hoverMargin:u=u+this.hoverHeight/2+this.options.hoverPaddingY+this.options.hoverMargin,u=Math.max(this.top+this.hoverHeight/2+this.options.hoverPaddingY,u),u=Math.min(this.bottom-this.hoverHeight/2-this.options.hoverPaddingY,u),s=Math.min(this.right-r/2-this.options.hoverPaddingX,this.data[t]._x),s=Math.max(this.left+r/2+this.options.hoverPaddingX,s),this.hoverSet.attr("transform","t"+s+","+u)},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;e-=this.el.offset().left;for(t=n=0,r=this.hoverMargins.length;0<=r?nr;t=0<=r?++n:--n)if(this.hoverMargins[t]>e)break;return this.hilight(t)},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 diff --git a/spec/lib/line_spec.coffee b/spec/lib/line_spec.coffee index 24f603c..89c941c 100644 --- a/spec/lib/line_spec.coffee +++ b/spec/lib/line_spec.coffee @@ -60,7 +60,7 @@ describe 'Morris.Line', -> xkey: 'x' ykeys: ['y'] labels: ['dontcare'] - chart.columnLabels.should == ['2012 Q1', '2012 Q2'] + chart.data.map((x) -> x.label).should == ['2012 Q1', '2012 Q2'] it 'should use a default format for timestamp x-values', -> d1 = new Date(2012, 0, 1) @@ -71,7 +71,7 @@ describe 'Morris.Line', -> xkey: 'x' ykeys: ['y'] labels: ['dontcare'] - chart.columnLabels.should == [d2.toString(), d1.toString()] + chart.data.map((x) -> x.label).should == [d2.toString(), d1.toString()] it 'should use user-defined formatters', -> d = new Date(2012, 0, 1) @@ -84,4 +84,4 @@ describe 'Morris.Line', -> dateFormat: (d) -> x = new Date(d) "#{x.getYear()}/#{x.getMonth()+1}/#{x.getDay()}" - chart.columnLabels.should == ['2012/1/1', '2012/1/2'] + chart.data.map((x) -> x.label).should == ['2012/1/1', '2012/1/2']