source: trunk/themes/default/js/ui/jquery.ui.spinner.js @ 20824

Last change on this file since 20824 was 20824, checked in by rvelices, 11 years ago

upgraded jquery ui from 1.9.0 to 1.10.1

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