1 // ==========================================================================
  2 // Project:   The M-Project - Mobile HTML5 Application Framework
  3 // Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
  4 //            (c) 2011 panacoda GmbH. All rights reserved.
  5 // Creator:   Dominik
  6 // Date:      26.11.2010
  7 // License:   Dual licensed under the MIT or GPL Version 2 licenses.
  8 //            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
  9 //            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
 10 // ==========================================================================
 11 
 12 /**
 13  * @class
 14  *
 15  * M.SearchBarView defines a prototype of a search bar that can be used inside of a list
 16  * view or independently as a plain input field with a search styling.
 17  *
 18  * @extends M.View
 19  */
 20 M.SearchBarView = M.View.extend(
 21 /** @scope M.SearchBarView.prototype */ {
 22 
 23     /**
 24      * The type of this object.
 25      *
 26      * @type String
 27      */
 28     type: 'M.SearchBarView',
 29 
 30     /**
 31      * Determines whether the search bar is part of a list view.
 32      *
 33      * @type Boolean
 34      */
 35     isListViewSearchBar: NO,
 36 
 37     /**
 38      * If the search bar belongs to a list view, this property contains this
 39      * list view.
 40      *
 41      * @type M.ListView
 42      */
 43     listView: null,
 44 
 45     /**
 46      * The initial text shown inside the search bar field describing the input or making a suggestion for
 47      * input e.g. "Please enter your Name."
 48      *
 49      * @type String
 50      */
 51     initialText: '',
 52 
 53     /**
 54      * This property specifies the recommended events for this type of view.
 55      *
 56      * @type Array
 57      */
 58     recommendedEvents: ['focus', 'blur', 'enter', 'keyup'],
 59 
 60     /**
 61      * Renders a search bar.
 62      *
 63      * @private
 64      * @returns {String} The search bar view's html representation.
 65      */
 66     render: function() {
 67         this.html += '<form role="search"' + this.style() + '>';
 68 
 69         this.html += '<input id="' + this.id + '" type="search" value="' + (this.value ? this.value : this.initialText) + '" class="' + this.cssClass + '" />';
 70 
 71         this.html += '</form>';
 72 
 73         return this.html;
 74     },
 75 
 76     /**
 77      * This method is responsible for registering events for view elements and its child views. It
 78      * basically passes the view's event-property to M.EventDispatcher to bind the appropriate
 79      * events.
 80      *
 81      * It extend M.View's registerEvents method with some special stuff for text field views and
 82      * their internal events.
 83      */
 84     registerEvents: function() {
 85         this.internalEvents = {
 86             focus: {
 87                 target: this,
 88                 action: 'gotFocus'
 89             },
 90             blur: {
 91                 target: this,
 92                 action: 'lostFocus'
 93             },
 94             keyup: {
 95                 target: this,
 96                 action: 'setValueFromDOM'
 97             }
 98         }
 99         this.bindToCaller(this, M.View.registerEvents)();
100     },
101 
102     /**
103      * Updates a SearchBarView with DOM access by jQuery.
104      *
105      * @private
106      */
107     renderUpdate: function() {
108         $('#' + this.id).val(this.value);
109         this.styleUpdate();
110     },
111 
112     /**
113      * This method sets its value to the value it has in its DOM representation
114      * and then delegates these changes to a controller property if the
115      * contentBindingReverse property is set.
116      *
117      * Additionally call target / action if set.
118      *
119      * @param {Object} evt The event triggered this method.
120      */
121     setValueFromDOM: function(id, event, nextEvent) {
122         this.value = this.secure($('#' + this.id).val());
123         this.delegateValueUpdate();
124 
125         if(nextEvent) {
126             M.EventDispatcher.callHandler(nextEvent, event, YES);
127         }
128     },
129 
130     /**
131      * Applies some style-attributes to the button.
132      *
133      * @private
134      * @returns {String} The search bar's styling as html representation.
135      */
136     style: function() {
137         var html = '';
138         if(this.isListViewSearchBar) {
139             html += ' class="ui-listview-filter"';
140         }
141         return html;
142     },
143 
144     /**
145      * Method to append css styles inline to the rendered view on the fly.
146      *
147      * @private
148      */
149     styleUpdate: function() {
150         if(this.isInline) {
151             $('#' + this.id).attr('display', 'inline');
152         } else {
153             $('#' + this.id).removeAttr('display');
154         }
155 
156         if(!this.isEnabled) {
157             $('#' + this.id).attr('disabled', 'disabled');
158         } else {
159             $('#' + this.id).removeAttr('disabled');
160         }
161     },
162 
163     /**
164      * This method is called whenever the view gets the focus.
165      * If there is a initial text specified and the value of this search bar field
166      * still equals this initial text, the value is emptied.
167      */
168     gotFocus: function() {
169         if(this.initialText && (!this.value || this.initialText === this.value)) {
170             this.setValue('');
171             if(this.cssClassOnInit) {
172                 this.removeCssClass(this.cssClassOnInit);
173             }
174         }
175         this.hasFocus = YES;
176     },
177 
178     /**
179      * This method is called whenever the view lost the focus.
180      * If there is a initial text specified and the value of this search bar field
181      * is empty, the value is set to the initial text.
182      */
183     lostFocus: function() {
184         if(this.initialText && !this.value) {
185             this.setValue(this.initialText, NO);
186             this.value = '';
187             if(this.cssClassOnInit) {
188                 this.addCssClass(this.cssClassOnInit);
189             }
190         }
191         this.hasFocus = NO;
192     },
193 
194     /**
195      * This method sets the text field's value, initiates its re-rendering
196      * and call the delegateValueUpdate().
197      *
198      * @param {String} value The value to be applied to the text field view.
199      * @param {Boolean} delegateUpdate Determines whether to delegate this value update to any observer or not.
200      */
201     setValue: function(value, delegateUpdate) {
202         this.value = value;
203         this.renderUpdate();
204 
205         if(delegateUpdate) {
206             this.delegateValueUpdate();
207         }
208     },
209 
210     /**
211      * This method disables the search bar by setting the disabled property of its
212      * html representation to true.
213      */
214     disable: function() {
215         this.isEnabled = NO;
216         this.renderUpdate();
217     },
218 
219     /**
220      * This method enables the search bar by setting the disabled property of its
221      * html representation to false.
222      */
223     enable: function() {
224         this.isEnabled = YES;
225         this.renderUpdate();
226     },
227 
228     /**
229      * This method clears the search bar's value, both in the DOM and within the JS object.
230      */
231     clearValue: function() {
232         this.setValue('');
233 
234         /* call lostFocus() to get the initial text displayed */
235         this.lostFocus();
236     },
237 
238     /**
239      * Triggers the rendering engine, jQuery mobile, to style the search bar field.
240      *
241      * @private
242      */
243     theme: function() {
244         if(this.initialText && !this.value && this.cssClassOnInit) {
245             this.addCssClass(this.cssClassOnInit);
246         }
247 
248         /* register tap event for delete button */
249         var that = this;
250         $('#' + this.id).siblings('a.ui-input-clear').bind('tap', function() {
251             that.setValue('', YES);
252         });
253     },
254 
255     /**
256      * This method returns the search bar view's value.
257      *
258      * @returns {String} The search bar view's value.
259      */
260     getValue: function() {
261         return this.value;
262     }
263 
264 });