1 /* ***************************************************************************
2 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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 * ***************************************************************************
23 * Author: Wongi Lee <wongi11.lee@samsung.com>
24 * Youmin Ha <youmin.ha@samsung.com>
28 * Virtual List Widget for unlimited data.
29 * To support more then 1,000 items, special list widget developed.
30 * Fast initialize and light DOM tree.
31 * DB connection and works like DB cursor.
35 * data-role: virtuallistview
36 * data-template : jQuery.template ID that populate into virtual list
37 * data-row : Optional. Set number of <li> elements that are used for data handling.
39 * ID : <UL> element that has "data-role=virtuallist" must have ID attribute.
44 * itemData: function ( idx ) { return json_obj; },
45 * numItemData: number or function () { return number; },
46 * cacheItemData: function ( minIdx, maxIdx ) {}
48 * : Create a virtuallist widget. At this moment, _create method is called.
49 * args : A collection of options
50 * itemData: A function that returns JSON object for given index. Mandatory.
51 * numItemData: Total number of itemData. Mandatory.
52 * cacheItemData: Virtuallist will ask itemData between minIdx and maxIdx.
53 * Developers can implement this function for preparing data.
58 * touchstart : Temporary preventDefault applied on touchstart event to avoid broken screen.
62 * <script id="tmp-3-2-7" type="text/x-jquery-tmpl">
63 * <li class="ui-li-3-2-7">
64 * <span class="ui-li-text-main">${NAME}</span>
65 * <img src="00_winset_icon_favorite_on.png" class="ui-li-icon-sub">
66 * <span class="ui-li-text-sub">${ACTIVE}</span>
67 * <span class="ui-li-text-sub2">${FROM}</span>
71 * <ul id="virtuallist-normal_3_2_7_ul" data-role="virtuallistview" data-template="tmp-3-2-7" data-dbtable="JSON_DATA" data-row="100">
78 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.
80 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.
82 To add a virtual list widget to the application, use the following code:
84 <script id="tmp-3-2-7" type="text/x-jquery-tmpl">
85 <li class="ui-li-3-2-7">
86 <span class="ui-li-text-main">${NAME}</span>
87 <img src="00_winset_icon_favorite_on.png" class="ui-li-icon-sub"/>
88 <span class="ui-li-text-sub">${ACTIVE}</span>
89 <span class="ui-li-text-sub2">${FROM}</span>
92 <ul id="vlist" data-role="virtuallistview" data-template="tmp-3-2-7" data-dbtable="JSON_DATA" data-row="100"></ul>
95 @property {String} data-role
96 Creates the virtual list view. The value must be set to virtuallistview.
97 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.
100 @property {String} data-template
101 Defines the jQuery.template element ID.
102 The jQuery.template must be defined. The template style can use rem units to support scalability.
105 @property {Number} data-row
106 Defines the number of virtual list child elements.
107 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.
111 @param {function} itemData(index)
112 : function itemData(index) returns the JSON object matched with the given index. The index value is between 0 and numItemData-1.
113 @param {Number} numItemData
114 : number numItemData or function numItemData() defines or returns a static number of items.
115 @param {function} cacheItemData(minIndex, maxIndex)
116 : function cacheItemData(minIndex, maxIndex) prepares the JSON data. This method is called before calling the itemData() method with index values between minIndex and maxIndex.
119 (function ( $, undefined ) {
121 /* Code for Virtual List Demo */
122 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 */
123 _NO_SCROLL = 0, /* ENUM */
124 _SCROLL_DOWN = 1, /* ENUM */
125 _SCROLL_UP = -1; /* ENUM */
127 $.widget( "tizen.virtuallistview", $.mobile.widget, {
133 splitIcon: "arrow-r",
136 id: "", /* Virtual list UL elemet's ID */
137 childSelector: " li", /* To support swipe list */
140 dbkey: false, /* Data's unique Key */
144 initSelector: ":jqmData(role='virtuallistview')"
147 _stylerMouseUp: function () {
148 $( this ).addClass( "ui-btn-up-s" );
149 $( this ).removeClass( "ui-btn-down-s" );
152 _stylerMouseDown: function () {
153 $( this ).addClass( "ui-btn-down-s" );
154 $( this ).removeClass( "ui-btn-up-s" );
157 _stylerMouseOver: function () {
158 $( this ).toggleClass( "ui-btn-hover-s" );
161 _stylerMouseOut: function () {
162 $( this ).toggleClass( "ui-btn-hover-s" );
163 $( this ).addClass( "ui-btn-up-s" );
164 $( this ).removeClass( "ui-btn-down-s" );
168 // this virtuallistview object
169 // @param[in] template template name(string)
170 _pushData: function ( template ) {
171 var o = this.options,
173 myTemplate = $( "#" + template ), // Get template object
174 // NOTE: o.row = # of rows handled at once. Default value is 100.
175 lastIndex = ( o.row > this._numItemData ? this._numItemData : o.row ), // last index of handled data
178 for ( i = 0; i < lastIndex; i++ ) {
179 htmlData = myTemplate.tmpl( this._itemData( i ) ); // Make rows with template,
180 $( o.id ).append( $( htmlData ).attr( 'id', o.itemIDPrefix + i ) ); // and append it to the vlist object
183 // After pushing data re-style virtuallist widget
184 $( o.id ).trigger( "create" );
187 // Set children <li> elements' position
189 // this: virtuallist element
190 // event: virtuallistview.options
191 // TODO: Why this arg name is 'event'? Not resonable.
192 // (this function is not called with event element as args!)
193 _reposition: function ( event ) {
204 if ( $( o.id + o.childSelector ).size() > 0 ) { // $("#vlistid li")
205 // first child's top position
206 // NOTE: the first element may not be '0'!!!
207 t._title_h = $( o.id + o.childSelector + ':first' ).position().top;
208 // first child's outer height (TODO: reuse selected items)
209 t._line_h = $( o.id + o.childSelector + ':first' ).outerHeight();
211 // container(vlist element)'s innerwidth
212 t._container_w = $( o.id ).innerWidth();
214 // get sum of container's left/right padding
215 padding = parseInt( $( o.id + o.childSelector ).css( "padding-left" ), 10 )
216 + parseInt( $( o.id + o.childSelector ).css( "padding-right" ), 10 );
218 // Add CSS to all <li> elements
219 // * absolute position
221 // * mouse up/down/over/out styles
222 $( o.id + ">" + o.childSelector )
223 .addClass( "position_absolute" )
224 .addClass( "ui-btn-up-s" )
225 .bind( "mouseup", t._stylerMouseUp )
226 .bind( "mousedown", t._stylerMouseDown )
227 .bind( "mouseover", t._stylerMouseOver )
228 .bind( "mouseout", t._stylerMouseOut );
231 // Set absolute top/left position of each <li>
232 $( o.id + ">" + o.childSelector ).each( function ( index ) {
233 margin = parseInt( $( this ).css( "margin-left" ), 10 )
234 + parseInt( $( this ).css( "margin-right" ), 10 );
236 $( this ).css( "top", t._title_h + t._line_h * index + 'px' )
237 .css( "width", t._container_w - padding - margin );
240 // Set Max Listview Height
241 $( o.id ).height( t._numItemData * t._line_h );
244 _resize: function ( event ) {
256 t._container_w = $( o.id ).innerWidth();
258 padding = parseInt( $( o.id + o.childSelector ).css( "padding-left" ), 10 )
259 + parseInt( $( o.id + o.childSelector ).css( "padding-right" ), 10 );
261 $( o.id + o.childSelector ).each( function (index) {
262 margin = parseInt( $( this ).css( "margin-left" ), 10 )
263 + parseInt( $( this ).css( "margin-right" ), 10 );
264 $( this ).css( "width", t._container_w - padding - margin );
268 // New scrollmove function supporting scrollTo
269 _scrollmove: function ( ev ) {
270 var t = ev.data, // vlist (JQM object)
271 o = t.options, // options
272 prevTopBufLen = t._num_top_items, // Previous(remembered) top buf length
279 viewTop: function ( ) {
280 var sv = $( o.id ).parentsUntil( ".ui-page" ).find( ".ui-scrollview-view" ),
281 svTrans = sv.css( "-webkit-transform" ),
282 svTransVal = "0,0,0,0,0,0";
284 svTransVal = svTrans.replace( /matrix\s*\((.*)\)/, "$1" ); // matrix(a,c,b,d,tx,ty)
286 return - parseInt( svTransVal.split(',')[5], 10 );
290 viewTop: function ( ) {
291 return $( window ).scrollTop( ); // TODO: - _line_h?
294 // Get current view top position
295 function viewTop ( ) {
296 return o.scrollview ? _scrollView.viewTop() : _normalScroll.viewTop();
298 // log function for debug
299 function log ( msg ) {
302 console.log( ">>virtualllist: " + msg );
306 // Timer interval function
307 // @param[in] vl virtuallist object (JQM object)
308 function timerMove ( vl, undefined ) {
309 var cy, // current y position
310 cti, // current top idx
311 cbi, // current bottom idx
312 oti = vl._first_index, // old top idx
313 obi = vl._last_index, // old botton idx
314 dti, // delta of top idx
316 toIdx, // index range to be moved
317 delta, // moveItem delta
318 rowLen = vl.options.row, // max. # of items handled at once
319 bufSize, // top/bottom buffer size. unit: # of items
322 // subroutine: Move itemContents in i2 into i1
323 function moveItemContents( vl, i1, i2 ) {
324 // TODO: Find a efficient way to replace data!
325 // Assumption: i1 and i2 has same children.
326 var NODETYPE = { ELEMENT_NODE: 1, TEXT_NODE: 3 },
327 c1, // child item 1 (old)
328 c2, // child item 2 (new)
333 $( i1 ).find( ".ui-li-text-main", ".ui-li-text-sub", ".ui-li-text-sub2", "ui-btn-text" ).each( function ( index ) {
335 newText = $( i2 ).find( ".ui-li-text-main", ".ui-li-text-sub", "ui-btn-text" ).eq( index ).text();
337 $( c1 ).contents().filter( function () {
338 return ( this.nodeType == NODETYPE.TEXT_NODE );
339 } ).get( 0 ).data = newText;
342 $( i1 ).find( "img" ).each( function ( imgIdx ) {
344 newImg = $( i2 ).find( "img" ).eq( imgIdx ).attr( "src" );
346 $( c1 ).attr( "src", newImg );
349 $( i1 ).removeData( ); // Clear old data
352 // subroutine: Move item
353 function moveItem( vl, fromIdx, toIdx ) {
354 var itemData, // data from itemData()
355 item, // item element
356 newItem, // new item element
359 log( ">> move item: " + fromIdx + " --> " + toIdx );
362 item = $( '#' + vl.options.itemIDPrefix + fromIdx ); // TODO: refactor ID generation!
363 if ( ! item || ! item.length ) {
368 tmpl = $( "#" + vl.options.template );
370 newItem = tmpl.tmpl( vl._itemData( toIdx ) );
372 // TODO: Consider touch block while moving?
374 // Move item contents
375 moveItemContents( vl, item, newItem );
377 // clean up temporary item
381 // Move position, and set id
382 item.css( 'top', toIdx * vl._line_h )
383 .attr( 'id' , vl.options.itemIDPrefix + toIdx ); // TODO: refactor ID generation!
385 // TODO: Apply jqmdata? check following old code;
386 // $( oldItem ).removeData( ); // Clear old data
387 // if (key) { $( oldItem ).data( key, $( newItem ).data( key ) ); }
393 // Get current view position
396 // Calculate bufSize: rowLen / 3
397 // NOTE: Assumption: total row length = visible items * 3 (upper+visible+lower)
398 bufSize = Math.ceil( rowLen / 3 );
400 // Calculate current top/bottom index (to be applied)
401 // top index = current position / line height
402 cti = Math.floor( cy / vl._line_h ) - bufSize; // TODO: consider buffer!
403 cbi = cti + rowLen - 1;
405 if ( cti < 0 ) { // Top boundary check
408 } else if ( cbi > ( vl._numItemData - 1 ) ) { // Bottom boundary check
409 cti -= ( cbi - ( vl._numItemData - 1 ) );
410 cbi = ( vl._numItemData - 1 );
415 log( "cy=" + cy + ", oti=" + oti + ", obi=" + obi + ", cti=" + cti + ", cbi=" + cbi + ", dti=" + dti );
417 // switch: dti = 0 --> timer stop condition: delta=0 or scrollstop event comes. END.
419 // Check timer runtime
420 vl.timerStillCount += 1;
421 if ( vl.timerStillCount < 12 ) { // check count ( TODO: test and adjust )
422 log("dti=0 " + vl.timerStillCount + " times");
423 vl.timerMoveID = setTimeout( timerMove, timerInterval, vl ); // run once more
427 log("dti=0 " + vl.timerStillCount + " times. End timer.");
428 vl.timerStillCount = 0;
430 if ( vl.timerMoveID ) {
431 clearTimeout( vl.timerMoveID );
432 vl.timerMoveID = null;
435 // switch: dti >= # of max elements --> total replace.
436 vl.timerStillCount = 0; // Reset still counter
438 if ( Math.abs( dti ) >= rowLen ) {
442 log( ">>> WHOLE CHANGE! delta=" + delta );
444 // switch: dti < # of max elements --> move t2b or b2t until new top/bottom idx is covered
447 toIdx = oti + dti - 1;
450 fromIdx = obi + dti + 1; // dti < 0
454 log( ">>> partial change. delta=" + delta );
458 for ( i = fromIdx; i <= toIdx; i++ ) {
459 moveItem( vl, i, i + delta ); // Change data and position
462 // Store current top/bottom idx into vl
463 vl._first_index = cti;
464 vl._last_index = cbi;
466 // Register timer to check again
467 vl.timerMoveID = setTimeout( timerMove, timerInterval, vl );
469 return; // End of function
472 // ==== function start ====
474 t.timerStillCount = 0; // Count do-nothing time. For behavior tuning.
476 // If a timer function is alive, clear it
477 if ( t.timerMoveID ) {
478 clearTimeout( t.timerMoveID );
479 t.timerMoveID = null;
485 _recreate: function ( newArray ) {
491 t._numItemData = newArray.length;
492 t._direction = _NO_SCROLL;
494 t._last_index = o.row - 1;
496 t._pushData( o.template );
498 if (o.childSelector == " ul" ) {
499 $( o.id + " ul" ).swipelist();
502 $( o.id ).virtuallistview();
509 // Init virtuallistview
510 // this virtuallistview object
511 _initList: function () {
515 /* After AJAX loading success */
517 // Put initial <li> elements
518 t._pushData( o.template );
520 // find a parent page, and run _reposition() at 'pageshow' event
521 // TODO: Consider replace parentsUntil().parent() to parent('.ui-page') ???
522 $( o.id ).parentsUntil( ".ui-page" ).parent().one( "pageshow", function () {
523 setTimeout( function () {
528 // Bind _scrollmove() at 'scrollstart.virtuallist' event
529 $( document ).bind( "scrollstart.virtuallist scrollstop.vrituallist", t, t._scrollmove );
531 // Bind _resize() at 'resize.virtuallist'
532 $( window ).bind( "resize.virtuallist", t._resize );
534 // when ul is a childselector, assume that this is also a swipelist,
535 // and run swipelist constructor
536 if ( o.childSelector == " ul" ) {
537 $( o.id + " ul" ).swipelist();
543 create: function () {
544 var o = this.options;
546 /* external API for AJAX callback */
547 this._create.apply( this, arguments );
549 // TODO: remove this line? _initList() calls reposition...
550 this._reposition( o );
553 _create: function ( args ) {
554 // Extend instance variables
556 _itemData : function ( idx ) { return null; },
558 _cacheItemData : function ( minIdx, maxIdx ) { },
562 _direction : _NO_SCROLL,
565 _num_top_items : 0 // By scroll move, number of hidden elements.
572 shortcutsContainer = $('<div class="ui-virtuallist"/>'),
573 shortcutsList = $('<ul></ul>'),
574 dividers = $el.find(':jqmData(role="virtuallistview" )'),
576 shortcutscroll = this,
581 // Add CSS classes to $el (=virtuallistview)
582 $el.addClass( function ( i, orig ) {
583 return orig + " ui-listview ui-virtual-list-container" + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" );
586 // keep the vlist's ID
587 o.itemIDPrefix = $el.attr( "id" ) + '_';
588 o.id = "#" + $el.attr( "id" );
590 // when page hides, empty all child elements
591 $( o.id ).bind( "pagehide", function ( e ) {
595 // Find if scrollview is used
596 if ( $( ".ui-scrollview-clip" ).size() > 0 ) {
599 o.scrollview = false;
602 // Calculate page buffer size
603 if ( $el.data( "row" ) ) {
604 o.row = $el.data( "row" );
606 if ( o.row < t._minimum_row ) {
607 o.row = t._minimum_row;
610 o.page_buf = parseInt( ( o.row / 2 ), 10 );
615 if ( args.itemData && typeof args.itemData == 'function' ) {
616 t._itemData = args.itemData;
620 if ( args.numItemData ) {
621 if ( typeof args.numItemData == 'function' ) {
622 t._numItemData = args.numItemData( );
623 } else if ( typeof args.numItemData == 'number' ) {
624 t._numItemData = args.numItemData;
631 } else { // No option is given
632 // Legacy support: dbtable
633 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!" );
635 /* After DB Load complete, Init Vritual list */
636 if ( $( o.id ).hasClass( "vlLoadSuccess" ) ) {
637 dbtable_name = $el.jqmData('dbtable');
638 dbtable = window[ dbtable_name ];
646 t._itemData = function ( idx ) {
647 return dbtable[ idx ];
649 t._numItemData = dbtable.length;
651 return; // Do nothing
656 if ( $el.data( "template" ) ) {
657 o.template = $el.data( "template" );
659 /* to support swipe list, <li> or <ul> can be main node of virtual list. */
660 if ( $el.data( "swipelist" ) == true ) {
661 o.childSelector = " ul";
663 o.childSelector = " li";
667 // Set data's unique key
668 // NOTE: Unnecessary?
669 if ( $el.data( "dbkey" ) ) {
670 o.dbkey = $el.data( "dbkey" );
673 t._first_index = 0; // initial top idx of <li> element.
674 t._last_index = o.row - 1; // initial bottom idx of <li> element.
675 t._initList(); // NOTE: Called at here only!
678 destroy : function () {
679 var o = this.options;
681 $( document ).unbind( "scrollstop" );
683 $( window ).unbind( "resize.virtuallist" );
687 if ( this.timerMoveID ) {
688 clearTimeout( this.timerMoveID );
689 this.timerMoveID = null;
693 _itemApply: function ( $list, item ) {
694 var $countli = item.find( ".ui-li-count" );
696 if ( $countli.length ) {
697 item.addClass( "ui-li-has-count" );
700 $countli.addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" );
702 // TODO class has to be defined in markup
703 item.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
704 .find( "p, dl" ).addClass( "ui-li-desc" ).end()
705 .find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each( function () {
706 item.addClass( $( this ).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
708 .find( ".ui-li-aside" ).each(function () {
709 var $this = $( this );
710 $this.prependTo( $this.parent() ); //shift aside to front for css float
714 _removeCorners: function ( li, which ) {
715 var top = "ui-corner-top ui-corner-tr ui-corner-tl",
716 bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
718 li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
720 if ( which === "top" ) {
721 li.removeClass( top );
722 } else if ( which === "bottom" ) {
723 li.removeClass( bot );
725 li.removeClass( top + " " + bot );
729 _refreshCorners: function ( create ) {
735 if ( this.options.inset ) {
736 $li = this.element.children( "li" );
737 // at create time the li are not visible yet so we need to rely on .ui-screen-hidden
738 $visibleli = create ? $li.not( ".ui-screen-hidden" ) : $li.filter( ":visible" );
740 this._removeCorners( $li );
742 // Select the first visible li element
743 $topli = $visibleli.first()
744 .addClass( "ui-corner-top" );
746 $topli.add( $topli.find( ".ui-btn-inner" ) )
747 .find( ".ui-li-link-alt" )
748 .addClass( "ui-corner-tr" )
750 .find( ".ui-li-thumb" )
751 .not( ".ui-li-icon" )
752 .addClass( "ui-corner-tl" );
754 // Select the last visible li element
755 $bottomli = $visibleli.last()
756 .addClass( "ui-corner-bottom" );
758 $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
759 .find( ".ui-li-link-alt" )
760 .addClass( "ui-corner-br" )
762 .find( ".ui-li-thumb" )
763 .not( ".ui-li-icon" )
764 .addClass( "ui-corner-bl" );
768 // this virtuallistview object
769 refresh: function ( create ) {
770 this.parentPage = this.element.closest( ".ui-page" );
771 // Make sub page, and move the virtuallist into it...
772 // NOTE: check this subroutine.
773 this._createSubPages();
775 var o = this.options,
776 $list = this.element,
778 dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme,
779 listsplittheme = $list.jqmData( "splittheme" ),
780 listspliticon = $list.jqmData( "spliticon" ),
781 li = $list.children( "li" ),
782 counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1,
797 $list.find( ".ui-li-dec" ).remove();
800 for ( pos = 0, numli = li.length; pos < numli; pos++ ) {
804 // If we're creating the element, we update it regardless
805 if ( create || !item.hasClass( "ui-li" ) ) {
806 itemTheme = item.jqmData( "theme" ) || o.theme;
807 a = item.children( "a" );
810 icon = item.jqmData( "icon" );
817 /* icon: a.length > 1 || icon === false ? false : icon || "arrow-r",*/
818 icon: false, /* Remove unnecessary arrow icon */
822 if ( ( icon != false ) && ( a.length == 1 ) ) {
823 item.addClass( "ui-li-has-arrow" );
826 a.first().addClass( "ui-link-inherit" );
828 if ( a.length > 1 ) {
829 itemClass += " ui-li-has-alt";
832 splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme;
835 .attr( "title", last.getEncodedText() )
836 .addClass( "ui-li-link-alt" )
845 .find( ".ui-btn-inner" )
847 $( "<span />" ).buttonMarkup({
852 icon: listspliticon || last.jqmData( "icon" ) || o.splitIcon
856 } else if ( item.jqmData( "role" ) === "list-divider" ) {
858 itemClass += " ui-li-divider ui-btn ui-bar-" + dividertheme;
859 item.attr( "role", "heading" );
861 //reset counter when a divider heading is encountered
867 itemClass += " ui-li-static ui-body-" + itemTheme;
871 if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
872 countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
874 countParent.addClass( "ui-li-jsnumbering" )
875 .prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" );
878 item.add( item.children( ".ui-btn-inner" ) ).addClass( itemClass );
880 self._itemApply( $list, item );
883 this._refreshCorners( create );
886 //create a string for ID/subpage url creation
887 _idStringEscape: function ( str ) {
888 return str.replace(/\W/g , "-");
892 // this virtuallistview object
893 _createSubPages: function () {
894 var parentList = this.element,
895 parentPage = parentList.closest( ".ui-page" ),
896 parentUrl = parentPage.jqmData( "url" ),
897 parentId = parentUrl || parentPage[ 0 ][ $.expando ],
898 parentListId = parentList.attr( "id" ),
900 dns = "data-" + $.mobile.ns,
902 persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
906 if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
907 listCountPerPage[ parentId ] = -1;
910 parentListId = parentListId || ++listCountPerPage[ parentId ];
912 $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function ( i ) {
915 listId = list.attr( "id" ) || parentListId + "-" + i,
916 parent = list.parent(),
918 title = nodeEls.first().getEncodedText(),//url limits to first 30 chars of text
919 id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
920 theme = list.jqmData( "theme" ) || o.theme,
921 countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme,
925 nodeEls = $( list.prevAll().toArray().reverse() );
926 nodeEls = nodeEls.length ? nodeEls : $( "<span>" + $.trim( parent.contents()[ 0 ].nodeValue ) + "</span>" );
928 //define hasSubPages for use in later removal
931 newPage = list.detach()
932 .wrap( "<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
934 .before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
935 .after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='" + persistentFooterID + "'>" ) : "" )
937 .appendTo( $.mobile.pageContainer );
941 anchor = parent.find('a:first');
943 if ( !anchor.length ) {
944 anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
947 anchor.attr( "href", "#" + id );
949 }).virtuallistview();
951 // on pagehide, remove any nested pages along with the parent page, as long as they aren't active
952 // and aren't embedded
954 parentPage.is( ":jqmData(external-page='true')" ) &&
955 parentPage.data( "page" ).options.domCache === false ) {
957 newRemove = function ( e, ui ) {
958 var nextPage = ui.nextPage, npURL;
961 npURL = nextPage.jqmData( "url" );
962 if ( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ) {
963 self.childPages().remove();
969 // unbind the original page remove and replace with our specialized version
971 .unbind( "pagehide.remove" )
972 .bind( "pagehide.remove", newRemove );
976 // TODO sort out a better way to track sub pages of the virtuallistview this is brittle
977 childPages: function () {
978 var parentUrl = this.parentPage.jqmData( "url" );
980 return $( ":jqmData(url^='" + parentUrl + "&" + $.mobile.subPageUrlKey + "')" );
984 //auto self-init widgets
985 $( document ).bind( "pagecreate create", function ( e ) {
986 $( $.tizen.virtuallistview.prototype.options.initSelector, e.target ).virtuallistview();