[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

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


Generated: Wed Dec 2 01:00:04 2020 Cross-referenced by PHPXref 0.7.1