]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/dial/dial.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / dial / dial.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('dial', function(Y) {
9
10 /**
11  * Create a circular dial value range input visualized as a draggable handle on a
12  * background element.
13  * 
14  * @module dial
15  */
16         var supportsVML = false,
17         testVMLNode;
18
19         if (Y.UA.ie && Y.UA.ie < 9){
20         supportsVML = true;
21         }
22
23     var Lang = Y.Lang,
24         Widget = Y.Widget,
25         Node = Y.Node;
26
27         /**
28          * Create a dial to represent an input control capable of representing a
29          * series of intermediate states based on the position of the Dial's handle.
30          * These states are typically aligned to a value algorithm whereby the angle of the handle's
31          * position corresponds to a given value.
32          *
33          * @class Dial
34          * @extends Widget
35          * @param config {Object} Configuration object
36          * @constructor
37          */
38     function Dial(config) {
39         Dial.superclass.constructor.apply(this, arguments);
40     }
41
42     // Y.Dial static properties
43
44     /**
45      * The identity of the widget.
46      *
47      * @property Dial.NAME
48      * @type String
49      * @default 'dial'
50      * @readOnly
51      * @protected
52      * @static
53      */
54     Dial.NAME = "dial";
55
56     /**
57      * Static property used to define the default attribute configuration of
58      * the Widget.
59      *
60      * @property Dial.ATTRS
61      * @type {Object}
62      * @protected
63      * @static
64      */
65     Dial.ATTRS = {
66
67                 /**
68          * minimum value allowed
69          *
70          * @attribute min
71          * @type {Number}
72          * @default -220
73          */
74         min : {
75             value:-220
76         },
77
78                 /**
79          * maximum value allowed
80          *
81          * @attribute max
82          * @type {Number}
83          * @default 220
84          */
85         max : {
86             value:220
87         },
88
89                 /**
90          * diameter of the circular background object.
91                  * Other objects scale accordingly
92                  * Set this only before rendering.
93          *
94          * @attribute diameter
95          * @type {Number} the number of px in diameter
96          * @default 100
97                  * @writeOnce
98          */
99                 diameter : {
100                         value:100
101                 },
102
103                 /**
104                  * initial value of the Dial
105          *
106          * @attribute value
107          * @type {Number}
108          * @default 0
109          */
110         value : {
111             value:0,
112             validator: function(val) {
113                 return this._validateValue(val);
114             }
115         },
116                 
117                 /**
118                  * amount to increment/decrement the dial value
119                  * when the arrow up/down/left/right keys are pressed
120          *
121          * @attribute minorStep
122          * @type {Number}
123          * @default 1
124          */
125         minorStep : {
126             value:1
127         },
128
129                 /**
130                  * amount to increment/decrement the dial value
131                  * when the page up/down keys are pressed
132          *
133          * @attribute majorStep
134          * @type {Number}
135          * @default 10
136          */
137         majorStep : {
138             value:10
139         },
140
141                 /**
142                  * number of value increments in one 360 degree revolution 
143                  * of the handle around the dial
144          *
145          * @attribute stepsPerRev
146          * @type {Number}
147          * @default 100
148          */
149                 stepsPerRev : {
150                         value:100
151                 },
152
153                 /**
154                  * number of decimal places of accuracy in the value 
155          *
156          * @attribute decimalPlaces
157          * @type {Number}
158          * @default 0
159          */
160                 decimalPlaces : {
161                         value:0
162                 },
163
164                 /**
165                  * visible strings for the dial UI. This attribute is 
166                  * defined by the base Widget class but has an empty value. The
167                  * Dial is simply providing a default value for the attribute.
168                  * Gets localized strings in the current language
169          *
170          * @attribute strings
171          * @type {Object}
172          * @default {label: 'My label', resetStr: 'Reset', tooltipHandle: 'Drag to set value'}
173          */
174         strings: {
175                         valueFn: function () {
176                                 return Y.Intl.get('dial');
177                         }
178         },
179
180                 /**
181                  * distance from the center of the dial to the 
182                  * resting place of the center of the handle and marker. 
183                  * The value is a percent of the radius of the dial.
184          *
185          * @attribute handleDist
186          * @type {number}
187          * @default 0.75
188          */
189                 handleDist:{
190                         value:0.75
191                 }
192                 
193     };
194
195         /**
196          * returns a properly formed yui class name
197          *
198          * @function
199          * @param {String} string to be appended at the end of class name
200          * @return
201          * @private
202          */
203         function makeClassName(str) {
204                 return Y.ClassNameManager.getClassName(Dial.NAME, str);
205         }
206         
207     /**
208          * array of static constants used to identify the classname applied to the Dial DOM objects 
209          *
210      * @property Dial.CSS_CLASSES
211      * @type {Array}
212          * @private
213      * @static
214          */
215         Dial.CSS_CLASSES = {
216                 label : makeClassName("label"),
217                 labelString : makeClassName("label-string"),
218                 valueString : makeClassName("value-string"),
219                 northMark : makeClassName("north-mark"),
220                 ring : makeClassName('ring'),
221                 ringVml : makeClassName('ring-vml'),
222                 marker : makeClassName("marker"),
223                 markerUser : makeClassName("marker-user"),
224                 markerUserVml : makeClassName("marker-user-vml"),
225                 centerButton : makeClassName("center-button"),
226                 centerButtonVml : makeClassName('center-button-vml'),
227                 resetString : makeClassName("reset-str"),
228                 handle : makeClassName("handle"),
229                 handleUser : makeClassName("handle-user"),
230                 handleUserVml : makeClassName("handle-user-vml"),
231                 markerHidden : makeClassName("marker-hidden"),
232                 dragging : Y.ClassNameManager.getClassName("dd-dragging")
233         };
234     
235         
236     /* Static constants used to define the markup templates used to create Dial DOM elements */
237         var labelId = Dial.CSS_CLASSES.label + Y.guid(); //get this unique id once then use
238
239     /**
240      * template that will contain the Dial's label.
241      *
242      * @property Dial.LABEL_TEMPLATE
243      * @type {String}
244      * @default &lt;div>...&lt;/div>
245          * @protected
246      */
247         Dial.LABEL_TEMPLATE = '<div id="' + labelId + '" class="' + Dial.CSS_CLASSES.label + '"><span class="' + Dial.CSS_CLASSES.labelString + '">{label}</span><span class="' + Dial.CSS_CLASSES.valueString + '"></span></div>';
248
249         if(supportsVML === false){
250                 /**
251                  * template that will contain the Dial's background ring.
252                  *
253                  * @property Dial.RING_TEMPLATE
254                  * @type {String}
255                  * @default &lt;div class="[...-ring]">&lt;div class="[...-northMark]">&lt;/div>&lt;/div>
256                  * @protected
257                  */
258                 Dial.RING_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.ring + '"><div class="' + Dial.CSS_CLASSES.northMark + '"></div></div>';
259
260                 /**
261                  * template that will contain the Dial's current angle marker.
262                  *
263                  * @property Dial.MARKER_TEMPLATE
264                  * @type {String}
265                  * @default &lt;div class="[...-marker] [...-marker-hidden]">&lt;div class="[...-markerUser]">&lt;/div>&lt;/div>
266                  * @protected
267                  */
268                 Dial.MARKER_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.marker + ' ' + Dial.CSS_CLASSES.markerHidden + '"><div class="' + Dial.CSS_CLASSES.markerUser + '"></div></div>';
269
270                 /**
271                  * template that will contain the Dial's center button.
272                  *
273                  * @property Dial.CENTER_BUTTON_TEMPLATE
274                  * @type {String}
275                  * @default &lt;div class="[...-centerButton]">&lt;div class="[...-resetString]">' + Y.substitute('{resetStr}', Dial.ATTRS.strings.value) + '&lt;/div>&lt;/div>
276                  * @protected
277                  */
278                 Dial.CENTER_BUTTON_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.centerButton + '"><div class="' + Dial.CSS_CLASSES.resetString + '">{resetStr}</div></div>';
279
280                 /**
281                  * template that will contain the Dial's handle.
282                  *
283                  * @property Dial.HANDLE_TEMPLATE
284                  * @type {String}
285                  * @default &lt;div class="[...-handle]">&lt;div class="[...-handleUser]" aria-labelledby="' + labelId + '" aria-valuetext="" aria-valuemax="" aria-valuemin="" aria-valuenow="" role="slider"  tabindex="0">&lt;/div>&lt;/div>';// title="{tooltipHandle}"
286                  * @protected
287                  */
288                 Dial.HANDLE_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.handle + '"><div class="' + Dial.CSS_CLASSES.handleUser + '" aria-labelledby="' + labelId + '" aria-valuetext="" aria-valuemax="" aria-valuemin="" aria-valuenow="" role="slider"  tabindex="0" title="{tooltipHandle}"></div></div>';// title="{tooltipHandle}"
289         
290         }else{ // VML case
291                 Dial.RING_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.ring +  ' ' + Dial.CSS_CLASSES.ringVml + '">'+
292                                                                 '<div class="' + Dial.CSS_CLASSES.northMark + '"></div>'+
293                                                                         '<v:oval strokecolor="#ceccc0" strokeweight="1px"><v:fill type=gradient color="#8B8A7F" color2="#EDEDEB" angle="45"/></v:oval>'+
294                                                                         //'<v:oval></v:oval>'+
295                                                                 '</div>'+
296                                                                 '';
297                 Dial.MARKER_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.marker + ' ' + Dial.CSS_CLASSES.markerHidden + '">'+
298                                                                         '<div class="' + Dial.CSS_CLASSES.markerUserVml + '">'+
299                                                                                 '<v:oval stroked="false">'+
300                                                                                         '<v:fill opacity="20%" color="#000"/>'+
301                                                                                 '</v:oval>'+
302                                                                         '</div>'+
303                                                                         //'<v:oval></v:oval>'+
304                                                                 '</div>'+
305                                                                 '';
306                 Dial.CENTER_BUTTON_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.centerButton + ' ' + Dial.CSS_CLASSES.centerButtonVml + '">'+
307                                                                                         '<v:oval strokecolor="#ceccc0" strokeweight="1px">'+
308                                                                                                 '<v:fill type=gradient color="#C7C5B9" color2="#fefcf6" colors="35% #d9d7cb, 65% #fefcf6" angle="45"/>'+
309                                                                                                 '<v:shadow on="True" color="#000" opacity="10%" offset="2px, 2px"/>'+
310                                                                                         '</v:oval>'+
311                                                                                         //'<v:oval></v:oval>'+
312                                                                                         '<div class="' + Dial.CSS_CLASSES.resetString + '">{resetStr}</div>'+
313                                                                         '</div>'+
314                                                                         '';
315                 Dial.HANDLE_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.handle + '">'+
316                                                                         '<div class="' + Dial.CSS_CLASSES.handleUserVml + '" aria-labelledby="' + labelId + '" aria-valuetext="" aria-valuemax="" aria-valuemin="" aria-valuenow="" role="slider"  tabindex="0" title="{tooltipHandle}">'+
317                                                                                 '<v:oval stroked="false">'+
318                                                                                         '<v:fill opacity="20%" color="#6C3A3A"/>'+
319                                                                                 '</v:oval>'+
320                                                                         '</div>'+
321                                                                         //'<v:oval></v:oval>'+
322                                                                 '</div>'+
323                                                                 '';
324         }
325
326     /* Dial extends the base Widget class */
327     Y.extend(Dial, Widget, {
328
329                 /**
330                  * creates the DOM structure for the Dial.
331                  *
332                  * @method renderUI
333                  * @protected
334                  */
335         renderUI : function() {
336                         this._renderLabel();
337                         this._renderRing();
338                         this._renderMarker();
339                         this._renderCenterButton();
340                         this._renderHandle();
341                         
342                         if(supportsVML){
343                                 this._setVMLSizes();
344                         }
345                         this._setBorderRadius();
346                         
347                         // object handles
348                         this.contentBox = this.get("contentBox");
349                         
350                         // constants
351                         this._centerX = this.get('diameter') / 2;
352                         this._centerY = this.get('diameter') / 2;
353                         this._handleDist = this._centerX * this.get('handleDist');
354                         this._originalValue = this.get('value');
355
356                         // variables
357                         this._timesWrapped = 0;
358                         this._angle = this._getAngleFromValue(this.get('value'));
359                         this._setTimesWrapedFromValue(this.get('value'));
360                         
361                         // init
362                         this._handleUserNode.set('aria-valuemin', this.get('min'));
363                         this._handleUserNode.set('aria-valuemax', this.get('max'));
364         },
365
366                 /**
367                  * Sets -webkit-border-radius to 50% of width/height of the ring, handle-user, marker-user, and center-button.
368                  * This is needed for iOS 3.x.
369                  * The objects render square if the radius is > 50% of the width/height
370                  * @method _setBorderRadius
371                  * @private
372                  */
373                 _setBorderRadius : function(){
374                         // Fixme: Would this be a good thing to do for all browsers instead of relying on % dimensions in CSS?
375                         var dia = this.get('diameter');
376                         this._ringNode.setStyle('WebkitBorderRadius', Math.floor(dia * 0.5) + 'px');
377                         this._handleUserNode.setStyle('WebkitBorderRadius', Math.floor(dia * 0.1) + 'px');
378                         this._markerUserNode.setStyle('WebkitBorderRadius',  Math.floor(dia * 0.05) + 'px');
379                         this._centerButtonNode.setStyle('WebkitBorderRadius',  Math.floor(dia * 0.25) + 'px');
380                 },
381                 
382                 /**
383                  * Creates the Y.DD.Drag instance used for the handle movement and
384                  * binds Dial interaction to the configured value model.
385                  *
386                  * @method bindUI
387                  * @protected
388                  */
389         bindUI : function() {
390             this.after("valueChange", this._afterValueChange);
391
392             var boundingBox = this.get("boundingBox"),
393
394             // Looking for a key event which will fire continously across browsers while the key is held down.  
395             keyEventSpec = (!Y.UA.opera) ? "down:" : "press:",
396                         keyLeftRightSpec = (!Y.UA.opera) ? "down:" : "press:";
397                         // 38, 40 = arrow up/down, 33, 34 = page up/down,  35 , 36 = end/home
398             keyEventSpec += "38, 40, 33, 34, 35, 36";
399                         // 37 , 39 = arrow left/right
400             keyLeftRightSpec += "37, 39";
401
402             Y.on("key", Y.bind(this._onDirectionKey, this), boundingBox, keyEventSpec);
403             Y.on("key", Y.bind(this._onLeftRightKey, this), boundingBox, keyLeftRightSpec);
404                         Y.on('mouseenter', Y.bind(this._dialCenterOver, this), this._centerButtonNode);
405                         Y.on('mouseleave', Y.bind(this._dialCenterOut, this), this._centerButtonNode);
406                         Y.on('click', Y.bind(this._resetDial, this), this._centerButtonNode);                   
407                         Y.on('mousedown', Y.bind(function(){this._handleUserNode.focus();}, this), this._handleNode);                   
408
409                         var dd1 = new Y.DD.Drag({
410                                 node: this._handleNode,
411                                 on : {
412                                         'drag:drag' : Y.bind(this._handleDrag, this),
413                                         'drag:start' : Y.bind(this._handleDragStart, this),
414                                         'drag:end' : Y.bind(this._handleDragEnd, this)
415                                 }
416                         });
417                 },
418
419                 /**
420                  * Sets _timesWrapped based on Dial value
421                  * to net integer revolutions the user dragged the handle around the Dial
422                  *
423                  * @method _setTimesWrapedFromValue
424                  * @param val {Number} current value of the Dial
425                  * @private
426                  */
427                 _setTimesWrapedFromValue : function(val){
428                         if(val % this.get('stepsPerRev') === 0){
429                                 this._timesWrapped = (val / this.get('stepsPerRev')) -1;
430                         }else{
431                                 this._timesWrapped = Math.floor(val / this.get('stepsPerRev'));
432                         }
433                 },
434                 
435                 /**
436                  * Sets the string in the object the user clicks to reset the Dial value
437                  * 
438                  * @method _dialCenterOver
439          * @param e {DOMEvent} the mouseover event object
440                  */
441                 _dialCenterOver : function(e){
442                         this._resetString.setContent(Y.substitute('{resetStr}', this.get('strings')));
443                 },
444                 
445                 /**
446                  * Sets the string in the object the user clicks to reset the Dial value
447                  * to ""
448                  * 
449                  * @method _dialCenterOut
450          * @param e {DOMEvent} the mouseover event object
451                  */
452                 _dialCenterOut : function(e){
453                         this._resetString.setContent(''); 
454                 },
455                 
456                 /**
457                  * handles the user dragging the handle around the Dial, calculates the angle, 
458                  * checks for wrapping around top center, handles exceeding min or max values 
459                  *
460                  * @method _handleDrag
461          * @param e {DOMEvent} the drag event object
462                  * @protected
463                  */
464                 _handleDrag : function(e){
465                         var handleCenterX = e.pageX + this._handleUserNodeRadius,
466                         handleCenterY = e.pageY + this._handleUserNodeRadius;
467                         
468                         
469                         var ang = Math.atan( (this._centerYOnPage - handleCenterY)  /  (this._centerXOnPage - handleCenterX)  ) * (180 / Math.PI), 
470                         deltaX = (this._centerXOnPage - handleCenterX);
471                         if(deltaX < 0){
472                                 ang = (ang + 90);
473                         }else{
474                                 ang = (ang - 90);
475                         }
476                         // check for need to set timesWrapped
477                         if(handleCenterY < this._centerYOnPage){ //if handle is above the middle of the dial...
478                                 if((this._prevX <= this._centerXOnPage) && (handleCenterX > this._centerXOnPage)){ // If wrapping, clockwise
479                                         this._timesWrapped = (this._timesWrapped + 1);
480                                 }else if((this._prevX > this._centerXOnPage) && (handleCenterX <= this._centerXOnPage)){ // if un-wrapping, counter-clockwise
481                                         this._timesWrapped = (this._timesWrapped - 1);
482                                 }
483                         }
484                         this._prevX = handleCenterX;
485                         var newValue = this._getValueFromAngle(ang); // This function needs the current _timesWrapped value
486                         // handle hitting max and min and going beyond, stops at max or min 
487                         //if((newValue > this.get('min')) && (newValue < this.get('max'))) {
488                         if((newValue > this.get('min')) && (newValue < this.get('max'))) {
489                                 this.set('value', newValue);
490                         }else if(newValue > this.get('max')){
491                                 this.set('value', this.get('max'));
492                         }else if(newValue < this.get('min')){
493                                 this.set('value', this.get('min'));
494                         }
495                 },
496
497                 /**
498                  * handles the user starting to drag the handle around the Dial
499                  *
500                  * @method _handleDragStart
501          * @param e {DOMEvent} the drag event object
502                  * @protected
503                  */
504                 _handleDragStart : function(e){
505                         this._markerNode.removeClass(Dial.CSS_CLASSES.markerHidden);
506                         if(!this._prevX){
507                                 this._prevX = this._handleNode.getX();
508                         }
509                         this._centerYOnPage = (this._ringNode.getY() + this._centerY);
510                         this._centerXOnPage = (this._ringNode.getX() + this._centerX);
511                 },
512
513                 /*
514                  * When handle is handleDragEnd, this animates the return to the fixed dial
515                  */             
516
517                 /**
518                  * handles the end of a user dragging the handle, animates the handle returning to
519                  * resting position.
520                  *
521                  * @method _handleDragEnd
522                  * @protected
523                  */
524                 _handleDragEnd : function(){
525                         var node = this._handleNode;                    
526                                 node.transition({
527                                         duration: 0.08, // seconds
528                                         easing: 'ease-in',
529                                         left: this._setNodeToFixedRadius(this._handleNode, true)[0] + 'px',
530                                         top: this._setNodeToFixedRadius(this._handleNode, true)[1] + 'px'
531                                 }, Y.bind(function(){
532                                                 this._markerNode.addClass(Dial.CSS_CLASSES.markerHidden);
533                                                 this._prevX = this._handleNode.getX(); //makes us ready for next drag.
534                                         }, this)
535                                 );
536                         this._setTimesWrapedFromValue(this.get('value'));
537                 },
538
539                 /**
540                  * returns the XY of the fixed position, handleDist, from the center of the Dial (resting position)
541                  * The XY also represents the angle related to the current value
542                  * If typeArray is true, [X,Y] is returned.
543                  * If typeArray is false, the XY of the node passed is set.
544                  *
545                  * @method _setNodeToFixedRadius
546                  * @param obj {Node}
547                  * @param typeArray {Boolean} true returns an array [X,Y]
548                  * @protected
549                  * @return {Array} an array of [XY] is optionally returned
550                  */
551                  _setNodeToFixedRadius : function(obj, typeArray){
552                         var thisAngle = (this._angle - 90),
553                         rad = (Math.PI / 180),
554                         newY = Math.round(Math.sin(thisAngle * rad) * this._handleDist),
555                         newX = Math.round(Math.cos(thisAngle * rad) * this._handleDist),
556                         dia = parseInt(obj.getStyle('width'), 10);
557                         
558                         newY = newY - (dia * 0.5);
559                         newX = newX - (dia * 0.5);
560                         if(typeArray){ // just need the style for css transform left and top to animate the handle drag:end
561                                 return [this._centerX + newX, this._centerX + newY];
562                         }else{
563                                 obj.setStyle('left', (this._centerX + newX) + 'px');
564                                 obj.setStyle('top', (this._centerX + newY) + 'px');
565                         }
566                  },
567
568                 /**
569                  * Synchronizes the DOM state with the attribute settings.
570                  *
571                  * @method syncUI
572                  */
573         syncUI : function() {
574             this._uiSetValue(this.get("value"));
575         },
576
577                 /**
578                  * sets the sizes of ring, center-button, marker and handle VML ovals in pixels.
579                  * Needed only in some IE versions 
580                  * that ignore percent style sizes/offsets.
581                  * so these must be set in pixels.
582                  * Normally these are set in % of the ring.
583                  *
584                  * @method _setVMLSizes
585                  * @protected
586                  */
587                 _setVMLSizes : function(){
588                         var dia = this.get('diameter');
589                         var setSize = function(node, dia, percent){
590                         var suffix = 'px';
591                                 node.getElementsByTagName('oval').setStyle('width', (dia * percent) + suffix);
592                                 node.getElementsByTagName('oval').setStyle('height', (dia * percent) + suffix);
593                                 node.setStyle('width', (dia * percent) + suffix);
594                                 node.setStyle('height', (dia * percent) + suffix);
595                         };
596                         setSize(this._ringNode, dia, 1.0);
597                         setSize(this._handleNode, dia, 0.2);
598                         setSize(this._handleUserNode, dia, 0.2);
599                         setSize(this._markerNode, dia, 0.1);
600                         setSize(this._markerUserNode, dia, 0.1);
601                         setSize(this._centerButtonNode, dia, 0.5);
602                         this._handleUserNodeRadius = (dia * 0.1);
603                         this._markerUserNodeRadius = (dia * 0.05);
604                 },
605
606
607                 /**
608                  * renders the DOM object for the Dial's label
609                  *
610                  * @method _renderLabel
611                  * @protected
612                  */
613         _renderLabel : function() {
614             var contentBox = this.get("contentBox"),
615                 label = contentBox.one("." + Dial.CSS_CLASSES.label);
616             if (!label) {
617                                 label = Node.create(Y.substitute(Dial.LABEL_TEMPLATE, this.get('strings')));
618                                 contentBox.append(label);
619             }
620             this._labelNode = label;
621                         this._valueStringNode = this._labelNode.one("." + Dial.CSS_CLASSES.valueString);
622         },
623
624                 /**
625                  * renders the DOM object for the Dial's background ring
626                  *
627                  * @method _renderRing
628                  * @protected
629                  */
630         _renderRing : function() {
631             var contentBox = this.get("contentBox"),
632                 ring = contentBox.one("." + Dial.CSS_CLASSES.ring);
633             if (!ring) {
634                 ring = contentBox.appendChild(Dial.RING_TEMPLATE);
635                                 ring.setStyles({width:this.get('diameter') + 'px', height:this.get('diameter') + 'px'});
636             }
637             this._ringNode = ring;
638         },
639
640                 /**
641                  * renders the DOM object for the Dial's background marker which 
642                  * tracks the angle of the user dragging the handle
643                  *
644                  * @method _renderMarker
645                  * @protected
646                  */
647         _renderMarker : function() {
648             var contentBox = this.get("contentBox"),
649                         marker = contentBox.one("." + Dial.CSS_CLASSES.marker);
650             if (!marker) {
651                 marker = contentBox.one('.' + Dial.CSS_CLASSES.ring).appendChild(Dial.MARKER_TEMPLATE);
652             }
653             this._markerNode = marker;
654                         if(supportsVML === true){
655                                 this._markerUserNode = this._markerNode.one('.' + Dial.CSS_CLASSES.markerUserVml);
656                         }else{
657                                 this._markerUserNode = this._markerNode.one('.' + Dial.CSS_CLASSES.markerUser);
658                         }
659                         this._markerUserNodeRadius = parseInt(this._markerUserNode.getStyle('width'), 10) * 0.5;
660         },
661                 
662                 /**
663                  * places the centerbutton's reset string in the center of the button
664                  * based on the size of the string object 
665                  *
666                  * @method _setXYResetString
667                  * @protected
668                  */
669                 _setXYResetString : function(){
670                         this._resetString.setStyle('top', (this._centerButtonNode.get('region').height / 2) - (this._resetString.get('region').height / 2) + 'px');
671                         this._resetString.setStyle('left', (this._centerButtonNode.get('region').width / 2) - (this._resetString.get('region').width / 2) + 'px');
672                 },
673
674                 /**
675                  * renders the DOM object for the Dial's center
676                  *
677                  * @method _renderCenterButton
678                  * @protected
679                  */
680         _renderCenterButton : function() {
681             var contentBox = this.get("contentBox"),
682                 centerButton = contentBox.one("." + Dial.CSS_CLASSES.centerButton);
683             if (!centerButton) {
684                                 centerButton = Node.create(Y.substitute(Dial.CENTER_BUTTON_TEMPLATE, this.get('strings')));
685                 contentBox.one('.' + Dial.CSS_CLASSES.ring).append(centerButton);
686             }
687             this._centerButtonNode = centerButton;
688                         this._resetString = this._centerButtonNode.one('.' + Dial.CSS_CLASSES.resetString);
689                         this._setXYResetString(); // centering the reset string in the button
690                         this._resetString.setContent('');
691                         var offset = this.get('diameter') * 0.25;
692                         this._centerButtonNode.setStyle('left', (offset) + 'px');
693                         this._centerButtonNode.setStyle('top', (offset) + 'px');
694                 },
695
696                 /**
697                  * renders the DOM object for the Dial's user draggable handle
698                  *
699                  * @method _renderHandle
700                  * @protected
701                  */
702         _renderHandle : function() {
703             var contentBox = this.get("contentBox"),
704                 handle = contentBox.one("." + Dial.CSS_CLASSES.handle);
705             if (!handle) {
706                 handle = Node.create(Y.substitute(Dial.HANDLE_TEMPLATE, this.get('strings')));
707                 contentBox.one('.' + Dial.CSS_CLASSES.ring).append(handle);
708             }
709             this._handleNode = handle;
710                         if(supportsVML === true){
711                                 this._handleUserNode = this._handleNode.one('.' + Dial.CSS_CLASSES.handleUserVml);
712                         }else{
713                                 this._handleUserNode = this._handleNode.one('.' + Dial.CSS_CLASSES.handleUser);
714                         }
715                         this._handleUserNodeRadius = parseInt(this._handleUserNode.getStyle('width'), 10) * 0.5;
716         },
717
718         /**
719          * sets the visible UI label string
720                  *
721                  * @method _setLabelString
722                  * @param str {String}
723                  * @protected
724                  */
725         _setLabelString : function(str) {
726             this.get("contentBox").one("." + Dial.CSS_CLASSES.labelString).setContent(str);
727         },
728
729         /**
730          * sets the visible UI label string
731                  *
732                  * @method _setResetString
733                  * @param str {String}
734                  * @protected
735                  */
736         _setResetString : function(str) {
737                         this.set('strings.resetStr', str);
738             this.get("contentBox").one("." + Dial.CSS_CLASSES.resetString).setContent(str);
739                         this._setXYResetString(); // recenters the string in the button
740                         this._resetString.setContent('');
741         },
742
743         /**
744          * sets the tooltip string in the Dial's handle
745                  *
746                  * @method _setTooltipString
747                  * @param str {String}
748                  * @protected
749                  */
750         _setTooltipString : function(str) {
751             this._handleUserNode.set('title', str);
752         },
753
754                 /**
755                  * sets the Dial's value in response to key events.
756                  * Left and right keys are in a separate method 
757                  * in case an implementation wants to increment values
758                  * but needs left and right arrow keys for other purposes.
759                  *
760                  * @method _onDirectionKey
761                  * @param e {Event} the key event
762                  * @protected
763                  */
764         _onDirectionKey : function(e) {
765             e.preventDefault();
766             switch (e.charCode) {
767                 case 38: // up
768                                         this._incrMinor();
769                     break;
770                 case 40: // down
771                                         this._decrMinor();
772                     break;
773                 case 36: // home
774                                         this._resetDial();
775                     break;
776                 case 35: // end
777                     this._setToMax();
778                                         break;
779                 case 33: // page up
780                                         this._incrMajor();
781                     break;
782                 case 34: // page down
783                     this._decrMajor();
784                                         break;
785             }
786         },
787
788                 /**
789                  * sets the Dial's value in response to left or right key events
790                  *
791                  * @method _onLeftRightKey
792                  * @param e {Event} the key event
793                  * @protected
794                  */
795         _onLeftRightKey : function(e) {
796             e.preventDefault();
797             switch (e.charCode) {
798                 case 37: // left
799                                         this._decrMinor();
800                     break;
801                 case 39: // right
802                                         this._incrMinor();
803                     break;
804             }
805         },
806                 
807                 /**
808                  * increments Dial value by a minor increment
809                  *
810                  * @method _incrMinor
811                  * @protected
812                  */
813                 _incrMinor : function(){
814                                 var newVal = (this.get('value') + this.get("minorStep"));
815                                 newVal = Math.min(newVal, this.get("max"));
816                                 this.set('value', newVal.toFixed(this.get('decimalPlaces')) - 0);
817                 },
818                 
819                 /**
820                  * decrements Dial value by a minor increment
821                  *
822                  * @method _decrMinor
823                  * @protected
824                  */
825                 _decrMinor : function(){
826                                 var newVal = (this.get('value') - this.get("minorStep"));
827                                 newVal = Math.max(newVal, this.get("min"));
828                                 this.set('value', newVal.toFixed(this.get('decimalPlaces')) - 0);
829                 },
830                 
831                 /**
832                  * increments Dial value by a major increment
833                  *
834                  * @method _incrMajor
835                  * @protected
836                  */
837                 _incrMajor : function(){
838                                 var newVal = (this.get('value') + this.get("majorStep"));
839                                 newVal = Math.min(newVal, this.get("max"));
840                                 this.set('value', newVal.toFixed(this.get('decimalPlaces')) - 0);
841                 },
842                 
843                 /**
844                  * decrements Dial value by a major increment
845                  *
846                  * @method _decrMajor
847                  * @protected
848                  */
849                 _decrMajor : function(){
850                                 var newVal = (this.get('value') - this.get("majorStep"));
851                                 newVal = Math.max(newVal, this.get("min"));
852                                 this.set('value', newVal.toFixed(this.get('decimalPlaces')) - 0);
853                 },
854
855                 /**
856                  * sets Dial value to dial's max attr
857                  *
858                  * @method _decrMajor
859                  * @protected
860                  */
861                 _setToMax : function(){
862                                 this.set('value', this.get("max"));
863                 },              
864                 
865                 /**
866                  * sets Dial value to dial's min attr
867                  *
868                  * @method _decrMajor
869                  * @protected
870                  */
871                 _setToMin : function(){
872                                 this.set('value', this.get("min"));
873                 },              
874                 
875                 /**
876                  * resets Dial value to the orignal initial value. 
877                  *
878                  * @method _resetDial
879                  * @protected
880                  */
881                 _resetDial : function(){
882                         this.set('value', this._originalValue);
883                         this._handleUserNode.focus();
884                 },
885                 
886                 /**
887                  * returns the handle angle associated with the current value of the Dial
888                  *
889                  * @method _getAngleFromValue
890                  * @param newVal {Number} the current value of the Dial
891                  * @return {Number} the angle associated with the current Dial value
892                  * @protected
893                  */
894                 _getAngleFromValue : function(newVal){
895                         var nonWrapedPartOfValue = newVal % this.get('stepsPerRev');
896                         var angleFromValue = nonWrapedPartOfValue / this.get('stepsPerRev') * 360;
897                         return angleFromValue; 
898                 },
899
900                 /**
901                  * returns the value of the Dial calculated from the current handle angle
902                  *
903                  * @method _getValueFromAngle
904                  * @param angle {Number} the current angle of the Dial's handle
905                  * @return {Number} the current Dial value corresponding to the handle position
906                  * @protected
907                  */
908                 _getValueFromAngle : function(angle){
909                         if(angle < 0){
910                                 angle = (360 + angle);
911                         }else if(angle === 0){
912                                 angle = 360;
913                         }
914                         var value = (angle / 360) * this.get('stepsPerRev');
915                         value = (value + (this._timesWrapped * this.get('stepsPerRev')));
916                         //return Math.round(value * 100) / 100;
917                         return value.toFixed(this.get('decimalPlaces')) - 0;
918                 },
919
920                 /**
921                  * calls the method to update the UI whenever the Dial value changes
922                  *
923                  * @method _afterValueChange
924                  * @param e {Event}
925                  * @protected
926                  */
927         _afterValueChange : function(e) {
928             this._uiSetValue(e.newVal);
929         },
930
931                 /**
932          * Updates the UI display value of the Dial to reflect 
933          * the value passed in.
934                  * Makes all other needed UI display changes
935                  *
936                  * @method _uiSetValue
937                  * @param val {Number} value of the Dial
938                  * @protected
939                  */
940         _uiSetValue : function(val) {
941                         this._angle = this._getAngleFromValue(val);
942                         if(this._handleNode.hasClass(Dial.CSS_CLASSES.dragging) === false){
943                                 this._setTimesWrapedFromValue(val);
944                                 this._setNodeToFixedRadius(this._handleNode, false);
945                                 this._prevX = this._handleNode.getX();
946                         }
947                         this._valueStringNode.setContent(val); 
948                         this._handleUserNode.set('aria-valuenow', val);
949                         this._handleUserNode.set('aria-valuetext', val);
950                         this._setNodeToFixedRadius(this._markerNode, false);
951                         if((val === this.get('max')) || (val === this.get('min'))){
952                                 if(this._markerUserNode.hasClass('marker-max-min') === false){
953                                         this._markerUserNode.addClass('marker-max-min');
954                                         if(supportsVML === true){
955                                                 this._markerUserNode.getElementsByTagName('fill').set('color', '#AB3232');
956                                         }
957                                 }
958                         }else{
959                                 if(supportsVML === true){
960                                         this._markerNode.getElementsByTagName('fill').set('color', '#000');
961                                 }
962                                 if(this._markerUserNode.hasClass('marker-max-min') === true){
963                                         this._markerUserNode.removeClass('marker-max-min');
964                                 }
965                         }
966         },
967
968                 /**
969          * value attribute default validator. Verifies that
970          * the value being set lies between the min/max value
971                  *
972                  * @method _validateValue
973                  * @param val {Number} value of the Dial
974                  * @protected
975                  */
976         _validateValue: function(val) {
977             var min = this.get("min"),
978                 max = this.get("max");
979             return (Lang.isNumber(val) && val >= min && val <= max);
980         }
981     });
982         Y.Dial = Dial;
983
984
985
986 }, '3.3.0' ,{requires:['widget', 'dd-drag', 'substitute', 'event-mouseenter', 'transition', 'intl'], skinnable:true, lang:['en','es' ]});