Dygraph.DEFAULT_HEIGHT = 320;
Dygraph.AXIS_LINE_WIDTH = 0.3;
+
// Default attribute values.
Dygraph.DEFAULT_ATTRS = {
highlightCircleSize: 3,
stepPlot: false,
avoidMinZero: false,
+
+ interactionModel: null // will be set to Dygraph.defaultInteractionModel.
};
// Various logging levels.
/**
* Initializes the Dygraph. This creates a new DIV and constructs the PlotKit
- * and context <canvas> inside of it. See the constructor for details
+ * and context <canvas> inside of it. See the constructor for details.
* on the parameters.
* @param {Element} div the Element to render the graph into.
* @param {String | Function} file Source data
};
-//
-// An attempt at scroll wheel management.
-//
// Based on the article at
// http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel
-
Dygraph.cancelEvent = function(e) {
e = e ? e : window.event;
if (e.stopPropagation) {
return false;
}
-
/**
* Generates interface elements for the Dygraph: a containing div, a div to
* display the current point, and a textbox to adjust the rolling average
return Dygraph.pageY(e) - context.py
};
+// Called in response to an interaction model operation that
+// should start the default panning behavior.
+//
+// It's used in the default callback for "mousedown" operations.
+// Custom interaction model builders can use it to provide the default
+// panning behavior.
+//
Dygraph.startPan = function(event, g, context) {
// have to be zoomed in to pan.
+ // TODO(konigsberg): Let's loosen this zoom-to-pan restriction, also
+ // perhaps create panning boundaries? A more flexible pan would make it,
+ // ahem, 'pan-useful'.
var zoomedY = false;
for (var i = 0; i < g.axes_.length; i++) {
if (g.axes_[i].valueWindow || g.axes_[i].valueRange) {
context.draggingDate = (context.dragStartX / g.width_) * context.dateRange + xRange[0];
};
+// Called in response to an interaction model operation that
+// responds to an event that pans the view.
+//
+// It's used in the default callback for "mousemove" operations.
+// Custom interaction model builders can use it to provide the default
+// panning behavior.
+//
Dygraph.movePan = function(event, g, context) {
context.dragEndX = g.dragGetX_(event, context);
context.dragEndY = g.dragGetY_(event, context);
g.drawGraph_();
}
+// Called in response to an interaction model operation that
+// responds to an event that ends panning.
+//
+// It's used in the default callback for "mouseup" operations.
+// Custom interaction model builders can use it to provide the default
+// panning behavior.
+//
Dygraph.endPan = function(event, g, context) {
context.isPanning = false;
context.is2DPan = false;
context.valueRange = null;
}
+// Called in response to an interaction model operation that
+// responds to an event that starts zooming.
+//
+// It's used in the default callback for "mousedown" operations.
+// Custom interaction model builders can use it to provide the default
+// zooming behavior.
+//
Dygraph.startZoom = function(event, g, context) {
context.isZooming = true;
}
+// Called in response to an interaction model operation that
+// responds to an event that defines zoom boundaries.
+//
+// It's used in the default callback for "mousemove" operations.
+// Custom interaction model builders can use it to provide the default
+// zooming behavior.
+//
Dygraph.moveZoom = function(event, g, context) {
context.dragEndX = g.dragGetX_(event, context);
context.dragEndY = g.dragGetY_(event, context);
context.prevDragDirection = context.dragDirection;
}
+// Called in response to an interaction model operation that
+// responds to an event that performs a zoom based on previously defined
+// bounds..
+//
+// It's used in the default callback for "mouseup" operations.
+// Custom interaction model builders can use it to provide the default
+// zooming behavior.
+//
Dygraph.endZoom = function(event, g, context) {
context.isZooming = false;
context.dragEndX = g.dragGetX_(event, context);
context.dragStartY = null;
}
-// Track the beginning of drag events
-Dygraph.prototype.defaultMouseDownFunction = function(event, g, context) {
- context.initializeMouseDown(event, g, context);
+Dygraph.defaultInteractionModel = {
+ // Track the beginning of drag events
+ mousedown: function(event, g, context) {
+ context.initializeMouseDown(event, g, context);
- if (event.altKey || event.shiftKey) {
- Dygraph.startPan(event, g, context);
- } else {
- Dygraph.startZoom(event, g, context);
- }
-};
+ if (event.altKey || event.shiftKey) {
+ Dygraph.startPan(event, g, context);
+ } else {
+ Dygraph.startZoom(event, g, context);
+ }
+ },
-// Draw zoom rectangles when the mouse is down and the user moves around
-Dygraph.prototype.defaultMouseMoveFunction = function(event, g, context) {
- if (context.isZooming) {
- Dygraph.moveZoom(event, g, context);
- } else if (context.isPanning) {
- Dygraph.movePan(event, g, context);
- }
-};
+ // Draw zoom rectangles when the mouse is down and the user moves around
+ mousemove: function(event, g, context) {
+ if (context.isZooming) {
+ Dygraph.moveZoom(event, g, context);
+ } else if (context.isPanning) {
+ Dygraph.movePan(event, g, context);
+ }
+ },
-Dygraph.prototype.defaultMouseUpFunction = function(event, g, context) {
- if (context.isZooming) {
- Dygraph.endZoom(event, g, context);
- } else if (context.isPanning) {
- Dygraph.endPan(event, g, context);
- }
-};
+ mouseup: function(event, g, context) {
+ if (context.isZooming) {
+ Dygraph.endZoom(event, g, context);
+ } else if (context.isPanning) {
+ Dygraph.endPan(event, g, context);
+ }
+ },
-Dygraph.prototype.defaultMouseOutFunction = function(event, g, context) {
// Temporarily cancel the dragging event when the mouse leaves the graph
- if (context.isZooming) {
- context.dragEndX = null;
- context.dragEndY = null;
- }
-};
+ mouseout: function(event, g, context) {
+ if (context.isZooming) {
+ context.dragEndX = null;
+ context.dragEndY = null;
+ }
+ },
-// Double-clicking zooms back out
-Dygraph.prototype.defaultMouseDoubleClickFunction = function(event, g, context) {
// Disable zooming out if panning.
- if (event.altKey || event.shiftKey) {
- return;
+ dblclick: function(event, g, context) {
+ if (event.altKey || event.shiftKey) {
+ return;
+ }
+ // TODO(konigsberg): replace g.doUnzoom()_ with something that is
+ // friendlier to public use.
+ g.doUnzoom_();
}
- g.doUnzoom_();
};
+Dygraph.DEFAULT_ATTRS.interactionModel = Dygraph.defaultInteractionModel;
+
/**
* Set up all the mouse handlers needed to capture dragging behavior for zoom
* events.
Dygraph.prototype.createDragInterface_ = function() {
var context = {
// Tracks whether the mouse is down right now
- isZooming : false,
- isPanning : false, // is this drag part of a pan?
- is2DPan : false, // if so, is that pan 1- or 2-dimensional?
- dragStartX : null,
- dragStartY : null,
- dragEndX : null,
- dragEndY : null,
- dragDirection : null,
- prevEndX : null,
- prevEndY : null,
- prevDragDirection : null,
+ isZooming: false,
+ isPanning: false, // is this drag part of a pan?
+ is2DPan: false, // if so, is that pan 1- or 2-dimensional?
+ dragStartX: null,
+ dragStartY: null,
+ dragEndX: null,
+ dragEndY: null,
+ dragDirection: null,
+ prevEndX: null,
+ prevEndY: null,
+ prevDragDirection: null,
// TODO(danvk): update this comment
// draggingDate and draggingValue represent the [date,value] point on the
// graph at which the mouse was pressed. As the mouse moves while panning,
// the viewport must pan so that the mouse position points to
// [draggingDate, draggingValue]
- draggingDate : null,
+ draggingDate: null,
// TODO(danvk): update this comment
// The range in second/value units that the viewport encompasses during a
// panning operation.
- dateRange : null,
+ dateRange: null,
// Utility function to convert page-wide coordinates to canvas coords
- px : 0,
- py : 0,
+ px: 0,
+ py: 0,
- initializeMouseDown : function(event, g, context) {
- // prevents mouse drags from selecting page text.
+ initializeMouseDown: function(event, g, context) {
+ // prevents mouse drags from selecting page text.
if (event.preventDefault) {
event.preventDefault(); // Firefox, Chrome, etc.
} else {
event.returnValue = false; // IE
- event.cancelBubble = true;
+ event.cancelBubble = true;
}
-
+
context.px = Dygraph.findPosX(g.canvas_);
context.py = Dygraph.findPosY(g.canvas_);
context.dragStartX = g.dragGetX_(event, context);
}
};
- // Defines default behavior if there are no event handlers.
- var handlers = this.user_attrs_.interactionModel || {
- 'mousedown' : this.defaultMouseDownFunction,
- 'mousemove' : this.defaultMouseMoveFunction,
- 'mouseup' : this.defaultMouseUpFunction,
- 'mouseout' : this.defaultMouseOutFunction,
- 'dblclick' : this.defaultMouseDoubleClickFunction
- };
+ var interactionModel = this.attr_("interactionModel");
+
+ // Self is the graph.
+ var self = this;
- // Function that binds g and context to the handler.
- var bindHandler = function(handler, g) {
+ // Function that binds the graph and context to the handler.
+ var bindHandler = function(handler) {
return function(event) {
- handler(event, g, context);
+ handler(event, self, context);
};
};
- for (var eventName in handlers) {
+ for (var eventName in interactionModel) {
+ if (!interactionModel.hasOwnProperty(eventName)) continue;
Dygraph.addEvent(this.mouseEventElement_, eventName,
- bindHandler(handlers[eventName], this));
+ bindHandler(interactionModel[eventName]));
}
- // Self is the graph.
- var self = this;
-
// If the user releases the mouse button during a drag, but not over the
// canvas, then it doesn't count as a zooming action.
Dygraph.addEvent(document, 'mouseup', function(event) {
/**
* Draw a gray zoom rectangle over the desired area of the canvas. Also clears
* up any previous zoom rectangles that were drawn. This could be optimized to
- * avoid extra redrawing, but it's tricky to avoid contexts with the status
+ * avoid extra redrawing, but it's tricky to avoid interactions with the status
* dots.
*
* @param {Number} direction the direction of the zoom rectangle. Acceptable
* @private
*/
Dygraph.dateAxisFormatter = function(date, granularity) {
- if (granularity >= Dygraph.MONTHLY) {
+ if (granularity >= Dygraph.DECADAL) {
+ return date.strftime('%Y');
+ } else if (granularity >= Dygraph.MONTHLY) {
return date.strftime('%b %y');
} else {
var frac = date.getHours() * 3600 + date.getMinutes() * 60 + date.getSeconds() + date.getMilliseconds();
Dygraph.BIANNUAL = 17;
Dygraph.ANNUAL = 18;
Dygraph.DECADAL = 19;
-Dygraph.NUM_GRANULARITIES = 20;
+Dygraph.CENTENNIAL = 20;
+Dygraph.NUM_GRANULARITIES = 21;
Dygraph.SHORT_SPACINGS = [];
Dygraph.SHORT_SPACINGS[Dygraph.SECONDLY] = 1000 * 1;
if (granularity == Dygraph.BIANNUAL) num_months = 2;
if (granularity == Dygraph.ANNUAL) num_months = 1;
if (granularity == Dygraph.DECADAL) { num_months = 1; year_mod = 10; }
+ if (granularity == Dygraph.CENTENNIAL) { num_months = 1; year_mod = 100; }
var msInYear = 365.2524 * 24 * 3600 * 1000;
var num_years = 1.0 * (end_time - start_time) / msInYear;
} else if (granularity == Dygraph.DECADAL) {
months = [ 0 ];
year_mod = 10;
+ } else if (granularity == Dygraph.CENTENNIAL) {
+ months = [ 0 ];
+ year_mod = 100;
+ } else {
+ this.warn("Span of dates is too long");
}
var start_year = new Date(start_time).getFullYear();