2 /* ***************************************************************************
3 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 * ***************************************************************************
24 * Author: Wongi Lee <wongi11.lee@samsung.com>
25 * Youmin Ha <youmin.ha@samsung.com>
29 * Virtual List Widget for unlimited data.
30 * To support more then 1,000 items, special list widget developed.
31 * Fast initialize and light DOM tree.
32 * DB connection and works like DB cursor.
36 * data-role: virtuallistview
37 * data-template : jQuery.template ID that populate into virtual list
38 * data-row : Optional. Set number of <li> elements that are used for data handling.
40 * ID : <UL> element that has "data-role=virtuallist" must have ID attribute.
45 * itemData: function ( idx ) { return json_obj; },
46 * numItemData: number or function () { return number; },
47 * cacheItemData: function ( minIdx, maxIdx ) {}
49 * : Create a virtuallist widget. At this moment, _create method is called.
50 * args : A collection of options
51 * itemData: A function that returns JSON object for given index. Mandatory.
52 * numItemData: Total number of itemData. Mandatory.
53 * cacheItemData: Virtuallist will ask itemData between minIdx and maxIdx.
54 * Developers can implement this function for preparing data.
59 * touchstart : Temporary preventDefault applied on touchstart event to avoid broken screen.
63 * <script id="tmp-3-2-7" type="text/x-jquery-tmpl">
64 * <li class="ui-li-3-2-7">
65 * <span class="ui-li-text-main">${NAME}</span>
66 * <img src="00_winset_icon_favorite_on.png" class="ui-li-icon-sub">
67 * <span class="ui-li-text-sub">${ACTIVE}</span>
68 * <span class="ui-li-text-sub2">${FROM}</span>
72 * <ul id="virtuallist-normal_3_2_7_ul" data-role="virtuallistview" data-template="tmp-3-2-7" data-dbtable="JSON_DATA" data-row="100">
79 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.
81 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.
83 To add a virtual list widget to the application, use the following code:
85 <script id="tmp-3-2-7" type="text/x-jquery-tmpl">
86 <li class="ui-li-3-2-7">
87 <span class="ui-li-text-main">${NAME}</span>
88 <img src="00_winset_icon_favorite_on.png" class="ui-li-icon-sub"/>
89 <span class="ui-li-text-sub">${ACTIVE}</span>
90 <span class="ui-li-text-sub2">${FROM}</span>
93 <ul id="vlist" data-role="virtuallistview" data-template="tmp-3-2-7" data-dbtable="JSON_DATA" data-row="100"></ul>
96 @property {String} data-role
97 Creates the virtual list view. The value must be set to virtuallistview.
98 Only the >ul< element, which a id attribute defined, supports this option. Also, the vlLoadSuccess class attribute must be defined in the >ul< element to ensure that loading data from the database is complete.
101 @property {String} data-template
102 Defines the jQuery.template element ID.
103 The jQuery.template must be defined. The template style can use rem units to support scalability.
106 @property {Number} data-row
107 Defines the number of virtual list child elements.
108 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.
112 @param {function} itemData(index)
113 : function itemData(index) returns the JSON object matched with the given index. The index value is between 0 and numItemData-1.
114 @param {Number} numItemData
115 : number numItemData or function numItemData() defines or returns a static number of items.
116 @param {function} cacheItemData(minIndex, maxIndex)
117 : function cacheItemData(minIndex, maxIndex) prepares the JSON data. This method is called before calling the itemData() method with index values between minIndex and maxIndex.
120 (function ( $, undefined ) {
122 /* Code for Virtual List Demo */
123 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 */
124 _NO_SCROLL = 0, /* ENUM */
125 _SCROLL_DOWN = 1, /* ENUM */
126 _SCROLL_UP = -1; /* ENUM */
128 $.widget( "tizen.virtuallistview", $.mobile.widget, {
134 splitIcon: "arrow-r",
137 id: "", /* Virtual list UL elemet's ID */
138 childSelector: " li", /* To support swipe list */
141 dbkey: false, /* Data's unique Key */
145 initSelector: ":jqmData(role='virtuallistview')"
148 _stylerMouseUp: function () {
149 $( this ).addClass( "ui-btn-up-s" );
150 $( this ).removeClass( "ui-btn-down-s" );
153 _stylerMouseDown: function () {
154 $( this ).addClass( "ui-btn-down-s" );
155 $( this ).removeClass( "ui-btn-up-s" );
158 _stylerMouseOver: function () {
159 $( this ).toggleClass( "ui-btn-hover-s" );
162 _stylerMouseOut: function () {
163 $( this ).toggleClass( "ui-btn-hover-s" );
164 $( this ).addClass( "ui-btn-up-s" );
165 $( this ).removeClass( "ui-btn-down-s" );
169 // this virtuallistview object
170 // @param[in] template template name(string)
171 _pushData: function ( template ) {
172 var o = this.options,
174 myTemplate = $( "#" + template ), // Get template object
175 // NOTE: o.row = # of rows handled at once. Default value is 100.
176 lastIndex = ( o.row > this._numItemData ? this._numItemData : o.row ), // last index of handled data
179 for ( i = 0; i < lastIndex; i++ ) {
180 htmlData = myTemplate.tmpl( this._itemData( i ) ); // Make rows with template,
181 $( o.id ).append( $( htmlData ).attr( 'id', o.itemIDPrefix + i ) ); // and append it to the vlist object
184 // After pushing data re-style virtuallist widget
185 $( o.id ).trigger( "create" );
188 // Set children <li> elements' position
190 // this: virtuallist element
191 // event: virtuallistview.options
192 // TODO: Why this arg name is 'event'? Not resonable.
193 // (this function is not called with event element as args!)
194 _reposition: function ( event ) {
205 if ( $( o.id + o.childSelector ).size() > 0 ) { // $("#vlistid li")
206 // first child's top position
207 // NOTE: the first element may not be '0'!!!
208 t._title_h = $( o.id + o.childSelector + ':first' ).position().top;
209 // first child's outer height (TODO: reuse selected items)
210 t._line_h = $( o.id + o.childSelector + ':first' ).outerHeight();
212 // container(vlist element)'s innerwidth
213 t._container_w = $( o.id ).innerWidth();
215 // get sum of container's left/right padding
216 padding = parseInt( $( o.id + o.childSelector ).css( "padding-left" ), 10 )
217 + parseInt( $( o.id + o.childSelector ).css( "padding-right" ), 10 );
219 // Add CSS to all <li> elements
220 // * absolute position
222 // * mouse up/down/over/out styles
223 $( o.id + ">" + o.childSelector )
224 .addClass( "position_absolute" )
225 .addClass( "ui-btn-up-s" )
226 .bind( "mouseup", t._stylerMouseUp )
227 .bind( "mousedown", t._stylerMouseDown )
228 .bind( "mouseover", t._stylerMouseOver )
229 .bind( "mouseout", t._stylerMouseOut );
232 // Set absolute top/left position of each <li>
233 $( o.id + ">" + o.childSelector ).each( function ( index ) {
234 margin = parseInt( $( this ).css( "margin-left" ), 10 )
235 + parseInt( $( this ).css( "margin-right" ), 10 );
237 $( this ).css( "top", t._title_h + t._line_h * index + 'px' )
238 .css( "width", t._container_w - padding - margin );
241 // Set Max Listview Height
242 $( o.id ).height( t._numItemData * t._line_h );
245 // Resize each listitem's width
246 _resize: function ( event ) {
258 li = $( o ).children( o.childSelector )
260 t._container_w = $( o ).width();
262 padding = parseInt( li.css( "padding-left" ), 10 )
263 + parseInt( li.css( "padding-right" ), 10 );
265 li.each( function ( index, obj ) {
266 margin = parseInt( $( this ).css( "margin-left" ), 10 )
267 + parseInt( $( this ).css( "margin-right" ), 10 );
268 $( this ).css( "width", t._container_w - padding - margin );
272 // New scrollmove function supporting scrollTo
273 _scrollmove: function ( ev ) {
274 var t = ev.data, // vlist (JQM object)
275 o = t.options, // options
276 prevTopBufLen = t._num_top_items, // Previous(remembered) top buf length
283 viewTop: function ( ) {
284 var sv = $( o.id ).parentsUntil( ".ui-page" ).find( ".ui-scrollview-view" ),
285 svTrans = sv.css( "-webkit-transform" ),
286 svTransVal = "0,0,0,0,0,0";
288 svTransVal = svTrans.replace( /matrix\s*\((.*)\)/, "$1" ); // matrix(a,c,b,d,tx,ty)
290 return - parseInt( svTransVal.split(',')[5], 10 );
294 viewTop: function ( ) {
295 return $( window ).scrollTop( ); // TODO: - _line_h?
298 // Get current view top position
299 function viewTop ( ) {
300 return o.scrollview ? _scrollView.viewTop() : _normalScroll.viewTop();
302 // log function for debug
303 function log ( msg ) {
306 console.log( ">>virtualllist: " + msg );
310 // Timer interval function
311 // @param[in] vl virtuallist object (JQM object)
312 function timerMove ( vl, undefined ) {
313 var cy, // current y position
314 cti, // current top idx
315 cbi, // current bottom idx
316 oti = vl._first_index, // old top idx
317 obi = vl._last_index, // old botton idx
318 dti, // delta of top idx
320 toIdx, // index range to be moved
321 delta, // moveItem delta
322 rowLen = vl.options.row, // max. # of items handled at once
323 bufSize, // top/bottom buffer size. unit: # of items
326 // subroutine: Move itemContents in i2 into i1
327 function moveItemContents( vl, i1, i2 ) {
328 // TODO: Find a efficient way to replace data!
329 // Assumption: i1 and i2 has same children.
330 var NODETYPE = { ELEMENT_NODE: 1, TEXT_NODE: 3 },
331 c1, // child item 1 (old)
332 c2, // child item 2 (new)
337 $( i1 ).find( ".ui-li-text-main", ".ui-li-text-sub", ".ui-li-text-sub2", "ui-btn-text" ).each( function ( index ) {
339 newText = $( i2 ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).eq( index ).text();
341 $( c1 ).contents().filter( function () {
342 return ( this.nodeType == NODETYPE.TEXT_NODE );
343 } ).get( 0 ).data = newText;
346 $( i1 ).find( "img" ).each( function ( imgIdx ) {
348 newImg = $( i2 ).find( "img" ).eq( imgIdx ).attr( "src" );
350 $( c1 ).attr( "src", newImg );
353 $( i1 ).removeData( ); // Clear old data
356 // subroutine: Move item
357 function moveItem( vl, fromIdx, toIdx ) {
358 var itemData, // data from itemData()
359 item, // item element
360 newItem, // new item element
363 log( ">> move item: " + fromIdx + " --> " + toIdx );
366 item = $( '#' + vl.options.itemIDPrefix + fromIdx ); // TODO: refactor ID generation!
367 if ( ! item || ! item.length ) {
372 tmpl = $( "#" + vl.options.template );
374 newItem = tmpl.tmpl( vl._itemData( toIdx ) );
376 // TODO: Consider touch block while moving?
378 // Move item contents
379 moveItemContents( vl, item, newItem );
381 // clean up temporary item
385 // Move position, and set id
386 item.css( 'top', toIdx * vl._line_h )
387 .attr( 'id' , vl.options.itemIDPrefix + toIdx ); // TODO: refactor ID generation!
389 // TODO: Apply jqmdata? check following old code;
390 // $( oldItem ).removeData( ); // Clear old data
391 // if (key) { $( oldItem ).data( key, $( newItem ).data( key ) ); }
397 // Get current view position
400 // Calculate bufSize: rowLen / 3
401 // NOTE: Assumption: total row length = visible items * 3 (upper+visible+lower)
402 bufSize = Math.ceil( rowLen / 3 );
404 // Calculate current top/bottom index (to be applied)
405 // top index = current position / line height
406 cti = Math.floor( cy / vl._line_h ) - bufSize; // TODO: consider buffer!
407 cbi = cti + rowLen - 1;
409 if ( cti < 0 ) { // Top boundary check
412 } else if ( cbi > ( vl._numItemData - 1 ) ) { // Bottom boundary check
413 cti -= ( cbi - ( vl._numItemData - 1 ) );
414 cbi = ( vl._numItemData - 1 );
419 log( "cy=" + cy + ", oti=" + oti + ", obi=" + obi + ", cti=" + cti + ", cbi=" + cbi + ", dti=" + dti );
421 // switch: dti = 0 --> timer stop condition: delta=0 or scrollstop event comes. END.
423 // Check timer runtime
424 vl.timerStillCount += 1;
425 if ( vl.timerStillCount < 12 ) { // check count ( TODO: test and adjust )
426 log("dti=0 " + vl.timerStillCount + " times");
427 vl.timerMoveID = setTimeout( timerMove, timerInterval, vl ); // run once more
431 log("dti=0 " + vl.timerStillCount + " times. End timer.");
432 vl.timerStillCount = 0;
434 if ( vl.timerMoveID ) {
435 clearTimeout( vl.timerMoveID );
436 vl.timerMoveID = null;
439 // switch: dti >= # of max elements --> total replace.
440 vl.timerStillCount = 0; // Reset still counter
442 if ( Math.abs( dti ) >= rowLen ) {
446 log( ">>> WHOLE CHANGE! delta=" + delta );
448 // switch: dti < # of max elements --> move t2b or b2t until new top/bottom idx is covered
451 toIdx = oti + dti - 1;
454 fromIdx = obi + dti + 1; // dti < 0
458 log( ">>> partial change. delta=" + delta );
462 for ( i = fromIdx; i <= toIdx; i++ ) {
463 moveItem( vl, i, i + delta ); // Change data and position
466 // Store current top/bottom idx into vl
467 vl._first_index = cti;
468 vl._last_index = cbi;
470 // Register timer to check again
471 vl.timerMoveID = setTimeout( timerMove, timerInterval, vl );
473 return; // End of function
476 // ==== function start ====
478 t.timerStillCount = 0; // Count do-nothing time. For behavior tuning.
480 // If a timer function is alive, clear it
481 if ( t.timerMoveID ) {
482 clearTimeout( t.timerMoveID );
483 t.timerMoveID = null;
489 _recreate: function ( newArray ) {
495 t._numItemData = newArray.length;
496 t._direction = _NO_SCROLL;
498 t._last_index = o.row - 1;
500 t._pushData( o.template );
502 if (o.childSelector == " ul" ) {
503 $( o.id + " ul" ).swipelist();
506 $( o.id ).virtuallistview();
513 // Init virtuallistview
514 // this virtuallistview object
515 _initList: function () {
519 /* After AJAX loading success */
521 // Put initial <li> elements
522 t._pushData( o.template );
524 // find a parent page, and run _reposition() at 'pageshow' event
525 // TODO: Consider replace parentsUntil().parent() to parent('.ui-page') ???
526 $( o.id ).parentsUntil( ".ui-page" ).parent().one( "pageshow", function () {
527 setTimeout( function () {
532 // Bind _scrollmove() at 'scrollstart.virtuallist' event
533 $( document ).bind( "scrollstart.virtuallist scrollstop.vrituallist", t, t._scrollmove );
536 $( window ).on( "throttledresize", $( o.id ), t._resize );
538 // when ul is a childselector, assume that this is also a swipelist,
539 // and run swipelist constructor
540 if ( o.childSelector == " ul" ) {
541 $( o.id + " ul" ).swipelist();
547 create: function () {
548 var o = this.options;
550 /* external API for AJAX callback */
551 this._create.apply( this, arguments );
553 // TODO: remove this line? _initList() calls reposition...
554 this._reposition( o );
557 _create: function ( args ) {
558 // Extend instance variables
560 _itemData : function ( idx ) { return null; },
562 _cacheItemData : function ( minIdx, maxIdx ) { },
566 _direction : _NO_SCROLL,
569 _num_top_items : 0 // By scroll move, number of hidden elements.
576 shortcutsContainer = $('<div class="ui-virtuallist"/>'),
577 shortcutsList = $('<ul></ul>'),
578 dividers = $el.find(':jqmData(role="virtuallistview" )'),
580 shortcutscroll = this,
585 // Add CSS classes to $el (=virtuallistview)
586 $el.addClass( function ( i, orig ) {
587 return orig + " ui-listview ui-virtual-list-container" + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" );
590 // keep the vlist's ID
591 o.itemIDPrefix = $el.attr( "id" ) + '_';
592 o.id = "#" + $el.attr( "id" );
594 // when page hides, empty all child elements
595 $( o.id ).bind( "pagehide", function ( e ) {
599 // Find if scrollview is used
600 if ( $( ".ui-scrollview-clip" ).size() > 0 ) {
603 o.scrollview = false;
606 // Calculate page buffer size
607 if ( $el.data( "row" ) ) {
608 o.row = $el.data( "row" );
610 if ( o.row < t._minimum_row ) {
611 o.row = t._minimum_row;
614 o.page_buf = parseInt( ( o.row / 2 ), 10 );
619 if ( args.itemData && typeof args.itemData == 'function' ) {
620 t._itemData = args.itemData;
624 if ( args.numItemData ) {
625 if ( typeof args.numItemData == 'function' ) {
626 t._numItemData = args.numItemData( );
627 } else if ( typeof args.numItemData == 'number' ) {
628 t._numItemData = args.numItemData;
635 } else { // No option is given
636 // Legacy support: dbtable
637 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!" );
639 /* After DB Load complete, Init Vritual list */
640 if ( $( o.id ).hasClass( "vlLoadSuccess" ) ) {
641 dbtable_name = $el.jqmData('dbtable');
642 dbtable = window[ dbtable_name ];
650 t._itemData = function ( idx ) {
651 return dbtable[ idx ];
653 t._numItemData = dbtable.length;
655 return; // Do nothing
660 if ( $el.data( "template" ) ) {
661 o.template = $el.data( "template" );
663 /* to support swipe list, <li> or <ul> can be main node of virtual list. */
664 if ( $el.data( "swipelist" ) == true ) {
665 o.childSelector = " ul";
667 o.childSelector = " li";
671 // Set data's unique key
672 // NOTE: Unnecessary?
673 if ( $el.data( "dbkey" ) ) {
674 o.dbkey = $el.data( "dbkey" );
677 t._first_index = 0; // initial top idx of <li> element.
678 t._last_index = o.row - 1; // initial bottom idx of <li> element.
679 t._initList(); // NOTE: Called at here only!
682 destroy : function () {
683 var o = this.options;
685 $( document ).unbind( "scrollstop" );
687 $( window ).off( "throttledresize", this._resize );
691 if ( this.timerMoveID ) {
692 clearTimeout( this.timerMoveID );
693 this.timerMoveID = null;
697 _itemApply: function ( $list, item ) {
698 var $countli = item.find( ".ui-li-count" );
700 if ( $countli.length ) {
701 item.addClass( "ui-li-has-count" );
704 $countli.addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" );
706 // TODO class has to be defined in markup
707 item.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
708 .find( "p, dl" ).addClass( "ui-li-desc" ).end()
709 .find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each( function () {
710 item.addClass( $( this ).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
712 .find( ".ui-li-aside" ).each(function () {
713 var $this = $( this );
714 $this.prependTo( $this.parent() ); //shift aside to front for css float
718 _removeCorners: function ( li, which ) {
719 var top = "ui-corner-top ui-corner-tr ui-corner-tl",
720 bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
722 li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
724 if ( which === "top" ) {
725 li.removeClass( top );
726 } else if ( which === "bottom" ) {
727 li.removeClass( bot );
729 li.removeClass( top + " " + bot );
733 _refreshCorners: function ( create ) {
739 if ( this.options.inset ) {
740 $li = this.element.children( "li" );
741 // at create time the li are not visible yet so we need to rely on .ui-screen-hidden
742 $visibleli = create ? $li.not( ".ui-screen-hidden" ) : $li.filter( ":visible" );
744 this._removeCorners( $li );
746 // Select the first visible li element
747 $topli = $visibleli.first()
748 .addClass( "ui-corner-top" );
750 $topli.add( $topli.find( ".ui-btn-inner" ) )
751 .find( ".ui-li-link-alt" )
752 .addClass( "ui-corner-tr" )
754 .find( ".ui-li-thumb" )
755 .not( ".ui-li-icon" )
756 .addClass( "ui-corner-tl" );
758 // Select the last visible li element
759 $bottomli = $visibleli.last()
760 .addClass( "ui-corner-bottom" );
762 $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
763 .find( ".ui-li-link-alt" )
764 .addClass( "ui-corner-br" )
766 .find( ".ui-li-thumb" )
767 .not( ".ui-li-icon" )
768 .addClass( "ui-corner-bl" );
772 // this virtuallistview object
773 refresh: function ( create ) {
774 this.parentPage = this.element.closest( ".ui-page" );
775 // Make sub page, and move the virtuallist into it...
776 // NOTE: check this subroutine.
777 this._createSubPages();
779 var o = this.options,
780 $list = this.element,
782 dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme,
783 listsplittheme = $list.jqmData( "splittheme" ),
784 listspliticon = $list.jqmData( "spliticon" ),
785 li = $list.children( "li" ),
786 counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1,
801 $list.find( ".ui-li-dec" ).remove();
804 for ( pos = 0, numli = li.length; pos < numli; pos++ ) {
808 // If we're creating the element, we update it regardless
809 if ( create || !item.hasClass( "ui-li" ) ) {
810 itemTheme = item.jqmData( "theme" ) || o.theme;
811 a = item.children( "a" );
814 icon = item.jqmData( "icon" );
821 /* icon: a.length > 1 || icon === false ? false : icon || "arrow-r",*/
822 icon: false, /* Remove unnecessary arrow icon */
826 if ( ( icon != false ) && ( a.length == 1 ) ) {
827 item.addClass( "ui-li-has-arrow" );
830 a.first().addClass( "ui-link-inherit" );
832 if ( a.length > 1 ) {
833 itemClass += " ui-li-has-alt";
836 splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme;
839 .attr( "title", last.getEncodedText() )
840 .addClass( "ui-li-link-alt" )
849 .find( ".ui-btn-inner" )
851 $( "<span />" ).buttonMarkup({
856 icon: listspliticon || last.jqmData( "icon" ) || o.splitIcon
860 } else if ( item.jqmData( "role" ) === "list-divider" ) {
862 itemClass += " ui-li-divider ui-btn ui-bar-" + dividertheme;
863 item.attr( "role", "heading" );
865 //reset counter when a divider heading is encountered
871 itemClass += " ui-li-static ui-body-" + itemTheme;
875 if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
876 countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
878 countParent.addClass( "ui-li-jsnumbering" )
879 .prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" );
882 item.add( item.children( ".ui-btn-inner" ) ).addClass( itemClass );
884 self._itemApply( $list, item );
887 this._refreshCorners( create );
890 //create a string for ID/subpage url creation
891 _idStringEscape: function ( str ) {
892 return str.replace(/\W/g , "-");
896 // this virtuallistview object
897 _createSubPages: function () {
898 var parentList = this.element,
899 parentPage = parentList.closest( ".ui-page" ),
900 parentUrl = parentPage.jqmData( "url" ),
901 parentId = parentUrl || parentPage[ 0 ][ $.expando ],
902 parentListId = parentList.attr( "id" ),
904 dns = "data-" + $.mobile.ns,
906 persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
910 if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
911 listCountPerPage[ parentId ] = -1;
914 parentListId = parentListId || ++listCountPerPage[ parentId ];
916 $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function ( i ) {
919 listId = list.attr( "id" ) || parentListId + "-" + i,
920 parent = list.parent(),
922 title = nodeEls.first().getEncodedText(),//url limits to first 30 chars of text
923 id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
924 theme = list.jqmData( "theme" ) || o.theme,
925 countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme,
929 nodeEls = $( list.prevAll().toArray().reverse() );
930 nodeEls = nodeEls.length ? nodeEls : $( "<span>" + $.trim( parent.contents()[ 0 ].nodeValue ) + "</span>" );
932 //define hasSubPages for use in later removal
935 newPage = list.detach()
936 .wrap( "<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
938 .before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
939 .after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='" + persistentFooterID + "'>" ) : "" )
941 .appendTo( $.mobile.pageContainer );
945 anchor = parent.find('a:first');
947 if ( !anchor.length ) {
948 anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
951 anchor.attr( "href", "#" + id );
953 }).virtuallistview();
955 // on pagehide, remove any nested pages along with the parent page, as long as they aren't active
956 // and aren't embedded
958 parentPage.is( ":jqmData(external-page='true')" ) &&
959 parentPage.data( "page" ).options.domCache === false ) {
961 newRemove = function ( e, ui ) {
962 var nextPage = ui.nextPage, npURL;
965 npURL = nextPage.jqmData( "url" );
966 if ( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ) {
967 self.childPages().remove();
973 // unbind the original page remove and replace with our specialized version
975 .unbind( "pagehide.remove" )
976 .bind( "pagehide.remove", newRemove );
980 // TODO sort out a better way to track sub pages of the virtuallistview this is brittle
981 childPages: function () {
982 var parentUrl = this.parentPage.jqmData( "url" );
984 return $( ":jqmData(url^='" + parentUrl + "&" + $.mobile.subPageUrlKey + "')" );
988 //auto self-init widgets
989 $( document ).bind( "pagecreate create", function ( e ) {
990 $( $.tizen.virtuallistview.prototype.options.initSelector, e.target ).virtuallistview();