Revert "Export"
[framework/web/web-ui-fw.git] / src / widgets / listviewcontrols / js / listviewcontrols.js
1 /*
2  * jQuery Mobile Widget @VERSION - listview controls
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 // This extension supplies API to toggle the "mode" in which a list
33 // is displayed. The modes available is configurable, but defaults
34 // to ['edit', 'view']. A list can also have a control panel associated
35 // with it. The visibility of the control panel is governed by the current
36 // mode (by default, it is visible in 'edit' mode); elements within
37 // the listview can also be marked up to be visible in one or more of the
38 // available modes.
39 //
40 // One example use case would be a control panel with a "Select all" checkbox
41 // which, when clicked, selects all of the checkboxes in the associated
42 // listview items.
43 //
44 // The control panel itself should be defined as a form element.
45 // By default, the control panel will be hidden when the listview is
46 // initialised, unless you supply mode="edit" as a
47 // data-listview-controls option (when using the default modes). If you
48 // want the control panel to be visible in some mode other than
49 // the default, use a data-listviewcontrols-show-in="<mode>" attribute
50 // on the control panel element.
51 //
52 // Example usage (using the default 'edit' and 'view' modes):
53 //
54 // <!-- this is the controls element, displayed in 'edit' mode by default -->
55 // <form id="listviewcontrols-control-panel">
56 //   <fieldset data-role="controlgroup">
57 //     <input type="checkbox" id="listviewcontrols-demo-checkbox-uber" />
58 //     <label for="listviewcontrols-demo-checkbox-uber">Select all</label>
59 //   </fieldset>
60 // </form>
61 //
62 // <!-- this is the list associated with the controls -->
63 // <ul data-role="listview" data-listviewcontrols="#listviewcontrols-control-panel">
64 //
65 //   <li>
66 //
67 //     <!-- this element is only visible in 'edit' mode -->
68 //     <fieldset data-role="controlgroup" data-listviewcontrols-show-in="edit">
69 //       <input type="checkbox" id="listviewcontrols-demo-checkbox-1" />
70 //       <label for="listviewcontrols-demo-checkbox-1">Greg</label>
71 //     </fieldset>
72 //
73 //     <!-- this element is only visible in 'view' mode -->
74 //     <span data-listviewcontrols-show-in="view">Greg</span>
75 //
76 //   </li>
77 //
78 //   ... more li elements marked up the same way ...
79 //
80 // </ul>
81 //
82 // To associate the listview with the control panel, add
83 // data-listviewcontrols="..selector.." to a listview, where
84 // selector selects a single element (the control panel
85 // you defined). You can then call
86 // listviewcontrols('option', 'mode', '<mode>') on the
87 // listview to set the mode.
88 //
89 // Inside the listview's items, add controls to each item
90 // which are only visible when in one of the modes. To do this,
91 // add form elements (e.g. checkboxes) to the items as you see fit. Then,
92 // mark each form element with data-listviewcontrols-show-in="<mode>".
93 // The control's visibility now depends on the mode of the listviewcontrols:
94 // it is only shown when its <mode> setting matches the current mode
95 // of the listviewcontrols widget. You are responsible for properly
96 // styling the form elements inside the listview so the listview looks
97 // correct when they are hidden or visible.
98 //
99 // The control panel (by default, visible when in "show" mode) is flexible
100 // and can contain any valid form elements (or other jqm components). It's
101 // up to you to define the behaviour associated with interactions on
102 // the control panel and/or controls inside list items.
103 //
104 // Methods:
105 //
106 //   visibleListItems
107 //     Returns a jQuery object containing all the li elements in the
108 //     listview which are currently visible and not dividers. (This
109 //     is just a convenience to make operating on the list as a whole
110 //     slightly simpler.)
111 //
112 // Options (set in options hash passed to constructor, or via the
113 // option method, or declaratively by attribute described below):
114 //
115 //   controlPanelSelector {String}
116 //     Selector string for selecting the element representing the
117 //     control panel for the listview. The context for find() is the
118 //     document (to give the most flexibility), so your selector
119 //     should be specific. Set declaratively with
120 //       data-listviewcontrols="...selector...".
121 //
122 //   modesAvailable {String[]; default=['edit', 'view']}
123 //     An array of the modes available for these controls.
124 //
125 //   mode {String; default='view'}
126 //     Current mode for the widget, which governs the visibility
127 //     of the listview control panel and any elements marked
128 //     with data-listviewcontrols-show-in="<mode>".
129 //     Set declaratively with
130 //       data-listviewcontrols-options='{"mode":"<mode>"}'.
131 //
132 //   controlPanelShowIn {String; default=modesAvailable[0]}
133 //     The mode in which the control panel is visible; defaults to the
134 //     first element of modesAvailable. Can be set declaratively
135 //     on the listview controls element with
136 //       data-listviewcontrols-show-in="<mode>"
137
138 (function ($) {
139
140         $.widget( "todons.listviewcontrols", $.mobile.widget, {
141                 _defaults: {
142                         controlPanelSelector: null,
143                         modesAvailable: ['edit', 'view'],
144                         mode: 'view',
145                         controlPanelShowIn: null
146                 },
147
148                 _listviewCssClass: 'ui-listviewcontrols-listview',
149                 _controlsCssClass: 'ui-listviewcontrols-panel',
150
151                 _create: function () {
152                         var self = this,
153                                 o = this.options,
154                                 optionsValid = true,
155                                 page = this.element.closest( '.ui-page' ),
156                                 controlPanelSelectorAttr = 'data-' + $.mobile.ns + 'listviewcontrols',
157                                 controlPanelSelector = this.element.attr( controlPanelSelectorAttr ),
158                                 dataOptions = this.element.jqmData( 'listviewcontrols-options' ),
159                                 controlPanelShowInAttr;
160
161                         o.controlPanelSelector = o.controlPanelSelector || controlPanelSelector;
162
163                         // precedence for options: defaults < jqmData attribute < options arg
164                         o = $.extend( {}, this._defaults, dataOptions, o );
165
166                         optionsValid = ( this._validOption( 'modesAvailable', o.modesAvailable, o ) &&
167                                         this._validOption( 'controlPanelSelector', o.controlPanelSelector, o ) &&
168                                         this._validOption( 'mode', o.mode, o ) );
169
170                         if ( !optionsValid ) {
171                                 return false;
172                         }
173
174                         // get the controls element
175                         this.controlPanel = $( document ).find( o.controlPanelSelector ).first();
176
177                         if ( this.controlPanel.length === 0 ) {
178                                 return false;
179                         }
180
181                         // once we have the controls element, we may need to override the
182                         // mode in which controls are shown
183                         controlPanelShowInAttr = this.controlPanel.jqmData( 'listviewcontrols-show-in' );
184                         if ( controlPanelShowInAttr ) {
185                                 o.controlPanelShowIn = controlPanelShowInAttr;
186                         } else if ( !o.controlPanelShowIn ) {
187                                 o.controlPanelShowIn = o.modesAvailable[0];
188                         }
189
190                         if ( !this._validOption( 'controlPanelShowIn', o.controlPanelShowIn, o ) ) {
191                                 return;
192                         }
193
194                         // done setting options
195                         this.options = o;
196
197                         // mark the controls and the list with a class
198                         this.element.removeClass(this._listviewCssClass).addClass(this._listviewCssClass);
199                         this.controlPanel.removeClass(this._controlsCssClass).addClass(this._controlsCssClass);
200
201                         // show the widget
202                         if ( page && !page.is( ':visible' ) ) {
203                                 page.bind( 'pageshow', function () { self.refresh(); } );
204                         } else {
205                                 this.refresh();
206                         }
207                 },
208
209                 _validOption: function ( varName, value, otherOptions ) {
210                         var ok = false,
211                                 i = 0;
212
213                         if ( varName === 'mode' ) {
214                                 ok = ( $.inArray( value, otherOptions.modesAvailable ) >= 0 );
215                         } else if ( varName === 'controlPanelSelector' ) {
216                                 ok = ( $.type( value ) === 'string' );
217                         } else if ( varName === 'modesAvailable' ) {
218                                 ok = ( $.isArray( value ) && value.length > 1 );
219
220                                 if ( ok ) {
221                                         for ( i = 0; i < value.length; i++ ) {
222                                                 if ( value[i] === '' || $.type( value[i] ) !== 'string' ) {
223                                                         ok = false;
224                                                 }
225                                         }
226                                 }
227                         } else if ( varName === 'controlPanelShowIn' ) {
228                                 ok = ( $.inArray( value, otherOptions.modesAvailable ) >= 0 );
229                         }
230
231                         return ok;
232                 },
233
234                 _setOption: function ( varName, value ) {
235                         var oldValue = this.options[varName];
236
237                         if ( oldValue !== value && this._validOption( varName, value, this.options ) ) {
238                                 this.options[varName] = value;
239                                 this.refresh();
240                         }
241                 },
242
243                 visibleListItems: function () {
244                         return this.element.find( 'li:not(:jqmData(role=list-divider)):visible' );
245                 },
246
247                 refresh: function () {
248                         var self = this,
249                                 triggerUpdateLayout = false,
250                                 isVisible = null,
251                                 showIn,
252                                 modalElements;
253
254                         // hide/show the control panel and hide/show controls inside
255                         // list items based on their "show-in" option
256                         isVisible = this.controlPanel.is( ':visible' );
257
258                         if ( this.options.mode === this.options.controlPanelShowIn ) {
259                                 this.controlPanel.show();
260                         } else {
261                                 this.controlPanel.hide();
262                         }
263
264                         if ( this.controlPanel.is( ':visible' ) !== isVisible ) {
265                                 triggerUpdateLayout = true;
266                         }
267
268                         // we only operate on elements inside list items which aren't dividers
269                         modalElements = this.element
270                                                                 .find( 'li:not(:jqmData(role=list-divider))' )
271                                                                 .find( ':jqmData(listviewcontrols-show-in)' );
272
273                         modalElements.each(function () {
274                                 showIn = $( this ).jqmData( 'listviewcontrols-show-in' );
275
276                                 isVisible = $( this ).is( ':visible' );
277
278                                 if ( showIn === self.options.mode ) {
279                                         $( this ).show();
280                                 } else {
281                                         $( this ).hide();
282                                 }
283
284                                 if ( $( this ).is( ':visible' ) !== isVisible ) {
285                                         triggerUpdateLayout = true;
286                                 }
287                         } );
288
289                         if ( triggerUpdateLayout ) {
290                                 this.element.trigger( 'updatelayout' );
291                         }
292                 }
293         } );
294
295         $( 'ul' ).live( 'listviewcreate', function () {
296                 var list = $(this);
297
298                 if ( list.is( ':jqmData(listviewcontrols)' ) ) {
299                         list.listviewcontrols();
300                 }
301         } );
302
303 }( jQuery ) );