Revert "Export"
[framework/web/web-ui-fw.git] / src / widgets / virtuallist / js / jquery.mobile.tizen.virtuallistview.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: Wongi Lee <wongi11.lee@samsung.com>
24  */
25
26 /**
27  * Virtual List Widget for unlimited data.
28  * To support more then 1,000 items, special list widget developed. 
29  * Fast initialize and light DOM tree.
30  * DB connection and works like DB cursor.     
31  * 
32  * HTML Attributes:
33  *
34  *              data-role:      virtuallistview
35  *              data-template : jQuery.template ID that populate into virtual list 
36  *              data-row : Optional. Set number of <li> elements that are used for data handling. 
37  *              
38  *              ID : <UL> element that has "data-role=virtuallist" must have ID attribute.
39  *
40  * * APIs:
41  *
42  *              create ( {
43  *                              itemData: function ( idx ) { return json_obj; },
44  *                              numItemData: number or function () { return number; },
45  *                              cacheItemData: function ( minIdx, maxIdx ) {}
46  *                              } )
47  *                      : Create a virtuallist widget. At this moment, _create method is called.
48  *                      args : A collection of options
49  *                              itemData: A function that returns JSON object for given index. Mandatory.
50  *                              numItemData: Total number of itemData. Mandatory.
51  *                              cacheItemData: Virtuallist will ask itemData between minIdx and maxIdx.
52  *                                             Developers can implement this function for preparing data.
53  *                                             Optional.
54  *
55  * Events:
56  *
57  *              touchstart : Temporary preventDefault applied on touchstart event to avoid broken screen.
58  *
59  * Examples:
60  *
61  *              <script id="tmp-3-2-7" type="text/x-jquery-tmpl">
62  *                      <li class="ui-li-3-2-7">
63  *                              <span class="ui-li-text-main">${NAME}</span>
64  *                              <img src="00_winset_icon_favorite_on.png" class="ui-li-icon-sub">
65  *                              <span class="ui-li-text-sub">${ACTIVE}</span>
66  *                              <span class="ui-li-text-sub2">${FROM}</span>
67  *                      </li>
68  *              </script>
69  *
70  *              <ul id="virtuallist-normal_3_2_7_ul" data-role="virtuallistview" data-template="tmp-3-2-7" data-dbtable="JSON_DATA" data-row="100">
71  *              </ul>
72  *
73  */
74
75
76 (function ( $, undefined ) {
77
78         /* Code for Virtual List Demo */
79         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 */
80                 _NO_SCROLL = 0,                                 /* ENUM */
81                 _SCROLL_DOWN = 1,                               /* ENUM */
82                 _SCROLL_UP = -1;                                        /* ENUM */
83
84         $.widget( "tizen.virtuallistview", $.mobile.widget, {
85                 options: {
86                         theme: "s",
87                         countTheme: "c",
88                         headerTheme: "b",
89                         dividerTheme: "b",
90                         splitIcon: "arrow-r",
91                         splitTheme: "b",
92                         inset: false,
93                         id:     "",                                     /* Virtual list UL elemet's ID */
94                         childSelector: " li",   /* To support swipe list */
95                         dbtable: "",
96                         template : "",
97                         dbkey: false,                   /* Data's unique Key */
98                         scrollview: false,
99                         row: 100,
100                         page_buf: 50,
101                         initSelector: ":jqmData(role='virtuallistview')"
102                 },
103
104                 _stylerMouseUp: function () {
105                         $( this ).addClass( "ui-btn-up-s" );
106                         $( this ).removeClass( "ui-btn-down-s" );
107                 },
108
109                 _stylerMouseDown: function () {
110                         $( this ).addClass( "ui-btn-down-s" );
111                         $( this ).removeClass( "ui-btn-up-s" );
112                 },
113
114                 _stylerMouseOver: function () {
115                         $( this ).toggleClass( "ui-btn-hover-s" );
116                 },
117
118                 _stylerMouseOut: function () {
119                         $( this ).toggleClass( "ui-btn-hover-s" );
120                         $( this ).addClass( "ui-btn-up-s" );
121                         $( this ).removeClass( "ui-btn-down-s" );
122                 },
123
124                 _pushData: function ( template ) {
125                         var o = this.options,
126                                 i,
127                                 myTemplate = $( "#" + template ),
128                                 lastIndex = ( o.row > this._numItemData ? this._numItemData : o.row ),
129                                 htmlData;
130
131                         for ( i = 0; i < lastIndex; i++ ) {
132                                 htmlData = myTemplate.tmpl( this._itemData( i ) );
133                                 $( o.id ).append( $( htmlData ).attr( 'id', 'li_' + i ) );
134                         }
135
136                         /* After push data, re-style virtuallist widget */
137                         $( o.id ).trigger( "create" );
138                 },
139
140                 _reposition: function ( event ) {
141                         var o,
142                                 t = this,
143                                 padding;
144
145                         if ( event.data ) {
146                                 o = event.data;
147                         } else {
148                                 o = event;
149                         }
150
151                         if ( $( o.id + o.childSelector ).size() > 0 ) {
152                                 t._title_h = $( o.id + o.childSelector + ':first' ).position().top;
153                                 t._line_h = $( o.id + o.childSelector + ':first' ).outerHeight();
154
155                                 t._container_w = $( o.id ).innerWidth();
156
157                                 padding = parseInt( $( o.id + o.childSelector ).css( "padding-left" ), 10 ) + parseInt( $( o.id + o.childSelector ).css( "padding-right" ), 10 );
158
159                                 /* Add style */
160                                 $( o.id + ">" + o.childSelector )
161                                         .addClass( "position_absolute" )
162                                         .addClass( "ui-btn-up-s" )
163                                         .bind( "mouseup", t._stylerMouseUp )
164                                         .bind( "mousedown", t._stylerMouseDown )
165                                         .bind( "mouseover", t._stylerMouseOver )
166                                         .bind( "mouseout", t._stylerMouseOut );
167                         }
168
169                         $( o.id + ">" + o.childSelector ).each( function ( index ) {
170                                 $( this ).css( "top", t._title_h + t._line_h * index + 'px' )
171                                         .css( "width", t._container_w - padding );
172                         } );
173
174                         /* Set Max List Height */
175                         $( o.id ).height( t._numItemData * t._line_h );
176                 },
177
178                 _resize: function ( event ) {
179                         var o,
180                                 t = this,
181                                 padding;
182
183                         if ( event.data ) {
184                                 o = event.data;
185                         } else {
186                                 o = event;
187                         }
188
189                         t._container_w = $( o.id ).innerWidth();
190
191                         padding = parseInt( $( o.id + o.childSelector ).css( "padding-left" ), 10 ) + parseInt( $( o.id + o.childSelector ).css( "padding-right" ), 10 );
192
193                         $( o.id + o.childSelector ).each( function (index) {
194                                 $( this ).css( "width", t._container_w - padding );
195                         } );
196                 },
197
198                 _scrollmove: function ( event ) {
199                         var t = event.data,     // document
200                                 o = t.options,
201                                 velocity = 0,
202                                 i,
203                                 _replace,               /* Function */
204                                 _moveTopBottom, /* Function */
205                                 _moveBottomTop, /* Function */
206                                 _matrixToArray, /* Function */
207                                 $el,
208                                 transformValue,
209                                 curWindowTop,
210                                 cur_num_top_items;
211
212                         /* Text & image src replace function */
213                         _replace = function ( oldItem, newItem, key ) {
214                                 var oldObj,
215                                         newText,
216                                         newImg;
217
218                                 $( oldItem ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).each( function ( index ) {
219                                         oldObj = $( this );
220                                         newText = $( newItem ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).eq( index ).text();
221
222                                         $( oldObj).contents().filter( function () {
223                                                 return ( this.nodeType == 3 );
224                                         } ).get( 0 ).data = newText;
225                                 } );
226
227                                 $( oldItem ).find( "img" ).each( function ( imgIndex ) {
228                                         oldObj = $( this );
229                                         newImg = $( newItem ).find( "img" ).eq( imgIndex ).attr( "src" );
230
231                                         $( oldObj ).attr( "src", newImg );
232                                 } );
233
234                                 $( oldItem ).removeData( );     // Clear old data
235
236                                 if (key) {
237                                         $( oldItem ).data( key, $( newItem ).data( key ) );
238                                 }
239                         };
240
241                         //Move older item to bottom
242                         _moveTopBottom = function ( v_firstIndex, v_lastIndex, num, key ) {
243                                 var myTemplate,
244                                         htmlData,
245                                         cur_item;
246
247                                 if (v_firstIndex < 0) {
248                                         return;
249                                 }
250
251                                 for ( i = 0; i < num; i++ ) {
252                                         if ( v_lastIndex + i > t._numItemData ) {
253                                                 break;
254                                         }
255
256                                         cur_item = $( '#li_' + ( v_firstIndex + i ) );
257
258                                         if ( cur_item ) {
259                                                 /* Make New <LI> element from template. */
260                                                 myTemplate = $( "#" + o.template );
261                                                 htmlData = myTemplate.tmpl( t._itemData( v_lastIndex + i ) );
262
263                                                 /* Copy all data to current item. */
264                                                 _replace( cur_item, htmlData, key );
265
266                                                 // Clear temporary htmlData to free cache
267                                                 htmlData.remove();
268
269                                                 /* Set New Position */
270                                                 ( cur_item ).css( 'top', t._title_h + t._line_h * ( v_lastIndex + 1 + i ) ).attr( 'id', 'li_' + ( v_lastIndex + 1 + i ) );
271
272                                         } else {
273                                                 break;
274                                         }
275                                 }
276                         };
277
278                         // Move older item to bottom
279                         _moveBottomTop = function ( v_firstIndex, v_lastIndex, num, key ) {
280                                 var myTemplate,
281                                         htmlData,
282                                         cur_item;
283
284                                 if ( v_firstIndex < 0 ) {
285                                         return;
286                                 }
287
288                                 for ( i = 0; i < num; i++ ) {
289                                         cur_item = $( '#li_' + ( v_lastIndex - i ) );
290
291                                         if ( cur_item ) {
292                                                 if ( v_firstIndex - 1 - i < 0 ) {
293                                                         break;
294                                                 }
295
296                                                 /* Make New <LI> element from template. */
297                                                 myTemplate = $( "#" + o.template );
298                                                 htmlData = myTemplate.tmpl( t._itemData( v_firstIndex - 1 - i ) );
299
300                                                 /* Copy all data to current item. */
301                                                 _replace( cur_item, htmlData, key );
302
303                                                 // Clear temporary htmlData to free cache
304                                                 htmlData.remove();
305
306                                                 /* Set New Position */
307                                                 $( cur_item ).css( 'top', t._title_h + t._line_h * ( v_firstIndex - 1 - i ) ).attr( 'id', 'li_' + ( v_firstIndex - 1 - i ) );
308
309                                         } else {
310                                                 break;
311                                         }
312                                 }
313                         };
314
315                         /* Matrix to Array function written by Blender@stackoverflow.nnikishi@emich.edu*/
316                         _matrixToArray = function ( matrix ) {
317                                 var contents = matrix.substr( 7 );
318
319                                 contents = contents.substr( 0, contents.length - 1 );
320
321                                 return contents.split( ', ' );
322                         };
323
324                         // Get scroll direction and velocity
325                         /* with Scroll view */
326                         if ( o.scrollview ) {
327                                 $el = $( o.id ).parentsUntil( ".ui-page" ).find( ".ui-scrollview-view" );
328                                 transformValue = _matrixToArray( $el.css( "-webkit-transform" ) );
329                                 curWindowTop = Math.abs( transformValue[ 5 ] ); /* Y vector */
330                         } else {
331                                 curWindowTop = $( window ).scrollTop() - t._line_h;
332                         }
333
334                         cur_num_top_items = $( o.id + o.childSelector ).filter( function () {
335                                 return (parseInt( $( this ).css( "top" ), 10 ) < curWindowTop );
336                         } ).size();
337
338                         if ( t._num_top_items < cur_num_top_items ) {
339                                 t._direction = _SCROLL_DOWN;
340                                 velocity = cur_num_top_items - t._num_top_items;
341                                 t._num_top_items = cur_num_top_items;
342                         } else if ( t._num_top_items > cur_num_top_items ) {
343                                 t._direction = _SCROLL_UP;
344                                 velocity = t._num_top_items - cur_num_top_items;
345                                 t._num_top_items = cur_num_top_items;
346                         }
347
348                         // Move items
349                         if ( t._direction == _SCROLL_DOWN ) {
350                                 if ( cur_num_top_items > o.page_buf ) {
351                                         if ( t._last_index + velocity > t._numItemData ) {
352                                                 velocity = t._numItemData - t._last_index - 1;
353                                         }
354
355                                         /* Prevent scroll touch event while DOM access */
356                                         $(document).bind( "touchstart.virtuallist", function (event) {
357                                                 event.preventDefault();
358                                         });
359
360                                         _moveTopBottom( t._first_index, t._last_index, velocity, o.dbkey );
361
362                                         t._first_index += velocity;
363                                         t._last_index += velocity;
364                                         t._num_top_items -= velocity;
365
366                                         /* Unset prevent touch event */
367                                         $( document ).unbind( "touchstart.virtuallist" );
368                                 }
369                         } else if ( t._direction == _SCROLL_UP ) {
370                                 if ( cur_num_top_items <= o.page_buf ) {
371                                         if ( t._first_index < velocity ) {
372                                                 velocity = t._first_index;
373                                         }
374
375                                         /* Prevent scroll touch event while DOM access */
376                                         $( document ).bind( "touchstart.virtuallist", function ( event ) {
377                                                 event.preventDefault();
378                                         });
379
380                                         _moveBottomTop( t._first_index, t._last_index, velocity, o.dbkey );
381
382                                         t._first_index -= velocity;
383                                         t._last_index -= velocity;
384                                         t._num_top_items += velocity;
385
386                                         /* Unset prevent touch event */
387                                         $( document ).unbind( "touchstart.virtuallist" );
388                                 }
389
390                                 if ( t._first_index < o.page_buf ) {
391                                         t._num_top_items = t._first_index;
392                                 }
393                         }
394                 },
395
396                 _recreate: function ( newArray ) {
397                         var t = this,
398                                 o = this.options;
399
400                         $( o.id ).empty();
401
402                         t._numItemData = newArray.length;
403                         t._direction = _NO_SCROLL;
404                         t._first_index = 0;
405                         t._last_index = o.row - 1;
406
407                         t._pushData( o.template );
408
409                         if (o.childSelector == " ul" ) {
410                                 $( o.id + " ul" ).swipelist();
411                         }
412
413                         $( o.id ).virtuallistview();
414
415                         t.refresh( true );
416
417                         t._reposition( o );
418                 },
419
420                 _initList: function () {
421                         var t = this,
422                                 o = this.options;
423
424                         /* After AJAX loading success */
425
426                         /* Make Gen list by template */
427                         t._pushData( o.template );
428
429                         $( o.id ).parentsUntil( ".ui-page" ).parent().one( "pageshow", function () {
430                                 setTimeout( function () {
431                                         t._reposition( o );
432                                 }, 0);
433                         });
434
435                         /* Scrollview */
436                         $( document ).bind( "scrollstop.virtuallist", t, t._scrollmove );
437
438                         $( window ).bind( "resize.virtuallist", t._resize );
439
440                         if ( o.childSelector == " ul" ) {
441                                 $( o.id + " ul" ).swipelist();
442                         }
443
444                         t.refresh( true );
445                 },
446
447                 create: function () {
448                         var o = this.options;
449
450                         /* external API for AJAX callback */
451                         this._create.apply( this, arguments );
452
453                         this._reposition( o );
454                 },
455
456                 _create: function ( args ) {
457                         // Extend required vars
458                         $.extend( this, {
459                                 _itemData : function ( idx ) { return null; },
460                                 _numItemData : 0,
461                                 _cacheItemData : function ( minIdx, maxIdx ) { },
462                                 _line_h : 0,
463                                 _title_h : 0,
464                                 _container_w : 0,
465                                 _minimum_row : 20,
466                                 _direction : _NO_SCROLL,
467                                 _first_index : 0,
468                                 _last_index : 0,
469                                 _num_top_items : 0      // By scroll move, number of hidden elements.
470                         } );
471
472                         var t = this,
473                                 o = this.options,
474                                 $el = this.element,
475                                 shortcutsContainer = $('<div class="ui-virtuallist"/>'),
476                                 shortcutsList = $('<ul></ul>'),
477                                 dividers = $el.find(':jqmData(role="virtuallistview" )'),
478                                 lastListItem = null,
479                                 shortcutscroll = this,
480                                 dbtable_name,
481                                 dbtable;
482
483
484                         // create listview markup
485                         t.element.addClass( function ( i, orig ) {
486                                 return orig + " ui-listview ui-virtual-list-container" + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" );
487                         });
488
489                         o.id = "#" + $el.attr( "id" );
490
491                         $( o.id ).bind( "pagehide", function ( e ) {
492                                 $( o.id ).empty();
493                         });
494
495                         /* Scroll view */
496                         if ( $( ".ui-scrollview-clip" ).size() > 0 ) {
497                                 o.scrollview = true;
498                         } else {
499                                 o.scrollview = false;
500                         }
501
502                         /* Init list and page buf */
503                         if ( $el.data( "row" ) ) {
504                                 o.row = $el.data( "row" );
505
506                                 if ( o.row < t._minimum_row ) {
507                                         o.row = t._minimum_row;
508                                 }
509
510                                 o.page_buf = parseInt( ( o.row / 2 ), 10 );
511                         }
512
513                         // Get arguments
514                         if ( args ) {
515                                 if ( args.itemData && typeof args.itemData == 'function'  ) {
516                                         t._itemData = args.itemData;
517                                 } else {
518                                         return;
519                                 }
520                                 if ( args.numItemData ) {
521                                         if ( typeof args.numItemData == 'function' ) {
522                                                 t._numItemData = args.numItemData( );
523                                         } else if ( typeof args.numItemData == 'number' ) {
524                                                 t._numItemData = args.numItemData;
525                                         } else {
526                                                 return;
527                                         }
528                                 } else {
529                                         return;
530                                 }
531                         } else {        // No option is given
532                                 // Legacy support: dbtable
533                                 console.log("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!");
534
535                                 /* After DB Load complete, Init Vritual list */
536                                 if ( $( o.id ).hasClass( "vlLoadSuccess" ) ) {
537                                         dbtable_name = $el.jqmData('dbtable');
538                                         dbtable = window[ dbtable_name ];
539
540                                         if ( !dbtable ) {
541                                                 dbtable = { };
542                                         }
543
544                                         $( o.id ).empty();
545
546                                         if ( $el.data( "template" ) ) {
547                                                 o.template = $el.data( "template" );
548
549                                                 /* to support swipe list, <li> or <ul> can be main node of virtual list. */
550                                                 if ( $el.data( "swipelist" ) == true ) {
551                                                         o.childSelector = " ul";
552                                                 } else {
553                                                         o.childSelector = " li";
554                                                 }
555                                         }
556
557                                         /* Set data's unique key */
558                                         if ( $el.data( "dbkey" ) ) {
559                                                 o.dbkey = $el.data( "dbkey" );
560                                         }
561
562                                         t._first_index = 0;                     //first id of <li> element.
563                                         t._last_index = o.row - 1;              //last id of <li> element.
564
565                                         t._itemData = function ( idx ) {
566                                                 return dbtable[ idx ];
567                                         };
568                                         t._numItemData = dbtable.length;
569
570                                         t._initList();
571                                 }
572                         }
573
574                 },
575
576                 destroy : function () {
577                         var o = this.options;
578
579                         $( document ).unbind( "scrollstop" );
580
581                         $( window ).unbind( "resize.virtuallist" );
582
583                         $( o.id ).empty();
584                 },
585
586                 _itemApply: function ( $list, item ) {
587                         var $countli = item.find( ".ui-li-count" );
588
589                         if ( $countli.length ) {
590                                 item.addClass( "ui-li-has-count" );
591                         }
592
593                         $countli.addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" );
594
595                         // TODO class has to be defined in markup
596                         item.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
597                                 .find( "p, dl" ).addClass( "ui-li-desc" ).end()
598                                 .find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each( function () {
599                                         item.addClass( $( this ).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
600                                 }).end()
601                                 .find( ".ui-li-aside" ).each(function () {
602                                         var $this = $( this );
603                                         $this.prependTo( $this.parent() ); //shift aside to front for css float
604                                 } );
605                 },
606
607                 _removeCorners: function ( li, which ) {
608                         var top = "ui-corner-top ui-corner-tr ui-corner-tl",
609                                 bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
610
611                         li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
612
613                         if ( which === "top" ) {
614                                 li.removeClass( top );
615                         } else if ( which === "bottom" ) {
616                                 li.removeClass( bot );
617                         } else {
618                                 li.removeClass( top + " " + bot );
619                         }
620                 },
621
622                 _refreshCorners: function ( create ) {
623                         var $li,
624                                 $visibleli,
625                                 $topli,
626                                 $bottomli;
627
628                         if ( this.options.inset ) {
629                                 $li = this.element.children( "li" );
630                                 // at create time the li are not visible yet so we need to rely on .ui-screen-hidden
631                                 $visibleli = create ? $li.not( ".ui-screen-hidden" ) : $li.filter( ":visible" );
632
633                                 this._removeCorners( $li );
634
635                                 // Select the first visible li element
636                                 $topli = $visibleli.first()
637                                         .addClass( "ui-corner-top" );
638
639                                 $topli.add( $topli.find( ".ui-btn-inner" ) )
640                                         .find( ".ui-li-link-alt" )
641                                                 .addClass( "ui-corner-tr" )
642                                         .end()
643                                         .find( ".ui-li-thumb" )
644                                                 .not( ".ui-li-icon" )
645                                                 .addClass( "ui-corner-tl" );
646
647                                 // Select the last visible li element
648                                 $bottomli = $visibleli.last()
649                                         .addClass( "ui-corner-bottom" );
650
651                                 $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
652                                         .find( ".ui-li-link-alt" )
653                                                 .addClass( "ui-corner-br" )
654                                         .end()
655                                         .find( ".ui-li-thumb" )
656                                                 .not( ".ui-li-icon" )
657                                                 .addClass( "ui-corner-bl" );
658                         }
659                 },
660
661                 refresh: function ( create ) {
662                         this.parentPage = this.element.closest( ".ui-page" );
663                         this._createSubPages();
664
665                         var o = this.options,
666                                 $list = this.element,
667                                 self = this,
668                                 dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme,
669                                 listsplittheme = $list.jqmData( "splittheme" ),
670                                 listspliticon = $list.jqmData( "spliticon" ),
671                                 li = $list.children( "li" ),
672                                 counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1,
673                                 item,
674                                 itemClass,
675                                 temTheme,
676                                 a,
677                                 last,
678                                 splittheme,
679                                 countParent,
680                                 icon,
681                                 pos,
682                                 numli,
683                                 itemTheme;
684
685                         if ( counter ) {
686                                 $list.find( ".ui-li-dec" ).remove();
687                         }
688
689                         for ( pos = 0, numli = li.length; pos < numli; pos++ ) {
690                                 item = li.eq( pos );
691                                 itemClass = "ui-li";
692
693                                 // If we're creating the element, we update it regardless
694                                 if ( create || !item.hasClass( "ui-li" ) ) {
695                                         itemTheme = item.jqmData( "theme" ) || o.theme;
696                                         a = item.children( "a" );
697
698                                         if ( a.length ) {
699                                                 icon = item.jqmData( "icon" );
700
701                                                 item.buttonMarkup({
702                                                         wrapperEls: "div",
703                                                         shadow: false,
704                                                         corners: false,
705                                                         iconpos: "right",
706                                                         /* icon: a.length > 1 || icon === false ? false : icon || "arrow-r",*/
707                                                         icon: false,    /* Remove unnecessary arrow icon */
708                                                         theme: itemTheme
709                                                 });
710
711                                                 if ( ( icon != false ) && ( a.length == 1 ) ) {
712                                                         item.addClass( "ui-li-has-arrow" );
713                                                 }
714
715                                                 a.first().addClass( "ui-link-inherit" );
716
717                                                 if ( a.length > 1 ) {
718                                                         itemClass += " ui-li-has-alt";
719
720                                                         last = a.last();
721                                                         splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme;
722
723                                                         last.appendTo(item)
724                                                                 .attr( "title", last.getEncodedText() )
725                                                                 .addClass( "ui-li-link-alt" )
726                                                                 .empty()
727                                                                 .buttonMarkup({
728                                                                         shadow: false,
729                                                                         corners: false,
730                                                                         theme: itemTheme,
731                                                                         icon: false,
732                                                                         iconpos: false
733                                                                 })
734                                                                 .find( ".ui-btn-inner" )
735                                                                 .append(
736                                                                         $( "<span />" ).buttonMarkup({
737                                                                                 shadow: true,
738                                                                                 corners: true,
739                                                                                 theme: splittheme,
740                                                                                 iconpos: "notext",
741                                                                                 icon: listspliticon || last.jqmData( "icon" ) || o.splitIcon
742                                                                         })
743                                                                 );
744                                                 }
745                                         } else if ( item.jqmData( "role" ) === "list-divider" ) {
746
747                                                 itemClass += " ui-li-divider ui-btn ui-bar-" + dividertheme;
748                                                 item.attr( "role", "heading" );
749
750                                                 //reset counter when a divider heading is encountered
751                                                 if ( counter ) {
752                                                         counter = 1;
753                                                 }
754
755                                         } else {
756                                                 itemClass += " ui-li-static ui-body-" + itemTheme;
757                                         }
758                                 }
759
760                                 if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
761                                         countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
762
763                                         countParent.addClass( "ui-li-jsnumbering" )
764                                                 .prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" );
765                                 }
766
767                                 item.add( item.children( ".ui-btn-inner" ) ).addClass( itemClass );
768
769                                 self._itemApply( $list, item );
770                         }
771
772                         this._refreshCorners( create );
773                 },
774
775                 //create a string for ID/subpage url creation
776                 _idStringEscape: function ( str ) {
777                         return str.replace(/\W/g , "-");
778                 },
779
780                 _createSubPages: function () {
781                         var parentList = this.element,
782                                 parentPage = parentList.closest( ".ui-page" ),
783                                 parentUrl = parentPage.jqmData( "url" ),
784                                 parentId = parentUrl || parentPage[ 0 ][ $.expando ],
785                                 parentListId = parentList.attr( "id" ),
786                                 o = this.options,
787                                 dns = "data-" + $.mobile.ns,
788                                 self = this,
789                                 persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
790                                 hasSubPages,
791                                 newRemove;
792
793                         if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
794                                 listCountPerPage[ parentId ] = -1;
795                         }
796
797                         parentListId = parentListId || ++listCountPerPage[ parentId ];
798
799                         $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function ( i ) {
800                                 var self = this,
801                                         list = $( this ),
802                                         listId = list.attr( "id" ) || parentListId + "-" + i,
803                                         parent = list.parent(),
804                                         nodeEls,
805                                         title = nodeEls.first().getEncodedText(),//url limits to first 30 chars of text
806                                         id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
807                                         theme = list.jqmData( "theme" ) || o.theme,
808                                         countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme,
809                                         newPage,
810                                         anchor;
811
812                                 nodeEls = $( list.prevAll().toArray().reverse() );
813                                 nodeEls = nodeEls.length ? nodeEls : $( "<span>" + $.trim( parent.contents()[ 0 ].nodeValue ) + "</span>" );
814
815                                 //define hasSubPages for use in later removal
816                                 hasSubPages = true;
817
818                                 newPage = list.detach()
819                                                         .wrap( "<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
820                                                         .parent()
821                                                                 .before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
822                                                                 .after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='" + persistentFooterID + "'>" ) : "" )
823                                                                 .parent()
824                                                                 .appendTo( $.mobile.pageContainer );
825
826                                 newPage.page();
827
828                                 anchor = parent.find('a:first');
829
830                                 if ( !anchor.length ) {
831                                         anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
832                                 }
833
834                                 anchor.attr( "href", "#" + id );
835
836                         }).virtuallistview();
837
838                         // on pagehide, remove any nested pages along with the parent page, as long as they aren't active
839                         // and aren't embedded
840                         if ( hasSubPages &&
841                                                 parentPage.is( ":jqmData(external-page='true')" ) &&
842                                                 parentPage.data( "page" ).options.domCache === false ) {
843
844                                 newRemove = function ( e, ui ) {
845                                         var nextPage = ui.nextPage, npURL;
846
847                                         if ( ui.nextPage ) {
848                                                 npURL = nextPage.jqmData( "url" );
849                                                 if ( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ) {
850                                                         self.childPages().remove();
851                                                         parentPage.remove();
852                                                 }
853                                         }
854                                 };
855
856                                 // unbind the original page remove and replace with our specialized version
857                                 parentPage
858                                         .unbind( "pagehide.remove" )
859                                         .bind( "pagehide.remove", newRemove );
860                         }
861                 },
862
863                 // TODO sort out a better way to track sub pages of the virtuallistview this is brittle
864                 childPages: function () {
865                         var parentUrl = this.parentPage.jqmData( "url" );
866
867                         return $( ":jqmData(url^='" +  parentUrl + "&" + $.mobile.subPageUrlKey + "')" );
868                 }
869         });
870
871         //auto self-init widgets
872         $( document ).bind( "pagecreate create", function ( e ) {
873                 $( $.tizen.virtuallistview.prototype.options.initSelector, e.target ).virtuallistview();
874         });
875
876 } ( jQuery ) );