gallery: use timeout function instead of interval
[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  *  empty: remove all of images from the gallery
41  *  length: get length of images
42  *  value(index): get or set current index of gallery (parameter: index of image)
43  *
44  * Events
45  *
46  *  N/A
47  *
48  * Example
49  *
50  * <div data-role="gallery" id="gallery" data-index="3" data-vertical-align="middle">
51  *      <img src="01.jpg">
52  *      <img src="02.jpg">
53  *      <img src="03.jpg">
54  *      <img src="04.jpg">
55  *      <img src="05.jpg">
56  * </div>
57  *
58  *
59  * $('#gallery-add').bind('vmouseup', function ( e ) {
60  *      $('#gallery').gallery('add', '9.jpg');
61  *      $('#gallery').gallery('add', '10.jpg');
62  *      $('#gallery').gallery('refresh');
63  * });
64  *
65  * $('#gallery-del').bind('vmouseup', function ( e ) {
66  *      $('#gallery').gallery('remove');
67  * });
68  *
69  */
70
71  /**
72         @class Gallery
73         The gallery widget shows images in a gallery on the screen. <br/><br/> To add an gallery widget to the application, use the following code:
74
75                 <div data-role="gallery" id="gallery" data-vertical-align="middle" data-index="3">
76                         <img src="01.jpg">
77                         <img src="02.jpg">
78                         <img src="03.jpg">
79                         <img src="04.jpg">
80                         <img src="05.jpg">
81                 </div>
82 */
83 /**
84         @property {Integer} data-index
85         Defines the index number of the first image in the gallery.
86         <br/>The default value is 0.
87 */
88 /**
89         @property {String} data-vertical-align
90         Defines the image alignment. The alignment options are top, middle, and bottom.
91         <br/>The default value is top.
92 */
93 /**
94         @method add
95         The add method is used to add an image to the gallery. The image_file attribute defines the image file URL.
96
97                 <div id="gallery" data-role="gallery" data-vertical-align="middle"></div>
98                 $("#gallery").gallery('add', [image_file]);
99 */
100 /**
101         @method remove
102         The remove method is used to delete an image from the gallery. The image_index attribute defines the index of the image to be deleted. If not set removes current image.
103
104                 <div id="gallery" data-role="gallery" data-vertical-align="middle"></div>
105                 $("#gallery").gallery('remove', [image_index]);
106 */
107 /**
108         @method refresh
109         The refresh method is used to refresh the gallery. This method must be called after adding images to the gallery.
110
111                 <div id="gallery" data-role="gallery" data-vertical-align="middle"></div>
112                 $("#gallery").gallery('refresh');
113 */
114 /**
115         @method empty
116         The empty method is used to remove all of images from the gallery.
117
118                 <div id="gallery" data-role="gallery" data-vertical-align="middle"></div>
119                 $("#gallery").gallery('empty');
120 */
121 /**
122         @method length
123         The length method is used to get length of images.
124
125                 <div id="gallery" data-role="gallery" data-vertical-align="middle"></div>
126                 length = $("#gallery").gallery('length');
127 */
128 /**
129         @method value
130         The value method is used to get or set current index of gallery. The image_index attribute defines the index of the image to be set. If not get current index.
131
132                 <div id="gallery" data-role="gallery" data-vertical-align="middle"></div>
133                 value = $("#gallery").gallery('value');
134                 $("#gallery").gallery('value', [image_index]);
135 */
136 (function ( $, window, undefined ) {
137         $.widget( "tizen.gallery", $.mobile.widget, {
138                 options: {
139                         flicking: false,
140                         duration: 500
141                 },
142
143                 dragging: false,
144                 moving: false,
145                 max_width: 0,
146                 max_height: 0,
147                 org_x: 0,
148                 org_time: null,
149                 cur_img: null,
150                 prev_img: null,
151                 next_img: null,
152                 images: [],
153                 images_hold: [],
154                 index: 0,
155                 align_type: null,
156                 direction: 1,
157                 container: null,
158
159                 _resize: function ( index ) {
160                         var img = this.images[index],
161                                 width = this.images[index].width(),
162                                 height = this.images[index].height(),
163                                 margin = 0,
164                                 ratio,
165                                 img_max_width = this.max_width - margin,
166                                 img_max_height = this.max_height - margin;
167
168                         ratio = height / width;
169
170                         if ( width > img_max_width ) {
171                                 img.width( img_max_width );
172                                 img.height( img_max_width * ratio );
173                         }
174
175                         height = img.height();
176
177                         if ( height > img_max_height ) {
178                                 img.height( img_max_height );
179                                 img.width( img_max_height / ratio );
180                         }
181                 },
182
183                 _align: function ( index, obj ) {
184                         var img = this.images[index],
185                                 img_top = 0;
186
187                         if ( !obj ) {
188                                 return;
189                         }
190                         if ( !obj.length ) {
191                                 return;
192                         }
193
194                         if ( this.align_type == "middle" ) {
195                                 img_top = ( this.max_height - img.height() ) / 2;
196                         } else if ( this.align_type == "bottom" ) {
197                                 img_top = this.max_height - img.height();
198                         } else {
199                                 img_top = 0;
200                         }
201
202                         obj.css( 'top', img_top + 'px' );
203                 },
204
205                 _attach: function ( index, obj ) {
206                         var self = this,
207                                 processing = function () {
208                                         self._resize( index );
209                                         self._align( index, obj );
210                                 },
211                                 loading = function () {
212                                         if ( self.images[index] === undefined ) {
213                                                 return;
214                                         }
215
216                                         if ( !self.images[index].height() ) {
217                                                 setTimeout( loading, 10 );
218                                                 return;
219                                         }
220
221                                         processing();
222                                 };
223
224                         if ( !obj ) {
225                                 return;
226                         }
227                         if ( !obj.length ) {
228                                 return;
229                         }
230                         if ( index < 0 ) {
231                                 return;
232                         }
233                         if ( !this.images.length ) {
234                                 return;
235                         }
236                         if ( index >= this.images.length ) {
237                                 return;
238                         }
239
240                         obj.css( "display", "block" );
241                         obj.append( this.images[index] );
242
243                         loading();
244                 },
245
246                 _detach: function ( index, obj ) {
247                         if ( !obj ) {
248                                 return;
249                         }
250                         if ( !obj.length ) {
251                                 return;
252                         }
253                         if ( index < 0 ) {
254                                 return;
255                         }
256                         if ( index >= this.images.length ) {
257                                 return;
258                         }
259
260                         obj.css( "display", "none" );
261                         this.images[index].removeAttr("style");
262                         this.images[index].detach();
263                 },
264
265                 _detach_all: function () {
266                         var i;
267
268                         for ( i = 0; i < this.images.length; i++ ) {
269                                 this.images[i].detach();
270                         }
271                 },
272
273                 _drag: function ( _x ) {
274                         var delta,
275                                 coord_x;
276
277                         if ( !this.dragging ) {
278                                 return;
279                         }
280
281                         if ( this.options.flicking === false ) {
282                                 delta = this.org_x - _x;
283
284                                 // first image
285                                 if ( delta < 0 && !this.prev_img.length ) {
286                                         return;
287                                 }
288                                 // last image
289                                 if ( delta > 0 && !this.next_img.length ) {
290                                         return;
291                                 }
292                         }
293
294                         coord_x = _x - this.org_x;
295
296                         this.cur_img.css( 'left', coord_x + 'px' );
297                         if ( this.next_img.length ) {
298                                 this.next_img.css( 'left', coord_x + this.window_width + 'px' );
299                         }
300                         if ( this.prev_img.length ) {
301                                 this.prev_img.css( 'left', coord_x - this.window_width + 'px' );
302                         }
303                 },
304
305                 _move: function ( _x ) {
306                         var delta = this.org_x - _x,
307                                 flip = 0,
308                                 drag_time,
309                                 sec,
310                                 self;
311
312                         if ( delta == 0 ) {
313                                 return;
314                         }
315
316                         if ( delta > 0 ) {
317                                 flip = delta < ( this.max_width * 0.45 ) ? 0 : 1;
318                         } else {
319                                 flip = -delta < ( this.max_width * 0.45 ) ? 0 : 1;
320                         }
321
322                         if ( !flip ) {
323                                 drag_time = Date.now() - this.org_time;
324
325                                 if ( Math.abs( delta ) / drag_time > 1 ) {
326                                         flip = 1;
327                                 }
328                         }
329
330                         if ( flip ) {
331                                 if ( delta > 0 && this.next_img.length ) {
332                                         /* next */
333                                         this._detach( this.index - 1, this.prev_img );
334
335                                         this.prev_img = this.cur_img;
336                                         this.cur_img = this.next_img;
337                                         this.next_img = this.next_img.next();
338
339                                         this.index++;
340
341                                         if ( this.next_img.length ) {
342                                                 this.next_img.css( 'left', this.window_width + 'px' );
343                                                 this._attach( this.index + 1, this.next_img );
344                                         }
345
346                                         this.direction = 1;
347
348                                 } else if ( delta < 0 && this.prev_img.length ) {
349                                         /* prev */
350                                         this._detach( this.index + 1, this.next_img );
351
352                                         this.next_img = this.cur_img;
353                                         this.cur_img = this.prev_img;
354                                         this.prev_img = this.prev_img.prev();
355
356                                         this.index--;
357
358                                         if ( this.prev_img.length ) {
359                                                 this.prev_img.css( 'left', -this.window_width + 'px' );
360                                                 this._attach( this.index - 1, this.prev_img );
361                                         }
362
363                                         this.direction = -1;
364                                 }
365                         }
366
367                         sec = this.options.duration;
368                         self = this;
369
370                         this.moving = true;
371
372                         setTimeout( function () {
373                                 self.moving = false;
374                         }, sec - 50 );
375
376                         this.cur_img.animate( { left: 0 }, sec );
377                         if ( this.next_img.length ) {
378                                 this.next_img.animate( { left: this.window_width }, sec );
379                         }
380                         if ( this.prev_img.length ) {
381                                 this.prev_img.animate( { left: -this.window_width }, sec );
382                         }
383                 },
384
385                 _add_event: function () {
386                         var self = this,
387                                 date;
388
389                         this.container.bind( 'vmousemove', function ( e ) {
390                                 e.preventDefault();
391
392                                 if ( self.moving ) {
393                                         return;
394                                 }
395                                 if ( !self.dragging ) {
396                                         return;
397                                 }
398
399                                 self._drag( e.pageX );
400                         } );
401
402                         this.container.bind( 'vmousedown', function ( e ) {
403                                 e.preventDefault();
404
405                                 if ( self.moving ) {
406                                         return;
407                                 }
408
409                                 self.dragging = true;
410
411                                 self.org_x = e.pageX;
412
413                                 self.org_time = Date.now();
414                         } );
415
416                         this.container.bind( 'vmouseup', function ( e ) {
417                                 if ( self.moving ) {
418                                         return;
419                                 }
420
421                                 self.dragging = false;
422
423                                 self._move( e.pageX );
424                         } );
425
426                         this.container.bind( 'vmouseout', function ( e ) {
427                                 if ( self.moving ) {
428                                         return;
429                                 }
430                                 if ( !self.dragging ) {
431                                         return;
432                                 }
433
434                                 if ( ( e.pageX < 20 ) ||
435                                                 ( e.pageX > ( self.max_width - 20 ) ) ) {
436                                         self._move( e.pageX );
437                                         self.dragging = false;
438                                 }
439                         } );
440                 },
441
442                 _del_event: function () {
443                         this.container.unbind( 'vmousemove' );
444                         this.container.unbind( 'vmousedown' );
445                         this.container.unbind( 'vmouseup' );
446                         this.container.unbind( 'vmouseout' );
447                 },
448
449                 _show: function () {
450                         /* resizing */
451                         this.window_width = $( window ).width();
452                         this.max_width = this._get_width();
453                         this.max_height = this._get_height();
454                         this.container.css( 'height', this.max_height );
455
456                         this.cur_img = $( 'div' ).find( '.ui-gallery-bg:eq(' + this.index + ')' );
457                         this.prev_img = this.cur_img.prev();
458                         this.next_img = this.cur_img.next();
459
460                         this._attach( this.index - 1, this.prev_img );
461                         this._attach( this.index, this.cur_img );
462                         this._attach( this.index + 1, this.next_img );
463
464                         if ( this.prev_img.length ) {
465                                 this.prev_img.css( 'left', -this.window_width + 'px' );
466                         }
467
468                         this.cur_img.css( 'left', '0px' );
469
470                         if ( this.next_img.length ) {
471                                 this.next_img.css( 'left', this.window_width + 'px' );
472                         }
473                 },
474
475                 show: function () {
476                         if ( !this.images.length ) {
477                                 return;
478                         }
479
480                         this._show();
481                         this._add_event();
482                 },
483
484                 _hide: function () {
485                         this._detach( this.index - 1, this.prev_img );
486                         this._detach( this.index, this.cur_img );
487                         this._detach( this.index + 1, this.next_img );
488                 },
489
490                 hide: function () {
491                         this._hide();
492                         this._del_event();
493                 },
494
495                 _get_width: function () {
496                         return $( this.element ).width();
497                 },
498
499                 _get_height: function () {
500                         var $page = $( this.element ).parentsUntil( 'ui-page' ),
501                                 $content = $page.children( '.ui-content' ),
502                                 header_h = $page.children( '.ui-header' ).outerHeight() || 0,
503                                 footer_h = $page.children( '.ui-footer' ).outerHeight() || 0,
504                                 padding = parseFloat( $content.css( 'padding-top' ) )
505                                         + parseFloat( $content.css( 'padding-bottom' ) ),
506                                 content_h = $( window ).height() - header_h - footer_h - padding;
507
508                         return content_h;
509                 },
510
511                 _create: function () {
512                         var temp_img,
513                                 self = this,
514                                 index,
515                                 i = 0;
516
517                         $( this.element ).wrapInner( '<div class="ui-gallery"></div>' );
518                         $( this.element ).find( 'img' ).wrap( '<div class="ui-gallery-bg"></div>' );
519
520                         this.container = $( this.element ).find('.ui-gallery');
521
522                         temp_img = $( 'div' ).find( '.ui-gallery-bg:first' );
523
524                         while ( temp_img.length ) {
525                                 this.images[i] = temp_img.find( 'img' );
526                                 temp_img = temp_img.next();
527                                 i++;
528                         }
529
530                         this._detach_all();
531
532                         index = parseInt( $( this.element ).jqmData( 'index' ), 10 );
533                         if ( !index ) {
534                                 index = 0;
535                         }
536                         if ( index < 0 ) {
537                                 index = 0;
538                         }
539                         if ( index >= this.images.length ) {
540                                 index = this.images.length - 1;
541                         }
542
543                         this.index = index;
544
545                         this.align_type = $( this.element ).jqmData( 'vertical-align' );
546
547                         $( window ).bind( 'resize', function () {
548                                 self.refresh();
549                         });
550                 },
551
552                 _update: function () {
553                         var image_file,
554                                 bg_html,
555                                 temp_img;
556
557                         while ( this.images_hold.length ) {
558                                 image_file = this.images_hold.shift();
559
560                                 bg_html = $( '<div class="ui-gallery-bg"></div>' );
561                                 temp_img = $( '<img src="' + image_file + '"></div>' );
562
563                                 bg_html.append( temp_img );
564                                 this.container.append( bg_html );
565                                 this.images.push( temp_img );
566                         }
567
568                         this._detach_all();
569                 },
570
571                 refresh: function ( start_index ) {
572                         this._update();
573
574                         this._hide();
575
576                         if ( start_index === undefined ) {
577                                 start_index = this.index;
578                         }
579                         if ( start_index < 0 ) {
580                                 start_index = 0;
581                         }
582                         if ( start_index >= this.images.length ) {
583                                 start_index = this.images.length - 1;
584                         }
585
586                         this.index = start_index;
587
588                         this._show();
589
590                         return this.index;
591                 },
592
593                 add: function ( file ) {
594                         this.images_hold.push( file );
595                 },
596
597                 remove: function ( index ) {
598                         var temp_img;
599
600                         if ( index === undefined ) {
601                                 index = this.index;
602                         }
603
604                         if ( index < 0 || index >= this.images.length ) {
605                                 return;
606                         }
607
608                         if ( index == this.index ) {
609                                 temp_img = this.cur_img;
610
611                                 if ( this.index == 0 ) {
612                                         this.direction = 1;
613                                 } else if ( this.index == this.images.length - 1 ) {
614                                         this.direction = -1;
615                                 }
616
617                                 if ( this.direction < 0 ) {
618                                         this.cur_img = this.prev_img;
619                                         this.prev_img = this.prev_img.prev();
620                                         if ( this.prev_img.length ) {
621                                                 this.prev_img.css( 'left', -this.window_width );
622                                                 this._attach( index - 2, this.prev_img );
623                                         }
624                                         this.index--;
625                                 } else {
626                                         this.cur_img = this.next_img;
627                                         this.next_img = this.next_img.next();
628                                         if ( this.next_img.length ) {
629                                                 this.next_img.css( 'left', this.window_width );
630                                                 this._attach( index + 2, this.next_img );
631                                         }
632                                 }
633
634                                 this.cur_img.animate( { left: 0 }, this.options.duration );
635
636                         } else if ( index == this.index - 1 ) {
637                                 temp_img = this.prev_img;
638                                 this.prev_img = this.prev_img.prev();
639                                 if ( this.prev_img.length ) {
640                                         this.prev_img.css( 'left', -this.window_width );
641                                         this._attach( index - 1, this.prev_img );
642                                 }
643                                 this.index--;
644
645                         } else if ( index == this.index + 1 ) {
646                                 temp_img = this.next_img;
647                                 this.next_img = this.next_img.next();
648                                 if ( this.next_img.length ) {
649                                         this.next_img.css( 'left', this.window_width );
650                                         this._attach( index + 1, this.next_img );
651                                 }
652
653                         } else {
654                                 temp_img = $( 'div' ).find( '.ui-gallery-bg:eq(' + index + ')' );
655                         }
656
657                         this.images.splice( index, 1 );
658                         temp_img.detach();
659                 },
660
661                 empty: function () {
662                         this.images.splice( 0, this.images.length );
663                         this.container.find('.ui-gallery-bg').detach();
664                 },
665
666                 length: function () {
667                         return this.images.length;
668                 },
669
670                 value: function ( index ) {
671                         if ( index === undefined ) {
672                                 return this.index;
673                         }
674
675                         this.refresh( index );
676                 }
677         }); /* End of widget */
678
679         // auto self-init widgets
680         $( document ).bind( "pagecreate", function ( e ) {
681                 $( e.target ).find( ":jqmData(role='gallery')" ).gallery();
682         });
683
684         $( document ).bind( "pageshow", function ( e ) {
685                 $( e.target ).find( ":jqmData(role='gallery')" ).gallery( 'show' );
686         });
687
688         $( document ).bind( "pagebeforehide", function ( e ) {
689                 $( e.target ).find( ":jqmData(role='gallery')" ).gallery( 'hide' );
690         } );
691
692 }( jQuery, this ) );