UnitTC: Additional unit testcases have been added
[platform/framework/web/web-ui-fw.git] / src / js / widgets / jquery.mobile.tizen.virtuallistview.js
1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2 //>>description: Shows listview swapping its contents automatically
3 //>>label: Virtual listview
4 //>>group: Tizen:Widgets
5
6 define( [ '../jquery.mobile.tizen.core', '../jquery.mobile.tizen.scrollview' ], function ( ) {
7 //>>excludeEnd("jqmBuildExclude");
8
9 /* ***************************************************************************
10  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included in
20  * all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  * ***************************************************************************
30  *
31  *      Author: Wongi Lee <wongi11.lee@samsung.com>
32  *              Youmin Ha <youmin.ha@samsung.com>
33  */
34
35 /**
36  * Virtual List Widget for unlimited data.
37  * To support more then 1,000 items, special list widget developed. 
38  * Fast initialize and light DOM tree.
39  * DB connection and works like DB cursor.
40  * 
41  * HTML Attributes:
42  *
43  *              data-role:      virtuallistview
44  *              data-template : jQuery.template ID that populate into virtual list 
45  *              data-row : Optional. Set number of <li> elements that are used for data handling. 
46  *              
47  *              ID : <UL> element that has "data-role=virtuallist" must have ID attribute.
48  *
49  * * APIs:
50  *
51  *              create ( {
52  *                              itemData: function ( idx ) { return json_obj; },
53  *                              numItemData: number or function () { return number; },
54  *                              cacheItemData: function ( minIdx, maxIdx ) {}
55  *                              } )
56  *                      : Create a virtuallist widget. At this moment, _create method is called.
57  *                      args : A collection of options
58  *                              itemData: A function that returns JSON object for given index. Mandatory.
59  *                              numItemData: Total number of itemData. Mandatory.
60  *                              cacheItemData: Virtuallist will ask itemData between minIdx and maxIdx.
61  *                                             Developers can implement this function for preparing data.
62  *                                             Optional.
63  *
64  * Events:
65  *
66  *              touchstart : Temporary preventDefault applied on touchstart event to avoid broken screen.
67  *
68  * Examples:
69  *
70  *              <script id="tmp-3-2-7" type="text/x-jquery-tmpl">
71  *                      <li class="ui-li-3-2-7">
72  *                              <span class="ui-li-text-main">${NAME}</span>
73  *                              <img src="00_winset_icon_favorite_on.png" class="ui-li-icon-sub">
74  *                              <span class="ui-li-text-sub">${ACTIVE}</span>
75  *                              <span class="ui-li-text-sub2">${FROM}</span>
76  *                      </li>
77  *              </script>
78  *
79  *              <ul id="virtuallist-normal_3_2_7_ul" data-role="virtuallistview" data-template="tmp-3-2-7" data-dbtable="JSON_DATA" data-row="100">
80  *              </ul>
81  *
82  */
83
84 /**
85         @class VirtualList
86         In the Web environment, it is challenging to display a large amount of data in a list, such as displaying a contact list of over 1000 list items. It takes time to display the entire list in HTML and the DOM manipulation is complex.
87
88         The virtual list widget is used to display a list of unlimited data elements on the screen for better performance. This widget provides easy access to databases to retrieve and display data. Virtual lists are based on the jQuery.template plugin as described in the jQuery documentation for jQuery.template plugin.
89
90         To add a virtual list widget to the application, use the following code:
91
92                 <script id="tmp-3-2-7" type="text/x-jquery-tmpl">
93                         <li class="ui-li-3-2-7">
94                                 <span class="ui-li-text-main">${NAME}</span>
95                                 <img src="00_winset_icon_favorite_on.png" class="ui-li-icon-sub"/>
96                                 <span class="ui-li-text-sub">${ACTIVE}</span>
97                                 <span class="ui-li-text-sub2">${FROM}</span>
98                         </li>
99                 </script>
100                 <ul id="vlist" data-role="virtuallistview" data-template="tmp-3-2-7" data-dbtable="JSON_DATA" data-row="100"></ul>
101 */
102 /**
103         @property {String} data-role
104         Creates the virtual list view. The value must be set to virtuallistview.
105         Only the &gt;ul&lt; element, which a id attribute defined, supports this option. Also, the vlLoadSuccess class attribute must be defined in the &gt;ul&lt; element to ensure that loading data from the database is complete.
106 */
107 /**
108         @property {String} data-template
109         Defines the jQuery.template element ID.
110         The jQuery.template must be defined. The template style can use rem units to support scalability.
111 */
112 /**
113         @property {Number} data-row
114         Defines the number of virtual list child elements.
115         The minimum value is 20 and the default value is 100. As the value gets higher, the loading time increases while the system performance improves. So you need to pick a value that provides the best performance without excessive loading time.
116 */
117 /**
118         @method create
119         @param {function} itemData(index)
120         : function itemData(index) returns the JSON object matched with the given index. The index value is between 0 and numItemData-1.
121         @param {Number} numItemData
122         : number numItemData or function numItemData() defines or returns a static number of items.
123         @param {function} cacheItemData(minIndex, maxIndex)
124         : function cacheItemData(minIndex, maxIndex) prepares the JSON data. This method is called before calling the itemData() method with index values between minIndex and maxIndex.
125 */
126
127 (function ( $, undefined ) {
128
129         /* Code for Virtual List Demo */
130         var listCountPerPage = {},      /* Keeps track of the number of lists per page UID. This allows support for multiple nested list in the same page. https://github.com/jquery/jquery-mobile/issues/1617 */
131                 _NO_SCROLL = 0,                                 /* ENUM */
132                 _SCROLL_DOWN = 1,                               /* ENUM */
133                 _SCROLL_UP = -1;                                        /* ENUM */
134
135         $.widget( "tizen.virtuallistview", $.mobile.widget, {
136                 options: {
137                         theme: "s",
138                         countTheme: "s",
139                         headerTheme: "s",
140                         dividerTheme: "s",
141                         splitIcon: "arrow-r",
142                         splitTheme: "s",
143                         inset: false,
144                         id:     "",                                     /* Virtual list UL elemet's ID */
145                         childSelector: " li",   /* To support swipe list */
146                         dbtable: "",
147                         template : "",
148                         dbkey: false,                   /* Data's unique Key */
149                         scrollview: false,
150                         row: 100,
151                         page_buf: 30,
152                         initSelector: ":jqmData(role='virtuallistview')"
153                 },
154
155                 _stylerMouseUp: function () {
156                         $( this ).addClass( "ui-btn-up-s" );
157                         $( this ).removeClass( "ui-btn-down-s" );
158                 },
159
160                 _stylerMouseDown: function () {
161                         $( this ).addClass( "ui-btn-down-s" );
162                         $( this ).removeClass( "ui-btn-up-s" );
163                 },
164
165                 _stylerMouseOver: function () {
166                         $( this ).toggleClass( "ui-btn-hover-s" );
167                 },
168
169                 _stylerMouseOut: function () {
170                         $( this ).toggleClass( "ui-btn-hover-s" );
171                         $( this ).addClass( "ui-btn-up-s" );
172                         $( this ).removeClass( "ui-btn-down-s" );
173                 },
174
175                 // ?
176                 // this         virtuallistview object
177                 // @param[in]   template        template name(string)
178                 _pushData: function ( template ) {
179                         var o = this.options,
180                                 i,
181                                 myTemplate = $( "#" + template ),       // Get template object
182                                 // NOTE: o.row = # of rows handled at once. Default value is 100.
183                                 lastIndex = ( o.row > this._numItemData ? this._numItemData : o.row ),  // last index of handled data
184                                 htmlData;
185
186                         for ( i = 0; i < lastIndex; i++ ) {
187                                 htmlData = myTemplate.tmpl( this._itemData( i ) );      // Make rows with template,
188                                 $( o.id ).append( $( htmlData ).attr( 'id', o.itemIDPrefix + i ) );     // and append it to the vlist object
189                         }
190
191                         // After pushing data re-style virtuallist widget
192                         $( o.id ).trigger( "create" );
193                 },
194
195                 // Set children <li> elements' position
196                 //
197                 // this: virtuallist element
198                 // event: virtuallistview.options
199                 //              TODO: Why this arg name is 'event'? Not resonable.
200                 //              (this function is not called with event element as args!)
201                 _reposition: function ( event ) {
202                         var o,
203                                 t = this,
204                                 padding,
205                                 margin;
206
207                         if ( event.data ) {
208                                 o = event.data;
209                         } else {
210                                 o = event;
211                         }
212                         if ( $( o.id + o.childSelector ).size() > 0 ) { // $("#vlistid li")
213                                 // first child's top position
214                                 // NOTE: the first element may not be '0'!!!
215                                 t._title_h = $( o.id + o.childSelector + ':first' ).position().top;
216                                 // first child's outer height (TODO: reuse selected items)
217                                 t._line_h = $( o.id + o.childSelector + ':first' ).outerHeight();
218
219                                 // container(vlist element)'s innerwidth
220                                 t._container_w = $( o.id ).innerWidth();
221
222                                 // get sum of container's left/right padding
223                                 padding = parseInt( $( o.id + o.childSelector ).css( "padding-left" ), 10 )
224                                         + parseInt( $( o.id + o.childSelector ).css( "padding-right" ), 10 );
225
226                                 // Add CSS to all <li> elements
227                                 //      * absolute position
228                                 //      * btn-up
229                                 //      * mouse up/down/over/out styles
230                                 $( o.id + ">" + o.childSelector )
231                                         .addClass( "position_absolute" )
232                                         .addClass( "ui-btn-up-s" )
233                                         .bind( "mouseup", t._stylerMouseUp )
234                                         .bind( "mousedown", t._stylerMouseDown )
235                                         .bind( "mouseover", t._stylerMouseOver )
236                                         .bind( "mouseout", t._stylerMouseOut );
237                         }
238
239                         // Set absolute top/left position of each <li>
240                         $( o.id + ">" + o.childSelector ).each( function ( index ) {
241                                 margin = parseInt( $( this ).css( "margin-left" ), 10 )
242                                         + parseInt( $( this ).css( "margin-right" ), 10 );
243
244                                 $( this ).css( "top", t._title_h + t._line_h * index + 'px' )
245                                         .css( "width", t._container_w - padding - margin );
246                         } );
247
248                         // Set Max Listview Height
249                         $( o.id ).height( t._numItemData * t._line_h );
250                 },
251
252                 _resize: function ( event ) {
253                         var o,
254                                 t = this,
255                                 padding,
256                                 margin;
257
258                         if ( event.data ) {
259                                 o = event.data;
260                         } else {
261                                 o = event;
262                         }
263
264                         t._container_w = $( o.id ).innerWidth();
265
266                         padding = parseInt( $( o.id + o.childSelector ).css( "padding-left" ), 10 )
267                                 + parseInt( $( o.id + o.childSelector ).css( "padding-right" ), 10 );
268
269                         $( o.id + o.childSelector ).each( function (index) {
270                                 margin = parseInt( $( this ).css( "margin-left" ), 10 )
271                                         + parseInt( $( this ).css( "margin-right" ), 10 );
272                                 $( this ).css( "width", t._container_w - padding - margin );
273                         } );
274                 },
275
276                 // New scrollmove function supporting scrollTo
277                 _scrollmove: function ( ev ) {
278                         var t = ev.data,        // vlist (JQM object)
279                                 o = t.options,  // options
280                                 prevTopBufLen = t._num_top_items,       // Previous(remembered) top buf length
281                                 timerInterval = 100,
282                                 i,
283                                 _scrollView,
284                                 _normalScroll;
285
286                         _scrollView = {
287                                 viewTop: function ( ) {
288                                         var sv = $( o.id ).parentsUntil( ".ui-page" ).find( ".ui-scrollview-view" ),
289                                                 svTrans = sv.css( "-webkit-transform" ),
290                                                 svTransVal = "0,0,0,0,0,0";
291                                         if ( svTrans ) {
292                                                 svTransVal = svTrans.replace( /matrix\s*\((.*)\)/, "$1" );      // matrix(a,c,b,d,tx,ty)
293                                         }
294                                         return - parseInt( svTransVal.split(',')[5], 10 );
295                                 }
296                         };
297                         _normalScroll = {
298                                 viewTop: function ( ) {
299                                         return $( window ).scrollTop( );        // TODO: - _line_h?
300                                 }
301                         };
302                         // Get current view top position
303                         function viewTop ( ) {
304                                 return o.scrollview ? _scrollView.viewTop() : _normalScroll.viewTop();
305                         }
306                         // log function for debug
307                         function log ( msg ) {
308                                 var debug = false;
309                                 if ( debug ) {
310                                         console.log( ">>virtualllist: " + msg );
311                                 }
312                         }
313
314                         // Timer interval function
315                         // @param[in]   vl      virtuallist object (JQM object)
316                         function timerMove ( vl, undefined ) {
317                                 var cy,                         // current y position
318                                         cti,            // current top idx
319                                         cbi,            // current bottom idx
320                                         oti = vl._first_index,  // old top idx
321                                         obi = vl._last_index,   // old botton idx
322                                         dti,                    // delta of top idx
323                                         fromIdx,
324                                         toIdx,  // index range to be moved
325                                         delta,                  // moveItem delta
326                                         rowLen = vl.options.row,        // max. # of items handled at once
327                                         bufSize,                // top/bottom buffer size. unit: # of items
328                                         i;
329
330                                 // subroutine: Move itemContents in i2 into i1
331                                 function moveItemContents( vl, i1, i2 ) {
332                                         // TODO: Find a efficient way to replace data!
333                                         // Assumption: i1 and i2 has same children.
334                                         var NODETYPE = { ELEMENT_NODE: 1, TEXT_NODE: 3 },
335                                                 c1,     // child item 1 (old)
336                                                 c2,     // child item 2 (new)
337                                                 newText,
338                                                 newImg,
339                                                 i;
340
341                                         $( i1 ).find( ".ui-li-text-main", ".ui-li-text-sub", ".ui-li-text-sub2", "ui-btn-text" ).each( function ( index ) {
342                                                 c1 = $( this );
343                                                 newText = $( i2 ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).eq( index ).text();
344
345                                                 $( c1 ).contents().filter( function () {
346                                                         return ( this.nodeType == NODETYPE.TEXT_NODE );
347                                                 } ).get( 0 ).data = newText;
348                                         } );
349
350                                         $( i1 ).find( "img" ).each( function ( imgIdx ) {
351                                                 var c1 = $( this );
352                                                 newImg = $( i2 ).find( "img" ).eq( imgIdx ).attr( "src" );
353
354                                                 $( c1 ).attr( "src", newImg );
355                                         } );
356
357                                         $( i1 ).removeData( );  // Clear old data
358                                 }
359
360                                 // subroutine: Move item
361                                 function moveItem( vl, fromIdx, toIdx ) {
362                                         var itemData,   // data from itemData()
363                                                 item,           // item element
364                                                 newItem,        // new item element
365                                                 tmpl;           // template
366
367                                         log( ">> move item: " + fromIdx + " --> " + toIdx );
368
369                                         // Find current item
370                                         item = $( '#' + vl.options.itemIDPrefix + fromIdx );    // TODO: refactor ID generation!
371                                         if ( ! item || ! item.length ) {
372                                                 return false;
373                                         }
374
375                                         // Get new item
376                                         tmpl = $( "#" + vl.options.template );
377                                         if ( tmpl ) {
378                                                 newItem = tmpl.tmpl( vl._itemData( toIdx ) );
379
380                                                 // TODO: Consider touch block while moving?
381
382                                                 // Move item contents
383                                                 moveItemContents( vl, item, newItem );
384
385                                                 // clean up temporary item
386                                                 newItem.remove();
387                                         }
388
389                                         // Move position, and set id
390                                         item.css( 'top', toIdx * vl._line_h )
391                                                 .attr( 'id' , vl.options.itemIDPrefix + toIdx );        // TODO: refactor ID generation!
392
393                                         // TODO: Apply jqmdata? check following old code;
394                                         // $( oldItem ).removeData( );  // Clear old data
395                                         // if (key) { $( oldItem ).data( key, $( newItem ).data( key ) ); }
396
397                                         return true;
398                                 }
399
400
401                                 // Get current view position
402                                 cy = viewTop();
403
404                                 // Calculate bufSize: rowLen / 3
405                                 // NOTE: Assumption: total row length = visible items * 3 (upper+visible+lower)
406                                 bufSize = Math.ceil( rowLen / 3 );
407
408                                 // Calculate current top/bottom index (to be applied)
409                                 // top index = current position / line height
410                                 cti = Math.floor( cy / vl._line_h ) - bufSize;  // TODO: consider buffer!
411                                 cbi = cti + rowLen - 1;
412
413                                 if ( cti < 0 ) {                // Top boundary check
414                                         cbi += ( - cti );
415                                         cti = 0;
416                                 } else if ( cbi > ( vl._numItemData - 1 ) ) {           // Bottom boundary check
417                                         cti -= ( cbi - ( vl._numItemData - 1 ) );
418                                         cbi = ( vl._numItemData - 1 );
419                                 }
420
421                                 // Calculate dti
422                                 dti = cti - oti;
423                                 log( "cy=" + cy + ", oti=" + oti + ", obi=" + obi + ", cti=" + cti + ", cbi=" + cbi + ", dti=" + dti );
424
425                                 // switch: dti = 0 --> timer stop condition: delta=0 or scrollstop event comes. END.
426                                 if ( 0 == dti ) {
427                                         // Check timer runtime
428                                         vl.timerStillCount += 1;
429                                         if ( vl.timerStillCount < 12 ) {        // check count ( TODO: test and adjust )
430                                                 log("dti=0 " + vl.timerStillCount + " times");
431                                                 vl.timerMoveID = setTimeout( timerMove, timerInterval, vl );    // run once more
432                                                 return;
433                                         }
434
435                                         log("dti=0 " + vl.timerStillCount + " times. End timer.");
436                                         vl.timerStillCount = 0;
437                                         // Stop timer
438                                         if ( vl.timerMoveID ) {
439                                                 clearTimeout( vl.timerMoveID );
440                                                 vl.timerMoveID = null;
441                                         }
442                                 } else {
443                                         // switch: dti >= # of max elements --> total replace.
444                                         vl.timerStillCount = 0;         // Reset still counter
445
446                                         if ( Math.abs( dti ) >= rowLen ) {
447                                                 fromIdx = oti;
448                                                 toIdx = obi;
449                                                 delta = dti;
450                                                 log( ">>> WHOLE CHANGE! delta=" + delta );
451                                         } else {
452                                                 // switch: dti < # of max elements --> move t2b or b2t until new top/bottom idx is covered
453                                                 if ( dti > 0 ) {
454                                                         fromIdx = oti;
455                                                         toIdx = oti + dti - 1;
456                                                         delta = rowLen;
457                                                 } else {
458                                                         fromIdx = obi + dti + 1;        // dti < 0
459                                                         toIdx = obi;
460                                                         delta = -rowLen;
461                                                 }
462                                                 log( ">>> partial change. delta=" + delta );
463                                         }
464
465                                         // Move items
466                                         for ( i = fromIdx; i <= toIdx; i++ ) {
467                                                 moveItem( vl, i, i + delta );           // Change data and position
468                                         }
469
470                                         // Store current top/bottom idx into vl
471                                         vl._first_index = cti;
472                                         vl._last_index = cbi;
473
474                                         // Register timer to check again
475                                         vl.timerMoveID = setTimeout( timerMove, timerInterval, vl );
476                                 }
477                                 return; // End of function
478                         }
479
480                         // ==== function start ====
481
482                         t.timerStillCount = 0;  // Count do-nothing time.       For behavior tuning.
483
484                         // If a timer function is alive, clear it
485                         if ( t.timerMoveID ) {
486                                 clearTimeout( t.timerMoveID );
487                                 t.timerMoveID = null;
488                         }
489                         // run TimerMove()
490                         timerMove( t );
491                 },
492
493                 _recreate: function ( newArray ) {
494                         var t = this,
495                                 o = this.options;
496
497                         $( o.id ).empty();
498
499                         t._numItemData = newArray.length;
500                         t._direction = _NO_SCROLL;
501                         t._first_index = 0;
502                         t._last_index = o.row - 1;
503
504                         t._pushData( o.template );
505
506                         if (o.childSelector == " ul" ) {
507                                 $( o.id + " ul" ).swipelist();
508                         }
509
510                         $( o.id ).virtuallistview();
511
512                         t.refresh( true );
513
514                         t._reposition( o );
515                 },
516
517                 // Init virtuallistview
518                 // this         virtuallistview object
519                 _initList: function () {
520                         var t = this,
521                                 o = this.options;
522
523                         /* After AJAX loading success */
524
525                         // Put initial <li> elements
526                         t._pushData( o.template );
527
528                         // find a parent page, and run _reposition() at 'pageshow' event
529                         // TODO: Consider replace parentsUntil().parent() to parent('.ui-page') ???
530                         $( o.id ).parentsUntil( ".ui-page" ).parent().one( "pageshow", function () {
531                                 setTimeout( function () {
532                                         t._reposition( o );
533                                 }, 0);
534                         });
535
536                         // Bind _scrollmove() at 'scrollstart.virtuallist' event
537                         $( document ).bind( "scrollstart.virtuallist scrollstop.vrituallist", t, t._scrollmove );
538
539                         // Bind _resize() at 'resize.virtuallist'
540                         $( window ).bind( "resize.virtuallist", t._resize );
541
542                         // when ul is a childselector, assume that this is also a swipelist,
543                         // and run swipelist constructor
544                         if ( o.childSelector == " ul" ) {
545                                 $( o.id + " ul" ).swipelist();
546                         }
547
548                         t.refresh( true );
549                 },
550
551                 create: function () {
552                         var o = this.options;
553
554                         /* external API for AJAX callback */
555                         this._create.apply( this, arguments );
556
557                         // TODO: remove this line? _initList() calls reposition...
558                         this._reposition( o );
559                 },
560
561                 _create: function ( args ) {
562                         // Extend instance variables
563                         $.extend( this, {
564                                 _itemData : function ( idx ) { return null; },
565                                 _numItemData : 0,
566                                 _cacheItemData : function ( minIdx, maxIdx ) { },
567                                 _title_h : 0,
568                                 _container_w : 0,
569                                 _minimum_row : 100,
570                                 _direction : _NO_SCROLL,
571                                 _first_index : 0,
572                                 _last_index : 0,
573                                 _num_top_items : 0      // By scroll move, number of hidden elements.
574                         } );
575
576                         // local variables
577                         var t = this,
578                                 o = this.options,
579                                 $el = this.element,
580                                 shortcutsContainer = $('<div class="ui-virtuallist"/>'),
581                                 shortcutsList = $('<ul></ul>'),
582                                 dividers = $el.find(':jqmData(role="virtuallistview" )'),
583                                 lastListItem = null,
584                                 shortcutscroll = this,
585                                 dbtable_name,
586                                 dbtable;
587
588
589                         // Add CSS classes to $el (=virtuallistview)
590                         $el.addClass( function ( i, orig ) {
591                                 return orig + " ui-listview ui-virtual-list-container" + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" );
592                         });
593
594                         // keep the vlist's ID
595                         o.itemIDPrefix = $el.attr( "id" ) + '_';
596                         o.id = "#" + $el.attr( "id" );
597
598                         // when page hides, empty all child elements
599                         $( o.id ).bind( "pagehide", function ( e ) {
600                                 $( o.id ).empty();
601                         });
602
603                         // Find if scrollview is used
604                         if ( $( ".ui-scrollview-clip" ).size() > 0 ) {
605                                 o.scrollview = true;
606                         } else {
607                                 o.scrollview = false;
608                         }
609
610                         // Calculate page buffer size
611                         if ( $el.data( "row" ) ) {
612                                 o.row = $el.data( "row" );
613
614                                 if ( o.row < t._minimum_row ) {
615                                         o.row = t._minimum_row;
616                                 }
617
618                                 o.page_buf = parseInt( ( o.row / 2 ), 10 );
619                         }
620
621                         // Get arguments
622                         if ( args ) {
623                                 if ( args.itemData && typeof args.itemData == 'function'  ) {
624                                         t._itemData = args.itemData;
625                                 } else {
626                                         return;
627                                 }
628                                 if ( args.numItemData ) {
629                                         if ( typeof args.numItemData == 'function' ) {
630                                                 t._numItemData = args.numItemData( );
631                                         } else if ( typeof args.numItemData == 'number' ) {
632                                                 t._numItemData = args.numItemData;
633                                         } else {
634                                                 return;
635                                         }
636                                 } else {
637                                         return;
638                                 }
639                         } else {        // No option is given
640                                 // Legacy support: dbtable
641                                 console.warn( "WARNING: The data interface of virtuallist is changed. \nOld data interface(data-dbtable) is still supported, but will be removed in next version. \nPlease fix your code soon!" );
642
643                                 /* After DB Load complete, Init Vritual list */
644                                 if ( $( o.id ).hasClass( "vlLoadSuccess" ) ) {
645                                         dbtable_name = $el.jqmData('dbtable');
646                                         dbtable = window[ dbtable_name ];
647
648                                         $( o.id ).empty();
649
650                                         if ( !dbtable ) {
651                                                 dbtable = { };
652                                         }
653
654                                         t._itemData = function ( idx ) {
655                                                 return dbtable[ idx ];
656                                         };
657                                         t._numItemData = dbtable.length;
658                                 } else {
659                                         return; // Do nothing
660                                 }
661                         }
662
663                         // Get template data
664                         if ( $el.data( "template" ) ) {
665                                 o.template = $el.data( "template" );
666
667                                 /* to support swipe list, <li> or <ul> can be main node of virtual list. */
668                                 if ( $el.data( "swipelist" ) == true ) {
669                                         o.childSelector = " ul";
670                                 } else {
671                                         o.childSelector = " li";
672                                 }
673                         }
674
675                         // Set data's unique key
676                         // NOTE: Unnecessary?
677                         if ( $el.data( "dbkey" ) ) {
678                                 o.dbkey = $el.data( "dbkey" );
679                         }
680
681                         t._first_index = 0;                     // initial top idx of <li> element.
682                         t._last_index = o.row - 1;              // initial bottom idx of <li> element.
683                         t._initList();  // NOTE: Called at here only!
684                 },
685
686                 destroy : function () {
687                         var o = this.options;
688
689                         $( document ).unbind( "scrollstop" );
690
691                         $( window ).unbind( "resize.virtuallist" );
692
693                         $( o.id ).empty();
694
695                         if ( this.timerMoveID ) {
696                                 clearTimeout( this.timerMoveID );
697                                 this.timerMoveID = null;
698                         }
699                 },
700
701                 _itemApply: function ( $list, item ) {
702                         var $countli = item.find( ".ui-li-count" );
703
704                         if ( $countli.length ) {
705                                 item.addClass( "ui-li-has-count" );
706                         }
707
708                         $countli.addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" );
709
710                         // TODO class has to be defined in markup
711                         item.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
712                                 .find( "p, dl" ).addClass( "ui-li-desc" ).end()
713                                 .find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each( function () {
714                                         item.addClass( $( this ).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
715                                 }).end()
716                                 .find( ".ui-li-aside" ).each(function () {
717                                         var $this = $( this );
718                                         $this.prependTo( $this.parent() ); //shift aside to front for css float
719                                 } );
720                 },
721
722                 _removeCorners: function ( li, which ) {
723                         var top = "ui-corner-top ui-corner-tr ui-corner-tl",
724                                 bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
725
726                         li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
727
728                         if ( which === "top" ) {
729                                 li.removeClass( top );
730                         } else if ( which === "bottom" ) {
731                                 li.removeClass( bot );
732                         } else {
733                                 li.removeClass( top + " " + bot );
734                         }
735                 },
736
737                 _refreshCorners: function ( create ) {
738                         var $li,
739                                 $visibleli,
740                                 $topli,
741                                 $bottomli;
742
743                         if ( this.options.inset ) {
744                                 $li = this.element.children( "li" );
745                                 // at create time the li are not visible yet so we need to rely on .ui-screen-hidden
746                                 $visibleli = create ? $li.not( ".ui-screen-hidden" ) : $li.filter( ":visible" );
747
748                                 this._removeCorners( $li );
749
750                                 // Select the first visible li element
751                                 $topli = $visibleli.first()
752                                         .addClass( "ui-corner-top" );
753
754                                 $topli.add( $topli.find( ".ui-btn-inner" ) )
755                                         .find( ".ui-li-link-alt" )
756                                                 .addClass( "ui-corner-tr" )
757                                         .end()
758                                         .find( ".ui-li-thumb" )
759                                                 .not( ".ui-li-icon" )
760                                                 .addClass( "ui-corner-tl" );
761
762                                 // Select the last visible li element
763                                 $bottomli = $visibleli.last()
764                                         .addClass( "ui-corner-bottom" );
765
766                                 $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
767                                         .find( ".ui-li-link-alt" )
768                                                 .addClass( "ui-corner-br" )
769                                         .end()
770                                         .find( ".ui-li-thumb" )
771                                                 .not( ".ui-li-icon" )
772                                                 .addClass( "ui-corner-bl" );
773                         }
774                 },
775
776                 // this         virtuallistview object
777                 refresh: function ( create ) {
778                         this.parentPage = this.element.closest( ".ui-page" );
779                         // Make sub page, and move the virtuallist into it...
780                         // NOTE: check this subroutine.
781                         this._createSubPages();
782
783                         var o = this.options,
784                                 $list = this.element,
785                                 self = this,
786                                 dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme,
787                                 listsplittheme = $list.jqmData( "splittheme" ),
788                                 listspliticon = $list.jqmData( "spliticon" ),
789                                 li = $list.children( "li" ),
790                                 counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1,
791                                 item,
792                                 itemClass,
793                                 temTheme,
794                                 a,
795                                 last,
796                                 splittheme,
797                                 countParent,
798                                 icon,
799                                 pos,
800                                 numli,
801                                 itemTheme;
802
803                         // TODO: ?
804                         if ( counter ) {
805                                 $list.find( ".ui-li-dec" ).remove();
806                         }
807
808                         for ( pos = 0, numli = li.length; pos < numli; pos++ ) {
809                                 item = li.eq( pos );
810                                 itemClass = "ui-li";
811
812                                 // If we're creating the element, we update it regardless
813                                 if ( create || !item.hasClass( "ui-li" ) ) {
814                                         itemTheme = item.jqmData( "theme" ) || o.theme;
815                                         a = item.children( "a" );
816
817                                         if ( a.length ) {
818                                                 icon = item.jqmData( "icon" );
819
820                                                 item.buttonMarkup({
821                                                         wrapperEls: "div",
822                                                         shadow: false,
823                                                         corners: false,
824                                                         iconpos: "right",
825                                                         /* icon: a.length > 1 || icon === false ? false : icon || "arrow-r",*/
826                                                         icon: false,    /* Remove unnecessary arrow icon */
827                                                         theme: itemTheme
828                                                 });
829
830                                                 if ( ( icon != false ) && ( a.length == 1 ) ) {
831                                                         item.addClass( "ui-li-has-arrow" );
832                                                 }
833
834                                                 a.first().addClass( "ui-link-inherit" );
835
836                                                 if ( a.length > 1 ) {
837                                                         itemClass += " ui-li-has-alt";
838
839                                                         last = a.last();
840                                                         splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme;
841
842                                                         last.appendTo(item)
843                                                                 .attr( "title", last.getEncodedText() )
844                                                                 .addClass( "ui-li-link-alt" )
845                                                                 .empty()
846                                                                 .buttonMarkup({
847                                                                         shadow: false,
848                                                                         corners: false,
849                                                                         theme: itemTheme,
850                                                                         icon: false,
851                                                                         iconpos: false
852                                                                 })
853                                                                 .find( ".ui-btn-inner" )
854                                                                 .append(
855                                                                         $( "<span />" ).buttonMarkup({
856                                                                                 shadow: true,
857                                                                                 corners: true,
858                                                                                 theme: splittheme,
859                                                                                 iconpos: "notext",
860                                                                                 icon: listspliticon || last.jqmData( "icon" ) || o.splitIcon
861                                                                         })
862                                                                 );
863                                                 }
864                                         } else if ( item.jqmData( "role" ) === "list-divider" ) {
865
866                                                 itemClass += " ui-li-divider ui-btn ui-bar-" + dividertheme;
867                                                 item.attr( "role", "heading" );
868
869                                                 //reset counter when a divider heading is encountered
870                                                 if ( counter ) {
871                                                         counter = 1;
872                                                 }
873
874                                         } else {
875                                                 itemClass += " ui-li-static ui-body-" + itemTheme;
876                                         }
877                                 }
878
879                                 if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
880                                         countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
881
882                                         countParent.addClass( "ui-li-jsnumbering" )
883                                                 .prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" );
884                                 }
885
886                                 item.add( item.children( ".ui-btn-inner" ) ).addClass( itemClass );
887
888                                 self._itemApply( $list, item );
889                         }
890
891                         this._refreshCorners( create );
892                 },
893
894                 //create a string for ID/subpage url creation
895                 _idStringEscape: function ( str ) {
896                         return str.replace(/\W/g , "-");
897                 },
898
899                 // ?
900                 // this         virtuallistview object
901                 _createSubPages: function () {
902                         var parentList = this.element,
903                                 parentPage = parentList.closest( ".ui-page" ),
904                                 parentUrl = parentPage.jqmData( "url" ),
905                                 parentId = parentUrl || parentPage[ 0 ][ $.expando ],
906                                 parentListId = parentList.attr( "id" ),
907                                 o = this.options,
908                                 dns = "data-" + $.mobile.ns,
909                                 self = this,
910                                 persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
911                                 hasSubPages,
912                                 newRemove;
913
914                         if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
915                                 listCountPerPage[ parentId ] = -1;
916                         }
917
918                         parentListId = parentListId || ++listCountPerPage[ parentId ];
919
920                         $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function ( i ) {
921                                 var self = this,
922                                         list = $( this ),
923                                         listId = list.attr( "id" ) || parentListId + "-" + i,
924                                         parent = list.parent(),
925                                         nodeEls,
926                                         title = nodeEls.first().getEncodedText(),//url limits to first 30 chars of text
927                                         id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
928                                         theme = list.jqmData( "theme" ) || o.theme,
929                                         countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme,
930                                         newPage,
931                                         anchor;
932
933                                 nodeEls = $( list.prevAll().toArray().reverse() );
934                                 nodeEls = nodeEls.length ? nodeEls : $( "<span>" + $.trim( parent.contents()[ 0 ].nodeValue ) + "</span>" );
935
936                                 //define hasSubPages for use in later removal
937                                 hasSubPages = true;
938
939                                 newPage = list.detach()
940                                                         .wrap( "<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
941                                                         .parent()
942                                                                 .before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
943                                                                 .after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='" + persistentFooterID + "'>" ) : "" )
944                                                                 .parent()
945                                                                 .appendTo( $.mobile.pageContainer );
946
947                                 newPage.page();
948
949                                 anchor = parent.find('a:first');
950
951                                 if ( !anchor.length ) {
952                                         anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
953                                 }
954
955                                 anchor.attr( "href", "#" + id );
956
957                         }).virtuallistview();
958
959                         // on pagehide, remove any nested pages along with the parent page, as long as they aren't active
960                         // and aren't embedded
961                         if ( hasSubPages &&
962                                                 parentPage.is( ":jqmData(external-page='true')" ) &&
963                                                 parentPage.data( "page" ).options.domCache === false ) {
964
965                                 newRemove = function ( e, ui ) {
966                                         var nextPage = ui.nextPage, npURL;
967
968                                         if ( ui.nextPage ) {
969                                                 npURL = nextPage.jqmData( "url" );
970                                                 if ( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ) {
971                                                         self.childPages().remove();
972                                                         parentPage.remove();
973                                                 }
974                                         }
975                                 };
976
977                                 // unbind the original page remove and replace with our specialized version
978                                 parentPage
979                                         .unbind( "pagehide.remove" )
980                                         .bind( "pagehide.remove", newRemove );
981                         }
982                 },
983
984                 // TODO sort out a better way to track sub pages of the virtuallistview this is brittle
985                 childPages: function () {
986                         var parentUrl = this.parentPage.jqmData( "url" );
987
988                         return $( ":jqmData(url^='" +  parentUrl + "&" + $.mobile.subPageUrlKey + "')" );
989                 }
990         });
991
992         //auto self-init widgets
993         $( document ).bind( "pagecreate create", function ( e ) {
994                 $( $.tizen.virtuallistview.prototype.options.initSelector, e.target ).virtuallistview();
995         });
996
997 } ( jQuery ) );
998
999 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
1000 } );
1001 //>>excludeEnd("jqmBuildExclude");