[TemporaryStorage] add files required for SDK build
[samples/web/TemporaryStorage.git] / tizen-web-ui-fw / latest / js / src / widgets / listview.js
1 (function( $, undefined ) {
2
3 //Keeps track of the number of lists per page UID
4 //This allows support for multiple nested list in the same page
5 //https://github.com/jquery/jquery-mobile/issues/1617
6 var listCountPerPage = {};
7
8 $.widget( "mobile.listview", $.mobile.widget, {
9
10         options: {
11                 theme: null,
12                 countTheme: "c",
13                 headerTheme: "b",
14                 dividerTheme: "b",
15                 splitIcon: "arrow-r",
16                 splitTheme: "b",
17                 inset: false,
18                 initSelector: ":jqmData(role='listview')"
19         },
20
21         _create: function() {
22                 var t = this,
23                         listviewClasses = "";
24
25                 listviewClasses += t.options.inset ? " ui-listview-inset ui-corner-all ui-shadow " : "";
26
27                 // create listview markup
28                 t.element.addClass(function( i, orig ) {
29                         return orig + " ui-listview " + listviewClasses;
30                 });
31
32                 t.refresh( true );
33         },
34
35         _removeCorners: function( li, which ) {
36                 var top = "ui-corner-top ui-corner-tr ui-corner-tl",
37                         bot = "ui-corner-bottom ui-corner-br ui-corner-bl";
38
39                 li = li.add( li.find( ".ui-btn-inner, .ui-li-link-alt, .ui-li-thumb" ) );
40
41                 if ( which === "top" ) {
42                         li.removeClass( top );
43                 } else if ( which === "bottom" ) {
44                         li.removeClass( bot );
45                 } else {
46                         li.removeClass( top + " " + bot );
47                 }
48         },
49
50         _refreshCorners: function( create ) {
51                 var $li,
52                         $visibleli,
53                         $topli,
54                         $bottomli;
55
56                 $li = this.element.children( "li" );
57                 // At create time and when autodividers calls refresh the li are not visible yet so we need to rely on .ui-screen-hidden
58                 $visibleli = create || $li.filter( ":visible" ).length === 0 ? $li.not( ".ui-screen-hidden" ) : $li.filter( ":visible" );
59
60                 // ui-li-last is used for setting border-bottom on the last li          
61                 $li.filter( ".ui-li-last" ).removeClass( "ui-li-last" );
62                                         
63                 if ( this.options.inset ) {
64                         this._removeCorners( $li );
65
66                         // Select the first visible li element
67                         $topli = $visibleli.first()
68                                 .addClass( "ui-corner-top" );
69
70                         $topli.add( $topli.find( ".ui-btn-inner" )
71                                 .not( ".ui-li-link-alt span:first-child" ) )
72                                         .addClass( "ui-corner-top" )
73                                 .end()
74                                 .find( ".ui-li-link-alt, .ui-li-link-alt span:first-child" )
75                                         .addClass( "ui-corner-tr" )
76                                 .end()
77                                 .find( ".ui-li-thumb" )
78                                         .not( ".ui-li-icon" )
79                                         .addClass( "ui-corner-tl" );
80
81                         // Select the last visible li element
82                         $bottomli = $visibleli.last()
83                                 .addClass( "ui-corner-bottom ui-li-last" );
84
85                         $bottomli.add( $bottomli.find( ".ui-btn-inner" ) )
86                                 .find( ".ui-li-link-alt" )
87                                         .addClass( "ui-corner-br" )
88                                 .end()
89                                 .find( ".ui-li-thumb" )
90                                         .not( ".ui-li-icon" )
91                                         .addClass( "ui-corner-bl" );
92                 } else {
93                         $visibleli.last().addClass( "ui-li-last" );
94                 }
95                 if ( !create ) {
96                         this.element.trigger( "updatelayout" );
97                 }
98         },
99
100         // This is a generic utility method for finding the first
101         // node with a given nodeName. It uses basic DOM traversal
102         // to be fast and is meant to be a substitute for simple
103         // $.fn.closest() and $.fn.children() calls on a single
104         // element. Note that callers must pass both the lowerCase
105         // and upperCase version of the nodeName they are looking for.
106         // The main reason for this is that this function will be
107         // called many times and we want to avoid having to lowercase
108         // the nodeName from the element every time to ensure we have
109         // a match. Note that this function lives here for now, but may
110         // be moved into $.mobile if other components need a similar method.
111         _findFirstElementByTagName: function( ele, nextProp, lcName, ucName ) {
112                 var dict = {};
113                 dict[ lcName ] = dict[ ucName ] = true;
114                 while ( ele ) {
115                         if ( dict[ ele.nodeName ] ) {
116                                 return ele;
117                         }
118                         ele = ele[ nextProp ];
119                 }
120                 return null;
121         },
122         _getChildrenByTagName: function( ele, lcName, ucName ) {
123                 var results = [],
124                         dict = {};
125                 dict[ lcName ] = dict[ ucName ] = true;
126                 ele = ele.firstChild;
127                 while ( ele ) {
128                         if ( dict[ ele.nodeName ] ) {
129                                 results.push( ele );
130                         }
131                         ele = ele.nextSibling;
132                 }
133                 return $( results );
134         },
135
136         _addThumbClasses: function( containers ) {
137                 var i, img, len = containers.length;
138                 for ( i = 0; i < len; i++ ) {
139                         img = $( this._findFirstElementByTagName( containers[ i ].firstChild, "nextSibling", "img", "IMG" ) );
140                         if ( img.length ) {
141                                 img.addClass( "ui-li-thumb" ).attr( {
142                                         "role" : "",
143                                         "aria-label" : "icon"
144                                 });
145                                 $( this._findFirstElementByTagName( img[ 0 ].parentNode, "parentNode", "li", "LI" ) ).addClass( img.is( ".ui-li-icon" ) ? "ui-li-has-icon" : "ui-li-has-thumb" );
146                         }
147                 }
148         },
149
150         _addCheckboxRadioClasses: function( containers )
151         {
152                 var i, inputAttr, len = containers.length;
153                 for ( i = 0; i < len; i++ ) {
154                         inputAttr = $( containers[ i ] ).find( "input" );
155                         if ( inputAttr.attr( "type" ) == "checkbox" ) {
156                                 $( containers[ i ] ).addClass( "ui-li-has-checkbox" );
157                         } else if ( inputAttr.attr( "type" ) == "radio" ) {
158                                 $( containers[ i ] ).addClass( "ui-li-has-radio" );
159                         }
160                 }
161         },
162
163         _addRightBtnClasses: function( containers )
164         {
165                 var i, btnAttr, len = containers.length;
166                 for ( i = 0; i < len; i++ ) {
167                         btnAttr = $( containers[ i ] ).find( ":jqmData(role='button'),input[type='button'],select:jqmData(role='slider')" );
168                         if ( btnAttr.length ) {
169                                 if ( btnAttr.jqmData( "style" ) == "circle" )  {
170                                         $( containers[ i ] ).addClass( "ui-li-has-right-circle-btn" );
171                                 } else {
172                                         $( containers[ i ] ).addClass( "ui-li-has-right-btn" );
173                                 }
174                         }
175                 }
176         },
177
178         refresh: function( create ) {
179                 this.parentPage = this.element.closest( ".ui-page" );
180                 this._createSubPages();
181
182                 var o = this.options,
183                         $list = this.element,
184                         self = this,
185                         dividertheme = $.mobile.getAttrFixed( $list[0], "data-" + $.mobile.ns + "dividertheme" ) || o.dividerTheme,
186                         listsplittheme =  $.mobile.getAttrFixed( $list[0], "data-" + $.mobile.ns + "splittheme" ),
187                         listspliticon = $.mobile.getAttrFixed( $list[0], "data-" + $.mobile.ns + "spliticon" ),
188                         li = this._getChildrenByTagName( $list[ 0 ], "li", "LI" ),
189                         ol = !!$.nodeName( $list[ 0 ], "ol" ),
190                         jsCount = !$.support.cssPseudoElement,
191                         start = $list.attr( "start" ),
192                         itemClassDict = {},
193                         item, itemClass, itemTheme,
194                         a, last, splittheme, counter, startCount, newStartCount, countParent, icon, imgParents, img, linkIcon;
195
196                 if ( ol && jsCount ) {
197                         $list.find( ".ui-li-dec" ).remove();
198                 }
199
200                 if ( ol ) {     
201                         // Check if a start attribute has been set while taking a value of 0 into account
202                         if ( start || start === 0 ) {
203                                 if ( !jsCount ) {
204                                         startCount = parseFloat( start ) - 1;
205                                         $list.css( "counter-reset", "listnumbering " + startCount );
206                                 } else {
207                                         counter = parseFloat( start );
208                                 }
209                         } else if ( jsCount ) {
210                                         counter = 1;
211                         }       
212                 }
213
214                 if ( !o.theme ) {
215                         o.theme = $.mobile.getInheritedTheme( this.element, "c" );
216                 }
217
218                 for ( var pos = 0, numli = li.length; pos < numli; pos++ ) {
219                         item = li.eq( pos );
220                         itemClass = "ui-li";
221
222                         // If we're creating the element, we update it regardless
223                         if ( create || !item.hasClass( "ui-li" ) ) {
224                                 itemTheme = $.mobile.getAttrFixed( item[0], "data-" + $.mobile.ns + "theme" ) || o.theme;
225                                 a = this._getChildrenByTagName( item[ 0 ], "a", "A" ).attr( {
226                                         "role": "",
227                                         "tabindex": "0"
228                                 });
229                                 var isDivider = ( $.mobile.getAttrFixed( item[0], "data-" + $.mobile.ns + "role" ) === "list-divider" );
230
231                                 if ( item.hasClass( "ui-li-has-checkbox" ) || item.hasClass( "ui-li-has-radio" ) ) {
232                                         item.on( "vclick", function ( e ) {
233                                                 var targetItem = $( e.target );
234                                                 var checkboxradio = targetItem.find( ".ui-checkbox" );
235                                                 if ( !checkboxradio.length ) {
236                                                         checkboxradio = targetItem.find( ".ui-radio" );
237                                                 }
238
239                                                 if ( checkboxradio.length ) {
240                                                         checkboxradio.children( "label" ).trigger( "vclick" );
241                                                 }
242                                         });
243                                 }
244
245                                 if ( a.length && !isDivider ) {
246                                         icon = $.mobile.getAttrFixed( item[0], "data-" + $.mobile.ns + "icon" );
247
248                                         /* Remove auto populated right-arrow button. */
249                                         if ( icon === undefined ) {
250                                                 icon = false;
251                                         }
252
253                                         item.buttonMarkup({
254                                                 wrapperEls: "div",
255                                                 shadow: false,
256                                                 corners: false,
257                                                 iconpos: "right",
258                                                 icon: a.length > 1 || icon === false ? false : icon || "arrow-r",
259                                                 theme: itemTheme
260                                         });
261
262                                         if ( ( icon !== false ) && ( a.length === 1 ) ) {
263                                                 item.addClass( "ui-li-has-arrow" );
264                                         }
265
266                                         a.first().removeClass( "ui-link" ).addClass( "ui-link-inherit" );
267
268                                         if ( a.length > 1 ) {
269                                                 itemClass += " ui-li-has-alt";
270
271                                                 last = a.last();
272                                                 splittheme = listsplittheme || $.mobile.getAttrFixed( last[0], "data-" + $.mobile.ns + "theme" ) || o.splitTheme;
273                                                 linkIcon = $.mobile.getAttrFixed( last[0], "data-" + $.mobile.ns + "icon" );
274
275                                                 last.appendTo( item )
276                                                         .attr( "title", last.getEncodedText() )
277                                                         .addClass( "ui-li-link-alt" )
278                                                         .empty()
279                                                         .buttonMarkup({
280                                                                 shadow: false,
281                                                                 corners: false,
282                                                                 theme: itemTheme,
283                                                                 icon: false,
284                                                                 iconpos: "notext"
285                                                         })
286                                                         .find( ".ui-btn-inner" )
287                                                                 .append(
288                                                                         $( document.createElement( "span" ) ).buttonMarkup({
289                                                                                 shadow: true,
290                                                                                 corners: true,
291                                                                                 theme: splittheme,
292                                                                                 iconpos: "notext",
293                                                                                 // link icon overrides list item icon overrides ul element overrides options
294                                                                                 icon: linkIcon || icon || listspliticon || o.splitIcon
295                                                                         })
296                                                                 );
297                                         }
298                                 } else if ( isDivider ) {
299
300                                         itemClass += " ui-li-divider ui-bar-" + dividertheme;
301                                         item.attr( { "role": "heading", "tabindex": "0" } );
302
303                                         if ( ol ) {     
304                                                 //reset counter when a divider heading is encountered
305                                                 if ( start || start === 0 ) {
306                                                         if ( !jsCount ) {
307                                                                 newStartCount = parseFloat( start ) - 1;
308                                                                 item.css( "counter-reset", "listnumbering " + newStartCount );
309                                                         } else {
310                                                                 counter = parseFloat( start );
311                                                         }
312                                                 } else if ( jsCount ) {
313                                                                 counter = 1;
314                                                 }       
315                                         }
316                                 
317                                 } else {
318                                         itemClass += " ui-li-static ui-btn-up-" + itemTheme;
319                                         item.attr( "tabindex", "0" );
320                                 }
321                         }
322
323                         if ( ol && jsCount && itemClass.indexOf( "ui-li-divider" ) < 0 ) {
324                                 countParent = itemClass.indexOf( "ui-li-static" ) > 0 ? item : item.find( ".ui-link-inherit" );
325
326                                 countParent.addClass( "ui-li-jsnumbering" )
327                                         .prepend( "<span class='ui-li-dec'>" + ( counter++ ) + ". </span>" );
328                         }
329
330                         // Instead of setting item class directly on the list item and its
331                         // btn-inner at this point in time, push the item into a dictionary
332                         // that tells us what class to set on it so we can do this after this
333                         // processing loop is finished.
334
335                         if ( !itemClassDict[ itemClass ] ) {
336                                 itemClassDict[ itemClass ] = [];
337                         }
338
339                         itemClassDict[ itemClass ].push( item[ 0 ] );
340                 }
341
342                 // Set the appropriate listview item classes on each list item
343                 // and their btn-inner elements. The main reason we didn't do this
344                 // in the for-loop above is because we can eliminate per-item function overhead
345                 // by calling addClass() and children() once or twice afterwards. This
346                 // can give us a significant boost on platforms like WP7.5.
347
348                 for ( itemClass in itemClassDict ) {
349                         $( itemClassDict[ itemClass ] ).addClass( itemClass ).children( ".ui-btn-inner" ).addClass( itemClass );
350                 }
351
352                 $list.find( "h1, h2, h3, h4, h5, h6" ).addClass( "ui-li-heading" )
353                         .end()
354
355                         .find( "p, dl" ).addClass( "ui-li-desc" )
356                         .end()
357
358                         .find( ".ui-li-aside" ).each(function() {
359                                         var $this = $( this );
360                                         $this.prependTo( $this.parent() ); //shift aside to front for css float
361                                 })
362                         .end()
363
364                         .find( ".ui-li-count" ).each(function() {
365                                         $( this ).closest( "li" ).addClass( "ui-li-has-count" );
366                                 }).addClass( "ui-btn-up-" + ( $.mobile.getAttrFixed( $list[0], "data-" + $.mobile.ns + "counttheme" ) || this.options.countTheme) + " ui-btn-corner-all" );
367
368                 // The idea here is to look at the first image in the list item
369                 // itself, and any .ui-link-inherit element it may contain, so we
370                 // can place the appropriate classes on the image and list item.
371                 // Note that we used to use something like:
372                 //
373                 //    li.find(">img:eq(0), .ui-link-inherit>img:eq(0)").each( ... );
374                 //
375                 // But executing a find() like that on Windows Phone 7.5 took a
376                 // really long time. Walking things manually with the code below
377                 // allows the 400 listview item page to load in about 3 seconds as
378                 // opposed to 30 seconds.
379
380                 this._addThumbClasses( li );
381                 this._addThumbClasses( $list.find( ".ui-link-inherit" ) );
382
383                 this._addCheckboxRadioClasses( li );
384                 this._addCheckboxRadioClasses( $list.find( ".ui-link-inherit" ) );
385
386                 this._addRightBtnClasses( li );
387                 this._addRightBtnClasses( $list.find( ".ui-link-inherit" ) );
388
389                 this._refreshCorners( create );
390
391     // autodividers binds to this to redraw dividers after the listview refresh
392                 this._trigger( "afterrefresh" );
393         },
394
395         //create a string for ID/subpage url creation
396         _idStringEscape: function( str ) {
397                 return str.replace(/[^a-zA-Z0-9]/g, '-');
398         },
399
400         _createSubPages: function() {
401                 var parentList = this.element,
402                         parentPage = parentList.closest( ".ui-page" ),
403                         parentUrl = parentPage.jqmData( "url" ),
404                         parentId = parentUrl || parentPage[ 0 ][ $.expando ],
405                         parentListId = parentList.attr( "id" ),
406                         o = this.options,
407                         dns = "data-" + $.mobile.ns,
408                         self = this,
409                         persistentFooterID = parentPage.find( ":jqmData(role='footer')" ).jqmData( "id" ),
410                         hasSubPages;
411
412                 if ( typeof listCountPerPage[ parentId ] === "undefined" ) {
413                         listCountPerPage[ parentId ] = -1;
414                 }
415
416                 parentListId = parentListId || ++listCountPerPage[ parentId ];
417
418                 $( parentList.find( "li>ul, li>ol" ).toArray().reverse() ).each(function( i ) {
419                         var self = this,
420                                 list = $( this ),
421                                 listId = list.attr( "id" ) || parentListId + "-" + i,
422                                 parent = list.parent(),
423                                 nodeElsFull = $( list.prevAll().toArray().reverse() ),
424                                 nodeEls = nodeElsFull.length ? nodeElsFull : $( "<span>" + $.trim(parent.contents()[ 0 ].nodeValue) + "</span>" ),
425                                 title = nodeEls.first().getEncodedText(),//url limits to first 30 chars of text
426                                 id = ( parentUrl || "" ) + "&" + $.mobile.subPageUrlKey + "=" + listId,
427                                 theme = $.mobile.getAttrFixed( list[0], "data-" + $.mobile.ns + "theme" ) || o.theme,
428                                 countTheme = $.mobile.getAttrFixed( list[0], "data-" + $.mobile.ns + "counttheme" ) || $.mobile.getAttrFixed( parentList[0], "data-" + $.mobile.ns + "counttheme" ) || o.countTheme,
429                                 newPage, anchor;
430
431                         //define hasSubPages for use in later removal
432                         hasSubPages = true;
433
434                         newPage = list.detach()
435                                                 .wrap( "<div " + dns + "role='page' " + dns + "url='" + id + "' " + dns + "theme='" + theme + "' " + dns + "count-theme='" + countTheme + "'><div " + dns + "role='content'></div></div>" )
436                                                 .parent()
437                                                         .before( "<div " + dns + "role='header' " + dns + "theme='" + o.headerTheme + "'><div class='ui-title'>" + title + "</div></div>" )
438                                                         .after( persistentFooterID ? $( "<div " + dns + "role='footer' " + dns + "id='"+ persistentFooterID +"'>" ) : "" )
439                                                         .parent()
440                                                                 .appendTo( $.mobile.pageContainer );
441
442                         newPage.page();
443
444                         anchor = parent.find( 'a:first' );
445
446                         if ( !anchor.length ) {
447                                 anchor = $( "<a/>" ).html( nodeEls || title ).prependTo( parent.empty() );
448                         }
449
450                         anchor.attr( "href", "#" + id );
451
452                 }).listview();
453
454                 // on pagehide, remove any nested pages along with the parent page, as long as they aren't active
455                 // and aren't embedded
456                 if ( hasSubPages &&
457                         parentPage.is( ":jqmData(external-page='true')" ) &&
458                         parentPage.data( "page" ).options.domCache === false ) {
459
460                         var newRemove = function( e, ui ) {
461                                 var nextPage = ui.nextPage, npURL,
462                                         prEvent = new $.Event( "pageremove" );
463
464                                 if ( ui.nextPage ) {
465                                         npURL = nextPage.jqmData( "url" );
466                                         if ( npURL.indexOf( parentUrl + "&" + $.mobile.subPageUrlKey ) !== 0 ) {
467                                                 self.childPages().remove();
468                                                 parentPage.trigger( prEvent );
469                                                 if ( !prEvent.isDefaultPrevented() ) {
470                                                         parentPage.removeWithDependents();
471                                                 }
472                                         }
473                                 }
474                         };
475
476                         // unbind the original page remove and replace with our specialized version
477                         parentPage
478                                 .unbind( "pagehide.remove" )
479                                 .bind( "pagehide.remove", newRemove);
480                 }
481         },
482
483         addItem : function( listitem , idx ) {
484                 var $item = $(listitem),
485                         $li,
486                         _self = this;
487
488                 $li = _self.element.children( 'li' );
489                 $item.css( { 'opacity' : 0,
490                                          'display' : 'none' } );
491                 if( $li.length == 0
492                         || $li.length <= idx)
493                 {
494                         $( _self.element ).append( $item );
495                 } else {
496                         $( $li.get( idx ) ).before( $item );
497                 }
498                 $(_self.element).trigger("create")
499                         .listview( 'refresh' );
500
501                 $item.css( 'min-height' , '0px' );
502
503                 $item.slideDown( 'fast' , function( ){
504                         $item.addClass("addli");
505                         $item.css( { 'opacity' : 1 } );
506                 } );
507         },
508
509         removeItem : function( idx ) {
510                 var $item,
511                         $li,
512                         _self = this;
513
514                 $li = _self.element.children( 'li' );
515                 if( $li.length <= 0 ||
516                         $li.length < idx ) {
517                         return ;
518                 }
519                 $item = $( $li.get( idx ) );
520                 $item.addClass("removeli");
521                 $item.slideUp('normal',
522                         function( ) {
523                         $(this).remove();
524                 });
525         },
526
527         // TODO sort out a better way to track sub pages of the listview this is brittle
528         childPages: function() {
529                 var parentUrl = this.parentPage.jqmData( "url" );
530
531                 return $( ":jqmData(url^='"+  parentUrl + "&" + $.mobile.subPageUrlKey + "')" );
532         }
533 });
534
535 //delegate auto self-init widgets
536 $.delegateSelfInitWithSingleSelector( $.mobile.listview );
537
538 })( jQuery );