Merge branch 'range-selection' of https://github.com/arachnys/morris.js into arachnys-range-selection

Conflicts:
	morris.min.js
This commit is contained in:
Olly Smith 2013-10-12 13:27:35 +01:00
commit e868701016
5 changed files with 124 additions and 34 deletions

View File

@ -127,9 +127,9 @@ class Morris.Bar extends Morris.Grid
else else
@options.barColors[sidx % @options.barColors.length] @options.barColors[sidx % @options.barColors.length]
# hit test - returns the index of the row beneath the given coordinate # hit test - returns the index of the row at the given x-coordinate
# #
hitTest: (x, y) -> hitTest: (x) ->
return null if @data.length == 0 return null if @data.length == 0
x = Math.max(Math.min(x, @right), @left) x = Math.max(Math.min(x, @right), @left)
Math.min(@data.length - 1, Math.min(@data.length - 1,
@ -139,14 +139,14 @@ class Morris.Bar extends Morris.Grid
# #
# @private # @private
onGridClick: (x, y) => onGridClick: (x, y) =>
index = @hitTest(x, y) index = @hitTest(x)
@fire 'click', index, @options.data[index], x, y @fire 'click', index, @options.data[index], x, y
# hover movement event handler # hover movement event handler
# #
# @private # @private
onHoverMove: (x, y) => onHoverMove: (x, y) =>
index = @hitTest(x, y) index = @hitTest(x)
@hover.update(@hoverContentForRow(index)...) @hover.update(@hoverContentForRow(index)...)
# hover out event handler # hover out event handler

View File

@ -29,6 +29,9 @@ class Morris.Grid extends Morris.EventEmitter
@elementHeight = null @elementHeight = null
@dirty = false @dirty = false
# range selection
@selectFrom = null
# more stuff # more stuff
@init() if @init @init() if @init
@ -38,9 +41,19 @@ class Morris.Grid extends Morris.EventEmitter
# hover # hover
@el.bind 'mousemove', (evt) => @el.bind 'mousemove', (evt) =>
offset = @el.offset() offset = @el.offset()
@fire 'hovermove', evt.pageX - offset.left, evt.pageY - offset.top x = evt.pageX - offset.left
if @selectFrom
left = @data[@hitTest(Math.min(x, @selectFrom))]._x
right = @data[@hitTest(Math.max(x, @selectFrom))]._x
width = right - left
@selectionRect.attr({ x: left, width: width })
else
@fire 'hovermove', x, evt.pageY - offset.top
@el.bind 'mouseout', (evt) => @el.bind 'mouseleave', (evt) =>
if @selectFrom
@selectionRect.hide()
@selectFrom = null
@fire 'hoverout' @fire 'hoverout'
@el.bind 'touchstart touchmove touchend', (evt) => @el.bind 'touchstart touchmove touchend', (evt) =>
@ -53,6 +66,21 @@ class Morris.Grid extends Morris.EventEmitter
offset = @el.offset() offset = @el.offset()
@fire 'gridclick', evt.pageX - offset.left, evt.pageY - offset.top @fire 'gridclick', evt.pageX - offset.left, evt.pageY - offset.top
if @options.rangeSelect
@selectionRect = @raphael.rect(0, 0, 0, @el.innerHeight())
.attr({ fill: @options.rangeSelectColor, stroke: false })
.toBack()
.hide()
@el.bind 'mousedown', (evt) =>
offset = @el.offset()
@startRange evt.pageX - offset.left
@el.bind 'mouseup', (evt) =>
offset = @el.offset()
@endRange evt.pageX - offset.left
@fire 'hovermove', evt.pageX - offset.left, evt.pageY - offset.top
@postInit() if @postInit @postInit() if @postInit
# Default options # Default options
@ -93,6 +121,8 @@ class Morris.Grid extends Morris.EventEmitter
'#3a5f0b' '#3a5f0b'
'#005502' '#005502'
] ]
rangeSelect: null
rangeSelectColor: '#eef'
# Update the data series and redraw the chart. # Update the data series and redraw the chart.
# #
@ -299,11 +329,6 @@ class Morris.Grid extends Morris.EventEmitter
else else
"#{@options.preUnits}#{Morris.commas(label)}#{@options.postUnits}" "#{@options.preUnits}#{Morris.commas(label)}#{@options.postUnits}"
updateHover: (x, y) ->
hit = @hitTest(x, y)
if hit?
@hover.update(hit...)
# draw y axis labels, horizontal lines # draw y axis labels, horizontal lines
# #
drawGrid: -> drawGrid: ->
@ -351,6 +376,22 @@ class Morris.Grid extends Morris.EventEmitter
.attr('stroke', @options.gridLineColor) .attr('stroke', @options.gridLineColor)
.attr('stroke-width', @options.gridStrokeWidth) .attr('stroke-width', @options.gridStrokeWidth)
# Range selection
#
startRange: (x) ->
@hover.hide()
@selectFrom = x
@selectionRect.attr({ x: x, width: 0 }).show()
endRange: (x) ->
if @selectFrom
start = Math.min(@selectFrom, x)
end = Math.max(@selectFrom, x)
@options.rangeSelect.call @el,
start: @data[@hitTest(start)].x
end: @data[@hitTest(end)].x
@selectFrom = null
# Parse a date into a javascript timestamp # Parse a date into a javascript timestamp
# #
# #

View File

@ -57,9 +57,9 @@ class Morris.Line extends Morris.Grid
if y? then @transY(y) else y if y? then @transY(y) else y
row._ymax = Math.min.apply(null, [@bottom].concat(y for y in row._y when y?)) row._ymax = Math.min.apply(null, [@bottom].concat(y for y in row._y when y?))
# hit test - returns the index of the row beneath the given coordinate # hit test - returns the index of the row at the given x-coordinate
# #
hitTest: (x, y) -> hitTest: (x) ->
return null if @data.length == 0 return null if @data.length == 0
# TODO better search algo # TODO better search algo
for r, index in @data.slice(1) for r, index in @data.slice(1)
@ -70,14 +70,14 @@ class Morris.Line extends Morris.Grid
# #
# @private # @private
onGridClick: (x, y) => onGridClick: (x, y) =>
index = @hitTest(x, y) index = @hitTest(x)
@fire 'click', index, @options.data[index], x, y @fire 'click', index, @options.data[index], x, y
# hover movement event handler # hover movement event handler
# #
# @private # @private
onHoverMove: (x, y) => onHoverMove: (x, y) =>
index = @hitTest(x, y) index = @hitTest(x)
@displayHoverForRow(index) @displayHoverForRow(index)
# hover out event handler # hover out event handler

View File

@ -89,16 +89,32 @@
this.elementWidth = null; this.elementWidth = null;
this.elementHeight = null; this.elementHeight = null;
this.dirty = false; this.dirty = false;
this.selectFrom = null;
if (this.init) { if (this.init) {
this.init(); this.init();
} }
this.setData(this.options.data); this.setData(this.options.data);
this.el.bind('mousemove', function(evt) { this.el.bind('mousemove', function(evt) {
var offset; var left, offset, right, width, x;
offset = _this.el.offset(); offset = _this.el.offset();
return _this.fire('hovermove', evt.pageX - offset.left, evt.pageY - offset.top); x = evt.pageX - offset.left;
if (_this.selectFrom) {
left = _this.data[_this.hitTest(Math.min(x, _this.selectFrom))]._x;
right = _this.data[_this.hitTest(Math.max(x, _this.selectFrom))]._x;
width = right - left;
return _this.selectionRect.attr({
x: left,
width: width
});
} else {
return _this.fire('hovermove', x, evt.pageY - offset.top);
}
}); });
this.el.bind('mouseout', function(evt) { this.el.bind('mouseleave', function(evt) {
if (_this.selectFrom) {
_this.selectionRect.hide();
_this.selectFrom = null;
}
return _this.fire('hoverout'); return _this.fire('hoverout');
}); });
this.el.bind('touchstart touchmove touchend', function(evt) { this.el.bind('touchstart touchmove touchend', function(evt) {
@ -113,6 +129,23 @@
offset = _this.el.offset(); offset = _this.el.offset();
return _this.fire('gridclick', evt.pageX - offset.left, evt.pageY - offset.top); return _this.fire('gridclick', evt.pageX - offset.left, evt.pageY - offset.top);
}); });
if (this.options.rangeSelect) {
this.selectionRect = this.raphael.rect(0, 0, 0, this.el.innerHeight()).attr({
fill: this.options.rangeSelectColor,
stroke: false
}).toBack().hide();
this.el.bind('mousedown', function(evt) {
var offset;
offset = _this.el.offset();
return _this.startRange(evt.pageX - offset.left);
});
this.el.bind('mouseup', function(evt) {
var offset;
offset = _this.el.offset();
_this.endRange(evt.pageX - offset.left);
return _this.fire('hovermove', evt.pageX - offset.left, evt.pageY - offset.top);
});
}
if (this.postInit) { if (this.postInit) {
this.postInit(); this.postInit();
} }
@ -143,7 +176,9 @@
goalLineColors: ['#666633', '#999966', '#cc6666', '#663333'], goalLineColors: ['#666633', '#999966', '#cc6666', '#663333'],
events: [], events: [],
eventStrokeWidth: 1.0, eventStrokeWidth: 1.0,
eventLineColors: ['#005a04', '#ccffbb', '#3a5f0b', '#005502'] eventLineColors: ['#005a04', '#ccffbb', '#3a5f0b', '#005502'],
rangeSelect: null,
rangeSelectColor: '#eef'
}; };
Grid.prototype.setData = function(data, redraw) { Grid.prototype.setData = function(data, redraw) {
@ -438,14 +473,6 @@
} }
}; };
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);
}
};
Grid.prototype.drawGrid = function() { Grid.prototype.drawGrid = function() {
var lineY, y, _i, _len, _ref, _results; var lineY, y, _i, _len, _ref, _results;
if (this.options.grid === false && this.options.axes === false) { if (this.options.grid === false && this.options.axes === false) {
@ -508,6 +535,28 @@
return this.raphael.path(path).attr('stroke', this.options.gridLineColor).attr('stroke-width', this.options.gridStrokeWidth); return this.raphael.path(path).attr('stroke', this.options.gridLineColor).attr('stroke-width', this.options.gridStrokeWidth);
}; };
Grid.prototype.startRange = function(x) {
this.hover.hide();
this.selectFrom = x;
return this.selectionRect.attr({
x: x,
width: 0
}).show();
};
Grid.prototype.endRange = function(x) {
var end, start;
if (this.selectFrom) {
start = Math.min(this.selectFrom, x);
end = Math.max(this.selectFrom, x);
this.options.rangeSelect.call(this.el, {
start: this.data[this.hitTest(start)].x,
end: this.data[this.hitTest(end)].x
});
return this.selectFrom = null;
}
};
return Grid; return Grid;
})(Morris.EventEmitter); })(Morris.EventEmitter);
@ -723,7 +772,7 @@
return _results; return _results;
}; };
Line.prototype.hitTest = function(x, y) { Line.prototype.hitTest = function(x) {
var index, r, _i, _len, _ref; var index, r, _i, _len, _ref;
if (this.data.length === 0) { if (this.data.length === 0) {
return null; return null;
@ -740,13 +789,13 @@
Line.prototype.onGridClick = function(x, y) { Line.prototype.onGridClick = function(x, y) {
var index; var index;
index = this.hitTest(x, y); index = this.hitTest(x);
return this.fire('click', index, this.options.data[index], x, y); return this.fire('click', index, this.options.data[index], x, y);
}; };
Line.prototype.onHoverMove = function(x, y) { Line.prototype.onHoverMove = function(x, y) {
var index; var index;
index = this.hitTest(x, y); index = this.hitTest(x);
return this.displayHoverForRow(index); return this.displayHoverForRow(index);
}; };
@ -1465,7 +1514,7 @@
} }
}; };
Bar.prototype.hitTest = function(x, y) { Bar.prototype.hitTest = function(x) {
if (this.data.length === 0) { if (this.data.length === 0) {
return null; return null;
} }
@ -1475,13 +1524,13 @@
Bar.prototype.onGridClick = function(x, y) { Bar.prototype.onGridClick = function(x, y) {
var index; var index;
index = this.hitTest(x, y); index = this.hitTest(x);
return this.fire('click', index, this.options.data[index], x, y); return this.fire('click', index, this.options.data[index], x, y);
}; };
Bar.prototype.onHoverMove = function(x, y) { Bar.prototype.onHoverMove = function(x, y) {
var index, _ref; var index, _ref;
index = this.hitTest(x, y); index = this.hitTest(x);
return (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(index)); return (_ref = this.hover).update.apply(_ref, this.hoverContentForRow(index));
}; };

2
morris.min.js vendored

File diff suppressed because one or more lines are too long