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