diff options
Diffstat (limited to 'js/dojo/dojox/charting/action2d')
| -rw-r--r-- | js/dojo/dojox/charting/action2d/Base.js | 37 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/ChartAction.js | 39 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/Highlight.js | 127 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/Magnify.js | 117 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/MouseIndicator.js | 222 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/MouseZoomAndPan.js | 246 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/MoveSlice.js | 122 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/PlotAction.js | 79 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/Shake.js | 109 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/Tooltip.js | 167 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/TouchIndicator.js | 233 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/TouchZoomAndPan.js | 250 | ||||
| -rw-r--r-- | js/dojo/dojox/charting/action2d/_IndicatorElement.js | 371 |
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()); + } + }); +}); |
