gallery: get the width correctly
[platform/framework/web/web-ui-fw.git] / src / widgets / gallery / js / jquery.mobile.tizen.gallery.js
1 /* ***************************************************************************
2  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  * ***************************************************************************
22  *
23  *      Author: Minkyu Kang <mk7.kang@samsung.com>
24  */
25
26 /*
27  * Gallery widget
28  *
29  * HTML Attributes
30  *
31  *  data-role: set to 'gallery'
32  *  data-index: start index
33  *  data-vertical-align: set to top or middle or bottom.
34  *
35  * APIs
36  *
37  *  add(file): add the image (parameter: url of iamge)
38  *  remove(index): remove the image (parameter: index of image)
39  *  refresh(index): refresh the widget, should be called after add or remove. (parameter: start index)
40  *
41  * Events
42  *
43  *  N/A
44  *
45  * Example
46  *
47  * <div data-role="gallery" id="gallery" data-index="3" data-vertical-align="middle">
48  *      <img src="01.jpg">
49  *      <img src="02.jpg">
50  *      <img src="03.jpg">
51  *      <img src="04.jpg">
52  *      <img src="05.jpg">
53  * </div>
54  *
55  *
56  * $('#gallery-add').bind('vmouseup', function ( e ) {
57  *      $('#gallery').gallery('add', '9.jpg');
58  *      $('#gallery').gallery('add', '10.jpg');
59  *      $('#gallery').gallery('refresh');
60  * });
61  *
62  * $('#gallery-del').bind('vmouseup', function ( e ) {
63  *      $('#gallery').gallery('remove');
64  * });
65  *
66  */
67
68  /**
69         @class Gallery
70         The image slider widget shows images in a gallery on the screen. <br/><br/> To add an image slider widget to the application, use the following code:
71
72                 <div data-role="gallery" id="gallery" data-vertical-align="middle" data-index="3">
73                         <img src="01.jpg">
74                         <img src="02.jpg">
75                         <img src="03.jpg">
76                         <img src="04.jpg">
77                         <img src="05.jpg">
78                 </div>
79 */
80 /**
81         @property {Integer} data-index
82         Defines the index number of the first image in the gallery.
83         <br/>The default value is 0.
84 */
85 /**
86         @property {String} data-vertical-align
87         Defines the image alignment. The alignment options are top, middle, and bottom.
88         <br/>The default value is top.
89 */
90 /**
91         @method add
92         The add method is used to add an image to the image slider. The image_file attribute defines the image file URL.
93
94                 <div id="gallery" data-role="gallery" data-vertical-align="middle"></div>
95                 $("#gallery").gallery('add', [image_file]);
96 */
97 /**
98         @method remove
99         The remove method is used to delete an image from the image slider. The image_index attribute defines the index of the image to be deleted.
100
101                 <div id="gallery" data-role="gallery" data-vertical-align="middle"></div>
102                 $("#gallery").gallery('remove', [image_index]);
103 */
104 /**
105         @method refresh
106         The refresh method is used to refresh the image slider. This method must be called after adding images to the image slider.
107
108                 <div id="gallery" data-role="gallery" data-vertical-align="middle"></div>
109                 $("#gallery").gallery('refresh');
110 */
111 (function ( $, window, undefined ) {
112         $.widget( "tizen.gallery", $.mobile.widget, {
113                 options: {
114                         flicking: false,
115                         duration: 500
116                 },
117
118                 dragging: false,
119                 moving: false,
120                 max_width: 0,
121                 max_height: 0,
122                 org_x: 0,
123                 org_time: null,
124                 cur_img: null,
125                 prev_img: null,
126                 next_img: null,
127                 images: [],
128                 images_hold: [],
129                 index: 0,
130                 align_type: null,
131                 direction: 1,
132                 container: null,
133                 loader: [],
134
135                 _resize: function ( index ) {
136                         var img = this.images[index],
137                                 width = this.images[index].width(),
138                                 height = this.images[index].height(),
139                                 margin = 0,
140                                 ratio,
141                                 img_max_width = this.max_width - margin,
142                                 img_max_height = this.max_height - margin;
143
144                         ratio = height / width;
145
146                         if ( width > img_max_width ) {
147                                 img.width( img_max_width );
148                                 img.height( img_max_width * ratio );
149                         }
150
151                         height = img.height();
152
153                         if ( height > img_max_height ) {
154                                 img.height( img_max_height );
155                                 img.width( img_max_height / ratio );
156                         }
157                 },
158
159                 _align: function ( index, obj ) {
160                         var img = this.images[index],
161                                 img_top = 0;
162
163                         if ( !obj) {
164                                 return;
165                         }
166                         if ( !obj.length ) {
167                                 return;
168                         }
169
170                         if ( this.align_type == "middle" ) {
171                                 img_top = ( this.max_height - img.height() ) / 2;
172                         } else if ( this.align_type == "bottom" ) {
173                                 img_top = this.max_height - img.height();
174                         } else {
175                                 img_top = 0;
176                         }
177
178                         obj.css( 'top', img_top + 'px' );
179                 },
180
181                 _attach: function ( index, obj ) {
182                         var self = this,
183                                 processing = function () {
184                                         self._resize( index );
185                                         self._align( index, obj );
186                                 };
187
188                         if ( !obj) {
189                                 return;
190                         }
191                         if ( !obj.length ) {
192                                 return;
193                         }
194                         if ( index < 0 ) {
195                                 return;
196                         }
197                         if ( index >= this.images.length ) {
198                                 return;
199                         }
200
201                         obj.css( "display", "block" );
202                         obj.append( this.images[index] );
203
204                         if ( this.images[index].height() ) {
205                                 processing();
206                         } else {
207                                 this.loader[index] = setInterval( function () {
208                                         if ( !self.images[index].height() ) {
209                                                 return;
210                                         }
211
212                                         processing();
213                                         clearInterval( self.loader[index] );
214                                 }, 10);
215                         }
216                 },
217
218                 _detach: function ( index, obj ) {
219                         if ( !obj) {
220                                 return;
221                         }
222                         if ( !obj.length ) {
223                                 return;
224                         }
225                         if ( index < 0 ) {
226                                 return;
227                         }
228                         if ( index >= this.images.length ) {
229                                 return;
230                         }
231
232                         obj.css( "display", "none" );
233                         this.images[index].removeAttr("style");
234                         this.images[index].detach();
235
236                         clearInterval( this.loader[index] );
237                 },
238
239                 _drag: function ( _x ) {
240                         var delta,
241                                 coord_x;
242
243                         if ( !this.dragging ) {
244                                 return;
245                         }
246
247                         if ( this.options.flicking === false ) {
248                                 delta = this.org_x - _x;
249
250                                 // first image
251                                 if ( delta < 0 && !this.prev_img.length ) {
252                                         return;
253                                 }
254                                 // last image
255                                 if ( delta > 0 && !this.next_img.length ) {
256                                         return;
257                                 }
258                         }
259
260                         coord_x = _x - this.org_x;
261
262                         this.cur_img.css( 'left', coord_x + 'px' );
263                         if ( this.next_img.length ) {
264                                 this.next_img.css( 'left', coord_x + this.window_width + 'px' );
265                         }
266                         if ( this.prev_img.length ) {
267                                 this.prev_img.css( 'left', coord_x - this.window_width + 'px' );
268                         }
269                 },
270
271                 _move: function ( _x ) {
272                         var delta = this.org_x - _x,
273                                 flip = 0,
274                                 drag_time,
275                                 sec,
276                                 self;
277
278                         if ( delta == 0 ) {
279                                 return;
280                         }
281
282                         if ( delta > 0 ) {
283                                 flip = delta < ( this.max_width * 0.45 ) ? 0 : 1;
284                         } else {
285                                 flip = -delta < ( this.max_width * 0.45 ) ? 0 : 1;
286                         }
287
288                         if ( !flip ) {
289                                 drag_time = Date.now() - this.org_time;
290
291                                 if ( Math.abs( delta ) / drag_time > 1 ) {
292                                         flip = 1;
293                                 }
294                         }
295
296                         if ( flip ) {
297                                 if ( delta > 0 && this.next_img.length ) {
298                                         /* next */
299                                         this._detach( this.index - 1, this.prev_img );
300
301                                         this.prev_img = this.cur_img;
302                                         this.cur_img = this.next_img;
303                                         this.next_img = this.next_img.next();
304
305                                         this.index++;
306
307                                         if ( this.next_img.length ) {
308                                                 this.next_img.css( 'left', this.window_width + 'px' );
309                                                 this._attach( this.index + 1, this.next_img );
310                                         }
311
312                                         this.direction = 1;
313
314                                 } else if ( delta < 0 && this.prev_img.length ) {
315                                         /* prev */
316                                         this._detach( this.index + 1, this.next_img );
317
318                                         this.next_img = this.cur_img;
319                                         this.cur_img = this.prev_img;
320                                         this.prev_img = this.prev_img.prev();
321
322                                         this.index--;
323
324                                         if ( this.prev_img.length ) {
325                                                 this.prev_img.css( 'left', -this.window_width + 'px' );
326                                                 this._attach( this.index - 1, this.prev_img );
327                                         }
328
329                                         this.direction = -1;
330                                 }
331                         }
332
333                         sec = this.options.duration;
334                         self = this;
335
336                         this.moving = true;
337
338                         setTimeout( function () {
339                                 self.moving = false;
340                         }, sec - 50 );
341
342                         this.cur_img.animate( { left: 0 }, sec );
343                         if ( this.next_img.length ) {
344                                 this.next_img.animate( { left: this.window_width }, sec );
345                         }
346                         if ( this.prev_img.length ) {
347                                 this.prev_img.animate( { left: -this.window_width }, sec );
348                         }
349                 },
350
351                 _add_event: function () {
352                         var self = this,
353                                 date;
354
355                         this.container.bind( 'vmousemove', function ( e ) {
356                                 e.preventDefault();
357
358                                 if ( self.moving ) {
359                                         return;
360                                 }
361                                 if ( !self.dragging ) {
362                                         return;
363                                 }
364
365                                 self._drag( e.pageX );
366                         } );
367
368                         this.container.bind( 'vmousedown', function ( e ) {
369                                 e.preventDefault();
370
371                                 if ( self.moving ) {
372                                         return;
373                                 }
374
375                                 self.dragging = true;
376
377                                 self.org_x = e.pageX;
378
379                                 self.org_time = Date.now();
380                         } );
381
382                         this.container.bind( 'vmouseup', function ( e ) {
383                                 if ( self.moving ) {
384                                         return;
385                                 }
386
387                                 self.dragging = false;
388
389                                 self._move( e.pageX );
390                         } );
391
392                         this.container.bind( 'vmouseout', function ( e ) {
393                                 if ( self.moving ) {
394                                         return;
395                                 }
396                                 if ( !self.dragging ) {
397                                         return;
398                                 }
399
400                                 if ( ( e.pageX < 20 ) ||
401                                                 ( e.pageX > ( self.max_width - 20 ) ) ) {
402                                         self._move( e.pageX );
403                                         self.dragging = false;
404                                 }
405                         } );
406                 },
407
408                 _del_event: function () {
409                         this.container.unbind( 'vmousemove' );
410                         this.container.unbind( 'vmousedown' );
411                         this.container.unbind( 'vmouseup' );
412                         this.container.unbind( 'vmouseout' );
413                 },
414
415                 _show: function () {
416                         /* resizing */
417                         this.window_width = $( window ).width();
418                         this.max_width = this._get_width();
419                         this.max_height = this._get_height();
420                         this.container.css( 'height', this.max_height );
421
422                         this.cur_img = $( 'div' ).find( '.ui-gallery-bg:eq(' + this.index + ')' );
423                         this.prev_img = this.cur_img.prev();
424                         this.next_img = this.cur_img.next();
425
426                         this._attach( this.index - 1, this.prev_img );
427                         this._attach( this.index, this.cur_img );
428                         this._attach( this.index + 1, this.next_img );
429
430                         if ( this.prev_img.length ) {
431                                 this.prev_img.css( 'left', -this.window_width + 'px' );
432                         }
433
434                         this.cur_img.css( 'left', '0px' );
435
436                         if ( this.next_img.length ) {
437                                 this.next_img.css( 'left', this.window_width + 'px' );
438                         }
439                 },
440
441                 show: function () {
442                         this._show();
443                         this._add_event();
444                 },
445
446                 _hide: function () {
447                         this._detach( this.index - 1, this.prev_img );
448                         this._detach( this.index, this.cur_img );
449                         this._detach( this.index + 1, this.next_img );
450                 },
451
452                 hide: function () {
453                         this._hide();
454                         this._del_event();
455                 },
456
457                 _get_width: function () {
458                         return $( this.element ).width();
459                 },
460
461                 _get_height: function () {
462                         var $page = $( this.element ).parentsUntil( 'ui-page' ),
463                                 $content = $page.children( '.ui-content' ),
464                                 header_h = $page.children( '.ui-header' ).outerHeight() || 0,
465                                 footer_h = $page.children( '.ui-footer' ).outerHeight() || 0,
466                                 padding = parseFloat( $content.css( 'padding-top' ) )
467                                         + parseFloat( $content.css( 'padding-bottom' ) ),
468                                 content_h = $( window ).height() - header_h - footer_h - padding;
469
470                         return content_h;
471                 },
472
473                 _create: function () {
474                         var temp_img,
475                                 self = this,
476                                 index,
477                                 i = 0;
478
479                         $( this.element ).wrapInner( '<div class="ui-gallery"></div>' );
480                         $( this.element ).find( 'img' ).wrap( '<div class="ui-gallery-bg"></div>' );
481
482                         this.container = $( this.element ).find('.ui-gallery');
483
484                         temp_img = $( 'div' ).find( '.ui-gallery-bg:first' );
485
486                         while ( temp_img.length ) {
487                                 this.images[i] = temp_img.find( 'img' );
488                                 temp_img = temp_img.next();
489                                 i++;
490                         }
491
492                         for ( i = 0; i < this.images.length; i++ ) {
493                                 this.images[i].detach();
494                         }
495
496                         index = parseInt( $( this.element ).jqmData( 'index' ), 10 );
497                         if ( !index ) {
498                                 index = 0;
499                         }
500                         if ( index < 0 ) {
501                                 index = 0;
502                         }
503                         if ( index >= this.images.length ) {
504                                 index = this.images.length - 1;
505                         }
506
507                         this.index = index;
508
509                         this.align_type = $( this.element ).jqmData( 'vertical-align' );
510
511                         $( window ).bind( 'resize', function () {
512                                 self.refresh();
513                         });
514                 },
515
516                 _update: function () {
517                         var image_file,
518                                 bg_html,
519                                 temp_img;
520
521                         while ( this.images_hold.length ) {
522                                 image_file = this.images_hold.shift();
523
524                                 bg_html = $( '<div class="ui-gallery-bg"></div>' );
525                                 temp_img = $( '<img src="' + image_file + '"></div>' );
526
527                                 bg_html.append( temp_img );
528                                 this.container.append( bg_html );
529                                 this.images.push( temp_img );
530                         }
531                 },
532
533                 refresh: function ( start_index ) {
534                         this._update();
535
536                         this._hide();
537
538                         if ( start_index === undefined ) {
539                                 start_index = this.index;
540                         }
541                         if ( start_index < 0 ) {
542                                 start_index = 0;
543                         }
544                         if ( start_index >= this.images.length ) {
545                                 start_index = this.images.length - 1;
546                         }
547
548                         this.index = start_index;
549
550                         this._show();
551                 },
552
553                 add: function ( file ) {
554                         this.images_hold.push( file );
555                 },
556
557                 remove: function ( index ) {
558                         var temp_img;
559
560                         if ( index === undefined ) {
561                                 index = this.index;
562                         }
563
564                         if ( index < 0 || index >= this.images.length ) {
565                                 return;
566                         }
567
568                         if ( index == this.index ) {
569                                 temp_img = this.cur_img;
570
571                                 if ( this.index == 0 ) {
572                                         this.direction = 1;
573                                 } else if ( this.index == this.images.length - 1 ) {
574                                         this.direction = -1;
575                                 }
576
577                                 if ( this.direction < 0 ) {
578                                         this.cur_img = this.prev_img;
579                                         this.prev_img = this.prev_img.prev();
580                                         if ( this.prev_img.length ) {
581                                                 this.prev_img.css( 'left', -this.window_width );
582                                                 this._attach( index - 2, this.prev_img );
583                                         }
584                                         this.index--;
585                                 } else {
586                                         this.cur_img = this.next_img;
587                                         this.next_img = this.next_img.next();
588                                         if ( this.next_img.length ) {
589                                                 this.next_img.css( 'left', this.window_width );
590                                                 this._attach( index + 2, this.next_img );
591                                         }
592                                 }
593
594                                 this.cur_img.animate( { left: 0 }, this.options.duration );
595
596                         } else if ( index == this.index - 1 ) {
597                                 temp_img = this.prev_img;
598                                 this.prev_img = this.prev_img.prev();
599                                 if ( this.prev_img.length ) {
600                                         this.prev_img.css( 'left', -this.window_width );
601                                         this._attach( index - 1, this.prev_img );
602                                 }
603                                 this.index--;
604
605                         } else if ( index == this.index + 1 ) {
606                                 temp_img = this.next_img;
607                                 this.next_img = this.next_img.next();
608                                 if ( this.next_img.length ) {
609                                         this.next_img.css( 'left', this.window_width );
610                                         this._attach( index + 1, this.next_img );
611                                 }
612
613                         } else {
614                                 temp_img = $( 'div' ).find( '.ui-gallery-bg:eq(' + index + ')' );
615                         }
616
617                         this.images.splice( index, 1 );
618                         temp_img.detach();
619                 }
620         }); /* End of widget */
621
622         // auto self-init widgets
623         $( document ).bind( "pagecreate", function ( e ) {
624                 $( e.target ).find( ":jqmData(role='gallery')" ).gallery();
625         });
626
627         $( document ).bind( "pageshow", function ( e ) {
628                 $( e.target ).find( ":jqmData(role='gallery')" ).gallery( 'show' );
629         });
630
631         $( document ).bind( "pagebeforehide", function ( e ) {
632                 $( e.target ).find( ":jqmData(role='gallery')" ).gallery( 'hide' );
633         } );
634
635 }( jQuery, this ) );