[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 /* 2 YUI 3.17.2 (build 9c3c78e) 3 Copyright 2014 Yahoo! Inc. All rights reserved. 4 Licensed under the BSD License. 5 http://yuilibrary.com/license/ 6 */ 7 8 YUI.add('dial', function (Y, NAME) { 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 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 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 * diameter of the handle object which users drag to change the value. 105 * Dial sets the pixel dimension of the handle equal to handleDiameter * diameter. 106 * Set this only before rendering. 107 * 108 * @attribute handleDiameter 109 * @type {Number} 110 * @default 0.2 111 * @writeOnce 112 */ 113 handleDiameter : { 114 value:0.2 115 }, 116 117 /** 118 * diameter of the marker object which follows the angle of the handle during value changes. 119 * Dial sets the pixel dimension of the marker equal to markerDiameter * diameter. 120 * Set this only before rendering. 121 * 122 * @attribute markerDiameter 123 * @type {Number} 124 * @default 0.1 125 * @writeOnce 126 */ 127 markerDiameter : { 128 value:0.1 129 }, 130 131 /** 132 * diameter of the center button object. 133 * Dial sets the pixel dimension of the centerButton equal to centerButtonDiameter * diameter. 134 * Set this only before rendering. 135 * 136 * @attribute centerButtonDiameter 137 * @type {Number} 138 * @default 0.1 139 * @writeOnce 140 */ 141 centerButtonDiameter : { 142 value:0.5 143 }, 144 145 /** 146 * initial value of the Dial 147 * 148 * @attribute value 149 * @type {Number} 150 * @default 0 151 */ 152 value : { 153 value:0, 154 validator: function(val) { 155 return this._validateValue(val); 156 } 157 }, 158 159 /** 160 * amount to increment/decrement the dial value 161 * when the arrow up/down/left/right keys are pressed 162 * 163 * @attribute minorStep 164 * @type {Number} 165 * @default 1 166 */ 167 minorStep : { 168 value:1 169 }, 170 171 /** 172 * amount to increment/decrement the dial value 173 * when the page up/down keys are pressed 174 * 175 * @attribute majorStep 176 * @type {Number} 177 * @default 10 178 */ 179 majorStep : { 180 value:10 181 }, 182 183 /** 184 * number of value increments in one 360 degree revolution 185 * of the handle around the dial 186 * 187 * @attribute stepsPerRevolution 188 * @type {Number} 189 * @default 100 190 */ 191 stepsPerRevolution : { 192 value:100 193 }, 194 195 /** 196 * number of decimal places of accuracy in the value 197 * 198 * @attribute decimalPlaces 199 * @type {Number} 200 * @default 0 201 */ 202 decimalPlaces : { 203 value:0 204 }, 205 206 /** 207 * visible strings for the dial UI. This attribute is 208 * defined by the base Widget class but has an empty value. The 209 * Dial is simply providing a default value for the attribute. 210 * Gets localized strings in the current language 211 * 212 * @attribute strings 213 * @type {Object} the values are HTML strings 214 * @default {label: 'My label', resetStr: 'Reset', tooltipHandle: 'Drag to set value'} 215 */ 216 strings: { 217 valueFn: function () { 218 return Y.Intl.get('dial'); 219 } 220 }, 221 222 /** 223 * distance from the center of the dial to the 224 * center of the marker and handle, when at rest. 225 * The value is a percent of the radius of the dial. 226 * 227 * @attribute handleDistance 228 * @type {number} 229 * @default 0.75 230 */ 231 handleDistance:{ 232 value:0.75 233 } 234 235 }; 236 237 /** 238 * returns a properly formed yui class name 239 * 240 * @method 241 * @param {String} string to be appended at the end of class name 242 * @return 243 * @private 244 */ 245 function makeClassName(str) { 246 return Y.ClassNameManager.getClassName(Dial.NAME, str); 247 } 248 249 /** array of static constants used to identify the classname applied to the Dial DOM objects 250 * 251 * @property CSS_CLASSES 252 * @type {Array} 253 * @private 254 * @static 255 */ 256 Dial.CSS_CLASSES = { 257 label : makeClassName("label"), 258 labelString : makeClassName("label-string"), 259 valueString : makeClassName("value-string"), 260 northMark : makeClassName("north-mark"), 261 ring : makeClassName('ring'), 262 ringVml : makeClassName('ring-vml'), 263 marker : makeClassName("marker"), 264 markerVml : makeClassName("marker-vml"), 265 markerMaxMin : makeClassName("marker-max-min"), 266 centerButton : makeClassName("center-button"), 267 centerButtonVml : makeClassName('center-button-vml'), 268 resetString : makeClassName("reset-string"), 269 handle : makeClassName("handle"), 270 handleVml : makeClassName("handle-vml"), 271 hidden : makeClassName("hidden"), 272 dragging : Y.ClassNameManager.getClassName("dd-dragging") 273 }; 274 275 /* Static constants used to define the markup templates used to create Dial DOM elements */ 276 277 278 /** 279 * template that will contain the Dial's label. 280 * 281 * @property LABEL_TEMPLATE 282 * @type {String} 283 * @default <div class="[...-label]"><span id="" class="[...-label-string]">{label}</span><span class="[...-value-string]"></span></div> 284 * @protected 285 */ 286 287 Dial.LABEL_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.label + '"><span id="" class="' + Dial.CSS_CLASSES.labelString + '">{label}</span><span class="' + Dial.CSS_CLASSES.valueString + '"></span></div>'; 288 289 if(supportsVML === false){ 290 /** 291 * template that will contain the Dial's background ring. 292 * 293 * @property RING_TEMPLATE 294 * @type {String} 295 * @default <div class="[...-ring]"><div class="[...-northMark]"></div></div> 296 * @protected 297 */ 298 Dial.RING_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.ring + '"><div class="' + Dial.CSS_CLASSES.northMark + '"></div></div>'; 299 300 /** 301 * template that will contain the Dial's current angle marker. 302 * 303 * @property MARKER_TEMPLATE 304 * @type {String} 305 * @default <div class="[...-marker] [...-marker-hidden]"><div class="[...-markerUser]"></div></div> 306 * @protected 307 */ 308 Dial.MARKER_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.marker + ' ' + Dial.CSS_CLASSES.hidden + '"></div>'; 309 310 /** 311 * template that will contain the Dial's center button. 312 * 313 * @property CENTER_BUTTON_TEMPLATE 314 * @type {String} 315 * @default <div class="[...-centerButton]"><div class="[...-resetString]">' + Y.Lang.sub('{resetStr}', Dial.ATTRS.strings.value) + '</div></div> 316 * @protected 317 */ 318 Dial.CENTER_BUTTON_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.centerButton + '"><div class="' + Dial.CSS_CLASSES.resetString + ' ' + Dial.CSS_CLASSES.hidden + '">{resetStr}</div></div>'; 319 320 /** 321 * template that will contain the Dial's handle. 322 * 323 * @property HANDLE_TEMPLATE 324 * @type {String} 325 * @default <div class="[...-handle]"><div class="[...-handleUser]" aria-labelledby="" aria-valuetext="" aria-valuemax="" aria-valuemin="" aria-valuenow="" role="slider" tabindex="0"></div></div>';// title="{tooltipHandle}" 326 * @protected 327 */ 328 Dial.HANDLE_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.handle + '" aria-labelledby="" aria-valuetext="" aria-valuemax="" aria-valuemin="" aria-valuenow="" role="slider" tabindex="0" title="{tooltipHandle}">'; 329 330 }else{ // VML case 331 Dial.RING_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.ring + ' ' + Dial.CSS_CLASSES.ringVml + '">'+ 332 '<div class="' + Dial.CSS_CLASSES.northMark + '"></div>'+ 333 '<v:oval strokecolor="#ceccc0" strokeweight="1px"><v:fill type=gradient color="#8B8A7F" color2="#EDEDEB" angle="45"/></v:oval>'+ 334 '</div>'+ 335 ''; 336 Dial.MARKER_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.markerVml + ' ' + Dial.CSS_CLASSES.hidden + '">'+ 337 '<v:oval stroked="false">'+ 338 '<v:fill opacity="20%" color="#000"/>'+ 339 '</v:oval>'+ 340 '</div>'+ 341 ''; 342 Dial.CENTER_BUTTON_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.centerButton + ' ' + Dial.CSS_CLASSES.centerButtonVml + '">'+ 343 '<v:oval strokecolor="#ceccc0" strokeweight="1px">'+ 344 '<v:fill type=gradient color="#C7C5B9" color2="#fefcf6" colors="35% #d9d7cb, 65% #fefcf6" angle="45"/>'+ 345 '<v:shadow on="True" color="#000" opacity="10%" offset="2px, 2px"/>'+ 346 '</v:oval>'+ 347 '<div class="' + Dial.CSS_CLASSES.resetString + ' ' + Dial.CSS_CLASSES.hidden + '">{resetStr}</div>'+ 348 '</div>'+ 349 ''; 350 Dial.HANDLE_TEMPLATE = '<div class="' + Dial.CSS_CLASSES.handleVml + '" aria-labelledby="" aria-valuetext="" aria-valuemax="" aria-valuemin="" aria-valuenow="" role="slider" tabindex="0" title="{tooltipHandle}">'+ 351 '<v:oval stroked="false">'+ 352 '<v:fill opacity="20%" color="#6C3A3A"/>'+ 353 '</v:oval>'+ 354 '</div>'+ 355 ''; 356 } 357 358 /* Dial extends the base Widget class */ 359 Y.extend(Dial, Widget, { 360 361 /** 362 * creates the DOM structure for the Dial. 363 * 364 * @method renderUI 365 * @protected 366 */ 367 renderUI : function() { 368 this._renderLabel(); 369 this._renderRing(); 370 this._renderMarker(); 371 this._renderCenterButton(); 372 this._renderHandle(); 373 374 // object handles 375 this.contentBox = this.get("contentBox"); 376 377 // constants 378 this._originalValue = this.get('value'); 379 this._minValue = this.get('min'); // saves doing a .get many times, but we need to remember to update this if/when we allow changing min or max after instantiation 380 this._maxValue = this.get('max'); 381 this._stepsPerRevolution = this.get('stepsPerRevolution'); 382 this._minTimesWrapped = (Math.floor(this._minValue / this._stepsPerRevolution - 1)); 383 this._maxTimesWrapped = (Math.floor(this._maxValue / this._stepsPerRevolution + 1)); 384 385 // variables 386 this._timesWrapped = 0; 387 this._angle = this._getAngleFromValue(this.get('value')); 388 this._prevAng = this._angle; 389 390 // init 391 this._setTimesWrappedFromValue(this._originalValue); 392 this._handleNode.set('aria-valuemin', this._minValue); 393 this._handleNode.set('aria-valuemax', this._maxValue); 394 }, 395 396 /** 397 * Sets -webkit-border-radius to 50% of width/height of the ring, handle, marker, and center-button. 398 * This is needed for iOS 3.x. 399 * The objects render square if the radius is > 50% of the width/height 400 * @method _setBorderRadius 401 * @private 402 */ 403 _setBorderRadius : function(){ 404 this._ringNode.setStyles({'WebkitBorderRadius':this._ringNodeRadius + 'px', 405 'MozBorderRadius':this._ringNodeRadius + 'px', 406 'borderRadius':this._ringNodeRadius + 'px' 407 }); 408 this._handleNode.setStyles({'WebkitBorderRadius':this._handleNodeRadius + 'px', 409 'MozBorderRadius':this._handleNodeRadius + 'px', 410 'borderRadius':this._handleNodeRadius + 'px' 411 }); 412 this._markerNode.setStyles({'WebkitBorderRadius':this._markerNodeRadius + 'px', 413 'MozBorderRadius':this._markerNodeRadius + 'px', 414 'borderRadius':this._markerNodeRadius + 'px' 415 }); 416 this._centerButtonNode.setStyles({'WebkitBorderRadius':this._centerButtonNodeRadius + 'px', 417 'MozBorderRadius':this._centerButtonNodeRadius + 'px', 418 'borderRadius':this._centerButtonNodeRadius + 'px' 419 }); 420 }, 421 422 /** 423 * Handles the mouseenter on the centerButton 424 * 425 * @method _handleCenterButtonEnter 426 * @protected 427 */ 428 _handleCenterButtonEnter : function(){ 429 this._resetString.removeClass(Dial.CSS_CLASSES.hidden); 430 }, 431 432 /** 433 * Handles the mouseleave on the centerButton 434 * 435 * @method _handleCenterButtonLeave 436 * @protected 437 */ 438 _handleCenterButtonLeave : function(){ 439 this._resetString.addClass(Dial.CSS_CLASSES.hidden); 440 }, 441 442 /** 443 * Creates the Y.DD.Drag instance used for the handle movement and 444 * binds Dial interaction to the configured value model. 445 * 446 * @method bindUI 447 * @protected 448 */ 449 bindUI : function() { 450 451 this.after("valueChange", this._afterValueChange); 452 453 var boundingBox = this.get("boundingBox"), 454 // Looking for a key event which will fire continously across browsers while the key is held down. 455 keyEvent = (!Y.UA.opera) ? "down:" : "press:", 456 // 38, 40 = arrow up/down, 33, 34 = page up/down, 35 , 36 = end/home 457 keyEventSpec = keyEvent + "38,40,33,34,35,36", 458 // 37 , 39 = arrow left/right 459 keyLeftRightSpec = keyEvent + "37,39", 460 // 37 , 39 = arrow left/right + meta (command/apple key) for mac 461 keyLeftRightSpecMeta = keyEvent + "37+meta,39+meta", 462 Drag = Y.DD.Drag; 463 464 Y.on("key", Y.bind(this._onDirectionKey, this), boundingBox, keyEventSpec); 465 Y.on("key", Y.bind(this._onLeftRightKey, this), boundingBox, keyLeftRightSpec); 466 boundingBox.on("key", this._onLeftRightKeyMeta, keyLeftRightSpecMeta, this); 467 468 Y.on('mouseenter', Y.bind(this._handleCenterButtonEnter, this), this._centerButtonNode); 469 Y.on('mouseleave', Y.bind(this._handleCenterButtonLeave, this), this._centerButtonNode); 470 // Needed to replace mousedown/up with gesturemovestart/end to make behavior on touch devices work the same. 471 Y.on('gesturemovestart', Y.bind(this._resetDial, this), this._centerButtonNode); //[#2530441] 472 Y.on('gesturemoveend', Y.bind(this._handleCenterButtonMouseup, this), this._centerButtonNode); 473 474 475 Y.on(Drag.START_EVENT, Y.bind(this._handleHandleMousedown, this), this._handleNode); 476 Y.on(Drag.START_EVENT, Y.bind(this._handleMousedown, this), this._ringNode); // [#2530766] 477 478 //TODO: Can this be merged this into the drag:end event listener to avoid another registration? 479 Y.on('gesturemoveend', Y.bind(this._handleRingMouseup, this), this._ringNode); 480 481 this._dd1 = new Drag({ //// [#2530206] changed global this._dd1 from just var dd1 = new Y.DD.drag so 482 node: this._handleNode, 483 on : { 484 'drag:drag' : Y.bind(this._handleDrag, this), 485 'drag:start' : Y.bind(this._handleDragStart, this), 486 'drag:end' : Y.bind(this._handleDragEnd, this) //, 487 } 488 }); 489 Y.bind(this._dd1.addHandle(this._ringNode), this); // [#2530206] added the ring as a handle to the dd1 (the dd of the handleNode) 490 }, 491 492 /** 493 * Sets _timesWrapped based on Dial value 494 * to net integer revolutions the user dragged the handle around the Dial 495 * 496 * @method _setTimesWrappedFromValue 497 * @param val {Number} current value of the Dial 498 * @private 499 */ 500 _setTimesWrappedFromValue : function(val){ 501 if(val % this._stepsPerRevolution === 0){ 502 this._timesWrapped = (val / this._stepsPerRevolution); 503 }else{ 504 this._timesWrapped = Math.floor(val / this._stepsPerRevolution); 505 } 506 }, 507 508 /** 509 * gets the angle of the line from the center of the Dial to the center of the handle 510 * 511 * @method _getAngleFromHandleCenter 512 * @param handleCenterX {number} 513 * @param handleCenterY {number} 514 * @return ang {number} the angle 515 * @protected 516 */ 517 _getAngleFromHandleCenter : function(handleCenterX, handleCenterY){ 518 var ang = Math.atan( (this._dialCenterY - handleCenterY) / (this._dialCenterX - handleCenterX) ) * (180 / Math.PI); 519 ang = ((this._dialCenterX - handleCenterX) < 0) ? ang + 90 : ang + 90 + 180; // Compensate for neg angles from Math.atan 520 return ang; 521 }, 522 523 /** 524 * calculates the XY of the center of the dial relative to the ring node. 525 * This is needed for calculating the angle of the handle 526 * 527 * @method _calculateDialCenter 528 * @protected 529 */ 530 _calculateDialCenter : function(){ // #2531111 value, and marker don't track handle when dial position changes on page (resize when inline) 531 this._dialCenterX = this._ringNode.get('offsetWidth') / 2; 532 this._dialCenterY = this._ringNode.get('offsetHeight') / 2; 533 }, 534 535 /** 536 * Handles the mouseup on the ring 537 * 538 * @method _handleRingMouseup 539 * @protected 540 */ 541 _handleRingMouseup : function(){ 542 this._handleNode.focus(); // need to re-focus on the handle so keyboard is accessible [#2530206] 543 }, 544 545 /** 546 * Handles the mouseup on the centerButton 547 * 548 * @method _handleCenterButtonMouseup 549 * @protected 550 */ 551 _handleCenterButtonMouseup : function(){ 552 this._handleNode.focus(); // need to re-focus on the handle so keyboard is accessible [#2530206] 553 }, 554 555 /** 556 * Handles the mousedown on the handle 557 * 558 * @method _handleHandleMousedown 559 * @protected 560 */ 561 _handleHandleMousedown : function(){ 562 this._handleNode.focus(); // need to re-focus on the handle so keyboard is accessible [#2530206] 563 // this is better done here instead of on _handleDragEnd 564 // because we should make the keyboard accessible after a click of the handle 565 }, 566 567 /** 568 * handles the user dragging the handle around the Dial, gets the angle, 569 * checks for wrapping around top center. 570 * Sets the new value of the Dial 571 * 572 * @method _handleDrag 573 * @param e {DOMEvent} the drag event object 574 * @protected 575 */ 576 _handleDrag : function(e){ 577 var handleCenterX, 578 handleCenterY, 579 ang, 580 newValue; 581 582 // The event was emitted from drag:drag of handle. 583 // The center of the handle is top left position of the handle node + radius of handle. 584 // This is different than a mousedown on the ring. 585 handleCenterX = (parseInt(this._handleNode.getStyle('left'),10) + this._handleNodeRadius); 586 handleCenterY = (parseInt(this._handleNode.getStyle('top'),10) + this._handleNodeRadius); 587 ang = this._getAngleFromHandleCenter(handleCenterX, handleCenterY); 588 589 // check for need to set timesWrapped 590 if((this._prevAng > 270) && (ang < 90)){ // If wrapping, clockwise 591 if(this._timesWrapped < this._maxTimesWrapped){ 592 this._timesWrapped = (this._timesWrapped + 1); 593 } 594 }else if((this._prevAng < 90) && (ang > 270)){ // if un-wrapping, counter-clockwise 595 if(this._timesWrapped > this._minTimesWrapped){ 596 this._timesWrapped = (this._timesWrapped - 1); 597 } 598 } 599 newValue = this._getValueFromAngle(ang); // This function needs the current _timesWrapped value. That's why it comes after the _timesWrapped code above 600 601 // If you've gone past max more than one full revolution, we decrement the _timesWrapped value 602 // This gives the effect of a ratchet mechanism. 603 // It feels like you are never more than one revolution past max 604 // The effect is the same for min, only in reverse. 605 // We can't reset the _timesWrapped to the max or min here. 606 // If we did, the next (continuous) drag would reset the value incorrectly. 607 if(newValue > (this._maxValue + this._stepsPerRevolution) ){ 608 this._timesWrapped --; 609 }else if(newValue < (this._minValue - this._stepsPerRevolution) ){ 610 this._timesWrapped ++; 611 } 612 this._prevAng = ang; // need to keep the previous angle in order to check for wrapping on the next drag, click, or keypress 613 614 this._handleValuesBeyondMinMax(e, newValue); 615 }, 616 617 /** 618 * handles a mousedown or gesturemovestart event on the ring node 619 * 620 * @method _handleMousedown 621 * @param e {DOMEvent} the event object 622 * @private 623 */ 624 _handleMousedown : function(e){ // #2530306 625 626 if (this._ringNode.compareTo(e.target)) { 627 var minAng = this._getAngleFromValue(this._minValue), 628 maxAng = this._getAngleFromValue(this._maxValue), 629 newValue, oppositeMidRangeAngle, 630 handleCenterX, handleCenterY, 631 ang; 632 633 634 635 // The event was emitted from mousedown on the ring node, 636 // so the center of the handle should be the XY of mousedown. 637 if(Y.UA.ios){ // ios adds the scrollLeft and top onto clientX and Y in a native click 638 handleCenterX = (e.clientX - this._ringNode.getX()); 639 handleCenterY = (e.clientY - this._ringNode.getY()); 640 }else{ 641 handleCenterX = (e.clientX + Y.one('document').get('scrollLeft') - this._ringNode.getX()); 642 handleCenterY = (e.clientY + Y.one('document').get('scrollTop') - this._ringNode.getY()); 643 } 644 ang = this._getAngleFromHandleCenter(handleCenterX, handleCenterY); 645 646 /* /////////////////////////////////////////////////////////////////////////////////////////////////////// 647 * The next sections of logic 648 * set this._timesWrapped in the different cases of value range 649 * and value range position, 650 * then the Dial value is set at the end of this method 651 */ /////////////////////////////////////////////////////////////////////////////////////////////////////// 652 653 654 //////////////////////////////////////////////////////////////////////////////////////////////////////////// 655 if(this._maxValue - this._minValue > this._stepsPerRevolution){ 656 657 // Case: range min-to-max is greater than stepsPerRevolution (one revolution) 658 659 // This checks the shortest way around the dial between the prevAng and this ang. 660 if(Math.abs(this._prevAng - ang) > 180){ // this crossed a wrapping 661 662 // Only change the _timesWrapped if it's between minTimesWrapped and maxTimesWrapped 663 if((this._timesWrapped > this._minTimesWrapped) && 664 (this._timesWrapped < this._maxTimesWrapped) 665 ){ 666 // this checks which direction, clock wise or CCW and incr or decr _timesWrapped 667 this._timesWrapped = ((this._prevAng - ang) > 0) ? (this._timesWrapped + 1) : (this._timesWrapped - 1); 668 } 669 // special case of getting un-stuck from a min value case 670 // where timesWrapped is minTimesWrapped but new ang won't trigger a cross wrap boundry 671 // because prevAng is set to 0 or > 0 672 }else if( 673 (this._timesWrapped === this._minTimesWrapped) && 674 (ang - this._prevAng < 180) 675 ){ 676 this._timesWrapped ++; 677 } //it didn't cross a wrapping boundary 678 679 } ///////////////////////////////////////////////////////////////////////////////////////////////////////// 680 else if(this._maxValue - this._minValue === this._stepsPerRevolution){ 681 // Case: range min-to-max === stepsPerRevolution (one revolution) 682 // This means min and max will be at same angle 683 // This does not mean they are at "north" 684 685 if(ang < minAng){ // if mousedown angle is < minAng (and maxAng, because they're the same) 686 // The only way it can be, is if min and max are not at north 687 this._timesWrapped = 1; 688 }else{ 689 this._timesWrapped = 0; 690 } 691 692 } ////////////////////////////////////////////////////////////////////////////////////////////////////////// 693 else if(minAng > maxAng){ 694 // Case: range includes the wrap point (north) 695 // Because of "else if"... 696 // range is < stepsPerRevolution 697 698 if( 699 (this._prevAng >= minAng) && // if prev angle was greater than angle of min and... 700 (ang <= (minAng + maxAng) / 2) // the angle of this click is less than 701 // the angle opposite the mid-range angle, then... 702 ){ 703 this._timesWrapped ++; 704 }else if( 705 (this._prevAng <= maxAng) && 706 // if prev angle is < max angle and... 707 708 (ang > (minAng + maxAng) / 2) 709 // the angle of this click is greater than, 710 // the angle opposite the mid-range angle and... 711 712 ){ 713 this._timesWrapped --; 714 } 715 716 } //////////////////////////////////////////////////////////////////////////////////////////////////// 717 else{ 718 // "else" Case: min-to-max range doesn't include the wrap point 719 // Because of "else if"... 720 // range is still < stepsPerRevolution 721 722 if ((ang < minAng) || (ang > maxAng)){ // angle is out of range 723 oppositeMidRangeAngle = (((minAng + maxAng) / 2) + 180) % 360; 724 // This is the bisection of the min-to-max range + 180. (opposite the bisection) 725 726 if(oppositeMidRangeAngle > 180){ 727 newValue = ((maxAng < ang) && (ang < oppositeMidRangeAngle)) ? this.get('max') : this.get('min'); 728 }else{ //oppositeMidRangeAngle <= 180 729 newValue = ((minAng > ang) && (ang > oppositeMidRangeAngle)) ? this.get('min') : this.get('max'); 730 } 731 this._prevAng = this._getAngleFromValue(newValue); 732 this.set('value', newValue); 733 this._setTimesWrappedFromValue(newValue); 734 return; 735 } 736 } 737 738 // Now that _timesWrapped is set, set newValue ....................................................................... 739 newValue = this._getValueFromAngle(ang); // This function needs the correct, current _timesWrapped value. 740 741 742 /* updating _prevAng (previous angle) 743 * When past min or max, _prevAng is set to the angle of min or max 744 * Don't do this in a drag method, or it will affect wrapping, 745 * causing the marker to stick at min, when min is 0 degrees (north) 746 * #2532878 747 */ 748 if (newValue > this._maxValue) { 749 this._prevAng = this._getAngleFromValue(this._maxValue); // #2530766 need for mousedown on the ring; causes prob for drag 750 } else if (newValue < this._minValue) { 751 this._prevAng = this._getAngleFromValue(this._minValue); 752 } else { 753 this._prevAng = ang; 754 } 755 756 this._handleValuesBeyondMinMax(e, newValue); 757 } 758 }, 759 760 /** 761 * handles the case where the value is less than min or greater than max 762 * This is used both when handle is dragged and when the ring is clicked 763 * 764 * @method _handleValuesBeyondMinMax 765 * @param e {DOMEvent} the event object 766 * @param newValue {number} current value of the dial 767 * @protected 768 */ 769 _handleValuesBeyondMinMax : function(e, newValue){ // #2530306 770 // If _getValueFromAngle() is passed 0, it increments the _timesWrapped value. 771 // handle hitting max and min and going beyond, stops at max or min 772 if((newValue >= this._minValue) && (newValue <= this._maxValue)) { 773 this.set('value', newValue); 774 // [#2530206] transfer the mousedown event from the _ringNode to the _handleNode drag, so we can mousedown, then continue dragging 775 if(e.currentTarget === this._ringNode){ 776 // Delegate to DD's natural behavior 777 this._dd1._handleMouseDownEvent(e); 778 } 779 } else if (newValue > this._maxValue) { 780 this.set('value', this._maxValue); 781 } else if (newValue < this._minValue) { 782 this.set('value', this._minValue); 783 } 784 }, 785 786 /** 787 * handles the user starting to drag the handle around the Dial 788 * 789 * @method _handleDragStart 790 * @param e {DOMEvent} the drag event object 791 * @protected 792 */ 793 _handleDragStart : function(e){ 794 this._markerNode.removeClass(Dial.CSS_CLASSES.hidden); 795 }, 796 797 /* 798 * When handle is handleDragEnd, this animates the return to the fixed dial 799 */ 800 801 /** 802 * handles the end of a user dragging the handle, animates the handle returning to 803 * resting position. 804 * 805 * @method _handleDragEnd 806 * @protected 807 */ 808 _handleDragEnd : function(){ 809 var node = this._handleNode; 810 node.transition({ 811 duration: 0.08, // seconds 812 easing: 'ease-in', 813 left: this._setNodeToFixedRadius(this._handleNode, true)[0] + 'px', 814 top: this._setNodeToFixedRadius(this._handleNode, true)[1] + 'px' 815 }, Y.bind(function(){ 816 var value = this.get('value'); 817 //[#2530206] only hide marker if not at max or min 818 // more persistant user visibility of when the dial is at max or min 819 if((value > this._minValue) && (value < this._maxValue)){ 820 this._markerNode.addClass(Dial.CSS_CLASSES.hidden); 821 }else{ 822 this._setTimesWrappedFromValue(value); //#2530766 secondary bug when drag past max + cross wrapping boundry 823 this._prevAng = this._getAngleFromValue(value); //#2530766 secondary bug when drag past max + cross wrapping boundry 824 } 825 }, this) 826 ); 827 }, 828 829 /** 830 * returns the XY of the fixed position, handleDistance, from the center of the Dial (resting position). 831 * The XY also represents the angle related to the current value. 832 * If typeArray is true, [X,Y] is returned. 833 * If typeArray is false, the XY of the obj node passed in is set. 834 * 835 * @method _setNodeToFixedRadius 836 * @param obj {Node} 837 * @param typeArray {Boolean} true returns an array [X,Y] 838 * @protected 839 * @return {Array} an array of [XY] is optionally returned 840 */ 841 _setNodeToFixedRadius : function(obj, typeArray){ 842 var thisAngle = (this._angle - 90), 843 rad = (Math.PI / 180), 844 newY = Math.round(Math.sin(thisAngle * rad) * this._handleDistance), 845 newX = Math.round(Math.cos(thisAngle * rad) * this._handleDistance), 846 dia = obj.get('offsetWidth'); //Ticket #2529852 847 848 newY = newY - (dia * 0.5); 849 newX = newX - (dia * 0.5); 850 if(typeArray){ // just need the style for css transform left and top to animate the handle drag:end 851 return [(this._ringNodeRadius + newX), (this._ringNodeRadius + newY)]; 852 }else{ 853 obj.setStyle('left', (this._ringNodeRadius + newX) + 'px'); 854 obj.setStyle('top', (this._ringNodeRadius + newY) + 'px'); 855 } 856 }, 857 858 /** 859 * Synchronizes the DOM state with the attribute settings. 860 * 861 * @method syncUI 862 */ 863 syncUI : function() { 864 // Make the marker and the resetString display so their placement and borderRadius can be calculated, then hide them again. 865 // We would have used visibility:hidden in the css of this class, 866 // but IE8 VML never returns to visible after applying visibility:hidden then removing it. 867 this._setSizes(); 868 this._calculateDialCenter(); // #2531111 initialize center of dial 869 this._setBorderRadius(); 870 this._uiSetValue(this.get("value")); 871 this._markerNode.addClass(Dial.CSS_CLASSES.hidden); 872 this._resetString.addClass(Dial.CSS_CLASSES.hidden); 873 }, 874 875 /** 876 * sets the sizes of ring, center-button, marker, handle, and VML ovals in pixels. 877 * Needed only because some IE versions 878 * ignore CSS percent sizes/offsets. 879 * so these must be set in pixels. 880 * Normally these are set in % of the ring. 881 * 882 * @method _setSizes 883 * @protected 884 */ 885 _setSizes : function(){ 886 var dia = this.get('diameter'), 887 offset, offsetResetX, offsetResetY, 888 setSize = function(node, dia, percent){ 889 var suffix = 'px'; 890 node.getElementsByTagName('oval').setStyle('width', (dia * percent) + suffix); 891 node.getElementsByTagName('oval').setStyle('height', (dia * percent) + suffix); 892 node.setStyle('width', (dia * percent) + suffix); 893 node.setStyle('height', (dia * percent) + suffix); 894 }; 895 setSize(this._ringNode, dia, 1.0); 896 setSize(this._handleNode, dia, this.get('handleDiameter')); 897 setSize(this._markerNode, dia, this.get('markerDiameter')); 898 setSize(this._centerButtonNode, dia, this.get('centerButtonDiameter')); 899 900 // Set these (used for trig) this way instead of relative to dia, 901 // in case they have borders, have images etc. 902 this._ringNodeRadius = this._ringNode.get('offsetWidth') * 0.5; 903 this._handleNodeRadius = this._handleNode.get('offsetWidth') * 0.5; 904 this._markerNodeRadius = this._markerNode.get('offsetWidth') * 0.5; 905 this._centerButtonNodeRadius = this._centerButtonNode.get('offsetWidth') * 0.5; 906 this._handleDistance = this._ringNodeRadius * this.get('handleDistance'); 907 // place the centerButton 908 offset = (this._ringNodeRadius - this._centerButtonNodeRadius); 909 this._centerButtonNode.setStyle('left', offset + 'px'); 910 this._centerButtonNode.setStyle('top', offset + 'px'); 911 /* 912 Place the resetString 913 This seems like it should be able to be done with CSS, 914 But since there is also a VML oval in IE that is absolute positioned, 915 The resetString ends up behind the VML oval. 916 */ 917 offsetResetX = (this._centerButtonNodeRadius - (this._resetString.get('offsetWidth') * 0.5)); 918 offsetResetY = (this._centerButtonNodeRadius - (this._resetString.get('offsetHeight') * 0.5)); 919 this._resetString.setStyles({'left':offsetResetX + 'px', 'top':offsetResetY + 'px'}); 920 }, 921 922 923 /** 924 * renders the DOM object for the Dial's label 925 * 926 * @method _renderLabel 927 * @protected 928 */ 929 _renderLabel : function() { 930 var contentBox = this.get("contentBox"), 931 label = contentBox.one("." + Dial.CSS_CLASSES.label); 932 if (!label) { 933 label = Node.create(Y.Lang.sub(Dial.LABEL_TEMPLATE, this.get('strings'))); 934 contentBox.append(label); 935 } 936 this._labelNode = label; 937 this._valueStringNode = this._labelNode.one("." + Dial.CSS_CLASSES.valueString); 938 }, 939 940 /** 941 * renders the DOM object for the Dial's background ring 942 * 943 * @method _renderRing 944 * @protected 945 */ 946 _renderRing : function() { 947 var contentBox = this.get("contentBox"), 948 ring = contentBox.one("." + Dial.CSS_CLASSES.ring); 949 if (!ring) { 950 ring = contentBox.appendChild(Dial.RING_TEMPLATE); 951 ring.setStyles({width:this.get('diameter') + 'px', height:this.get('diameter') + 'px'}); 952 } 953 this._ringNode = ring; 954 }, 955 956 /** 957 * renders the DOM object for the Dial's background marker which 958 * tracks the angle of the user dragging the handle 959 * 960 * @method _renderMarker 961 * @protected 962 */ 963 _renderMarker : function() { 964 var contentBox = this.get("contentBox"), 965 marker = contentBox.one("." + Dial.CSS_CLASSES.marker); 966 if (!marker) { 967 marker = contentBox.one('.' + Dial.CSS_CLASSES.ring).appendChild(Dial.MARKER_TEMPLATE); 968 } 969 this._markerNode = marker; 970 }, 971 972 /** 973 * renders the DOM object for the Dial's center 974 * 975 * @method _renderCenterButton 976 * @protected 977 */ 978 _renderCenterButton : function() { 979 var contentBox = this.get("contentBox"), 980 centerButton = contentBox.one("." + Dial.CSS_CLASSES.centerButton); 981 if (!centerButton) { 982 centerButton = Node.create(Y.Lang.sub(Dial.CENTER_BUTTON_TEMPLATE, this.get('strings'))); 983 contentBox.one('.' + Dial.CSS_CLASSES.ring).append(centerButton); 984 } 985 this._centerButtonNode = centerButton; 986 this._resetString = this._centerButtonNode.one('.' + Dial.CSS_CLASSES.resetString); 987 }, 988 989 /** 990 * renders the DOM object for the Dial's user draggable handle 991 * 992 * @method _renderHandle 993 * @protected 994 */ 995 _renderHandle : function() { 996 var labelId = Dial.CSS_CLASSES.label + Y.guid(), //get this unique id once then use for handle and label for ARIA 997 contentBox = this.get("contentBox"), 998 handle = contentBox.one("." + Dial.CSS_CLASSES.handle); 999 if (!handle) { 1000 handle = Node.create(Y.Lang.sub(Dial.HANDLE_TEMPLATE, this.get('strings'))); 1001 handle.setAttribute('aria-labelledby', labelId); // get unique id for specifying a label & handle for ARIA 1002 this._labelNode.one('.' + Dial.CSS_CLASSES.labelString).setAttribute('id', labelId); // When handle gets focus, screen reader will include label text when reading the value. 1003 contentBox.one('.' + Dial.CSS_CLASSES.ring).append(handle); 1004 } 1005 this._handleNode = handle; 1006 }, 1007 1008 /** 1009 * sets the visible UI label HTML string 1010 * 1011 * @method _setLabelString 1012 * @param str {String} 1013 * @protected 1014 * @deprecated Use DialObjName.set('strings',{'label':'My new label'}); before DialObjName.render(); 1015 1016 */ 1017 _setLabelString : function(str) { 1018 this.get("contentBox").one("." + Dial.CSS_CLASSES.labelString).setHTML(str); 1019 }, 1020 1021 /** 1022 * sets the visible UI label HTML string 1023 * 1024 * @method _setResetString 1025 * @param str {String} 1026 * @protected 1027 * @deprecated Use DialObjName.set('strings',{'resetStr':'My new reset string'}); before DialObjName.render(); 1028 */ 1029 _setResetString : function(str) { 1030 this.get("contentBox").one("." + Dial.CSS_CLASSES.resetString).setHTML(str); 1031 // this._setXYResetString(); // This used to recenter the string in the button. Done with CSS now. Method has been removed. 1032 // this._resetString.setHTML(''); //We no longer show/hide the reset string with setHTML but by addClass and removeClass .yui3-dial-reset-string-hidden 1033 }, 1034 1035 /** 1036 * sets the tooltip HTML string in the Dial's handle 1037 * 1038 * @method _setTooltipString 1039 * @param str {String} 1040 * @protected 1041 * @deprecated Use DialObjName.set('strings',{'tooltipHandle':'My new tooltip'}); before DialObjName.render(); 1042 */ 1043 _setTooltipString : function(str) { 1044 this._handleNode.set('title', str); 1045 }, 1046 1047 /** 1048 * sets the Dial's value in response to key events. 1049 * Left and right keys are in a separate method 1050 * in case an implementation wants to increment values 1051 * but needs left and right arrow keys for other purposes. 1052 * 1053 * @method _onDirectionKey 1054 * @param e {Event} the key event 1055 * @protected 1056 */ 1057 _onDirectionKey : function(e) { 1058 e.preventDefault(); 1059 switch (e.charCode) { 1060 case 38: // up 1061 this._incrMinor(); 1062 break; 1063 case 40: // down 1064 this._decrMinor(); 1065 break; 1066 case 36: // home 1067 this._setToMin(); 1068 break; 1069 case 35: // end 1070 this._setToMax(); 1071 break; 1072 case 33: // page up 1073 this._incrMajor(); 1074 break; 1075 case 34: // page down 1076 this._decrMajor(); 1077 break; 1078 } 1079 }, 1080 1081 /** 1082 * sets the Dial's value in response to left or right key events 1083 * 1084 * @method _onLeftRightKey 1085 * @param e {Event} the key event 1086 * @protected 1087 */ 1088 _onLeftRightKey : function(e) { 1089 e.preventDefault(); 1090 switch (e.charCode) { 1091 case 37: // left 1092 this._decrMinor(); 1093 break; 1094 case 39: // right 1095 this._incrMinor(); 1096 break; 1097 } 1098 }, 1099 1100 /** 1101 * sets the Dial's value in response to left or right key events when a meta (mac command/apple) key is also pressed 1102 * 1103 * @method _onLeftRightKeyMeta 1104 * @param e {Event} the key event 1105 * @protected 1106 */ 1107 _onLeftRightKeyMeta : function(e) { 1108 e.preventDefault(); 1109 switch (e.charCode) { 1110 case 37: // left + meta 1111 this._setToMin(); 1112 break; 1113 case 39: // right + meta 1114 this._setToMax(); 1115 break; 1116 } 1117 }, 1118 1119 /** 1120 * increments Dial value by a minor increment 1121 * 1122 * @method _incrMinor 1123 * @protected 1124 */ 1125 _incrMinor : function(){ 1126 var newVal = (this.get('value') + this.get("minorStep")); 1127 newVal = Math.min(newVal, this.get("max")); 1128 // [#2530045] .toFixed returns a string. 1129 // Dial's value needs a number. -0 makes it a number, but removes trailing zeros. 1130 // Added toFixed(...) again in _uiSetValue where content of yui3-dial-value-string is set. 1131 // Removing the toFixed here, loses the feature of "snap-to" when for example, stepsPerRevolution is 10 and decimalPlaces is 0. 1132 this.set('value', newVal.toFixed(this.get('decimalPlaces')) - 0); 1133 }, 1134 1135 /** 1136 * decrements Dial value by a minor increment 1137 * 1138 * @method _decrMinor 1139 * @protected 1140 */ 1141 _decrMinor : function(){ 1142 var newVal = (this.get('value') - this.get("minorStep")); 1143 newVal = Math.max(newVal, this.get("min")); 1144 this.set('value', newVal.toFixed(this.get('decimalPlaces')) - 0); 1145 }, 1146 1147 /** 1148 * increments Dial value by a major increment 1149 * 1150 * @method _incrMajor 1151 * @protected 1152 */ 1153 _incrMajor : function(){ 1154 var newVal = (this.get('value') + this.get("majorStep")); 1155 newVal = Math.min(newVal, this.get("max")); 1156 this.set('value', newVal.toFixed(this.get('decimalPlaces')) - 0); 1157 }, 1158 1159 /** 1160 * decrements Dial value by a major increment 1161 * 1162 * @method _decrMajor 1163 * @protected 1164 */ 1165 _decrMajor : function(){ 1166 var newVal = (this.get('value') - this.get("majorStep")); 1167 newVal = Math.max(newVal, this.get("min")); 1168 this.set('value', newVal.toFixed(this.get('decimalPlaces')) - 0); 1169 }, 1170 1171 /** 1172 * sets Dial value to dial's max attr 1173 * 1174 * @method _setToMax 1175 * @protected 1176 */ 1177 _setToMax : function(){ 1178 this.set('value', this.get("max")); 1179 }, 1180 1181 /** 1182 * sets Dial value to dial's min attr 1183 * 1184 * @method _setToMin 1185 * @protected 1186 */ 1187 _setToMin : function(){ 1188 this.set('value', this.get("min")); 1189 }, 1190 1191 /** 1192 * resets Dial value to the orignal initial value. 1193 * 1194 * @method _resetDial 1195 * @protected 1196 */ 1197 _resetDial : function(e){ 1198 if(e){ 1199 e.stopPropagation(); //[#2530206] need to add so mousedown doesn't propagate to ring and move the handle 1200 } 1201 this.set('value', this._originalValue); 1202 this._resetString.addClass(Dial.CSS_CLASSES.hidden); //[#2530441] 1203 this._handleNode.focus(); 1204 }, 1205 1206 /** 1207 * returns the handle angle associated with the current value of the Dial. 1208 * Returns a number between 0 and 360. 1209 * 1210 * @method _getAngleFromValue 1211 * @param newVal {Number} the current value of the Dial 1212 * @return {Number} the angle associated with the current Dial value 1213 * @protected 1214 */ 1215 _getAngleFromValue : function(newVal){ 1216 var nonWrappedPartOfValue = newVal % this._stepsPerRevolution, 1217 angleFromValue = nonWrappedPartOfValue / this._stepsPerRevolution * 360; 1218 return (angleFromValue < 0) ? (angleFromValue + 360) : angleFromValue; 1219 }, 1220 1221 /** 1222 * returns the value of the Dial calculated from the current handle angle 1223 * 1224 * @method _getValueFromAngle 1225 * @param angle {Number} the current angle of the Dial's handle 1226 * @return {Number} the current Dial value corresponding to the handle position 1227 * @protected 1228 */ 1229 _getValueFromAngle : function(angle){ 1230 if(angle < 0){ 1231 angle = (360 + angle); 1232 }else if(angle === 0){ 1233 angle = 360; 1234 } 1235 var value = (angle / 360) * this._stepsPerRevolution; 1236 value = (value + (this._timesWrapped * this._stepsPerRevolution)); 1237 //return Math.round(value * 100) / 100; 1238 return value.toFixed(this.get('decimalPlaces')) - 0; 1239 }, 1240 1241 /** 1242 * calls the method to update the UI whenever the Dial value changes 1243 * 1244 * @method _afterValueChange 1245 * @param e {Event} 1246 * @protected 1247 */ 1248 _afterValueChange : function(e) { 1249 this._uiSetValue(e.newVal); 1250 }, 1251 1252 /** 1253 * Changes a value to have the correct decimal places per the attribute decimalPlaces 1254 * 1255 * @method _valueToDecimalPlaces 1256 * @param val {Number} a raw value to set to the Dial 1257 * @return {Number} the input val changed to have the correct decimal places 1258 * @protected 1259 */ 1260 _valueToDecimalPlaces : function(val) { // [#2530206] cleaned up and better user feedback of when it's max or min. 1261 1262 }, 1263 1264 /** 1265 * Updates the UI display value of the Dial to reflect 1266 * the value passed in. 1267 * Makes all other needed UI display changes 1268 * 1269 * @method _uiSetValue 1270 * @param val {Number} value of the Dial 1271 * @protected 1272 */ 1273 _uiSetValue : function(val) { // [#2530206] cleaned up and better user feedback of when it's max or min. 1274 this._angle = this._getAngleFromValue(val); 1275 if(this._handleNode.hasClass(Dial.CSS_CLASSES.dragging) === false){ 1276 this._setTimesWrappedFromValue(val); 1277 this._setNodeToFixedRadius(this._handleNode, false); 1278 this._prevAng = this._getAngleFromValue(this.get('value')); 1279 } 1280 this._valueStringNode.setHTML(val.toFixed(this.get('decimalPlaces'))); // [#2530045] 1281 this._handleNode.set('aria-valuenow', val); 1282 this._handleNode.set('aria-valuetext', val); 1283 this._setNodeToFixedRadius(this._markerNode, false); 1284 if((val === this._maxValue) || (val === this._minValue)){ 1285 this._markerNode.addClass(Dial.CSS_CLASSES.markerMaxMin); 1286 if(supportsVML === true){ 1287 this._markerNode.getElementsByTagName('fill').set('color', '#AB3232'); 1288 } 1289 this._markerNode.removeClass(Dial.CSS_CLASSES.hidden); 1290 }else{ // not max or min 1291 if(supportsVML === true){ 1292 this._markerNode.getElementsByTagName('fill').set('color', '#000'); 1293 } 1294 this._markerNode.removeClass(Dial.CSS_CLASSES.markerMaxMin); 1295 if(this._handleNode.hasClass(Dial.CSS_CLASSES.dragging) === false){ // if not max || min, and not dragging handle, hide the marker 1296 this._markerNode.addClass(Dial.CSS_CLASSES.hidden); 1297 } 1298 } 1299 }, 1300 1301 /** 1302 * value attribute default validator. Verifies that 1303 * the value being set lies between the min/max value 1304 * 1305 * @method _validateValue 1306 * @param val {Number} value of the Dial 1307 * @protected 1308 */ 1309 _validateValue: function(val) { 1310 var min = this.get("min"), 1311 max = this.get("max"); 1312 return (Lang.isNumber(val) && val >= min && val <= max); 1313 } 1314 }); 1315 Y.Dial = Dial; 1316 1317 1318 }, '3.17.2', { 1319 "requires": [ 1320 "widget", 1321 "dd-drag", 1322 "event-mouseenter", 1323 "event-move", 1324 "event-key", 1325 "transition", 1326 "intl" 1327 ], 1328 "lang": [ 1329 "en", 1330 "es", 1331 "hu" 1332 ], 1333 "skinnable": true 1334 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |