Projet

Général

Profil

Paste
Télécharger (12,1 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / jquery_update / replace / ui / ui / jquery.ui.spinner.js @ 503b3f7b

1
/*!
2
 * jQuery UI Spinner 1.10.2
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

    
18
function 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.2",
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._stop();
106
                        this._refresh();
107
                        if ( this.previous !== this.element.val() ) {
108
                                this._trigger( "change", event );
109
                        }
110
                },
111
                mousewheel: function( event, delta ) {
112
                        if ( !delta ) {
113
                                return;
114
                        }
115
                        if ( !this.spinning && !this._start( event ) ) {
116
                                return false;
117
                        }
118

    
119
                        this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
120
                        clearTimeout( this.mousewheelTimer );
121
                        this.mousewheelTimer = this._delay(function() {
122
                                if ( this.spinning ) {
123
                                        this._stop( event );
124
                                }
125
                        }, 100 );
126
                        event.preventDefault();
127
                },
128
                "mousedown .ui-spinner-button": function( event ) {
129
                        var previous;
130

    
131
                        // We never want the buttons to have focus; whenever the user is
132
                        // interacting with the spinner, the focus should be on the input.
133
                        // If the input is focused then this.previous is properly set from
134
                        // when the input first received focus. If the input is not focused
135
                        // then we need to set this.previous based on the value before spinning.
136
                        previous = this.element[0] === this.document[0].activeElement ?
137
                                this.previous : this.element.val();
138
                        function checkFocus() {
139
                                var isActive = this.element[0] === this.document[0].activeElement;
140
                                if ( !isActive ) {
141
                                        this.element.focus();
142
                                        this.previous = previous;
143
                                        // support: IE
144
                                        // IE sets focus asynchronously, so we need to check if focus
145
                                        // moved off of the input because the user clicked on the button.
146
                                        this._delay(function() {
147
                                                this.previous = previous;
148
                                        });
149
                                }
150
                        }
151

    
152
                        // ensure focus is on (or stays on) the text field
153
                        event.preventDefault();
154
                        checkFocus.call( this );
155

    
156
                        // support: IE
157
                        // IE doesn't prevent moving focus even with event.preventDefault()
158
                        // so we set a flag to know when we should ignore the blur event
159
                        // and check (again) if focus moved off of the input.
160
                        this.cancelBlur = true;
161
                        this._delay(function() {
162
                                delete this.cancelBlur;
163
                                checkFocus.call( this );
164
                        });
165

    
166
                        if ( this._start( event ) === false ) {
167
                                return;
168
                        }
169

    
170
                        this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
171
                },
172
                "mouseup .ui-spinner-button": "_stop",
173
                "mouseenter .ui-spinner-button": function( event ) {
174
                        // button will add ui-state-active if mouse was down while mouseleave and kept down
175
                        if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
176
                                return;
177
                        }
178

    
179
                        if ( this._start( event ) === false ) {
180
                                return false;
181
                        }
182
                        this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
183
                },
184
                // TODO: do we really want to consider this a stop?
185
                // shouldn't we just stop the repeater and wait until mouseup before
186
                // we trigger the stop event?
187
                "mouseleave .ui-spinner-button": "_stop"
188
        },
189

    
190
        _draw: function() {
191
                var uiSpinner = this.uiSpinner = this.element
192
                        .addClass( "ui-spinner-input" )
193
                        .attr( "autocomplete", "off" )
194
                        .wrap( this._uiSpinnerHtml() )
195
                        .parent()
196
                                // add buttons
197
                                .append( this._buttonHtml() );
198

    
199
                this.element.attr( "role", "spinbutton" );
200

    
201
                // button bindings
202
                this.buttons = uiSpinner.find( ".ui-spinner-button" )
203
                        .attr( "tabIndex", -1 )
204
                        .button()
205
                        .removeClass( "ui-corner-all" );
206

    
207
                // IE 6 doesn't understand height: 50% for the buttons
208
                // unless the wrapper has an explicit height
209
                if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
210
                                uiSpinner.height() > 0 ) {
211
                        uiSpinner.height( uiSpinner.height() );
212
                }
213

    
214
                // disable spinner if element was already disabled
215
                if ( this.options.disabled ) {
216
                        this.disable();
217
                }
218
        },
219

    
220
        _keydown: function( event ) {
221
                var options = this.options,
222
                        keyCode = $.ui.keyCode;
223

    
224
                switch ( event.keyCode ) {
225
                case keyCode.UP:
226
                        this._repeat( null, 1, event );
227
                        return true;
228
                case keyCode.DOWN:
229
                        this._repeat( null, -1, event );
230
                        return true;
231
                case keyCode.PAGE_UP:
232
                        this._repeat( null, options.page, event );
233
                        return true;
234
                case keyCode.PAGE_DOWN:
235
                        this._repeat( null, -options.page, event );
236
                        return true;
237
                }
238

    
239
                return false;
240
        },
241

    
242
        _uiSpinnerHtml: function() {
243
                return "<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>";
244
        },
245

    
246
        _buttonHtml: function() {
247
                return "" +
248
                        "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" +
249
                                "<span class='ui-icon " + this.options.icons.up + "'>&#9650;</span>" +
250
                        "</a>" +
251
                        "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" +
252
                                "<span class='ui-icon " + this.options.icons.down + "'>&#9660;</span>" +
253
                        "</a>";
254
        },
255

    
256
        _start: function( event ) {
257
                if ( !this.spinning && this._trigger( "start", event ) === false ) {
258
                        return false;
259
                }
260

    
261
                if ( !this.counter ) {
262
                        this.counter = 1;
263
                }
264
                this.spinning = true;
265
                return true;
266
        },
267

    
268
        _repeat: function( i, steps, event ) {
269
                i = i || 500;
270

    
271
                clearTimeout( this.timer );
272
                this.timer = this._delay(function() {
273
                        this._repeat( 40, steps, event );
274
                }, i );
275

    
276
                this._spin( steps * this.options.step, event );
277
        },
278

    
279
        _spin: function( step, event ) {
280
                var value = this.value() || 0;
281

    
282
                if ( !this.counter ) {
283
                        this.counter = 1;
284
                }
285

    
286
                value = this._adjustValue( value + step * this._increment( this.counter ) );
287

    
288
                if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
289
                        this._value( value );
290
                        this.counter++;
291
                }
292
        },
293

    
294
        _increment: function( i ) {
295
                var incremental = this.options.incremental;
296

    
297
                if ( incremental ) {
298
                        return $.isFunction( incremental ) ?
299
                                incremental( i ) :
300
                                Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
301
                }
302

    
303
                return 1;
304
        },
305

    
306
        _precision: function() {
307
                var precision = this._precisionOf( this.options.step );
308
                if ( this.options.min !== null ) {
309
                        precision = Math.max( precision, this._precisionOf( this.options.min ) );
310
                }
311
                return precision;
312
        },
313

    
314
        _precisionOf: function( num ) {
315
                var str = num.toString(),
316
                        decimal = str.indexOf( "." );
317
                return decimal === -1 ? 0 : str.length - decimal - 1;
318
        },
319

    
320
        _adjustValue: function( value ) {
321
                var base, aboveMin,
322
                        options = this.options;
323

    
324
                // make sure we're at a valid step
325
                // - find out where we are relative to the base (min or 0)
326
                base = options.min !== null ? options.min : 0;
327
                aboveMin = value - base;
328
                // - round to the nearest step
329
                aboveMin = Math.round(aboveMin / options.step) * options.step;
330
                // - rounding is based on 0, so adjust back to our base
331
                value = base + aboveMin;
332

    
333
                // fix precision from bad JS floating point math
334
                value = parseFloat( value.toFixed( this._precision() ) );
335

    
336
                // clamp the value
337
                if ( options.max !== null && value > options.max) {
338
                        return options.max;
339
                }
340
                if ( options.min !== null && value < options.min ) {
341
                        return options.min;
342
                }
343

    
344
                return value;
345
        },
346

    
347
        _stop: function( event ) {
348
                if ( !this.spinning ) {
349
                        return;
350
                }
351

    
352
                clearTimeout( this.timer );
353
                clearTimeout( this.mousewheelTimer );
354
                this.counter = 0;
355
                this.spinning = false;
356
                this._trigger( "stop", event );
357
        },
358

    
359
        _setOption: function( key, value ) {
360
                if ( key === "culture" || key === "numberFormat" ) {
361
                        var prevValue = this._parse( this.element.val() );
362
                        this.options[ key ] = value;
363
                        this.element.val( this._format( prevValue ) );
364
                        return;
365
                }
366

    
367
                if ( key === "max" || key === "min" || key === "step" ) {
368
                        if ( typeof value === "string" ) {
369
                                value = this._parse( value );
370
                        }
371
                }
372
                if ( key === "icons" ) {
373
                        this.buttons.first().find( ".ui-icon" )
374
                                .removeClass( this.options.icons.up )
375
                                .addClass( value.up );
376
                        this.buttons.last().find( ".ui-icon" )
377
                                .removeClass( this.options.icons.down )
378
                                .addClass( value.down );
379
                }
380

    
381
                this._super( key, value );
382

    
383
                if ( key === "disabled" ) {
384
                        if ( value ) {
385
                                this.element.prop( "disabled", true );
386
                                this.buttons.button( "disable" );
387
                        } else {
388
                                this.element.prop( "disabled", false );
389
                                this.buttons.button( "enable" );
390
                        }
391
                }
392
        },
393

    
394
        _setOptions: modifier(function( options ) {
395
                this._super( options );
396
                this._value( this.element.val() );
397
        }),
398

    
399
        _parse: function( val ) {
400
                if ( typeof val === "string" && val !== "" ) {
401
                        val = window.Globalize && this.options.numberFormat ?
402
                                Globalize.parseFloat( val, 10, this.options.culture ) : +val;
403
                }
404
                return val === "" || isNaN( val ) ? null : val;
405
        },
406

    
407
        _format: function( value ) {
408
                if ( value === "" ) {
409
                        return "";
410
                }
411
                return window.Globalize && this.options.numberFormat ?
412
                        Globalize.format( value, this.options.numberFormat, this.options.culture ) :
413
                        value;
414
        },
415

    
416
        _refresh: function() {
417
                this.element.attr({
418
                        "aria-valuemin": this.options.min,
419
                        "aria-valuemax": this.options.max,
420
                        // TODO: what should we do with values that can't be parsed?
421
                        "aria-valuenow": this._parse( this.element.val() )
422
                });
423
        },
424

    
425
        // update the value without triggering change
426
        _value: function( value, allowAny ) {
427
                var parsed;
428
                if ( value !== "" ) {
429
                        parsed = this._parse( value );
430
                        if ( parsed !== null ) {
431
                                if ( !allowAny ) {
432
                                        parsed = this._adjustValue( parsed );
433
                                }
434
                                value = this._format( parsed );
435
                        }
436
                }
437
                this.element.val( value );
438
                this._refresh();
439
        },
440

    
441
        _destroy: function() {
442
                this.element
443
                        .removeClass( "ui-spinner-input" )
444
                        .prop( "disabled", false )
445
                        .removeAttr( "autocomplete" )
446
                        .removeAttr( "role" )
447
                        .removeAttr( "aria-valuemin" )
448
                        .removeAttr( "aria-valuemax" )
449
                        .removeAttr( "aria-valuenow" );
450
                this.uiSpinner.replaceWith( this.element );
451
        },
452

    
453
        stepUp: modifier(function( steps ) {
454
                this._stepUp( steps );
455
        }),
456
        _stepUp: function( steps ) {
457
                if ( this._start() ) {
458
                        this._spin( (steps || 1) * this.options.step );
459
                        this._stop();
460
                }
461
        },
462

    
463
        stepDown: modifier(function( steps ) {
464
                this._stepDown( steps );
465
        }),
466
        _stepDown: function( steps ) {
467
                if ( this._start() ) {
468
                        this._spin( (steps || 1) * -this.options.step );
469
                        this._stop();
470
                }
471
        },
472

    
473
        pageUp: modifier(function( pages ) {
474
                this._stepUp( (pages || 1) * this.options.page );
475
        }),
476

    
477
        pageDown: modifier(function( pages ) {
478
                this._stepDown( (pages || 1) * this.options.page );
479
        }),
480

    
481
        value: function( newVal ) {
482
                if ( !arguments.length ) {
483
                        return this._parse( this.element.val() );
484
                }
485
                modifier( this._value ).call( this, newVal );
486
        },
487

    
488
        widget: function() {
489
                return this.uiSpinner;
490
        }
491
});
492

    
493
}( jQuery ) );