2 * jQuery Mobile Widget @VERSION
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) 2000 - 2011 Samsung Electronics Co., Ltd.
9 * Copyright (c) 2011 by Intel Corporation Ltd.
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
18 * The above copyright notice and this permission notice shall be included in
19 * all copies or substantial portions of the Software.
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 * ***************************************************************************
30 * Authors: Elliot Smith <elliot.smith@intel.com>
33 // fastscroll is a scrollview controller, which binds
34 // a scrollview to a a list of short cuts; the shortcuts are built
35 // from the text on dividers in the list. Clicking on a shortcut
36 // instantaneously jumps the scrollview to the selected list divider;
37 // mouse movements on the shortcut column move the scrollview to the
38 // list divider matching the text currently under the touch; a popup
39 // with the text currently under the touch is also displayed.
41 // To apply, add the attribute data-fastscroll="true" to a listview
42 // (a <ul> or <ol> element inside a page). Alternatively, call
43 // fastscroll() on an element.
45 // The closest element with class ui-scrollview-clip is used as the
46 // scrollview to be controlled.
48 // If a listview has no dividers or a single divider, the widget won't
53 The shortcut scroll widget shows a shortcut list that is bound to its parent scroll bar and respective list view. This widget is displayed as a text pop-up representing shortcuts to different list dividers in the list view. If you select a shortcut text from the shortcut scroll, the parent list view is moved to the location representing the selected shortcut.
55 To add a shortcut scroll widget to the application, use the following code:
57 <div class="content" data-role="content" data-scroll="y">
58 <ul id="contacts" data-role="listview" data-fastscroll="true">
63 For the shortcut scroll widget to be visible, the parent list view must have multiple list dividers.
67 @property {Boolean} data-fastscroll
68 When set to true, creates a shortcut scroll using the HTML unordered list (<ul>) element.
72 The shortcut scroll is created for the closest list view with the ui-scrollview-clip class.
74 (function ( $, undefined ) {
76 $.widget( "tizen.fastscroll", $.mobile.widget, {
78 initSelector: ":jqmData(fastscroll)"
81 _create: function () {
82 var $el = this.element,
85 page = $el.closest( ':jqmData(role="page")' ),
88 this.scrollview = $el.closest( '.ui-scrollview-clip' );
89 this.shortcutsContainer = $( '<div class="ui-fastscroll"/>' );
90 this.shortcutsList = $( '<ul></ul>' );
92 // popup for the hovering character
93 this.scrollview.append($( '<div class="ui-fastscroll-popup"></div>' ) );
94 $popup = this.scrollview.find( '.ui-fastscroll-popup' );
96 this.shortcutsContainer.append( this.shortcutsList );
97 this.scrollview.append( this.shortcutsContainer );
99 // find the bottom of the last item in the listview
100 this.lastListItem = $el.children().last();
102 // remove scrollbars from scrollview
103 this.scrollview.find( '.ui-scrollbar' ).hide();
105 jumpToDivider = function ( divider ) {
106 // get the vertical position of the divider (so we can scroll to it)
107 var dividerY = $( divider ).position().top,
108 // find the bottom of the last list item
109 bottomOffset = self.lastListItem.outerHeight( true ) + self.lastListItem.position().top,
110 scrollviewHeight = self.scrollview.height(),
112 // check that after the candidate scroll, the bottom of the
113 // last item will still be at the bottom of the scroll view
114 // and not some way up the page
115 maxScroll = bottomOffset - scrollviewHeight,
118 dividerY = ( dividerY > maxScroll ? maxScroll : dividerY );
120 // don't apply a negative scroll, as this means the
121 // divider should already be visible
122 dividerY = Math.max( dividerY, 0 );
125 self.scrollview.scrollview( 'scrollTo', 0, -dividerY );
127 dstOffset = self.scrollview.offset();
129 .text( $( divider ).text() )
130 .css( { marginLeft: -($popup.width() / 2),
131 marginTop: -($popup.height() / 2) } )
136 // bind mouse over so it moves the scroller to the divider
137 .bind( 'touchstart mousedown vmousedown touchmove vmousemove vmouseover ', function ( e ) {
138 // Get coords relative to the element
139 var coords = $.mobile.tizen.targetRelativeCoordsFromEvent( e ),
140 shortcutsListOffset = self.shortcutsList.offset();
142 // If the element is a list item, get coordinates relative to the shortcuts list
143 if ( e.target.tagName.toLowerCase() === "li" ) {
144 coords.x += $( e.target ).offset().left - shortcutsListOffset.left;
145 coords.y += $( e.target ).offset().top - shortcutsListOffset.top;
148 self.shortcutsList.find( 'li' ).each( function () {
149 var listItem = $( this );
151 .removeClass( "ui-fastscroll-hover" )
152 .removeClass( "ui-fastscroll-hover-up" )
153 .removeClass( "ui-fastscroll-hover-down" );
155 // Hit test each list item
156 self.shortcutsList.find( 'li' ).each( function () {
157 var listItem = $( this ),
158 l = listItem.offset().left - shortcutsListOffset.left,
159 t = listItem.offset().top - shortcutsListOffset.top,
160 r = l + Math.abs(listItem.outerWidth( true ) ),
161 b = t + Math.abs(listItem.outerHeight( true ) );
163 if ( coords.x >= l && coords.x <= r && coords.y >= t && coords.y <= b ) {
164 jumpToDivider( $( listItem.data( 'divider' ) ) );
165 $( listItem ).addClass( "ui-fastscroll-hover" );
166 if ( listItem.index() > 0 ) {
167 $( listItem ).siblings().eq( listItem.index() - 1 ).addClass( "ui-fastscroll-hover-up" );
169 $( listItem ).siblings().eq( listItem.index() ).addClass( "ui-fastscroll-hover-down" );
180 // bind mouseout of the fastscroll container to remove popup
181 .bind( 'touchend mouseup vmouseup vmouseout', function () {
185 if ( page && !( page.is( ':visible' ) ) ) {
186 page.bind( 'pageshow', function () { self.refresh(); } );
191 // refresh the list when dividers are filtered out
192 $el.bind( 'updatelayout', function () {
197 refresh: function () {
204 this.shortcutsList.find( 'li' ).remove();
206 // get all the dividers from the list and turn them into shortcuts
207 dividers = this.element.find( '.ui-li-divider' );
209 // get all the list items
210 listItems = this.element.find('li').not('.ui-li-divider');
212 // only use visible dividers
213 dividers = dividers.filter( ':visible' );
214 listItems = listItems.filter( ':visible' );
216 if ( dividers.length < 2 ) {
217 this.shortcutsList.hide();
221 this.shortcutsList.show();
223 this.lastListItem = listItems.last();
225 dividers.each( function ( index, divider ) {
227 .append( $( '<li>' + $( divider ).text() + '</li>' )
228 .data( 'divider', divider ) );
231 // position the shortcut flush with the top of the first list divider
232 shortcutsTop = dividers.first().position().top;
233 this.shortcutsContainer.css( 'top', shortcutsTop );
235 // make the scrollview clip tall enough to show the whole of the shortcutslist
236 minClipHeight = shortcutsTop + this.shortcutsContainer.outerHeight() + 'px';
237 this.scrollview.css( 'min-height', minClipHeight );
241 $( document ).bind( "pagecreate create", function ( e ) {
242 $( $.tizen.fastscroll.prototype.options.initSelector, e.target )
243 .not( ":jqmData(role='none'), :jqmData(role='nojs')" )