allow string based values for comboboxarray
[roojs1] / Roo / Editor.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12 /**
13  * @class Roo.Editor
14  * @extends Roo.Component
15  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
16  * @constructor
17  * Create a new Editor
18  * @param {Roo.form.Field} field The Field object (or descendant)
19  * @param {Object} config The config object
20  */
21 Roo.Editor = function(field, config){
22     Roo.Editor.superclass.constructor.call(this, config);
23     this.field = field;
24     this.addEvents({
25         /**
26              * @event beforestartedit
27              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28              * false from the handler of this event.
29              * @param {Editor} this
30              * @param {Roo.Element} boundEl The underlying element bound to this editor
31              * @param {Mixed} value The field value being set
32              */
33         "beforestartedit" : true,
34         /**
35              * @event startedit
36              * Fires when this editor is displayed
37              * @param {Roo.Element} boundEl The underlying element bound to this editor
38              * @param {Mixed} value The starting field value
39              */
40         "startedit" : true,
41         /**
42              * @event beforecomplete
43              * Fires after a change has been made to the field, but before the change is reflected in the underlying
44              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
45              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
46              * event will not fire since no edit actually occurred.
47              * @param {Editor} this
48              * @param {Mixed} value The current field value
49              * @param {Mixed} startValue The original field value
50              */
51         "beforecomplete" : true,
52         /**
53              * @event complete
54              * Fires after editing is complete and any changed value has been written to the underlying field.
55              * @param {Editor} this
56              * @param {Mixed} value The current field value
57              * @param {Mixed} startValue The original field value
58              */
59         "complete" : true,
60         /**
61          * @event specialkey
62          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
63          * {@link Roo.EventObject#getKey} to determine which key was pressed.
64          * @param {Roo.form.Field} this
65          * @param {Roo.EventObject} e The event object
66          */
67         "specialkey" : true
68     });
69 };
70
71 Roo.extend(Roo.Editor, Roo.Component, {
72     /**
73      * @cfg {Boolean/String} autosize
74      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
75      * or "height" to adopt the height only (defaults to false)
76      */
77     /**
78      * @cfg {Boolean} revertInvalid
79      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
80      * validation fails (defaults to true)
81      */
82     /**
83      * @cfg {Boolean} ignoreNoChange
84      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
85      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
86      * will never be ignored.
87      */
88     /**
89      * @cfg {Boolean} hideEl
90      * False to keep the bound element visible while the editor is displayed (defaults to true)
91      */
92     /**
93      * @cfg {Mixed} value
94      * The data value of the underlying field (defaults to "")
95      */
96     value : "",
97     /**
98      * @cfg {String} alignment
99      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
100      */
101     alignment: "c-c?",
102     /**
103      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
104      * for bottom-right shadow (defaults to "frame")
105      */
106     shadow : "frame",
107     /**
108      * @cfg {Boolean} constrain True to constrain the editor to the viewport
109      */
110     constrain : false,
111     /**
112      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
113      */
114     completeOnEnter : false,
115     /**
116      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
117      */
118     cancelOnEsc : false,
119     /**
120      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
121      */
122     updateEl : false,
123
124     // private
125     onRender : function(ct, position){
126         this.el = new Roo.Layer({
127             shadow: this.shadow,
128             cls: "x-editor",
129             parentEl : ct,
130             shim : this.shim,
131             shadowOffset:4,
132             id: this.id,
133             constrain: this.constrain
134         });
135         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
136         if(this.field.msgTarget != 'title'){
137             this.field.msgTarget = 'qtip';
138         }
139         this.field.render(this.el);
140         if(Roo.isGecko){
141             this.field.el.dom.setAttribute('autocomplete', 'off');
142         }
143         this.field.on("specialkey", this.onSpecialKey, this);
144         if(this.swallowKeys){
145             this.field.el.swallowEvent(['keydown','keypress']);
146         }
147         this.field.show();
148         this.field.on("blur", this.onBlur, this);
149         if(this.field.grow){
150             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
151         }
152     },
153
154     onSpecialKey : function(field, e)
155     {
156         //Roo.log('editor onSpecialKey');
157         if(this.completeOnEnter && e.getKey() == e.ENTER){
158             e.stopEvent();
159             this.completeEdit();
160             return;
161         }
162         // do not fire special key otherwise it might hide close the editor...
163         if(e.getKey() == e.ENTER){    
164             return;
165         }
166         if(this.cancelOnEsc && e.getKey() == e.ESC){
167             this.cancelEdit();
168             return;
169         } 
170         this.fireEvent('specialkey', field, e);
171     
172     },
173
174     /**
175      * Starts the editing process and shows the editor.
176      * @param {String/HTMLElement/Element} el The element to edit
177      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
178       * to the innerHTML of el.
179      */
180     startEdit : function(el, value){
181         if(this.editing){
182             this.completeEdit();
183         }
184         this.boundEl = Roo.get(el);
185         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
186         if(!this.rendered){
187             this.render(this.parentEl || document.body);
188         }
189         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
190             return;
191         }
192         this.startValue = v;
193         this.field.setValue(v);
194         if(this.autoSize){
195             var sz = this.boundEl.getSize();
196             switch(this.autoSize){
197                 case "width":
198                 this.setSize(sz.width,  "");
199                 break;
200                 case "height":
201                 this.setSize("",  sz.height);
202                 break;
203                 default:
204                 this.setSize(sz.width,  sz.height);
205             }
206         }
207         this.el.alignTo(this.boundEl, this.alignment);
208         this.editing = true;
209         if(Roo.QuickTips){
210             Roo.QuickTips.disable();
211         }
212         this.show();
213     },
214
215     /**
216      * Sets the height and width of this editor.
217      * @param {Number} width The new width
218      * @param {Number} height The new height
219      */
220     setSize : function(w, h){
221         this.field.setSize(w, h);
222         if(this.el){
223             this.el.sync();
224         }
225     },
226
227     /**
228      * Realigns the editor to the bound field based on the current alignment config value.
229      */
230     realign : function(){
231         this.el.alignTo(this.boundEl, this.alignment);
232     },
233
234     /**
235      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
236      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
237      */
238     completeEdit : function(remainVisible){
239         if(!this.editing){
240             return;
241         }
242         var v = this.getValue();
243         if(this.revertInvalid !== false && !this.field.isValid()){
244             v = this.startValue;
245             this.cancelEdit(true);
246         }
247         if(String(v) === String(this.startValue) && this.ignoreNoChange){
248             this.editing = false;
249             this.hide();
250             return;
251         }
252         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
253             this.editing = false;
254             if(this.updateEl && this.boundEl){
255                 this.boundEl.update(v);
256             }
257             if(remainVisible !== true){
258                 this.hide();
259             }
260             this.fireEvent("complete", this, v, this.startValue);
261         }
262     },
263
264     // private
265     onShow : function(){
266         this.el.show();
267         if(this.hideEl !== false){
268             this.boundEl.hide();
269         }
270         this.field.show();
271         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
272             this.fixIEFocus = true;
273             this.deferredFocus.defer(50, this);
274         }else{
275             this.field.focus();
276         }
277         this.fireEvent("startedit", this.boundEl, this.startValue);
278     },
279
280     deferredFocus : function(){
281         if(this.editing){
282             this.field.focus();
283         }
284     },
285
286     /**
287      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
288      * reverted to the original starting value.
289      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
290      * cancel (defaults to false)
291      */
292     cancelEdit : function(remainVisible){
293         if(this.editing){
294             this.setValue(this.startValue);
295             if(remainVisible !== true){
296                 this.hide();
297             }
298         }
299     },
300
301     // private
302     onBlur : function(){
303         if(this.allowBlur !== true && this.editing){
304             this.completeEdit();
305         }
306     },
307
308     // private
309     onHide : function(){
310         if(this.editing){
311             this.completeEdit();
312             return;
313         }
314         this.field.blur();
315         if(this.field.collapse){
316             this.field.collapse();
317         }
318         this.el.hide();
319         if(this.hideEl !== false){
320             this.boundEl.show();
321         }
322         if(Roo.QuickTips){
323             Roo.QuickTips.enable();
324         }
325     },
326
327     /**
328      * Sets the data value of the editor
329      * @param {Mixed} value Any valid value supported by the underlying field
330      */
331     setValue : function(v){
332         this.field.setValue(v);
333     },
334
335     /**
336      * Gets the data value of the editor
337      * @return {Mixed} The data value
338      */
339     getValue : function(){
340         return this.field.getValue();
341     }
342 });