+ this.roller_.size = "2";
+ this.roller_.value = this.rollPeriod_;
+ for (var name in textAttr) {
+ if (textAttr.hasOwnProperty(name)) {
+ this.roller_.style[name] = textAttr[name];
+ }
+ }
+
+ var dygraph = this;
+ this.roller_.onchange = function() { dygraph.adjustRoll(dygraph.roller_.value); };
+};
+
+// These functions are taken from MochiKit.Signal
+Dygraph.pageX = function(e) {
+ if (e.pageX) {
+ return (!e.pageX || e.pageX < 0) ? 0 : e.pageX;
+ } else {
+ var de = document;
+ var b = document.body;
+ return e.clientX +
+ (de.scrollLeft || b.scrollLeft) -
+ (de.clientLeft || 0);
+ }
+};
+
+Dygraph.pageY = function(e) {
+ if (e.pageY) {
+ return (!e.pageY || e.pageY < 0) ? 0 : e.pageY;
+ } else {
+ var de = document;
+ var b = document.body;
+ return e.clientY +
+ (de.scrollTop || b.scrollTop) -
+ (de.clientTop || 0);
+ }
+};
+
+Dygraph.prototype.dragGetX_ = function(e, context) {
+ return Dygraph.pageX(e) - context.px
+};
+
+Dygraph.prototype.dragGetY_ = function(e, context) {
+ 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) {
+ zoomedY = true;
+ break;
+ }
+ }
+ if (!g.dateWindow_ && !zoomedY) return;
+
+ context.isPanning = true;
+ var xRange = g.xAxisRange();
+ context.dateRange = xRange[1] - xRange[0];
+
+ // Record the range of each y-axis at the start of the drag.
+ // If any axis has a valueRange or valueWindow, then we want a 2D pan.
+ context.is2DPan = false;
+ for (var i = 0; i < g.axes_.length; i++) {
+ var axis = g.axes_[i];
+ var yRange = g.yAxisRange(i);
+ axis.dragValueRange = yRange[1] - yRange[0];
+ var r = g.toDataCoords(null, context.dragStartY, i);
+ axis.draggingValue = r[1];
+ if (axis.valueWindow || axis.valueRange) context.is2DPan = true;
+ }
+
+ // TODO(konigsberg): Switch from all this math to toDataCoords?
+ // Seems to work for the dragging value.
+ 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);
+
+ // TODO(danvk): update this comment
+ // Want to have it so that:
+ // 1. draggingDate appears at dragEndX, draggingValue appears at dragEndY.
+ // 2. daterange = (dateWindow_[1] - dateWindow_[0]) is unaltered.
+ // 3. draggingValue appears at dragEndY.
+ // 4. valueRange is unaltered.
+
+ var minDate = context.draggingDate - (context.dragEndX / g.width_) * context.dateRange;
+ var maxDate = minDate + context.dateRange;
+ g.dateWindow_ = [minDate, maxDate];
+
+ // y-axis scaling is automatic unless this is a full 2D pan.
+ if (context.is2DPan) {
+ // Adjust each axis appropriately.
+ var y_frac = context.dragEndY / g.height_;
+ for (var i = 0; i < g.axes_.length; i++) {
+ var axis = g.axes_[i];
+ var maxValue = axis.draggingValue + y_frac * axis.dragValueRange;
+ var minValue = maxValue - axis.dragValueRange;
+ axis.valueWindow = [ minValue, maxValue ];
+ }
+ }
+
+ g.drawGraph_();