2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
8 YUI.add('dial', function(Y) {
11 * Create a circular dial value range input visualized as a draggable handle on a
16 var supportsVML = false,
19 if (Y.UA.ie && Y.UA.ie < 9){
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.
35 * @param config {Object} Configuration object
38 function Dial(config) {
39 Dial.superclass.constructor.apply(this, arguments);
42 // Y.Dial static properties
45 * The identity of the widget.
57 * Static property used to define the default attribute configuration of
60 * @property Dial.ATTRS
68 * minimum value allowed
79 * maximum value allowed
90 * diameter of the circular background object.
91 * Other objects scale accordingly
92 * Set this only before rendering.
95 * @type {Number} the number of px in diameter
104 * initial value of the Dial
112 validator: function(val) {
113 return this._validateValue(val);
118 * amount to increment/decrement the dial value
119 * when the arrow up/down/left/right keys are pressed
121 * @attribute minorStep
130 * amount to increment/decrement the dial value
131 * when the page up/down keys are pressed
133 * @attribute majorStep
142 * number of value increments in one 360 degree revolution
143 * of the handle around the dial
145 * @attribute stepsPerRev
154 * number of decimal places of accuracy in the value
156 * @attribute decimalPlaces
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
172 * @default {label: 'My label', resetStr: 'Reset', tooltipHandle: 'Drag to set value'}
175 valueFn: function () {
176 return Y.Intl.get('dial');
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.
185 * @attribute handleDist
196 * returns a properly formed yui class name
199 * @param {String} string to be appended at the end of class name
203 function makeClassName(str) {
204 return Y.ClassNameManager.getClassName(Dial.NAME, str);
208 * array of static constants used to identify the classname applied to the Dial DOM objects
210 * @property 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")
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
240 * template that will contain the Dial's label.
242 * @property Dial.LABEL_TEMPLATE
244 * @default <div>...</div>
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>';
249 if(supportsVML === false){
251 * template that will contain the Dial's background ring.
253 * @property Dial.RING_TEMPLATE
255 * @default <div class="[...-ring]"><div class="[...-northMark]"></div></div>
258 Dial.RING_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.ring + '"><div class="' + Dial.CSS_CLASSES.northMark + '"></div></div>';
261 * template that will contain the Dial's current angle marker.
263 * @property Dial.MARKER_TEMPLATE
265 * @default <div class="[...-marker] [...-marker-hidden]"><div class="[...-markerUser]"></div></div>
268 Dial.MARKER_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.marker + ' ' + Dial.CSS_CLASSES.markerHidden + '"><div class="' + Dial.CSS_CLASSES.markerUser + '"></div></div>';
271 * template that will contain the Dial's center button.
273 * @property Dial.CENTER_BUTTON_TEMPLATE
275 * @default <div class="[...-centerButton]"><div class="[...-resetString]">' + Y.substitute('{resetStr}', Dial.ATTRS.strings.value) + '</div></div>
278 Dial.CENTER_BUTTON_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.centerButton + '"><div class="' + Dial.CSS_CLASSES.resetString + '">{resetStr}</div></div>';
281 * template that will contain the Dial's handle.
283 * @property Dial.HANDLE_TEMPLATE
285 * @default <div class="[...-handle]"><div class="[...-handleUser]" aria-labelledby="' + labelId + '" aria-valuetext="" aria-valuemax="" aria-valuemin="" aria-valuenow="" role="slider" tabindex="0"></div></div>';// title="{tooltipHandle}"
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}"
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>'+
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"/>'+
303 //'<v:oval></v:oval>'+
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"/>'+
311 //'<v:oval></v:oval>'+
312 '<div class="' + Dial.CSS_CLASSES.resetString + '">{resetStr}</div>'+
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"/>'+
321 //'<v:oval></v:oval>'+
326 /* Dial extends the base Widget class */
327 Y.extend(Dial, Widget, {
330 * creates the DOM structure for the Dial.
335 renderUI : function() {
338 this._renderMarker();
339 this._renderCenterButton();
340 this._renderHandle();
345 this._setBorderRadius();
348 this.contentBox = this.get("contentBox");
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');
357 this._timesWrapped = 0;
358 this._angle = this._getAngleFromValue(this.get('value'));
359 this._setTimesWrapedFromValue(this.get('value'));
362 this._handleUserNode.set('aria-valuemin', this.get('min'));
363 this._handleUserNode.set('aria-valuemax', this.get('max'));
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
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');
383 * Creates the Y.DD.Drag instance used for the handle movement and
384 * binds Dial interaction to the configured value model.
389 bindUI : function() {
390 this.after("valueChange", this._afterValueChange);
392 var boundingBox = this.get("boundingBox"),
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";
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);
409 var dd1 = new Y.DD.Drag({
410 node: this._handleNode,
412 'drag:drag' : Y.bind(this._handleDrag, this),
413 'drag:start' : Y.bind(this._handleDragStart, this),
414 'drag:end' : Y.bind(this._handleDragEnd, this)
420 * Sets _timesWrapped based on Dial value
421 * to net integer revolutions the user dragged the handle around the Dial
423 * @method _setTimesWrapedFromValue
424 * @param val {Number} current value of the Dial
427 _setTimesWrapedFromValue : function(val){
428 if(val % this.get('stepsPerRev') === 0){
429 this._timesWrapped = (val / this.get('stepsPerRev')) -1;
431 this._timesWrapped = Math.floor(val / this.get('stepsPerRev'));
436 * Sets the string in the object the user clicks to reset the Dial value
438 * @method _dialCenterOver
439 * @param e {DOMEvent} the mouseover event object
441 _dialCenterOver : function(e){
442 this._resetString.setContent(Y.substitute('{resetStr}', this.get('strings')));
446 * Sets the string in the object the user clicks to reset the Dial value
449 * @method _dialCenterOut
450 * @param e {DOMEvent} the mouseover event object
452 _dialCenterOut : function(e){
453 this._resetString.setContent('');
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
460 * @method _handleDrag
461 * @param e {DOMEvent} the drag event object
464 _handleDrag : function(e){
465 var handleCenterX = e.pageX + this._handleUserNodeRadius,
466 handleCenterY = e.pageY + this._handleUserNodeRadius;
469 var ang = Math.atan( (this._centerYOnPage - handleCenterY) / (this._centerXOnPage - handleCenterX) ) * (180 / Math.PI),
470 deltaX = (this._centerXOnPage - handleCenterX);
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);
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'));
498 * handles the user starting to drag the handle around the Dial
500 * @method _handleDragStart
501 * @param e {DOMEvent} the drag event object
504 _handleDragStart : function(e){
505 this._markerNode.removeClass(Dial.CSS_CLASSES.markerHidden);
507 this._prevX = this._handleNode.getX();
509 this._centerYOnPage = (this._ringNode.getY() + this._centerY);
510 this._centerXOnPage = (this._ringNode.getX() + this._centerX);
514 * When handle is handleDragEnd, this animates the return to the fixed dial
518 * handles the end of a user dragging the handle, animates the handle returning to
521 * @method _handleDragEnd
524 _handleDragEnd : function(){
525 var node = this._handleNode;
527 duration: 0.08, // seconds
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.
536 this._setTimesWrapedFromValue(this.get('value'));
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.
545 * @method _setNodeToFixedRadius
547 * @param typeArray {Boolean} true returns an array [X,Y]
549 * @return {Array} an array of [XY] is optionally returned
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);
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];
563 obj.setStyle('left', (this._centerX + newX) + 'px');
564 obj.setStyle('top', (this._centerX + newY) + 'px');
569 * Synchronizes the DOM state with the attribute settings.
573 syncUI : function() {
574 this._uiSetValue(this.get("value"));
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.
584 * @method _setVMLSizes
587 _setVMLSizes : function(){
588 var dia = this.get('diameter');
589 var setSize = function(node, dia, percent){
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);
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);
608 * renders the DOM object for the Dial's label
610 * @method _renderLabel
613 _renderLabel : function() {
614 var contentBox = this.get("contentBox"),
615 label = contentBox.one("." + Dial.CSS_CLASSES.label);
617 label = Node.create(Y.substitute(Dial.LABEL_TEMPLATE, this.get('strings')));
618 contentBox.append(label);
620 this._labelNode = label;
621 this._valueStringNode = this._labelNode.one("." + Dial.CSS_CLASSES.valueString);
625 * renders the DOM object for the Dial's background ring
627 * @method _renderRing
630 _renderRing : function() {
631 var contentBox = this.get("contentBox"),
632 ring = contentBox.one("." + Dial.CSS_CLASSES.ring);
634 ring = contentBox.appendChild(Dial.RING_TEMPLATE);
635 ring.setStyles({width:this.get('diameter') + 'px', height:this.get('diameter') + 'px'});
637 this._ringNode = ring;
641 * renders the DOM object for the Dial's background marker which
642 * tracks the angle of the user dragging the handle
644 * @method _renderMarker
647 _renderMarker : function() {
648 var contentBox = this.get("contentBox"),
649 marker = contentBox.one("." + Dial.CSS_CLASSES.marker);
651 marker = contentBox.one('.' + Dial.CSS_CLASSES.ring).appendChild(Dial.MARKER_TEMPLATE);
653 this._markerNode = marker;
654 if(supportsVML === true){
655 this._markerUserNode = this._markerNode.one('.' + Dial.CSS_CLASSES.markerUserVml);
657 this._markerUserNode = this._markerNode.one('.' + Dial.CSS_CLASSES.markerUser);
659 this._markerUserNodeRadius = parseInt(this._markerUserNode.getStyle('width'), 10) * 0.5;
663 * places the centerbutton's reset string in the center of the button
664 * based on the size of the string object
666 * @method _setXYResetString
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');
675 * renders the DOM object for the Dial's center
677 * @method _renderCenterButton
680 _renderCenterButton : function() {
681 var contentBox = this.get("contentBox"),
682 centerButton = contentBox.one("." + Dial.CSS_CLASSES.centerButton);
684 centerButton = Node.create(Y.substitute(Dial.CENTER_BUTTON_TEMPLATE, this.get('strings')));
685 contentBox.one('.' + Dial.CSS_CLASSES.ring).append(centerButton);
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');
697 * renders the DOM object for the Dial's user draggable handle
699 * @method _renderHandle
702 _renderHandle : function() {
703 var contentBox = this.get("contentBox"),
704 handle = contentBox.one("." + Dial.CSS_CLASSES.handle);
706 handle = Node.create(Y.substitute(Dial.HANDLE_TEMPLATE, this.get('strings')));
707 contentBox.one('.' + Dial.CSS_CLASSES.ring).append(handle);
709 this._handleNode = handle;
710 if(supportsVML === true){
711 this._handleUserNode = this._handleNode.one('.' + Dial.CSS_CLASSES.handleUserVml);
713 this._handleUserNode = this._handleNode.one('.' + Dial.CSS_CLASSES.handleUser);
715 this._handleUserNodeRadius = parseInt(this._handleUserNode.getStyle('width'), 10) * 0.5;
719 * sets the visible UI label string
721 * @method _setLabelString
722 * @param str {String}
725 _setLabelString : function(str) {
726 this.get("contentBox").one("." + Dial.CSS_CLASSES.labelString).setContent(str);
730 * sets the visible UI label string
732 * @method _setResetString
733 * @param str {String}
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('');
744 * sets the tooltip string in the Dial's handle
746 * @method _setTooltipString
747 * @param str {String}
750 _setTooltipString : function(str) {
751 this._handleUserNode.set('title', str);
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.
760 * @method _onDirectionKey
761 * @param e {Event} the key event
764 _onDirectionKey : function(e) {
766 switch (e.charCode) {
782 case 34: // page down
789 * sets the Dial's value in response to left or right key events
791 * @method _onLeftRightKey
792 * @param e {Event} the key event
795 _onLeftRightKey : function(e) {
797 switch (e.charCode) {
808 * increments Dial value by a minor increment
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);
820 * decrements Dial value by a minor increment
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);
832 * increments Dial value by a major increment
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);
844 * decrements Dial value by a major increment
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);
856 * sets Dial value to dial's max attr
861 _setToMax : function(){
862 this.set('value', this.get("max"));
866 * sets Dial value to dial's min attr
871 _setToMin : function(){
872 this.set('value', this.get("min"));
876 * resets Dial value to the orignal initial value.
881 _resetDial : function(){
882 this.set('value', this._originalValue);
883 this._handleUserNode.focus();
887 * returns the handle angle associated with the current value of the Dial
889 * @method _getAngleFromValue
890 * @param newVal {Number} the current value of the Dial
891 * @return {Number} the angle associated with the current Dial value
894 _getAngleFromValue : function(newVal){
895 var nonWrapedPartOfValue = newVal % this.get('stepsPerRev');
896 var angleFromValue = nonWrapedPartOfValue / this.get('stepsPerRev') * 360;
897 return angleFromValue;
901 * returns the value of the Dial calculated from the current handle angle
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
908 _getValueFromAngle : function(angle){
910 angle = (360 + angle);
911 }else if(angle === 0){
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;
921 * calls the method to update the UI whenever the Dial value changes
923 * @method _afterValueChange
927 _afterValueChange : function(e) {
928 this._uiSetValue(e.newVal);
932 * Updates the UI display value of the Dial to reflect
933 * the value passed in.
934 * Makes all other needed UI display changes
936 * @method _uiSetValue
937 * @param val {Number} value of the Dial
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();
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');
959 if(supportsVML === true){
960 this._markerNode.getElementsByTagName('fill').set('color', '#000');
962 if(this._markerUserNode.hasClass('marker-max-min') === true){
963 this._markerUserNode.removeClass('marker-max-min');
969 * value attribute default validator. Verifies that
970 * the value being set lies between the min/max value
972 * @method _validateValue
973 * @param val {Number} value of the Dial
976 _validateValue: function(val) {
977 var min = this.get("min"),
978 max = this.get("max");
979 return (Lang.isNumber(val) && val >= min && val <= max);
986 }, '3.3.0' ,{requires:['widget', 'dd-drag', 'substitute', 'event-mouseenter', 'transition', 'intl'], skinnable:true, lang:['en','es' ]});