summaryrefslogtreecommitdiff
path: root/js/dojo/dojox/charting/action2d
diff options
context:
space:
mode:
Diffstat (limited to 'js/dojo/dojox/charting/action2d')
-rw-r--r--js/dojo/dojox/charting/action2d/Base.js37
-rw-r--r--js/dojo/dojox/charting/action2d/ChartAction.js39
-rw-r--r--js/dojo/dojox/charting/action2d/Highlight.js127
-rw-r--r--js/dojo/dojox/charting/action2d/Magnify.js117
-rw-r--r--js/dojo/dojox/charting/action2d/MouseIndicator.js222
-rw-r--r--js/dojo/dojox/charting/action2d/MouseZoomAndPan.js246
-rw-r--r--js/dojo/dojox/charting/action2d/MoveSlice.js122
-rw-r--r--js/dojo/dojox/charting/action2d/PlotAction.js79
-rw-r--r--js/dojo/dojox/charting/action2d/Shake.js109
-rw-r--r--js/dojo/dojox/charting/action2d/Tooltip.js167
-rw-r--r--js/dojo/dojox/charting/action2d/TouchIndicator.js233
-rw-r--r--js/dojo/dojox/charting/action2d/TouchZoomAndPan.js250
-rw-r--r--js/dojo/dojox/charting/action2d/_IndicatorElement.js371
13 files changed, 2119 insertions, 0 deletions
diff --git a/js/dojo/dojox/charting/action2d/Base.js b/js/dojo/dojox/charting/action2d/Base.js
new file mode 100644
index 0000000..9f57e25
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/Base.js
@@ -0,0 +1,37 @@
+//>>built
+define("dojox/charting/action2d/Base", ["dojo/_base/lang", "dojo/_base/declare"],
+ function(lang, declare){
+
+ return declare("dojox.charting.action2d.Base", null, {
+ // summary:
+ // Base action class for plot and chart actions.
+
+ constructor: function(chart, plot){
+ // summary:
+ // Create a new base action. This can either be a plot or a chart action.
+ // chart: dojox.charting.Chart
+ // The chart this action applies to.
+ // plot: String?|dojox.charting.plot2d.Base?
+ // Optional target plot for this action. Default is "default".
+ this.chart = chart;
+ this.plot = plot ? (lang.isString(plot) ? this.chart.getPlot(plot) : plot) : this.chart.getPlot("default");
+ },
+
+ connect: function(){
+ // summary:
+ // Connect this action to the plot or the chart.
+ },
+
+ disconnect: function(){
+ // summary:
+ // Disconnect this action from the plot or the chart.
+ },
+
+ destroy: function(){
+ // summary:
+ // Do any cleanup needed when destroying parent elements.
+ this.disconnect();
+ }
+ });
+
+});
diff --git a/js/dojo/dojox/charting/action2d/ChartAction.js b/js/dojo/dojox/charting/action2d/ChartAction.js
new file mode 100644
index 0000000..a46d86f
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/ChartAction.js
@@ -0,0 +1,39 @@
+//>>built
+define("dojox/charting/action2d/ChartAction", ["dojo/_base/connect", "dojo/_base/declare", "./Base"],
+ function(hub, declare, Base){
+ /*=====
+ var Base = dojox.charting.action2d.Base;
+ =====*/
+ return declare("dojox.charting.action2d.ChartAction", Base, {
+ // summary:
+ // Base action class for chart actions.
+
+ constructor: function(chart, plot){
+ // summary:
+ // Create a new base chart action.
+ // chart: dojox.charting.Chart
+ // The chart this action applies to.
+ // plot: String?|dojox.charting.plot2d.Base?
+ // Optional target plot for this chart action. Default is "default".
+ },
+
+ connect: function(){
+ // summary:
+ // Connect this action to the chart.
+ for(var i = 0; i < this._listeners.length; ++i){
+ this._listeners[i].handle = hub.connect(this.chart.node, this._listeners[i].eventName,
+ this, this._listeners[i].methodName);
+ }
+ },
+
+ disconnect: function(){
+ // summary:
+ // Disconnect this action from the chart.
+ for(var i = 0; i < this._listeners.length; ++i){
+ hub.disconnect(this._listeners[i].handle);
+ delete this._listeners[i].handle;
+ }
+ }
+});
+
+});
diff --git a/js/dojo/dojox/charting/action2d/Highlight.js b/js/dojo/dojox/charting/action2d/Highlight.js
new file mode 100644
index 0000000..8656a7b
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/Highlight.js
@@ -0,0 +1,127 @@
+//>>built
+define("dojox/charting/action2d/Highlight", ["dojo/_base/kernel", "dojo/_base/lang", "dojo/_base/declare", "dojo/_base/Color", "dojo/_base/connect", "dojox/color/_base",
+ "./PlotAction", "dojo/fx/easing", "dojox/gfx/fx"],
+ function(dojo, lang, declare, Color, hub, c, PlotAction, dfe, dgf){
+
+ /*=====
+ dojo.declare("dojox.charting.action2d.__HighlightCtorArgs", dojox.charting.action2d.__PlotActionCtorArgs, {
+ // summary:
+ // Additional arguments for highlighting actions.
+
+ // highlight: String|dojo.Color|Function?
+ // Either a color or a function that creates a color when highlighting happens.
+ highlight: null
+ });
+ var PlotAction = dojox.charting.action2d.PlotAction;
+ =====*/
+
+ var DEFAULT_SATURATION = 100, // %
+ DEFAULT_LUMINOSITY1 = 75, // %
+ DEFAULT_LUMINOSITY2 = 50, // %
+ cc = function(color){
+ return function(){ return color; };
+ },
+
+ hl = function(color){
+ var a = new c.Color(color),
+ x = a.toHsl();
+ if(x.s == 0){
+ x.l = x.l < 50 ? 100 : 0;
+ }else{
+ x.s = DEFAULT_SATURATION;
+ if(x.l < DEFAULT_LUMINOSITY2){
+ x.l = DEFAULT_LUMINOSITY1;
+ }else if(x.l > DEFAULT_LUMINOSITY1){
+ x.l = DEFAULT_LUMINOSITY2;
+ }else{
+ x.l = x.l - DEFAULT_LUMINOSITY2 > DEFAULT_LUMINOSITY1 - x.l ?
+ DEFAULT_LUMINOSITY2 : DEFAULT_LUMINOSITY1;
+ }
+ }
+ return c.fromHsl(x);
+ };
+
+ return declare("dojox.charting.action2d.Highlight", PlotAction, {
+ // summary:
+ // Creates a highlighting action on a plot, where an element on that plot
+ // has a highlight on it.
+
+ // the data description block for the widget parser
+ defaultParams: {
+ duration: 400, // duration of the action in ms
+ easing: dfe.backOut // easing for the action
+ },
+ optionalParams: {
+ highlight: "red" // name for the highlight color
+ // programmatic instantiation can use functions and color objects
+ },
+
+ constructor: function(chart, plot, kwArgs){
+ // summary:
+ // Create the highlighting action and connect it to the plot.
+ // chart: dojox.charting.Chart
+ // The chart this action belongs to.
+ // plot: String?
+ // The plot this action is attached to. If not passed, "default" is assumed.
+ // kwArgs: charting.action2d.__HighlightCtorArgs?
+ // Optional keyword arguments object for setting parameters.
+ var a = kwArgs && kwArgs.highlight;
+ this.colorFun = a ? (lang.isFunction(a) ? a : cc(a)) : hl;
+
+ this.connect();
+ },
+
+ process: function(o){
+ // summary:
+ // Process the action on the given object.
+ // o: dojox.gfx.Shape
+ // The object on which to process the highlighting action.
+ if(!o.shape || !(o.type in this.overOutEvents)){ return; }
+
+ var runName = o.run.name, index = o.index, anim, startFill, endFill;
+
+ if(runName in this.anim){
+ anim = this.anim[runName][index];
+ }else{
+ this.anim[runName] = {};
+ }
+
+ if(anim){
+ anim.action.stop(true);
+ }else{
+ var color = o.shape.getFill();
+ if(!color || !(color instanceof Color)){
+ return;
+ }
+ this.anim[runName][index] = anim = {
+ start: color,
+ end: this.colorFun(color)
+ };
+ }
+
+ var start = anim.start, end = anim.end;
+ if(o.type == "onmouseout"){
+ // swap colors
+ var t = start;
+ start = end;
+ end = t;
+ }
+
+ anim.action = dgf.animateFill({
+ shape: o.shape,
+ duration: this.duration,
+ easing: this.easing,
+ color: {start: start, end: end}
+ });
+ if(o.type == "onmouseout"){
+ hub.connect(anim.action, "onEnd", this, function(){
+ if(this.anim[runName]){
+ delete this.anim[runName][index];
+ }
+ });
+ }
+ anim.action.play();
+ }
+ });
+
+});
diff --git a/js/dojo/dojox/charting/action2d/Magnify.js b/js/dojo/dojox/charting/action2d/Magnify.js
new file mode 100644
index 0000000..c59c143
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/Magnify.js
@@ -0,0 +1,117 @@
+//>>built
+define("dojox/charting/action2d/Magnify", ["dojo/_base/connect", "dojo/_base/declare",
+ "./PlotAction", "dojox/gfx/matrix",
+ "dojox/gfx/fx", "dojo/fx", "dojo/fx/easing"],
+ function(Hub, declare, PlotAction, m, gf, df, dfe){
+
+ /*=====
+ dojo.declare("dojox.charting.action2d.__MagnifyCtorArgs", dojox.charting.action2d.__PlotActionCtorArgs, {
+ // summary:
+ // Additional arguments for highlighting actions.
+
+ // scale: Number?
+ // The amount to magnify the given object to. Default is 2.
+ scale: 2
+ });
+ var PlotAction = dojox.charting.action2d.PlotAction;
+ =====*/
+
+ var DEFAULT_SCALE = 2;
+
+ return declare("dojox.charting.action2d.Magnify", PlotAction, {
+ // summary:
+ // Create an action that magnifies the object the action is applied to.
+
+ // the data description block for the widget parser
+ defaultParams: {
+ duration: 400, // duration of the action in ms
+ easing: dfe.backOut, // easing for the action
+ scale: DEFAULT_SCALE // scale of magnification
+ },
+ optionalParams: {}, // no optional parameters
+
+ constructor: function(chart, plot, kwArgs){
+ // summary:
+ // Create the magnifying action.
+ // chart: dojox.charting.Chart
+ // The chart this action belongs to.
+ // plot: String?
+ // The plot to apply the action to. If not passed, "default" is assumed.
+ // kwArgs: dojox.charting.action2d.__MagnifyCtorArgs?
+ // Optional keyword arguments for this action.
+
+ // process optional named parameters
+ this.scale = kwArgs && typeof kwArgs.scale == "number" ? kwArgs.scale : DEFAULT_SCALE;
+
+ this.connect();
+ },
+
+ process: function(o){
+ // summary:
+ // Process the action on the given object.
+ // o: dojox.gfx.Shape
+ // The object on which to process the magnifying action.
+ if(!o.shape || !(o.type in this.overOutEvents) ||
+ !("cx" in o) || !("cy" in o)){ return; }
+
+ var runName = o.run.name, index = o.index, vector = [], anim, init, scale;
+
+ if(runName in this.anim){
+ anim = this.anim[runName][index];
+ }else{
+ this.anim[runName] = {};
+ }
+
+ if(anim){
+ anim.action.stop(true);
+ }else{
+ this.anim[runName][index] = anim = {};
+ }
+
+ if(o.type == "onmouseover"){
+ init = m.identity;
+ scale = this.scale;
+ }else{
+ init = m.scaleAt(this.scale, o.cx, o.cy);
+ scale = 1 / this.scale;
+ }
+
+ var kwArgs = {
+ shape: o.shape,
+ duration: this.duration,
+ easing: this.easing,
+ transform: [
+ {name: "scaleAt", start: [1, o.cx, o.cy], end: [scale, o.cx, o.cy]},
+ init
+ ]
+ };
+ if(o.shape){
+ vector.push(gf.animateTransform(kwArgs));
+ }
+ if(o.oultine){
+ kwArgs.shape = o.outline;
+ vector.push(gf.animateTransform(kwArgs));
+ }
+ if(o.shadow){
+ kwArgs.shape = o.shadow;
+ vector.push(gf.animateTransform(kwArgs));
+ }
+
+ if(!vector.length){
+ delete this.anim[runName][index];
+ return;
+ }
+
+ anim.action = df.combine(vector);
+ if(o.type == "onmouseout"){
+ Hub.connect(anim.action, "onEnd", this, function(){
+ if(this.anim[runName]){
+ delete this.anim[runName][index];
+ }
+ });
+ }
+ anim.action.play();
+ }
+ });
+
+});
diff --git a/js/dojo/dojox/charting/action2d/MouseIndicator.js b/js/dojo/dojox/charting/action2d/MouseIndicator.js
new file mode 100644
index 0000000..b6cd8fe
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/MouseIndicator.js
@@ -0,0 +1,222 @@
+//>>built
+define("dojox/charting/action2d/MouseIndicator", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/connect", "dojo/_base/window", "dojo/_base/sniff",
+ "./ChartAction", "./_IndicatorElement", "dojox/lang/utils", "dojo/_base/event","dojo/_base/array"],
+ function(lang, declare, hub, win, has, ChartAction, IndicatorElement, du, eventUtil, arr){
+
+ /*=====
+ dojo.declare("dojox.charting.action2d.__MouseIndicatorCtorArgs", null, {
+ // summary:
+ // Additional arguments for mouse indicator.
+
+ // series: String
+ // Target series name for this action.
+ series: "",
+
+ // autoScroll: Boolean?
+ // Whether when moving indicator the chart is automatically scrolled. Default is true.
+ autoScroll: true,
+
+ // vertical: Boolean?
+ // Whether the indicator is vertical or not. Default is true.
+ vertical: true,
+
+ // fixed: Boolean?
+ // Whether a fixed precision must be applied to data values for display. Default is true.
+ fixed: true,
+
+ // precision: Number?
+ // The precision at which to round data values for display. Default is 1.
+ precision: 0,
+
+ // lineStroke: dojo.gfx.Stroke?
+ // An optional stroke to use for indicator line.
+ lineStroke: {},
+
+ // lineOutline: dojo.gfx.Stroke?
+ // An optional outline to use for indicator line.
+ lineOutline: {},
+
+ // lineShadow: dojo.gfx.Stroke?
+ // An optional shadow to use for indicator line.
+ lineShadow: {},
+
+ // stroke: dojo.gfx.Stroke?
+ // An optional stroke to use for indicator label background.
+ stroke: {},
+
+ // outline: dojo.gfx.Stroke?
+ // An optional outline to use for indicator label background.
+ outline: {},
+
+ // shadow: dojo.gfx.Stroke?
+ // An optional shadow to use for indicator label background.
+ shadow: {},
+
+ // fill: dojo.gfx.Fill?
+ // An optional fill to use for indicator label background.
+ fill: {},
+
+ // fillFunc: Function?
+ // An optional function to use to compute label background fill. It takes precedence over
+ // fill property when available.
+ fillFunc: null,
+
+ // labelFunc: Function?
+ // An optional function to use to compute label text. It takes precedence over
+ // the default text when available.
+ labelFunc: {},
+
+ // font: String?
+ // A font definition to use for indicator label background.
+ font: "",
+
+ // fontColor: String|dojo.Color?
+ // The color to use for indicator label background.
+ fontColor: "",
+
+ // markerStroke: dojo.gfx.Stroke?
+ // An optional stroke to use for indicator marker.
+ markerStroke: {},
+
+ // markerOutline: dojo.gfx.Stroke?
+ // An optional outline to use for indicator marker.
+ markerOutline: {},
+
+ // markerShadow: dojo.gfx.Stroke?
+ // An optional shadow to use for indicator marker.
+ markerShadow: {},
+
+ // markerFill: dojo.gfx.Fill?
+ // An optional fill to use for indicator marker.
+ markerFill: {},
+
+ // markerSymbol: String?
+ // An optional symbol string to use for indicator marker.
+ markerFill: {}
+ });
+ var ChartAction = dojox.charting.action2d.ChartAction;
+ =====*/
+
+ return declare("dojox.charting.action2d.MouseIndicator", ChartAction, {
+ // summary:
+ // Create a mouse indicator action. You can drag mouse over the chart to display a data indicator.
+
+ // the data description block for the widget parser
+ defaultParams: {
+ series: "",
+ vertical: true,
+ autoScroll: true,
+ fixed: true,
+ precision: 0
+ },
+ optionalParams: {
+ lineStroke: {},
+ outlineStroke: {},
+ shadowStroke: {},
+ stroke: {},
+ outline: {},
+ shadow: {},
+ fill: {},
+ fillFunc: null,
+ labelFunc: null,
+ font: "",
+ fontColor: "",
+ markerStroke: {},
+ markerOutline: {},
+ markerShadow: {},
+ markerFill: {},
+ markerSymbol: ""
+ },
+
+ constructor: function(chart, plot, kwArgs){
+ // summary:
+ // Create an mouse indicator action and connect it.
+ // chart: dojox.charting.Chart
+ // The chart this action applies to.
+ // kwArgs: dojox.charting.action2d.__MouseIndicatorCtorArgs?
+ // Optional arguments for the chart action.
+ this._listeners = [{eventName: "onmousedown", methodName: "onMouseDown"}];
+ this.opt = lang.clone(this.defaultParams);
+ du.updateWithObject(this.opt, kwArgs);
+ du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
+ this._uName = "mouseIndicator"+this.opt.series;
+ this._handles = [];
+ this.connect();
+ },
+
+ _disconnectHandles: function(){
+ if(has("ie")){
+ this.chart.node.releaseCapture();
+ }
+ arr.forEach(this._handles, hub.disconnect);
+ this._handles = [];
+ },
+
+ connect: function(){
+ // summary:
+ // Connect this action to the chart. This adds a indicator plot
+ // to the chart that's why Chart.render() must be called after connect.
+ this.inherited(arguments);
+ // add plot with unique name
+ this.chart.addPlot(this._uName, {type: IndicatorElement, inter: this});
+ },
+
+ disconnect: function(){
+ // summary:
+ // Disconnect this action from the chart.
+ if(this._isMouseDown){
+ this.onMouseUp();
+ }
+ this.chart.removePlot(this._uName);
+ this.inherited(arguments);
+ this._disconnectHandles();
+ },
+
+ onMouseDown: function(event){
+ // summary:
+ // Called when mouse is down on the chart.
+ this._isMouseDown = true;
+
+ // we now want to capture mouse move events everywhere to avoid
+ // stop scrolling when going out of the chart window
+ if(has("ie")){
+ this._handles.push(hub.connect(this.chart.node, "onmousemove", this, "onMouseMove"));
+ this._handles.push(hub.connect(this.chart.node, "onmouseup", this, "onMouseUp"));
+ this.chart.node.setCapture();
+ }else{
+ this._handles.push(hub.connect(win.doc, "onmousemove", this, "onMouseMove"));
+ this._handles.push(hub.connect(win.doc, "onmouseup", this, "onMouseUp"));
+ }
+
+ this._onMouseSingle(event);
+ },
+
+ onMouseMove: function(event){
+ // summary:
+ // Called when the mouse is moved on the chart.
+ if(this._isMouseDown){
+ this._onMouseSingle(event);
+ }
+ },
+
+ _onMouseSingle: function(event){
+ var plot = this.chart.getPlot(this._uName);
+ plot.pageCoord = {x: event.pageX, y: event.pageY};
+ plot.dirty = true;
+ this.chart.render();
+ eventUtil.stop(event);
+ },
+
+ onMouseUp: function(event){
+ // summary:
+ // Called when mouse is up on the chart.
+ var plot = this.chart.getPlot(this._uName);
+ plot.stopTrack();
+ this._isMouseDown = false;
+ this._disconnectHandles();
+ plot.pageCoord = null;
+ plot.dirty = true;
+ this.chart.render();
+ }
+ });
+});
diff --git a/js/dojo/dojox/charting/action2d/MouseZoomAndPan.js b/js/dojo/dojox/charting/action2d/MouseZoomAndPan.js
new file mode 100644
index 0000000..eea708b
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/MouseZoomAndPan.js
@@ -0,0 +1,246 @@
+//>>built
+define("dojox/charting/action2d/MouseZoomAndPan", ["dojo/_base/html", "dojo/_base/declare", "dojo/_base/window", "dojo/_base/array", "dojo/_base/event",
+ "dojo/_base/connect", "./ChartAction", "dojo/_base/sniff", "dojo/dom-prop", "dojo/keys"],
+ function(html, declare, win, arr, eventUtil, connect, ChartAction, has, domProp, keys){
+
+ /*=====
+ dojo.declare("dojox.charting.action2d.__MouseZoomAndPanCtorArgs", 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 mouse wheel zoom. Default is 1.2.
+ scaleFactor: 1.2,
+ // maxScale: Number?
+ // The max scale factor accepted by this chart action. Default is 100.
+ maxScale: 100,
+ // enableScroll: Boolean?
+ // Whether mouse drag gesture should scroll the chart. Default is true.
+ enableScroll: true,
+ // enableDoubleClickZoom: Boolean?
+ // Whether a double click gesture should toggle between fit and zoom on the chart. Default is true.
+ enableDoubleClickZoom: true,
+ // enableKeyZoom: Boolean?
+ // Whether a keyZoomModifier + + or keyZoomModifier + - key press should zoom in our out on the chart. Default is true.
+ enableKeyZoom: true,
+ // keyZoomModifier: String?
+ // Which keyboard modifier should used for keyboard zoom in and out. This should be one of "alt", "ctrl", "shift" or "none" for no modifier. Default is "ctrl".
+ keyZoomModifier: "ctrl"
+ });
+ var ChartAction = dojox.charting.action2d.ChartAction;
+ =====*/
+
+ var sUnit = has("mozilla") ? -3 : 120;
+ var keyTests = {
+ none: function(event){
+ return !event.ctrlKey && !event.altKey && !event.shiftKey;
+ },
+ ctrl: function(event){
+ return event.ctrlKey && !event.altKey && !event.shiftKey;
+ },
+ alt: function(event){
+ return !event.ctrlKey && event.altKey && !event.shiftKey;
+ },
+ shift: function(event){
+ return !event.ctrlKey && !event.altKey && event.shiftKey;
+ }
+ };
+
+ return declare("dojox.charting.action2d.MouseZoomAndPan", ChartAction, {
+ // summary:
+ // Create an mouse zoom and pan action.
+ // You can zoom in or out the data window with mouse wheel. You can scroll using mouse drag gesture.
+ // You can toggle between zoom and fit view using double click on the chart.
+
+ // the data description block for the widget parser
+ defaultParams: {
+ axis: "x",
+ scaleFactor: 1.2,
+ maxScale: 100,
+ enableScroll: true,
+ enableDoubleClickZoom: true,
+ enableKeyZoom: true,
+ keyZoomModifier: "ctrl"
+ },
+ optionalParams: {}, // no optional parameters
+
+ constructor: function(chart, plot, kwArgs){
+ // summary:
+ // Create an mouse zoom and pan action and connect it.
+ // chart: dojox.charting.Chart
+ // The chart this action applies to.
+ // kwArgs: dojox.charting.action2d.__MouseZoomAndPanCtorArgs?
+ // Optional arguments for the chart action.
+ this._listeners = [{eventName: !has("mozilla") ? "onmousewheel" : "DOMMouseScroll", methodName: "onMouseWheel"}];
+ 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.enableDoubleClickZoom = kwArgs.enableDoubleClickZoom != undefined ? kwArgs.enableDoubleClickZoom : true;
+ this.enableKeyZoom = kwArgs.enableKeyZoom != undefined ? kwArgs.enableKeyZoom : true;
+ this.keyZoomModifier = kwArgs.keyZoomModifier ? kwArgs.keyZoomModifier : "ctrl";
+ if(this.enableScroll){
+ this._listeners.push({eventName: "onmousedown", methodName: "onMouseDown"});
+ }
+ if(this.enableDoubleClickZoom){
+ this._listeners.push({eventName: "ondblclick", methodName: "onDoubleClick"});
+ }
+ if(this.enableKeyZoom){
+ this._listeners.push({eventName: "keypress", methodName: "onKeyPress"});
+ }
+ this._handles = [];
+ this.connect();
+ },
+
+ _disconnectHandles: function(){
+ if(has("ie")){
+ this.chart.node.releaseCapture();
+ }
+ arr.forEach(this._handles, connect.disconnect);
+ this._handles = [];
+ },
+
+ connect: function(){
+ // summary:
+ // Connect this action to the chart.
+ this.inherited(arguments);
+ if(this.enableKeyZoom){
+ // we want to be able to get focus to receive key events
+ domProp.set(this.chart.node, "tabindex", "0");
+ // if one doesn't want a focus border he can do something like
+ // dojo.style(this.chart.node, "outline", "none");
+ }
+ },
+
+ disconnect: function(){
+ // summary:
+ // Disconnect this action from the chart.
+ this.inherited(arguments);
+ if(this.enableKeyZoom){
+ // we don't need anymore to be able to get focus to receive key events
+ domProp.set(this.chart.node, "tabindex", "-1");
+ }
+ // in case we disconnect before the end of the action
+ this._disconnectHandles();
+ },
+
+ onMouseDown: function(event){
+ // summary:
+ // Called when mouse is down on the chart.
+ var chart = this.chart, axis = chart.getAxis(this.axis);
+ if(!axis.vertical){
+ this._startCoord = event.pageX;
+ }else{
+ this._startCoord = event.pageY;
+ }
+ this._startOffset = axis.getWindowOffset();
+ this._isPanning = true;
+ // we now want to capture mouse move events everywhere to avoid
+ // stop scrolling when going out of the chart window
+ if(has("ie")){
+ this._handles.push(connect.connect(this.chart.node, "onmousemove", this, "onMouseMove"));
+ this._handles.push(connect.connect(this.chart.node, "onmouseup", this, "onMouseUp"));
+ this.chart.node.setCapture();
+ }else{
+ this._handles.push(connect.connect(win.doc, "onmousemove", this, "onMouseMove"));
+ this._handles.push(connect.connect(win.doc, "onmouseup", this, "onMouseUp"));
+ }
+ chart.node.focus();
+ // prevent the browser from trying the drag on the "image"
+ eventUtil.stop(event);
+ },
+
+ onMouseMove: function(event){
+ // summary:
+ // Called when mouse is moved on the chart.
+ if(this._isPanning){
+ var chart = this.chart, axis = chart.getAxis(this.axis);
+ var delta = axis.vertical?(this._startCoord- event.pageY):(event.pageX - this._startCoord);
+
+ var bounds = axis.getScaler().bounds,
+ s = bounds.span / (bounds.upper - bounds.lower);
+
+ var scale = axis.getWindowScale();
+
+ chart.setAxisWindow(this.axis, scale, this._startOffset - delta / s / scale);
+ chart.render();
+ }
+ },
+
+ onMouseUp: function(event){
+ // summary:
+ // Called when mouse is up on the chart.
+ this._isPanning = false;
+ this._disconnectHandles();
+ },
+
+ onMouseWheel: function(event){
+ // summary:
+ // Called when mouse wheel is used on the chart.
+ var scroll = event[(has("mozilla") ? "detail" : "wheelDelta")] / sUnit;
+ // on Mozilla the sUnit might actually not always be 3
+ // make sure we never have -1 < scroll < 1
+ if(scroll > -1 && scroll < 0){
+ scroll = -1;
+ }else if(scroll > 0 && scroll < 1){
+ scroll = 1;
+ }
+ this._onZoom(scroll, event);
+ },
+
+ onKeyPress: function(event){
+ // summary:
+ // Called when a key is pressed on the chart.
+ if(keyTests[this.keyZoomModifier](event)){
+ if(event.keyChar == "+" || event.keyCode == keys.NUMPAD_PLUS){
+ this._onZoom(1, event);
+ }else if(event.keyChar == "-" || event.keyCode == keys.NUMPAD_MINUS){
+ this._onZoom(-1, event);
+ }
+ }
+ },
+
+ onDoubleClick: function(event){
+ // summary:
+ // Called when the mouse is double is double clicked on the chart. Toggle between zoom and fit 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({x: event.pageX, y: event.pageY})[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);
+ },
+
+ _onZoom: function(scroll, event){
+ var scale = (scroll < 0 ? Math.abs(scroll)*this.scaleFactor :
+ 1 / (Math.abs(scroll)*this.scaleFactor));
+ var chart = this.chart, axis = chart.getAxis(this.axis);
+ // after wheel reset event position exactly if we could start a new scroll action
+ var cscale = axis.getWindowScale();
+ if(cscale / scale > this.maxScale){
+ return;
+ }
+ var scaler = axis.getScaler(), start = scaler.bounds.from, end = scaler.bounds.to;
+ // keep mouse pointer as transformation center if available otherwise center
+ var middle = (event.type == "keypress") ? (start + end) / 2 :
+ this.plot.toData({x: event.pageX, y: event.pageY})[this.axis];
+ var newStart = scale * (start - middle) + middle, newEnd = scale * (end - middle) + middle;
+ chart.zoomIn(this.axis, [newStart, newEnd]);
+ // do not scroll browser
+ eventUtil.stop(event);
+ }
+ });
+});
diff --git a/js/dojo/dojox/charting/action2d/MoveSlice.js b/js/dojo/dojox/charting/action2d/MoveSlice.js
new file mode 100644
index 0000000..9029120
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/MoveSlice.js
@@ -0,0 +1,122 @@
+//>>built
+define("dojox/charting/action2d/MoveSlice", ["dojo/_base/connect", "dojo/_base/declare", "./PlotAction", "dojo/fx/easing", "dojox/gfx/matrix",
+ "dojox/gfx/fx", "dojox/lang/functional", "dojox/lang/functional/scan", "dojox/lang/functional/fold"],
+ function(hub, declare, PlotAction, dfe, m, gf, df, dfs, dff){
+
+ /*=====
+ dojo.declare("dojox.charting.action2d.__MoveSliceCtorArgs", dojox.charting.action2d.__PlotActionCtorArgs, {
+ // summary:
+ // Additional arguments for highlighting actions.
+
+ // scale: Number?
+ // The amount to scale the pie slice. Default is 1.05.
+ scale: 1.05,
+
+ // shift: Number?
+ // The amount in pixels to shift the pie slice. Default is 7.
+ shift: 7
+ });
+ var PlotAction = dojox.charting.action2d.PlotAction;
+ =====*/
+
+ var DEFAULT_SCALE = 1.05,
+ DEFAULT_SHIFT = 7; // px
+
+ return declare("dojox.charting.action2d.MoveSlice", PlotAction, {
+ // summary:
+ // Create an action for a pie chart that moves and scales a pie slice.
+
+ // the data description block for the widget parser
+ defaultParams: {
+ duration: 400, // duration of the action in ms
+ easing: dfe.backOut, // easing for the action
+ scale: DEFAULT_SCALE, // scale of magnification
+ shift: DEFAULT_SHIFT // shift of the slice
+ },
+ optionalParams: {}, // no optional parameters
+
+ constructor: function(chart, plot, kwArgs){
+ // summary:
+ // Create the slice moving action and connect it to the plot.
+ // chart: dojox.charting.Chart
+ // The chart this action belongs to.
+ // plot: String?
+ // The plot this action is attached to. If not passed, "default" is assumed.
+ // kwArgs: dojox.charting.action2d.__MoveSliceCtorArgs?
+ // Optional keyword arguments object for setting parameters.
+ if(!kwArgs){ kwArgs = {}; }
+ this.scale = typeof kwArgs.scale == "number" ? kwArgs.scale : DEFAULT_SCALE;
+ this.shift = typeof kwArgs.shift == "number" ? kwArgs.shift : DEFAULT_SHIFT;
+
+ this.connect();
+ },
+
+ process: function(o){
+ // summary:
+ // Process the action on the given object.
+ // o: dojox.gfx.Shape
+ // The object on which to process the slice moving action.
+ if(!o.shape || o.element != "slice" || !(o.type in this.overOutEvents)){ return; }
+
+ if(!this.angles){
+ // calculate the running total of slice angles
+ var startAngle = m._degToRad(o.plot.opt.startAngle);
+ if(typeof o.run.data[0] == "number"){
+ this.angles = df.map(df.scanl(o.run.data, "+", startAngle),
+ "* 2 * Math.PI / this", df.foldl(o.run.data, "+", 0));
+ }else{
+ this.angles = df.map(df.scanl(o.run.data, "a + b.y", startAngle),
+ "* 2 * Math.PI / this", df.foldl(o.run.data, "a + b.y", 0));
+ }
+ }
+
+ var index = o.index, anim, startScale, endScale, startOffset, endOffset,
+ angle = (this.angles[index] + this.angles[index + 1]) / 2,
+ rotateTo0 = m.rotateAt(-angle, o.cx, o.cy),
+ rotateBack = m.rotateAt( angle, o.cx, o.cy);
+
+ anim = this.anim[index];
+
+ if(anim){
+ anim.action.stop(true);
+ }else{
+ this.anim[index] = anim = {};
+ }
+
+ if(o.type == "onmouseover"){
+ startOffset = 0;
+ endOffset = this.shift;
+ startScale = 1;
+ endScale = this.scale;
+ }else{
+ startOffset = this.shift;
+ endOffset = 0;
+ startScale = this.scale;
+ endScale = 1;
+ }
+
+ anim.action = gf.animateTransform({
+ shape: o.shape,
+ duration: this.duration,
+ easing: this.easing,
+ transform: [
+ rotateBack,
+ {name: "translate", start: [startOffset, 0], end: [endOffset, 0]},
+ {name: "scaleAt", start: [startScale, o.cx, o.cy], end: [endScale, o.cx, o.cy]},
+ rotateTo0
+ ]
+ });
+
+ if(o.type == "onmouseout"){
+ hub.connect(anim.action, "onEnd", this, function(){
+ delete this.anim[index];
+ });
+ }
+ anim.action.play();
+ },
+
+ reset: function(){
+ delete this.angles;
+ }
+ });
+});
diff --git a/js/dojo/dojox/charting/action2d/PlotAction.js b/js/dojo/dojox/charting/action2d/PlotAction.js
new file mode 100644
index 0000000..2766651
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/PlotAction.js
@@ -0,0 +1,79 @@
+//>>built
+define("dojox/charting/action2d/PlotAction", ["dojo/_base/connect", "dojo/_base/declare", "./Base", "dojo/fx/easing", "dojox/lang/functional",
+ "dojox/lang/functional/object"],
+ function(hub, declare, Base, dfe, df, dlfo){
+
+ /*=====
+ dojox.charting.action2d.__PlotActionCtorArgs = function(duration, easing){
+ // summary:
+ // The base keyword arguments object for creating an action2d.
+ // duration: Number?
+ // The amount of time in milliseconds for an animation to last. Default is 400.
+ // easing: dojo.fx.easing.*?
+ // An easing object (see dojo.fx.easing) for use in an animation. The
+ // default is dojo.fx.easing.backOut.
+ this.duration = duration;
+ this.easing = easing;
+ }
+ var Base = dojox.charting.action2d.Base;
+ =====*/
+
+ var DEFAULT_DURATION = 400, // ms
+ DEFAULT_EASING = dfe.backOut;
+
+ return declare("dojox.charting.action2d.PlotAction", Base, {
+ // summary:
+ // Base action class for plot actions.
+
+ overOutEvents: {onmouseover: 1, onmouseout: 1},
+
+ constructor: function(chart, plot, kwargs){
+ // summary:
+ // Create a new base PlotAction.
+ // chart: dojox.charting.Chart
+ // The chart this action applies to.
+ // plot: String?
+ // The name of the plot this action belongs to. If none is passed "default" is assumed.
+ // kwargs: dojox.charting.action2d.__PlotActionCtorArgs?
+ // Optional arguments for the action.
+ this.anim = {};
+
+ // process common optional named parameters
+ if(!kwargs){ kwargs = {}; }
+ this.duration = kwargs.duration ? kwargs.duration : DEFAULT_DURATION;
+ this.easing = kwargs.easing ? kwargs.easing : DEFAULT_EASING;
+ },
+
+ connect: function(){
+ // summary:
+ // Connect this action to the given plot.
+ this.handle = this.chart.connectToPlot(this.plot.name, this, "process");
+ },
+
+ disconnect: function(){
+ // summary:
+ // Disconnect this action from the given plot, if connected.
+ if(this.handle){
+ hub.disconnect(this.handle);
+ this.handle = null;
+ }
+ },
+
+ reset: function(){
+ // summary:
+ // Reset the action.
+ },
+
+ destroy: function(){
+ // summary:
+ // Do any cleanup needed when destroying parent elements.
+ this.inherited(arguments);
+ df.forIn(this.anim, function(o){
+ df.forIn(o, function(anim){
+ anim.action.stop(true);
+ });
+ });
+ this.anim = {};
+ }
+ });
+});
diff --git a/js/dojo/dojox/charting/action2d/Shake.js b/js/dojo/dojox/charting/action2d/Shake.js
new file mode 100644
index 0000000..478e1bb
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/Shake.js
@@ -0,0 +1,109 @@
+//>>built
+define("dojox/charting/action2d/Shake", ["dojo/_base/connect", "dojo/_base/declare", "./PlotAction",
+ "dojo/fx", "dojo/fx/easing", "dojox/gfx/matrix", "dojox/gfx/fx"],
+ function(hub, declare, PlotAction, df, dfe, m, gf){
+
+ /*=====
+ dojo.declare("dojox.charting.action2d.__ShakeCtorArgs", dojox.charting.action2d.__PlotActionCtorArgstorArgs, {
+ // summary:
+ // Additional arguments for highlighting actions.
+
+ // shift: Number?
+ // The amount in pixels to shift the pie slice. Default is 3.
+ shift: 3
+ });
+ var PlotAction = dojox.charting.action2d.PlotAction;
+ =====*/
+
+ var DEFAULT_SHIFT = 3;
+
+ return declare("dojox.charting.action2d.Shake", PlotAction, {
+ // summary:
+ // Create a shaking action for use on an element in a chart.
+
+ // the data description block for the widget parser
+ defaultParams: {
+ duration: 400, // duration of the action in ms
+ easing: dfe.backOut, // easing for the action
+ shiftX: DEFAULT_SHIFT, // shift of the element along the X axis
+ shiftY: DEFAULT_SHIFT // shift of the element along the Y axis
+ },
+ optionalParams: {}, // no optional parameters
+
+ constructor: function(chart, plot, kwArgs){
+ // summary:
+ // Create the shaking action and connect it to the plot.
+ // chart: dojox.charting.Chart
+ // The chart this action belongs to.
+ // plot: String?
+ // The plot this action is attached to. If not passed, "default" is assumed.
+ // kwArgs: dojox.charting.action2d.__ShakeCtorArgs?
+ // Optional keyword arguments object for setting parameters.
+ if(!kwArgs){ kwArgs = {}; }
+ this.shiftX = typeof kwArgs.shiftX == "number" ? kwArgs.shiftX : DEFAULT_SHIFT;
+ this.shiftY = typeof kwArgs.shiftY == "number" ? kwArgs.shiftY : DEFAULT_SHIFT;
+
+ this.connect();
+ },
+
+ process: function(o){
+ // summary:
+ // Process the action on the given object.
+ // o: dojox.gfx.Shape
+ // The object on which to process the slice moving action.
+ if(!o.shape || !(o.type in this.overOutEvents)){ return; }
+
+ var runName = o.run.name, index = o.index, vector = [], anim,
+ shiftX = o.type == "onmouseover" ? this.shiftX : -this.shiftX,
+ shiftY = o.type == "onmouseover" ? this.shiftY : -this.shiftY;
+
+ if(runName in this.anim){
+ anim = this.anim[runName][index];
+ }else{
+ this.anim[runName] = {};
+ }
+
+ if(anim){
+ anim.action.stop(true);
+ }else{
+ this.anim[runName][index] = anim = {};
+ }
+
+ var kwArgs = {
+ shape: o.shape,
+ duration: this.duration,
+ easing: this.easing,
+ transform: [
+ {name: "translate", start: [this.shiftX, this.shiftY], end: [0, 0]},
+ m.identity
+ ]
+ };
+ if(o.shape){
+ vector.push(gf.animateTransform(kwArgs));
+ }
+ if(o.oultine){
+ kwArgs.shape = o.outline;
+ vector.push(gf.animateTransform(kwArgs));
+ }
+ if(o.shadow){
+ kwArgs.shape = o.shadow;
+ vector.push(gf.animateTransform(kwArgs));
+ }
+
+ if(!vector.length){
+ delete this.anim[runName][index];
+ return;
+ }
+
+ anim.action = df.combine(vector);
+ if(o.type == "onmouseout"){
+ hub.connect(anim.action, "onEnd", this, function(){
+ if(this.anim[runName]){
+ delete this.anim[runName][index];
+ }
+ });
+ }
+ anim.action.play();
+ }
+ });
+});
diff --git a/js/dojo/dojox/charting/action2d/Tooltip.js b/js/dojo/dojox/charting/action2d/Tooltip.js
new file mode 100644
index 0000000..9acad33
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/Tooltip.js
@@ -0,0 +1,167 @@
+//>>built
+define("dojox/charting/action2d/Tooltip", ["dojo/_base/kernel", "dijit/Tooltip","dojo/_base/lang", "dojo/_base/html", "dojo/_base/declare", "./PlotAction",
+ "dojox/gfx/matrix", "dojox/lang/functional", "dojox/lang/functional/scan", "dojox/lang/functional/fold"],
+ function(dojo, Tooltip, lang, html, declare, PlotAction, m, df, dfs, dff){
+
+ /*=====
+ dojo.declare("dojox.charting.action2d.__TooltipCtorArgs", dojox.charting.action2d.__PlotActionCtorArgs, {
+ // summary:
+ // Additional arguments for tooltip actions.
+
+ // text: Function?
+ // The function that produces the text to be shown within a tooltip. By default this will be
+ // set by the plot in question, by returning the value of the element.
+ text: null
+ });
+ var PlotAction = dojox.charting.action2d.PlotAction;
+ =====*/
+
+ var DEFAULT_TEXT = function(o){
+ var t = o.run && o.run.data && o.run.data[o.index];
+ if(t && typeof t != "number" && (t.tooltip || t.text)){
+ return t.tooltip || t.text;
+ }
+ if(o.element == "candlestick"){
+ return '<table cellpadding="1" cellspacing="0" border="0" style="font-size:0.9em;">'
+ + '<tr><td>Open:</td><td align="right"><strong>' + o.data.open + '</strong></td></tr>'
+ + '<tr><td>High:</td><td align="right"><strong>' + o.data.high + '</strong></td></tr>'
+ + '<tr><td>Low:</td><td align="right"><strong>' + o.data.low + '</strong></td></tr>'
+ + '<tr><td>Close:</td><td align="right"><strong>' + o.data.close + '</strong></td></tr>'
+ + (o.data.mid !== undefined ? '<tr><td>Mid:</td><td align="right"><strong>' + o.data.mid + '</strong></td></tr>' : '')
+ + '</table>';
+ }
+ return o.element == "bar" ? o.x : o.y;
+ };
+
+ var pi4 = Math.PI / 4, pi2 = Math.PI / 2;
+
+ return declare("dojox.charting.action2d.Tooltip", PlotAction, {
+ // summary:
+ // Create an action on a plot where a tooltip is shown when hovering over an element.
+
+ // the data description block for the widget parser
+ defaultParams: {
+ text: DEFAULT_TEXT // the function to produce a tooltip from the object
+ },
+ optionalParams: {}, // no optional parameters
+
+ constructor: function(chart, plot, kwArgs){
+ // summary:
+ // Create the tooltip action and connect it to the plot.
+ // chart: dojox.charting.Chart
+ // The chart this action belongs to.
+ // plot: String?
+ // The plot this action is attached to. If not passed, "default" is assumed.
+ // kwArgs: dojox.charting.action2d.__TooltipCtorArgs?
+ // Optional keyword arguments object for setting parameters.
+ this.text = kwArgs && kwArgs.text ? kwArgs.text : DEFAULT_TEXT;
+
+ this.connect();
+ },
+
+ process: function(o){
+ // summary:
+ // Process the action on the given object.
+ // o: dojox.gfx.Shape
+ // The object on which to process the highlighting action.
+ if(o.type === "onplotreset" || o.type === "onmouseout"){
+ Tooltip.hide(this.aroundRect);
+ this.aroundRect = null;
+ if(o.type === "onplotreset"){
+ delete this.angles;
+ }
+ return;
+ }
+
+ if(!o.shape || o.type !== "onmouseover"){ return; }
+
+ // calculate relative coordinates and the position
+ var aroundRect = {type: "rect"}, position = ["after", "before"];
+ switch(o.element){
+ case "marker":
+ aroundRect.x = o.cx;
+ aroundRect.y = o.cy;
+ aroundRect.w = aroundRect.h = 1;
+ break;
+ case "circle":
+ aroundRect.x = o.cx - o.cr;
+ aroundRect.y = o.cy - o.cr;
+ aroundRect.w = aroundRect.h = 2 * o.cr;
+ break;
+ case "column":
+ position = ["above", "below"];
+ // intentional fall down
+ case "bar":
+ aroundRect = lang.clone(o.shape.getShape());
+ aroundRect.w = aroundRect.width;
+ aroundRect.h = aroundRect.height;
+ break;
+ case "candlestick":
+ aroundRect.x = o.x;
+ aroundRect.y = o.y;
+ aroundRect.w = o.width;
+ aroundRect.h = o.height;
+ break;
+ default:
+ //case "slice":
+ if(!this.angles){
+ // calculate the running total of slice angles
+ if(typeof o.run.data[0] == "number"){
+ this.angles = df.map(df.scanl(o.run.data, "+", 0),
+ "* 2 * Math.PI / this", df.foldl(o.run.data, "+", 0));
+ }else{
+ this.angles = df.map(df.scanl(o.run.data, "a + b.y", 0),
+ "* 2 * Math.PI / this", df.foldl(o.run.data, "a + b.y", 0));
+ }
+ }
+ var startAngle = m._degToRad(o.plot.opt.startAngle),
+ angle = (this.angles[o.index] + this.angles[o.index + 1]) / 2 + startAngle;
+ aroundRect.x = o.cx + o.cr * Math.cos(angle);
+ aroundRect.y = o.cy + o.cr * Math.sin(angle);
+ aroundRect.w = aroundRect.h = 1;
+ // calculate the position
+ if(angle < pi4){
+ // do nothing: the position is right
+ }else if(angle < pi2 + pi4){
+ position = ["below", "above"];
+ }else if(angle < Math.PI + pi4){
+ position = ["before", "after"];
+ }else if(angle < 2 * Math.PI - pi4){
+ position = ["above", "below"];
+ }
+ /*
+ else{
+ // do nothing: the position is right
+ }
+ */
+ break;
+ }
+
+ // adjust relative coordinates to absolute, and remove fractions
+ var lt = this.chart.getCoords();
+ aroundRect.x += lt.x;
+ aroundRect.y += lt.y;
+ aroundRect.x = Math.round(aroundRect.x);
+ aroundRect.y = Math.round(aroundRect.y);
+ aroundRect.w = Math.ceil(aroundRect.w);
+ aroundRect.h = Math.ceil(aroundRect.h);
+ this.aroundRect = aroundRect;
+
+ var tooltip = this.text(o);
+ if(this.chart.getTextDir){
+ var isChartDirectionRtl = (html.style(this.chart.node,"direction") == "rtl");
+ var isBaseTextDirRtl = (this.chart.getTextDir(tooltip) == "rtl");
+ }
+ if(tooltip){
+ if(isBaseTextDirRtl && !isChartDirectionRtl){
+ Tooltip.show("<span dir = 'rtl'>" + tooltip +"</span>", this.aroundRect, position);
+ }
+ else if(!isBaseTextDirRtl && isChartDirectionRtl){
+ Tooltip.show("<span dir = 'ltr'>" + tooltip +"</span>", this.aroundRect, position);
+ }else{
+ Tooltip.show(tooltip, this.aroundRect, position);
+ }
+ }
+ }
+ });
+});
diff --git a/js/dojo/dojox/charting/action2d/TouchIndicator.js b/js/dojo/dojox/charting/action2d/TouchIndicator.js
new file mode 100644
index 0000000..36cec47
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/TouchIndicator.js
@@ -0,0 +1,233 @@
+//>>built
+define("dojox/charting/action2d/TouchIndicator", ["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/event", "./ChartAction", "./_IndicatorElement", "dojox/lang/utils"],
+ function(lang, declare, eventUtil, ChartAction, IndicatorElement, du){
+
+ /*=====
+ dojo.declare("dojox.charting.action2d.__TouchIndicatorCtorArgs", null, {
+ // summary:
+ // Additional arguments for Touch indicator.
+
+ // series: String
+ // Target series name for this chart action.
+ series: "",
+
+ // dualIndicator: Boolean?
+ // Whether a double touch on the chart creates a dual indicator showing data trend between the two touch points. Default is false.
+ dualIndicator: false,
+
+ // autoScroll: Boolean?
+ // Whether when moving indicator the chart is automatically scrolled. Default is true.
+ autoScroll: true,
+
+ // vertical: Boolean?
+ // Whether the indicator is vertical or not. Default is true.
+ vertical: true,
+
+ // fixed: Boolean?
+ // Whether a fixed precision must be applied to data values for display. Default is true.
+ fixed: true,
+
+ // precision: Number?
+ // The precision at which to round data values for display. Default is 1.
+ precision: 0,
+
+ // lineStroke: gfx.Stroke?
+ // An optional stroke to use for indicator line.
+ lineStroke: {},
+
+ // lineOutline: dojo.gfx.Stroke?
+ // An optional outline to use for indicator line.
+ lineOutline: {},
+
+ // lineShadow: dojo.gfx.Stroke?
+ // An optional shadow to use for indicator line.
+ lineShadow: {},
+
+ // stroke: dojo.gfx.Stroke?
+ // An optional stroke to use for indicator label background.
+ stroke: {},
+
+ // outline: dojo.gfx.Stroke?
+ // An optional outline to use for indicator label background.
+ outline: {},
+
+ // shadow: dojo.gfx.Stroke?
+ // An optional shadow to use for indicator label background.
+ shadow: {},
+
+ // fill: dojo.gfx.Fill?
+ // An optional fill to use for indicator label background.
+ fill: {},
+
+ // fillFunc: Function?
+ // An optional function to use to compute label background fill. It takes precedence over
+ // fill property when available.
+ fillFunc: null,
+
+ // labelFunc: Function?
+ // An optional function to use to compute label text. It takes precedence over
+ // the default text when available.
+ labelFunc: {},
+
+ // font: String?
+ // A font definition to use for indicator label background.
+ font: "",
+
+ // fontColor: String|dojo.Color?
+ // The color to use for indicator label background.
+ fontColor: "",
+
+ // markerStroke: dojo.gfx.Stroke?
+ // An optional stroke to use for indicator marker.
+ markerStroke: {},
+
+ // markerOutline: dojo.gfx.Stroke?
+ // An optional outline to use for indicator marker.
+ markerOutline: {},
+
+ // markerShadow: dojo.gfx.Stroke?
+ // An optional shadow to use for indicator marker.
+ markerShadow: {},
+
+ // markerFill: dojo.gfx.Fill?
+ // An optional fill to use for indicator marker.
+ markerFill: {},
+
+ // markerSymbol: String?
+ // An optional symbol string to use for indicator marker.
+ markerFill: {}
+ });
+ var ChartAction = dojox.charting.action2d.ChartAction;
+ =====*/
+
+ return declare("dojox.charting.action2d.TouchIndicator", ChartAction, {
+ // summary:
+ // Create a touch indicator action. You can touch over the chart to display a data indicator.
+
+ // the data description block for the widget parser
+ defaultParams: {
+ series: "",
+ dualIndicator: false,
+ vertical: true,
+ autoScroll: true,
+ fixed: true,
+ precision: 0
+ },
+ optionalParams: {
+ lineStroke: {},
+ outlineStroke: {},
+ shadowStroke: {},
+ stroke: {},
+ outline: {},
+ shadow: {},
+ fill: {},
+ fillFunc: null,
+ labelFunc: null,
+ font: "",
+ fontColor: "",
+ markerStroke: {},
+ markerOutline: {},
+ markerShadow: {},
+ markerFill: {},
+ markerSymbol: ""
+ },
+
+ constructor: function(chart, plot, kwArgs){
+ // summary:
+ // Create a new touch indicator action and connect it.
+ // chart: dojox.charting.Chart
+ // The chart this action applies to.
+ // kwArgs: dojox.charting.action2d.__TouchIndicatorCtorArgs?
+ // Optional arguments for the chart action.
+ this._listeners = [{eventName: "ontouchstart", methodName: "onTouchStart"},
+ {eventName: "ontouchmove", methodName: "onTouchMove"},
+ {eventName: "ontouchend", methodName: "onTouchEnd"},
+ {eventName: "ontouchcancel", methodName: "onTouchEnd"}];
+ this.opt = lang.clone(this.defaultParams);
+ du.updateWithObject(this.opt, kwArgs);
+ du.updateWithPattern(this.opt, kwArgs, this.optionalParams);
+ this._uName = "touchIndicator"+this.opt.series;
+ this.connect();
+ },
+
+ connect: function(){
+ // summary:
+ // Connect this action to the chart. This adds a indicator plot
+ // to the chart that's why Chart.render() must be called after connect.
+ this.inherited(arguments);
+ // add plot with unique name
+ this.chart.addPlot(this._uName, {type: IndicatorElement, inter: this});
+ },
+
+ disconnect: function(){
+ // summary:
+ // Disconnect this action from the chart.
+ var plot = this.chart.getPlot(this._uName);
+ if(plot.pageCoord){
+ // we might still have something drawn on the screen
+ this.onTouchEnd();
+ }
+ this.chart.removePlot(this._uName);
+ this.inherited(arguments);
+ },
+
+ onTouchStart: function(event){
+ // summary:
+ // Called when touch is started on the chart.
+ if(event.touches.length==1){
+ this._onTouchSingle(event, true);
+ }else if(this.opt.dualIndicator && event.touches.length==2){
+ this._onTouchDual(event);
+ }
+ },
+
+ onTouchMove: function(event){
+ // summary:
+ // Called when touch is moved on the chart.
+ if(event.touches.length==1){
+ this._onTouchSingle(event);
+ }else if(this.opt.dualIndicator && event.touches.length==2){
+ this._onTouchDual(event);
+ }
+ },
+
+ _onTouchSingle: function(event, delayed){
+ // sync
+ if(this.chart._delayedRenderHandle && !delayed){
+ // we have pending rendering from a previous call, let's sync
+ clearTimeout(this.chart._delayedRenderHandle);
+ this.chart._delayedRenderHandle = null;
+ this.chart.render();
+ }
+ var plot = this.chart.getPlot(this._uName);
+ plot.pageCoord = {x: event.touches[0].pageX, y: event.touches[0].pageY};
+ plot.dirty = true;
+ if(delayed){
+ this.chart.delayedRender();
+ }else{
+ this.chart.render();
+ }
+ eventUtil.stop(event);
+ },
+
+ _onTouchDual: function(event){
+ var plot = this.chart.getPlot(this._uName);
+ plot.pageCoord = {x: event.touches[0].pageX, y: event.touches[0].pageY};
+ plot.secondCoord = {x: event.touches[1].pageX, y: event.touches[1].pageY};
+ plot.dirty = true;
+ this.chart.render();
+ eventUtil.stop(event);
+ },
+
+ onTouchEnd: function(event){
+ // summary:
+ // Called when touch is ended or canceled on the chart.
+ var plot = this.chart.getPlot(this._uName);
+ plot.stopTrack();
+ plot.pageCoord = null;
+ plot.secondCoord = null;
+ plot.dirty = true;
+ this.chart.delayedRender();
+ }
+ });
+});
diff --git a/js/dojo/dojox/charting/action2d/TouchZoomAndPan.js b/js/dojo/dojox/charting/action2d/TouchZoomAndPan.js
new file mode 100644
index 0000000..9ad36b9
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/TouchZoomAndPan.js
@@ -0,0 +1,250 @@
+//>>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);
+ }
+ });
+});
diff --git a/js/dojo/dojox/charting/action2d/_IndicatorElement.js b/js/dojo/dojox/charting/action2d/_IndicatorElement.js
new file mode 100644
index 0000000..ab71c63
--- /dev/null
+++ b/js/dojo/dojox/charting/action2d/_IndicatorElement.js
@@ -0,0 +1,371 @@
+//>>built
+define("dojox/charting/action2d/_IndicatorElement", ["dojo/_base/lang", "dojo/_base/declare", "../Element", "../plot2d/common",
+ "../axis2d/common", "dojox/gfx"],
+ function(lang, declare, Element, dcpc, dcac, gfx){
+
+ // all the code below should be removed when http://trac.dojotoolkit.org/ticket/11299 will be available
+ var getBoundingBox = function(shape){
+ return getTextBBox(shape, shape.getShape().text);
+ };
+ var getTextBBox = function(s, t){
+ var c = s.declaredClass;
+ if (c.indexOf("svg")!=-1){
+ // try/catch the FF native getBBox error. cheaper than walking up in the DOM
+ // hierarchy to check the conditions (bench show /10 )
+ try {
+ return lang.mixin({}, s.rawNode.getBBox());
+ }catch (e){
+ return null;
+ }
+ }else if(c.indexOf("vml")!=-1){
+ var rawNode = s.rawNode, _display = rawNode.style.display;
+ rawNode.style.display = "inline";
+ var w = gfx.pt2px(parseFloat(rawNode.currentStyle.width));
+ var h = gfx.pt2px(parseFloat(rawNode.currentStyle.height));
+ var sz = {x: 0, y: 0, width: w, height: h};
+ // in VML, the width/height we get are in view coordinates
+ // in our case we don't zoom the view so that is ok
+ // It's impossible to get the x/y from the currentStyle.left/top,
+ // because all negative coordinates are 'clipped' to 0.
+ // (x:0 + translate(-100) -> x=0
+ computeLocation(s, sz);
+ rawNode.style.display = _display;
+ return sz;
+ }else if(c.indexOf("silverlight")!=-1){
+ var bb = {width: s.rawNode.actualWidth, height: s.rawNode.actualHeight};
+ return computeLocation(s, bb, 0.75);
+ }else if(s.getTextWidth){
+ // canvas
+ var w = s.getTextWidth();
+ var font = s.getFont();
+ var fz = font ? font.size : gfx.defaultFont.size;
+ var h = gfx.normalizedLength(fz);
+ sz = {width: w, height: h};
+ computeLocation(s, sz, 0.75);
+ return sz;
+ }
+ };
+ var computeLocation = function(s, sz, coef){
+ var width = sz.width, height = sz.height, sh = s.getShape(), align = sh.align;
+ switch (align) {
+ case "end":
+ sz.x = sh.x - width;
+ break;
+ case "middle":
+ sz.x = sh.x - width / 2;
+ break;
+ case "start":
+ default:
+ sz.x = sh.x;
+ break;
+ }
+ coef = coef || 1;
+ sz.y = sh.y - height*coef; // rough approximation of the ascent!...
+ return sz;
+ };
+
+ return declare("dojox.charting.action2d._IndicatorElement",[Element], {
+ // summary:
+ // Internal element used by indicator actions.
+ // tags:
+ // private
+ constructor: function(chart, kwArgs){
+ if(!kwArgs){ kwArgs = {}; }
+ this.inter = kwArgs.inter;
+ },
+ _updateVisibility: function(cp, limit, attr){
+ var axis = attr=="x"?this.inter.plot._hAxis:this.inter.plot._vAxis;
+ var scale = axis.getWindowScale();
+ this.chart.setAxisWindow(axis.name, scale, axis.getWindowOffset() + (cp[attr] - limit[attr]) / scale);
+ this._noDirty = true;
+ this.chart.render();
+ this._noDirty = false;
+ if(!this._tracker){
+ this.initTrack();
+ }
+ },
+ _trackMove: function(){
+ // let's update the selector
+ this._updateIndicator(this.pageCoord);
+ // if we reached that point once, then we don't stop until mouse up
+ if(this._initTrackPhase){
+ this._initTrackPhase = false;
+ this._tracker = setInterval(lang.hitch(this, this._trackMove), 100);
+ }
+ },
+ initTrack: function(){
+ this._initTrackPhase = true;
+ this._tracker = setTimeout(lang.hitch(this, this._trackMove), 500);
+ },
+ stopTrack: function(){
+ if(this._tracker){
+ if(this._initTrackPhase){
+ clearTimeout(this._tracker);
+ }else{
+ clearInterval(this._tracker);
+ }
+ this._tracker = null;
+ }
+ },
+ render: function(){
+ if(!this.isDirty()){
+ return;
+ }
+
+ this.cleanGroup();
+
+ if (!this.pageCoord){
+ return;
+ }
+
+ this._updateIndicator(this.pageCoord, this.secondCoord);
+ },
+ _updateIndicator: function(cp1, cp2){
+ var inter = this.inter, plot = inter.plot, v = inter.opt.vertical;
+ var hAxis = this.chart.getAxis(plot.hAxis), vAxis = this.chart.getAxis(plot.vAxis);
+ var hn = hAxis.name, vn = vAxis.name, hb = hAxis.getScaler().bounds, vb = vAxis.getScaler().bounds;
+ var attr = v?"x":"y", n = v?hn:vn, bounds = v?hb:vb;
+
+ // sort data point
+ if(cp2){
+ var tmp;
+ if(v){
+ if(cp1.x>cp2.x){
+ tmp = cp2;
+ cp2 = cp1;
+ cp1 = tmp;
+ }
+ }else{
+ if(cp1.y>cp2.y){
+ tmp = cp2;
+ cp2 = cp1;
+ cp1 = tmp;
+ }
+ }
+ }
+
+ var cd1 = plot.toData(cp1), cd2;
+ if(cp2){
+ cd2 = plot.toData(cp2);
+ }
+
+ var o = {};
+ o[hn] = hb.from;
+ o[vn] = vb.from;
+ var min = plot.toPage(o);
+ o[hn] = hb.to;
+ o[vn] = vb.to;
+ var max = plot.toPage(o);
+
+ if(cd1[n] < bounds.from){
+ // do not autoscroll if dual indicator
+ if(!cd2 && inter.opt.autoScroll){
+ this._updateVisibility(cp1, min, attr);
+ return;
+ }else{
+ cp1[attr] = min[attr];
+ }
+ // cp1 might have changed, let's update cd1
+ cd1 = plot.toData(cp1);
+ }else if(cd1[n] > bounds.to){
+ if(!cd2 && inter.opt.autoScroll){
+ this._updateVisibility(cp1, max, attr);
+ return;
+ }else{
+ cp1[attr] = max[attr];
+ }
+ // cp1 might have changed, let's update cd1
+ cd1 = plot.toData(cp1);
+ }
+
+ var c1 = this._getData(cd1, attr, v), c2;
+
+ if(c1.y == null){
+ // we have no data for that point let's just return
+ return;
+ }
+
+ if(cp2){
+ if(cd2[n] < bounds.from){
+ cp2[attr] = min[attr];
+ cd2 = plot.toData(cp2);
+ }else if(cd2[n] > bounds.to){
+ cp2[attr] = max[attr];
+ cd2 = plot.toData(cp2);
+ }
+ c2 = this._getData(cd2, attr, v);
+ if(c2.y == null){
+ // we have no data for that point let's pretend we have a single touch point
+ cp2 = null;
+ }
+ }
+
+ var t1 = this._renderIndicator(c1, cp2?1:0, hn, vn, min, max);
+ if(cp2){
+ var t2 = this._renderIndicator(c2, 2, hn, vn, min, max);
+ var delta = v?c2.y-c1.y:c2.x-c1.y;
+ var text = inter.opt.labelFunc?inter.opt.labelFunc(c1, c2, inter.opt.fixed, inter.opt.precision):
+ (dcpc.getLabel(delta, inter.opt.fixed, inter.opt.precision)+" ("+dcpc.getLabel(100*delta/(v?c1.y:c1.x), true, 2)+"%)");
+ this._renderText(text, inter, this.chart.theme, v?(t1.x+t2.x)/2:t1.x, v?t1.y:(t1.y+t2.y)/2, c1, c2);
+ };
+
+ },
+ _renderIndicator: function(coord, index, hn, vn, min, max){
+ var t = this.chart.theme, c = this.chart.getCoords(), inter = this.inter, plot = inter.plot, v = inter.opt.vertical;
+
+ var mark = {};
+ mark[hn] = coord.x;
+ mark[vn] = coord.y;
+ mark = plot.toPage(mark);
+
+ var cx = mark.x - c.x, cy = mark.y - c.y;
+ var x1 = v?cx:min.x - c.x, y1 = v?min.y - c.y:cy, x2 = v?x1:max.x - c.x, y2 = v?max.y - c.y:y1;
+ var sh = inter.opt.lineShadow?inter.opt.lineShadow:t.indicator.lineShadow,
+ ls = inter.opt.lineStroke?inter.opt.lineStroke:t.indicator.lineStroke,
+ ol = inter.opt.lineOutline?inter.opt.lineOutline:t.indicator.lineOutline;
+ if(sh){
+ this.group.createLine({x1: x1 + sh.dx, y1: y1 + sh.dy, x2: x2 + sh.dx, y2: y2 + sh.dy}).setStroke(sh);
+ }
+ if(ol){
+ ol = dcpc.makeStroke(ol);
+ ol.width = 2 * ol.width + ls.width;
+ this.group.createLine({x1: x1, y1: y1, x2: x2, y2: y2}).setStroke(ol);
+ }
+ this.group.createLine({x1: x1, y1: y1, x2: x2, y2: y2}).setStroke(ls);
+
+ var ms = inter.opt.markerSymbol?inter.opt.markerSymbol:t.indicator.markerSymbol,
+ path = "M" + cx + " " + cy + " " + ms;
+ sh = inter.opt.markerShadow?inter.opt.markerShadow:t.indicator.markerShadow;
+ ls = inter.opt.markerStroke?inter.opt.markerStroke:t.indicator.markerStroke;
+ ol = inter.opt.markerOutline?inter.opt.markerOutline:t.indicator.markerOutline;
+ if(sh){
+ var sp = "M" + (cx + sh.dx) + " " + (cy + sh.dy) + " " + ms;
+ this.group.createPath(sp).setFill(sh.color).setStroke(sh);
+ }
+ if(ol){
+ ol = dcpc.makeStroke(ol);
+ ol.width = 2 * ol.width + ls.width;
+ this.group.createPath(path).setStroke(ol);
+ }
+
+ var shape = this.group.createPath(path);
+ var sf = this._shapeFill(inter.opt.markerFill?inter.opt.markerFill:t.indicator.markerFill, shape.getBoundingBox());
+ shape.setFill(sf).setStroke(ls);
+
+ if(index==0){
+ var text = inter.opt.labelFunc?inter.opt.labelFunc(coord, null, inter.opt.fixed, inter.opt.precision):
+ dcpc.getLabel(v?coord.y:coord.x, inter.opt.fixed, inter.opt.precision);
+ this._renderText(text, inter, t, v?x1:x2+5, v?y2+5:y1, coord);
+ }else{
+ return v?{x: x1, y: y2+5}:{x: x2+5, y: y1};
+ }
+
+ },
+ _renderText: function(text, inter, t, x, y, c1, c2){
+ var label = dcac.createText.gfx(
+ this.chart,
+ this.group,
+ x, y,
+ "middle",
+ text, inter.opt.font?inter.opt.font:t.indicator.font, inter.opt.fontColor?inter.opt.fontColor:t.indicator.fontColor);
+ var b = getBoundingBox(label);
+ b.x-=2; b.y-=1; b.width+=4; b.height+=2; b.r = inter.opt.radius?inter.opt.radius:t.indicator.radius;
+ sh = inter.opt.shadow?inter.opt.shadow:t.indicator.shadow;
+ ls = inter.opt.stroke?inter.opt.stroke:t.indicator.stroke;
+ ol = inter.opt.outline?inter.opt.outline:t.indicator.outline;
+ if(sh){
+ this.group.createRect(b).setFill(sh.color).setStroke(sh);
+ }
+ if(ol){
+ ol = dcpc.makeStroke(ol);
+ ol.width = 2 * ol.width + ls.width;
+ this.group.createRect(b).setStroke(ol);
+ }
+ var f = inter.opt.fillFunc?inter.opt.fillFunc(c1, c2):(inter.opt.fill?inter.opt.fill:t.indicator.fill);
+ this.group.createRect(b).setFill(this._shapeFill(f, b)).setStroke(ls);
+ label.moveToFront();
+ },
+ _getData: function(cd, attr, v){
+ // we need to find which actual data point is "close" to the data value
+ var data = this.chart.getSeries(this.inter.opt.series).data;
+ // let's consider data are sorted because anyway rendering will be "weird" with unsorted data
+ // i is an index in the array, which is different from a x-axis value even for index based data
+ var i, r, l = data.length;
+ for (i = 0; i < l; ++i){
+ r = data[i];
+ if(r == null){
+ // move to next item
+ }else if(typeof r == "number"){
+ if(i + 1 > cd[attr]){
+ break;
+ }
+ }else if(r[attr] > cd[attr]){
+ break;
+ }
+ }
+ var x,y,px,py;
+ if(typeof r == "number"){
+ x = i+1;
+ y = r;
+ if(i>0){
+ px = i;
+ py = data[i-1];
+ }
+ }else{
+ x = r.x;
+ y = r.y;
+ if(i>0){
+ px = data[i-1].x;
+ py = data[i-1].y;
+ }
+ }
+ if(i>0){
+ var m = v?(x+px)/2:(y+py)/2;
+ if(cd[attr]<=m){
+ x = px;
+ y = py;
+ }
+ }
+ return {x: x, y: y};
+ },
+ 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);
+ // we always want to be above regular plots and not clipped
+ this.group.moveToFront();
+ 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;
+ 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(dcpc.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._noDirty && (this.dirty || this.inter.plot.isDirty());
+ }
+ });
+});