Tizen 2.1 base
[platform/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         }
187         else if (!o.controlPanelShowIn) {
188             o.controlPanelShowIn = o.modesAvailable[0];
189         }
190
191         if (!this._validOption('controlPanelShowIn', o.controlPanelShowIn, o)) {
192             return;
193         }
194
195         // done setting options
196         this.options = o;
197
198         // mark the controls and the list with a class
199         this.element.removeClass(this._listviewCssClass).addClass(this._listviewCssClass);
200         this.controlPanel.removeClass(this._controlsCssClass).addClass(this._controlsCssClass);
201
202         // show the widget
203         if (page && !page.is(':visible')) {
204             page.bind('pageshow', function () { self.refresh(); });
205         }
206         else {
207             this.refresh();
208         }
209     },
210
211     _validOption: function (varName, value, otherOptions) {
212         var ok = false;
213
214         if (varName === 'mode') {
215             ok = ($.inArray(value, otherOptions.modesAvailable) >= 0);
216         }
217         else if (varName === 'controlPanelSelector') {
218             ok = ($.type(value) === 'string');
219         }
220         else if (varName === 'modesAvailable') {
221             ok = ($.isArray(value) && value.length > 1);
222
223             if (ok) {
224                 for (var i = 0; i < value.length; i++) {
225                     if (value[i] === '' || $.type(value[i]) !== 'string') {
226                         ok = false;
227                     }
228                 }
229             }
230         }
231         else if (varName === 'controlPanelShowIn') {
232             ok = ($.inArray(value, otherOptions.modesAvailable) >= 0);
233         }
234
235         return ok;
236     },
237
238     _setOption: function (varName, value) {
239         var oldValue = this.options[varName];
240
241         if (oldValue !== value && this._validOption(varName, value, this.options)) {
242             this.options[varName] = value;
243             this.refresh();
244         }
245     },
246
247     visibleListItems: function () {
248         return this.element.find('li:not(:jqmData(role=list-divider)):visible');
249     },
250
251     refresh: function () {
252         var self = this,
253             triggerUpdateLayout = false,
254             isVisible = null,
255             showIn,
256             modalElements;
257
258         // hide/show the control panel and hide/show controls inside
259         // list items based on their "show-in" option
260         isVisible = this.controlPanel.is(':visible');
261
262         if (this.options.mode === this.options.controlPanelShowIn) {
263             this.controlPanel.show();
264         }
265         else {
266             this.controlPanel.hide();
267         }
268
269         if (this.controlPanel.is(':visible') !== isVisible) {
270             triggerUpdateLayout = true;
271         }
272
273         // we only operate on elements inside list items which aren't dividers
274         modalElements = this.element.find('li:not(:jqmData(role=list-divider))')
275                                     .find(':jqmData(listviewcontrols-show-in)');
276
277         modalElements.each(function () {
278             showIn = $(this).jqmData('listviewcontrols-show-in');
279
280             isVisible = $(this).is(':visible');
281
282             if (showIn === self.options.mode) {
283                 $(this).show();
284             }
285             else {
286                 $(this).hide();
287             }
288
289             if ($(this).is(':visible') !== isVisible) {
290                 triggerUpdateLayout = true;
291             }
292         });
293
294         if (triggerUpdateLayout) {
295             this.element.trigger('updatelayout');
296         }
297     }
298 });
299
300 $('ul').live('listviewcreate', function () {
301         var list = $(this);
302
303         if (list.is(':jqmData(listviewcontrols)')) {
304                 list.listviewcontrols();
305         }
306 });
307
308 })(jQuery);