Dygraph.DEFAULT_HEIGHT = 320;
Dygraph.AXIS_LINE_WIDTH = 0.3;
+
+Dygraph.DEFAULT_INTERACTION_MODEL = {
+ // 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);
+ }
+ },
+
+ // 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);
+ }
+ },
+
+ 'mouseup' : function(event, g, context) {
+ if (context.isZooming) {
+ Dygraph.endZoom(event, g, context);
+ } else if (context.isPanning) {
+ Dygraph.endPan(event, g, context);
+ }
+ },
+
+ // Temporarily cancel the dragging event when the mouse leaves the graph
+ 'mouseout' : function(event, g, context) {
+ if (context.isZooming) {
+ context.dragEndX = null;
+ context.dragEndY = null;
+ }
+ },
+
+ // Disable zooming out if panning.
+ 'dblclick' : function(event, g, context) {
+ if (event.altKey || event.shiftKey) {
+ return;
+ }
+ g.doUnzoom_();
+ }
+};
+
// Default attribute values.
Dygraph.DEFAULT_ATTRS = {
highlightCircleSize: 3,
stepPlot: false,
avoidMinZero: false,
+
+ interactionModel: Dygraph.DEFAULT_INTERACTION_MODEL
};
// 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.
var zoomedY = false;
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;
}
-Dygraph.prototype.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);
- }
- },
-
- // 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);
- }
- },
-
- 'mouseup' : function(event, g, context) {
- if (context.isZooming) {
- Dygraph.endZoom(event, g, context);
- } else if (context.isPanning) {
- Dygraph.endPan(event, g, context);
- }
- },
-
- // Temporarily cancel the dragging event when the mouse leaves the graph
- 'mouseout' : function(event, g, context) {
- if (context.isZooming) {
- context.dragEndX = null;
- context.dragEndY = null;
- }
- },
-
- // Disable zooming out if panning.
- 'dblclick' : function(event, g, context) {
- if (event.altKey || event.shiftKey) {
- return;
- }
- g.doUnzoom_();
- }
-};
/**
* Set up all the mouse handlers needed to capture dragging behavior for zoom
py : 0,
initializeMouseDown : function(event, g, context) {
- // prevents mouse drags from selecting page text.
+ // prevents mouse drags from selecting page text.
if (event.preventDefault) {
event.preventDefault(); // Firefox, Chrome, etc.
} else {
}
};
- // Defines default behavior if there are no event handlers.
- var handlers = this.user_attrs_.interactionModel || this.defaultInteractionModel;
+ var interactionModel = this.attr_("interactionModel");
+
// Function that binds g and context to the handler.
var bindHandler = function(handler, g) {
};
};
- 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], this));
}
// Self is the graph.
/**
* 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
--- /dev/null
+
+<html>
+ <head>
+ <title>interaction model</title>
+ <!--[if IE]>
+ <script type="text/javascript" src="../excanvas.js"></script>
+ <![endif]-->
+ <script type="text/javascript" src="../strftime/strftime-min.js"></script>
+ <script type="text/javascript" src="../rgbcolor/rgbcolor.js"></script>
+ <script type="text/javascript" src="../dygraph-canvas.js"></script>
+ <script type="text/javascript" src="../dygraph.js"></script>
+ <script type="text/javascript" src="data.js"></script>
+ </head>
+ <body>
+ <table border='1'>
+ <tr><td>
+ <b>Default interaction model</b>
+ <div id="div_g" style="width:600px; height:300px;"></div>
+ </td><td>Zoom: click-drag<br/>Pan: shift-click-drag<br/>Restore zoom level: double-click<br/>
+ </td></tr>
+ <tr><td>
+ <b>No interaction model</b>
+ <div id="div_g2" style="width:600px; height:300px;"></div>
+ </td><td>Click and drag all you like, it won't do anything!</td></tr>
+ <tr><td>
+ <b>Custom interaction model</b>
+
+ <input type="button" value="Unzoom" onclick="unzoomGraph(g3)">
+ <div id="div_g3" style="width:600px; height:300px;"></div>
+ </td><td>
+ Zoom in: double-click, scroll wheel<br/>
+ Zoom out: ctrl-double-click, scroll wheel<br/>
+ Pan: click-drag<br/>
+ Restore zoom level: press button<br/>
+ </td></tr>
+ <tr><td>
+ <b>Fun model!</b>
+ <div id="div_g4" style="width:600px; height:300px;"></div>
+ </td><td>
+ Keep the mouse button pressed, and hover over all points
+ to mark them.
+ </td></tr>
+
+ </table>
+
+ <script type="text/javascript">
+ function downV3(event, g, context) {
+ context.initializeMouseDown(event, g, context);
+ Dygraph.startPan(event, g, context);
+ }
+
+ function moveV3(event, g, context) {
+ if (context.isPanning) {
+ Dygraph.movePan(event, g, context);
+ }
+ }
+
+ function upV3(event, g, context) {
+ if (context.isPanning) {
+ Dygraph.endPan(event, g, context);
+ }
+ }
+
+ function dblClickV3(event, g, context) {
+ if (event.ctrlKey) {
+ zoom(g, -(1/9));
+ } else {
+ zoom(g, +.1);
+ }
+ }
+
+ function scrollV3(event, g, context) {
+ var normal = event.detail ? event.detail * -1 : event.wheelDelta / 40;
+ // For me the normalized value shows 0.075 for one click. If I took
+ // that verbatim, it would be a 7.5%. I think I'm gonna take 1/10 of that.
+ // (double for left and right side)
+ var percentage = normal / 100;
+
+ zoom(g, percentage);
+ Dygraph.cancelEvent(event);
+ }
+
+ function zoom(g, percentage) {
+ // Adjusts [x, y] toward each other by percentage%
+ function adjustAxis(axis, percentage) {
+ var delta = axis[1] - axis[0];
+ var increment = delta * percentage;
+ return [ axis[0] + increment, axis[1] - increment ];
+ }
+
+ var yAxes = g.yAxisRanges();
+ var newYAxes = [];
+ for (var i = 0; i < yAxes.length; i++) {
+ newYAxes[i] = adjustAxis(yAxes[i], percentage);
+ }
+
+ g.updateOptions({
+ dateWindow: adjustAxis(g.xAxisRange(), percentage),
+ valueRange: newYAxes[0]
+ });
+ }
+
+ var v4Active = false;
+ var v4Canvas = null;
+
+ function downV4(event, g, context) {
+ context.initializeMouseDown(event, g, context);
+ v4Active = true;
+ moveV4(event, g, context); // in case the mouse went down on a data point.
+ }
+
+ var processed = [];
+
+ function moveV4(event, g, context) {
+ var RANGE = 7;
+
+ if (v4Active) {
+ var canvasx = Dygraph.pageX(event) - Dygraph.findPosX(g.graphDiv);
+ var canvasy = Dygraph.pageY(event) - Dygraph.findPosY(g.graphDiv);
+
+ var rows = g.numRows();
+ // Row layout:
+ // [date, [val1, stdev1], [val2, stdev2]]
+ for (var row = 0; row < rows; row++) {
+ var date = g.getValue(row, 0);
+ var x = g.toDomCoords(date, null)[0];
+ var diff = Math.abs(canvasx - x);
+ if (diff < RANGE) {
+ for (var col = 1; col < 3; col++) {
+ // TODO(konigsberg): these will throw exceptions as data is removed.
+ var vals = g.getValue(row, col);
+ if (vals == null) { continue; }
+ var val = vals[0];
+ var y = g.toDomCoords(null, val)[1];
+ var diff2 = Math.abs(canvasy - y);
+ if (diff2 < RANGE) {
+ var found = false;
+ for (var i in processed) {
+ var stored = processed[i];
+ if(stored[0] == row && stored[1] == col) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ processed.push([row, col]);
+ drawV4(x, y);
+ }
+ return;
+ }
+ }
+ // drawV4(false, canvasx, canvasy);
+ }
+ }
+ // drawV4(false, canvasx, canvasy);
+ }
+ }
+
+ function upV4(event, g, context) {
+ if (v4Active) {
+ v4Active = false;
+ }
+ }
+
+ function dblClickV4(event, g, context) {
+ unzoomGraph(g4);
+ }
+
+ function drawV4(x, y) {
+ var ctx = v4Canvas;
+
+ ctx.strokeStyle = "#000000";
+ ctx.fillStyle = "#FFFF00";
+ ctx.beginPath();
+ ctx.arc(x,y,5,0,Math.PI*2,true);
+ ctx.closePath();
+ ctx.stroke();
+ ctx.fill();
+ }
+
+ function captureCanvas(canvas, area, g) {
+ v4Canvas = canvas;
+ }
+
+ var g = new Dygraph(document.getElementById("div_g"),
+ NoisyData, { errorBars : true });
+ var g2 = new Dygraph(document.getElementById("div_g2"),
+ NoisyData, { errorBars : true, interactionModel : {} });
+ var g3 = new Dygraph(document.getElementById("div_g3"),
+ NoisyData, { errorBars : true, interactionModel : {
+ 'mousedown' : downV3,
+ 'mousemove' : moveV3,
+ 'mouseup' : upV3,
+ 'dblclick' : dblClickV3,
+ 'mousewheel' : scrollV3
+ }});
+ var g4 = new Dygraph(document.getElementById("div_g4"),
+ NoisyData, { errorBars : true, drawPoints : true, interactionModel : {
+ 'mousedown' : downV4,
+ 'mousemove' : moveV4,
+ 'mouseup' : upV4,
+ 'dblclick' : dblClickV4,
+ },
+ underlayCallback : captureCanvas
+ });
+
+ function unzoomGraph(g) {
+ g.updateOptions({
+ dateWindow: null,
+ valueRange: null
+ });
+ }
+ </script>
+
+ </body>
+</html>