Export 0.1.63
[platform/framework/web/web-ui-fw.git] / src / widgets / autodividers / js / jquery.mobile.tizen.autodividers.js
1 /*
2  * jQuery Mobile Widget @VERSION - listview autodividers
3  *
4  * This software is licensed under the MIT licence (as defined by the OSI at
5  * http://www.opensource.org/licenses/mit-license.php)
6  * 
7  * ***************************************************************************
8  * Copyright (c) 2011 by Intel Corporation Ltd.
9  * 
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  * 
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  * 
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  * ***************************************************************************
28  *
29  * Authors: Elliot Smith <elliot.smith@intel.com>
30  */
31
32 // Applies dividers automatically to a listview, using link text
33 // (for link lists) or text (for readonly lists) as the basis for the
34 // divider text.
35 //
36 // Apply using autodividers({type: 'X'}) on a <ul> with
37 // data-role="listview", or with data-autodividers="true", where X
38 // is the type of divider to create. The default divider type is 'alpha',
39 // meaning first characters of list item text, upper-cased.
40 //
41 // The element used to derive the text for the auto dividers defaults
42 // to the first link inside the li; failing that, the text directly inside
43 // the li element is used. This can be overridden with the
44 // data-autodividers-selector attribute or via options; the selector
45 // will use each li element as its context.
46 //
47 // Any time a new li element is added to the list, or an li element is
48 // removed, this extension will update the dividers in the listview
49 // accordingly.
50 //
51 // Note that if a listview already has dividers, applying this
52 // extension will remove all the existing dividers and replace them
53 // with new, generated ones.
54 //
55 // Also note that this extension doesn't sort the list: it only creates
56 // dividers based on text inside list items. So if your list isn't
57 // alphabetically-sorted, you may get duplicate dividers.
58 //
59 // So, for example, this markup:
60 //
61 // <ul id="has-no-dividers" data-role="listview" data-autodividers="alpha">
62 //              <li>Barry</li>
63 //              <li>Carrie</li>
64 //              <li>Betty</li>
65 //              <li>Harry</li>
66 //              <li>Carly</li>
67 //              <li>Hetty</li>
68 // </ul>
69 //
70 // will produce dividers like this:
71 //
72 // <ul data-role="listview" data-autodividers="alpha">
73 //      <li data-role="list-divider">B</li>
74 //      <li>Barry</li>
75 //      <li data-role="list-divider">C</li>
76 //      <li>Carrie</li>
77 //      <li data-role="list-divider">B</li>
78 //      <li>Betty</li>
79 //      <li data-role="list-divider">H</li>
80 //      <li>Harry</li>
81 //      <li data-role="list-divider">C</li>
82 //      <li>Carly</li>
83 //      <li data-role="list-divider">H</li>
84 //      <li>Hetty</li>
85 // </ul>
86 //
87 // with each divider occuring twice.
88 //
89 // Options:
90 //
91 //      selector: The jQuery selector to use to find text for the
92 //                      generated dividers. Default is to use the first 'a'
93 //                      (link) element. If this selector doesn't find any
94 //                      text, the widget automatically falls back to the text
95 //                      inside the li (for read-only lists). Can be set to a custom
96 //                      selector via data-autodividers-selector="..." or the 'selector'
97 //                      option.
98 //
99 //       type: 'alpha' (default) sets the auto divider type to "uppercased
100 //               first character of text selected from each item"; "full" sets
101 //               it to the unmodified text selected from each item. Set via
102 //               the data-autodividers="<type>" attribute on the listview or
103 //               the 'type' option.
104 //
105 // Events:
106 //
107 //      updatelayout: Triggered if the dividers in the list change;
108 //              this happens if list items are added to the listview,
109 //              which causes the autodividers to be regenerated.
110
111 /**
112         @class Autodivider
113         The auto-divider widget is used to automatically create dividers for a list view based on the list view items.
114         The dividers are applied using the link texts (in link lists) or texts (in read-only lists).
115         The element used to derive the text for the divider defaults to the first link inside the element; failing that, the text directly inside the element is used.
116         This widget automatically updates the dividers every time a list view item is added or deleted.
117
118                         <ul data-role="listview" data-autodividers="alpha">
119                                 <li><a href="#">Amy</a></li>
120                                 <li><a href="#">Arabella</a></li>
121                                 <li><a href="#">Barry</a></li>
122                                 //Other list items
123                         </ul>
124
125
126         @since tizen2.0
127
128 */
129 (function ( $, undefined ) {
130
131         var autodividers = function ( options ) {
132                 var list = $( this ),
133                         listview = list.data( 'listview' ),
134                         dividerType,
135                         textSelector,
136                         getDividerText,
137                         mergeDividers,
138                         isNonDividerLi,
139                         liAdded,
140                         liRemoved;
141
142                 options = options || {};
143                 dividerType = options.type || list.jqmData( 'autodividers' ) || 'alpha';
144                 textSelector = options.selector || list.jqmData( 'autodividers-selector' ) || 'a';
145
146                 getDividerText = function ( elt ) {
147                         // look for some text in the item
148                         var text = elt.find( textSelector ).text() || elt.text() || null;
149
150                         if ( !text ) {
151                                 return null;
152                         }
153
154                         // create the text for the divider
155                         if ( dividerType === 'alpha' ) {
156                                 text = text.slice( 0, 1 ).toUpperCase();
157                         }
158
159                         return text;
160                 };
161
162                 mergeDividers = function () {
163                         var dividersChanged = false,
164                                 divider,
165                                 dividerText,
166                                 selector,
167                                 nextDividers;
168
169                         // any dividers which are following siblings of a divider, where
170                         // there are no dividers with different text inbetween, can be removed
171                         list.find( 'li.ui-li-divider' ).each(function () {
172                                 divider = $( this );
173                                 dividerText = divider.text();
174                                 selector = '.ui-li-divider:not(:contains(' + dividerText + '))';
175                                 nextDividers = divider.nextUntil( selector );
176                                 nextDividers = nextDividers.filter( '.ui-li-divider:contains(' + dividerText + ')' );
177
178                                 if ( nextDividers.length > 0 ) {
179                                         nextDividers.remove();
180                                         dividersChanged = true;
181                                 }
182                         } );
183
184                         if ( dividersChanged ) {
185                                 list.trigger( 'updatelayout' );
186                         }
187                 };
188
189                 // check that elt is a non-divider li element
190                 isNonDividerLi = function ( elt ) {
191                         return elt.is('li') &&
192                                         elt.jqmData( 'role' ) !== 'list-divider';
193                 };
194
195                 // li element inserted, so check whether it needs a divider
196                 liAdded = function ( li ) {
197                         var dividerText = getDividerText( li ),
198                                 existingDividers,
199                                 divider;
200
201                         if ( !dividerText ) {
202                                 listview.refresh();
203                                 return;
204                         }
205
206                         // add expected divider for this li if it doesn't exist
207                         existingDividers = li.prevAll( '.ui-li-divider:first:contains(' + dividerText + ')' );
208
209                         if ( existingDividers.length === 0 ) {
210                                 divider = $( '<li>' + dividerText + '</li>' );
211                                 divider.attr( 'data-' + $.mobile.ns + 'role', 'list-divider' );
212                                 li.before( divider );
213
214                                 listview.refresh();
215
216                                 mergeDividers();
217                         } else {
218                                 listview.refresh();
219                         }
220                 };
221
222                 // li element removed, so check whether its divider should go
223                 liRemoved = function ( li ) {
224                         var dividerText = getDividerText( li ),
225                                 precedingItems,
226                                 nextItems;
227
228                         if ( !dividerText ) {
229                                 listview.refresh();
230                                 return;
231                         }
232
233                         // remove divider for this li if there are no other
234                         // li items for the divider before or after this li item
235                         precedingItems = li.prevUntil( '.ui-li-divider:contains(' + dividerText + ')' );
236                         nextItems = li.nextUntil( '.ui-li-divider' );
237
238                         if ( precedingItems.length === 0 && nextItems.length === 0 ) {
239                                 li.prevAll( '.ui-li-divider:contains(' + dividerText + '):first' ).remove();
240
241                                 listview.refresh();
242
243                                 mergeDividers();
244                         } else {
245                                 listview.refresh();
246                         }
247                 };
248
249                 // set up the dividers on first create
250                 list.find( 'li' ).each( function () {
251                         var li = $( this );
252
253                         // remove existing dividers
254                         if ( li.jqmData( 'role' ) === 'list-divider' ) {
255                                 li.remove();
256                         } else {                        // make new dividers for list items
257                                 liAdded( li );
258                         }
259                 } );
260
261                 // bind to DOM events to keep list up to date
262                 list.bind( 'DOMNodeInserted', function ( e ) {
263                         var elt = $( e.target );
264
265                         if ( !isNonDividerLi( elt ) ) {
266                                 return;
267                         }
268
269                         liAdded( elt );
270                 } );
271
272                 list.bind( 'DOMNodeRemoved', function ( e ) {
273                         var elt = $( e.target );
274
275                         if ( !isNonDividerLi( elt ) ) {
276                                 return;
277                         }
278
279                         liRemoved( elt );
280                 } );
281         };
282
283         $.fn.autodividers = autodividers;
284
285         $( ":jqmData(role=listview)" ).live( "listviewcreate", function () {
286                 var list = $( this );
287
288                 if ( list.is( ':jqmData(autodividers)' ) ) {
289                         list.autodividers();
290                 }
291         } );
292 }( jQuery ) );