5d7325ded7a7f1801f3ed673dec51c3a7dc310b5
[platform/framework/web/web-ui-fw.git] / src / widgets / slider / js / jquery.mobile.tizen.slider.js
1 /*
2  * jQuery Mobile Widget @VERSION
3  *
4  * This software is licensed under the MIT licence (as defined by the OSI at
5  * http://www.opensource.org/licenses/mit-license.php)
6  *
7  * ***************************************************************************
8  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
9  * Copyright (c) 2011 by Intel Corporation Ltd.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  * ***************************************************************************
29  *
30  * Authors: Max Waterman <max.waterman@intel.com>
31  * Authors: Minkyu Kang <mk7.kang@samsung.com>
32  */
33
34 /**
35  * tizenslider modifies the JQuery Mobile slider and is created in the same way.
36  *
37  * See the JQuery Mobile slider widget for more information :
38  *     http://jquerymobile.com/demos/1.0a4.1/docs/forms/forms-slider.html
39  *
40  * The JQuery Mobile slider option:
41  *     theme: specify the theme using the 'data-theme' attribute
42  *
43  * Options:
44  *     theme: string; the theme to use if none is specified using the 'data-theme' attribute
45  *            default: 'c'
46  *     popup: boolean; controls whether the popup is displayed or not
47  *                   specify if the popup is enabled using the 'data-popup' attribute
48  *                   set from javascript using .tizenslider('option','popup',newValue)
49  *
50  * Events:
51  *     changed: triggers when the value is changed (rather than when the handle is moved)
52  *
53  * Examples:
54  *
55  *     <a href="#" id="popupEnabler" data-role="button" data-inline="true">Enable popup</a>
56  *     <a href="#" id="popupDisabler" data-role="button" data-inline="true">Disable popup</a>
57  *     <div data-role="fieldcontain">
58  *         <input id="mySlider" data-theme='a' data-popup='false' type="range" name="slider" value="7" min="0" max="9" />
59  *     </div>
60  *     <div data-role="fieldcontain">
61  *         <input id="mySlider2" type="range" name="slider" value="77" min="0" max="777" />
62  *     </div>
63  *
64  *     // disable popup from javascript
65  *     $('#mySlider').tizenslider('option','popup',false);
66  *
67  *     // from buttons
68  *     $('#popupEnabler').bind('vclick', function() {
69  *         $('#mySlider').tizenslider('option','popup',true);
70  *     });
71  *     $('#popupDisabler').bind('vclick', function() {
72  *         $('#mySlider').tizenslider('option','popup',false);
73  *     });
74  */
75
76 /**
77         @class Slider
78         The slider widget shows a control on the screen that you can use to change values by dragging a handle on a horizontal scale. Sliders can be used in Tizen as described in the jQueryMobile documentation for sliders.
79
80         To add a slider widget to the application, use the following code:
81
82                 <input data-popup='false' type="range" name="slider" value="5" min="0" max="10" data-icon="text" data-text-left="Min" data-text-right="Max" />
83
84         The slider can define callbacks for events as described in the jQueryMobile documentation for slider events.
85         You can use methods with the slider as described in the jQueryMobile documentation for slider methods.
86 */
87 /**
88         @property {String} data-icon
89         Defines the icon style for the slider ends. The icon options are bright, volume, and text.
90         The default value is text.
91 */
92 /**
93         @property {Boolean} data-popup
94         Enables or disables a pop-up showing the current value while the handle is dragged.
95         The default value is true.
96 */
97 /**
98         @property {String} data-text-left
99         Defines the text displayed on the left side of the slider.
100         The data-icon option must be set to text.
101 */
102 /**
103         @property {String} data-text-right
104         Defines the text displayed on the right side of the slider.
105         The data-icon option must be set to text.
106 */
107
108 (function ($, window, undefined) {
109         $.widget("tizen.tizenslider", $.mobile.widget, {
110                 options: {
111                         popup: true
112                 },
113
114                 popup: null,
115                 handle: null,
116                 handleText: null,
117
118                 _create: function () {
119                         this.currentValue = null;
120                         this.popupVisible = false;
121
122                         var self = this,
123                                 inputElement = $( this.element ),
124                                 slider,
125                                 handle_press,
126                                 popupEnabledAttr,
127                                 icon,
128                                 text_right,
129                                 text_left,
130                                 text_length,
131                                 elem_left,
132                                 elem_right,
133                                 margin_left,
134                                 margin_right;
135
136                         // apply jqm slider
137                         inputElement.slider();
138
139                         // hide the slider input element proper
140                         inputElement.hide();
141
142                         self.popup = $('<div class="ui-slider-popup"></div>');
143
144                         // set the popup according to the html attribute
145                         popupEnabledAttr = inputElement.jqmData('popup');
146                         if ( popupEnabledAttr !== undefined ) {
147                                 self.options.popup = ( popupEnabledAttr == true );
148                         }
149
150                         // get the actual slider added by jqm
151                         slider = inputElement.next('.ui-slider');
152
153                         icon = inputElement.attr('data-icon');
154
155                         // wrap the background
156                         slider.wrap('<div class="ui-slider-container"></div>');
157
158                         // get the handle
159                         self.handle = slider.find('.ui-slider-handle');
160
161                         // remove the rounded corners from the slider and its children
162                         slider.removeClass('ui-btn-corner-all');
163                         slider.find('*').removeClass('ui-btn-corner-all');
164
165                         // add icon
166                         switch ( icon ) {
167                         case 'bright':
168                         case 'volume':
169                                 elem_left = $('<div class="ui-slider-left-' + icon + '"></div>');
170                                 elem_right = $('<div class="ui-slider-right-' + icon + '"></div>');
171
172                                 slider.before( elem_left );
173                                 slider.after( elem_right );
174
175                                 margin_left = elem_left.width() + 16;
176                                 margin_right = elem_right.width() + 16;
177                                 break;
178
179                         case 'text':
180                                 text_left = ( inputElement.attr('data-text-left') === undefined ) ? '' :
181                                                 inputElement.attr('data-text-left').substring( 0, 3 );
182                                 text_right = ( inputElement.attr('data-text-right') === undefined ) ? '' :
183                                                 inputElement.attr('data-text-right').substring( 0, 3 );
184
185                                 text_length = Math.max( text_left.length, text_right.length );
186
187                                 margin_left = text_length + "rem";
188                                 margin_right = text_length + "rem";
189
190                                 elem_left = $('<div class="ui-slider-left-text" style="left:' +
191                                         -( text_length ) + 'rem; width:' + text_length + 'rem;">' +
192                                         '<span style="position:relative;top:0.4em;">' +
193                                         text_left +
194                                         '</span></div>');
195                                 elem_right = $('<div class="ui-slider-right-text" style="right:' +
196                                         -( text_length ) + 'rem; width:' + text_length + 'rem;">' +
197                                         '<span style="position:relative;top:0.4em;">' +
198                                         text_right +
199                                         '</span></div>');
200
201                                 slider.before( elem_left );
202                                 slider.after( elem_right );
203                                 break;
204                         }
205
206                         if ( icon ) {
207                                 slider.parent('.ui-slider-container').css({
208                                         "margin-left": margin_left,
209                                         "margin-right": margin_right
210                                 });
211                         }
212
213                         // handle press
214                         slider.append($('<div class="ui-slider-handle-press"></div>'));
215                         self.handle_press = slider.find('.ui-slider-handle-press');
216                         self.handle_press.css('display', 'none');
217
218                         // add a popup element (hidden initially)
219                         slider.before( self.popup );
220                         self.popup.hide();
221
222                         // get the element where value can be displayed
223                         self.handleText = slider.find('.ui-btn-text');
224
225                         // set initial value
226                         self.updateSlider();
227
228                         // bind to changes in the slider's value to update handle text
229                         this.element.bind('change', function () {
230                                 self.updateSlider();
231                         });
232
233                         // bind clicks on the handle to show the popup
234                         self.handle.bind('vmousedown', function () {
235                                 self.showPopup();
236                         });
237
238                         // watch events on the document to turn off the slider popup
239                         slider.add( document ).bind('vmouseup', function () {
240                                 self.hidePopup();
241                         });
242                 },
243
244                 _handle_press_show: function () {
245                         this.handle_press.css('display', '');
246                 },
247
248                 _handle_press_hide: function () {
249                         this.handle_press.css('display', 'none');
250                 },
251
252                 // position the popup
253                 positionPopup: function () {
254                         var dstOffset = this.handle.offset();
255
256                         this.popup.offset({
257                                 left: dstOffset.left + ( this.handle.width() - this.popup.width() ) / 2
258                         });
259
260                         this.handle_press.offset({
261                                 left: dstOffset.left,
262                                 top: dstOffset.top
263                         });
264                 },
265
266                 // show value on the handle and in popup
267                 updateSlider: function () {
268                         var font_size,
269                                 font_length,
270                                 font_top,
271                                 padding_size,
272                                 newValue,
273                                 get_value_length = function ( v ) {
274                                         var val = Math.abs( v ),
275                                                 len;
276
277                                         if ( val > 999 ) {
278                                                 len = 4;
279                                         } else if ( val > 99 ) {
280                                                 len = 3;
281                                         } else if ( val > 9 ) {
282                                                 len = 2;
283                                         } else {
284                                                 len = 1;
285                                         }
286
287                                         if ( v < 0 ) {
288                                                 len++;
289                                         }
290
291                                         return len;
292                                 };
293
294                         // remove the title attribute from the handle (which is
295                         // responsible for the annoying tooltip); NB we have
296                         // to do it here as the jqm slider sets it every time
297                         // the slider's value changes :(
298                         this.handle.removeAttr('title');
299
300                         newValue = this.element.val();
301
302                         font_length = get_value_length( newValue );
303
304                         if ( this.popupVisible ) {
305                                 this.positionPopup();
306
307                                 switch ( font_length ) {
308                                 case 1:
309                                 case 2:
310                                         font_size = '1.5rem';
311                                         padding_size = '0.15rem';
312                                         break;
313                                 case 3:
314                                         font_size = '1rem';
315                                         padding_size = '0.5rem';
316                                         break;
317                                 default:
318                                         font_size = '0.8rem';
319                                         padding_size = '0.5rem';
320                                         break;
321                                 }
322
323                                 this.popup.css({
324                                         "font-size": font_size,
325                                         "padding-top": padding_size
326                                 });
327                         }
328
329                         if ( newValue === this.currentValue ) {
330                                 return;
331                         }
332
333                         switch ( font_length ) {
334                         case 1:
335                                 font_size = '0.95rem';
336                                 font_top = '0';
337                                 break;
338                         case 2:
339                                 font_size = '0.85rem';
340                                 font_top = '-0.01rem';
341                                 break;
342                         case 3:
343                                 font_size = '0.65rem';
344                                 font_top = '-0.05rem';
345                                 break;
346                         default:
347                                 font_size = '0.45rem';
348                                 font_top = '-0.15rem';
349                                 break;
350                         }
351
352                         if ( font_size != this.handleText.css('font-size') ) {
353                                 this.handleText.css({
354                                         'font-size': font_size,
355                                         'top': font_top
356                                 });
357                         }
358
359                         this.currentValue = newValue;
360                         this.handleText.text( newValue );
361                         this.popup.html( newValue );
362
363                         this.element.trigger( 'update', newValue );
364                 },
365
366                 // show the popup
367                 showPopup: function () {
368                         if ( !this.options.popup || this.popupVisible ) {
369                                 return;
370                         }
371
372                         this.popup.show();
373                         this.popupVisible = true;
374                         this._handle_press_show();
375                 },
376
377                 // hide the popup
378                 hidePopup: function () {
379                         if ( !this.options.popup || !this.popupVisible ) {
380                                 return;
381                         }
382
383                         this.popup.hide();
384                         this.popupVisible = false;
385                         this._handle_press_hide();
386                 },
387
388                 _setOption: function (key, value) {
389                         var needToChange = ( value !== this.options[key] );
390
391                         if ( !needToChange ) {
392                                 return;
393                         }
394
395                         switch ( key ) {
396                         case 'popup':
397                                 this.options.popup = value;
398
399                                 if ( this.options.popup) {
400                                         this.updateSlider();
401                                 } else {
402                                         this.hidePopup();
403                                 }
404
405                                 break;
406                         }
407                 }
408         });
409
410         // stop jqm from initialising sliders
411         $( document ).bind( "pagebeforecreate", function ( e ) {
412                 if ( $.data( window, "jqmSliderInitSelector" ) === undefined ) {
413                         $.data( window, "jqmSliderInitSelector",
414                                 $.mobile.slider.prototype.options.initSelector );
415                         $.mobile.slider.prototype.options.initSelector = null;
416                 }
417         });
418
419         // initialise sliders with our own slider
420         $( document ).bind( "pagecreate", function ( e ) {
421                 var jqmSliderInitSelector = $.data( window, "jqmSliderInitSelector" );
422                 $( e.target ).find(jqmSliderInitSelector).not('select').tizenslider();
423                 $( e.target ).find(jqmSliderInitSelector).filter('select').slider();
424         });
425
426 }( jQuery, this ));