1 // ==========================================================================
  2 // Project:   The M-Project - Mobile HTML5 Application Framework
  3 // Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
  4 // Creator:   Sebastian
  5 // Date:      04.11.2010
  6 // License:   Dual licensed under the MIT or GPL Version 2 licenses.
  7 //            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
  8 //            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
  9 // ==========================================================================
 10 
 11 /**
 12  * @class
 13  *
 14  * M.TextFieldView is the prototype of any text field input view. It can be rendered as both
 15  * a single line text field and a multiple line text field. If it is styled as a multiple
 16  * line text field, is has a built-in autogrow mechanism so the textfield is getting larger
 17  * depending on the number of lines of text a user enters.
 18  *
 19  * @extends M.View
 20  */
 21 M.TextFieldView = M.View.extend(
 22 /** @scope M.TextFieldView.prototype */ {
 23 
 24    /**
 25      * The type of this object.
 26      *
 27      * @type String
 28      */
 29     type: 'M.TextFieldView',
 30 
 31    /**
 32     * The name of the text field. During the rendering, this property gets assigned to the name
 33     * property of the text field's html representation. This can be used to manually access the
 34     * text field's DOM representation later on.
 35     *
 36     * @type String
 37     */
 38     name: null,
 39 
 40     /**
 41      * The label proeprty defines a text that is shown above or next to the textfield as a 'title'
 42      * for the textfield. e.g. "Name:". If no label is specified, no label will be displayed.
 43      *
 44      * @type String
 45      */
 46     label: null,
 47 
 48     /**
 49      * The initial text shown inside the text field describing the input or making a suggestion for input
 50      * e.g. "Please enter your Name."
 51      *
 52      * @type String
 53      */
 54     initialText: '',
 55 
 56     /**
 57      * Defines whether the text field is rendered as an password field or not.
 58      *
 59      * @type Boolean
 60      */
 61     isPassword: NO,
 62 
 63     /**
 64      * Defines whether the text field has multiple lines respectively is a text area.
 65      *
 66      * @type Boolean
 67      */
 68     hasMultipleLines: NO,
 69 
 70     /**
 71      * Renders a TextFieldView
 72      * 
 73      * @private
 74      * @returns {String} The text field view's html representation.
 75      */
 76     render: function() {
 77         this.html += '<div ' + this.style() + ' data-role="fieldcontain">';
 78 
 79         if(this.label) {
 80             this.html += '<label for="' + (this.name ? this.name : this.id) + '">' + this.label + '</label>';
 81         }
 82 
 83         var type = this.isPassword ? 'password' : 'text';
 84 
 85         if(this.hasMultipleLines) {
 86             this.html += '<textarea cols="40" rows="8" name="' + (this.name ? this.name : this.id) + '" id="' + this.id + '">' + (this.value ? this.value : this.initialText) + '</textarea>';
 87             
 88         } else {
 89             this.html += '<input type="' + type + '" name="' + (this.name ? this.name : this.id) + '" id="' + this.id + '" value="' + (this.value ? this.value : this.initialText) + '" />';
 90         }
 91 
 92         this.html += '</div>';
 93 
 94         return this.html;
 95     },
 96 
 97     /**
 98      * Updates a TextFieldView with DOM access by jQuery.
 99      *
100      * @private
101      */
102     renderUpdate: function() {
103         $('#' + this.id).val(this.value);
104         this.styleUpdate();
105     },
106 
107     /**
108      * This method is called whenever the view gets the focus.
109      * If there is a initial text specified and the value of this text field
110      * still equals this initial text, the value is emptied.
111      */
112     gotFocus: function() {
113         if(this.initialText && (!this.value || this.initialText === this.value)) {
114             this.setValue('');
115             if(this.cssClassOnInit) {
116                 this.removeCssClass(this.cssClassOnInit);
117             }
118         }
119         this.hasFocus = YES;
120     },
121 
122     /**
123      * This method is called whenever the view lost the focus.
124      * If there is a initial text specified and the value of this text field
125      * is empty, the value is set to the initial text.
126      */
127     lostFocus: function() {
128         if(this.initialText && !this.value) {
129             this.setValue(this.initialText, NO);
130             this.value = '';
131             if(this.cssClassOnInit) {
132                 this.addCssClass(this.cssClassOnInit);
133             }
134         }
135         this.hasFocus = NO;
136     },
137 
138     /**
139      * Method to append css styles inline to the rendered text field.
140      *
141      * @private
142      * @returns {String} The text field's styling as html representation.
143      */
144     style: function() {
145         var html = '';
146         if(this.isInline) {
147             if(!html) {
148                 html += 'style="';
149             }
150             html += 'display:inline;';
151         }
152         if(!this.isEnabled) {
153             if(!html) {
154                 html += 'style="';
155             }
156             html += 'disabled="disabled"';
157         }
158         if(html) {
159             html += '"';
160         }
161         return html;
162     },
163 
164     /**
165      * Triggers the rendering engine, jQuery mobile, to style the text field.
166      *
167      * @private
168      */
169     theme: function() {
170         if(this.initialText && !this.value && this.cssClassOnInit) {
171             this.addCssClass(this.cssClassOnInit);
172         }
173     },
174 
175     /**
176      * Method to append css styles inline to the rendered view on the fly.
177      *
178      * @private
179      */
180     styleUpdate: function() {
181         if(this.isInline) {
182             $('#' + this.id).attr('display', 'inline');
183         } else {
184             $('#' + this.id).removeAttr('display');
185         }
186 
187         if(!this.isEnabled) {
188             $('#' + this.id).attr('disabled', 'disabled');
189         } else {
190             $('#' + this.id).removeAttr('disabled');
191         }
192     },
193 
194     /**
195      * This method sets its value to the value it has in its DOM representation
196      * and then delegates these changes to a controller property if the
197      * contentBindingReverse property is set.
198      *
199      * Additionally call target / action if set.
200      *
201      * @param {Object} evt The event triggered this method.
202      */
203     setValueFromDOM: function(evt) {
204         this.value = this.secure($('#' + this.id).val());
205         this.delegateValueUpdate();
206 
207         if((evt === 'change' && this.triggerActionOnChange || evt === 'keyup' && this.triggerActionOnKeyUp) && this.target && this.action) {
208             this.target[this.action](this.value);
209         }
210     },
211 
212     /**
213      * This method sets the text field's value, initiates its re-rendering
214      * and call the delegateValueUpdate().
215      *
216      * @param {String} value The value to be applied to the text field view.
217      * @param {Boolean} delegateUpdate Determines whether to delegate this value update to any observer or not.
218      */
219     setValue: function(value, delegateUpdate) {
220         this.value = value;
221         this.renderUpdate();
222 
223         if(delegateUpdate) {
224             this.delegateValueUpdate();
225         }
226     },
227 
228     /**
229      * This method disables the text field by setting the disabled property of its
230      * html representation to true.
231      */
232     disable: function() {
233         this.isEnabled = NO;
234         this.renderUpdate();
235     },
236 
237     /**
238      * This method enables the text field by setting the disabled property of its
239      * html representation to false.
240      */
241     enable: function() {
242         this.isEnabled = YES;
243         this.renderUpdate();
244     }
245 
246 });