diff --git a/examples/area.html b/examples/area.html index c3c71f5..e5ee6c5 100644 --- a/examples/area.html +++ b/examples/area.html @@ -7,6 +7,7 @@ +

Area charts

diff --git a/examples/bar-colors.html b/examples/bar-colors.html index 95f1d72..c4700ec 100644 --- a/examples/bar-colors.html +++ b/examples/bar-colors.html @@ -7,6 +7,7 @@ +

Bar charts

diff --git a/examples/bar.html b/examples/bar.html index 2209fa7..f3ff842 100644 --- a/examples/bar.html +++ b/examples/bar.html @@ -7,6 +7,7 @@ +

Bar charts

diff --git a/examples/days.html b/examples/days.html index 2c1d5d9..17d0339 100644 --- a/examples/days.html +++ b/examples/days.html @@ -7,6 +7,7 @@ +

Formatting Dates YYYY-MM-DD

diff --git a/examples/decimal.html b/examples/decimal-custom-hover.html similarity index 81% rename from examples/decimal.html rename to examples/decimal-custom-hover.html index 3a93cf5..879a2ec 100644 --- a/examples/decimal.html +++ b/examples/decimal-custom-hover.html @@ -7,6 +7,7 @@ +

Decimal Data

@@ -25,7 +26,11 @@ window.m = Morris.Line({ xkey: 'x', ykeys: ['y'], labels: ['sin(x)'], - parseTime: false + parseTime: false, + hoverCallback: function (index, options) { + var row = options.data[index]; + return "sin(" + row.x + ") = " + row.y; + } }); diff --git a/examples/donut-formatter.html b/examples/donut-formatter.html index 0df7c06..294db89 100644 --- a/examples/donut-formatter.html +++ b/examples/donut-formatter.html @@ -7,6 +7,7 @@ +

Donut Chart

diff --git a/examples/donut.html b/examples/donut.html index 752a047..a9b51ae 100644 --- a/examples/donut.html +++ b/examples/donut.html @@ -7,6 +7,7 @@ +

Donut Chart

diff --git a/examples/events.html b/examples/events.html index 3d8cad5..933af52 100644 --- a/examples/events.html +++ b/examples/events.html @@ -7,6 +7,7 @@ +

Time Events

diff --git a/examples/goals.html b/examples/goals.html index 3e3b19d..1922db8 100644 --- a/examples/goals.html +++ b/examples/goals.html @@ -7,6 +7,7 @@ +

Value Goals

diff --git a/examples/months-no-smooth.html b/examples/months-no-smooth.html index 95d4788..6e2cd3a 100644 --- a/examples/months-no-smooth.html +++ b/examples/months-no-smooth.html @@ -7,6 +7,7 @@ +

Formatting Dates with YYYY-MM

diff --git a/examples/negative.html b/examples/negative.html index d1ee1a2..b495b9f 100644 --- a/examples/negative.html +++ b/examples/negative.html @@ -7,6 +7,7 @@ +

Negative values

diff --git a/examples/non-continuous.html b/examples/non-continuous.html index e314850..5689772 100644 --- a/examples/non-continuous.html +++ b/examples/non-continuous.html @@ -7,6 +7,7 @@ +

Non-continuous data

diff --git a/examples/non-date.html b/examples/non-date.html index fe34d6e..41e56d4 100644 --- a/examples/non-date.html +++ b/examples/non-date.html @@ -7,6 +7,7 @@ +

Formatting Non-date Arbitrary X-axis

diff --git a/examples/quarters.html b/examples/quarters.html index 67b2212..366cfd6 100644 --- a/examples/quarters.html +++ b/examples/quarters.html @@ -7,6 +7,7 @@ +

Formatting Dates with Quarters

diff --git a/examples/staked_bars.html b/examples/staked_bars.html index fca3d55..a0ede35 100644 --- a/examples/staked_bars.html +++ b/examples/staked_bars.html @@ -7,6 +7,7 @@ +

Stacked Bars chart

diff --git a/examples/timestamps.html b/examples/timestamps.html index 0db051d..f4d701b 100644 --- a/examples/timestamps.html +++ b/examples/timestamps.html @@ -7,6 +7,7 @@ +

Timestamps

diff --git a/examples/updating.html b/examples/updating.html index 9f757d4..dfaf69e 100644 --- a/examples/updating.html +++ b/examples/updating.html @@ -7,6 +7,7 @@ +

Updating data

diff --git a/examples/weeks.html b/examples/weeks.html index 0e3943f..a8d5d3e 100644 --- a/examples/weeks.html +++ b/examples/weeks.html @@ -7,6 +7,7 @@ +

Formatting Dates With Weeks

diff --git a/examples/years.html b/examples/years.html index 33e973b..fd9d9f7 100644 --- a/examples/years.html +++ b/examples/years.html @@ -7,6 +7,7 @@ +

Formatting Dates YYYY

diff --git a/grunt.js b/grunt.js index 8bee7a4..494e69a 100644 --- a/grunt.js +++ b/grunt.js @@ -16,6 +16,7 @@ module.exports = function (grunt) { 'build/morris.coffee': [ 'lib/morris.coffee', 'lib/morris.grid.coffee', + 'lib/morris.hover.coffee', 'lib/morris.line.coffee', 'lib/morris.area.coffee', 'lib/morris.bar.coffee', @@ -23,6 +24,15 @@ module.exports = function (grunt) { ], 'build/spec.coffee': ['spec/support/**/*.coffee', 'spec/lib/**/*.coffee'] }, + less: { + all: { + src: 'less/*.less', + dest: 'morris.css', + options: { + compress: true + } + } + }, min: { 'morris.min.js': 'morris.js' }, @@ -33,13 +43,14 @@ module.exports = function (grunt) { } }, watch: { - files: ['lib/**/*.coffee', 'spec/lib/**/*.coffee', 'spec/support/**/*.coffee'], + files: ['lib/**/*.coffee', 'spec/lib/**/*.coffee', 'spec/support/**/*.coffee', 'less/**/*.less'], tasks: 'default' } }); grunt.loadNpmTasks('grunt-coffee'); grunt.loadNpmTasks('grunt-mocha'); + grunt.loadNpmTasks('grunt-less'); - grunt.registerTask('default', 'concat coffee min mocha'); + grunt.registerTask('default', 'concat coffee less min mocha'); }; diff --git a/less/morris.core.less b/less/morris.core.less new file mode 100644 index 0000000..1d27211 --- /dev/null +++ b/less/morris.core.less @@ -0,0 +1,27 @@ +.morris-hover { + position: absolute; + z-index: 1000; + + &.morris-default-style { + border-radius: 10px; + padding: 6px; + color: #666; + background: rgba(255, 255, 255, 0.8); + border: solid 2px rgba(230, 230, 230, 0.8); + + font-family: sans-serif; + font-size: 12px; + text-align: center; + + .morris-hover-row-label { + font-weight: bold; + margin: 0.25em 0; + } + + .morris-hover-point { + white-space: nowrap; + margin: 0.1em 0; + } + } + +} diff --git a/lib/morris.area.coffee b/lib/morris.area.coffee index 53b8130..a654ac3 100644 --- a/lib/morris.area.coffee +++ b/lib/morris.area.coffee @@ -16,6 +16,7 @@ class Morris.Area extends Morris.Line row._y = for y in row.y total += (y || 0) @transY(total) + row._ymax = row._y[row._y.length - 1] # draw the data series # @@ -31,7 +32,7 @@ class Morris.Area extends Morris.Line super() fillForSeries: (i) -> - color = Raphael.rgb2hsl @colorForSeries(i) + color = Raphael.rgb2hsl @colorFor(@data[i], i, 'line') Raphael.hsl( color.h, Math.min(255, color.s * 0.75), diff --git a/lib/morris.bar.coffee b/lib/morris.bar.coffee index 9ff96ee..ccedb5a 100644 --- a/lib/morris.bar.coffee +++ b/lib/morris.bar.coffee @@ -1,27 +1,15 @@ class Morris.Bar extends Morris.Grid - # Initialise the graph. - # constructor: (options) -> return new Morris.Bar(options) unless (@ instanceof Morris.Bar) super($.extend {}, options, parseTime: false) - # setup event handlers - # init: -> @cumulative = @options.stacked - @prevHilight = null - @el.mousemove (evt) => - @updateHilight evt.pageX - if @options.hideHover - @el.mouseout (evt) => - @hilight null - touchHandler = (evt) => - touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0] - @updateHilight touch.pageX - return touch - @el.bind 'touchstart', touchHandler - @el.bind 'touchmove', touchHandler - @el.bind 'touchend', touchHandler + + if @options.hideHover isnt 'always' + @hover = new Morris.Hover(parent: @el) + @on('hovermove', @onHoverMove) + @on('hoverout', @onHoverOut) # Default configuration # @@ -37,23 +25,14 @@ class Morris.Bar extends Morris.Grid '#cb4b4b' '#9440ed' ] - hoverPaddingX: 10 - hoverPaddingY: 5 - hoverMargin: 10 - hoverFillColor: '#fff' - hoverBorderColor: '#ccc' - hoverBorderWidth: 2 - hoverOpacity: 0.95 - hoverLabelColor: '#444' - hoverFontSize: 12 - hideHover: false # Do any size-related calculations # # @private calc: -> @calcBars() - @calcHoverMargins() + if @options.hideHover is false + @hover.update(@hoverContentForRow(@data.length - 1)...) # calculate series data bars coordinates and sizes # @@ -64,20 +43,11 @@ class Morris.Bar extends Morris.Grid row._y = for y in row.y if y? then @transY(y) else null - # calculate hover margins - # - # @private - calcHoverMargins: -> - @hoverMargins = for i in [1...@data.length] - @left + i * @width / @data.length - # Draws the bar chart. # draw: -> @drawXAxis() @drawSeries() - @drawHover() - @hilight(if @options.hideHover then null else @data.length - 1) # draw the x-axis labels # @@ -134,74 +104,11 @@ class Morris.Bar extends Morris.Grid else null - # draw the hover tooltip - # - # @private - drawHover: -> - # hover labels - @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) - .attr('stroke-width', @options.hoverBorderWidth) - .attr('opacity', @options.hoverOpacity) - @xLabel = @r.text(0, (@options.hoverFontSize * 0.75) - @hoverHeight / 2, '') - .attr('fill', @options.hoverLabelColor) - .attr('font-weight', 'bold') - .attr('font-size', @options.hoverFontSize) - @hoverSet = @r.set() - @hoverSet.push(@hover) - @hoverSet.push(@xLabel) - @yLabels = [] - for i in [0...@options.ykeys.length] - yLabel = @r.text(0, @options.hoverFontSize * 1.5 * (i + 1.5) - @hoverHeight / 2, '') - .attr('font-size', @options.hoverFontSize) - @yLabels.push(yLabel) - @hoverSet.push(yLabel) - - # @private - updateHover: (index) => - @hoverSet.show() - row = @data[index] - @xLabel.attr('text', row.label) - for y, i in row.y - @yLabels[i].attr('fill', @colorFor(row, i, 'hover')) - @yLabels[i].attr('text', "#{@options.labels[i]}: #{@yLabelFormat(y)}") - # recalculate hover box width - maxLabelWidth = Math.max.apply null, (l.getBBox().width for l in @yLabels) - maxLabelWidth = Math.max maxLabelWidth, @xLabel.getBBox().width - @hover.attr 'width', maxLabelWidth + @options.hoverPaddingX * 2 - @hover.attr 'x', -@options.hoverPaddingX - maxLabelWidth / 2 - # move to y pos - yloc = (@bottom + @top) / 2 - 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}" - - # @private - hideHover: -> - @hoverSet.hide() - - # @private - hilight: (index) => - if index isnt null and @prevHilight isnt index - @updateHover index - @prevHilight = index - if not index? - @hideHover() - - # @private - updateHilight: (x) => - x -= @el.offset().left - for hoverIndex in [0...@hoverMargins.length] - break if @hoverMargins[hoverIndex] > x - @hilight hoverIndex - # @private # # @param row [Object] row data # @param sidx [Number] series index - # @param type [String] "bar" or "hover" + # @param type [String] "bar", "hover" or "label" colorFor: (row, sidx, type) -> if typeof @options.barColors is 'function' r = { x: row.x, y: row.y[sidx], label: row.label } @@ -209,3 +116,43 @@ class Morris.Bar extends Morris.Grid @options.barColors.call(@, r, s, type) else @options.barColors[sidx % @options.barColors.length] + + # hit test - returns the index of the row beneath the given coordinate + # + hitTest: (x, y) -> + x = Math.max(Math.min(x, @right), @left) + Math.min(@data.length - 1, + Math.floor((x - @left) / ((@right - @left) / @data.length))) + + # hover movement event handler + # + # @private + onHoverMove: (x, y) => + index = @hitTest(x, y) + @hover.update(@hoverContentForRow(index)...) + + # hover out event handler + # + # @private + onHoverOut: => + if @options.hideHover is 'auto' + @hover.hide() + + # hover content for a point + # + # @private + hoverContentForRow: (index) -> + if typeof @options.hoverCallback is 'function' + content = @options.hoverCallback(index, @options) + else + row = @data[index] + content = "
#{row.label}
" + for y, j in row.y + content += """ +
+ #{@options.labels[j]}: + #{@yLabelFormat(y)} +
+ """ + x = @left + (index + 0.5) * (@right - @left) / @data.length + [content, x] diff --git a/lib/morris.grid.coffee b/lib/morris.grid.coffee index a9bda3d..4514276 100644 --- a/lib/morris.grid.coffee +++ b/lib/morris.grid.coffee @@ -12,6 +12,9 @@ class Morris.Grid extends Morris.EventEmitter if not @el? or @el.length == 0 throw new Error("Graph container element not found") + if @el.css('position') == 'static' + @el.css('position', 'relative') + @options = $.extend {}, @gridDefaults, (@defaults || {}), options # bail if there's no data @@ -36,6 +39,22 @@ class Morris.Grid extends Morris.EventEmitter # load data @setData @options.data + # hover + @el.bind 'mousemove', (evt) => + offset = @el.offset() + @fire 'hovermove', evt.pageX - offset.left, evt.pageY - offset.top + + @el.bind 'mouseout', (evt) => + @fire 'hoverout' + + @el.bind 'touchstart touchmove touchend', (evt) => + touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0] + offset = @el.offset() + @fire 'hover', touch.pageX - offset.left, touch.pageY - offset.top + touch + + @postInit() if @postInit + # Default options # gridDefaults: @@ -44,6 +63,7 @@ class Morris.Grid extends Morris.EventEmitter gridStrokeWidth: 0.5 gridTextColor: '#888' gridTextSize: 12 + hideHover: false numLines: 5 padding: 25 parseTime: true @@ -256,6 +276,10 @@ class Morris.Grid extends Morris.EventEmitter yLabelFormat: (label) -> "#{@options.preUnits}#{Morris.commas(label)}#{@options.postUnits}" + updateHover: (x, y) -> + hit = @hitTest(x, y) + if hit? + @hover.update(hit...) # Parse a date into a javascript timestamp # diff --git a/lib/morris.hover.coffee b/lib/morris.hover.coffee new file mode 100644 index 0000000..ffc600c --- /dev/null +++ b/lib/morris.hover.coffee @@ -0,0 +1,41 @@ +class Morris.Hover + # Displays contextual information in a floating HTML div. + + @defaults: + class: 'morris-hover morris-default-style' + + constructor: (options = {}) -> + @options = $.extend {}, Morris.Hover.defaults, options + @el = $ "
" + @el.hide() + @options.parent.append(@el) + + update: (html, x, y) -> + @html(html) + @show() + @moveTo(x, y) + + html: (content) -> + @el.html(content) + + moveTo: (x, y) -> + parentWidth = @options.parent.innerWidth() + parentHeight = @options.parent.innerHeight() + hoverWidth = @el.outerWidth() + hoverHeight = @el.outerHeight() + left = Math.min(Math.max(0, x - hoverWidth / 2), parentWidth - hoverWidth) + if y? + top = y - hoverHeight - 10 + if top < 0 + top = y + 10 + if top + hoverHeight > parentHeight + top = parentHeight / 2 - hoverHeight / 2 + else + top = parentHeight / 2 - hoverHeight / 2 + @el.css(left: left + "px", top: top + "px") + + show: -> + @el.show() + + hide: -> + @el.hide() diff --git a/lib/morris.line.coffee b/lib/morris.line.coffee index 1c73f5c..918291e 100644 --- a/lib/morris.line.coffee +++ b/lib/morris.line.coffee @@ -9,20 +9,11 @@ class Morris.Line extends Morris.Grid # Some instance variables for later @pointGrow = Raphael.animation r: @options.pointSize + 3, 25, 'linear' @pointShrink = Raphael.animation r: @options.pointSize, 25, 'linear' - # column hilight events - @prevHilight = null - @el.mousemove (evt) => - @updateHilight evt.pageX - if @options.hideHover - @el.mouseout (evt) => - @hilight null - touchHandler = (evt) => - touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0] - @updateHilight touch.pageX - return touch - @el.bind 'touchstart', touchHandler - @el.bind 'touchmove', touchHandler - @el.bind 'touchend', touchHandler + + if @options.hideHover isnt 'always' + @hover = new Morris.Hover(parent: @el) + @on('hovermove', @onHoverMove) + @on('hoverout', @onHoverOut) # Default configuration # @@ -41,20 +32,11 @@ class Morris.Line extends Morris.Grid 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 continuousLine: true + hideHover: false # Do any size-related calculations # @@ -62,7 +44,6 @@ class Morris.Line extends Morris.Grid calc: -> @calcPoints() @generatePaths() - @calcHoverMargins() # calculate series data point coordinates # @@ -72,12 +53,59 @@ class Morris.Line extends Morris.Grid row._x = @transX(row.x) row._y = for y in row.y if y? then @transY(y) else y + row._ymax = Math.min.apply(null, [@bottom].concat(y for y in row._y when y?)) - # calculate hover margins + # hit test - returns the index of the row beneath the given coordinate + # + hitTest: (x, y) -> + # TODO better search algo + for r, index in @data.slice(1) + break if x < (r._x + @data[index]._x) / 2 + index + + # hover movement event handler # # @private - calcHoverMargins: -> - @hoverMargins = ((r._x + @data[i]._x) / 2 for r, i in @data.slice(1)) + onHoverMove: (x, y) => + index = @hitTest(x, y) + @displayHoverForRow(index) + + # hover out event handler + # + # @private + onHoverOut: => + if @options.hideHover is 'auto' + @displayHoverForIndex(null) + + # display a hover popup over the given row + # + # @private + displayHoverForRow: (index) -> + if index? + @hover.update(@hoverContentForRow(index)...) + @hilight(index) + else + @hover.hide() + @hilight() + + # hover content for a point + # + # @private + hoverContentForRow: (index) -> + row = @data[index] + if typeof @options.hoverCallback is 'function' + content = @options.hoverCallback(index, @options) + else + content = "
#{row.label}
" + for y, j in row.y + content += """ +
+ #{@options.labels[j]}: + #{@yLabelFormat(y)} +
+ """ + [content, row._x, row._ymax] + # generate paths for series lines # @@ -98,8 +126,8 @@ class Morris.Line extends Morris.Grid draw: -> @drawXAxis() @drawSeries() - @drawHover() - @hilight(if @options.hideHover then null else @data.length - 1) + if @options.hideHover is false + @displayHoverForRow(@data.length - 1) # draw the x-axis labels # @@ -143,14 +171,14 @@ class Morris.Line extends Morris.Grid path = @paths[i] if path isnt null @r.path(path) - .attr('stroke', @colorForSeries(i)) + .attr('stroke', @colorFor(row, i, 'line')) .attr('stroke-width', @options.lineWidth) @seriesPoints = ([] for i in [0...@options.ykeys.length]) for i in [@options.ykeys.length-1..0] for row in @data if row._y[i]? circle = @r.circle(row._x, row._y[i], @options.pointSize) - .attr('fill', @pointFillColorForSeries(i) || @colorForSeries(i)) + .attr('fill', @colorFor(row, i, 'point')) .attr('stroke-width', @strokeWidthForSeries(i)) .attr('stroke', @strokeForSeries(i)) else @@ -205,61 +233,6 @@ class Morris.Line extends Morris.Grid else null - # draw the hover tooltip - # - # @private - drawHover: -> - # hover labels - @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) - .attr('stroke-width', @options.hoverBorderWidth) - .attr('opacity', @options.hoverOpacity) - @xLabel = @r.text(0, (@options.hoverFontSize * 0.75) - @hoverHeight / 2, '') - .attr('fill', @options.hoverLabelColor) - .attr('font-weight', 'bold') - .attr('font-size', @options.hoverFontSize) - @hoverSet = @r.set() - @hoverSet.push(@hover) - @hoverSet.push(@xLabel) - @yLabels = [] - for i in [0...@options.ykeys.length] - idx = if @cumulative then (@options.ykeys.length - i - 1) else i - yLabel = @r.text(0, @options.hoverFontSize * 1.5 * (idx + 1.5) - @hoverHeight / 2, '') - .attr('fill', @colorForSeries(i)) - .attr('font-size', @options.hoverFontSize) - @yLabels.push(yLabel) - @hoverSet.push(yLabel) - - # @private - updateHover: (index) => - @hoverSet.show() - 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, (l.getBBox().width for l in @yLabels) - maxLabelWidth = Math.max maxLabelWidth, @xLabel.getBBox().width - @hover.attr 'width', maxLabelWidth + @options.hoverPaddingX * 2 - @hover.attr 'x', -@options.hoverPaddingX - maxLabelWidth / 2 - # move to y pos - yloc = Math.min.apply null, (y for y in row._y when y?).concat(@bottom) - if yloc > @hoverHeight + @options.hoverPaddingY * 2 + @options.hoverMargin + @top - yloc = yloc - @hoverHeight / 2 - @options.hoverPaddingY - @options.hoverMargin - else - yloc = yloc + @hoverHeight / 2 + @options.hoverPaddingY + @options.hoverMargin - yloc = Math.max @top + @hoverHeight / 2 + @options.hoverPaddingY, yloc - yloc = Math.min @bottom - @hoverHeight / 2 - @options.hoverPaddingY, yloc - xloc = Math.min @right - maxLabelWidth / 2 - @options.hoverPaddingX, @data[index]._x - xloc = Math.max @left + maxLabelWidth / 2 + @options.hoverPaddingX, xloc - @hoverSet.attr 'transform', "t#{xloc},#{yloc}" - - # @private - hideHover: -> - @hoverSet.hide() - # @private hilight: (index) => if @prevHilight isnt null and @prevHilight isnt index @@ -270,21 +243,7 @@ class Morris.Line extends Morris.Grid for i in [0..@seriesPoints.length-1] if @seriesPoints[i][index] @seriesPoints[i][index].animate @pointGrow - @updateHover index @prevHilight = index - if not index? - @hideHover() - - # @private - updateHilight: (x) => - x -= @el.offset().left - for hoverIndex in [0...@hoverMargins.length] - break if @hoverMargins[hoverIndex] > x - @hilight hoverIndex - - # @private - colorForSeries: (index) -> - @options.lineColors[index % @options.lineColors.length] # @private strokeWidthForSeries: (index) -> @@ -294,9 +253,13 @@ class Morris.Line extends Morris.Grid strokeForSeries: (index) -> @options.pointStrokeColors[index % @options.pointStrokeColors.length] - # @private - pointFillColorForSeries: (index) -> - @options.pointFillColors[index % @options.pointFillColors.length] + colorFor: (row, sidx, type) -> + if typeof @options.lineColors is 'function' + @options.lineColors.call(@, row, sidx, type) + else if type is 'point' + @options.pointFillColors[sidx % @options.pointFillColors.length] || @options.lineColors[sidx % @options.lineColors.length] + else + @options.lineColors[sidx % @options.lineColors.length] # generate a series of label, timestamp pairs for x-axis labels diff --git a/morris.css b/morris.css new file mode 100644 index 0000000..bc68724 --- /dev/null +++ b/morris.css @@ -0,0 +1,2 @@ +.morris-hover{position:absolute;z-index:1000;}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255, 255, 255, 0.8);border:solid 2px rgba(230, 230, 230, 0.8);font-family:sans-serif;font-size:12px;text-align:center;}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0;} +.morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0;} diff --git a/morris.js b/morris.js index 170eec6..e811e56 100644 --- a/morris.js +++ b/morris.js @@ -68,6 +68,7 @@ __extends(Grid, _super); function Grid(options) { + var _this = this; if (typeof options.element === 'string') { this.el = $(document.getElementById(options.element)); } else { @@ -76,6 +77,9 @@ if (!(this.el != null) || this.el.length === 0) { throw new Error("Graph container element not found"); } + if (this.el.css('position') === 'static') { + this.el.css('position', 'relative'); + } this.options = $.extend({}, this.gridDefaults, this.defaults || {}, options); if (this.options.data === void 0 || this.options.data.length === 0) { return; @@ -91,6 +95,24 @@ this.init(); } this.setData(this.options.data); + this.el.bind('mousemove', function(evt) { + var offset; + offset = _this.el.offset(); + return _this.fire('hovermove', evt.pageX - offset.left, evt.pageY - offset.top); + }); + this.el.bind('mouseout', function(evt) { + return _this.fire('hoverout'); + }); + this.el.bind('touchstart touchmove touchend', function(evt) { + var offset, touch; + touch = evt.originalEvent.touches[0] || evt.originalEvent.changedTouches[0]; + offset = _this.el.offset(); + _this.fire('hover', touch.pageX - offset.left, touch.pageY - offset.top); + return touch; + }); + if (this.postInit) { + this.postInit(); + } } Grid.prototype.gridDefaults = { @@ -99,6 +121,7 @@ gridStrokeWidth: 0.5, gridTextColor: '#888', gridTextSize: 12, + hideHover: false, numLines: 5, padding: 25, parseTime: true, @@ -359,6 +382,14 @@ return "" + this.options.preUnits + (Morris.commas(label)) + this.options.postUnits; }; + Grid.prototype.updateHover = function(x, y) { + var hit, _ref; + hit = this.hitTest(x, y); + if (hit != null) { + return (_ref = this.hover).update.apply(_ref, hit); + } + }; + return Grid; })(Morris.EventEmitter); @@ -420,16 +451,78 @@ } }; + Morris.Hover = (function() { + + Hover.defaults = { + "class": 'morris-hover morris-default-style' + }; + + function Hover(options) { + if (options == null) { + options = {}; + } + this.options = $.extend({}, Morris.Hover.defaults, options); + this.el = $("
"); + this.el.hide(); + this.options.parent.append(this.el); + } + + Hover.prototype.update = function(html, x, y) { + this.html(html); + this.show(); + return this.moveTo(x, y); + }; + + Hover.prototype.html = function(content) { + return this.el.html(content); + }; + + Hover.prototype.moveTo = function(x, y) { + var hoverHeight, hoverWidth, left, parentHeight, parentWidth, top; + parentWidth = this.options.parent.innerWidth(); + parentHeight = this.options.parent.innerHeight(); + hoverWidth = this.el.outerWidth(); + hoverHeight = this.el.outerHeight(); + left = Math.min(Math.max(0, x - hoverWidth / 2), parentWidth - hoverWidth); + if (y != null) { + top = y - hoverHeight - 10; + if (top < 0) { + top = y + 10; + if (top + hoverHeight > parentHeight) { + top = parentHeight / 2 - hoverHeight / 2; + } + } + } else { + top = parentHeight / 2 - hoverHeight / 2; + } + return this.el.css({ + left: left + "px", + top: top + "px" + }); + }; + + Hover.prototype.show = function() { + return this.el.show(); + }; + + Hover.prototype.hide = function() { + return this.el.hide(); + }; + + return Hover; + + })(); + 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); + this.onHoverOut = __bind(this.onHoverOut, this); + + this.onHoverMove = __bind(this.onHoverMove, this); if (!(this instanceof Morris.Line)) { return new Morris.Line(options); } @@ -437,32 +530,19 @@ } 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); + if (this.options.hideHover !== 'always') { + this.hover = new Morris.Hover({ + parent: this.el }); + this.on('hovermove', this.onHoverMove); + return this.on('hoverout', this.onHoverOut); } - 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 = { @@ -472,26 +552,16 @@ 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, - continuousLine: true + continuousLine: true, + hideHover: false }; Line.prototype.calc = function() { this.calcPoints(); - this.generatePaths(); - return this.calcHoverMargins(); + return this.generatePaths(); }; Line.prototype.calcPoints = function() { @@ -501,7 +571,7 @@ for (_i = 0, _len = _ref.length; _i < _len; _i++) { row = _ref[_i]; row._x = this.transX(row.x); - _results.push(row._y = (function() { + row._y = (function() { var _j, _len1, _ref1, _results1; _ref1 = row.y; _results1 = []; @@ -514,23 +584,72 @@ } } return _results1; - }).call(this)); + }).call(this); + _results.push(row._ymax = Math.min.apply(null, [this.bottom].concat((function() { + var _j, _len1, _ref1, _results1; + _ref1 = row._y; + _results1 = []; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + y = _ref1[_j]; + if (y != null) { + _results1.push(y); + } + } + return _results1; + })()))); } return _results; }; - Line.prototype.calcHoverMargins = function() { - var i, r; - return this.hoverMargins = (function() { - var _i, _len, _ref, _results; - _ref = this.data.slice(1); - _results = []; - for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { - r = _ref[i]; - _results.push((r._x + this.data[i]._x) / 2); + Line.prototype.hitTest = function(x, y) { + var index, r, _i, _len, _ref; + _ref = this.data.slice(1); + for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) { + r = _ref[index]; + if (x < (r._x + this.data[index]._x) / 2) { + break; } - return _results; - }).call(this); + } + return index; + }; + + Line.prototype.onHoverMove = function(x, y) { + var index; + index = this.hitTest(x, y); + return this.displayHoverForRow(index); + }; + + Line.prototype.onHoverOut = function() { + if (this.options.hideHover === 'auto') { + return this.displayHoverForIndex(null); + } + }; + + Line.prototype.displayHoverForRow = function(index) { + var _ref; + if (index != null) { + (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(index)); + return this.hilight(index); + } else { + this.hover.hide(); + return this.hilight(); + } + }; + + Line.prototype.hoverContentForRow = function(index) { + var content, j, row, y, _i, _len, _ref; + row = this.data[index]; + if (typeof this.options.hoverCallback === 'function') { + content = this.options.hoverCallback(index, this.options); + } else { + content = "
" + row.label + "
"; + _ref = row.y; + for (j = _i = 0, _len = _ref.length; _i < _len; j = ++_i) { + y = _ref[j]; + content += "
\n " + this.options.labels[j] + ":\n " + (this.yLabelFormat(y)) + "\n
"; + } + } + return [content, row._x, row._ymax]; }; Line.prototype.generatePaths = function() { @@ -581,8 +700,9 @@ Line.prototype.draw = function() { this.drawXAxis(); this.drawSeries(); - this.drawHover(); - return this.hilight(this.options.hideHover ? null : this.data.length - 1); + if (this.options.hideHover === false) { + return this.displayHoverForRow(this.data.length - 1); + } }; Line.prototype.drawXAxis = function() { @@ -633,7 +753,7 @@ for (i = _i = _ref = this.options.ykeys.length - 1; _ref <= 0 ? _i <= 0 : _i >= 0; i = _ref <= 0 ? ++_i : --_i) { path = this.paths[i]; if (path !== null) { - this.r.path(path).attr('stroke', this.colorForSeries(i)).attr('stroke-width', this.options.lineWidth); + this.r.path(path).attr('stroke', this.colorFor(row, i, 'line')).attr('stroke-width', this.options.lineWidth); } } this.seriesPoints = (function() { @@ -653,7 +773,7 @@ for (_k = 0, _len = _ref2.length; _k < _len; _k++) { row = _ref2[_k]; if (row._y[i] != null) { - 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)); + circle = this.r.circle(row._x, row._y[i], this.options.pointSize).attr('fill', this.colorFor(row, i, 'point')).attr('stroke-width', this.strokeWidthForSeries(i)).attr('stroke', this.strokeForSeries(i)); } else { circle = null; } @@ -732,76 +852,6 @@ return _results; }; - Line.prototype.drawHover = function() { - var i, idx, yLabel, _i, _ref, _results; - 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(); - this.hoverSet.push(this.hover); - this.hoverSet.push(this.xLabel); - this.yLabels = []; - _results = []; - for (i = _i = 0, _ref = this.options.ykeys.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) { - idx = this.cumulative ? this.options.ykeys.length - i - 1 : i; - yLabel = this.r.text(0, this.options.hoverFontSize * 1.5 * (idx + 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, l, maxLabelWidth, row, xloc, y, yloc, _i, _len, _ref; - this.hoverSet.show(); - 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, (function() { - var _j, _len1, _ref1, _results; - _ref1 = this.yLabels; - _results = []; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - l = _ref1[_j]; - _results.push(l.getBBox().width); - } - return _results; - }).call(this)); - 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, ((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(y); - } - } - return _results; - })()).concat(this.bottom)); - if (yloc > this.hoverHeight + this.options.hoverPaddingY * 2 + this.options.hoverMargin + this.top) { - yloc = yloc - this.hoverHeight / 2 - this.options.hoverPaddingY - this.options.hoverMargin; - } else { - yloc = yloc + this.hoverHeight / 2 + this.options.hoverPaddingY + this.options.hoverMargin; - } - yloc = Math.max(this.top + this.hoverHeight / 2 + this.options.hoverPaddingY, yloc); - yloc = Math.min(this.bottom - this.hoverHeight / 2 - this.options.hoverPaddingY, yloc); - xloc = Math.min(this.right - maxLabelWidth / 2 - this.options.hoverPaddingX, this.data[index]._x); - xloc = Math.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) { @@ -817,27 +867,8 @@ 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; - x -= this.el.offset().left; - for (hoverIndex = _i = 0, _ref = this.hoverMargins.length; 0 <= _ref ? _i < _ref : _i > _ref; hoverIndex = 0 <= _ref ? ++_i : --_i) { - if (this.hoverMargins[hoverIndex] > x) { - break; - } - } - return this.hilight(hoverIndex); - }; - - Line.prototype.colorForSeries = function(index) { - return this.options.lineColors[index % this.options.lineColors.length]; + return this.prevHilight = index; }; Line.prototype.strokeWidthForSeries = function(index) { @@ -848,8 +879,14 @@ return this.options.pointStrokeColors[index % this.options.pointStrokeColors.length]; }; - Line.prototype.pointFillColorForSeries = function(index) { - return this.options.pointFillColors[index % this.options.pointFillColors.length]; + Line.prototype.colorFor = function(row, sidx, type) { + if (typeof this.options.lineColors === 'function') { + return this.options.lineColors.call(this, row, sidx, type); + } else if (type === 'point') { + return this.options.pointFillColors[sidx % this.options.pointFillColors.length] || this.options.lineColors[sidx % this.options.lineColors.length]; + } else { + return this.options.lineColors[sidx % this.options.lineColors.length]; + } }; return Line; @@ -1005,7 +1042,7 @@ row = _ref[_i]; row._x = this.transX(row.x); total = 0; - _results.push(row._y = (function() { + row._y = (function() { var _j, _len1, _ref1, _results1; _ref1 = row.y; _results1 = []; @@ -1015,7 +1052,8 @@ _results1.push(this.transY(total)); } return _results1; - }).call(this)); + }).call(this); + _results.push(row._ymax = row._y[row._y.length - 1]); } return _results; }; @@ -1034,7 +1072,7 @@ Area.prototype.fillForSeries = function(i) { var color; - color = Raphael.rgb2hsl(this.colorForSeries(i)); + color = Raphael.rgb2hsl(this.colorFor(this.data[i], i, 'line')); return Raphael.hsl(color.h, Math.min(255, color.s * 0.75), Math.min(255, color.l * 1.25)); }; @@ -1047,11 +1085,9 @@ __extends(Bar, _super); function Bar(options) { - this.updateHilight = __bind(this.updateHilight, this); + this.onHoverOut = __bind(this.onHoverOut, this); - this.hilight = __bind(this.hilight, this); - - this.updateHover = __bind(this.updateHover, this); + this.onHoverMove = __bind(this.onHoverMove, this); if (!(this instanceof Morris.Bar)) { return new Morris.Bar(options); } @@ -1061,48 +1097,28 @@ } Bar.prototype.init = function() { - var touchHandler, - _this = this; this.cumulative = this.options.stacked; - 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.options.hideHover !== 'always') { + this.hover = new Morris.Hover({ + parent: this.el }); + this.on('hovermove', this.onHoverMove); + return this.on('hoverout', this.onHoverOut); } - 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); }; Bar.prototype.defaults = { barSizeRatio: 0.75, barGap: 3, - barColors: ['#0b62a4', '#7a92a3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'], - hoverPaddingX: 10, - hoverPaddingY: 5, - hoverMargin: 10, - hoverFillColor: '#fff', - hoverBorderColor: '#ccc', - hoverBorderWidth: 2, - hoverOpacity: 0.95, - hoverLabelColor: '#444', - hoverFontSize: 12, - hideHover: false + barColors: ['#0b62a4', '#7a92a3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'] }; Bar.prototype.calc = function() { + var _ref; this.calcBars(); - return this.calcHoverMargins(); + if (this.options.hideHover === false) { + return (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(this.data.length - 1)); + } }; Bar.prototype.calcBars = function() { @@ -1130,23 +1146,9 @@ return _results; }; - Bar.prototype.calcHoverMargins = function() { - var i; - return this.hoverMargins = (function() { - var _i, _ref, _results; - _results = []; - for (i = _i = 1, _ref = this.data.length; 1 <= _ref ? _i < _ref : _i > _ref; i = 1 <= _ref ? ++_i : --_i) { - _results.push(this.left + i * this.width / this.data.length); - } - return _results; - }).call(this); - }; - Bar.prototype.draw = function() { this.drawXAxis(); - this.drawSeries(); - this.drawHover(); - return this.hilight(this.options.hideHover ? null : this.data.length - 1); + return this.drawSeries(); }; Bar.prototype.drawXAxis = function() { @@ -1217,79 +1219,6 @@ }).call(this); }; - Bar.prototype.drawHover = function() { - var i, yLabel, _i, _ref, _results; - 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(); - this.hoverSet.push(this.hover); - this.hoverSet.push(this.xLabel); - this.yLabels = []; - _results = []; - for (i = _i = 0, _ref = this.options.ykeys.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('font-size', this.options.hoverFontSize); - this.yLabels.push(yLabel); - _results.push(this.hoverSet.push(yLabel)); - } - return _results; - }; - - Bar.prototype.updateHover = function(index) { - var i, l, maxLabelWidth, row, xloc, y, yloc, _i, _len, _ref; - this.hoverSet.show(); - 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('fill', this.colorFor(row, i, 'hover')); - this.yLabels[i].attr('text', "" + this.options.labels[i] + ": " + (this.yLabelFormat(y))); - } - maxLabelWidth = Math.max.apply(null, (function() { - var _j, _len1, _ref1, _results; - _ref1 = this.yLabels; - _results = []; - for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - l = _ref1[_j]; - _results.push(l.getBBox().width); - } - return _results; - }).call(this)); - 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 = (this.bottom + this.top) / 2; - 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); - }; - - Bar.prototype.hideHover = function() { - return this.hoverSet.hide(); - }; - - Bar.prototype.hilight = function(index) { - if (index !== null && this.prevHilight !== index) { - this.updateHover(index); - } - this.prevHilight = index; - if (!(index != null)) { - return this.hideHover(); - } - }; - - Bar.prototype.updateHilight = function(x) { - var hoverIndex, _i, _ref; - x -= this.el.offset().left; - for (hoverIndex = _i = 0, _ref = this.hoverMargins.length; 0 <= _ref ? _i < _ref : _i > _ref; hoverIndex = 0 <= _ref ? ++_i : --_i) { - if (this.hoverMargins[hoverIndex] > x) { - break; - } - } - return this.hilight(hoverIndex); - }; - Bar.prototype.colorFor = function(row, sidx, type) { var r, s; if (typeof this.options.barColors === 'function') { @@ -1309,6 +1238,40 @@ } }; + Bar.prototype.hitTest = function(x, y) { + x = Math.max(Math.min(x, this.right), this.left); + return Math.min(this.data.length - 1, Math.floor((x - this.left) / ((this.right - this.left) / this.data.length))); + }; + + Bar.prototype.onHoverMove = function(x, y) { + var index, _ref; + index = this.hitTest(x, y); + return (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(index)); + }; + + Bar.prototype.onHoverOut = function() { + if (this.options.hideHover === 'auto') { + return this.hover.hide(); + } + }; + + Bar.prototype.hoverContentForRow = function(index) { + var content, j, row, x, y, _i, _len, _ref; + if (typeof this.options.hoverCallback === 'function') { + content = this.options.hoverCallback(index, this.options); + } else { + row = this.data[index]; + content = "
" + row.label + "
"; + _ref = row.y; + for (j = _i = 0, _len = _ref.length; _i < _len; j = ++_i) { + y = _ref[j]; + content += "
\n " + this.options.labels[j] + ":\n " + (this.yLabelFormat(y)) + "\n
"; + } + } + x = this.left + (index + 0.5) * (this.right - this.left) / this.data.length; + return [content, x]; + }; + return Bar; })(Morris.Grid); diff --git a/morris.min.js b/morris.min.js index df9ec83..7e55cae 100644 --- a/morris.min.js +++ b/morris.min.js @@ -1 +1 @@ -(function(){var e,t,n,r,i=[].slice,s={}.hasOwnProperty,o=function(e,t){function r(){this.constructor=e}for(var n in t)s.call(t,n)&&(e[n]=t[n]);return r.prototype=t.prototype,e.prototype=new r,e.__super__=t.prototype,e},u=function(e,t){return function(){return e.apply(t,arguments)}},a=[].indexOf||function(e){for(var t=0,n=this.length;tn.length&&(r+=i.slice(n.length)),r):"-"},t.pad2=function(e){return(e<10?"0":"")+e},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 o(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",goals:[],goalStrokeWidth:1,goalLineColors:["#666633","#999966","#cc6666","#663333"],events:[],eventStrokeWidth:1,eventLineColors:["#005a04","#ccffbb","#3a5f0b","#005502"]},r.prototype.setData=function(e,n){var r,i,s,o,u,a,f,l,c,h,p,d;n==null&&(n=!0),h=this.cumulative?0:null,p=this.cumulative?0:null,this.options.goals.length>0&&(u=Math.min.apply(null,this.options.goals),o=Math.max.apply(null,this.options.goals),p=p!=null?Math.min(p,u):u,h=h!=null?Math.max(h,o):o),this.data=function(){var n,r,o;o=[];for(s=n=0,r=e.length;nt.x)-(t.x>e.x)})),this.xmin=this.data[0].x,this.xmax=this.data[this.data.length-1].x,this.events=[],this.options.parseTime&&this.options.events.length>0&&(this.events=function(){var e,n,i,s;i=this.options.events,s=[];for(e=0,n=i.length;e5?(this.ymax=parseInt(this.options.ymax.slice(5),10),h!=null&&(this.ymax=Math.max(h,this.ymax))):this.ymax=h!=null?h:0: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=parseInt(this.options.ymin.slice(5),10),p!=null&&(this.ymin=Math.min(p,this.ymin))):this.ymin=p!==null?p:0:this.ymin=parseInt(this.options.ymin,10):this.ymin=this.options.ymin,this.ymin===this.ymax&&(p&&(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(n)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(),this.drawGoals(),this.drawEvents();if(this.draw)return this.draw()},r.prototype.drawGoals=function(){var e,t,n,r,i,s;i=this.options.goals,s=[];for(t=n=0,r=i.length;n=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(e){function n(e){this.updateHilight=u(this.updateHilight,this),this.hilight=u(this.hilight,this),this.updateHover=u(this.updateHover,this);if(!(this instanceof t.Line))return new t.Line(e);n.__super__.constructor.call(this,e)}return o(n,e),n.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)},n.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,continuousLine:!0},n.prototype.calc=function(){return this.calcPoints(),this.generatePaths(),this.calcHoverMargins()},n.prototype.calcPoints=function(){var e,t,n,r,i,s;i=this.data,s=[];for(n=0,r=i.length;nu;r=0<=u?++o:--o)s=this.options.smooth===!0||(f=this.options.ykeys[r],a.call(this.options.smooth,f)>=0),n=function(){var e,t,n,s;n=this.data,s=[];for(e=0,t=n.length;e1?l.push(t.Line.createPath(n,s,this.bottom)):l.push(null);return l}.call(this)},n.prototype.draw=function(){return this.drawXAxis(),this.drawSeries(),this.drawHover(),this.hilight(this.options.hideHover?null:this.data.length-1)},n.prototype.drawXAxis=function(){var e,n,r,i,s,o,u,a,f,l,c=this;u=this.bottom+this.options.gridTextSize*1.25,o=50,i=null,e=function(e,t){var n,r;return n=c.r.text(c.transX(t),u,e).attr("font-size",c.options.gridTextSize).attr("fill",c.options.gridTextColor),r=n.getBBox(),(i==null||i>=r.x+r.width)&&r.x>=0&&r.x+r.width=0;t=o<=0?++i:--i)n=this.paths[t],n!==null&&this.r.path(n).attr("stroke",this.colorForSeries(t)).attr("stroke-width",this.options.lineWidth);this.seriesPoints=function(){var e,n,r;r=[];for(t=e=0,n=this.options.ykeys.length;0<=n?en;t=0<=n?++e:--e)r.push([]);return r}.call(this),a=[];for(t=s=u=this.options.ykeys.length-1;u<=0?s<=0:s>=0;t=u<=0?++s:--s)a.push(function(){var n,i,s,o;s=this.data,o=[];for(n=0,i=s.length;ni;e=0<=i?++r:--r)t=this.cumulative?this.options.ykeys.length-e-1:e,n=this.r.text(0,this.options.hoverFontSize*1.5*(t+1.5)-this.hoverHeight/2,"").attr("fill",this.colorForSeries(e)).attr("font-size",this.options.hoverFontSize),this.yLabels.push(n),s.push(this.hoverSet.push(n));return s},n.prototype.updateHover=function(e){var t,n,r,i,s,o,u,a,f,l;this.hoverSet.show(),i=this.data[e],this.xLabel.attr("text",i.label),l=i.y;for(t=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[e]._x),s=Math.max(this.left+r/2+this.options.hoverPaddingX,s),this.hoverSet.attr("transform","t"+s+","+u)},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;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)},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.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={decade:{span:1728e8,start:function(e){return new Date(e.getFullYear()-e.getFullYear()%10,0,1)},fmt:function(e){return""+e.getFullYear()},incr:function(e){return e.setFullYear(e.getFullYear()+10)}},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=["decade","year","month","day","hour","30min","15min","10min","5min","minute","30sec","15sec","10sec","5sec","second"],t.Area=function(e){function n(e){if(!(this instanceof t.Area))return new t.Area(e);this.cumulative=!0,n.__super__.constructor.call(this,e)}return o(n,e),n.prototype.calcPoints=function(){var e,t,n,r,i,s,o;s=this.data,o=[];for(r=0,i=s.length;r=0;e=i<=0?++r:--r)t=this.paths[e],t!==null&&(t+="L"+this.transX(this.xmax)+","+this.bottom+"L"+this.transX(this.xmin)+","+this.bottom+"Z",this.r.path(t).attr("fill",this.fillForSeries(e)).attr("stroke-width",0));return n.__super__.drawSeries.call(this)},n.prototype.fillForSeries=function(e){var t;return t=Raphael.rgb2hsl(this.colorForSeries(e)),Raphael.hsl(t.h,Math.min(255,t.s*.75),Math.min(255,t.l*1.25))},n}(t.Line),t.Bar=function(n){function r(n){this.updateHilight=u(this.updateHilight,this),this.hilight=u(this.hilight,this),this.updateHover=u(this.updateHover,this);if(!(this instanceof t.Bar))return new t.Bar(n);r.__super__.constructor.call(this,e.extend({},n,{parseTime:!1}))}return o(r,n),r.prototype.init=function(){var e,t=this;return this.cumulative=this.options.stacked,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={barSizeRatio:.75,barGap:3,barColors:["#0b62a4","#7a92a3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],hoverPaddingX:10,hoverPaddingY:5,hoverMargin:10,hoverFillColor:"#fff",hoverBorderColor:"#ccc",hoverBorderWidth:2,hoverOpacity:.95,hoverLabelColor:"#444",hoverFontSize:12,hideHover:!1},r.prototype.calc=function(){return this.calcBars(),this.calcHoverMargins()},r.prototype.calcBars=function(){var e,t,n,r,i,s,o;s=this.data,o=[];for(e=r=0,i=s.length;rn;e=1<=n?++t:--t)r.push(this.left+e*this.width/this.data.length);return r}.call(this)},r.prototype.draw=function(){return this.drawXAxis(),this.drawSeries(),this.drawHover(),this.hilight(this.options.hideHover?null:this.data.length-1)},r.prototype.drawXAxis=function(){var e,t,n,r,i,s,o,u,a,f;o=this.bottom+this.options.gridTextSize*1.25,s=50,r=null,f=[];for(e=u=0,a=this.data.length;0<=a?ua;e=0<=a?++u:--u)i=this.data[this.data.length-1-e],t=this.r.text(i._x,o,i.label).attr("font-size",this.options.gridTextSize).attr("fill",this.options.gridTextColor),n=t.getBBox(),(r==null||r>=n.x+n.width)&&n.x>=0&&n.x+n.width=0?this.transY(0):null,this.bars=function(){var u,d,v,m;v=this.data,m=[];for(r=u=0,d=v.length;ur;e=0<=r?++n:--n)t=this.r.text(0,this.options.hoverFontSize*1.5*(e+1.5)-this.hoverHeight/2,"").attr("font-size",this.options.hoverFontSize),this.yLabels.push(t),i.push(this.hoverSet.push(t));return i},r.prototype.updateHover=function(e){var t,n,r,i,s,o,u,a,f,l;this.hoverSet.show(),i=this.data[e],this.xLabel.attr("text",i.label),l=i.y;for(t=a=0,f=l.length;ar;t=0<=r?++n:--n)if(this.hoverMargins[t]>e)break;return this.hilight(t)},r.prototype.colorFor=function(e,t,n){var r,i;return typeof this.options.barColors=="function"?(r={x:e.x,y:e.y[t],label:e.label},i={index:t,key:this.options.ykeys[t],label:this.options.labels[t]},this.options.barColors.call(this,r,i,n)):this.options.barColors[t%this.options.barColors.length]},r}(t.Grid),t.Donut=function(){function n(n){this.select=u(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.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 o(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)}).call(this); \ No newline at end of file +(function(){var e,t,n,r,i=[].slice,s={}.hasOwnProperty,o=function(e,t){function r(){this.constructor=e}for(var n in t)s.call(t,n)&&(e[n]=t[n]);return r.prototype=t.prototype,e.prototype=new r,e.__super__=t.prototype,e},u=function(e,t){return function(){return e.apply(t,arguments)}},a=[].indexOf||function(e){for(var t=0,n=this.length;tn.length&&(r+=i.slice(n.length)),r):"-"},t.pad2=function(e){return(e<10?"0":"")+e},t.Grid=function(n){function r(t){var n=this;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.el.css("position")==="static"&&this.el.css("position","relative"),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),this.el.bind("mousemove",function(e){var t;return t=n.el.offset(),n.fire("hovermove",e.pageX-t.left,e.pageY-t.top)}),this.el.bind("mouseout",function(e){return n.fire("hoverout")}),this.el.bind("touchstart touchmove touchend",function(e){var t,r;return r=e.originalEvent.touches[0]||e.originalEvent.changedTouches[0],t=n.el.offset(),n.fire("hover",r.pageX-t.left,r.pageY-t.top),r}),this.postInit&&this.postInit()}return o(r,n),r.prototype.gridDefaults={dateFormat:null,gridLineColor:"#aaa",gridStrokeWidth:.5,gridTextColor:"#888",gridTextSize:12,hideHover:!1,numLines:5,padding:25,parseTime:!0,postUnits:"",preUnits:"",ymax:"auto",ymin:"auto 0",goals:[],goalStrokeWidth:1,goalLineColors:["#666633","#999966","#cc6666","#663333"],events:[],eventStrokeWidth:1,eventLineColors:["#005a04","#ccffbb","#3a5f0b","#005502"]},r.prototype.setData=function(e,n){var r,i,s,o,u,a,f,l,c,h,p,d;n==null&&(n=!0),h=this.cumulative?0:null,p=this.cumulative?0:null,this.options.goals.length>0&&(u=Math.min.apply(null,this.options.goals),o=Math.max.apply(null,this.options.goals),p=p!=null?Math.min(p,u):u,h=h!=null?Math.max(h,o):o),this.data=function(){var n,r,o;o=[];for(s=n=0,r=e.length;nt.x)-(t.x>e.x)})),this.xmin=this.data[0].x,this.xmax=this.data[this.data.length-1].x,this.events=[],this.options.parseTime&&this.options.events.length>0&&(this.events=function(){var e,n,i,s;i=this.options.events,s=[];for(e=0,n=i.length;e5?(this.ymax=parseInt(this.options.ymax.slice(5),10),h!=null&&(this.ymax=Math.max(h,this.ymax))):this.ymax=h!=null?h:0: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=parseInt(this.options.ymin.slice(5),10),p!=null&&(this.ymin=Math.min(p,this.ymin))):this.ymin=p!==null?p:0:this.ymin=parseInt(this.options.ymin,10):this.ymin=this.options.ymin,this.ymin===this.ymax&&(p&&(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(n)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(),this.drawGoals(),this.drawEvents();if(this.draw)return this.draw()},r.prototype.drawGoals=function(){var e,t,n,r,i,s;i=this.options.goals,s=[];for(t=n=0,r=i.length;n=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.prototype.updateHover=function(e,t){var n,r;n=this.hitTest(e,t);if(n!=null)return(r=this.hover).update.apply(r,n)},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.Hover=function(){function n(n){n==null&&(n={}),this.options=e.extend({},t.Hover.defaults,n),this.el=e("
"),this.el.hide(),this.options.parent.append(this.el)}return n.defaults={"class":"morris-hover morris-default-style"},n.prototype.update=function(e,t,n){return this.html(e),this.show(),this.moveTo(t,n)},n.prototype.html=function(e){return this.el.html(e)},n.prototype.moveTo=function(e,t){var n,r,i,s,o,u;return o=this.options.parent.innerWidth(),s=this.options.parent.innerHeight(),r=this.el.outerWidth(),n=this.el.outerHeight(),i=Math.min(Math.max(0,e-r/2),o-r),t!=null?(u=t-n-10,u<0&&(u=t+10,u+n>s&&(u=s/2-n/2))):u=s/2-n/2,this.el.css({left:i+"px",top:u+"px"})},n.prototype.show=function(){return this.el.show()},n.prototype.hide=function(){return this.el.hide()},n}(),t.Line=function(e){function n(e){this.hilight=u(this.hilight,this),this.onHoverOut=u(this.onHoverOut,this),this.onHoverMove=u(this.onHoverMove,this);if(!(this instanceof t.Line))return new t.Line(e);n.__super__.constructor.call(this,e)}return o(n,e),n.prototype.init=function(){this.pointGrow=Raphael.animation({r:this.options.pointSize+3},25,"linear"),this.pointShrink=Raphael.animation({r:this.options.pointSize},25,"linear");if(this.options.hideHover!=="always")return this.hover=new t.Hover({parent:this.el}),this.on("hovermove",this.onHoverMove),this.on("hoverout",this.onHoverOut)},n.prototype.defaults={lineWidth:3,pointSize:4,lineColors:["#0b62a4","#7A92A3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],pointWidths:[1],pointStrokeColors:["#ffffff"],pointFillColors:[],smooth:!0,xLabels:"auto",xLabelFormat:null,continuousLine:!0,hideHover:!1},n.prototype.calc=function(){return this.calcPoints(),this.generatePaths()},n.prototype.calcPoints=function(){var e,t,n,r,i,s;i=this.data,s=[];for(n=0,r=i.length;n"+r.label+"",u=r.y;for(n=s=0,o=u.length;s\n "+this.options.labels[n]+":\n "+this.yLabelFormat(i)+"\n"}return[t,r._x,r._ymax]},n.prototype.generatePaths=function(){var e,n,r,i,s;return this.paths=function(){var o,u,f,l;l=[];for(r=o=0,u=this.options.ykeys.length;0<=u?ou;r=0<=u?++o:--o)s=this.options.smooth===!0||(f=this.options.ykeys[r],a.call(this.options.smooth,f)>=0),n=function(){var e,t,n,s;n=this.data,s=[];for(e=0,t=n.length;e1?l.push(t.Line.createPath(n,s,this.bottom)):l.push(null);return l}.call(this)},n.prototype.draw=function(){this.drawXAxis(),this.drawSeries();if(this.options.hideHover===!1)return this.displayHoverForRow(this.data.length-1)},n.prototype.drawXAxis=function(){var e,n,r,i,s,o,u,a,f,l,c=this;u=this.bottom+this.options.gridTextSize*1.25,o=50,i=null,e=function(e,t){var n,r;return n=c.r.text(c.transX(t),u,e).attr("font-size",c.options.gridTextSize).attr("fill",c.options.gridTextColor),r=n.getBBox(),(i==null||i>=r.x+r.width)&&r.x>=0&&r.x+r.width=0;t=o<=0?++i:--i)n=this.paths[t],n!==null&&this.r.path(n).attr("stroke",this.colorFor(r,t,"line")).attr("stroke-width",this.options.lineWidth);this.seriesPoints=function(){var e,n,r;r=[];for(t=e=0,n=this.options.ykeys.length;0<=n?en;t=0<=n?++e:--e)r.push([]);return r}.call(this),a=[];for(t=s=u=this.options.ykeys.length-1;u<=0?s<=0:s>=0;t=u<=0?++s:--s)a.push(function(){var n,i,s,o;s=this.data,o=[];for(n=0,i=s.length;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);return this.prevHilight=e},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.colorFor=function(e,t,n){return typeof this.options.lineColors=="function"?this.options.lineColors.call(this,e,t,n):n==="point"?this.options.pointFillColors[t%this.options.pointFillColors.length]||this.options.lineColors[t%this.options.lineColors.length]:this.options.lineColors[t%this.options.lineColors.length]},n}(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={decade:{span:1728e8,start:function(e){return new Date(e.getFullYear()-e.getFullYear()%10,0,1)},fmt:function(e){return""+e.getFullYear()},incr:function(e){return e.setFullYear(e.getFullYear()+10)}},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=["decade","year","month","day","hour","30min","15min","10min","5min","minute","30sec","15sec","10sec","5sec","second"],t.Area=function(e){function n(e){if(!(this instanceof t.Area))return new t.Area(e);this.cumulative=!0,n.__super__.constructor.call(this,e)}return o(n,e),n.prototype.calcPoints=function(){var e,t,n,r,i,s,o;s=this.data,o=[];for(r=0,i=s.length;r=0;e=i<=0?++r:--r)t=this.paths[e],t!==null&&(t+="L"+this.transX(this.xmax)+","+this.bottom+"L"+this.transX(this.xmin)+","+this.bottom+"Z",this.r.path(t).attr("fill",this.fillForSeries(e)).attr("stroke-width",0));return n.__super__.drawSeries.call(this)},n.prototype.fillForSeries=function(e){var t;return t=Raphael.rgb2hsl(this.colorFor(this.data[e],e,"line")),Raphael.hsl(t.h,Math.min(255,t.s*.75),Math.min(255,t.l*1.25))},n}(t.Line),t.Bar=function(n){function r(n){this.onHoverOut=u(this.onHoverOut,this),this.onHoverMove=u(this.onHoverMove,this);if(!(this instanceof t.Bar))return new t.Bar(n);r.__super__.constructor.call(this,e.extend({},n,{parseTime:!1}))}return o(r,n),r.prototype.init=function(){this.cumulative=this.options.stacked;if(this.options.hideHover!=="always")return this.hover=new t.Hover({parent:this.el}),this.on("hovermove",this.onHoverMove),this.on("hoverout",this.onHoverOut)},r.prototype.defaults={barSizeRatio:.75,barGap:3,barColors:["#0b62a4","#7a92a3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"]},r.prototype.calc=function(){var e;this.calcBars();if(this.options.hideHover===!1)return(e=this.hover).update.apply(e,this.hoverContentForRow(this.data.length-1))},r.prototype.calcBars=function(){var e,t,n,r,i,s,o;s=this.data,o=[];for(e=r=0,i=s.length;ra;e=0<=a?++u:--u)i=this.data[this.data.length-1-e],t=this.r.text(i._x,o,i.label).attr("font-size",this.options.gridTextSize).attr("fill",this.options.gridTextColor),n=t.getBBox(),(r==null||r>=n.x+n.width)&&n.x>=0&&n.x+n.width=0?this.transY(0):null,this.bars=function(){var u,d,v,m;v=this.data,m=[];for(r=u=0,d=v.length;u"+r.label+"",a=r.y;for(n=o=0,u=a.length;o\n "+this.options.labels[n]+":\n "+this.yLabelFormat(s)+"\n"}return i=this.left+(e+.5)*(this.right-this.left)/this.data.length,[t,i]},r}(t.Grid),t.Donut=function(){function n(n){this.select=u(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.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 o(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)}).call(this); \ No newline at end of file diff --git a/package.json b/package.json index 969e9dc..b23a0eb 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ }, "devDependencies": { "grunt-coffee": "~> 0.0.6", - "grunt-mocha": "~> 0.1.7" + "grunt-mocha": "~> 0.1.7", + "grunt-less": "~> 0.1.7" } } diff --git a/spec/lib/hover_spec.coffee b/spec/lib/hover_spec.coffee new file mode 100644 index 0000000..868f1a3 --- /dev/null +++ b/spec/lib/hover_spec.coffee @@ -0,0 +1,64 @@ +describe "Morris.Hover", -> + + describe "with dummy content", -> + + beforeEach -> + parent = $('
') + .appendTo($('#test')) + @hover = new Morris.Hover(parent: parent) + @element = $('#test .morris-hover') + + it "should initialise a hidden, empty popup", -> + @element.should.exist + @element.should.be.hidden + @element.should.be.empty + + describe "#show", -> + it "should show the popup", -> + @hover.show() + @element.should.be.visible + + describe "#hide", -> + it "should hide the popup", -> + @hover.show() + @hover.hide() + @element.should.be.hidden + + describe "#html", -> + it "should replace the contents of the element", -> + @hover.html('
Foobarbaz
') + @element.should.have.html('
Foobarbaz
') + + describe "#moveTo", -> + beforeEach -> + @hover.html('
') + + it "should place the popup directly above the given point", -> + @hover.moveTo(100, 150) + @element.should.have.css('left', '50px') + @element.should.have.css('top', '40px') + + it "should place the popup below the given point if it does not fit above", -> + @hover.moveTo(100, 50) + @element.should.have.css('left', '50px') + @element.should.have.css('top', '60px') + + it "should center the popup vertically if it will not fit above or below", -> + @hover.moveTo(100, 100) + @element.should.have.css('left', '50px') + @element.should.have.css('top', '40px') + + it "should center the popup vertically if no y value is supplied", -> + @hover.moveTo(100) + @element.should.have.css('left', '50px') + @element.should.have.css('top', '40px') + + describe "#update", -> + it "should update content, show and reposition the popup", -> + hover = new Morris.Hover(parent: $('#test')) + html = "
Hello, Everyone!
" + hover.update(html, 150, 200) + el = $('#test .morris-hover') + el.should.have.css('left', '100px') + el.should.have.css('top', '90px') + el.should.have.text('Hello, Everyone!') diff --git a/spec/lib/line/line_spec.coffee b/spec/lib/line/line_spec.coffee index aa09cc3..b1c4b72 100644 --- a/spec/lib/line/line_spec.coffee +++ b/spec/lib/line/line_spec.coffee @@ -29,9 +29,8 @@ describe 'Morris.Line', -> chart.strokeForSeries(0).should.equal red chart.strokeWidthForSeries(1).should.equal 2 chart.strokeForSeries(1).should.equal blue - (null == chart.pointFillColorForSeries(0)).should.be - (chart.pointFillColorForSeries(0) || chart.colorForSeries(0)).should.equal chart.colorForSeries(0) - chart.pointFillColorForSeries(1).should.equal red + chart.colorFor(chart.data[0], 0, 'point').should.equal chart.colorFor(chart.data[0], 0, 'line') + chart.colorFor(chart.data[1], 1, 'point').should.equal red describe 'generating column labels', -> diff --git a/spec/specs.html b/spec/specs.html index 62dce17..24bdc41 100644 --- a/spec/specs.html +++ b/spec/specs.html @@ -3,6 +3,7 @@ morris.js tests + @@ -15,6 +16,7 @@ + - +