mirror of
https://github.com/morrisjs/morris.js.git
synced 2024-11-10 21:36:34 +01:00
Merge pull request #134 from oesmith/html-hover
Hover refactor (work in progress)
This commit is contained in:
commit
8b06d9972d
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Area charts</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Bar charts</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Bar charts</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Formatting Dates YYYY-MM-DD</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Decimal Data</h1>
|
||||
@ -25,7 +26,11 @@ window.m = Morris.Line({
|
||||
xkey: 'x',
|
||||
ykeys: ['y'],
|
||||
labels: ['sin(x)'],
|
||||
parseTime: false
|
||||
parseTime: false,
|
||||
hoverCallback: function (index, options) {
|
||||
var row = options.data[index];
|
||||
return "sin(" + row.x + ") = " + row.y;
|
||||
}
|
||||
});
|
||||
</pre>
|
||||
</body>
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Donut Chart</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Donut Chart</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Time Events</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Value Goals</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Formatting Dates with YYYY-MM</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Negative values</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Non-continuous data</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Formatting Non-date Arbitrary X-axis</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Formatting Dates with Quarters</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Stacked Bars chart</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Timestamps</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Updating data</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Formatting Dates With Weeks</h1>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<script src="lib/example.js"></script>
|
||||
<link rel="stylesheet" href="lib/example.css">
|
||||
<link rel="stylesheet" href="lib/prettify.css">
|
||||
<link rel="stylesheet" href="../morris.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Formatting Dates YYYY</h1>
|
||||
|
15
grunt.js
15
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');
|
||||
};
|
||||
|
27
less/morris.core.less
Normal file
27
less/morris.core.less
Normal file
@ -0,0 +1,27 @@
|
||||
.morris-hover {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
|
||||
&.morris-default-style {
|
||||
border-radius: 10px;
|
||||
padding: 6px;
|
||||
color: #666;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
border: solid 2px rgba(230, 230, 230, 0.8);
|
||||
|
||||
font-family: sans-serif;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
|
||||
.morris-hover-row-label {
|
||||
font-weight: bold;
|
||||
margin: 0.25em 0;
|
||||
}
|
||||
|
||||
.morris-hover-point {
|
||||
white-space: nowrap;
|
||||
margin: 0.1em 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -16,6 +16,7 @@ class Morris.Area extends Morris.Line
|
||||
row._y = for y in row.y
|
||||
total += (y || 0)
|
||||
@transY(total)
|
||||
row._ymax = row._y[row._y.length - 1]
|
||||
|
||||
# draw the data series
|
||||
#
|
||||
@ -31,7 +32,7 @@ class Morris.Area extends Morris.Line
|
||||
super()
|
||||
|
||||
fillForSeries: (i) ->
|
||||
color = Raphael.rgb2hsl @colorForSeries(i)
|
||||
color = Raphael.rgb2hsl @colorFor(@data[i], i, 'line')
|
||||
Raphael.hsl(
|
||||
color.h,
|
||||
Math.min(255, color.s * 0.75),
|
||||
|
@ -1,27 +1,15 @@
|
||||
class Morris.Bar extends Morris.Grid
|
||||
# Initialise the graph.
|
||||
#
|
||||
constructor: (options) ->
|
||||
return new Morris.Bar(options) unless (@ instanceof Morris.Bar)
|
||||
super($.extend {}, options, parseTime: false)
|
||||
|
||||
# setup event handlers
|
||||
#
|
||||
init: ->
|
||||
@cumulative = @options.stacked
|
||||
@prevHilight = null
|
||||
@el.mousemove (evt) =>
|
||||
@updateHilight evt.pageX
|
||||
if @options.hideHover
|
||||
@el.mouseout (evt) =>
|
||||
@hilight null
|
||||
touchHandler = (evt) =>
|
||||
touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0]
|
||||
@updateHilight touch.pageX
|
||||
return touch
|
||||
@el.bind 'touchstart', touchHandler
|
||||
@el.bind 'touchmove', touchHandler
|
||||
@el.bind 'touchend', touchHandler
|
||||
|
||||
if @options.hideHover isnt 'always'
|
||||
@hover = new Morris.Hover(parent: @el)
|
||||
@on('hovermove', @onHoverMove)
|
||||
@on('hoverout', @onHoverOut)
|
||||
|
||||
# Default configuration
|
||||
#
|
||||
@ -37,23 +25,14 @@ class Morris.Bar extends Morris.Grid
|
||||
'#cb4b4b'
|
||||
'#9440ed'
|
||||
]
|
||||
hoverPaddingX: 10
|
||||
hoverPaddingY: 5
|
||||
hoverMargin: 10
|
||||
hoverFillColor: '#fff'
|
||||
hoverBorderColor: '#ccc'
|
||||
hoverBorderWidth: 2
|
||||
hoverOpacity: 0.95
|
||||
hoverLabelColor: '#444'
|
||||
hoverFontSize: 12
|
||||
hideHover: false
|
||||
|
||||
# Do any size-related calculations
|
||||
#
|
||||
# @private
|
||||
calc: ->
|
||||
@calcBars()
|
||||
@calcHoverMargins()
|
||||
if @options.hideHover is false
|
||||
@hover.update(@hoverContentForRow(@data.length - 1)...)
|
||||
|
||||
# calculate series data bars coordinates and sizes
|
||||
#
|
||||
@ -64,20 +43,11 @@ class Morris.Bar extends Morris.Grid
|
||||
row._y = for y in row.y
|
||||
if y? then @transY(y) else null
|
||||
|
||||
# calculate hover margins
|
||||
#
|
||||
# @private
|
||||
calcHoverMargins: ->
|
||||
@hoverMargins = for i in [1...@data.length]
|
||||
@left + i * @width / @data.length
|
||||
|
||||
# Draws the bar chart.
|
||||
#
|
||||
draw: ->
|
||||
@drawXAxis()
|
||||
@drawSeries()
|
||||
@drawHover()
|
||||
@hilight(if @options.hideHover then null else @data.length - 1)
|
||||
|
||||
# draw the x-axis labels
|
||||
#
|
||||
@ -134,74 +104,11 @@ class Morris.Bar extends Morris.Grid
|
||||
else
|
||||
null
|
||||
|
||||
# draw the hover tooltip
|
||||
#
|
||||
# @private
|
||||
drawHover: ->
|
||||
# hover labels
|
||||
@hoverHeight = @options.hoverFontSize * 1.5 * (@options.ykeys.length + 1)
|
||||
@hover = @r.rect(-10, -@hoverHeight / 2 - @options.hoverPaddingY, 20, @hoverHeight + @options.hoverPaddingY * 2, 10)
|
||||
.attr('fill', @options.hoverFillColor)
|
||||
.attr('stroke', @options.hoverBorderColor)
|
||||
.attr('stroke-width', @options.hoverBorderWidth)
|
||||
.attr('opacity', @options.hoverOpacity)
|
||||
@xLabel = @r.text(0, (@options.hoverFontSize * 0.75) - @hoverHeight / 2, '')
|
||||
.attr('fill', @options.hoverLabelColor)
|
||||
.attr('font-weight', 'bold')
|
||||
.attr('font-size', @options.hoverFontSize)
|
||||
@hoverSet = @r.set()
|
||||
@hoverSet.push(@hover)
|
||||
@hoverSet.push(@xLabel)
|
||||
@yLabels = []
|
||||
for i in [0...@options.ykeys.length]
|
||||
yLabel = @r.text(0, @options.hoverFontSize * 1.5 * (i + 1.5) - @hoverHeight / 2, '')
|
||||
.attr('font-size', @options.hoverFontSize)
|
||||
@yLabels.push(yLabel)
|
||||
@hoverSet.push(yLabel)
|
||||
|
||||
# @private
|
||||
updateHover: (index) =>
|
||||
@hoverSet.show()
|
||||
row = @data[index]
|
||||
@xLabel.attr('text', row.label)
|
||||
for y, i in row.y
|
||||
@yLabels[i].attr('fill', @colorFor(row, i, 'hover'))
|
||||
@yLabels[i].attr('text', "#{@options.labels[i]}: #{@yLabelFormat(y)}")
|
||||
# recalculate hover box width
|
||||
maxLabelWidth = Math.max.apply null, (l.getBBox().width for l in @yLabels)
|
||||
maxLabelWidth = Math.max maxLabelWidth, @xLabel.getBBox().width
|
||||
@hover.attr 'width', maxLabelWidth + @options.hoverPaddingX * 2
|
||||
@hover.attr 'x', -@options.hoverPaddingX - maxLabelWidth / 2
|
||||
# move to y pos
|
||||
yloc = (@bottom + @top) / 2
|
||||
xloc = Math.min @right - maxLabelWidth / 2 - @options.hoverPaddingX, @data[index]._x
|
||||
xloc = Math.max @left + maxLabelWidth / 2 + @options.hoverPaddingX, xloc
|
||||
@hoverSet.attr 'transform', "t#{xloc},#{yloc}"
|
||||
|
||||
# @private
|
||||
hideHover: ->
|
||||
@hoverSet.hide()
|
||||
|
||||
# @private
|
||||
hilight: (index) =>
|
||||
if index isnt null and @prevHilight isnt index
|
||||
@updateHover index
|
||||
@prevHilight = index
|
||||
if not index?
|
||||
@hideHover()
|
||||
|
||||
# @private
|
||||
updateHilight: (x) =>
|
||||
x -= @el.offset().left
|
||||
for hoverIndex in [0...@hoverMargins.length]
|
||||
break if @hoverMargins[hoverIndex] > x
|
||||
@hilight hoverIndex
|
||||
|
||||
# @private
|
||||
#
|
||||
# @param row [Object] row data
|
||||
# @param sidx [Number] series index
|
||||
# @param type [String] "bar" or "hover"
|
||||
# @param type [String] "bar", "hover" or "label"
|
||||
colorFor: (row, sidx, type) ->
|
||||
if typeof @options.barColors is 'function'
|
||||
r = { x: row.x, y: row.y[sidx], label: row.label }
|
||||
@ -209,3 +116,43 @@ class Morris.Bar extends Morris.Grid
|
||||
@options.barColors.call(@, r, s, type)
|
||||
else
|
||||
@options.barColors[sidx % @options.barColors.length]
|
||||
|
||||
# hit test - returns the index of the row beneath the given coordinate
|
||||
#
|
||||
hitTest: (x, y) ->
|
||||
x = Math.max(Math.min(x, @right), @left)
|
||||
Math.min(@data.length - 1,
|
||||
Math.floor((x - @left) / ((@right - @left) / @data.length)))
|
||||
|
||||
# hover movement event handler
|
||||
#
|
||||
# @private
|
||||
onHoverMove: (x, y) =>
|
||||
index = @hitTest(x, y)
|
||||
@hover.update(@hoverContentForRow(index)...)
|
||||
|
||||
# hover out event handler
|
||||
#
|
||||
# @private
|
||||
onHoverOut: =>
|
||||
if @options.hideHover is 'auto'
|
||||
@hover.hide()
|
||||
|
||||
# hover content for a point
|
||||
#
|
||||
# @private
|
||||
hoverContentForRow: (index) ->
|
||||
if typeof @options.hoverCallback is 'function'
|
||||
content = @options.hoverCallback(index, @options)
|
||||
else
|
||||
row = @data[index]
|
||||
content = "<div class='morris-hover-row-label'>#{row.label}</div>"
|
||||
for y, j in row.y
|
||||
content += """
|
||||
<div class='morris-hover-point' style='color: #{@colorFor(row, j, 'label')}'>
|
||||
#{@options.labels[j]}:
|
||||
#{@yLabelFormat(y)}
|
||||
</div>
|
||||
"""
|
||||
x = @left + (index + 0.5) * (@right - @left) / @data.length
|
||||
[content, x]
|
||||
|
@ -12,6 +12,9 @@ class Morris.Grid extends Morris.EventEmitter
|
||||
if not @el? or @el.length == 0
|
||||
throw new Error("Graph container element not found")
|
||||
|
||||
if @el.css('position') == 'static'
|
||||
@el.css('position', 'relative')
|
||||
|
||||
@options = $.extend {}, @gridDefaults, (@defaults || {}), options
|
||||
|
||||
# bail if there's no data
|
||||
@ -36,6 +39,22 @@ class Morris.Grid extends Morris.EventEmitter
|
||||
# load data
|
||||
@setData @options.data
|
||||
|
||||
# hover
|
||||
@el.bind 'mousemove', (evt) =>
|
||||
offset = @el.offset()
|
||||
@fire 'hovermove', evt.pageX - offset.left, evt.pageY - offset.top
|
||||
|
||||
@el.bind 'mouseout', (evt) =>
|
||||
@fire 'hoverout'
|
||||
|
||||
@el.bind 'touchstart touchmove touchend', (evt) =>
|
||||
touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0]
|
||||
offset = @el.offset()
|
||||
@fire 'hover', touch.pageX - offset.left, touch.pageY - offset.top
|
||||
touch
|
||||
|
||||
@postInit() if @postInit
|
||||
|
||||
# Default options
|
||||
#
|
||||
gridDefaults:
|
||||
@ -44,6 +63,7 @@ class Morris.Grid extends Morris.EventEmitter
|
||||
gridStrokeWidth: 0.5
|
||||
gridTextColor: '#888'
|
||||
gridTextSize: 12
|
||||
hideHover: false
|
||||
numLines: 5
|
||||
padding: 25
|
||||
parseTime: true
|
||||
@ -256,6 +276,10 @@ class Morris.Grid extends Morris.EventEmitter
|
||||
yLabelFormat: (label) ->
|
||||
"#{@options.preUnits}#{Morris.commas(label)}#{@options.postUnits}"
|
||||
|
||||
updateHover: (x, y) ->
|
||||
hit = @hitTest(x, y)
|
||||
if hit?
|
||||
@hover.update(hit...)
|
||||
|
||||
# Parse a date into a javascript timestamp
|
||||
#
|
||||
|
41
lib/morris.hover.coffee
Normal file
41
lib/morris.hover.coffee
Normal file
@ -0,0 +1,41 @@
|
||||
class Morris.Hover
|
||||
# Displays contextual information in a floating HTML div.
|
||||
|
||||
@defaults:
|
||||
class: 'morris-hover morris-default-style'
|
||||
|
||||
constructor: (options = {}) ->
|
||||
@options = $.extend {}, Morris.Hover.defaults, options
|
||||
@el = $ "<div class='#{@options.class}'></div>"
|
||||
@el.hide()
|
||||
@options.parent.append(@el)
|
||||
|
||||
update: (html, x, y) ->
|
||||
@html(html)
|
||||
@show()
|
||||
@moveTo(x, y)
|
||||
|
||||
html: (content) ->
|
||||
@el.html(content)
|
||||
|
||||
moveTo: (x, y) ->
|
||||
parentWidth = @options.parent.innerWidth()
|
||||
parentHeight = @options.parent.innerHeight()
|
||||
hoverWidth = @el.outerWidth()
|
||||
hoverHeight = @el.outerHeight()
|
||||
left = Math.min(Math.max(0, x - hoverWidth / 2), parentWidth - hoverWidth)
|
||||
if y?
|
||||
top = y - hoverHeight - 10
|
||||
if top < 0
|
||||
top = y + 10
|
||||
if top + hoverHeight > parentHeight
|
||||
top = parentHeight / 2 - hoverHeight / 2
|
||||
else
|
||||
top = parentHeight / 2 - hoverHeight / 2
|
||||
@el.css(left: left + "px", top: top + "px")
|
||||
|
||||
show: ->
|
||||
@el.show()
|
||||
|
||||
hide: ->
|
||||
@el.hide()
|
@ -9,20 +9,11 @@ class Morris.Line extends Morris.Grid
|
||||
# Some instance variables for later
|
||||
@pointGrow = Raphael.animation r: @options.pointSize + 3, 25, 'linear'
|
||||
@pointShrink = Raphael.animation r: @options.pointSize, 25, 'linear'
|
||||
# column hilight events
|
||||
@prevHilight = null
|
||||
@el.mousemove (evt) =>
|
||||
@updateHilight evt.pageX
|
||||
if @options.hideHover
|
||||
@el.mouseout (evt) =>
|
||||
@hilight null
|
||||
touchHandler = (evt) =>
|
||||
touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0]
|
||||
@updateHilight touch.pageX
|
||||
return touch
|
||||
@el.bind 'touchstart', touchHandler
|
||||
@el.bind 'touchmove', touchHandler
|
||||
@el.bind 'touchend', touchHandler
|
||||
|
||||
if @options.hideHover isnt 'always'
|
||||
@hover = new Morris.Hover(parent: @el)
|
||||
@on('hovermove', @onHoverMove)
|
||||
@on('hoverout', @onHoverOut)
|
||||
|
||||
# Default configuration
|
||||
#
|
||||
@ -41,20 +32,11 @@ class Morris.Line extends Morris.Grid
|
||||
pointWidths: [1]
|
||||
pointStrokeColors: ['#ffffff']
|
||||
pointFillColors: []
|
||||
hoverPaddingX: 10
|
||||
hoverPaddingY: 5
|
||||
hoverMargin: 10
|
||||
hoverFillColor: '#fff'
|
||||
hoverBorderColor: '#ccc'
|
||||
hoverBorderWidth: 2
|
||||
hoverOpacity: 0.95
|
||||
hoverLabelColor: '#444'
|
||||
hoverFontSize: 12
|
||||
smooth: true
|
||||
hideHover: false
|
||||
xLabels: 'auto'
|
||||
xLabelFormat: null
|
||||
continuousLine: true
|
||||
hideHover: false
|
||||
|
||||
# Do any size-related calculations
|
||||
#
|
||||
@ -62,7 +44,6 @@ class Morris.Line extends Morris.Grid
|
||||
calc: ->
|
||||
@calcPoints()
|
||||
@generatePaths()
|
||||
@calcHoverMargins()
|
||||
|
||||
# calculate series data point coordinates
|
||||
#
|
||||
@ -72,12 +53,59 @@ class Morris.Line extends Morris.Grid
|
||||
row._x = @transX(row.x)
|
||||
row._y = for y in row.y
|
||||
if y? then @transY(y) else y
|
||||
row._ymax = Math.min.apply(null, [@bottom].concat(y for y in row._y when y?))
|
||||
|
||||
# calculate hover margins
|
||||
# hit test - returns the index of the row beneath the given coordinate
|
||||
#
|
||||
hitTest: (x, y) ->
|
||||
# TODO better search algo
|
||||
for r, index in @data.slice(1)
|
||||
break if x < (r._x + @data[index]._x) / 2
|
||||
index
|
||||
|
||||
# hover movement event handler
|
||||
#
|
||||
# @private
|
||||
calcHoverMargins: ->
|
||||
@hoverMargins = ((r._x + @data[i]._x) / 2 for r, i in @data.slice(1))
|
||||
onHoverMove: (x, y) =>
|
||||
index = @hitTest(x, y)
|
||||
@displayHoverForRow(index)
|
||||
|
||||
# hover out event handler
|
||||
#
|
||||
# @private
|
||||
onHoverOut: =>
|
||||
if @options.hideHover is 'auto'
|
||||
@displayHoverForIndex(null)
|
||||
|
||||
# display a hover popup over the given row
|
||||
#
|
||||
# @private
|
||||
displayHoverForRow: (index) ->
|
||||
if index?
|
||||
@hover.update(@hoverContentForRow(index)...)
|
||||
@hilight(index)
|
||||
else
|
||||
@hover.hide()
|
||||
@hilight()
|
||||
|
||||
# hover content for a point
|
||||
#
|
||||
# @private
|
||||
hoverContentForRow: (index) ->
|
||||
row = @data[index]
|
||||
if typeof @options.hoverCallback is 'function'
|
||||
content = @options.hoverCallback(index, @options)
|
||||
else
|
||||
content = "<div class='morris-hover-row-label'>#{row.label}</div>"
|
||||
for y, j in row.y
|
||||
content += """
|
||||
<div class='morris-hover-point' style='color: #{@colorFor(row, j, 'label')}'>
|
||||
#{@options.labels[j]}:
|
||||
#{@yLabelFormat(y)}
|
||||
</div>
|
||||
"""
|
||||
[content, row._x, row._ymax]
|
||||
|
||||
|
||||
# generate paths for series lines
|
||||
#
|
||||
@ -98,8 +126,8 @@ class Morris.Line extends Morris.Grid
|
||||
draw: ->
|
||||
@drawXAxis()
|
||||
@drawSeries()
|
||||
@drawHover()
|
||||
@hilight(if @options.hideHover then null else @data.length - 1)
|
||||
if @options.hideHover is false
|
||||
@displayHoverForRow(@data.length - 1)
|
||||
|
||||
# draw the x-axis labels
|
||||
#
|
||||
@ -143,14 +171,14 @@ class Morris.Line extends Morris.Grid
|
||||
path = @paths[i]
|
||||
if path isnt null
|
||||
@r.path(path)
|
||||
.attr('stroke', @colorForSeries(i))
|
||||
.attr('stroke', @colorFor(row, i, 'line'))
|
||||
.attr('stroke-width', @options.lineWidth)
|
||||
@seriesPoints = ([] for i in [0...@options.ykeys.length])
|
||||
for i in [@options.ykeys.length-1..0]
|
||||
for row in @data
|
||||
if row._y[i]?
|
||||
circle = @r.circle(row._x, row._y[i], @options.pointSize)
|
||||
.attr('fill', @pointFillColorForSeries(i) || @colorForSeries(i))
|
||||
.attr('fill', @colorFor(row, i, 'point'))
|
||||
.attr('stroke-width', @strokeWidthForSeries(i))
|
||||
.attr('stroke', @strokeForSeries(i))
|
||||
else
|
||||
@ -205,61 +233,6 @@ class Morris.Line extends Morris.Grid
|
||||
else
|
||||
null
|
||||
|
||||
# draw the hover tooltip
|
||||
#
|
||||
# @private
|
||||
drawHover: ->
|
||||
# hover labels
|
||||
@hoverHeight = @options.hoverFontSize * 1.5 * (@options.ykeys.length + 1)
|
||||
@hover = @r.rect(-10, -@hoverHeight / 2 - @options.hoverPaddingY, 20, @hoverHeight + @options.hoverPaddingY * 2, 10)
|
||||
.attr('fill', @options.hoverFillColor)
|
||||
.attr('stroke', @options.hoverBorderColor)
|
||||
.attr('stroke-width', @options.hoverBorderWidth)
|
||||
.attr('opacity', @options.hoverOpacity)
|
||||
@xLabel = @r.text(0, (@options.hoverFontSize * 0.75) - @hoverHeight / 2, '')
|
||||
.attr('fill', @options.hoverLabelColor)
|
||||
.attr('font-weight', 'bold')
|
||||
.attr('font-size', @options.hoverFontSize)
|
||||
@hoverSet = @r.set()
|
||||
@hoverSet.push(@hover)
|
||||
@hoverSet.push(@xLabel)
|
||||
@yLabels = []
|
||||
for i in [0...@options.ykeys.length]
|
||||
idx = if @cumulative then (@options.ykeys.length - i - 1) else i
|
||||
yLabel = @r.text(0, @options.hoverFontSize * 1.5 * (idx + 1.5) - @hoverHeight / 2, '')
|
||||
.attr('fill', @colorForSeries(i))
|
||||
.attr('font-size', @options.hoverFontSize)
|
||||
@yLabels.push(yLabel)
|
||||
@hoverSet.push(yLabel)
|
||||
|
||||
# @private
|
||||
updateHover: (index) =>
|
||||
@hoverSet.show()
|
||||
row = @data[index]
|
||||
@xLabel.attr('text', row.label)
|
||||
for y, i in row.y
|
||||
@yLabels[i].attr('text', "#{@options.labels[i]}: #{@yLabelFormat(y)}")
|
||||
# recalculate hover box width
|
||||
maxLabelWidth = Math.max.apply null, (l.getBBox().width for l in @yLabels)
|
||||
maxLabelWidth = Math.max maxLabelWidth, @xLabel.getBBox().width
|
||||
@hover.attr 'width', maxLabelWidth + @options.hoverPaddingX * 2
|
||||
@hover.attr 'x', -@options.hoverPaddingX - maxLabelWidth / 2
|
||||
# move to y pos
|
||||
yloc = Math.min.apply null, (y for y in row._y when y?).concat(@bottom)
|
||||
if yloc > @hoverHeight + @options.hoverPaddingY * 2 + @options.hoverMargin + @top
|
||||
yloc = yloc - @hoverHeight / 2 - @options.hoverPaddingY - @options.hoverMargin
|
||||
else
|
||||
yloc = yloc + @hoverHeight / 2 + @options.hoverPaddingY + @options.hoverMargin
|
||||
yloc = Math.max @top + @hoverHeight / 2 + @options.hoverPaddingY, yloc
|
||||
yloc = Math.min @bottom - @hoverHeight / 2 - @options.hoverPaddingY, yloc
|
||||
xloc = Math.min @right - maxLabelWidth / 2 - @options.hoverPaddingX, @data[index]._x
|
||||
xloc = Math.max @left + maxLabelWidth / 2 + @options.hoverPaddingX, xloc
|
||||
@hoverSet.attr 'transform', "t#{xloc},#{yloc}"
|
||||
|
||||
# @private
|
||||
hideHover: ->
|
||||
@hoverSet.hide()
|
||||
|
||||
# @private
|
||||
hilight: (index) =>
|
||||
if @prevHilight isnt null and @prevHilight isnt index
|
||||
@ -270,21 +243,7 @@ class Morris.Line extends Morris.Grid
|
||||
for i in [0..@seriesPoints.length-1]
|
||||
if @seriesPoints[i][index]
|
||||
@seriesPoints[i][index].animate @pointGrow
|
||||
@updateHover index
|
||||
@prevHilight = index
|
||||
if not index?
|
||||
@hideHover()
|
||||
|
||||
# @private
|
||||
updateHilight: (x) =>
|
||||
x -= @el.offset().left
|
||||
for hoverIndex in [0...@hoverMargins.length]
|
||||
break if @hoverMargins[hoverIndex] > x
|
||||
@hilight hoverIndex
|
||||
|
||||
# @private
|
||||
colorForSeries: (index) ->
|
||||
@options.lineColors[index % @options.lineColors.length]
|
||||
|
||||
# @private
|
||||
strokeWidthForSeries: (index) ->
|
||||
@ -294,9 +253,13 @@ class Morris.Line extends Morris.Grid
|
||||
strokeForSeries: (index) ->
|
||||
@options.pointStrokeColors[index % @options.pointStrokeColors.length]
|
||||
|
||||
# @private
|
||||
pointFillColorForSeries: (index) ->
|
||||
@options.pointFillColors[index % @options.pointFillColors.length]
|
||||
colorFor: (row, sidx, type) ->
|
||||
if typeof @options.lineColors is 'function'
|
||||
@options.lineColors.call(@, row, sidx, type)
|
||||
else if type is 'point'
|
||||
@options.pointFillColors[sidx % @options.pointFillColors.length] || @options.lineColors[sidx % @options.lineColors.length]
|
||||
else
|
||||
@options.lineColors[sidx % @options.lineColors.length]
|
||||
|
||||
|
||||
# generate a series of label, timestamp pairs for x-axis labels
|
||||
|
2
morris.css
Normal file
2
morris.css
Normal file
@ -0,0 +1,2 @@
|
||||
.morris-hover{position:absolute;z-index:1000;}.morris-hover.morris-default-style{border-radius:10px;padding:6px;color:#666;background:rgba(255, 255, 255, 0.8);border:solid 2px rgba(230, 230, 230, 0.8);font-family:sans-serif;font-size:12px;text-align:center;}.morris-hover.morris-default-style .morris-hover-row-label{font-weight:bold;margin:0.25em 0;}
|
||||
.morris-hover.morris-default-style .morris-hover-point{white-space:nowrap;margin:0.1em 0;}
|
499
morris.js
499
morris.js
@ -68,6 +68,7 @@
|
||||
__extends(Grid, _super);
|
||||
|
||||
function Grid(options) {
|
||||
var _this = this;
|
||||
if (typeof options.element === 'string') {
|
||||
this.el = $(document.getElementById(options.element));
|
||||
} else {
|
||||
@ -76,6 +77,9 @@
|
||||
if (!(this.el != null) || this.el.length === 0) {
|
||||
throw new Error("Graph container element not found");
|
||||
}
|
||||
if (this.el.css('position') === 'static') {
|
||||
this.el.css('position', 'relative');
|
||||
}
|
||||
this.options = $.extend({}, this.gridDefaults, this.defaults || {}, options);
|
||||
if (this.options.data === void 0 || this.options.data.length === 0) {
|
||||
return;
|
||||
@ -91,6 +95,24 @@
|
||||
this.init();
|
||||
}
|
||||
this.setData(this.options.data);
|
||||
this.el.bind('mousemove', function(evt) {
|
||||
var offset;
|
||||
offset = _this.el.offset();
|
||||
return _this.fire('hovermove', evt.pageX - offset.left, evt.pageY - offset.top);
|
||||
});
|
||||
this.el.bind('mouseout', function(evt) {
|
||||
return _this.fire('hoverout');
|
||||
});
|
||||
this.el.bind('touchstart touchmove touchend', function(evt) {
|
||||
var offset, touch;
|
||||
touch = evt.originalEvent.touches[0] || evt.originalEvent.changedTouches[0];
|
||||
offset = _this.el.offset();
|
||||
_this.fire('hover', touch.pageX - offset.left, touch.pageY - offset.top);
|
||||
return touch;
|
||||
});
|
||||
if (this.postInit) {
|
||||
this.postInit();
|
||||
}
|
||||
}
|
||||
|
||||
Grid.prototype.gridDefaults = {
|
||||
@ -99,6 +121,7 @@
|
||||
gridStrokeWidth: 0.5,
|
||||
gridTextColor: '#888',
|
||||
gridTextSize: 12,
|
||||
hideHover: false,
|
||||
numLines: 5,
|
||||
padding: 25,
|
||||
parseTime: true,
|
||||
@ -359,6 +382,14 @@
|
||||
return "" + this.options.preUnits + (Morris.commas(label)) + this.options.postUnits;
|
||||
};
|
||||
|
||||
Grid.prototype.updateHover = function(x, y) {
|
||||
var hit, _ref;
|
||||
hit = this.hitTest(x, y);
|
||||
if (hit != null) {
|
||||
return (_ref = this.hover).update.apply(_ref, hit);
|
||||
}
|
||||
};
|
||||
|
||||
return Grid;
|
||||
|
||||
})(Morris.EventEmitter);
|
||||
@ -420,16 +451,78 @@
|
||||
}
|
||||
};
|
||||
|
||||
Morris.Hover = (function() {
|
||||
|
||||
Hover.defaults = {
|
||||
"class": 'morris-hover morris-default-style'
|
||||
};
|
||||
|
||||
function Hover(options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
this.options = $.extend({}, Morris.Hover.defaults, options);
|
||||
this.el = $("<div class='" + this.options["class"] + "'></div>");
|
||||
this.el.hide();
|
||||
this.options.parent.append(this.el);
|
||||
}
|
||||
|
||||
Hover.prototype.update = function(html, x, y) {
|
||||
this.html(html);
|
||||
this.show();
|
||||
return this.moveTo(x, y);
|
||||
};
|
||||
|
||||
Hover.prototype.html = function(content) {
|
||||
return this.el.html(content);
|
||||
};
|
||||
|
||||
Hover.prototype.moveTo = function(x, y) {
|
||||
var hoverHeight, hoverWidth, left, parentHeight, parentWidth, top;
|
||||
parentWidth = this.options.parent.innerWidth();
|
||||
parentHeight = this.options.parent.innerHeight();
|
||||
hoverWidth = this.el.outerWidth();
|
||||
hoverHeight = this.el.outerHeight();
|
||||
left = Math.min(Math.max(0, x - hoverWidth / 2), parentWidth - hoverWidth);
|
||||
if (y != null) {
|
||||
top = y - hoverHeight - 10;
|
||||
if (top < 0) {
|
||||
top = y + 10;
|
||||
if (top + hoverHeight > parentHeight) {
|
||||
top = parentHeight / 2 - hoverHeight / 2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
top = parentHeight / 2 - hoverHeight / 2;
|
||||
}
|
||||
return this.el.css({
|
||||
left: left + "px",
|
||||
top: top + "px"
|
||||
});
|
||||
};
|
||||
|
||||
Hover.prototype.show = function() {
|
||||
return this.el.show();
|
||||
};
|
||||
|
||||
Hover.prototype.hide = function() {
|
||||
return this.el.hide();
|
||||
};
|
||||
|
||||
return Hover;
|
||||
|
||||
})();
|
||||
|
||||
Morris.Line = (function(_super) {
|
||||
|
||||
__extends(Line, _super);
|
||||
|
||||
function Line(options) {
|
||||
this.updateHilight = __bind(this.updateHilight, this);
|
||||
|
||||
this.hilight = __bind(this.hilight, this);
|
||||
|
||||
this.updateHover = __bind(this.updateHover, this);
|
||||
this.onHoverOut = __bind(this.onHoverOut, this);
|
||||
|
||||
this.onHoverMove = __bind(this.onHoverMove, this);
|
||||
if (!(this instanceof Morris.Line)) {
|
||||
return new Morris.Line(options);
|
||||
}
|
||||
@ -437,32 +530,19 @@
|
||||
}
|
||||
|
||||
Line.prototype.init = function() {
|
||||
var touchHandler,
|
||||
_this = this;
|
||||
this.pointGrow = Raphael.animation({
|
||||
r: this.options.pointSize + 3
|
||||
}, 25, 'linear');
|
||||
this.pointShrink = Raphael.animation({
|
||||
r: this.options.pointSize
|
||||
}, 25, 'linear');
|
||||
this.prevHilight = null;
|
||||
this.el.mousemove(function(evt) {
|
||||
return _this.updateHilight(evt.pageX);
|
||||
});
|
||||
if (this.options.hideHover) {
|
||||
this.el.mouseout(function(evt) {
|
||||
return _this.hilight(null);
|
||||
if (this.options.hideHover !== 'always') {
|
||||
this.hover = new Morris.Hover({
|
||||
parent: this.el
|
||||
});
|
||||
this.on('hovermove', this.onHoverMove);
|
||||
return this.on('hoverout', this.onHoverOut);
|
||||
}
|
||||
touchHandler = function(evt) {
|
||||
var touch;
|
||||
touch = evt.originalEvent.touches[0] || evt.originalEvent.changedTouches[0];
|
||||
_this.updateHilight(touch.pageX);
|
||||
return touch;
|
||||
};
|
||||
this.el.bind('touchstart', touchHandler);
|
||||
this.el.bind('touchmove', touchHandler);
|
||||
return this.el.bind('touchend', touchHandler);
|
||||
};
|
||||
|
||||
Line.prototype.defaults = {
|
||||
@ -472,26 +552,16 @@
|
||||
pointWidths: [1],
|
||||
pointStrokeColors: ['#ffffff'],
|
||||
pointFillColors: [],
|
||||
hoverPaddingX: 10,
|
||||
hoverPaddingY: 5,
|
||||
hoverMargin: 10,
|
||||
hoverFillColor: '#fff',
|
||||
hoverBorderColor: '#ccc',
|
||||
hoverBorderWidth: 2,
|
||||
hoverOpacity: 0.95,
|
||||
hoverLabelColor: '#444',
|
||||
hoverFontSize: 12,
|
||||
smooth: true,
|
||||
hideHover: false,
|
||||
xLabels: 'auto',
|
||||
xLabelFormat: null,
|
||||
continuousLine: true
|
||||
continuousLine: true,
|
||||
hideHover: false
|
||||
};
|
||||
|
||||
Line.prototype.calc = function() {
|
||||
this.calcPoints();
|
||||
this.generatePaths();
|
||||
return this.calcHoverMargins();
|
||||
return this.generatePaths();
|
||||
};
|
||||
|
||||
Line.prototype.calcPoints = function() {
|
||||
@ -501,7 +571,7 @@
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
row = _ref[_i];
|
||||
row._x = this.transX(row.x);
|
||||
_results.push(row._y = (function() {
|
||||
row._y = (function() {
|
||||
var _j, _len1, _ref1, _results1;
|
||||
_ref1 = row.y;
|
||||
_results1 = [];
|
||||
@ -514,23 +584,72 @@
|
||||
}
|
||||
}
|
||||
return _results1;
|
||||
}).call(this));
|
||||
}).call(this);
|
||||
_results.push(row._ymax = Math.min.apply(null, [this.bottom].concat((function() {
|
||||
var _j, _len1, _ref1, _results1;
|
||||
_ref1 = row._y;
|
||||
_results1 = [];
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
y = _ref1[_j];
|
||||
if (y != null) {
|
||||
_results1.push(y);
|
||||
}
|
||||
}
|
||||
return _results1;
|
||||
})())));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
Line.prototype.calcHoverMargins = function() {
|
||||
var i, r;
|
||||
return this.hoverMargins = (function() {
|
||||
var _i, _len, _ref, _results;
|
||||
_ref = this.data.slice(1);
|
||||
_results = [];
|
||||
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
|
||||
r = _ref[i];
|
||||
_results.push((r._x + this.data[i]._x) / 2);
|
||||
Line.prototype.hitTest = function(x, y) {
|
||||
var index, r, _i, _len, _ref;
|
||||
_ref = this.data.slice(1);
|
||||
for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) {
|
||||
r = _ref[index];
|
||||
if (x < (r._x + this.data[index]._x) / 2) {
|
||||
break;
|
||||
}
|
||||
return _results;
|
||||
}).call(this);
|
||||
}
|
||||
return index;
|
||||
};
|
||||
|
||||
Line.prototype.onHoverMove = function(x, y) {
|
||||
var index;
|
||||
index = this.hitTest(x, y);
|
||||
return this.displayHoverForRow(index);
|
||||
};
|
||||
|
||||
Line.prototype.onHoverOut = function() {
|
||||
if (this.options.hideHover === 'auto') {
|
||||
return this.displayHoverForIndex(null);
|
||||
}
|
||||
};
|
||||
|
||||
Line.prototype.displayHoverForRow = function(index) {
|
||||
var _ref;
|
||||
if (index != null) {
|
||||
(_ref = this.hover).update.apply(_ref, this.hoverContentForRow(index));
|
||||
return this.hilight(index);
|
||||
} else {
|
||||
this.hover.hide();
|
||||
return this.hilight();
|
||||
}
|
||||
};
|
||||
|
||||
Line.prototype.hoverContentForRow = function(index) {
|
||||
var content, j, row, y, _i, _len, _ref;
|
||||
row = this.data[index];
|
||||
if (typeof this.options.hoverCallback === 'function') {
|
||||
content = this.options.hoverCallback(index, this.options);
|
||||
} else {
|
||||
content = "<div class='morris-hover-row-label'>" + row.label + "</div>";
|
||||
_ref = row.y;
|
||||
for (j = _i = 0, _len = _ref.length; _i < _len; j = ++_i) {
|
||||
y = _ref[j];
|
||||
content += "<div class='morris-hover-point' style='color: " + (this.colorFor(row, j, 'label')) + "'>\n " + this.options.labels[j] + ":\n " + (this.yLabelFormat(y)) + "\n</div>";
|
||||
}
|
||||
}
|
||||
return [content, row._x, row._ymax];
|
||||
};
|
||||
|
||||
Line.prototype.generatePaths = function() {
|
||||
@ -581,8 +700,9 @@
|
||||
Line.prototype.draw = function() {
|
||||
this.drawXAxis();
|
||||
this.drawSeries();
|
||||
this.drawHover();
|
||||
return this.hilight(this.options.hideHover ? null : this.data.length - 1);
|
||||
if (this.options.hideHover === false) {
|
||||
return this.displayHoverForRow(this.data.length - 1);
|
||||
}
|
||||
};
|
||||
|
||||
Line.prototype.drawXAxis = function() {
|
||||
@ -633,7 +753,7 @@
|
||||
for (i = _i = _ref = this.options.ykeys.length - 1; _ref <= 0 ? _i <= 0 : _i >= 0; i = _ref <= 0 ? ++_i : --_i) {
|
||||
path = this.paths[i];
|
||||
if (path !== null) {
|
||||
this.r.path(path).attr('stroke', this.colorForSeries(i)).attr('stroke-width', this.options.lineWidth);
|
||||
this.r.path(path).attr('stroke', this.colorFor(row, i, 'line')).attr('stroke-width', this.options.lineWidth);
|
||||
}
|
||||
}
|
||||
this.seriesPoints = (function() {
|
||||
@ -653,7 +773,7 @@
|
||||
for (_k = 0, _len = _ref2.length; _k < _len; _k++) {
|
||||
row = _ref2[_k];
|
||||
if (row._y[i] != null) {
|
||||
circle = this.r.circle(row._x, row._y[i], this.options.pointSize).attr('fill', this.pointFillColorForSeries(i) || this.colorForSeries(i)).attr('stroke-width', this.strokeWidthForSeries(i)).attr('stroke', this.strokeForSeries(i));
|
||||
circle = this.r.circle(row._x, row._y[i], this.options.pointSize).attr('fill', this.colorFor(row, i, 'point')).attr('stroke-width', this.strokeWidthForSeries(i)).attr('stroke', this.strokeForSeries(i));
|
||||
} else {
|
||||
circle = null;
|
||||
}
|
||||
@ -732,76 +852,6 @@
|
||||
return _results;
|
||||
};
|
||||
|
||||
Line.prototype.drawHover = function() {
|
||||
var i, idx, yLabel, _i, _ref, _results;
|
||||
this.hoverHeight = this.options.hoverFontSize * 1.5 * (this.options.ykeys.length + 1);
|
||||
this.hover = this.r.rect(-10, -this.hoverHeight / 2 - this.options.hoverPaddingY, 20, this.hoverHeight + this.options.hoverPaddingY * 2, 10).attr('fill', this.options.hoverFillColor).attr('stroke', this.options.hoverBorderColor).attr('stroke-width', this.options.hoverBorderWidth).attr('opacity', this.options.hoverOpacity);
|
||||
this.xLabel = this.r.text(0, (this.options.hoverFontSize * 0.75) - this.hoverHeight / 2, '').attr('fill', this.options.hoverLabelColor).attr('font-weight', 'bold').attr('font-size', this.options.hoverFontSize);
|
||||
this.hoverSet = this.r.set();
|
||||
this.hoverSet.push(this.hover);
|
||||
this.hoverSet.push(this.xLabel);
|
||||
this.yLabels = [];
|
||||
_results = [];
|
||||
for (i = _i = 0, _ref = this.options.ykeys.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
|
||||
idx = this.cumulative ? this.options.ykeys.length - i - 1 : i;
|
||||
yLabel = this.r.text(0, this.options.hoverFontSize * 1.5 * (idx + 1.5) - this.hoverHeight / 2, '').attr('fill', this.colorForSeries(i)).attr('font-size', this.options.hoverFontSize);
|
||||
this.yLabels.push(yLabel);
|
||||
_results.push(this.hoverSet.push(yLabel));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
Line.prototype.updateHover = function(index) {
|
||||
var i, l, maxLabelWidth, row, xloc, y, yloc, _i, _len, _ref;
|
||||
this.hoverSet.show();
|
||||
row = this.data[index];
|
||||
this.xLabel.attr('text', row.label);
|
||||
_ref = row.y;
|
||||
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
|
||||
y = _ref[i];
|
||||
this.yLabels[i].attr('text', "" + this.options.labels[i] + ": " + (this.yLabelFormat(y)));
|
||||
}
|
||||
maxLabelWidth = Math.max.apply(null, (function() {
|
||||
var _j, _len1, _ref1, _results;
|
||||
_ref1 = this.yLabels;
|
||||
_results = [];
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
l = _ref1[_j];
|
||||
_results.push(l.getBBox().width);
|
||||
}
|
||||
return _results;
|
||||
}).call(this));
|
||||
maxLabelWidth = Math.max(maxLabelWidth, this.xLabel.getBBox().width);
|
||||
this.hover.attr('width', maxLabelWidth + this.options.hoverPaddingX * 2);
|
||||
this.hover.attr('x', -this.options.hoverPaddingX - maxLabelWidth / 2);
|
||||
yloc = Math.min.apply(null, ((function() {
|
||||
var _j, _len1, _ref1, _results;
|
||||
_ref1 = row._y;
|
||||
_results = [];
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
y = _ref1[_j];
|
||||
if (y != null) {
|
||||
_results.push(y);
|
||||
}
|
||||
}
|
||||
return _results;
|
||||
})()).concat(this.bottom));
|
||||
if (yloc > this.hoverHeight + this.options.hoverPaddingY * 2 + this.options.hoverMargin + this.top) {
|
||||
yloc = yloc - this.hoverHeight / 2 - this.options.hoverPaddingY - this.options.hoverMargin;
|
||||
} else {
|
||||
yloc = yloc + this.hoverHeight / 2 + this.options.hoverPaddingY + this.options.hoverMargin;
|
||||
}
|
||||
yloc = Math.max(this.top + this.hoverHeight / 2 + this.options.hoverPaddingY, yloc);
|
||||
yloc = Math.min(this.bottom - this.hoverHeight / 2 - this.options.hoverPaddingY, yloc);
|
||||
xloc = Math.min(this.right - maxLabelWidth / 2 - this.options.hoverPaddingX, this.data[index]._x);
|
||||
xloc = Math.max(this.left + maxLabelWidth / 2 + this.options.hoverPaddingX, xloc);
|
||||
return this.hoverSet.attr('transform', "t" + xloc + "," + yloc);
|
||||
};
|
||||
|
||||
Line.prototype.hideHover = function() {
|
||||
return this.hoverSet.hide();
|
||||
};
|
||||
|
||||
Line.prototype.hilight = function(index) {
|
||||
var i, _i, _j, _ref, _ref1;
|
||||
if (this.prevHilight !== null && this.prevHilight !== index) {
|
||||
@ -817,27 +867,8 @@
|
||||
this.seriesPoints[i][index].animate(this.pointGrow);
|
||||
}
|
||||
}
|
||||
this.updateHover(index);
|
||||
}
|
||||
this.prevHilight = index;
|
||||
if (!(index != null)) {
|
||||
return this.hideHover();
|
||||
}
|
||||
};
|
||||
|
||||
Line.prototype.updateHilight = function(x) {
|
||||
var hoverIndex, _i, _ref;
|
||||
x -= this.el.offset().left;
|
||||
for (hoverIndex = _i = 0, _ref = this.hoverMargins.length; 0 <= _ref ? _i < _ref : _i > _ref; hoverIndex = 0 <= _ref ? ++_i : --_i) {
|
||||
if (this.hoverMargins[hoverIndex] > x) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this.hilight(hoverIndex);
|
||||
};
|
||||
|
||||
Line.prototype.colorForSeries = function(index) {
|
||||
return this.options.lineColors[index % this.options.lineColors.length];
|
||||
return this.prevHilight = index;
|
||||
};
|
||||
|
||||
Line.prototype.strokeWidthForSeries = function(index) {
|
||||
@ -848,8 +879,14 @@
|
||||
return this.options.pointStrokeColors[index % this.options.pointStrokeColors.length];
|
||||
};
|
||||
|
||||
Line.prototype.pointFillColorForSeries = function(index) {
|
||||
return this.options.pointFillColors[index % this.options.pointFillColors.length];
|
||||
Line.prototype.colorFor = function(row, sidx, type) {
|
||||
if (typeof this.options.lineColors === 'function') {
|
||||
return this.options.lineColors.call(this, row, sidx, type);
|
||||
} else if (type === 'point') {
|
||||
return this.options.pointFillColors[sidx % this.options.pointFillColors.length] || this.options.lineColors[sidx % this.options.lineColors.length];
|
||||
} else {
|
||||
return this.options.lineColors[sidx % this.options.lineColors.length];
|
||||
}
|
||||
};
|
||||
|
||||
return Line;
|
||||
@ -1005,7 +1042,7 @@
|
||||
row = _ref[_i];
|
||||
row._x = this.transX(row.x);
|
||||
total = 0;
|
||||
_results.push(row._y = (function() {
|
||||
row._y = (function() {
|
||||
var _j, _len1, _ref1, _results1;
|
||||
_ref1 = row.y;
|
||||
_results1 = [];
|
||||
@ -1015,7 +1052,8 @@
|
||||
_results1.push(this.transY(total));
|
||||
}
|
||||
return _results1;
|
||||
}).call(this));
|
||||
}).call(this);
|
||||
_results.push(row._ymax = row._y[row._y.length - 1]);
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
@ -1034,7 +1072,7 @@
|
||||
|
||||
Area.prototype.fillForSeries = function(i) {
|
||||
var color;
|
||||
color = Raphael.rgb2hsl(this.colorForSeries(i));
|
||||
color = Raphael.rgb2hsl(this.colorFor(this.data[i], i, 'line'));
|
||||
return Raphael.hsl(color.h, Math.min(255, color.s * 0.75), Math.min(255, color.l * 1.25));
|
||||
};
|
||||
|
||||
@ -1047,11 +1085,9 @@
|
||||
__extends(Bar, _super);
|
||||
|
||||
function Bar(options) {
|
||||
this.updateHilight = __bind(this.updateHilight, this);
|
||||
this.onHoverOut = __bind(this.onHoverOut, this);
|
||||
|
||||
this.hilight = __bind(this.hilight, this);
|
||||
|
||||
this.updateHover = __bind(this.updateHover, this);
|
||||
this.onHoverMove = __bind(this.onHoverMove, this);
|
||||
if (!(this instanceof Morris.Bar)) {
|
||||
return new Morris.Bar(options);
|
||||
}
|
||||
@ -1061,48 +1097,28 @@
|
||||
}
|
||||
|
||||
Bar.prototype.init = function() {
|
||||
var touchHandler,
|
||||
_this = this;
|
||||
this.cumulative = this.options.stacked;
|
||||
this.prevHilight = null;
|
||||
this.el.mousemove(function(evt) {
|
||||
return _this.updateHilight(evt.pageX);
|
||||
});
|
||||
if (this.options.hideHover) {
|
||||
this.el.mouseout(function(evt) {
|
||||
return _this.hilight(null);
|
||||
if (this.options.hideHover !== 'always') {
|
||||
this.hover = new Morris.Hover({
|
||||
parent: this.el
|
||||
});
|
||||
this.on('hovermove', this.onHoverMove);
|
||||
return this.on('hoverout', this.onHoverOut);
|
||||
}
|
||||
touchHandler = function(evt) {
|
||||
var touch;
|
||||
touch = evt.originalEvent.touches[0] || evt.originalEvent.changedTouches[0];
|
||||
_this.updateHilight(touch.pageX);
|
||||
return touch;
|
||||
};
|
||||
this.el.bind('touchstart', touchHandler);
|
||||
this.el.bind('touchmove', touchHandler);
|
||||
return this.el.bind('touchend', touchHandler);
|
||||
};
|
||||
|
||||
Bar.prototype.defaults = {
|
||||
barSizeRatio: 0.75,
|
||||
barGap: 3,
|
||||
barColors: ['#0b62a4', '#7a92a3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed'],
|
||||
hoverPaddingX: 10,
|
||||
hoverPaddingY: 5,
|
||||
hoverMargin: 10,
|
||||
hoverFillColor: '#fff',
|
||||
hoverBorderColor: '#ccc',
|
||||
hoverBorderWidth: 2,
|
||||
hoverOpacity: 0.95,
|
||||
hoverLabelColor: '#444',
|
||||
hoverFontSize: 12,
|
||||
hideHover: false
|
||||
barColors: ['#0b62a4', '#7a92a3', '#4da74d', '#afd8f8', '#edc240', '#cb4b4b', '#9440ed']
|
||||
};
|
||||
|
||||
Bar.prototype.calc = function() {
|
||||
var _ref;
|
||||
this.calcBars();
|
||||
return this.calcHoverMargins();
|
||||
if (this.options.hideHover === false) {
|
||||
return (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(this.data.length - 1));
|
||||
}
|
||||
};
|
||||
|
||||
Bar.prototype.calcBars = function() {
|
||||
@ -1130,23 +1146,9 @@
|
||||
return _results;
|
||||
};
|
||||
|
||||
Bar.prototype.calcHoverMargins = function() {
|
||||
var i;
|
||||
return this.hoverMargins = (function() {
|
||||
var _i, _ref, _results;
|
||||
_results = [];
|
||||
for (i = _i = 1, _ref = this.data.length; 1 <= _ref ? _i < _ref : _i > _ref; i = 1 <= _ref ? ++_i : --_i) {
|
||||
_results.push(this.left + i * this.width / this.data.length);
|
||||
}
|
||||
return _results;
|
||||
}).call(this);
|
||||
};
|
||||
|
||||
Bar.prototype.draw = function() {
|
||||
this.drawXAxis();
|
||||
this.drawSeries();
|
||||
this.drawHover();
|
||||
return this.hilight(this.options.hideHover ? null : this.data.length - 1);
|
||||
return this.drawSeries();
|
||||
};
|
||||
|
||||
Bar.prototype.drawXAxis = function() {
|
||||
@ -1217,79 +1219,6 @@
|
||||
}).call(this);
|
||||
};
|
||||
|
||||
Bar.prototype.drawHover = function() {
|
||||
var i, yLabel, _i, _ref, _results;
|
||||
this.hoverHeight = this.options.hoverFontSize * 1.5 * (this.options.ykeys.length + 1);
|
||||
this.hover = this.r.rect(-10, -this.hoverHeight / 2 - this.options.hoverPaddingY, 20, this.hoverHeight + this.options.hoverPaddingY * 2, 10).attr('fill', this.options.hoverFillColor).attr('stroke', this.options.hoverBorderColor).attr('stroke-width', this.options.hoverBorderWidth).attr('opacity', this.options.hoverOpacity);
|
||||
this.xLabel = this.r.text(0, (this.options.hoverFontSize * 0.75) - this.hoverHeight / 2, '').attr('fill', this.options.hoverLabelColor).attr('font-weight', 'bold').attr('font-size', this.options.hoverFontSize);
|
||||
this.hoverSet = this.r.set();
|
||||
this.hoverSet.push(this.hover);
|
||||
this.hoverSet.push(this.xLabel);
|
||||
this.yLabels = [];
|
||||
_results = [];
|
||||
for (i = _i = 0, _ref = this.options.ykeys.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
|
||||
yLabel = this.r.text(0, this.options.hoverFontSize * 1.5 * (i + 1.5) - this.hoverHeight / 2, '').attr('font-size', this.options.hoverFontSize);
|
||||
this.yLabels.push(yLabel);
|
||||
_results.push(this.hoverSet.push(yLabel));
|
||||
}
|
||||
return _results;
|
||||
};
|
||||
|
||||
Bar.prototype.updateHover = function(index) {
|
||||
var i, l, maxLabelWidth, row, xloc, y, yloc, _i, _len, _ref;
|
||||
this.hoverSet.show();
|
||||
row = this.data[index];
|
||||
this.xLabel.attr('text', row.label);
|
||||
_ref = row.y;
|
||||
for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
|
||||
y = _ref[i];
|
||||
this.yLabels[i].attr('fill', this.colorFor(row, i, 'hover'));
|
||||
this.yLabels[i].attr('text', "" + this.options.labels[i] + ": " + (this.yLabelFormat(y)));
|
||||
}
|
||||
maxLabelWidth = Math.max.apply(null, (function() {
|
||||
var _j, _len1, _ref1, _results;
|
||||
_ref1 = this.yLabels;
|
||||
_results = [];
|
||||
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
||||
l = _ref1[_j];
|
||||
_results.push(l.getBBox().width);
|
||||
}
|
||||
return _results;
|
||||
}).call(this));
|
||||
maxLabelWidth = Math.max(maxLabelWidth, this.xLabel.getBBox().width);
|
||||
this.hover.attr('width', maxLabelWidth + this.options.hoverPaddingX * 2);
|
||||
this.hover.attr('x', -this.options.hoverPaddingX - maxLabelWidth / 2);
|
||||
yloc = (this.bottom + this.top) / 2;
|
||||
xloc = Math.min(this.right - maxLabelWidth / 2 - this.options.hoverPaddingX, this.data[index]._x);
|
||||
xloc = Math.max(this.left + maxLabelWidth / 2 + this.options.hoverPaddingX, xloc);
|
||||
return this.hoverSet.attr('transform', "t" + xloc + "," + yloc);
|
||||
};
|
||||
|
||||
Bar.prototype.hideHover = function() {
|
||||
return this.hoverSet.hide();
|
||||
};
|
||||
|
||||
Bar.prototype.hilight = function(index) {
|
||||
if (index !== null && this.prevHilight !== index) {
|
||||
this.updateHover(index);
|
||||
}
|
||||
this.prevHilight = index;
|
||||
if (!(index != null)) {
|
||||
return this.hideHover();
|
||||
}
|
||||
};
|
||||
|
||||
Bar.prototype.updateHilight = function(x) {
|
||||
var hoverIndex, _i, _ref;
|
||||
x -= this.el.offset().left;
|
||||
for (hoverIndex = _i = 0, _ref = this.hoverMargins.length; 0 <= _ref ? _i < _ref : _i > _ref; hoverIndex = 0 <= _ref ? ++_i : --_i) {
|
||||
if (this.hoverMargins[hoverIndex] > x) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this.hilight(hoverIndex);
|
||||
};
|
||||
|
||||
Bar.prototype.colorFor = function(row, sidx, type) {
|
||||
var r, s;
|
||||
if (typeof this.options.barColors === 'function') {
|
||||
@ -1309,6 +1238,40 @@
|
||||
}
|
||||
};
|
||||
|
||||
Bar.prototype.hitTest = function(x, y) {
|
||||
x = Math.max(Math.min(x, this.right), this.left);
|
||||
return Math.min(this.data.length - 1, Math.floor((x - this.left) / ((this.right - this.left) / this.data.length)));
|
||||
};
|
||||
|
||||
Bar.prototype.onHoverMove = function(x, y) {
|
||||
var index, _ref;
|
||||
index = this.hitTest(x, y);
|
||||
return (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(index));
|
||||
};
|
||||
|
||||
Bar.prototype.onHoverOut = function() {
|
||||
if (this.options.hideHover === 'auto') {
|
||||
return this.hover.hide();
|
||||
}
|
||||
};
|
||||
|
||||
Bar.prototype.hoverContentForRow = function(index) {
|
||||
var content, j, row, x, y, _i, _len, _ref;
|
||||
if (typeof this.options.hoverCallback === 'function') {
|
||||
content = this.options.hoverCallback(index, this.options);
|
||||
} else {
|
||||
row = this.data[index];
|
||||
content = "<div class='morris-hover-row-label'>" + row.label + "</div>";
|
||||
_ref = row.y;
|
||||
for (j = _i = 0, _len = _ref.length; _i < _len; j = ++_i) {
|
||||
y = _ref[j];
|
||||
content += "<div class='morris-hover-point' style='color: " + (this.colorFor(row, j, 'label')) + "'>\n " + this.options.labels[j] + ":\n " + (this.yLabelFormat(y)) + "\n</div>";
|
||||
}
|
||||
}
|
||||
x = this.left + (index + 0.5) * (this.right - this.left) / this.data.length;
|
||||
return [content, x];
|
||||
};
|
||||
|
||||
return Bar;
|
||||
|
||||
})(Morris.Grid);
|
||||
|
2
morris.min.js
vendored
2
morris.min.js
vendored
File diff suppressed because one or more lines are too long
@ -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"
|
||||
}
|
||||
}
|
||||
|
64
spec/lib/hover_spec.coffee
Normal file
64
spec/lib/hover_spec.coffee
Normal file
@ -0,0 +1,64 @@
|
||||
describe "Morris.Hover", ->
|
||||
|
||||
describe "with dummy content", ->
|
||||
|
||||
beforeEach ->
|
||||
parent = $('<div style="width:200px;height:180px"></div>')
|
||||
.appendTo($('#test'))
|
||||
@hover = new Morris.Hover(parent: parent)
|
||||
@element = $('#test .morris-hover')
|
||||
|
||||
it "should initialise a hidden, empty popup", ->
|
||||
@element.should.exist
|
||||
@element.should.be.hidden
|
||||
@element.should.be.empty
|
||||
|
||||
describe "#show", ->
|
||||
it "should show the popup", ->
|
||||
@hover.show()
|
||||
@element.should.be.visible
|
||||
|
||||
describe "#hide", ->
|
||||
it "should hide the popup", ->
|
||||
@hover.show()
|
||||
@hover.hide()
|
||||
@element.should.be.hidden
|
||||
|
||||
describe "#html", ->
|
||||
it "should replace the contents of the element", ->
|
||||
@hover.html('<div>Foobarbaz</div>')
|
||||
@element.should.have.html('<div>Foobarbaz</div>')
|
||||
|
||||
describe "#moveTo", ->
|
||||
beforeEach ->
|
||||
@hover.html('<div style="width:84px;height:84px"></div>')
|
||||
|
||||
it "should place the popup directly above the given point", ->
|
||||
@hover.moveTo(100, 150)
|
||||
@element.should.have.css('left', '50px')
|
||||
@element.should.have.css('top', '40px')
|
||||
|
||||
it "should place the popup below the given point if it does not fit above", ->
|
||||
@hover.moveTo(100, 50)
|
||||
@element.should.have.css('left', '50px')
|
||||
@element.should.have.css('top', '60px')
|
||||
|
||||
it "should center the popup vertically if it will not fit above or below", ->
|
||||
@hover.moveTo(100, 100)
|
||||
@element.should.have.css('left', '50px')
|
||||
@element.should.have.css('top', '40px')
|
||||
|
||||
it "should center the popup vertically if no y value is supplied", ->
|
||||
@hover.moveTo(100)
|
||||
@element.should.have.css('left', '50px')
|
||||
@element.should.have.css('top', '40px')
|
||||
|
||||
describe "#update", ->
|
||||
it "should update content, show and reposition the popup", ->
|
||||
hover = new Morris.Hover(parent: $('#test'))
|
||||
html = "<div style='width:84px;height:84px'>Hello, Everyone!</div>"
|
||||
hover.update(html, 150, 200)
|
||||
el = $('#test .morris-hover')
|
||||
el.should.have.css('left', '100px')
|
||||
el.should.have.css('top', '90px')
|
||||
el.should.have.text('Hello, Everyone!')
|
@ -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', ->
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
<meta charset="utf-8">
|
||||
<title>morris.js tests</title>
|
||||
<link rel="stylesheet" href="vendor/mocha-1.6.0.css" type="text/css" media="screen" />
|
||||
<link rel="stylesheet" href="../morris.css" type="text/css" media="screen" />
|
||||
<script src="vendor/jquery-1.8.2.min.js"></script>
|
||||
<script type="text/javascript" src="vendor/raphael-2.1.0.min.js"></script>
|
||||
</head>
|
||||
@ -15,6 +16,7 @@
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="vendor/chai-1.3.0.js"></script>
|
||||
<script type="text/javascript" src="vendor/chai-jquery-1.1.0.js"></script>
|
||||
<script type="text/javascript" src="vendor/sinon-1.5.0.js"></script>
|
||||
<script type="text/javascript" src="vendor/sinon-chai-2.1.2.js"></script>
|
||||
<script>
|
||||
@ -23,7 +25,7 @@
|
||||
|
||||
<script type="text/javascript" src="../morris.js"></script>
|
||||
<script type="text/javascript" src="../build/spec.js"></script>
|
||||
<div id="test" style="visibility: hidden"></div>
|
||||
<div id="test" style="width: 400px; height: 200px;"></div>
|
||||
<script>
|
||||
if (navigator.userAgent.indexOf('PhantomJS') < 0) {
|
||||
mocha.run();
|
||||
|
232
spec/vendor/chai-jquery-1.1.0.js
vendored
Normal file
232
spec/vendor/chai-jquery-1.1.0.js
vendored
Normal file
@ -0,0 +1,232 @@
|
||||
(function (chaiJquery) {
|
||||
// Module systems magic dance.
|
||||
if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
|
||||
// NodeJS
|
||||
module.exports = chaiJquery;
|
||||
} else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(function () {
|
||||
return chaiJquery;
|
||||
});
|
||||
} else {
|
||||
// Other environment (usually <script> tag): plug in to global chai instance directly.
|
||||
chai.use(chaiJquery);
|
||||
}
|
||||
}(function (chai, utils) {
|
||||
var inspect = utils.inspect,
|
||||
flag = utils.flag;
|
||||
|
||||
jQuery.fn.inspect = function (depth) {
|
||||
var el = jQuery('<div />').append(this.clone());
|
||||
if (depth) {
|
||||
var children = el.children();
|
||||
while (depth-- > 0)
|
||||
children = children.children();
|
||||
children.html('...');
|
||||
}
|
||||
return el.html();
|
||||
};
|
||||
|
||||
var props = {attr: 'attribute', css: 'CSS property'};
|
||||
for (var prop in props) {
|
||||
(function (prop, description) {
|
||||
chai.Assertion.addMethod(prop, function (name, val) {
|
||||
var actual = flag(this, 'object')[prop](name);
|
||||
|
||||
if (!flag(this, 'negate') || undefined === val) {
|
||||
this.assert(
|
||||
undefined !== actual
|
||||
, 'expected #{this} to have a #{exp} ' + description
|
||||
, 'expected #{this} not to have a #{exp} ' + description
|
||||
, name
|
||||
);
|
||||
}
|
||||
|
||||
if (undefined !== val) {
|
||||
this.assert(
|
||||
val === actual
|
||||
, 'expected #{this} to have a ' + inspect(name) + ' ' + description + ' with the value #{exp}, but the value was #{act}'
|
||||
, 'expected #{this} not to have a ' + inspect(name) + ' ' + description + ' with the value #{act}'
|
||||
, val
|
||||
, actual
|
||||
);
|
||||
}
|
||||
|
||||
flag(this, 'object', actual);
|
||||
});
|
||||
})(prop, props[prop]);
|
||||
}
|
||||
|
||||
chai.Assertion.addMethod('data', function (name, val) {
|
||||
// Work around a chai bug (https://github.com/logicalparadox/chai/issues/16)
|
||||
if (flag(this, 'negate') && undefined !== val && undefined === flag(this, 'object').data(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var assertion = new chai.Assertion(flag(this, 'object').data());
|
||||
if (flag(this, 'negate'))
|
||||
assertion = assertion.not;
|
||||
return assertion.property(name, val);
|
||||
});
|
||||
|
||||
chai.Assertion.addMethod('class', function (className) {
|
||||
this.assert(
|
||||
flag(this, 'object').hasClass(className)
|
||||
, 'expected #{this} to have class #{exp}'
|
||||
, 'expected #{this} not to have class #{exp}'
|
||||
, className
|
||||
);
|
||||
});
|
||||
|
||||
chai.Assertion.addMethod('id', function (id) {
|
||||
this.assert(
|
||||
flag(this, 'object').attr('id') === id
|
||||
, 'expected #{this} to have id #{exp}'
|
||||
, 'expected #{this} not to have id #{exp}'
|
||||
, id
|
||||
);
|
||||
});
|
||||
|
||||
chai.Assertion.addMethod('html', function (html) {
|
||||
this.assert(
|
||||
flag(this, 'object').html() === html
|
||||
, 'expected #{this} to have HTML #{exp}'
|
||||
, 'expected #{this} not to have HTML #{exp}'
|
||||
, html
|
||||
);
|
||||
});
|
||||
|
||||
chai.Assertion.addMethod('text', function (text) {
|
||||
this.assert(
|
||||
flag(this, 'object').text() === text
|
||||
, 'expected #{this} to have text #{exp}'
|
||||
, 'expected #{this} not to have text #{exp}'
|
||||
, text
|
||||
);
|
||||
});
|
||||
|
||||
chai.Assertion.addMethod('value', function (value) {
|
||||
this.assert(
|
||||
flag(this, 'object').val() === value
|
||||
, 'expected #{this} to have value #{exp}'
|
||||
, 'expected #{this} not to have value #{exp}'
|
||||
, value
|
||||
);
|
||||
});
|
||||
|
||||
jQuery.each(['visible', 'hidden', 'selected', 'checked', 'disabled'], function (i, attr) {
|
||||
chai.Assertion.addProperty(attr, function () {
|
||||
this.assert(
|
||||
flag(this, 'object').is(':' + attr)
|
||||
, 'expected #{this} to be ' + attr
|
||||
, 'expected #{this} not to be ' + attr);
|
||||
});
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteProperty('exist', function (_super) {
|
||||
return function () {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
this.assert(
|
||||
obj.length > 0
|
||||
, 'expected ' + inspect(obj.selector) + ' to exist'
|
||||
, 'expected ' + inspect(obj.selector) + ' not to exist');
|
||||
} else {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteProperty('empty', function (_super) {
|
||||
return function () {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
this.assert(
|
||||
obj.is(':empty')
|
||||
, 'expected #{this} to be empty'
|
||||
, 'expected #{this} not to be empty');
|
||||
} else {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteProperty('be', function (_super) {
|
||||
return function () {
|
||||
var be = function (selector) {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
this.assert(
|
||||
obj.is(selector)
|
||||
, 'expected #{this} to be #{exp}'
|
||||
, 'expected #{this} not to be #{exp}'
|
||||
, selector
|
||||
);
|
||||
} else {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
be.__proto__ = this;
|
||||
return be;
|
||||
}
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteMethod('match', function (_super) {
|
||||
return function (selector) {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
this.assert(
|
||||
obj.is(selector)
|
||||
, 'expected #{this} to match #{exp}'
|
||||
, 'expected #{this} not to match #{exp}'
|
||||
, selector
|
||||
);
|
||||
} else {
|
||||
_super.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteProperty('contain', function (_super) {
|
||||
return function () {
|
||||
_super.call(this);
|
||||
var contain = function (text) {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
this.assert(
|
||||
obj.is(':contains(\'' + text + '\')')
|
||||
, 'expected #{this} to contain #{exp}'
|
||||
, 'expected #{this} not to contain #{exp}'
|
||||
, text
|
||||
);
|
||||
} else {
|
||||
Function.prototype.apply.call(_super.call(this), this, arguments);
|
||||
}
|
||||
};
|
||||
contain.__proto__ = this;
|
||||
return contain;
|
||||
}
|
||||
});
|
||||
|
||||
chai.Assertion.overwriteProperty('have', function (_super) {
|
||||
return function () {
|
||||
var obj = flag(this, 'object');
|
||||
if (obj instanceof jQuery) {
|
||||
var have = function (selector) {
|
||||
this.assert(
|
||||
// Using find() rather than has() to work around a jQuery bug:
|
||||
// http://bugs.jquery.com/ticket/11706
|
||||
obj.find(selector).length > 0
|
||||
, 'expected #{this} to have #{exp}'
|
||||
, 'expected #{this} not to have #{exp}'
|
||||
, selector
|
||||
);
|
||||
};
|
||||
have.__proto__ = this;
|
||||
return have;
|
||||
} else {
|
||||
_super.call(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
Loading…
Reference in New Issue
Block a user