2 * jQuery Mobile Widget @VERSION - listview autodividers
4 * This software is licensed under the MIT licence (as defined by the OSI at
5 * http://www.opensource.org/licenses/mit-license.php)
7 * ***************************************************************************
8 * Copyright (c) 2011 by Intel Corporation Ltd.
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:
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
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 * ***************************************************************************
29 * Authors: Elliot Smith <elliot.smith@intel.com>
32 // Applies dividers automatically to a listview, using link text
33 // (for link lists) or text (for readonly lists) as the basis for the
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.
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.
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
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.
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.
59 // So, for example, this markup:
61 // <ul id="has-no-dividers" data-role="listview" data-autodividers="alpha">
70 // will produce dividers like this:
72 // <ul data-role="listview" data-autodividers="alpha">
73 // <li data-role="list-divider">B</li>
75 // <li data-role="list-divider">C</li>
77 // <li data-role="list-divider">B</li>
79 // <li data-role="list-divider">H</li>
81 // <li data-role="list-divider">C</li>
83 // <li data-role="list-divider">H</li>
87 // with each divider occuring twice.
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'
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.
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.
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.
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>
129 (function ( $, undefined ) {
131 var autodividers = function ( options ) {
132 var list = $( this ),
133 listview = list.data( 'listview' ),
142 options = options || {};
143 dividerType = options.type || list.jqmData( 'autodividers' ) || 'alpha';
144 textSelector = options.selector || list.jqmData( 'autodividers-selector' ) || 'a';
146 getDividerText = function ( elt ) {
147 // look for some text in the item
148 var text = elt.find( textSelector ).text() || elt.text() || null;
154 // create the text for the divider
155 if ( dividerType === 'alpha' ) {
156 text = text.slice( 0, 1 ).toUpperCase();
162 mergeDividers = function () {
163 var dividersChanged = false,
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 () {
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 + ')' );
178 if ( nextDividers.length > 0 ) {
179 nextDividers.remove();
180 dividersChanged = true;
184 if ( dividersChanged ) {
185 list.trigger( 'updatelayout' );
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';
195 // li element inserted, so check whether it needs a divider
196 liAdded = function ( li ) {
197 var dividerText = getDividerText( li ),
201 if ( !dividerText ) {
206 // add expected divider for this li if it doesn't exist
207 existingDividers = li.prevAll( '.ui-li-divider:first:contains(' + dividerText + ')' );
209 if ( existingDividers.length === 0 ) {
210 divider = $( '<li>' + dividerText + '</li>' );
211 divider.attr( 'data-' + $.mobile.ns + 'role', 'list-divider' );
212 li.before( divider );
222 // li element removed, so check whether its divider should go
223 liRemoved = function ( li ) {
224 var dividerText = getDividerText( li ),
228 if ( !dividerText ) {
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' );
238 if ( precedingItems.length === 0 && nextItems.length === 0 ) {
239 li.prevAll( '.ui-li-divider:contains(' + dividerText + '):first' ).remove();
249 // set up the dividers on first create
250 list.find( 'li' ).each( function () {
253 // remove existing dividers
254 if ( li.jqmData( 'role' ) === 'list-divider' ) {
256 } else { // make new dividers for list items
261 // bind to DOM events to keep list up to date
262 list.bind( 'DOMNodeInserted', function ( e ) {
263 var elt = $( e.target );
265 if ( !isNonDividerLi( elt ) ) {
272 list.bind( 'DOMNodeRemoved', function ( e ) {
273 var elt = $( e.target );
275 if ( !isNonDividerLi( elt ) ) {
283 $.fn.autodividers = autodividers;
285 $( ":jqmData(role=listview)" ).live( "listviewcreate", function () {
286 var list = $( this );
288 if ( list.is( ':jqmData(autodividers)' ) ) {