[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/js/jquery/ui/ -> spinner.js (source)

   1  /*!
   2   * jQuery UI Spinner 1.13.2
   3   * http://jqueryui.com
   4   *
   5   * Copyright jQuery Foundation and other contributors
   6   * Released under the MIT license.
   7   * http://jquery.org/license
   8   */
   9  
  10  //>>label: Spinner
  11  //>>group: Widgets
  12  //>>description: Displays buttons to easily input numbers via the keyboard or mouse.
  13  //>>docs: http://api.jqueryui.com/spinner/
  14  //>>demos: http://jqueryui.com/spinner/
  15  //>>css.structure: ../../themes/base/core.css
  16  //>>css.structure: ../../themes/base/spinner.css
  17  //>>css.theme: ../../themes/base/theme.css
  18  
  19  ( function( factory ) {
  20      "use strict";
  21  
  22      if ( typeof define === "function" && define.amd ) {
  23  
  24          // AMD. Register as an anonymous module.
  25          define( [
  26              "jquery",
  27              "./button",
  28              "./core"
  29          ], factory );
  30      } else {
  31  
  32          // Browser globals
  33          factory( jQuery );
  34      }
  35  } )( function( $ ) {
  36  "use strict";
  37  
  38  function spinnerModifier( fn ) {
  39      return function() {
  40          var previous = this.element.val();
  41          fn.apply( this, arguments );
  42          this._refresh();
  43          if ( previous !== this.element.val() ) {
  44              this._trigger( "change" );
  45          }
  46      };
  47  }
  48  
  49  $.widget( "ui.spinner", {
  50      version: "1.13.2",
  51      defaultElement: "<input>",
  52      widgetEventPrefix: "spin",
  53      options: {
  54          classes: {
  55              "ui-spinner": "ui-corner-all",
  56              "ui-spinner-down": "ui-corner-br",
  57              "ui-spinner-up": "ui-corner-tr"
  58          },
  59          culture: null,
  60          icons: {
  61              down: "ui-icon-triangle-1-s",
  62              up: "ui-icon-triangle-1-n"
  63          },
  64          incremental: true,
  65          max: null,
  66          min: null,
  67          numberFormat: null,
  68          page: 10,
  69          step: 1,
  70  
  71          change: null,
  72          spin: null,
  73          start: null,
  74          stop: null
  75      },
  76  
  77      _create: function() {
  78  
  79          // handle string values that need to be parsed
  80          this._setOption( "max", this.options.max );
  81          this._setOption( "min", this.options.min );
  82          this._setOption( "step", this.options.step );
  83  
  84          // Only format if there is a value, prevents the field from being marked
  85          // as invalid in Firefox, see #9573.
  86          if ( this.value() !== "" ) {
  87  
  88              // Format the value, but don't constrain.
  89              this._value( this.element.val(), true );
  90          }
  91  
  92          this._draw();
  93          this._on( this._events );
  94          this._refresh();
  95  
  96          // Turning off autocomplete prevents the browser from remembering the
  97          // value when navigating through history, so we re-enable autocomplete
  98          // if the page is unloaded before the widget is destroyed. #7790
  99          this._on( this.window, {
 100              beforeunload: function() {
 101                  this.element.removeAttr( "autocomplete" );
 102              }
 103          } );
 104      },
 105  
 106      _getCreateOptions: function() {
 107          var options = this._super();
 108          var element = this.element;
 109  
 110          $.each( [ "min", "max", "step" ], function( i, option ) {
 111              var value = element.attr( option );
 112              if ( value != null && value.length ) {
 113                  options[ option ] = value;
 114              }
 115          } );
 116  
 117          return options;
 118      },
 119  
 120      _events: {
 121          keydown: function( event ) {
 122              if ( this._start( event ) && this._keydown( event ) ) {
 123                  event.preventDefault();
 124              }
 125          },
 126          keyup: "_stop",
 127          focus: function() {
 128              this.previous = this.element.val();
 129          },
 130          blur: function( event ) {
 131              if ( this.cancelBlur ) {
 132                  delete this.cancelBlur;
 133                  return;
 134              }
 135  
 136              this._stop();
 137              this._refresh();
 138              if ( this.previous !== this.element.val() ) {
 139                  this._trigger( "change", event );
 140              }
 141          },
 142          mousewheel: function( event, delta ) {
 143              var activeElement = $.ui.safeActiveElement( this.document[ 0 ] );
 144              var isActive = this.element[ 0 ] === activeElement;
 145  
 146              if ( !isActive || !delta ) {
 147                  return;
 148              }
 149  
 150              if ( !this.spinning && !this._start( event ) ) {
 151                  return false;
 152              }
 153  
 154              this._spin( ( delta > 0 ? 1 : -1 ) * this.options.step, event );
 155              clearTimeout( this.mousewheelTimer );
 156              this.mousewheelTimer = this._delay( function() {
 157                  if ( this.spinning ) {
 158                      this._stop( event );
 159                  }
 160              }, 100 );
 161              event.preventDefault();
 162          },
 163          "mousedown .ui-spinner-button": function( event ) {
 164              var previous;
 165  
 166              // We never want the buttons to have focus; whenever the user is
 167              // interacting with the spinner, the focus should be on the input.
 168              // If the input is focused then this.previous is properly set from
 169              // when the input first received focus. If the input is not focused
 170              // then we need to set this.previous based on the value before spinning.
 171              previous = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] ) ?
 172                  this.previous : this.element.val();
 173  			function checkFocus() {
 174                  var isActive = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] );
 175                  if ( !isActive ) {
 176                      this.element.trigger( "focus" );
 177                      this.previous = previous;
 178  
 179                      // support: IE
 180                      // IE sets focus asynchronously, so we need to check if focus
 181                      // moved off of the input because the user clicked on the button.
 182                      this._delay( function() {
 183                          this.previous = previous;
 184                      } );
 185                  }
 186              }
 187  
 188              // Ensure focus is on (or stays on) the text field
 189              event.preventDefault();
 190              checkFocus.call( this );
 191  
 192              // Support: IE
 193              // IE doesn't prevent moving focus even with event.preventDefault()
 194              // so we set a flag to know when we should ignore the blur event
 195              // and check (again) if focus moved off of the input.
 196              this.cancelBlur = true;
 197              this._delay( function() {
 198                  delete this.cancelBlur;
 199                  checkFocus.call( this );
 200              } );
 201  
 202              if ( this._start( event ) === false ) {
 203                  return;
 204              }
 205  
 206              this._repeat( null, $( event.currentTarget )
 207                  .hasClass( "ui-spinner-up" ) ? 1 : -1, event );
 208          },
 209          "mouseup .ui-spinner-button": "_stop",
 210          "mouseenter .ui-spinner-button": function( event ) {
 211  
 212              // button will add ui-state-active if mouse was down while mouseleave and kept down
 213              if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
 214                  return;
 215              }
 216  
 217              if ( this._start( event ) === false ) {
 218                  return false;
 219              }
 220              this._repeat( null, $( event.currentTarget )
 221                  .hasClass( "ui-spinner-up" ) ? 1 : -1, event );
 222          },
 223  
 224          // TODO: do we really want to consider this a stop?
 225          // shouldn't we just stop the repeater and wait until mouseup before
 226          // we trigger the stop event?
 227          "mouseleave .ui-spinner-button": "_stop"
 228      },
 229  
 230      // Support mobile enhanced option and make backcompat more sane
 231      _enhance: function() {
 232          this.uiSpinner = this.element
 233              .attr( "autocomplete", "off" )
 234              .wrap( "<span>" )
 235              .parent()
 236  
 237                  // Add buttons
 238                  .append(
 239                      "<a></a><a></a>"
 240                  );
 241      },
 242  
 243      _draw: function() {
 244          this._enhance();
 245  
 246          this._addClass( this.uiSpinner, "ui-spinner", "ui-widget ui-widget-content" );
 247          this._addClass( "ui-spinner-input" );
 248  
 249          this.element.attr( "role", "spinbutton" );
 250  
 251          // Button bindings
 252          this.buttons = this.uiSpinner.children( "a" )
 253              .attr( "tabIndex", -1 )
 254              .attr( "aria-hidden", true )
 255              .button( {
 256                  classes: {
 257                      "ui-button": ""
 258                  }
 259              } );
 260  
 261          // TODO: Right now button does not support classes this is already updated in button PR
 262          this._removeClass( this.buttons, "ui-corner-all" );
 263  
 264          this._addClass( this.buttons.first(), "ui-spinner-button ui-spinner-up" );
 265          this._addClass( this.buttons.last(), "ui-spinner-button ui-spinner-down" );
 266          this.buttons.first().button( {
 267              "icon": this.options.icons.up,
 268              "showLabel": false
 269          } );
 270          this.buttons.last().button( {
 271              "icon": this.options.icons.down,
 272              "showLabel": false
 273          } );
 274  
 275          // IE 6 doesn't understand height: 50% for the buttons
 276          // unless the wrapper has an explicit height
 277          if ( this.buttons.height() > Math.ceil( this.uiSpinner.height() * 0.5 ) &&
 278                  this.uiSpinner.height() > 0 ) {
 279              this.uiSpinner.height( this.uiSpinner.height() );
 280          }
 281      },
 282  
 283      _keydown: function( event ) {
 284          var options = this.options,
 285              keyCode = $.ui.keyCode;
 286  
 287          switch ( event.keyCode ) {
 288          case keyCode.UP:
 289              this._repeat( null, 1, event );
 290              return true;
 291          case keyCode.DOWN:
 292              this._repeat( null, -1, event );
 293              return true;
 294          case keyCode.PAGE_UP:
 295              this._repeat( null, options.page, event );
 296              return true;
 297          case keyCode.PAGE_DOWN:
 298              this._repeat( null, -options.page, event );
 299              return true;
 300          }
 301  
 302          return false;
 303      },
 304  
 305      _start: function( event ) {
 306          if ( !this.spinning && this._trigger( "start", event ) === false ) {
 307              return false;
 308          }
 309  
 310          if ( !this.counter ) {
 311              this.counter = 1;
 312          }
 313          this.spinning = true;
 314          return true;
 315      },
 316  
 317      _repeat: function( i, steps, event ) {
 318          i = i || 500;
 319  
 320          clearTimeout( this.timer );
 321          this.timer = this._delay( function() {
 322              this._repeat( 40, steps, event );
 323          }, i );
 324  
 325          this._spin( steps * this.options.step, event );
 326      },
 327  
 328      _spin: function( step, event ) {
 329          var value = this.value() || 0;
 330  
 331          if ( !this.counter ) {
 332              this.counter = 1;
 333          }
 334  
 335          value = this._adjustValue( value + step * this._increment( this.counter ) );
 336  
 337          if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false ) {
 338              this._value( value );
 339              this.counter++;
 340          }
 341      },
 342  
 343      _increment: function( i ) {
 344          var incremental = this.options.incremental;
 345  
 346          if ( incremental ) {
 347              return typeof incremental === "function" ?
 348                  incremental( i ) :
 349                  Math.floor( i * i * i / 50000 - i * i / 500 + 17 * i / 200 + 1 );
 350          }
 351  
 352          return 1;
 353      },
 354  
 355      _precision: function() {
 356          var precision = this._precisionOf( this.options.step );
 357          if ( this.options.min !== null ) {
 358              precision = Math.max( precision, this._precisionOf( this.options.min ) );
 359          }
 360          return precision;
 361      },
 362  
 363      _precisionOf: function( num ) {
 364          var str = num.toString(),
 365              decimal = str.indexOf( "." );
 366          return decimal === -1 ? 0 : str.length - decimal - 1;
 367      },
 368  
 369      _adjustValue: function( value ) {
 370          var base, aboveMin,
 371              options = this.options;
 372  
 373          // Make sure we're at a valid step
 374          // - find out where we are relative to the base (min or 0)
 375          base = options.min !== null ? options.min : 0;
 376          aboveMin = value - base;
 377  
 378          // - round to the nearest step
 379          aboveMin = Math.round( aboveMin / options.step ) * options.step;
 380  
 381          // - rounding is based on 0, so adjust back to our base
 382          value = base + aboveMin;
 383  
 384          // Fix precision from bad JS floating point math
 385          value = parseFloat( value.toFixed( this._precision() ) );
 386  
 387          // Clamp the value
 388          if ( options.max !== null && value > options.max ) {
 389              return options.max;
 390          }
 391          if ( options.min !== null && value < options.min ) {
 392              return options.min;
 393          }
 394  
 395          return value;
 396      },
 397  
 398      _stop: function( event ) {
 399          if ( !this.spinning ) {
 400              return;
 401          }
 402  
 403          clearTimeout( this.timer );
 404          clearTimeout( this.mousewheelTimer );
 405          this.counter = 0;
 406          this.spinning = false;
 407          this._trigger( "stop", event );
 408      },
 409  
 410      _setOption: function( key, value ) {
 411          var prevValue, first, last;
 412  
 413          if ( key === "culture" || key === "numberFormat" ) {
 414              prevValue = this._parse( this.element.val() );
 415              this.options[ key ] = value;
 416              this.element.val( this._format( prevValue ) );
 417              return;
 418          }
 419  
 420          if ( key === "max" || key === "min" || key === "step" ) {
 421              if ( typeof value === "string" ) {
 422                  value = this._parse( value );
 423              }
 424          }
 425          if ( key === "icons" ) {
 426              first = this.buttons.first().find( ".ui-icon" );
 427              this._removeClass( first, null, this.options.icons.up );
 428              this._addClass( first, null, value.up );
 429              last = this.buttons.last().find( ".ui-icon" );
 430              this._removeClass( last, null, this.options.icons.down );
 431              this._addClass( last, null, value.down );
 432          }
 433  
 434          this._super( key, value );
 435      },
 436  
 437      _setOptionDisabled: function( value ) {
 438          this._super( value );
 439  
 440          this._toggleClass( this.uiSpinner, null, "ui-state-disabled", !!value );
 441          this.element.prop( "disabled", !!value );
 442          this.buttons.button( value ? "disable" : "enable" );
 443      },
 444  
 445      _setOptions: spinnerModifier( function( options ) {
 446          this._super( options );
 447      } ),
 448  
 449      _parse: function( val ) {
 450          if ( typeof val === "string" && val !== "" ) {
 451              val = window.Globalize && this.options.numberFormat ?
 452                  Globalize.parseFloat( val, 10, this.options.culture ) : +val;
 453          }
 454          return val === "" || isNaN( val ) ? null : val;
 455      },
 456  
 457      _format: function( value ) {
 458          if ( value === "" ) {
 459              return "";
 460          }
 461          return window.Globalize && this.options.numberFormat ?
 462              Globalize.format( value, this.options.numberFormat, this.options.culture ) :
 463              value;
 464      },
 465  
 466      _refresh: function() {
 467          this.element.attr( {
 468              "aria-valuemin": this.options.min,
 469              "aria-valuemax": this.options.max,
 470  
 471              // TODO: what should we do with values that can't be parsed?
 472              "aria-valuenow": this._parse( this.element.val() )
 473          } );
 474      },
 475  
 476      isValid: function() {
 477          var value = this.value();
 478  
 479          // Null is invalid
 480          if ( value === null ) {
 481              return false;
 482          }
 483  
 484          // If value gets adjusted, it's invalid
 485          return value === this._adjustValue( value );
 486      },
 487  
 488      // Update the value without triggering change
 489      _value: function( value, allowAny ) {
 490          var parsed;
 491          if ( value !== "" ) {
 492              parsed = this._parse( value );
 493              if ( parsed !== null ) {
 494                  if ( !allowAny ) {
 495                      parsed = this._adjustValue( parsed );
 496                  }
 497                  value = this._format( parsed );
 498              }
 499          }
 500          this.element.val( value );
 501          this._refresh();
 502      },
 503  
 504      _destroy: function() {
 505          this.element
 506              .prop( "disabled", false )
 507              .removeAttr( "autocomplete role aria-valuemin aria-valuemax aria-valuenow" );
 508  
 509          this.uiSpinner.replaceWith( this.element );
 510      },
 511  
 512      stepUp: spinnerModifier( function( steps ) {
 513          this._stepUp( steps );
 514      } ),
 515      _stepUp: function( steps ) {
 516          if ( this._start() ) {
 517              this._spin( ( steps || 1 ) * this.options.step );
 518              this._stop();
 519          }
 520      },
 521  
 522      stepDown: spinnerModifier( function( steps ) {
 523          this._stepDown( steps );
 524      } ),
 525      _stepDown: function( steps ) {
 526          if ( this._start() ) {
 527              this._spin( ( steps || 1 ) * -this.options.step );
 528              this._stop();
 529          }
 530      },
 531  
 532      pageUp: spinnerModifier( function( pages ) {
 533          this._stepUp( ( pages || 1 ) * this.options.page );
 534      } ),
 535  
 536      pageDown: spinnerModifier( function( pages ) {
 537          this._stepDown( ( pages || 1 ) * this.options.page );
 538      } ),
 539  
 540      value: function( newVal ) {
 541          if ( !arguments.length ) {
 542              return this._parse( this.element.val() );
 543          }
 544          spinnerModifier( this._value ).call( this, newVal );
 545      },
 546  
 547      widget: function() {
 548          return this.uiSpinner;
 549      }
 550  } );
 551  
 552  // DEPRECATED
 553  // TODO: switch return back to widget declaration at top of file when this is removed
 554  if ( $.uiBackCompat !== false ) {
 555  
 556      // Backcompat for spinner html extension points
 557      $.widget( "ui.spinner", $.ui.spinner, {
 558          _enhance: function() {
 559              this.uiSpinner = this.element
 560                  .attr( "autocomplete", "off" )
 561                  .wrap( this._uiSpinnerHtml() )
 562                  .parent()
 563  
 564                      // Add buttons
 565                      .append( this._buttonHtml() );
 566          },
 567          _uiSpinnerHtml: function() {
 568              return "<span>";
 569          },
 570  
 571          _buttonHtml: function() {
 572              return "<a></a><a></a>";
 573          }
 574      } );
 575  }
 576  
 577  return $.ui.spinner;
 578  
 579  } );


Generated : Sat Apr 20 08:20:01 2024 Cross-referenced by PHPXref