Tizen 2.1 base
[platform/framework/web/web-ui-fw.git] / libs / js / jquery-mobile-1.0.1pre / js / jquery.mobile.forms.slider.js
1 /*
2 * "slider" plugin
3 */
4
5 ( function( $, undefined ) {
6
7 $.widget( "mobile.slider", $.mobile.widget, {
8         options: {
9                 theme: null,
10                 trackTheme: null,
11                 disabled: false,
12                 initSelector: "input[type='range'], :jqmData(type='range'), :jqmData(role='slider')"
13         },
14
15         _create: function() {
16
17                 // TODO: Each of these should have comments explain what they're for
18                 var self = this,
19
20                         control = this.element,
21
22                         parentTheme = $.mobile.getInheritedTheme( control, "c" ),
23
24                         theme = this.options.theme || parentTheme,
25
26                         trackTheme = this.options.trackTheme || parentTheme,
27
28                         cType = control[ 0 ].nodeName.toLowerCase(),
29
30                         selectClass = ( cType == "select" ) ? "ui-slider-switch" : "",
31
32                         controlID = control.attr( "id" ),
33
34                         labelID = controlID + "-label",
35
36                         label = $( "[for='"+ controlID +"']" ).attr( "id", labelID ),
37
38                         val = function() {
39                                 return  cType == "input"  ? parseFloat( control.val() ) : control[0].selectedIndex;
40                         },
41
42                         min =  cType == "input" ? parseFloat( control.attr( "min" ) ) : 0,
43
44                         max =  cType == "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length-1,
45
46                         step = window.parseFloat( control.attr( "step" ) || 1 ),
47
48                         slider = $( "<div class='ui-slider " + selectClass + " ui-btn-down-" + trackTheme +
49                                                                         " ui-btn-corner-all' role='application'></div>" ),
50
51                         handle = $( "<a href='#' class='ui-slider-handle'></a>" )
52                                 .appendTo( slider )
53                                 .buttonMarkup({ corners: true, theme: theme, shadow: true })
54                                 .attr({
55                                         "role": "slider",
56                                         "aria-valuemin": min,
57                                         "aria-valuemax": max,
58                                         "aria-valuenow": val(),
59                                         "aria-valuetext": val(),
60                                         "title": val(),
61                                         "aria-labelledby": labelID
62                                 }),
63                         options;
64
65                 $.extend( this, {
66                         slider: slider,
67                         handle: handle,
68                         dragging: false,
69                         beforeStart: null,
70                         userModified: false,
71                         mouseMoved: false
72                 });
73
74                 if ( cType == "select" ) {
75
76                         slider.wrapInner( "<div class='ui-slider-inneroffset'></div>" );
77                         
78                         // make the handle move with a smooth transition
79                         handle.addClass( "ui-slider-handle-snapping" );
80
81                         options = control.find( "option" );
82
83                         control.find( "option" ).each(function( i ) {
84
85                                 var side = !i ? "b":"a",
86                                         corners = !i ? "right" :"left",
87                                         theme = !i ? " ui-btn-down-" + trackTheme :( " " + $.mobile.activeBtnClass );
88
89                                 $( "<div class='ui-slider-labelbg ui-slider-labelbg-" + side + theme + " ui-btn-corner-" + corners + "'></div>" )
90                                         .prependTo( slider );
91
92                                 $( "<span class='ui-slider-label ui-slider-label-" + side + theme + " ui-btn-corner-" + corners + "' role='img'>" + $( this ).getEncodedText() + "</span>" )
93                                         .prependTo( handle );
94                         });
95
96                 }
97
98                 label.addClass( "ui-slider" );
99
100                 // monitor the input for updated values
101                 control.addClass( cType === "input" ? "ui-slider-input" : "ui-slider-switch" )
102                         .change( function() {
103                                 // if the user dragged the handle, the "change" event was triggered from inside refresh(); don't call refresh() again
104                                 if (!self.mouseMoved) {
105                                         self.refresh( val(), true );
106                                 }
107                         })
108                         .keyup( function() { // necessary?
109                                 self.refresh( val(), true, true );
110                         })
111                         .blur( function() {
112                                 self.refresh( val(), true );
113                         });
114
115                 // prevent screen drag when slider activated
116                 $( document ).bind( "vmousemove", function( event ) {
117                         if ( self.dragging ) {
118                                 // self.mouseMoved must be updated before refresh() because it will be used in the control "change" event
119                                 self.mouseMoved = true;
120                                 
121                                 if ( cType === "select" ) {
122                                         // make the handle move in sync with the mouse
123                                         handle.removeClass( "ui-slider-handle-snapping" );
124                                 }
125                                 
126                                 self.refresh( event );
127                                 
128                                 // only after refresh() you can calculate self.userModified
129                                 self.userModified = self.beforeStart !== control[0].selectedIndex;
130                                 return false;
131                         }
132                 });
133
134                 slider.bind( "vmousedown", function( event ) {
135                         self.dragging = true;
136                         self.userModified = false;
137                         self.mouseMoved = false;
138
139                         if ( cType === "select" ) {
140                                 self.beforeStart = control[0].selectedIndex;
141                         }
142                         
143                         self.refresh( event );
144                         return false;
145                 });
146
147                 slider.add( document )
148                         .bind( "vmouseup", function() {
149                                 if ( self.dragging ) {
150
151                                         self.dragging = false;
152
153                                         if ( cType === "select") {
154                                         
155                                                 // make the handle move with a smooth transition
156                                                 handle.addClass( "ui-slider-handle-snapping" );
157                                         
158                                                 if ( self.mouseMoved ) {
159                                                 
160                                                         // this is a drag, change the value only if user dragged enough
161                                                         if ( self.userModified ) {
162                                                                 self.refresh( self.beforeStart == 0 ? 1 : 0 );
163                                                         }
164                                                         else {
165                                                                 self.refresh( self.beforeStart );
166                                                         }
167                                                         
168                                                 }
169                                                 else {
170                                                         // this is just a click, change the value
171                                                         self.refresh( self.beforeStart == 0 ? 1 : 0 );
172                                                 }
173                                                 
174                                         }
175                                         
176                                         self.mouseMoved = false;
177                                         
178                                         return false;
179                                 }
180                         });
181
182                 slider.insertAfter( control );
183
184                 // NOTE force focus on handle
185                 this.handle
186                         .bind( "vmousedown", function() {
187                                 $( this ).focus();
188                         })
189                         .bind( "vclick", false );
190
191                 this.handle
192                         .bind( "keydown", function( event ) {
193                                 var index = val();
194
195                                 if ( self.options.disabled ) {
196                                         return;
197                                 }
198
199                                 // In all cases prevent the default and mark the handle as active
200                                 switch ( event.keyCode ) {
201                                  case $.mobile.keyCode.HOME:
202                                  case $.mobile.keyCode.END:
203                                  case $.mobile.keyCode.PAGE_UP:
204                                  case $.mobile.keyCode.PAGE_DOWN:
205                                  case $.mobile.keyCode.UP:
206                                  case $.mobile.keyCode.RIGHT:
207                                  case $.mobile.keyCode.DOWN:
208                                  case $.mobile.keyCode.LEFT:
209                                         event.preventDefault();
210
211                                         if ( !self._keySliding ) {
212                                                 self._keySliding = true;
213                                                 $( this ).addClass( "ui-state-active" );
214                                         }
215                                         break;
216                                 }
217
218                                 // move the slider according to the keypress
219                                 switch ( event.keyCode ) {
220                                  case $.mobile.keyCode.HOME:
221                                         self.refresh( min );
222                                         break;
223                                  case $.mobile.keyCode.END:
224                                         self.refresh( max );
225                                         break;
226                                  case $.mobile.keyCode.PAGE_UP:
227                                  case $.mobile.keyCode.UP:
228                                  case $.mobile.keyCode.RIGHT:
229                                         self.refresh( index + step );
230                                         break;
231                                  case $.mobile.keyCode.PAGE_DOWN:
232                                  case $.mobile.keyCode.DOWN:
233                                  case $.mobile.keyCode.LEFT:
234                                         self.refresh( index - step );
235                                         break;
236                                 }
237                         }) // remove active mark
238                         .keyup( function( event ) {
239                                 if ( self._keySliding ) {
240                                         self._keySliding = false;
241                                         $( this ).removeClass( "ui-state-active" );
242                                 }
243                         });
244
245                 this.refresh(undefined, undefined, true);
246         },
247
248         refresh: function( val, isfromControl, preventInputUpdate ) {
249
250                 if ( this.options.disabled || this.element.attr('disabled')) { 
251                         this.disable();
252                 }
253
254                 var control = this.element, percent,
255                         cType = control[0].nodeName.toLowerCase(),
256                         min = cType === "input" ? parseFloat( control.attr( "min" ) ) : 0,
257                         max = cType === "input" ? parseFloat( control.attr( "max" ) ) : control.find( "option" ).length - 1;
258
259                 if ( typeof val === "object" ) {
260                         var data = val,
261                                 // a slight tolerance helped get to the ends of the slider
262                                 tol = 8;
263                         if ( !this.dragging ||
264                                         data.pageX < this.slider.offset().left - tol ||
265                                         data.pageX > this.slider.offset().left + this.slider.width() + tol ) {
266                                 return;
267                         }
268                         percent = Math.round( ( ( data.pageX - this.slider.offset().left ) / this.slider.width() ) * 100 );
269                 } else {
270                         if ( val == null ) {
271                                 val = cType === "input" ? parseFloat( control.val() ) : control[0].selectedIndex;
272                         }
273                         percent = ( parseFloat( val ) - min ) / ( max - min ) * 100;
274                 }
275
276                 if ( isNaN( percent ) ) {
277                         return;
278                 }
279
280                 if ( percent < 0 ) {
281                         percent = 0;
282                 }
283
284                 if ( percent > 100 ) {
285                         percent = 100;
286                 }
287
288                 var newval = Math.round( ( percent / 100 ) * ( max - min ) ) + min;
289
290                 if ( newval < min ) {
291                         newval = min;
292                 }
293
294                 if ( newval > max ) {
295                         newval = max;
296                 }
297
298                 // Flip the stack of the bg colors
299                 if ( percent > 60 && cType === "select" ) {
300                         // TODO: Dead path?
301                 }
302                 this.handle.css( "left", percent + "%" );
303                 this.handle.attr( {
304                                 "aria-valuenow": cType === "input" ? newval : control.find( "option" ).eq( newval ).attr( "value" ),
305                                 "aria-valuetext": cType === "input" ? newval : control.find( "option" ).eq( newval ).getEncodedText(),
306                                 title: newval
307                         });
308
309                 // add/remove classes for flip toggle switch
310                 if ( cType === "select" ) {
311                         if ( newval === 0 ) {
312                                 this.slider.addClass( "ui-slider-switch-a" )
313                                         .removeClass( "ui-slider-switch-b" );
314                         } else {
315                                 this.slider.addClass( "ui-slider-switch-b" )
316                                         .removeClass( "ui-slider-switch-a" );
317                         }
318                 }
319
320                 if ( !preventInputUpdate ) {
321                         var valueChanged = false;
322
323                         // update control"s value
324                         if ( cType === "input" ) {
325                                 valueChanged = control.val() !== newval;
326                                 control.val( newval );
327                         } else {
328                                 valueChanged = control[ 0 ].selectedIndex !== newval;
329                                 control[ 0 ].selectedIndex = newval;
330                         }
331                         if ( !isfromControl && valueChanged ) {
332                                 control.trigger( "change" );
333                         }
334                 }
335         },
336
337         enable: function() {
338                 this.element.attr( "disabled", false );
339                 this.slider.removeClass( "ui-disabled" ).attr( "aria-disabled", false );
340                 return this._setOption( "disabled", false );
341         },
342
343         disable: function() {
344                 this.element.attr( "disabled", true );
345                 this.slider.addClass( "ui-disabled" ).attr( "aria-disabled", true );
346                 return this._setOption( "disabled", true );
347         }
348
349 });
350
351 //auto self-init widgets
352 $( document ).bind( "pagecreate create", function( e ){
353         $.mobile.slider.prototype.enhanceWithin( e.target );
354 });
355
356 })( jQuery );