source: Dev/trunk/src/client/dojox/charting/action2d/_IndicatorElement.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 8.0 KB
Line 
1define(["dojo/_base/lang", "dojo/_base/array", "dojo/_base/declare", "../plot2d/Indicator",
2        "dojo/has", "../plot2d/common", "../axis2d/common", "dojox/gfx"],
3        function(lang, array, declare, Indicator, has){
4
5        var getXYCoordinates = function(v, values, data){
6                var c2, c1 = v?{ x: values[0], y : data[0][0] } :
7                                           { x : data[0][0], y : values[0] };
8                if(values.length > 1){
9                        c2 = v?{ x: values[1], y : data[1][0] } :
10                                   { x : data[1][0], y : values[1] };
11                }
12                return [c1, c2];
13        };
14
15        var _IndicatorElement = declare("dojox.charting.action2d._IndicatorElement", Indicator, {
16                // summary:
17                //              Internal element used by indicator actions.
18                // tags:
19                //              private
20                constructor: function(chart, kwArgs){
21                        if(!kwArgs){ kwArgs = {}; }
22                        this.inter = kwArgs.inter;
23                },
24                _updateVisibility: function(cp, limit, attr){
25                        var axis = attr=="x"?this._hAxis:this._vAxis;
26                        var scale = axis.getWindowScale();
27                        this.chart.setAxisWindow(axis.name, scale, axis.getWindowOffset() + (cp[attr] - limit[attr]) / scale);
28                        this._noDirty = true;
29                        this.chart.render();
30                        this._noDirty = false;
31                        this._initTrack();
32                },
33                _trackMove: function(){
34                        // let's update the selector
35                        this._updateIndicator(this.pageCoord);
36                        // if we reached that point once, then we don't stop until mouse up
37                        // use a recursive setTimeout to avoid intervals that might get backed up
38                        this._tracker = setTimeout(lang.hitch(this, this._trackMove), 100);
39                },
40                _initTrack: function(){
41                        if(!this._tracker){
42                                this._tracker = setTimeout(lang.hitch(this, this._trackMove), 500);
43                        }
44                },
45                stopTrack: function(){
46                        if(this._tracker){
47                                clearTimeout(this._tracker);
48                                this._tracker = null;
49                        }
50                },
51                render: function(){
52                        if(!this.isDirty()){
53                                return;
54                        }
55
56                        var inter = this.inter, plot = inter.plot, v = inter.opt.vertical;
57
58                        this.opt.offset = inter.opt.offset || (v?{ x:0 , y: 5}: { x: 5, y: 0});
59
60                        if(inter.opt.labelFunc){
61                                // adapt to indicator labelFunc format
62                                this.opt.labelFunc = function(index, values, data, fixed, precision){
63                                        var coords = getXYCoordinates(v, values, data);
64                                        return inter.opt.labelFunc(coords[0], coords[1], fixed, precision);
65                                };
66                        }
67                        if(inter.opt.fillFunc){
68                                // adapt to indicator fillFunc format
69                                this.opt.fillFunc = function(index, values, data){
70                                        var coords = getXYCoordinates(v, values, data);
71                                        return inter.opt.fillFunc(coords[0], coords[1]);
72                                };
73                        }
74
75                        this.opt = lang.delegate(inter.opt, this.opt);
76
77                        if(!this.pageCoord){
78                                this.opt.values = null;
79                                this.inter.onChange({});
80                        }else{
81                                // let's create a fake coordinate to not block parent render method
82                                // actual coordinate will be computed in _updateCoordinates
83                                this.opt.values = [];
84                                this.opt.labels = this.secondCoord?"trend":"markers";
85                        }
86
87                        // take axis on the interactor plot and forward them onto the indicator plot
88                        this.hAxis = plot.hAxis;
89                        this.vAxis = plot.vAxis;
90
91                        this.inherited(arguments);
92                },
93                _updateIndicator: function(){
94                        var coordinates = this._updateCoordinates(this.pageCoord, this.secondCoord);
95                        if(coordinates.length > 1){
96                                var v = this.opt.vertical;
97                                this._data= [];
98                                this.opt.values = [];
99                                array.forEach(coordinates, function(value){
100                                        if(value){
101                                                this.opt.values.push(v?value.x:value.y);
102                                                this._data.push([v?value.y:value.x]);
103                                        }
104                                }, this);
105                        }else{
106                                this.inter.onChange({});
107                                return;
108                        }
109                        this.inherited(arguments);
110                },
111                _renderText: function(g, text, t, x, y, index, values, data){
112                        // render only if labels is true
113                        if(this.inter.opt.labels){
114                                this.inherited(arguments);
115                        }
116                        // send the event in all cases
117                        var coords = getXYCoordinates(this.opt.vertical, values, data);
118                        this.inter.onChange({
119                                start: coords[0],
120                                end: coords[1],
121                                label: text
122                        });
123                },
124                _updateCoordinates: function(cp1, cp2){
125                        // chart mirroring starts
126                        if(has("dojo-bidi")){
127                                this._checkXCoords(cp1, cp2);
128                        }
129                        // chart mirroring ends
130                        var inter = this.inter, plot = inter.plot, v = inter.opt.vertical;
131                        var hAxis = this.chart.getAxis(plot.hAxis), vAxis = this.chart.getAxis(plot.vAxis);
132                        var hn = hAxis.name, vn = vAxis.name, hb = hAxis.getScaler().bounds, vb = vAxis.getScaler().bounds;
133                        var attr = v?"x":"y", n = v?hn:vn, bounds = v?hb:vb;
134
135                        // sort data point
136                        if(cp2){
137                                var tmp;
138                                if(v){
139                                        if(cp1.x > cp2.x){
140                                                tmp = cp2;
141                                                cp2 = cp1;
142                                                cp1 = tmp;
143                                        }
144                                }else{
145                                        if(cp1.y > cp2.y){
146                                                tmp = cp2;
147                                                cp2 = cp1;
148                                                cp1 = tmp;
149                                        }
150                                }
151                        }
152
153                        var cd1 = plot.toData(cp1), cd2;
154                        if(cp2){
155                                cd2 = plot.toData(cp2);
156                        }
157
158                        var o = {};
159                        o[hn] = hb.from;
160                        o[vn] = vb.from;
161                        var min = plot.toPage(o);
162                        o[hn] = hb.to;
163                        o[vn] = vb.to;
164                        var max = plot.toPage(o);
165
166                        if(cd1[n] < bounds.from){
167                                // do not autoscroll if dual indicator
168                                if(!cd2 && inter.opt.autoScroll && !inter.opt.mouseOver){
169                                        this._updateVisibility(cp1, min, attr);
170                                        return [];
171                                }else{
172                                        if(inter.opt.mouseOver){
173                                                return[];
174                                        }
175                                        cp1[attr] = min[attr];
176                                }
177                                // cp1 might have changed, let's update cd1
178                                cd1 = plot.toData(cp1);
179                        }else if(cd1[n] > bounds.to){
180                                if(!cd2 && inter.opt.autoScroll && !inter.opt.mouseOver){
181                                        this._updateVisibility(cp1, max, attr);
182                                        return [];
183                                }else{
184                                        if(inter.opt.mouseOver){
185                                                return[];
186                                        }
187                                        cp1[attr] = max[attr];
188                                }
189                                // cp1 might have changed, let's update cd1
190                                cd1 = plot.toData(cp1);
191                        }
192
193                        var c1 = this._snapData(cd1, attr, v), c2;
194
195                        if(c1.y == null){
196                                // we have no data for that point let's just return
197                                return [];
198                        }
199
200                        if(cp2){
201                                if(cd2[n] < bounds.from){
202                                        cp2[attr] = min[attr];
203                                        cd2 = plot.toData(cp2);
204                                }else if(cd2[n] > bounds.to){
205                                        cp2[attr] = max[attr];
206                                        cd2 = plot.toData(cp2);
207                                }
208                                c2 = this._snapData(cd2, attr, v);
209                                if(c2.y == null){
210                                        // we have no data for that point let's pretend we have a single touch point
211                                        c2 = null;
212                                }
213                        }
214
215                        return [c1, c2];
216                },
217                _snapData: function(cd, attr, v){
218                        // we need to find which actual data point is "close" to the data value
219                        var data = this.chart.getSeries(this.inter.opt.series).data;
220                        // let's consider data are sorted because anyway rendering will be "weird" with unsorted data
221                        // i is an index in the array, which is different from a x-axis value even for index based data
222                        var i, r, l = data.length;
223                        // first let's find which data index we are in
224                        for (i = 0; i < l; ++i){
225                                r = data[i];
226                                if(r == null){
227                                        // move to next item
228                                }else if(typeof r == "number"){
229                                        if(i + 1 > cd[attr]){
230                                                break;
231                                        }
232                                }else if(r[attr] > cd[attr]){
233                                        break;
234                                }
235                        }
236                        var x, y, px, py;
237                        if(typeof r == "number"){
238                                x = i+1;
239                                y = r;
240                                if(i > 0){
241                                        px = i;
242                                        py = data[i-1];
243                                }
244                        }else{
245                                x = r.x;
246                                y = r.y;
247                                if(i > 0){
248                                        px = data[i-1].x;
249                                        py = data[i-1].y;
250                                }
251                        }
252                        if(i > 0){
253                                var m = v?(x+px)/2:(y+py)/2;
254                                if(cd[attr]<=m){
255                                        x = px;
256                                        y = py;
257                                }
258                        }
259                        return {x: x, y: y};
260                },
261                cleanGroup: function(creator){
262                        // summary:
263                        //              Clean any elements (HTML or GFX-based) out of our group, and create a new one.
264                        // creator: dojox/gfx/Surface?
265                        //              An optional surface to work with.
266                        // returns: dojox/charting/Element
267                        //              A reference to this object for functional chaining.
268                        this.inherited(arguments);
269                        // we always want to be above regular plots and not clipped
270                        this.group.moveToFront();
271                        return this;    //      dojox/charting/Element
272                },
273                isDirty: function(){
274                        // summary:
275                        //              Return whether or not this plot needs to be redrawn.
276                        // returns: Boolean
277                        //              If this plot needs to be rendered, this will return true.
278                        return !this._noDirty && (this.dirty || this.inter.plot.isDirty());
279                }
280        });
281        if(has("dojo-bidi")){
282                _IndicatorElement.extend({
283                        _checkXCoords: function(cp1, cp2){
284                                if(this.chart.isRightToLeft()){
285                                        if(cp1){
286                                                cp1.x = this.chart.dim.width + (this.chart.offsets.l - this.chart.offsets.r) - cp1.x;
287                                        }
288                                        if(cp2){
289                                                cp2.x = this.chart.dim.width + (this.chart.offsets.l - this.chart.offsets.r) - cp2.x;
290                                        }
291                                }                       
292                        }                       
293                });
294        }
295        return _IndicatorElement;
296});
Note: See TracBrowser for help on using the repository browser.