Tizen 2.0 Release
[platform/framework/web/web-ui-fw.git] / src / widgets / extendablelist / js / jquery.mobile.tizen.extendablelist.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  *      Extendable List Widget for unlimited data.
28  *      To support more then 1,000 items, special list widget developed.
29  *      Fast initialize and append some element into the DOM tree repeatedly.
30  *      DB connection and works like DB cursor.
31  *
32  * HTML Attributes:
33  *
34  *              data-role:      extendablelist
35  *              data-template : jQuery.template ID that populate into extendable list. A button : a <DIV> element with "data-role : button" should be included on data-template.
36  *              data-dbtable : DB Table name. It used as window[DB NAME]. Loaded data should be converted as window object.
37  *              data-extenditems : Number of elements to extend at once.
38  *              
39  *              ID : <UL> element that has "data-role=extendablelist" must have ID attribute.
40  *              Class : <UL> element that has "data-role=extendablelist" should have "vlLoadSuccess" class to guaranty DB loading is completed.
41  *              tmp_load_more : Template ID for "load more" message and button.
42  *
43  *
44  *APIs:
45  *              create ( {
46  *                              itemData: function ( idx ) { return json_obj; },
47  *                              numItemData: number or function () { return number; },
48  *                              cacheItemData: function ( minIdx, maxIdx ) {}
49  *                              } )
50  *                      : Create a extendable list widget. At this moment, _create method is called.
51  *                      args : A collection of options
52  *                              itemData: A function that returns JSON object for given index. Mandatory.
53  *                              numItemData: Total number of itemData. Mandatory.
54  *                              cacheItemData: Extendable list will ask itemData between minIdx and maxIdx.
55  *                                  Developers can implement this function for preparing data.
56  *                                  Optional.
57  *
58  *Examples:
59  *
60  *              <script id="tmp-3-1-1" type="text/x-jquery-tmpl">
61  *                      <li class="ui-li-3-1-1"><span class="ui-li-text-main">${NAME}</span></li>
62  *              </script>
63  *
64  *              <script id="tmp_load_more" type="text/x-jquery-tmpl"> 
65  *                      <li class="ui-li-3-1-1" style="text-align:center; margin:0 auto">
66  *                              <div data-role="button">Load ${NUM_MORE_ITEMS} more items</div>
67  *                      </li>
68  *              </script>
69  *      
70  *              <ul id = "extendable_list_main" data-role="extendablelist" data-extenditems="50" data-template="tmp-3-1-1">
71  *              </ul>
72  *
73  */
74
75 /**
76         @class Extendablelist
77         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.
78         The extendable list widget is used to display a list of unlimited data elements on the screen for better performance. The list is extended if you click the button at the bottom of the list to load more data elements. Extendable lists are based on the jQuery.template plugin as described in the jQuery documentation for jQuery.template plugin.<br/>
79         To add a extendable list widget to the application, use the following code:
80
81                         <script id="tmp-3-1-1" type="text/x-jquery-tmpl">
82                                 <li class="ui-li-3-1-1"><span class="ui-li-text-main">${NAME}</span></li>
83                         </script>
84                         <script id="tmp_load_more" type="text/x-jquery-tmpl">
85                                 <li class="ui-li-3-1-1" style="text-align:center; margin:0 auto">
86                                 <div data-role="button">Load ${NUM_MORE_ITEMS} more items</div>
87                                 </li>
88                         </script>
89                         <ul id="extendable_list_main" data-role="extendablelist" data-extenditems="50" data-template="tmp-3-1-1">
90                         </ul>
91 */
92 /**
93         @property {String} data-role
94         Creates the extendable list view. The value must be set to extendablelist. Only the &lt;ul&gt; element, which a id attribute defined, supports this option. Also, the elLoadSuccess class attribute must be defined in the &lt;ul&gt; element to ensure that loading data from the database is complete.
95 */
96 /**
97         @property {String} data-template
98         Specifies the jQuery.template element ID. The jQuery.template must be defined. The template style can use rem units to support scalability. For using the button at the bottom of the list to load more data elements, there must be list view template with the button. The attribute ID must be tmp_load_more.
99 */
100 /**
101         @property {Integer} data-extenditems
102         Defines the number of data elements to be extended at a time.
103 */
104 ( function ( $, undefined ) {
105
106         //Keeps track of the number of lists per page UID
107         //This allows support for multiple nested list in the same page
108         //https://github.com/jquery/jquery-mobile/issues/1617
109         var listCountPerPage = {};
110
111         $.widget( "tizen.extendablelist", $.mobile.widget, {
112                 options: {
113                         theme: "s",
114                         countTheme: "c",
115                         headerTheme: "b",
116                         dividerTheme: "b",
117                         splitIcon: "arrow-r",
118                         splitTheme: "b",
119                         inset: false,
120                         id:     "",                                             /* Extendable list UL elemet's ID */
121                         extenditems: 50,                        /* Number of append items */
122                         childSelector: " li",           /* To support swipe list */
123                         dbtable: "",
124                         template : "",                          /* Template for each list item */
125                         loadmore : "tmp_load_more",     /* Template for "Load more" message */
126                         scrollview: false,
127                         initSelector: ":jqmData(role='extendablelist')"
128                 },
129
130                 _stylerMouseUp: function () {
131                         $( this ).addClass( "ui-btn-up-s" );
132                         $( this ).removeClass( "ui-btn-down-s" );
133                 },
134
135                 _stylerMouseDown: function () {
136                         $( this ).addClass( "ui-btn-down-s" );
137                         $( this ).removeClass( "ui-btn-up-s" );
138                 },
139
140                 _stylerMouseOver: function () {
141                         $( this ).toggleClass( "ui-btn-hover-s" );
142                 },
143
144                 _stylerMouseOut: function () {
145                         $( this ).toggleClass( "ui-btn-hover-s" );
146                         $( this ).addClass( "ui-btn-up-s" );
147                         $( this ).removeClass( "ui-btn-down-s" );
148                 },
149
150                 _pushData: function ( template ) {
151                         var o = this.options,
152                                 t = this,
153                                 i = 0,
154                                 myTemplate = $( "#" + template ),
155                                 loadMoreItems = ( o.extenditems > t._numItemData - t._lastIndex ? t._numItemData - t.lastIndex : o.extenditems ),
156                                 htmlData;
157
158                         for (i = 0; i < loadMoreItems; i++ ) {
159                                 htmlData = myTemplate.tmpl( t._itemData( i ) );
160                                 $( o.id ).append( $( htmlData ).attr( 'id', 'li_' + i ) );
161
162                                 /* Add style */
163                                 $( o.id + ">" + o.childSelector )
164                                         .addClass( "ui-btn-up-s" )
165                                         .bind( "mouseup", t._stylerMouseUp )
166                                         .bind( "mousedown", t._stylerMouseDown )
167                                         .bind( "mouseover", t._stylerMouseOver )
168                                         .bind( "mouseout", t._stylerMouseOut );
169
170                                 t._lastIndex += 1;
171                         }
172
173                         /* After push data, re-style extendable list widget */
174                         $( o.id ).trigger( "create" );
175                 },
176
177                 _loadmore: function ( event ) {
178                         var t = event.data,     // <li> element
179                                 o = t.options,
180                                 i = 0,
181                                 myTemplate = $( "#" + o.template ),
182                                 loadMoreItems = ( o.extenditems > t._numItemData - t._lastIndex ? t._numItemData - t._lastIndex : o.extenditems ),
183                                 htmlData,
184                                 more_items_to_load,
185                                 num_next_load_items;
186
187                         /* Remove load more message */
188                         $( "#load_more_message" ).remove();
189
190                         /* Append More Items */
191                         for ( i = 0; i < loadMoreItems; i++ ) {
192                                 htmlData = myTemplate.tmpl( t._itemData( t._lastIndex ) );
193                                 $( o.id ).append( $( htmlData ).attr( 'id', 'li_' + t._lastIndex ) );
194                                 t._lastIndex += 1;
195                         }
196
197                         /* Append "Load more" message on the last of list */
198                         if ( t._numItemData > t._lastIndex ) {
199                                 myTemplate = $( "#" + o.loadmore );
200                                 more_items_to_load = t._numItemData - t._lastIndex;
201                                 num_next_load_items = ( o.extenditems <= more_items_to_load ) ? o.extenditems : more_items_to_load;
202                                 htmlData = myTemplate.tmpl( { NUM_MORE_ITEMS : num_next_load_items } );
203
204                                 $( o.id ).append( $( htmlData ).attr( 'id', "load_more_message" ) );
205                         }
206
207                         $( o.id ).trigger( "create" );
208                         $( o.id ).extendablelist( "refresh" );
209                 },
210
211                 recreate: function ( newArray ) {
212                         this._create( {
213                                 itemData: function ( idx ) { return newArray[ idx ]; },
214                                 numItemData: newArray.length
215                         } );
216                 },
217
218                 _initList: function (args ) {
219                         var t = this,
220                                 o = this.options,
221                                 myTemplate,
222                                 more_items_to_load,
223                                 num_next_load_items,
224                                 htmlData;
225
226                         /* Make Gen list by template */
227                         if ( t._lastIndex <= 0 ) {
228                                 t._pushData( o.template );
229
230                                 /* Append "Load more" message on the last of list */
231                                 if ( t._numItemData > t._lastIndex ) {
232                                         myTemplate = $( "#" + o.loadmore );
233                                         more_items_to_load = t._numItemData - t._lastIndex;
234                                         num_next_load_items = ( o.extenditems <= more_items_to_load) ? o.extenditems : more_items_to_load;
235                                         htmlData = myTemplate.tmpl( { NUM_MORE_ITEMS : num_next_load_items } );
236
237                                         $( o.id ).append( $( htmlData ).attr( 'id', "load_more_message" ) );
238
239                                         $( "#load_more_message" ).live( "click", t, t._loadmore );
240                                 } else {
241                                         /* No more items to load */
242                                         $( "#load_more_message" ).die();
243                                         $( "#load_more_message" ).remove();
244                                 }
245                         }
246
247                         if ( o.childSelector == " ul" ) {
248                                 $( o.id + " ul" ).swipelist();
249                         }
250
251                         $( o.id ).trigger( "create" );
252
253                         t.refresh( true );
254                 },
255
256                 create: function () {
257                         var o = this.options;
258
259                         /* external API for AJAX callback */
260                         this._create.apply( this, arguments );
261                 },
262
263                 _create: function ( args ) {
264                         var t = this,
265                                 o = this.options,
266                                 $el = this.element,
267                                 dbtable_name;
268
269
270                         t.destroy();
271
272                         $.extend(this, {
273                                 _itemData: function ( idx ) { return null; },
274                                 _numItemData: 0,
275                                 _cacheItemData: function ( minIdx, maxIdx ) { },
276                                 _lastIndex: 0
277                         });
278
279
280                         // create listview markup
281                         t.element.addClass( function ( i, orig ) {
282                                 return orig + " ui-listview ui-extendable-list-container" + ( t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "" );
283                         });
284
285                         o.id = "#" + $el.attr( "id" );
286
287                         if ( $el.data( "extenditems" ) ) {
288                                 o.extenditems = parseInt( $el.data( "extenditems" ), 10 );
289                         }
290
291                         $( o.id ).bind( "pagehide", function (e) {
292                                 $( o.id ).empty();
293                         });
294
295                         /* Scroll view */
296                         if ( $( ".ui-scrollview-clip" ).size() > 0) {
297                                 o.scrollview = true;
298                         } else {
299                                 o.scrollview = false;
300                         }
301
302                         if ( args ) {
303                                 if ( !t._loadData( args ) ) {
304                                         return;
305                                 }
306                         } else {
307                                 // Legacy support: dbtable
308                                 console.warn("WARNING: The data interface of extendable list is changed. \nOld data interface(data-dbtable) is still supported, but will be removed in next version. \nPlease fix your code soon!");
309
310                                 if ( $( o.id ).hasClass( "elLoadSuccess" ) ) {
311                                         dbtable_name = $el.jqmData('dbtable');
312                                         o.dbtable = window[ dbtable_name ];
313                                         if ( !(o.dbtable) ) {
314                                                 o.dbtable = { };
315                                         }
316                                         t._itemData = function ( idx ) {
317                                                 return o.dbtable[ idx ];
318                                         };
319                                         t._numItemData = o.dbtable.length;
320
321                                 } else {
322                                         console.warn("No elLoadSuccess class");
323                                         return;
324                                 }
325                         }
326
327                         if ( $el.data( "template" ) ) {
328                                 o.template = $el.data( "template" );
329
330                                 /* to support swipe list, <li> or <ul> can be main node of extendable list. */
331                                 if ( $el.data( "swipelist" ) == true ) {
332                                         o.childSelector = " ul";
333                                 } else {
334                                         o.shildSelector = " li";
335                                 }
336                         }
337                         t._initList( args );
338                 },
339
340                 _loadData : function ( args ) {
341                         var self = this;
342
343                         if ( args.itemData && typeof args.itemData == 'function'  ) {
344                                 self._itemData = args.itemData;
345                         } else {
346                                 return false;
347                         }
348                         if ( args.numItemData ) {
349                                 if ( typeof args.numItemData == 'function' ) {
350                                         self._numItemData = args.numItemData( );
351                                 } else if ( typeof args.numItemData == 'number' ) {
352                                         self._numItemData = args.numItemData;
353                                 } else {
354                                         return false;
355                                 }
356                         } else {
357                                 return false;
358                         }
359                         return true;
360                 },
361
362
363                 destroy : function () {
364                         var o = this.options,
365                                 eOTAL_ITEMS = 0,
366                                 last_index = 0;
367
368                         $( o.id ).empty();
369
370                         $( "#load_more_message" ).die();
371                 },
372
373                 _itemApply: function ( $list, item ) {
374                         var $countli = item.find( ".ui-li-count" );
375
376                         if ( $countli.length ) {
377                                 item.addClass( "ui-li-has-count" );
378                         }
379
380                         $countli.addClass( "ui-btn-up-" + ( $list.jqmData( "counttheme" ) || this.options.countTheme ) + " ui-btn-corner-all" );
381
382                         // TODO class has to be defined in markup
383                         item.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" ).end()
384                                 .find( "p, dl" ).addClass( "ui-li-desc" ).end()
385                                 .find( ">img:eq(0), .ui-link-inherit>img:eq(0)" ).addClass( "ui-li-thumb" ).each(function () {
386                                         item.addClass( $( this ).is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
387                                 }).end()
388                                 .find( ".ui-li-aside" ).each(function () {
389                                         var $this = $( this );
390                                         $this.prependTo( $this.parent() ); //shift aside to front for css float
391                                 });
392                 },
393
394                 _removeCorners: function ( li, which ) {
395                         var top = "ui-corner-top ui-corner-tr ui-corner-tl",
396                                 bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
397
398                         li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
399
400                         if ( which === "top" ) {
401                                 li.removeClass( top );
402                         } else if ( which === "bottom" ) {
403                                 li.removeClass( bot );
404                         } else {
405                                 li.removeClass( top + " " + bot );
406                         }
407                 },
408
409                 _refreshCorners: function ( create ) {
410                         var $li,
411                                 $visibleli,
412                                 $topli,
413                                 $bottomli;
414
415                         if ( this.options.inset ) {
416                                 $li = this.element.children( "li" );
417                                 // at create time the li are not visible yet so we need to rely on .ui-screen-hidden
418                                 $visibleli = create ? $li.not( ".ui-screen-hidden" ) : $li.filter( ":visible" );
419
420                                 this._removeCorners( $li );
421
422                                 // Select the first visible li element
423                                 $topli = $visibleli.first()
424                                         .addClass( "ui-corner-top" );
425
426                                 $topli.add( $topli.find( ".ui-btn-inner" ) )
427                                         .find( ".ui-li-link-alt" )
428                                                 .addClass( "ui-corner-tr" )
429                                         .end()
430                                         .find( ".ui-li-thumb" )
431                                                 .not( ".ui-li-icon" )
432                                                 .addClass( "ui-corner-tl" );
433
434                                 // Select the last visible li element
435                                 $bottomli = $visibleli.last()
436                                         .addClass( "ui-corner-bottom" );
437
438                                 $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
439                                         .find( ".ui-li-link-alt" )
440                                                 .addClass( "ui-corner-br" )
441                                         .end()
442                                         .find( ".ui-li-thumb" )
443                                                 .not( ".ui-li-icon" )
444                                                 .addClass( "ui-corner-bl" );
445                         }
446                 },
447
448                 refresh: function ( create ) {
449                         this.parentPage = this.element.closest( ".ui-page" );
450                         this._createSubPages();
451
452                         var o = this.options,
453                                 $list = this.element,
454                                 self = this,
455                                 dividertheme = $list.jqmData( "dividertheme" ) || o.dividerTheme,
456                                 listsplittheme = $list.jqmData( "splittheme" ),
457                                 listspliticon = $list.jqmData( "spliticon" ),
458                                 li = $list.children( "li" ),
459                                 counter = $.support.cssPseudoElement || !$.nodeName( $list[ 0 ], "ol" ) ? 0 : 1,
460                                 item,
461                                 itemClass,
462                                 itemTheme,
463                                 a,
464                                 last,
465                                 splittheme,
466                                 countParent,
467                                 icon,
468                                 pos,
469                                 numli;
470
471                         if ( counter ) {
472                                 $list.find( ".ui-li-dec" ).remove();
473                         }
474
475                         for ( pos = 0, numli = li.length; pos < numli; pos++ ) {
476                                 item = li.eq( pos );
477                                 itemClass = "ui-li";
478
479                                 // If we're creating the element, we update it regardless
480                                 if ( create || !item.hasClass( "ui-li" ) ) {
481                                         itemTheme = item.jqmData( "theme" ) || o.theme;
482                                         a = item.children( "a" );
483
484                                         if ( a.length ) {
485                                                 icon = item.jqmData( "icon" );
486
487                                                 item.buttonMarkup({
488                                                         wrapperEls: "div",
489                                                         shadow: false,
490                                                         corners: false,
491                                                         iconpos: "right",
492                                                         /* icon: a.length > 1 || icon === false ? false : icon || "arrow-r",*/
493                                                         icon: false,    /* Remove unnecessary arrow icon */
494                                                         theme: itemTheme
495                                                 });
496
497                                                 if ( ( icon != false ) && ( a.length == 1 ) ) {
498                                                         item.addClass( "ui-li-has-arrow" );
499                                                 }
500
501                                                 a.first().addClass( "ui-link-inherit" );
502
503                                                 if ( a.length > 1 ) {
504                                                         itemClass += " ui-li-has-alt";
505
506                                                         last = a.last();
507                                                         splittheme = listsplittheme || last.jqmData( "theme" ) || o.splitTheme;
508
509                                                         last.appendTo(item)
510                                                                 .attr( "title", last.getEncodedText() )
511                                                                 .addClass( "ui-li-link-alt" )
512                                                                 .empty()
513                                                                 .buttonMarkup({
514                                                                         shadow: false,
515                                                                         corners: false,
516                                                                         theme: itemTheme,
517                                                                         icon: false,
518                                                                         iconpos: false
519                                                                 })
520                                                                 .find( ".ui-btn-inner" )
521                                                                 .append(
522                                                                         $( "<span />" ).buttonMarkup( {
523                                                                                 shadow : true,
524                                                                                 corners : true,
525                                                                                 theme : splittheme,
526                                                                                 iconpos : "notext",
527                                                                                 icon : listspliticon || last.jqmData( "icon" ) || o.splitIcon
528                                                                         })
529                                                                 );
530                                                 }
531                                         } else if ( item.jqmData( "role" ) === "list-divider" ) {
532
533                                                 itemClass += " ui-li-divider ui-btn ui-bar-" + dividertheme;
534                                                 item.attr( "role", "heading" );
535
536                                                 //reset counter when a divider heading is encountered
537                                                 if ( counter ) {
538                                                         counter = 1;
539                                                 }
540
541                                         } else {
542                                                 itemClass += " ui-li-static ui-body-" + itemTheme;
543                                         }
544                                 }
545
546                                 if ( counter && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
547                                         countParent = item.is( ".ui-li-static:first" ) ? item : item.find( ".ui-link-inherit" );
548
549                                         countParent.addClass( "ui-li-jsnumbering" )
550                                                 .prepend( "<span class='ui-li-dec'>" + (counter++) + ". </span>" );
551                                 }
552
553                                 item.add( item.children( ".ui-btn-inner" ) ).addClass( itemClass );
554
555                                 self._itemApply( $list, item );
556                         }
557
558                         this._refreshCorners( create );
559                 },
560
561                 //create a string for ID/subpage url creation
562                 _idStringEscape: function ( str ) {
563                         return str.replace(/\W/g , "-");
564
565                 },
566
567                 _createSubPages: function () {
568                         var parentList = this.element,
569                                 parentPage = parentList.closest( ".ui-page" ),
570                                 parentUrl = parentPage.jqmData( "url" ),
571                                 parentId = parentUrl || parentPage[ 0 ][ $.expando ],
572                                 parentListId = parentList.attr( "id" ),
573                                 o = this.options,
574                                 dns = "data-" + $.mobile.ns,
575                                 self = this,
576                                 persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
577                                 hasSubPages,
578                                 newRemove;
579
580                         if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
581                                 listCountPerPage[ parentId ] = -1;
582                         }
583
584                         parentListId = parentListId || ++listCountPerPage[ parentId ];
585
586                         $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function ( i ) {
587                                 var self = this,
588                                         list = $( this ),
589                                         listId = list.attr( "id" ) || parentListId + "-" + i,
590                                         parent = list.parent(),
591                                         nodeEls,
592                                         title = nodeEls.first().getEncodedText(),//url limits to first 30 chars of text
593                                         id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
594                                         theme = list.jqmData( "theme" ) || o.theme,
595                                         countTheme = list.jqmData( "counttheme" ) || parentList.jqmData( "counttheme" ) || o.countTheme,
596                                         newPage,
597                                         anchor;
598
599                                 nodeEls = $( list.prevAll().toArray().reverse() );
600                                 nodeEls = nodeEls.length ? nodeEls : $( "<span>" + $.trim(parent.contents()[ 0 ].nodeValue) + "</span>" );
601
602                                 //define hasSubPages for use in later removal
603                                 hasSubPages = true;
604
605                                 newPage = list.detach()
606                                                         .wrap( "<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
607                                                         .parent()
608                                                                 .before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
609                                                                 .after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='" + persistentFooterID + "'>" ) : "" )
610                                                                 .parent()
611                                                                         .appendTo( $.mobile.pageContainer );
612
613                                 newPage.page();
614
615                                 anchor = parent.find('a:first');
616
617                                 if ( !anchor.length ) {
618                                         anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
619                                 }
620
621                                 anchor.attr( "href", "#" + id );
622
623                         }).extendablelist();
624
625                         // on pagehide, remove any nested pages along with the parent page, as long as they aren't active
626                         // and aren't embedded
627                         if ( hasSubPages &&
628                                         parentPage.is( ":jqmData(external-page='true')" ) &&
629                                         parentPage.data( "page" ).options.domCache === false ) {
630
631                                 newRemove = function ( e, ui ) {
632                                         var nextPage = ui.nextPage, npURL;
633
634                                         if ( ui.nextPage ) {
635                                                 npURL = nextPage.jqmData( "url" );
636                                                 if ( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ) {
637                                                         self.childPages().remove();
638                                                         parentPage.remove();
639                                                 }
640                                         }
641                                 };
642
643                                 // unbind the original page remove and replace with our specialized version
644                                 parentPage
645                                         .unbind( "pagehide.remove" )
646                                         .bind( "pagehide.remove", newRemove);
647                         }
648                 },
649
650                 // TODO sort out a better way to track sub pages of the extendable listview this is brittle
651                 childPages: function () {
652                         var parentUrl = this.parentPage.jqmData( "url" );
653
654                         return $( ":jqmData(url^='" +  parentUrl + "&" + $.mobile.subPageUrlKey + "')" );
655                 }
656         });
657
658         //auto self-init widgets
659         $( document ).bind( "pagecreate create", function ( e ) {
660                 $( $.tizen.extendablelist.prototype.options.initSelector, e.target ).extendablelist();
661         });
662
663 }( jQuery ));