webui-aria2/js/libs/dojox/charting/action2d/TouchZoomAndPan.js.uncompressed.js
2012-05-01 19:52:07 +08:00

251 lines
9.3 KiB
JavaScript

//>>built
define("dojox/charting/action2d/TouchZoomAndPan", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/event", "dojo/_base/sniff",
"./ChartAction", "../Element", "dojox/gesture/tap", "../plot2d/common"],
function(lang, declare, eventUtil, has, ChartAction, Element, tap, common){
var GlassView = declare("dojox.charting.action2d._GlassView", [Element], {
// summary: Private internal class used by TouchZoomAndPan actions.
// tags:
// private
constructor: function(chart){
},
render: function(){
if(!this.isDirty()){
return;
}
this.cleanGroup();
this.group.createRect({width: this.chart.dim.width, height: this.chart.dim.height}).setFill("rgba(0,0,0,0)");
},
cleanGroup: function(creator){
// summary:
// Clean any elements (HTML or GFX-based) out of our group, and create a new one.
// creator: dojox.gfx.Surface?
// An optional surface to work with.
// returns: dojox.charting.Element
// A reference to this object for functional chaining.
this.inherited(arguments);
return this; // dojox.charting.Element
},
clear: function(){
// summary:
// Clear out any parameters set on this plot.
// returns: dojox.charting.action2d._IndicatorElement
// The reference to this plot for functional chaining.
this.dirty = true;
// glass view needs to be above
if(this.chart.stack[0] != this){
this.chart.movePlotToFront(this.name);
}
return this; // dojox.charting.plot2d._IndicatorElement
},
getSeriesStats: function(){
// summary:
// Returns default stats (irrelevant for this type of plot).
// returns: Object
// {hmin, hmax, vmin, vmax} min/max in both directions.
return lang.delegate(common.defaultStats);
},
initializeScalers: function(){
// summary:
// Does nothing (irrelevant for this type of plot).
return this;
},
isDirty: function(){
// summary:
// Return whether or not this plot needs to be redrawn.
// returns: Boolean
// If this plot needs to be rendered, this will return true.
return this.dirty;
}
});
/*=====
declare("dojox.charting.action2d.__TouchZoomAndPanCtorArgs", null, {
// summary:
// Additional arguments for mouse zoom and pan actions.
// axis: String?
// Target axis name for this action. Default is "x".
axis: "x",
// scaleFactor: Number?
// The scale factor applied on double tap. Default is 1.2.
scaleFactor: 1.2,
// maxScale: Number?
// The max scale factor accepted by this action. Default is 100.
maxScale: 100,
// enableScroll: Boolean?
// Whether touch drag gesture should scroll the chart. Default is true.
enableScroll: true,
// enableZoom: Boolean?
// Whether touch pinch and spread gesture should zoom out or in the chart. Default is true.
enableZoom: true,
});
var ChartAction = dojox.charting.action2d.ChartAction;
=====*/
return declare("dojox.charting.action2d.TouchZoomAndPan", ChartAction, {
// summary:
// Create a touch zoom and pan action.
// You can zoom out or in the data window with pinch and spread gestures. You can scroll using drag gesture.
// Finally this is possible to navigate between a fit window and a zoom one using double tap gesture.
// the data description block for the widget parser
defaultParams: {
axis: "x",
scaleFactor: 1.2,
maxScale: 100,
enableScroll: true,
enableZoom: true
},
optionalParams: {}, // no optional parameters
constructor: function(chart, plot, kwArgs){
// summary:
// Create a new touch zoom and pan action and connect it.
// chart: dojox.charting.Chart
// The chart this action applies to.
// kwArgs: dojox.charting.action2d.__TouchZoomAndPanCtorArgs?
// Optional arguments for the action.
this._listeners = [{eventName: "ontouchstart", methodName: "onTouchStart"},
{eventName: "ontouchmove", methodName: "onTouchMove"},
{eventName: "ontouchend", methodName: "onTouchEnd"},
{eventName: tap.doubletap, methodName: "onDoubleTap"}];
if(!kwArgs){ kwArgs = {}; }
this.axis = kwArgs.axis ? kwArgs.axis : "x";
this.scaleFactor = kwArgs.scaleFactor ? kwArgs.scaleFactor : 1.2;
this.maxScale = kwArgs.maxScale ? kwArgs.maxScale : 100;
this.enableScroll = kwArgs.enableScroll != undefined ? kwArgs.enableScroll : true;
this.enableZoom = kwArgs.enableScroll != undefined ? kwArgs.enableZoom : true;
this._uName = "touchZoomPan"+this.axis;
this.connect();
},
connect: function(){
// summary:
// Connect this action to the chart. On Safari this adds a new glass view plot
// to the chart that's why Chart.render() must be called after connect.
this.inherited(arguments);
// this is needed to workaround issue on Safari + SVG, because a touch start action
// started above a item that is removed during the touch action will stop
// dispatching touch events!
if(has("safari") && this.chart.surface.declaredClass.indexOf("svg")!=-1){
this.chart.addPlot(this._uName, {type: GlassView});
}
},
disconnect: function(){
// summary:
// Disconnect this action from the chart.
if(has("safari") && this.chart.surface.declaredClass.indexOf("svg")!=-1){
this.chart.removePlot(this._uName);
}
this.inherited(arguments);
},
onTouchStart: function(event){
// summary:
// Called when touch is started on the chart.
// we always want to be above regular plots and not clipped
var chart = this.chart, axis = chart.getAxis(this.axis);
var length = event.touches.length;
this._startPageCoord = {x: event.touches[0].pageX, y: event.touches[0].pageY};
if((this.enableZoom || this.enableScroll) && chart._delayedRenderHandle){
// we have pending rendering from a scroll, let's sync
clearTimeout(chart._delayedRenderHandle);
chart._delayedRenderHandle = null;
chart.render();
}
if(this.enableZoom && length >= 2){
this._endPageCoord = {x: event.touches[1].pageX, y: event.touches[1].pageY};
var middlePageCoord = {x: (this._startPageCoord.x + this._endPageCoord.x) / 2,
y: (this._startPageCoord.y + this._endPageCoord.y) / 2};
var scaler = axis.getScaler();
this._initScale = axis.getWindowScale();
var t = this._initData = this.plot.toData();
this._middleCoord = t(middlePageCoord)[this.axis];
this._startCoord = scaler.bounds.from;
this._endCoord = scaler.bounds.to;
}else if(this.enableScroll){
this._startScroll(axis);
// needed for Android, otherwise will get a touch cancel while swiping
eventUtil.stop(event);
}
},
onTouchMove: function(event){
// summary:
// Called when touch is moved on the chart.
var chart = this.chart, axis = chart.getAxis(this.axis);
var length = event.touches.length;
var pAttr = axis.vertical?"pageY":"pageX",
attr = axis.vertical?"y":"x";
if(this.enableZoom && length >= 2){
var newMiddlePageCoord = {x: (event.touches[1].pageX + event.touches[0].pageX) / 2,
y: (event.touches[1].pageY + event.touches[0].pageY) / 2};
var scale = (this._endPageCoord[attr] - this._startPageCoord[attr]) /
(event.touches[1][pAttr] - event.touches[0][pAttr]);
if(this._initScale / scale > this.maxScale){
return;
}
var newMiddleCoord = this._initData(newMiddlePageCoord)[this.axis];
var newStart = scale * (this._startCoord - newMiddleCoord) + this._middleCoord,
newEnd = scale * (this._endCoord - newMiddleCoord) + this._middleCoord;
chart.zoomIn(this.axis, [newStart, newEnd]);
// avoid browser pan
eventUtil.stop(event);
}else if(this.enableScroll){
var delta = axis.vertical?(this._startPageCoord[attr] - event.touches[0][pAttr]):
(event.touches[0][pAttr] - this._startPageCoord[attr]);
chart.setAxisWindow(this.axis, this._lastScale, this._initOffset - delta / this._lastFactor / this._lastScale);
chart.delayedRender();
// avoid browser pan
eventUtil.stop(event);
}
},
onTouchEnd: function(event){
// summary:
// Called when touch is ended on the chart.
var chart = this.chart, axis = chart.getAxis(this.axis);
if(event.touches.length == 1 && this.enableScroll){
// still one touch available, let's start back from here for
// potential pan
this._startPageCoord = {x: event.touches[0].pageX, y: event.touches[0].pageY};
this._startScroll(axis);
}
},
_startScroll: function(axis){
var bounds = axis.getScaler().bounds;
this._initOffset = axis.getWindowOffset();
// we keep it because of delay rendering we might now always have access to the
// information to compute it
this._lastScale = axis.getWindowScale();
this._lastFactor = bounds.span / (bounds.upper - bounds.lower);
},
onDoubleTap: function(event){
// summary:
// Called when double tap is performed on the chart.
var chart = this.chart, axis = chart.getAxis(this.axis);
var scale = 1 / this.scaleFactor;
// are we fit?
if(axis.getWindowScale()==1){
// fit => zoom
var scaler = axis.getScaler(), start = scaler.bounds.from, end = scaler.bounds.to,
oldMiddle = (start + end) / 2, newMiddle = this.plot.toData(this._startPageCoord)[this.axis],
newStart = scale * (start - oldMiddle) + newMiddle, newEnd = scale * (end - oldMiddle) + newMiddle;
chart.zoomIn(this.axis, [newStart, newEnd]);
}else{
// non fit => fit
chart.setAxisWindow(this.axis, 1, 0);
chart.render();
}
eventUtil.stop(event);
}
});
});