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.
111 (function ( $, undefined ) {
113 var autodividers = function ( options ) {
114 var list = $( this ),
115 listview = list.data( 'listview' ),
124 options = options || {};
125 dividerType = options.type || list.jqmData( 'autodividers' ) || 'alpha';
126 textSelector = options.selector || list.jqmData( 'autodividers-selector' ) || 'a';
128 getDividerText = function ( elt ) {
129 // look for some text in the item
130 var text = elt.find( textSelector ).text() || elt.text() || null;
136 // create the text for the divider
137 if ( dividerType === 'alpha' ) {
138 text = text.slice( 0, 1 ).toUpperCase();
144 mergeDividers = function () {
145 var dividersChanged = false,
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 () {
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 + ')' );
160 if ( nextDividers.length > 0 ) {
161 nextDividers.remove();
162 dividersChanged = true;
166 if ( dividersChanged ) {
167 list.trigger( 'updatelayout' );
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';
177 // li element inserted, so check whether it needs a divider
178 liAdded = function ( li ) {
179 var dividerText = getDividerText( li ),
183 if ( !dividerText ) {
188 // add expected divider for this li if it doesn't exist
189 existingDividers = li.prevAll( '.ui-li-divider:first:contains(' + dividerText + ')' );
191 if ( existingDividers.length === 0 ) {
192 divider = $( '<li>' + dividerText + '</li>' );
193 divider.attr( 'data-' + $.mobile.ns + 'role', 'list-divider' );
194 li.before( divider );
204 // li element removed, so check whether its divider should go
205 liRemoved = function ( li ) {
206 var dividerText = getDividerText( li ),
210 if ( !dividerText ) {
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' );
220 if ( precedingItems.length === 0 && nextItems.length === 0 ) {
221 li.prevAll( '.ui-li-divider:contains(' + dividerText + '):first' ).remove();
231 // set up the dividers on first create
232 list.find( 'li' ).each( function () {
235 // remove existing dividers
236 if ( li.jqmData( 'role' ) === 'list-divider' ) {
238 } else { // make new dividers for list items
243 // bind to DOM events to keep list up to date
244 list.bind( 'DOMNodeInserted', function ( e ) {
245 var elt = $( e.target );
247 if ( !isNonDividerLi( elt ) ) {
254 list.bind( 'DOMNodeRemoved', function ( e ) {
255 var elt = $( e.target );
257 if ( !isNonDividerLi( elt ) ) {
265 $.fn.autodividers = autodividers;
267 $( ":jqmData(role=listview)" ).live( "listviewcreate", function () {
268 var list = $( this );
270 if ( list.is( ':jqmData(autodividers)' ) ) {