1 | define(["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 | |
---|
10 | return 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 | }); |
---|