From 9bdabf1d0d7c9ad98750294ff0264c99a484abfe Mon Sep 17 00:00:00 2001 From: Marcin Chwedziak Date: Sun, 18 Nov 2012 20:02:46 +0100 Subject: [PATCH] first version of hover refactored to be 100% html --- examples/area.html | 1 + examples/bar-colors.html | 1 + examples/bar.html | 1 + examples/days.html | 1 + examples/decimal.html | 1 + examples/donut-formatter.html | 1 + examples/donut.html | 1 + examples/events.html | 1 + examples/goals.html | 1 + examples/months-no-smooth.html | 1 + examples/negative.html | 1 + examples/non-continuous.html | 1 + examples/non-date.html | 1 + examples/quarters.html | 1 + examples/timestamps.html | 1 + examples/updating.html | 1 + examples/weeks.html | 1 + examples/years.html | 1 + grunt.js | 15 +- less/morris.core.less | 19 + lib/morris.area.coffee | 2 +- lib/morris.bar.coffee | 119 +---- lib/morris.coffee | 101 ++++- lib/morris.grid.coffee | 2 + lib/morris.hover.coffee | 113 +++++ lib/morris.line.coffee | 225 ++-------- morris.css | 2 + morris.js | 797 +++++++++++++++++---------------- morris.min.js | 2 +- package.json | 3 +- spec/lib/line/line_spec.coffee | 5 +- 31 files changed, 751 insertions(+), 672 deletions(-) create mode 100644 less/morris.core.less create mode 100644 lib/morris.hover.coffee create mode 100644 morris.css 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.html index 3a93cf5..331735b 100644 --- a/examples/decimal.html +++ b/examples/decimal.html @@ -7,6 +7,7 @@ +

Decimal Data

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/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..02f089e --- /dev/null +++ b/less/morris.core.less @@ -0,0 +1,19 @@ +div.morris-popup { + border-radius: 10px; + position: absolute; + z-index: 1000; + padding: 6px; + font: normal 13px/16px Arial, sans-serif; + color: #666; + background: rgba(255,255,255,.8); + border: solid 2px rgba(230,230,230,.8); + + h4, p { + font: normal 13px/16px Arial, sans-serif; + text-align: center; + color: #666; + margin: 0; + } + + h4 { font-weight: bold; } +} \ No newline at end of file diff --git a/lib/morris.area.coffee b/lib/morris.area.coffee index 53b8130..819ef8c 100644 --- a/lib/morris.area.coffee +++ b/lib/morris.area.coffee @@ -31,7 +31,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 518bebe..f8a02cd 100644 --- a/lib/morris.bar.coffee +++ b/lib/morris.bar.coffee @@ -1,29 +1,22 @@ class Morris.Bar extends Morris.Grid - # Initialise the graph. - # + @include Morris.Hover + + # override hoverCalculatePosition + hoverGetPosition: (index) -> + [x, y] = Morris.Hover.hoverGetPosition.call(this, index) + + [x, (@top + @bottom)/2 - @hoverHeight/2] + constructor: (options) -> return new Morris.Bar(options) unless (@ instanceof Morris.Bar) super($.extend {}, options, parseTime: false) - # setup event handlers - # init: -> - @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 - - # Default configuration - # + @hoverConfigure @options.hoverOptions + + postInit: -> + @hoverInit() + defaults: barSizeRatio: 0.75 barGap: 3 @@ -36,23 +29,13 @@ 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() + @hoverCalculateMargins() # calculate series data bars coordinates and sizes # @@ -63,21 +46,12 @@ 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 # # @private @@ -125,69 +99,6 @@ 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 diff --git a/lib/morris.coffee b/lib/morris.coffee index 87f4d52..8cac223 100644 --- a/lib/morris.coffee +++ b/lib/morris.coffee @@ -2,10 +2,27 @@ Morris = window.Morris = {} $ = jQuery +# Very simple multiple-inheritance implementation. +# +# @private +class Morris.Module + @extend: (obj) -> + for key, value of obj when key not in ['extended', 'included'] + @[key] = value + + obj.extended?.apply(@) + this + + @include: (obj) -> + for key, value of obj when key not in ['extended', 'included'] + @::[key] = value + + obj.included?.apply(@) + # Very simple event-emitter class. # # @private -class Morris.EventEmitter +class Morris.EventEmitter extends Morris.Module on: (name, handler) -> unless @handlers? @handlers = {} @@ -40,3 +57,85 @@ Morris.commas = (num) -> # @example # Morris.pad2(1) -> '01' Morris.pad2 = (number) -> (if number < 10 then '0' else '') + number + +# generate a series of label, timestamp pairs for x-axis labels +# +# @private +Morris.labelSeries = (dmin, dmax, pxwidth, specName, xLabelFormat) -> + ddensity = 200 * (dmax - dmin) / pxwidth # seconds per `margin` pixels + d0 = new Date(dmin) + spec = Morris.LABEL_SPECS[specName] + # if the spec doesn't exist, search for the closest one in the list + if spec is undefined + for name in Morris.AUTO_LABEL_ORDER + s = Morris.LABEL_SPECS[name] + if ddensity >= s.span + spec = s + break + # if we run out of options, use second-intervals + if spec is undefined + spec = Morris.LABEL_SPECS["second"] + # check if there's a user-defined formatting function + if xLabelFormat + spec = $.extend({}, spec, {fmt: xLabelFormat}) + # calculate labels + d = spec.start(d0) + ret = [] + while (t = d.getTime()) <= dmax + if t >= dmin + ret.push [spec.fmt(d), t] + spec.incr(d) + return ret + +# @private +minutesSpecHelper = (interval) -> + span: interval * 60 * 1000 + start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours()) + fmt: (d) -> "#{Morris.pad2(d.getHours())}:#{Morris.pad2(d.getMinutes())}" + incr: (d) -> d.setMinutes(d.getMinutes() + interval) + +# @private +secondsSpecHelper = (interval) -> + span: interval * 1000 + start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes()) + fmt: (d) -> "#{Morris.pad2(d.getHours())}:#{Morris.pad2(d.getMinutes())}:#{Morris.pad2(d.getSeconds())}" + incr: (d) -> d.setSeconds(d.getSeconds() + interval) + +Morris.LABEL_SPECS = + "decade": + span: 172800000000 # 10 * 365 * 24 * 60 * 60 * 1000 + start: (d) -> new Date(d.getFullYear() - d.getFullYear() % 10, 0, 1) + fmt: (d) -> "#{d.getFullYear()}" + incr: (d) -> d.setFullYear(d.getFullYear() + 10) + "year": + span: 17280000000 # 365 * 24 * 60 * 60 * 1000 + start: (d) -> new Date(d.getFullYear(), 0, 1) + fmt: (d) -> "#{d.getFullYear()}" + incr: (d) -> d.setFullYear(d.getFullYear() + 1) + "month": + span: 2419200000 # 28 * 24 * 60 * 60 * 1000 + start: (d) -> new Date(d.getFullYear(), d.getMonth(), 1) + fmt: (d) -> "#{d.getFullYear()}-#{Morris.pad2(d.getMonth() + 1)}" + incr: (d) -> d.setMonth(d.getMonth() + 1) + "day": + span: 86400000 # 24 * 60 * 60 * 1000 + start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate()) + fmt: (d) -> "#{d.getFullYear()}-#{Morris.pad2(d.getMonth() + 1)}-#{Morris.pad2(d.getDate())}" + incr: (d) -> d.setDate(d.getDate() + 1) + "hour": minutesSpecHelper(60) + "30min": minutesSpecHelper(30) + "15min": minutesSpecHelper(15) + "10min": minutesSpecHelper(10) + "5min": minutesSpecHelper(5) + "minute": minutesSpecHelper(1) + "30sec": secondsSpecHelper(30) + "15sec": secondsSpecHelper(15) + "10sec": secondsSpecHelper(10) + "5sec": secondsSpecHelper(5) + "second": secondsSpecHelper(1) + +Morris.AUTO_LABEL_ORDER = [ + "decade", "year", "month", "day", "hour", + "30min", "15min", "10min", "5min", "minute", + "30sec", "15sec", "10sec", "5sec", "second" +] \ No newline at end of file diff --git a/lib/morris.grid.coffee b/lib/morris.grid.coffee index adfce69..8936945 100644 --- a/lib/morris.grid.coffee +++ b/lib/morris.grid.coffee @@ -35,6 +35,8 @@ class Morris.Grid extends Morris.EventEmitter # load data @setData @options.data + + @postInit() if @postInit # Default options # diff --git a/lib/morris.hover.coffee b/lib/morris.hover.coffee new file mode 100644 index 0000000..0ccf0e1 --- /dev/null +++ b/lib/morris.hover.coffee @@ -0,0 +1,113 @@ +Morris.Hover = + hoverConfigure: (options) -> + @hoverOptions = $.extend {}, @hoverDefaults, options ? {} + + hoverInit: -> + if @hoverOptions.enableHover + @hover = @hoverBuild() + @hoverBindEvents() + @hoverShow(if @hoverOptions.hideHover then null else @data.length - 1) + + hoverDefaults: + enableHover: true + popupClass: "morris-popup" + hideHover: false + allowOverflow: false + pointMargin: 10 + hoverFill: (index, row) -> @hoverFill(index, row) + + hoverBindEvents: -> + @el.mousemove (evt) => + @hoverUpdate evt.pageX + if @hoverOptions.hideHover + @el.mouseout (evt) => + @hoverShow null + touchHandler = (evt) => + touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0] + @hoverUpdate touch.pageX + return touch + @el.bind 'touchstart', touchHandler + @el.bind 'touchmove', touchHandler + @el.bind 'touchend', touchHandler + + @hover.mousemove (evt) -> evt.stopPropagation() + @hover.mouseout (evt) -> evt.stopPropagation() + @hover.bind 'touchstart', (evt) -> evt.stopPropagation() + @hover.bind 'touchmove', (evt) -> evt.stopPropagation() + @hover.bind 'touchend', (evt) -> evt.stopPropagation() + + hoverCalculateMargins: -> + @hoverMargins = for i in [1...@data.length] + @left + i * @width / @data.length + + hoverBuild: -> + hover = $ "
" + hover.addClass "#{@hoverOptions.popupClass} js-morris-popup" + hover.appendTo @el + hover.hide() + hover + + hoverUpdate: (x) -> + x -= @el.offset().left + for hoverIndex in [0...@hoverMargins.length] + break if @hoverMargins[hoverIndex] > x + @hoverShow hoverIndex + + hoverShow: (index) -> + if index isnt null + @hover.html("") + @hoverOptions.hoverFill.call(@, index, @data[index]) + @hoverPosition(index) + @fire "hover.show", index + @hover.show() + if not index? + @hoverHide() + + hoverHide: -> + @hover.hide() + + colorFor: (row, i, type) -> "inherit" + yLabelFormat: (label) -> Morris.commas(label) + + hoverPosition: (index) -> + [x, y] = @hoverGetPosition index + + @hover.css + top: "#{@el.offset().top + y}px" + left: "#{@el.offset().left + x}px" + + hoverGetPosition: (index) -> + row = @data[index] + + @hoverWidth = @hover.outerWidth(true) + @hoverHeight = @hover.outerHeight(true) + + miny = y = Math.min.apply(null, (y for y in row._y when y isnt null).concat(@bottom)) + + x = row._x - @hoverWidth/2 + y = miny + y = y - @hoverHeight - @hoverOptions.pointMargin + + unless @hoverOptions.allowOverflow + if x < @left + x = row._x + @hoverOptions.pointMargin + else if x > @right - @hoverWidth + x = row._x - @hoverWidth - @hoverOptions.pointMargin + + y = Math.max y, @top + y = Math.min y, (@bottom - @hoverHeight - @hoverOptions.pointMargin) + + if y - miny < @hoverWidth + @hoverOptions.pointMargin + y = miny + @hoverOptions.pointMargin + + [x, y] + + hoverFill: (index, row) -> + xLabel = $ "

" + xLabel.text row.label + xLabel.appendTo @hover + for y, i in row.y + yLabel = $ "

" + yLabel.css "color", @colorFor(row, i, "hover") + yLabel.text "#{@options.labels[i]}: #{@yLabelFormat(y)}" + yLabel.appendTo @hover \ No newline at end of file diff --git a/lib/morris.line.coffee b/lib/morris.line.coffee index 12c4a90..4a4475e 100644 --- a/lib/morris.line.coffee +++ b/lib/morris.line.coffee @@ -1,4 +1,6 @@ class Morris.Line extends Morris.Grid + @include Morris.Hover + # Initialise the graph. # constructor: (options) -> @@ -9,20 +11,27 @@ 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' + + @hoverConfigure @options.hoverOptions + # 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.hilight + @prevHilight = null + @el.mousemove (evt) => + @updateHilight evt.pageX + if @options.hilightAutoHide + @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 + + postInit: -> + @hoverInit() # Default configuration # @@ -41,17 +50,9 @@ 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 + hilight: true + hilightAutoHide: false xLabels: 'auto' xLabelFormat: null @@ -60,8 +61,9 @@ class Morris.Line extends Morris.Grid # @private calc: -> @calcPoints() + @hoverCalculateMargins() @generatePaths() - @calcHoverMargins() + @calcHilightMargins() # calculate series data point coordinates # @@ -72,10 +74,13 @@ class Morris.Line extends Morris.Grid row._y = for y in row.y if y? then @transY(y) else null - # calculate hover margins + # calculate hilight margins # # @private - calcHoverMargins: -> + calcHilightMargins: -> + @hilightMargins = ((r._x + @data[i]._x) / 2 for r, i in @data.slice(1)) + + hoverCalculateMargins: -> @hoverMargins = ((r._x + @data[i]._x) / 2 for r, i in @data.slice(1)) # generate paths for series lines @@ -95,8 +100,7 @@ class Morris.Line extends Morris.Grid draw: -> @drawXAxis() @drawSeries() - @drawHover() - @hilight(if @options.hideHover then null else @data.length - 1) + @hilight(if @options.hilightAutoHide then null else @data.length - 1) if @options.hilight # draw the x-axis labels # @@ -140,7 +144,7 @@ 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] @@ -149,7 +153,7 @@ class Morris.Line extends Morris.Grid circle = null else 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)) @seriesPoints[i].push(circle) @@ -191,61 +195,6 @@ class Morris.Line extends Morris.Grid else (coords[i + 1].y - coords[i - 1].y) / (coords[i + 1].x - coords[i - 1].x) - # 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 isnt null).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 @@ -256,21 +205,14 @@ 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] + for hilightIndex in [0...@hilightMargins.length] + break if @hilightMargins[hilightIndex] > x + @hilight hilightIndex # @private strokeWidthForSeries: (index) -> @@ -280,89 +222,10 @@ class Morris.Line extends Morris.Grid strokeForSeries: (index) -> @options.pointStrokeColors[index % @options.pointStrokeColors.length] - # @private - pointFillColorForSeries: (index) -> - @options.pointFillColors[index % @options.pointFillColors.length] - - -# generate a series of label, timestamp pairs for x-axis labels -# -# @private -Morris.labelSeries = (dmin, dmax, pxwidth, specName, xLabelFormat) -> - ddensity = 200 * (dmax - dmin) / pxwidth # seconds per `margin` pixels - d0 = new Date(dmin) - spec = Morris.LABEL_SPECS[specName] - # if the spec doesn't exist, search for the closest one in the list - if spec is undefined - for name in Morris.AUTO_LABEL_ORDER - s = Morris.LABEL_SPECS[name] - if ddensity >= s.span - spec = s - break - # if we run out of options, use second-intervals - if spec is undefined - spec = Morris.LABEL_SPECS["second"] - # check if there's a user-defined formatting function - if xLabelFormat - spec = $.extend({}, spec, {fmt: xLabelFormat}) - # calculate labels - d = spec.start(d0) - ret = [] - while (t = d.getTime()) <= dmax - if t >= dmin - ret.push [spec.fmt(d), t] - spec.incr(d) - return ret - -# @private -minutesSpecHelper = (interval) -> - span: interval * 60 * 1000 - start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours()) - fmt: (d) -> "#{Morris.pad2(d.getHours())}:#{Morris.pad2(d.getMinutes())}" - incr: (d) -> d.setMinutes(d.getMinutes() + interval) - -# @private -secondsSpecHelper = (interval) -> - span: interval * 1000 - start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes()) - fmt: (d) -> "#{Morris.pad2(d.getHours())}:#{Morris.pad2(d.getMinutes())}:#{Morris.pad2(d.getSeconds())}" - incr: (d) -> d.setSeconds(d.getSeconds() + interval) - -Morris.LABEL_SPECS = - "decade": - span: 172800000000 # 10 * 365 * 24 * 60 * 60 * 1000 - start: (d) -> new Date(d.getFullYear() - d.getFullYear() % 10, 0, 1) - fmt: (d) -> "#{d.getFullYear()}" - incr: (d) -> d.setFullYear(d.getFullYear() + 10) - "year": - span: 17280000000 # 365 * 24 * 60 * 60 * 1000 - start: (d) -> new Date(d.getFullYear(), 0, 1) - fmt: (d) -> "#{d.getFullYear()}" - incr: (d) -> d.setFullYear(d.getFullYear() + 1) - "month": - span: 2419200000 # 28 * 24 * 60 * 60 * 1000 - start: (d) -> new Date(d.getFullYear(), d.getMonth(), 1) - fmt: (d) -> "#{d.getFullYear()}-#{Morris.pad2(d.getMonth() + 1)}" - incr: (d) -> d.setMonth(d.getMonth() + 1) - "day": - span: 86400000 # 24 * 60 * 60 * 1000 - start: (d) -> new Date(d.getFullYear(), d.getMonth(), d.getDate()) - fmt: (d) -> "#{d.getFullYear()}-#{Morris.pad2(d.getMonth() + 1)}-#{Morris.pad2(d.getDate())}" - incr: (d) -> d.setDate(d.getDate() + 1) - "hour": minutesSpecHelper(60) - "30min": minutesSpecHelper(30) - "15min": minutesSpecHelper(15) - "10min": minutesSpecHelper(10) - "5min": minutesSpecHelper(5) - "minute": minutesSpecHelper(1) - "30sec": secondsSpecHelper(30) - "15sec": secondsSpecHelper(15) - "10sec": secondsSpecHelper(10) - "5sec": secondsSpecHelper(5) - "second": secondsSpecHelper(1) - -Morris.AUTO_LABEL_ORDER = [ - "decade", "year", "month", "day", "hour", - "30min", "15min", "10min", "5min", "minute", - "30sec", "15sec", "10sec", "5sec", "second" -] + 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] \ No newline at end of file diff --git a/morris.css b/morris.css new file mode 100644 index 0000000..d2ff85b --- /dev/null +++ b/morris.css @@ -0,0 +1,2 @@ +div.morris-popup{border-radius:10px;position:absolute;z-index:1000;padding:6px;font:normal 13px/16px Arial,sans-serif;color:#666;background:rgba(255, 255, 255, 0.8);border:solid 2px rgba(230, 230, 230, 0.8);}div.morris-popup h4,div.morris-popup p{font:normal 13px/16px Arial,sans-serif;text-align:center;color:#666;margin:0;} +div.morris-popup h4{font-weight:bold;} diff --git a/morris.js b/morris.js index 54e432d..d3e4f1f 100644 --- a/morris.js +++ b/morris.js @@ -1,8 +1,8 @@ (function() { var $, Morris, minutesSpecHelper, secondsSpecHelper, - __slice = [].slice, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + __slice = [].slice, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; @@ -10,9 +10,46 @@ $ = jQuery; - Morris.EventEmitter = (function() { + Morris.Module = (function() { - function EventEmitter() {} + function Module() {} + + Module.extend = function(obj) { + var key, value, _ref; + for (key in obj) { + value = obj[key]; + if (key !== 'extended' && key !== 'included') { + this[key] = value; + } + } + if ((_ref = obj.extended) != null) { + _ref.apply(this); + } + return this; + }; + + Module.include = function(obj) { + var key, value, _ref; + for (key in obj) { + value = obj[key]; + if (key !== 'extended' && key !== 'included') { + this.prototype[key] = value; + } + } + return (_ref = obj.included) != null ? _ref.apply(this) : void 0; + }; + + return Module; + + })(); + + Morris.EventEmitter = (function(_super) { + + __extends(EventEmitter, _super); + + function EventEmitter() { + return EventEmitter.__super__.constructor.apply(this, arguments); + } EventEmitter.prototype.on = function(name, handler) { if (this.handlers == null) { @@ -40,7 +77,7 @@ return EventEmitter; - })(); + })(Morris.Module); Morris.commas = function(num) { var absnum, intnum, ret, strabsnum; @@ -63,6 +100,135 @@ return (number < 10 ? '0' : '') + number; }; + Morris.labelSeries = function(dmin, dmax, pxwidth, specName, xLabelFormat) { + var d, d0, ddensity, name, ret, s, spec, t, _i, _len, _ref; + ddensity = 200 * (dmax - dmin) / pxwidth; + d0 = new Date(dmin); + spec = Morris.LABEL_SPECS[specName]; + if (spec === void 0) { + _ref = Morris.AUTO_LABEL_ORDER; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + name = _ref[_i]; + s = Morris.LABEL_SPECS[name]; + if (ddensity >= s.span) { + spec = s; + break; + } + } + } + if (spec === void 0) { + spec = Morris.LABEL_SPECS["second"]; + } + if (xLabelFormat) { + spec = $.extend({}, spec, { + fmt: xLabelFormat + }); + } + d = spec.start(d0); + ret = []; + while ((t = d.getTime()) <= dmax) { + if (t >= dmin) { + ret.push([spec.fmt(d), t]); + } + spec.incr(d); + } + return ret; + }; + + minutesSpecHelper = function(interval) { + return { + span: interval * 60 * 1000, + start: function(d) { + return new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours()); + }, + fmt: function(d) { + return "" + (Morris.pad2(d.getHours())) + ":" + (Morris.pad2(d.getMinutes())); + }, + incr: function(d) { + return d.setMinutes(d.getMinutes() + interval); + } + }; + }; + + secondsSpecHelper = function(interval) { + return { + span: interval * 1000, + start: function(d) { + return new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes()); + }, + fmt: function(d) { + return "" + (Morris.pad2(d.getHours())) + ":" + (Morris.pad2(d.getMinutes())) + ":" + (Morris.pad2(d.getSeconds())); + }, + incr: function(d) { + return d.setSeconds(d.getSeconds() + interval); + } + }; + }; + + Morris.LABEL_SPECS = { + "decade": { + span: 172800000000, + start: function(d) { + return new Date(d.getFullYear() - d.getFullYear() % 10, 0, 1); + }, + fmt: function(d) { + return "" + (d.getFullYear()); + }, + incr: function(d) { + return d.setFullYear(d.getFullYear() + 10); + } + }, + "year": { + span: 17280000000, + start: function(d) { + return new Date(d.getFullYear(), 0, 1); + }, + fmt: function(d) { + return "" + (d.getFullYear()); + }, + incr: function(d) { + return d.setFullYear(d.getFullYear() + 1); + } + }, + "month": { + span: 2419200000, + start: function(d) { + return new Date(d.getFullYear(), d.getMonth(), 1); + }, + fmt: function(d) { + return "" + (d.getFullYear()) + "-" + (Morris.pad2(d.getMonth() + 1)); + }, + incr: function(d) { + return d.setMonth(d.getMonth() + 1); + } + }, + "day": { + span: 86400000, + start: function(d) { + return new Date(d.getFullYear(), d.getMonth(), d.getDate()); + }, + fmt: function(d) { + return "" + (d.getFullYear()) + "-" + (Morris.pad2(d.getMonth() + 1)) + "-" + (Morris.pad2(d.getDate())); + }, + incr: function(d) { + return d.setDate(d.getDate() + 1); + } + }, + "hour": minutesSpecHelper(60), + "30min": minutesSpecHelper(30), + "15min": minutesSpecHelper(15), + "10min": minutesSpecHelper(10), + "5min": minutesSpecHelper(5), + "minute": minutesSpecHelper(1), + "30sec": secondsSpecHelper(30), + "15sec": secondsSpecHelper(15), + "10sec": secondsSpecHelper(10), + "5sec": secondsSpecHelper(5), + "second": secondsSpecHelper(1) + }; + + Morris.AUTO_LABEL_ORDER = ["decade", "year", "month", "day", "hour", "30min", "15min", "10min", "5min", "minute", "30sec", "15sec", "10sec", "5sec", "second"]; + Morris.Grid = (function(_super) { __extends(Grid, _super); @@ -91,6 +257,9 @@ this.init(); } this.setData(this.options.data); + if (this.postInit) { + this.postInit(); + } } Grid.prototype.gridDefaults = { @@ -420,16 +589,183 @@ } }; + Morris.Hover = { + hoverConfigure: function(options) { + return this.hoverOptions = $.extend({}, this.hoverDefaults, options != null ? options : {}); + }, + hoverInit: function() { + if (this.hoverOptions.enableHover) { + this.hover = this.hoverBuild(); + this.hoverBindEvents(); + return this.hoverShow(this.hoverOptions.hideHover ? null : this.data.length - 1); + } + }, + hoverDefaults: { + enableHover: true, + popupClass: "morris-popup", + hideHover: false, + allowOverflow: false, + pointMargin: 10, + hoverFill: function(index, row) { + return this.hoverFill(index, row); + } + }, + hoverBindEvents: function() { + var touchHandler, + _this = this; + this.el.mousemove(function(evt) { + return _this.hoverUpdate(evt.pageX); + }); + if (this.hoverOptions.hideHover) { + this.el.mouseout(function(evt) { + return _this.hoverShow(null); + }); + } + touchHandler = function(evt) { + var touch; + touch = evt.originalEvent.touches[0] || evt.originalEvent.changedTouches[0]; + _this.hoverUpdate(touch.pageX); + return touch; + }; + this.el.bind('touchstart', touchHandler); + this.el.bind('touchmove', touchHandler); + this.el.bind('touchend', touchHandler); + this.hover.mousemove(function(evt) { + return evt.stopPropagation(); + }); + this.hover.mouseout(function(evt) { + return evt.stopPropagation(); + }); + this.hover.bind('touchstart', function(evt) { + return evt.stopPropagation(); + }); + this.hover.bind('touchmove', function(evt) { + return evt.stopPropagation(); + }); + return this.hover.bind('touchend', function(evt) { + return evt.stopPropagation(); + }); + }, + hoverCalculateMargins: 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); + }, + hoverBuild: function() { + var hover; + hover = $("

"); + hover.addClass("" + this.hoverOptions.popupClass + " js-morris-popup"); + hover.appendTo(this.el); + hover.hide(); + return hover; + }, + hoverUpdate: 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.hoverShow(hoverIndex); + }, + hoverShow: function(index) { + if (index !== null) { + this.hover.html(""); + this.hoverOptions.hoverFill.call(this, index, this.data[index]); + this.hoverPosition(index); + this.fire("hover.show", index); + this.hover.show(); + } + if (!(index != null)) { + return this.hoverHide(); + } + }, + hoverHide: function() { + return this.hover.hide(); + }, + colorFor: function(row, i, type) { + return "inherit"; + }, + yLabelFormat: function(label) { + return Morris.commas(label); + }, + hoverPosition: function(index) { + var x, y, _ref; + _ref = this.hoverGetPosition(index), x = _ref[0], y = _ref[1]; + return this.hover.css({ + top: "" + (this.el.offset().top + y) + "px", + left: "" + (this.el.offset().left + x) + "px" + }); + }, + hoverGetPosition: function(index) { + var miny, row, x, y; + row = this.data[index]; + this.hoverWidth = this.hover.outerWidth(true); + this.hoverHeight = this.hover.outerHeight(true); + miny = y = Math.min.apply(null, ((function() { + var _i, _len, _ref, _results; + _ref = row._y; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + y = _ref[_i]; + if (y !== null) { + _results.push(y); + } + } + return _results; + })()).concat(this.bottom)); + x = row._x - this.hoverWidth / 2; + y = miny; + y = y - this.hoverHeight - this.hoverOptions.pointMargin; + if (!this.hoverOptions.allowOverflow) { + if (x < this.left) { + x = row._x + this.hoverOptions.pointMargin; + } else if (x > this.right - this.hoverWidth) { + x = row._x - this.hoverWidth - this.hoverOptions.pointMargin; + } + y = Math.max(y, this.top); + y = Math.min(y, this.bottom - this.hoverHeight - this.hoverOptions.pointMargin); + if (y - miny < this.hoverWidth + this.hoverOptions.pointMargin) { + y = miny + this.hoverOptions.pointMargin; + } + } + return [x, y]; + }, + hoverFill: function(index, row) { + var i, xLabel, y, yLabel, _i, _len, _ref, _results; + xLabel = $("

"); + xLabel.text(row.label); + xLabel.appendTo(this.hover); + _ref = row.y; + _results = []; + for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) { + y = _ref[i]; + yLabel = $("

"); + yLabel.css("color", this.colorFor(row, i, "hover")); + yLabel.text("" + this.options.labels[i] + ": " + (this.yLabelFormat(y))); + _results.push(yLabel.appendTo(this.hover)); + } + return _results; + } + }; + Morris.Line = (function(_super) { __extends(Line, _super); + Line.include(Morris.Hover); + function Line(options) { this.updateHilight = __bind(this.updateHilight, this); this.hilight = __bind(this.hilight, this); - - this.updateHover = __bind(this.updateHover, this); if (!(this instanceof Morris.Line)) { return new Morris.Line(options); } @@ -445,24 +781,31 @@ 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); + this.hoverConfigure(this.options.hoverOptions); + if (this.options.hilight) { + this.prevHilight = null; + this.el.mousemove(function(evt) { + return _this.updateHilight(evt.pageX); }); + if (this.options.hilightAutoHide) { + this.el.mouseout(function(evt) { + return _this.hilight(null); + }); + } + touchHandler = function(evt) { + var touch; + touch = evt.originalEvent.touches[0] || evt.originalEvent.changedTouches[0]; + _this.updateHilight(touch.pageX); + return touch; + }; + this.el.bind('touchstart', touchHandler); + this.el.bind('touchmove', touchHandler); + return this.el.bind('touchend', touchHandler); } - 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.postInit = function() { + return this.hoverInit(); }; Line.prototype.defaults = { @@ -472,25 +815,18 @@ 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, + hilight: true, + hilightAutoHide: false, xLabels: 'auto', xLabelFormat: null }; Line.prototype.calc = function() { this.calcPoints(); + this.hoverCalculateMargins(); this.generatePaths(); - return this.calcHoverMargins(); + return this.calcHilightMargins(); }; Line.prototype.calcPoints = function() { @@ -518,7 +854,21 @@ return _results; }; - Line.prototype.calcHoverMargins = function() { + Line.prototype.calcHilightMargins = function() { + var i, r; + return this.hilightMargins = (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); + } + return _results; + }).call(this); + }; + + Line.prototype.hoverCalculateMargins = function() { var i, r; return this.hoverMargins = (function() { var _i, _len, _ref, _results; @@ -567,8 +917,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.hilight) { + return this.hilight(this.options.hilightAutoHide ? null : this.data.length - 1); + } }; Line.prototype.drawXAxis = function() { @@ -619,7 +970,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() { @@ -641,7 +992,7 @@ if (row._y[i] === null) { circle = null; } else { - 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)); } _results1.push(this.seriesPoints[i].push(circle)); } @@ -702,76 +1053,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) { @@ -787,27 +1068,19 @@ this.seriesPoints[i][index].animate(this.pointGrow); } } - this.updateHover(index); - } - this.prevHilight = index; - if (!(index != null)) { - return this.hideHover(); } + return this.prevHilight = index; }; Line.prototype.updateHilight = function(x) { - var hoverIndex, _i, _ref; + var hilightIndex, _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) { + for (hilightIndex = _i = 0, _ref = this.hilightMargins.length; 0 <= _ref ? _i < _ref : _i > _ref; hilightIndex = 0 <= _ref ? ++_i : --_i) { + if (this.hilightMargins[hilightIndex] > x) { break; } } - return this.hilight(hoverIndex); - }; - - Line.prototype.colorForSeries = function(index) { - return this.options.lineColors[index % this.options.lineColors.length]; + return this.hilight(hilightIndex); }; Line.prototype.strokeWidthForSeries = function(index) { @@ -818,143 +1091,20 @@ 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; })(Morris.Grid); - Morris.labelSeries = function(dmin, dmax, pxwidth, specName, xLabelFormat) { - var d, d0, ddensity, name, ret, s, spec, t, _i, _len, _ref; - ddensity = 200 * (dmax - dmin) / pxwidth; - d0 = new Date(dmin); - spec = Morris.LABEL_SPECS[specName]; - if (spec === void 0) { - _ref = Morris.AUTO_LABEL_ORDER; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - name = _ref[_i]; - s = Morris.LABEL_SPECS[name]; - if (ddensity >= s.span) { - spec = s; - break; - } - } - } - if (spec === void 0) { - spec = Morris.LABEL_SPECS["second"]; - } - if (xLabelFormat) { - spec = $.extend({}, spec, { - fmt: xLabelFormat - }); - } - d = spec.start(d0); - ret = []; - while ((t = d.getTime()) <= dmax) { - if (t >= dmin) { - ret.push([spec.fmt(d), t]); - } - spec.incr(d); - } - return ret; - }; - - minutesSpecHelper = function(interval) { - return { - span: interval * 60 * 1000, - start: function(d) { - return new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours()); - }, - fmt: function(d) { - return "" + (Morris.pad2(d.getHours())) + ":" + (Morris.pad2(d.getMinutes())); - }, - incr: function(d) { - return d.setMinutes(d.getMinutes() + interval); - } - }; - }; - - secondsSpecHelper = function(interval) { - return { - span: interval * 1000, - start: function(d) { - return new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes()); - }, - fmt: function(d) { - return "" + (Morris.pad2(d.getHours())) + ":" + (Morris.pad2(d.getMinutes())) + ":" + (Morris.pad2(d.getSeconds())); - }, - incr: function(d) { - return d.setSeconds(d.getSeconds() + interval); - } - }; - }; - - Morris.LABEL_SPECS = { - "decade": { - span: 172800000000, - start: function(d) { - return new Date(d.getFullYear() - d.getFullYear() % 10, 0, 1); - }, - fmt: function(d) { - return "" + (d.getFullYear()); - }, - incr: function(d) { - return d.setFullYear(d.getFullYear() + 10); - } - }, - "year": { - span: 17280000000, - start: function(d) { - return new Date(d.getFullYear(), 0, 1); - }, - fmt: function(d) { - return "" + (d.getFullYear()); - }, - incr: function(d) { - return d.setFullYear(d.getFullYear() + 1); - } - }, - "month": { - span: 2419200000, - start: function(d) { - return new Date(d.getFullYear(), d.getMonth(), 1); - }, - fmt: function(d) { - return "" + (d.getFullYear()) + "-" + (Morris.pad2(d.getMonth() + 1)); - }, - incr: function(d) { - return d.setMonth(d.getMonth() + 1); - } - }, - "day": { - span: 86400000, - start: function(d) { - return new Date(d.getFullYear(), d.getMonth(), d.getDate()); - }, - fmt: function(d) { - return "" + (d.getFullYear()) + "-" + (Morris.pad2(d.getMonth() + 1)) + "-" + (Morris.pad2(d.getDate())); - }, - incr: function(d) { - return d.setDate(d.getDate() + 1); - } - }, - "hour": minutesSpecHelper(60), - "30min": minutesSpecHelper(30), - "15min": minutesSpecHelper(15), - "10min": minutesSpecHelper(10), - "5min": minutesSpecHelper(5), - "minute": minutesSpecHelper(1), - "30sec": secondsSpecHelper(30), - "15sec": secondsSpecHelper(15), - "10sec": secondsSpecHelper(10), - "5sec": secondsSpecHelper(5), - "second": secondsSpecHelper(1) - }; - - Morris.AUTO_LABEL_ORDER = ["decade", "year", "month", "day", "hour", "30min", "15min", "10min", "5min", "minute", "30sec", "15sec", "10sec", "5sec", "second"]; - Morris.Area = (function(_super) { __extends(Area, _super); @@ -1004,7 +1154,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)); }; @@ -1016,12 +1166,15 @@ __extends(Bar, _super); + Bar.include(Morris.Hover); + + Bar.prototype.hoverGetPosition = function(index) { + var x, y, _ref; + _ref = Morris.Hover.hoverGetPosition.call(this, index), x = _ref[0], y = _ref[1]; + return [x, (this.top + this.bottom) / 2 - this.hoverHeight / 2]; + }; + function Bar(options) { - this.updateHilight = __bind(this.updateHilight, this); - - this.hilight = __bind(this.hilight, this); - - this.updateHover = __bind(this.updateHover, this); if (!(this instanceof Morris.Bar)) { return new Morris.Bar(options); } @@ -1031,47 +1184,22 @@ } Bar.prototype.init = function() { - var touchHandler, - _this = this; - this.prevHilight = null; - this.el.mousemove(function(evt) { - return _this.updateHilight(evt.pageX); - }); - if (this.options.hideHover) { - this.el.mouseout(function(evt) { - return _this.hilight(null); - }); - } - touchHandler = function(evt) { - var touch; - touch = evt.originalEvent.touches[0] || evt.originalEvent.changedTouches[0]; - _this.updateHilight(touch.pageX); - return touch; - }; - this.el.bind('touchstart', touchHandler); - this.el.bind('touchmove', touchHandler); - return this.el.bind('touchend', touchHandler); + return this.hoverConfigure(this.options.hoverOptions); + }; + + Bar.prototype.postInit = function() { + return this.hoverInit(); }; 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() { this.calcBars(); - return this.calcHoverMargins(); + return this.hoverCalculateMargins(); }; Bar.prototype.calcBars = function() { @@ -1099,23 +1227,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() { @@ -1177,79 +1291,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') { diff --git a/morris.min.js b/morris.min.js index 2c8a0d1..d66583e 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},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;ns;t=0<=s?++i:--i)r=this.options.smooth===!0||(o=this.options.ykeys[t],a.call(this.options.smooth,o)>=0),e=function(){var e,r,i,s;i=this.data,s=[];for(e=0,r=i.length;e1?u.push(this.createPath(e,r)):u.push(null);return u}.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;n=v;s=0<=v?++d:--d)n=e[s],s===0?f+="M"+n.x+","+n.y:(r=i[s],u=e[s-1],a=i[s-1],o=(n.x-u.x)/4,l=u.x+o,h=Math.min(this.bottom,u.y+o*a),c=n.x-o,p=Math.min(this.bottom,n.y-o*r),f+="C"+l+","+h+","+c+","+p+","+n.x+","+n.y)}else f="M"+function(){var t,r,i;i=[];for(t=0,r=e.length;ti;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.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 o,h,p,d;p=this.data,d=[];for(r=o=0,h=p.length;or;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={}.hasOwnProperty,s=function(e,t){function r(){this.constructor=e}for(var n in t)i.call(t,n)&&(e[n]=t[n]);return r.prototype=t.prototype,e.prototype=new r,e.__super__=t.prototype,e},o=[].slice,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.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.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),this.postInit&&this.postInit()}return s(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.Hover={hoverConfigure:function(t){return this.hoverOptions=e.extend({},this.hoverDefaults,t!=null?t:{})},hoverInit:function(){if(this.hoverOptions.enableHover)return this.hover=this.hoverBuild(),this.hoverBindEvents(),this.hoverShow(this.hoverOptions.hideHover?null:this.data.length-1)},hoverDefaults:{enableHover:!0,popupClass:"morris-popup",hideHover:!1,allowOverflow:!1,pointMargin:10,hoverFill:function(e,t){return this.hoverFill(e,t)}},hoverBindEvents:function(){var e,t=this;return this.el.mousemove(function(e){return t.hoverUpdate(e.pageX)}),this.hoverOptions.hideHover&&this.el.mouseout(function(e){return t.hoverShow(null)}),e=function(e){var n;return n=e.originalEvent.touches[0]||e.originalEvent.changedTouches[0],t.hoverUpdate(n.pageX),n},this.el.bind("touchstart",e),this.el.bind("touchmove",e),this.el.bind("touchend",e),this.hover.mousemove(function(e){return e.stopPropagation()}),this.hover.mouseout(function(e){return e.stopPropagation()}),this.hover.bind("touchstart",function(e){return e.stopPropagation()}),this.hover.bind("touchmove",function(e){return e.stopPropagation()}),this.hover.bind("touchend",function(e){return e.stopPropagation()})},hoverCalculateMargins:function(){var e;return this.hoverMargins=function(){var t,n,r;r=[];for(e=t=1,n=this.data.length;1<=n?tn;e=1<=n?++t:--t)r.push(this.left+e*this.width/this.data.length);return r}.call(this)},hoverBuild:function(){var t;return t=e("

"),t.addClass(""+this.hoverOptions.popupClass+" js-morris-popup"),t.appendTo(this.el),t.hide(),t},hoverUpdate: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.hoverShow(t)},hoverShow:function(e){e!==null&&(this.hover.html(""),this.hoverOptions.hoverFill.call(this,e,this.data[e]),this.hoverPosition(e),this.fire("hover.show",e),this.hover.show());if(e==null)return this.hoverHide()},hoverHide:function(){return this.hover.hide()},colorFor:function(e,t,n){return"inherit"},yLabelFormat:function(e){return t.commas(e)},hoverPosition:function(e){var t,n,r;return r=this.hoverGetPosition(e),t=r[0],n=r[1],this.hover.css({top:""+(this.el.offset().top+n)+"px",left:""+(this.el.offset().left+t)+"px"})},hoverGetPosition:function(e){var t,n,r,i;return n=this.data[e],this.hoverWidth=this.hover.outerWidth(!0),this.hoverHeight=this.hover.outerHeight(!0),t=i=Math.min.apply(null,function(){var e,t,r,s;r=n._y,s=[];for(e=0,t=r.length;ethis.right-this.hoverWidth&&(r=n._x-this.hoverWidth-this.hoverOptions.pointMargin),i=Math.max(i,this.top),i=Math.min(i,this.bottom-this.hoverHeight-this.hoverOptions.pointMargin),i-t"),i.text(n.label),i.appendTo(this.hover),f=n.y,l=[];for(r=u=0,a=f.length;u"),o.css("color",this.colorFor(n,r,"hover")),o.text(""+this.options.labels[r]+": "+this.yLabelFormat(s)),l.push(o.appendTo(this.hover));return l}},t.Line=function(e){function n(e){this.updateHilight=u(this.updateHilight,this),this.hilight=u(this.hilight,this);if(!(this instanceof t.Line))return new t.Line(e);n.__super__.constructor.call(this,e)}return s(n,e),n.include(t.Hover),n.prototype.init=function(){var e,t=this;this.pointGrow=Raphael.animation({r:this.options.pointSize+3},25,"linear"),this.pointShrink=Raphael.animation({r:this.options.pointSize},25,"linear"),this.hoverConfigure(this.options.hoverOptions);if(this.options.hilight)return this.prevHilight=null,this.el.mousemove(function(e){return t.updateHilight(e.pageX)}),this.options.hilightAutoHide&&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.postInit=function(){return this.hoverInit()},n.prototype.defaults={lineWidth:3,pointSize:4,lineColors:["#0b62a4","#7A92A3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"],pointWidths:[1],pointStrokeColors:["#ffffff"],pointFillColors:[],smooth:!0,hilight:!0,hilightAutoHide:!1,xLabels:"auto",xLabelFormat:null},n.prototype.calc=function(){return this.calcPoints(),this.hoverCalculateMargins(),this.generatePaths(),this.calcHilightMargins()},n.prototype.calcPoints=function(){var e,t,n,r,i,s;i=this.data,s=[];for(n=0,r=i.length;ns;t=0<=s?++i:--i)r=this.options.smooth===!0||(o=this.options.ykeys[t],a.call(this.options.smooth,o)>=0),e=function(){var e,r,i,s;i=this.data,s=[];for(e=0,r=i.length;e1?u.push(this.createPath(e,r)):u.push(null);return u}.call(this)},n.prototype.draw=function(){this.drawXAxis(),this.drawSeries();if(this.options.hilight)return this.hilight(this.options.hilightAutoHide?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.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=v;s=0<=v?++d:--d)n=e[s],s===0?f+="M"+n.x+","+n.y:(r=i[s],u=e[s-1],a=i[s-1],o=(n.x-u.x)/4,l=u.x+o,h=Math.min(this.bottom,u.y+o*a),c=n.x-o,p=Math.min(this.bottom,n.y-o*r),f+="C"+l+","+h+","+c+","+p+","+n.x+","+n.y)}else f="M"+function(){var t,r,i;i=[];for(t=0,r=e.length;t=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.updateHilight=function(e){var t,n,r;e-=this.el.offset().left;for(t=n=0,r=this.hilightMargins.length;0<=r?nr;t=0<=r?++n:--n)if(this.hilightMargins[t]>e)break;return this.hilight(t)},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.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 s(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){if(!(this instanceof t.Bar))return new t.Bar(n);r.__super__.constructor.call(this,e.extend({},n,{parseTime:!1}))}return s(r,n),r.include(t.Hover),r.prototype.hoverGetPosition=function(e){var n,r,i;return i=t.Hover.hoverGetPosition.call(this,e),n=i[0],r=i[1],[n,(this.top+this.bottom)/2-this.hoverHeight/2]},r.prototype.init=function(){return this.hoverConfigure(this.options.hoverOptions)},r.prototype.postInit=function(){return this.hoverInit()},r.prototype.defaults={barSizeRatio:.75,barGap:3,barColors:["#0b62a4","#7a92a3","#4da74d","#afd8f8","#edc240","#cb4b4b","#9440ed"]},r.prototype.calc=function(){return this.calcBars(),this.hoverCalculateMargins()},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 o,h,p,d;p=this.data,d=[];for(r=o=0,h=p.length;oMath.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 s(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/line/line_spec.coffee b/spec/lib/line/line_spec.coffee index e540da7..8c0115f 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', ->