source: Dev/branches/rest-dojo-ui/client/dojox/gauges/AnalogGauge.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 11.0 KB
Line 
1define(["dojo/_base/kernel","dojo/_base/declare","dojo/_base/array","dojo/_base/lang","dojo/_base/html","dojo/_base/event",
2                "dojox/gfx", "./_Gauge","./AnalogLineIndicator", "dojo/dom-geometry"],
3        function(dojo, declare, arr, lang, html, event,
4                        gfx, Gauge, AnalogLineIndicator, domGeometry) {
5
6/*=====
7        Gauge = dojox.gauges._Gauge;
8=====*/
9
10return declare("dojox.gauges.AnalogGauge",Gauge,{
11        // summary:
12        //              a gauge built using the dojox.gfx package.
13        //
14        // description:
15        //              using dojo.gfx (and thus either SVG or VML based on what is supported), this widget
16        //              builds a gauge component, used to display numerical data in a familiar format
17        //
18        // example:
19        //      |       <script type="text/javascript">
20        //      |               require(["dojox/gauges/AnalogGauge"]);
21        //      |       </script>
22        //      |
23        //      |       <div    dojoType="dojox.gauges.AnalogGauge"
24        //      |                       id="testGauge"
25        //      |                       width="300"
26        //      |                       height="200"
27        //      |                       cx=150
28        //      |                       cy=175
29        //      |                       radius=125
30        //      |                       image="gaugeOverlay.png"
31        //      |                       imageOverlay="false"
32        //      |                       imageWidth="280"
33        //      |                       imageHeight="155"
34        //      |                       imageX="12"
35        //      |                       imageY="38">
36        //      |       </div>
37
38        // startAngle: Number
39        //              angle (in degrees) for start of gauge (default is -90)
40        startAngle: -90,
41
42        // endAngle: Number
43        //              angle (in degrees) for end of gauge (default is 90)
44        endAngle: 90,
45
46        // cx: Number
47        //              center of gauge x coordinate (default is gauge width / 2)
48        cx: 0,
49
50        // cy: Number
51        //              center of gauge x coordinate (default is gauge height / 2)
52        cy: 0,
53
54        // radius: Number
55        //              radius of gauge (default is smaller of cx-25 or cy-25)
56        radius: 0,
57       
58        // orientation: String
59        //              The orientation of the gauge. The value can be 'clockwise' or 'cclockwise' (default is 'clockwise')
60        orientation: "clockwise",
61
62        // _defaultIndicator: dojox.gauges._Indicator
63        //              override of dojox.gauges._Gauge._defaultIndicator
64        _defaultIndicator: AnalogLineIndicator,
65
66        startup: function(){
67                // handle settings from HTML by making sure all the options are
68                // converted correctly to numbers and that we calculate defaults
69                // for cx, cy and radius
70                // also connects mouse handling events
71
72                if(this.getChildren){
73                        arr.forEach(this.getChildren(), function(child){ child.startup(); });
74                }
75
76                this.startAngle = Number(this.startAngle);
77                this.endAngle = Number(this.endAngle);
78
79                this.cx = Number(this.cx);
80                if(!this.cx){this.cx = this.width/2;}
81                this.cy = Number(this.cy);
82                if(!this.cy){this.cy = this.height/2;}
83                this.radius = Number(this.radius);
84                if(!this.radius){this.radius = Math.min(this.cx,this.cy) - 25;}
85       
86
87                this.inherited(arguments);
88        },
89
90        _getAngle: function(/*Number*/value){
91                // summary:
92                //              This is a helper function used to determine the angle that represents
93                //              a given value on the gauge
94                // value:       Number
95                //              A value to be converted to an angle for this gauge.
96               
97                var v = Number(value);
98                var angle;
99                if (value == null || isNaN(v) || v <= this.min)
100                        angle = this._mod360(this.startAngle);
101                else
102                        if (v >= this.max)
103                                angle = this._mod360(this.endAngle);
104                        else {
105                                var startAngle = this._mod360(this.startAngle);
106                                var relativeValue = (v - this.min);
107                                if (this.orientation != 'clockwise')
108                                        relativeValue = -relativeValue;
109
110                                angle = this._mod360(startAngle + this._getAngleRange() * relativeValue / Math.abs(this.min - this.max));
111                        }
112
113                return angle;
114        },
115
116        _getValueForAngle: function(/*Number*/angle){
117                // summary:
118                //              This is a helper function used to determine the value represented by a
119                //              given angle on the gauge
120                // angle:       Number
121                //              A angle to be converted to a value for this gauge.
122                var startAngle = this._mod360(this.startAngle);
123                var endAngle = this._mod360(this.endAngle);
124
125                if (!this._angleInRange(angle)){
126
127                        var min1 = this._mod360(startAngle - angle);
128                        var min2 = 360 - min1;
129                        var max1 = this._mod360(endAngle - angle);
130                        var max2 = 360 - max1;
131                        if (Math.min(min1, min2) < Math.min(max1, max2))
132                                return this.min;
133                        else
134                                return this.max;
135                }
136                else {
137                        var range = Math.abs(this.max - this.min);
138                        var relativeAngle = this._mod360(this.orientation == 'clockwise' ?
139                                (angle - startAngle): (-angle + startAngle));
140                        return this.min + range * relativeAngle / this._getAngleRange();
141                }
142        },
143
144        _getAngleRange: function(){
145                // summary:
146                //              This is a helper function that returns the angle range
147                //      from startAngle to endAngle according to orientation.
148                var range;
149                var startAngle = this._mod360(this.startAngle);
150                var endAngle = this._mod360(this.endAngle);
151                if (startAngle == endAngle)
152                        return 360;
153                if (this.orientation == 'clockwise'){
154                        if (endAngle < startAngle)
155                                range = 360 - (startAngle - endAngle);
156                        else
157                                range = endAngle - startAngle;
158                }
159                else {
160                        if (endAngle < startAngle)
161                                range = startAngle - endAngle;
162                        else
163                                range = 360 - (endAngle - startAngle);
164                }
165                return range;
166        },
167       
168        _angleInRange: function(value){
169                // summary:
170                //              Test if the angle value is in the startAngle/endAngle range
171                var startAngle = this._mod360(this.startAngle);
172                var endAngle = this._mod360(this.endAngle);
173                if (startAngle == endAngle)
174                        return true;
175                        value = this._mod360(value);
176                if (this.orientation == "clockwise"){
177                        if (startAngle < endAngle)
178                                return value >= startAngle && value <= endAngle;
179                        else
180                                return !(value > endAngle && value < startAngle);
181                }
182                else {
183                        if (startAngle < endAngle)
184                                return !(value > startAngle && value < endAngle);
185                        else
186                                return value >= endAngle && value <= startAngle;
187                }
188        },
189       
190        _isScaleCircular: function(){
191                // summary:
192                //              internal method to check if the scale is fully circular
193                return (this._mod360(this.startAngle) == this._mod360(this.endAngle));
194        },
195       
196        _mod360:function(v){
197                // summary:
198                //     returns the angle between 0 and 360;
199                while (v>360) v = v - 360;
200                while (v<0) v = v + 360;
201                return v;
202        },
203
204        _getRadians: function(/*Number*/angle){
205                // summary:
206                //              This is a helper function than converts degrees to radians
207                // angle:       Number
208                //              An angle, in degrees, to be converted to radians.
209                return angle*Math.PI/180;
210        },
211
212        _getDegrees: function(/*Number*/radians){
213                // summary:
214                //              This is a helper function that converts radians to degrees
215                // radians:     Number
216                //              An angle, in radians, to be converted to degrees.
217                return radians*180/Math.PI;
218        },
219
220
221        drawRange: function(/*dojox.gfx.Group*/ group, /*Object*/range){
222                // summary:
223                //              This function is used to draw (or redraw) a range
224                // description:
225                //              Draws a range (colored area on the background of the gauge)
226                //              based on the given arguments.
227                // group:
228                //              The GFX group where the range must be drawn.
229                // range:
230                //              A range is a dojox.gauges.Range or an object
231                //              with similar parameters (low, high, hover, etc.).
232                var path;
233                if(range.shape){
234                        range.shape.parent.remove(range.shape);
235                        range.shape = null;
236                }
237                var a1, a2;
238                if((range.low == this.min) && (range.high == this.max) && ((this._mod360(this.endAngle) == this._mod360(this.startAngle)))){
239                        path = group.createCircle({cx: this.cx, cy: this.cy, r: this.radius});
240                }else{
241                       
242                       
243                        a1 = this._getRadians(this._getAngle(range.low));
244                        a2 = this._getRadians(this._getAngle(range.high));
245                        if (this.orientation == 'cclockwise')
246                        {
247                                var a = a2;
248                                a2 = a1;
249                                a1 = a;
250                        }
251                       
252                        var x1=this.cx+this.radius*Math.sin(a1),
253                                y1=this.cy-this.radius*Math.cos(a1),
254                                x2=this.cx+this.radius*Math.sin(a2),
255                                y2=this.cy-this.radius*Math.cos(a2),
256                                big=0
257                        ;
258                       
259                        var arange;
260                        if (a1<=a2)
261                           arange = a2-a1;
262                        else
263                           arange = 2*Math.PI-a1+a2;
264                        if(arange>Math.PI){big=1;}
265                       
266                        path = group.createPath();
267                        if(range.size){
268                                path.moveTo(this.cx+(this.radius-range.size)*Math.sin(a1),
269                                                        this.cy-(this.radius-range.size)*Math.cos(a1));
270                        }else{
271                                path.moveTo(this.cx,this.cy);
272                        }
273                        path.lineTo(x1,y1);
274                        path.arcTo(this.radius,this.radius,0,big,1,x2,y2);
275                        if(range.size){
276                                path.lineTo(this.cx+(this.radius-range.size)*Math.sin(a2),
277                                                        this.cy-(this.radius-range.size)*Math.cos(a2));
278                                path.arcTo((this.radius-range.size),(this.radius-range.size),0,big,0,
279                                                        this.cx+(this.radius-range.size)*Math.sin(a1),
280                                                        this.cy-(this.radius-range.size)*Math.cos(a1));
281                        }
282                        path.closePath();
283                }
284
285                if(lang.isArray(range.color) || lang.isString(range.color)){
286                        path.setStroke({color: range.color});
287                        path.setFill(range.color);
288                }else if(range.color.type){
289                        // Color is a gradient
290                        a1 = this._getRadians(this._getAngle(range.low));
291                        a2 = this._getRadians(this._getAngle(range.high));
292                        range.color.x1 = this.cx+(this.radius*Math.sin(a1))/2;
293                        range.color.x2 = this.cx+(this.radius*Math.sin(a2))/2;
294                        range.color.y1 = this.cy-(this.radius*Math.cos(a1))/2;
295                        range.color.y2 = this.cy-(this.radius*Math.cos(a2))/2;
296                        path.setFill(range.color);
297                        path.setStroke({color: range.color.colors[0].color});
298                }else if (gfx.svg){
299                        // We've defined a style rather than an explicit color
300                        path.setStroke({color: "green"});       // Arbitrary color, just have to indicate
301                        path.setFill("green");                          // that we want it filled
302                        path.getEventSource().setAttribute("class", range.color.style);
303                }
304               
305                path.connect("onmouseover", lang.hitch(this, this._handleMouseOverRange, range));
306                path.connect("onmouseout", lang.hitch(this, this._handleMouseOutRange, range));
307       
308                range.shape = path;
309        },
310
311        getRangeUnderMouse: function(/*Object*/e){
312                // summary:
313                //              Determines which range the mouse is currently over
314                // e:   Object
315                //              The event object as received by the mouse handling functions below.
316                var range = null,
317                        pos = domGeometry.getContentBox(this.gaugeContent),
318                        x = e.clientX - pos.x,
319                        y = e.clientY - pos.y,
320                        r = Math.sqrt((y - this.cy)*(y - this.cy) + (x - this.cx)*(x - this.cx))
321                ;
322                if(r < this.radius){
323                        var angle = this._getDegrees(Math.atan2(y - this.cy, x - this.cx) + Math.PI/2),
324                        //if(angle > this.endAngle){angle = angle - 360;}
325                                value = this._getValueForAngle(angle)
326                        ;
327                        if(this._rangeData){
328                                for(var i=0; (i<this._rangeData.length) && !range; i++){
329                                        if((Number(this._rangeData[i].low) <= value) && (Number(this._rangeData[i].high) >= value)){
330                                                range = this._rangeData[i];
331                                        }
332                                }
333                        }
334                }
335                return range;
336        },
337
338        _dragIndicator: function(/*Object*/ widget, /*Object*/ e){
339                // summary:
340                //              Handles the dragging of an indicator to the event position, including moving/re-drawing
341                //              get angle for mouse position
342                this._dragIndicatorAt(widget, e.pageX, e.pageY);
343                event.stop(e);
344        },
345               
346        _dragIndicatorAt: function(/*Object*/ widget, x,y){
347                // summary:
348                //              Handles the dragging of an indicator to a specific position, including moving/re-drawing
349                //              get angle for mouse position
350                var pos = domGeometry.position(widget.gaugeContent, true),
351                        xf = x - pos.x,
352                        yf = y - pos.y,
353                        angle = widget._getDegrees(Math.atan2(yf - widget.cy, xf - widget.cx) + Math.PI/2);
354               
355                // get value and restrict to our min/max
356                var value = widget._getValueForAngle(angle);
357                value = Math.min(Math.max(value, widget.min), widget.max);
358                // update the indicator
359                widget._drag.value = widget._drag.currentValue = value;
360                // callback
361                widget._drag.onDragMove(widget._drag);
362                // rotate indicator
363                widget._drag.draw(this._indicatorsGroup, true);
364                widget._drag.valueChanged();
365        }
366
367});
368});
Note: See TracBrowser for help on using the repository browser.