/**
* @license
- * Copyright 2013 Paul Felix (paul.eric.felix@gmail.com)
+ * Copyright 2011 Paul Felix (paul.eric.felix@gmail.com)
* MIT-licensed (http://opensource.org/licenses/MIT)
*/
+/*global Dygraph:false,TouchEvent:false */
/**
* @fileoverview This file contains the RangeSelector plugin used to provide
/*global Dygraph:false */
"use strict";
-var NOT_CREATED = 0;
-var CREATED = 1<<0;
-var ADDED_TO_GRAPH = 1<<1;
-
var rangeSelector = function() {
this.isIE_ = /MSIE/.test(navigator.userAgent) && !window.opera;
this.hasTouchInterface_ = typeof(TouchEvent) != 'undefined';
this.isMobileDevice_ = /mobile|android/gi.test(navigator.appVersion);
- this.status_ = NOT_CREATED;
+ this.interfaceCreated_ = false;
};
rangeSelector.prototype.toString = function() {
rangeSelector.prototype.activate = function(dygraph) {
this.dygraph_ = dygraph;
- this.layout_ = this.dygraph_.layout_;
this.isUsingExcanvas_ = dygraph.isUsingExcanvas_;
- if (this.attr_('showRangeSelector')) {
+ if (this.getOption_('showRangeSelector')) {
this.createInterface_();
}
return {
// Private methods
//------------------------------------------------------------------
-rangeSelector.prototype.attr_ = function(name) {
- return this.dygraph_.attr_(name);
+rangeSelector.prototype.getOption_ = function(name) {
+ return this.dygraph_.getOption(name);
+};
+
+rangeSelector.prototype.setDefaultOption_ = function(name, value) {
+ return this.dygraph_.attrs_[name] = value;
};
/**
this.initInteraction_();
// Range selector and animatedZooms have a bad interaction. See issue 359.
- if (this.attr_('animatedZooms')) {
- this.dygraph_.warn('You should not set animatedZooms=true when using the range selector.');
- this.dygraph_.attrs_.animatedZooms = false;
+ if (this.getOption_('animatedZooms')) {
+ this.dygraph_.warn('Animated zooms and range selector are not compatible; disabling animatedZooms.');
+ this.dygraph_.updateOptions({animatedZooms: false}, true);
}
+ this.interfaceCreated_ = true;
this.addToGraph_();
- this.status_ = CREATED;
};
/**
* Adds the range selector to the graph.
*/
rangeSelector.prototype.addToGraph_ = function() {
- var graphDiv = this.dygraph_.graphDiv;
+ var graphDiv = this.graphDiv_ = this.dygraph_.graphDiv;
graphDiv.appendChild(this.bgcanvas_);
graphDiv.appendChild(this.fgcanvas_);
graphDiv.appendChild(this.leftZoomHandle_);
graphDiv.appendChild(this.rightZoomHandle_);
- this.status_ |= ADDED_TO_GRAPH;
};
/**
* Removes the range selector from the graph.
*/
rangeSelector.prototype.removeFromGraph_ = function() {
- var graphDiv = this.dygraph_.graphDiv;
+ var graphDiv = this.graphDiv_;
graphDiv.removeChild(this.bgcanvas_);
graphDiv.removeChild(this.fgcanvas_);
graphDiv.removeChild(this.leftZoomHandle_);
graphDiv.removeChild(this.rightZoomHandle_);
- this.status_ ^= ADDED_TO_GRAPH;
+ this.graphDiv_ = null;
};
/**
* Called by Layout to allow range selector to reserve its space.
*/
rangeSelector.prototype.reserveSpace_ = function(e) {
- if (this.attr_('showRangeSelector')) {
- e.reserveSpaceBottom(this.attr_('rangeSelectorHeight') + 4);
+ if (this.getOption_('showRangeSelector')) {
+ e.reserveSpaceBottom(this.getOption_('rangeSelectorHeight') + 4);
}
};
* Renders the static portion of the range selector at the predraw stage.
*/
rangeSelector.prototype.renderStaticLayer_ = function() {
- if (!this.isEnabled_()) {
+ if (!this.updateVisibility_()) {
return;
}
this.resize_();
* Renders the interactive portion of the range selector after the chart has been drawn.
*/
rangeSelector.prototype.renderInteractiveLayer_ = function() {
- if (!this.isEnabled_() || this.isChangingRange_) {
+ if (!this.updateVisibility_() || this.isChangingRange_) {
return;
}
this.placeZoomHandles_();
/**
* @private
- * Check to see if the range selector is enabled and needs to be created or added to graph.
+ * Check to see if the range selector is enabled/disabled and update visibility accordingly.
*/
-rangeSelector.prototype.isEnabled_ = function() {
- var enabled = this.attr_('showRangeSelector');
+rangeSelector.prototype.updateVisibility_ = function() {
+ var enabled = this.getOption_('showRangeSelector');
if (enabled) {
- if (!(this.status_ & CREATED)) {
+ if (!this.interfaceCreated_) {
this.createInterface_();
- } else if (!(this.status_ & ADDED_TO_GRAPH)) {
+ } else if (!this.graphDiv_ || !this.graphDiv_.parentNode) {
this.addToGraph_();
}
- } else if (this.status_ & ADDED_TO_GRAPH) {
+ } else if (this.graphDiv_) {
this.removeFromGraph_();
var dygraph = this.dygraph_;
setTimeout(function() { dygraph.width_ = 0; dygraph.resize(); }, 1);
}
return enabled;
-}
+};
/**
* @private
canvas.style.height = canvas.height + 'px'; // for IE
}
- var plotArea = this.layout_.getPlotArea();
- var xAxisLabelHeight = this.attr_('xAxisHeight') || (this.attr_('axisLabelFontSize') + 2 * this.attr_('axisTickSize'));
+ var plotArea = this.dygraph_.layout_.getPlotArea();
+
+ var xAxisLabelHeight = 0;
+ if(this.getOption_('drawXAxis')){
+ xAxisLabelHeight = this.getOption_('xAxisHeight') || (this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize'));
+ }
this.canvasRect_ = {
x: plotArea.x,
y: plotArea.y + plotArea.h + xAxisLabelHeight + 4,
w: plotArea.w,
- h: this.attr_('rangeSelectorHeight')
+ h: this.getOption_('rangeSelectorHeight')
};
setElementRect(this.bgcanvas_, this.canvasRect_);
rangeSelector.prototype.initInteraction_ = function() {
var self = this;
var topElem = this.isIE_ ? document : window;
- var xLast = 0;
+ var clientXLast = 0;
var handle = null;
var isZooming = false;
var isPanning = false;
// functions, defined below. Defining them this way (rather than with
// "function foo() {...}" makes JSHint happy.
var toXDataWindow, onZoomStart, onZoom, onZoomEnd, doZoom, isMouseInPanZone,
- onPanStart, onPan, onPanEnd, doPan, onCanvasMouseMove, applyBrowserZoomLevel;
+ onPanStart, onPan, onPanEnd, doPan, onCanvasHover;
// Touch event functions
var onZoomHandleTouchEvent, onCanvasTouchEvent, addTouchEvents;
return [xDataMin, xDataMax];
};
- applyBrowserZoomLevel = function(delX) {
- var zoom = window.outerWidth/document.documentElement.clientWidth;
- if (!isNaN(zoom)) {
- return delX/zoom;
- } else {
- return delX;
- }
- };
-
onZoomStart = function(e) {
Dygraph.cancelEvent(e);
isZooming = true;
- xLast = e.screenX;
+ clientXLast = e.clientX;
handle = e.target ? e.target : e.srcElement;
- self.dygraph_.addEvent(topElem, 'mousemove', onZoom);
- self.dygraph_.addEvent(topElem, 'mouseup', onZoomEnd);
+ if (e.type === 'mousedown' || e.type === 'dragstart') {
+ // These events are removed manually.
+ Dygraph.addEvent(topElem, 'mousemove', onZoom);
+ Dygraph.addEvent(topElem, 'mouseup', onZoomEnd);
+ }
self.fgcanvas_.style.cursor = 'col-resize';
tarp.cover();
return true;
return false;
}
Dygraph.cancelEvent(e);
- var delX = e.screenX - xLast;
- if (Math.abs(delX) < 4 || e.screenX === 0) {
- // First iPad move event seems to have screenX = 0
+
+ var delX = e.clientX - clientXLast;
+ if (Math.abs(delX) < 4) {
return true;
}
- xLast = e.screenX;
- delX = applyBrowserZoomLevel(delX);
+ clientXLast = e.clientX;
// Move handle.
var zoomHandleStatus = self.getZoomHandleStatus_();
if (!isPanning && isMouseInPanZone(e) && self.getZoomHandleStatus_().isZoomed) {
Dygraph.cancelEvent(e);
isPanning = true;
- xLast = e.screenX;
- self.dygraph_.addEvent(topElem, 'mousemove', onPan);
- self.dygraph_.addEvent(topElem, 'mouseup', onPanEnd);
+ clientXLast = e.clientX;
+ if (e.type === 'mousedown') {
+ // These events are removed manually.
+ Dygraph.addEvent(topElem, 'mousemove', onPan);
+ Dygraph.addEvent(topElem, 'mouseup', onPanEnd);
+ }
return true;
}
return false;
}
Dygraph.cancelEvent(e);
- var delX = e.screenX - xLast;
+ var delX = e.clientX - clientXLast;
if (Math.abs(delX) < 4) {
return true;
}
- xLast = e.screenX;
- delX = applyBrowserZoomLevel(delX);
+ clientXLast = e.clientX;
// Move range view
var zoomHandleStatus = self.getZoomHandleStatus_();
}
};
- onCanvasMouseMove = function(e) {
+ onCanvasHover = function(e) {
if (isZooming || isPanning) {
return;
}
}
};
- this.dygraph_.attrs_.interactionModel =
- Dygraph.Interaction.dragIsPanInteractionModel;
- this.dygraph_.attrs_.panEdgeFraction = 0.0001;
+ this.setDefaultOption_('interactionModel', Dygraph.Interaction.dragIsPanInteractionModel);
+ this.setDefaultOption_('panEdgeFraction', 0.0001);
var dragStartEvent = window.opera ? 'mousedown' : 'dragstart';
this.dygraph_.addEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart);
this.dygraph_.addEvent(this.iePanOverlay_, 'mousedown', onPanStart);
} else {
this.dygraph_.addEvent(this.fgcanvas_, 'mousedown', onPanStart);
- this.dygraph_.addEvent(this.fgcanvas_, 'mousemove', onCanvasMouseMove);
+ this.dygraph_.addEvent(this.fgcanvas_, 'mousemove', onCanvasHover);
}
// Touch events
* Draws the mini plot in the background canvas.
*/
rangeSelector.prototype.drawMiniPlot_ = function() {
- var fillStyle = this.attr_('rangeSelectorPlotFillColor');
- var strokeStyle = this.attr_('rangeSelectorPlotStrokeColor');
+ var fillStyle = this.getOption_('rangeSelectorPlotFillColor');
+ var strokeStyle = this.getOption_('rangeSelectorPlotStrokeColor');
if (!fillStyle && !strokeStyle) {
return;
}
- var stepPlot = this.attr_('stepPlot');
+ var stepPlot = this.getOption_('stepPlot');
var combinedSeriesData = this.computeCombinedSeriesAndLimits_();
var yRange = combinedSeriesData.yMax - combinedSeriesData.yMin;
*/
rangeSelector.prototype.computeCombinedSeriesAndLimits_ = function() {
var data = this.dygraph_.rawData_;
- var logscale = this.attr_('logscale');
+ var logscale = this.getOption_('logscale');
// Create a combined series (average of all series values).
var combinedSeries = [];
*/
rangeSelector.prototype.getZoomHandleStatus_ = function() {
var halfHandleWidth = this.leftZoomHandle_.width/2;
- var leftHandlePos = parseInt(this.leftZoomHandle_.style.left, 10) + halfHandleWidth;
- var rightHandlePos = parseInt(this.rightZoomHandle_.style.left, 10) + halfHandleWidth;
+ var leftHandlePos = parseFloat(this.leftZoomHandle_.style.left) + halfHandleWidth;
+ var rightHandlePos = parseFloat(this.rightZoomHandle_.style.left) + halfHandleWidth;
return {
leftHandlePos: leftHandlePos,
rightHandlePos: rightHandlePos,