upload tizen1.0 source
[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 (function ( $, undefined ) {
112
113         var autodividers = function ( options ) {
114                 var list = $( this ),
115                         listview = list.data( 'listview' ),
116                         dividerType,
117                         textSelector,
118                         getDividerText,
119                         mergeDividers,
120                         isNonDividerLi,
121                         liAdded,
122                         liRemoved;
123
124                 options = options || {};
125                 dividerType = options.type || list.jqmData( 'autodividers' ) || 'alpha';
126                 textSelector = options.selector || list.jqmData( 'autodividers-selector' ) || 'a';
127
128                 getDividerText = function ( elt ) {
129                         // look for some text in the item
130                         var text = elt.find( textSelector ).text() || elt.text() || null;
131
132                         if ( !text ) {
133                                 return null;
134                         }
135
136                         // create the text for the divider
137                         if ( dividerType === 'alpha' ) {
138                                 text = text.slice( 0, 1 ).toUpperCase();
139                         }
140
141                         return text;
142                 };
143
144                 mergeDividers = function () {
145                         var dividersChanged = false,
146                                 divider,
147                                 dividerText,
148                                 selector,
149                                 nextDividers;
150
151                         // any dividers which are following siblings of a divider, where
152                         // there are no dividers with different text inbetween, can be removed
153                         list.find( 'li.ui-li-divider' ).each(function () {
154                                 divider = $( this );
155                                 dividerText = divider.text();
156                                 selector = '.ui-li-divider:not(:contains(' + dividerText + '))';
157                                 nextDividers = divider.nextUntil( selector );
158                                 nextDividers = nextDividers.filter( '.ui-li-divider:contains(' + dividerText + ')' );
159
160                                 if ( nextDividers.length > 0 ) {
161                                         nextDividers.remove();
162                                         dividersChanged = true;
163                                 }
164                         } );
165
166                         if ( dividersChanged ) {
167                                 list.trigger( 'updatelayout' );
168                         }
169                 };
170
171                 // check that elt is a non-divider li element
172                 isNonDividerLi = function ( elt ) {
173                         return elt.is('li') &&
174                                         elt.jqmData( 'role' ) !== 'list-divider';
175                 };
176
177                 // li element inserted, so check whether it needs a divider
178                 liAdded = function ( li ) {
179                         var dividerText = getDividerText( li ),
180                                 existingDividers,
181                                 divider;
182
183                         if ( !dividerText ) {
184                                 listview.refresh();
185                                 return;
186                         }
187
188                         // add expected divider for this li if it doesn't exist
189                         existingDividers = li.prevAll( '.ui-li-divider:first:contains(' + dividerText + ')' );
190
191                         if ( existingDividers.length === 0 ) {
192                                 divider = $( '<li>' + dividerText + '</li>' );
193                                 divider.attr( 'data-' + $.mobile.ns + 'role', 'list-divider' );
194                                 li.before( divider );
195
196                                 listview.refresh();
197
198                                 mergeDividers();
199                         } else {
200                                 listview.refresh();
201                         }
202                 };
203
204                 // li element removed, so check whether its divider should go
205                 liRemoved = function ( li ) {
206                         var dividerText = getDividerText( li ),
207                                 precedingItems,
208                                 nextItems;
209
210                         if ( !dividerText ) {
211                                 listview.refresh();
212                                 return;
213                         }
214
215                         // remove divider for this li if there are no other
216                         // li items for the divider before or after this li item
217                         precedingItems = li.prevUntil( '.ui-li-divider:contains(' + dividerText + ')' );
218                         nextItems = li.nextUntil( '.ui-li-divider' );
219
220                         if ( precedingItems.length === 0 && nextItems.length === 0 ) {
221                                 li.prevAll( '.ui-li-divider:contains(' + dividerText + '):first' ).remove();
222
223                                 listview.refresh();
224
225                                 mergeDividers();
226                         } else {
227                                 listview.refresh();
228                         }
229                 };
230
231                 // set up the dividers on first create
232                 list.find( 'li' ).each( function () {
233                         var li = $( this );
234
235                         // remove existing dividers
236                         if ( li.jqmData( 'role' ) === 'list-divider' ) {
237                                 li.remove();
238                         } else {                        // make new dividers for list items
239                                 liAdded( li );
240                         }
241                 } );
242
243                 // bind to DOM events to keep list up to date
244                 list.bind( 'DOMNodeInserted', function ( e ) {
245                         var elt = $( e.target );
246
247                         if ( !isNonDividerLi( elt ) ) {
248                                 return;
249                         }
250
251                         liAdded( elt );
252                 } );
253
254                 list.bind( 'DOMNodeRemoved', function ( e ) {
255                         var elt = $( e.target );
256
257                         if ( !isNonDividerLi( elt ) ) {
258                                 return;
259                         }
260
261                         liRemoved( elt );
262                 } );
263         };
264
265         $.fn.autodividers = autodividers;
266
267         $( ":jqmData(role=listview)" ).live( "listviewcreate", function () {
268                 var list = $( this );
269
270                 if ( list.is( ':jqmData(autodividers)' ) ) {
271                         list.autodividers();
272                 }
273         } );
274 }( jQuery ) );