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