]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - resources/jquery.ui/jquery.ui.slider.js
MediaWiki 1.17.0
[autoinstallsdev/mediawiki.git] / resources / jquery.ui / jquery.ui.slider.js
1 /*
2  * jQuery UI Slider 1.8.2
3  *
4  * Copyright (c) 2010 AUTHORS.txt (http://jqueryui.com/about)
5  * Dual licensed under the MIT (MIT-LICENSE.txt)
6  * and GPL (GPL-LICENSE.txt) licenses.
7  *
8  * http://docs.jquery.com/UI/Slider
9  *
10  * Depends:
11  *      jquery.ui.core.js
12  *      jquery.ui.mouse.js
13  *      jquery.ui.widget.js
14  */
15
16 (function( $ ) {
17
18 // number of pages in a slider
19 // (how many times can you page up/down to go through the whole range)
20 var numPages = 5;
21
22 $.widget( "ui.slider", $.ui.mouse, {
23
24         widgetEventPrefix: "slide",
25
26         options: {
27                 animate: false,
28                 distance: 0,
29                 max: 100,
30                 min: 0,
31                 orientation: "horizontal",
32                 range: false,
33                 step: 1,
34                 value: 0,
35                 values: null
36         },
37
38         _create: function() {
39                 var self = this,
40                         o = this.options;
41
42                 this._keySliding = false;
43                 this._mouseSliding = false;
44                 this._animateOff = true;
45                 this._handleIndex = null;
46                 this._detectOrientation();
47                 this._mouseInit();
48
49                 this.element
50                         .addClass( "ui-slider" +
51                                 " ui-slider-" + this.orientation +
52                                 " ui-widget" +
53                                 " ui-widget-content" +
54                                 " ui-corner-all" );
55                 
56                 if ( o.disabled ) {
57                         this.element.addClass( "ui-slider-disabled ui-disabled" );
58                 }
59
60                 this.range = $([]);
61
62                 if ( o.range ) {
63                         if ( o.range === true ) {
64                                 this.range = $( "<div></div>" );
65                                 if ( !o.values ) {
66                                         o.values = [ this._valueMin(), this._valueMin() ];
67                                 }
68                                 if ( o.values.length && o.values.length !== 2 ) {
69                                         o.values = [ o.values[0], o.values[0] ];
70                                 }
71                         } else {
72                                 this.range = $( "<div></div>" );
73                         }
74
75                         this.range
76                                 .appendTo( this.element )
77                                 .addClass( "ui-slider-range" );
78
79                         if ( o.range === "min" || o.range === "max" ) {
80                                 this.range.addClass( "ui-slider-range-" + o.range );
81                         }
82
83                         // note: this isn't the most fittingly semantic framework class for this element,
84                         // but worked best visually with a variety of themes
85                         this.range.addClass( "ui-widget-header" );
86                 }
87
88                 if ( $( ".ui-slider-handle", this.element ).length === 0 ) {
89                         $( "<a href='#'></a>" )
90                                 .appendTo( this.element )
91                                 .addClass( "ui-slider-handle" );
92                 }
93
94                 if ( o.values && o.values.length ) {
95                         while ( $(".ui-slider-handle", this.element).length < o.values.length ) {
96                                 $( "<a href='#'></a>" )
97                                         .appendTo( this.element )
98                                         .addClass( "ui-slider-handle" );
99                         }
100                 }
101
102                 this.handles = $( ".ui-slider-handle", this.element )
103                         .addClass( "ui-state-default" +
104                                 " ui-corner-all" );
105
106                 this.handle = this.handles.eq( 0 );
107
108                 this.handles.add( this.range ).filter( "a" )
109                         .click(function( event ) {
110                                 event.preventDefault();
111                         })
112                         .hover(function() {
113                                 if ( !o.disabled ) {
114                                         $( this ).addClass( "ui-state-hover" );
115                                 }
116                         }, function() {
117                                 $( this ).removeClass( "ui-state-hover" );
118                         })
119                         .focus(function() {
120                                 if ( !o.disabled ) {
121                                         $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" );
122                                         $( this ).addClass( "ui-state-focus" );
123                                 } else {
124                                         $( this ).blur();
125                                 }
126                         })
127                         .blur(function() {
128                                 $( this ).removeClass( "ui-state-focus" );
129                         });
130
131                 this.handles.each(function( i ) {
132                         $( this ).data( "index.ui-slider-handle", i );
133                 });
134
135                 this.handles
136                         .keydown(function( event ) {
137                                 var ret = true,
138                                         index = $( this ).data( "index.ui-slider-handle" ),
139                                         allowed,
140                                         curVal,
141                                         newVal,
142                                         step;
143         
144                                 if ( self.options.disabled ) {
145                                         return;
146                                 }
147         
148                                 switch ( event.keyCode ) {
149                                         case $.ui.keyCode.HOME:
150                                         case $.ui.keyCode.END:
151                                         case $.ui.keyCode.PAGE_UP:
152                                         case $.ui.keyCode.PAGE_DOWN:
153                                         case $.ui.keyCode.UP:
154                                         case $.ui.keyCode.RIGHT:
155                                         case $.ui.keyCode.DOWN:
156                                         case $.ui.keyCode.LEFT:
157                                                 ret = false;
158                                                 if ( !self._keySliding ) {
159                                                         self._keySliding = true;
160                                                         $( this ).addClass( "ui-state-active" );
161                                                         allowed = self._start( event, index );
162                                                         if ( allowed === false ) {
163                                                                 return;
164                                                         }
165                                                 }
166                                                 break;
167                                 }
168         
169                                 step = self.options.step;
170                                 if ( self.options.values && self.options.values.length ) {
171                                         curVal = newVal = self.values( index );
172                                 } else {
173                                         curVal = newVal = self.value();
174                                 }
175         
176                                 switch ( event.keyCode ) {
177                                         case $.ui.keyCode.HOME:
178                                                 newVal = self._valueMin();
179                                                 break;
180                                         case $.ui.keyCode.END:
181                                                 newVal = self._valueMax();
182                                                 break;
183                                         case $.ui.keyCode.PAGE_UP:
184                                                 newVal = self._trimAlignValue( curVal + ( (self._valueMax() - self._valueMin()) / numPages ) );
185                                                 break;
186                                         case $.ui.keyCode.PAGE_DOWN:
187                                                 newVal = self._trimAlignValue( curVal - ( (self._valueMax() - self._valueMin()) / numPages ) );
188                                                 break;
189                                         case $.ui.keyCode.UP:
190                                         case $.ui.keyCode.RIGHT:
191                                                 if ( curVal === self._valueMax() ) {
192                                                         return;
193                                                 }
194                                                 newVal = self._trimAlignValue( curVal + step );
195                                                 break;
196                                         case $.ui.keyCode.DOWN:
197                                         case $.ui.keyCode.LEFT:
198                                                 if ( curVal === self._valueMin() ) {
199                                                         return;
200                                                 }
201                                                 newVal = self._trimAlignValue( curVal - step );
202                                                 break;
203                                 }
204         
205                                 self._slide( event, index, newVal );
206         
207                                 return ret;
208         
209                         })
210                         .keyup(function( event ) {
211                                 var index = $( this ).data( "index.ui-slider-handle" );
212         
213                                 if ( self._keySliding ) {
214                                         self._keySliding = false;
215                                         self._stop( event, index );
216                                         self._change( event, index );
217                                         $( this ).removeClass( "ui-state-active" );
218                                 }
219         
220                         });
221
222                 this._refreshValue();
223
224                 this._animateOff = false;
225         },
226
227         destroy: function() {
228                 this.handles.remove();
229                 this.range.remove();
230
231                 this.element
232                         .removeClass( "ui-slider" +
233                                 " ui-slider-horizontal" +
234                                 " ui-slider-vertical" +
235                                 " ui-slider-disabled" +
236                                 " ui-widget" +
237                                 " ui-widget-content" +
238                                 " ui-corner-all" )
239                         .removeData( "slider" )
240                         .unbind( ".slider" );
241
242                 this._mouseDestroy();
243
244                 return this;
245         },
246
247         _mouseCapture: function( event ) {
248                 var o = this.options,
249                         position,
250                         normValue,
251                         distance,
252                         closestHandle,
253                         self,
254                         index,
255                         allowed,
256                         offset,
257                         mouseOverHandle;
258
259                 if ( o.disabled ) {
260                         return false;
261                 }
262
263                 this.elementSize = {
264                         width: this.element.outerWidth(),
265                         height: this.element.outerHeight()
266                 };
267                 this.elementOffset = this.element.offset();
268
269                 position = { x: event.pageX, y: event.pageY };
270                 normValue = this._normValueFromMouse( position );
271                 distance = this._valueMax() - this._valueMin() + 1;
272                 self = this;
273                 this.handles.each(function( i ) {
274                         var thisDistance = Math.abs( normValue - self.values(i) );
275                         if ( distance > thisDistance ) {
276                                 distance = thisDistance;
277                                 closestHandle = $( this );
278                                 index = i;
279                         }
280                 });
281
282                 // workaround for bug #3736 (if both handles of a range are at 0,
283                 // the first is always used as the one with least distance,
284                 // and moving it is obviously prevented by preventing negative ranges)
285                 if( o.range === true && this.values(1) === o.min ) {
286                         index += 1;
287                         closestHandle = $( this.handles[index] );
288                 }
289
290                 allowed = this._start( event, index );
291                 if ( allowed === false ) {
292                         return false;
293                 }
294                 this._mouseSliding = true;
295
296                 self._handleIndex = index;
297
298                 closestHandle
299                         .addClass( "ui-state-active" )
300                         .focus();
301                 
302                 offset = closestHandle.offset();
303                 mouseOverHandle = !$( event.target ).parents().andSelf().is( ".ui-slider-handle" );
304                 this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
305                         left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
306                         top: event.pageY - offset.top -
307                                 ( closestHandle.height() / 2 ) -
308                                 ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
309                                 ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
310                                 ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
311                 };
312
313                 normValue = this._normValueFromMouse( position );
314                 this._slide( event, index, normValue );
315                 this._animateOff = true;
316                 return true;
317         },
318
319         _mouseStart: function( event ) {
320                 return true;
321         },
322
323         _mouseDrag: function( event ) {
324                 var position = { x: event.pageX, y: event.pageY },
325                         normValue = this._normValueFromMouse( position );
326                 
327                 this._slide( event, this._handleIndex, normValue );
328
329                 return false;
330         },
331
332         _mouseStop: function( event ) {
333                 this.handles.removeClass( "ui-state-active" );
334                 this._mouseSliding = false;
335
336                 this._stop( event, this._handleIndex );
337                 this._change( event, this._handleIndex );
338
339                 this._handleIndex = null;
340                 this._clickOffset = null;
341                 this._animateOff = false;
342
343                 return false;
344         },
345         
346         _detectOrientation: function() {
347                 this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
348         },
349
350         _normValueFromMouse: function( position ) {
351                 var pixelTotal,
352                         pixelMouse,
353                         percentMouse,
354                         valueTotal,
355                         valueMouse;
356
357                 if ( this.orientation === "horizontal" ) {
358                         pixelTotal = this.elementSize.width;
359                         pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
360                 } else {
361                         pixelTotal = this.elementSize.height;
362                         pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
363                 }
364
365                 percentMouse = ( pixelMouse / pixelTotal );
366                 if ( percentMouse > 1 ) {
367                         percentMouse = 1;
368                 }
369                 if ( percentMouse < 0 ) {
370                         percentMouse = 0;
371                 }
372                 if ( this.orientation === "vertical" ) {
373                         percentMouse = 1 - percentMouse;
374                 }
375
376                 valueTotal = this._valueMax() - this._valueMin();
377                 valueMouse = this._valueMin() + percentMouse * valueTotal;
378
379                 return this._trimAlignValue( valueMouse );
380         },
381
382         _start: function( event, index ) {
383                 var uiHash = {
384                         handle: this.handles[ index ],
385                         value: this.value()
386                 };
387                 if ( this.options.values && this.options.values.length ) {
388                         uiHash.value = this.values( index );
389                         uiHash.values = this.values();
390                 }
391                 return this._trigger( "start", event, uiHash );
392         },
393
394         _slide: function( event, index, newVal ) {
395                 var otherVal,
396                         newValues,
397                         allowed;
398
399                 if ( this.options.values && this.options.values.length ) {
400                         otherVal = this.values( index ? 0 : 1 );
401
402                         if ( ( this.options.values.length === 2 && this.options.range === true ) && 
403                                         ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
404                                 ) {
405                                 newVal = otherVal;
406                         }
407
408                         if ( newVal !== this.values( index ) ) {
409                                 newValues = this.values();
410                                 newValues[ index ] = newVal;
411                                 // A slide can be canceled by returning false from the slide callback
412                                 allowed = this._trigger( "slide", event, {
413                                         handle: this.handles[ index ],
414                                         value: newVal,
415                                         values: newValues
416                                 } );
417                                 otherVal = this.values( index ? 0 : 1 );
418                                 if ( allowed !== false ) {
419                                         this.values( index, newVal, true );
420                                 }
421                         }
422                 } else {
423                         if ( newVal !== this.value() ) {
424                                 // A slide can be canceled by returning false from the slide callback
425                                 allowed = this._trigger( "slide", event, {
426                                         handle: this.handles[ index ],
427                                         value: newVal
428                                 } );
429                                 if ( allowed !== false ) {
430                                         this.value( newVal );
431                                 }
432                         }
433                 }
434         },
435
436         _stop: function( event, index ) {
437                 var uiHash = {
438                         handle: this.handles[ index ],
439                         value: this.value()
440                 };
441                 if ( this.options.values && this.options.values.length ) {
442                         uiHash.value = this.values( index );
443                         uiHash.values = this.values();
444                 }
445
446                 this._trigger( "stop", event, uiHash );
447         },
448
449         _change: function( event, index ) {
450                 if ( !this._keySliding && !this._mouseSliding ) {
451                         var uiHash = {
452                                 handle: this.handles[ index ],
453                                 value: this.value()
454                         };
455                         if ( this.options.values && this.options.values.length ) {
456                                 uiHash.value = this.values( index );
457                                 uiHash.values = this.values();
458                         }
459
460                         this._trigger( "change", event, uiHash );
461                 }
462         },
463
464         value: function( newValue ) {
465                 if ( arguments.length ) {
466                         this.options.value = this._trimAlignValue( newValue );
467                         this._refreshValue();
468                         this._change( null, 0 );
469                 }
470
471                 return this._value();
472         },
473
474         values: function( index, newValue ) {
475                 var vals,
476                         newValues,
477                         i;
478
479                 if ( arguments.length > 1 ) {
480                         this.options.values[ index ] = this._trimAlignValue( newValue );
481                         this._refreshValue();
482                         this._change( null, index );
483                 }
484
485                 if ( arguments.length ) {
486                         if ( $.isArray( arguments[ 0 ] ) ) {
487                                 vals = this.options.values;
488                                 newValues = arguments[ 0 ];
489                                 for ( i = 0; i < vals.length; i += 1 ) {
490                                         vals[ i ] = this._trimAlignValue( newValues[ i ] );
491                                         this._change( null, i );
492                                 }
493                                 this._refreshValue();
494                         } else {
495                                 if ( this.options.values && this.options.values.length ) {
496                                         return this._values( index );
497                                 } else {
498                                         return this.value();
499                                 }
500                         }
501                 } else {
502                         return this._values();
503                 }
504         },
505
506         _setOption: function( key, value ) {
507                 var i,
508                         valsLength = 0;
509
510                 if ( $.isArray( this.options.values ) ) {
511                         valsLength = this.options.values.length;
512                 }
513
514                 $.Widget.prototype._setOption.apply( this, arguments );
515
516                 switch ( key ) {
517                         case "disabled":
518                                 if ( value ) {
519                                         this.handles.filter( ".ui-state-focus" ).blur();
520                                         this.handles.removeClass( "ui-state-hover" );
521                                         this.handles.attr( "disabled", "disabled" );
522                                         this.element.addClass( "ui-disabled" );
523                                 } else {
524                                         this.handles.removeAttr( "disabled" );
525                                         this.element.removeClass( "ui-disabled" );
526                                 }
527                                 break;
528                         case "orientation":
529                                 this._detectOrientation();
530                                 this.element
531                                         .removeClass( "ui-slider-horizontal ui-slider-vertical" )
532                                         .addClass( "ui-slider-" + this.orientation );
533                                 this._refreshValue();
534                                 break;
535                         case "value":
536                                 this._animateOff = true;
537                                 this._refreshValue();
538                                 this._change( null, 0 );
539                                 this._animateOff = false;
540                                 break;
541                         case "values":
542                                 this._animateOff = true;
543                                 this._refreshValue();
544                                 for ( i = 0; i < valsLength; i += 1 ) {
545                                         this._change( null, i );
546                                 }
547                                 this._animateOff = false;
548                                 break;
549                 }
550         },
551
552         //internal value getter
553         // _value() returns value trimmed by min and max, aligned by step
554         _value: function() {
555                 var val = this.options.value;
556                 val = this._trimAlignValue( val );
557
558                 return val;
559         },
560
561         //internal values getter
562         // _values() returns array of values trimmed by min and max, aligned by step
563         // _values( index ) returns single value trimmed by min and max, aligned by step
564         _values: function( index ) {
565                 var val,
566                         vals,
567                         i;
568
569                 if ( arguments.length ) {
570                         val = this.options.values[ index ];
571                         val = this._trimAlignValue( val );
572
573                         return val;
574                 } else {
575                         // .slice() creates a copy of the array
576                         // this copy gets trimmed by min and max and then returned
577                         vals = this.options.values.slice();
578                         for ( i = 0; i < vals.length; i+= 1) {
579                                 vals[ i ] = this._trimAlignValue( vals[ i ] );
580                         }
581
582                         return vals;
583                 }
584         },
585         
586         // returns the step-aligned value that val is closest to, between (inclusive) min and max
587         _trimAlignValue: function( val ) {
588                 if ( val < this._valueMin() ) {
589                         return this._valueMin();
590                 }
591                 if ( val > this._valueMax() ) {
592                         return this._valueMax();
593                 }
594                 var step = ( this.options.step > 0 ) ? this.options.step : 1,
595                         valModStep = val % step,
596                         alignValue = val - valModStep;
597
598                 if ( Math.abs(valModStep) * 2 >= step ) {
599                         alignValue += ( valModStep > 0 ) ? step : ( -step );
600                 }
601
602                 // Since JavaScript has problems with large floats, round
603                 // the final value to 5 digits after the decimal point (see #4124)
604                 return parseFloat( alignValue.toFixed(5) );
605         },
606
607         _valueMin: function() {
608                 return this.options.min;
609         },
610
611         _valueMax: function() {
612                 return this.options.max;
613         },
614         
615         _refreshValue: function() {
616                 var oRange = this.options.range,
617                         o = this.options,
618                         self = this,
619                         animate = ( !this._animateOff ) ? o.animate : false,
620                         valPercent,
621                         _set = {},
622                         lastValPercent,
623                         value,
624                         valueMin,
625                         valueMax;
626
627                 if ( this.options.values && this.options.values.length ) {
628                         this.handles.each(function( i, j ) {
629                                 valPercent = ( self.values(i) - self._valueMin() ) / ( self._valueMax() - self._valueMin() ) * 100;
630                                 _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
631                                 $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
632                                 if ( self.options.range === true ) {
633                                         if ( self.orientation === "horizontal" ) {
634                                                 if ( i === 0 ) {
635                                                         self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
636                                                 }
637                                                 if ( i === 1 ) {
638                                                         self.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
639                                                 }
640                                         } else {
641                                                 if ( i === 0 ) {
642                                                         self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
643                                                 }
644                                                 if ( i === 1 ) {
645                                                         self.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
646                                                 }
647                                         }
648                                 }
649                                 lastValPercent = valPercent;
650                         });
651                 } else {
652                         value = this.value();
653                         valueMin = this._valueMin();
654                         valueMax = this._valueMax();
655                         valPercent = ( valueMax !== valueMin ) ?
656                                         ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
657                                         0;
658                         _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
659                         this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
660
661                         if ( oRange === "min" && this.orientation === "horizontal" ) {
662                                 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
663                         }
664                         if ( oRange === "max" && this.orientation === "horizontal" ) {
665                                 this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
666                         }
667                         if ( oRange === "min" && this.orientation === "vertical" ) {
668                                 this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
669                         }
670                         if ( oRange === "max" && this.orientation === "vertical" ) {
671                                 this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
672                         }
673                 }
674         }
675
676 });
677
678 $.extend( $.ui.slider, {
679         version: "1.8.2"
680 });
681
682 }(jQuery));