source: trunk/web/addons/job_monarch/lib/extjs/source/widgets/form/BasicForm.js @ 619

Last change on this file since 619 was 619, checked in by ramonb, 15 years ago

lib/:

  • added new AJAX dependancies: ExtJS, pChart, Lightbox2
File size: 23.2 KB
Line 
1/*
2 * Ext JS Library 2.2.1
3 * Copyright(c) 2006-2009, Ext JS, LLC.
4 * licensing@extjs.com
5 *
6 * http://extjs.com/license
7 */
8
9/**
10 * @class Ext.form.BasicForm
11 * @extends Ext.util.Observable
12 * <p>Encapsulates the DOM &lt;form> element at the heart of the {@link Ext.form.FormPanel FormPanel} class, and provides
13 * input field management, validation, submission, and form loading services.</p>
14 * <p>By default, Ext Forms are submitted through Ajax, using an instance of {@link Ext.form.Action.Submit}.
15 * To enable normal browser submission of an Ext Form, use the {@link #standardSubmit} config option.</p>
16 * <p><h3>File Uploads</h3>{@link #fileUpload File uploads} are not performed using Ajax submission, that
17 * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
18 * manner with the DOM <tt>&lt;form></tt> element temporarily modified to have its
19 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
20 * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
21 * but removed after the return data has been gathered.</p>
22 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
23 * server is using JSON to send the return object, then the
24 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
25 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
26 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
27 * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
28 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
29 * is created containing a <tt>responseText</tt> property in order to conform to the
30 * requirements of event handlers and callbacks.</p>
31 * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
32 * and some server technologies (notably JEE) may require some custom processing in order to
33 * retrieve parameter names and parameter values from the packet content.</p>
34 * @constructor
35 * @param {Mixed} el The form element or its id
36 * @param {Object} config Configuration options
37 */
38Ext.form.BasicForm = function(el, config){
39    Ext.apply(this, config);
40    /*
41     * The Ext.form.Field items in this form.
42     * @type MixedCollection
43     */
44    this.items = new Ext.util.MixedCollection(false, function(o){
45        return o.id || (o.id = Ext.id());
46    });
47    this.addEvents(
48        /**
49         * @event beforeaction
50         * Fires before any action is performed. Return false to cancel the action.
51         * @param {Form} this
52         * @param {Action} action The {@link Ext.form.Action} to be performed
53         */
54        'beforeaction',
55        /**
56         * @event actionfailed
57         * Fires when an action fails.
58         * @param {Form} this
59         * @param {Action} action The {@link Ext.form.Action} that failed
60         */
61        'actionfailed',
62        /**
63         * @event actioncomplete
64         * Fires when an action is completed.
65         * @param {Form} this
66         * @param {Action} action The {@link Ext.form.Action} that completed
67         */
68        'actioncomplete'
69    );
70
71    if(el){
72        this.initEl(el);
73    }
74    Ext.form.BasicForm.superclass.constructor.call(this);
75};
76
77Ext.extend(Ext.form.BasicForm, Ext.util.Observable, {
78    /**
79     * @cfg {String} method
80     * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
81     */
82    /**
83     * @cfg {DataReader} reader
84     * An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read data when executing "load" actions.
85     * This is optional as there is built-in support for processing JSON.
86     */
87    /**
88     * @cfg {DataReader} errorReader
89     * <p>An Ext.data.DataReader (e.g. {@link Ext.data.XmlReader}) to be used to read field error messages returned from "submit" actions.
90     * This is completely optional as there is built-in support for processing JSON.</p>
91     * <p>The Records which provide messages for the invalid Fields must use the Field name (or id) as the Record ID,
92     * and must contain a field called "msg" which contains the error message.</p>
93     * <p>The errorReader does not have to be a full-blown implementation of a DataReader. It simply needs to implement a
94     * <tt>read(xhr)</tt> function which returns an Array of Records in an object with the following structure:<pre><code>
95{
96    records: recordArray
97}
98</code></pre>
99     */
100    /**
101     * @cfg {String} url
102     * The URL to use for form actions if one isn't supplied in the action options.
103     */
104    /**
105     * @cfg {Boolean} fileUpload
106     * Set to true if this form is a file upload.
107     * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
108     * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
109     * DOM <tt>&lt;form></tt> element temporarily modified to have its
110     * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
111     * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
112     * but removed after the return data has been gathered.</p>
113     * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
114     * server is using JSON to send the return object, then the
115     * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
116     * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
117     * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
118     * "&lt;" as "&amp;lt;", "&amp;" as "&amp;amp;" etc.</p>
119     * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
120     * is created containing a <tt>responseText</tt> property in order to conform to the
121     * requirements of event handlers and callbacks.</p>
122     * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
123     * and some server technologies (notably JEE) may require some custom processing in order to
124     * retrieve parameter names and parameter values from the packet content.</p>
125     */
126    /**
127     * @cfg {Object} baseParams
128     * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
129     */
130    /**
131     * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
132     */
133    timeout: 30,
134
135    // private
136    activeAction : null,
137
138    /**
139     * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
140     * or setValues() data instead of when the form was first created.
141     */
142    trackResetOnLoad : false,
143
144    /**
145     * @cfg {Boolean} standardSubmit If set to true, standard HTML form submits are used instead of XHR (Ajax) style
146     * form submissions. (defaults to false)<br>
147     * <p><b>Note:</b> When using standardSubmit, any the options to {@link #submit} are
148     * ignored because Ext's Ajax infrastracture is bypassed. To pass extra parameters, you will need to create
149     * hidden fields within the form.</p>
150     */
151    /**
152     * By default wait messages are displayed with Ext.MessageBox.wait. You can target a specific
153     * element by passing it or its id or mask the form itself by passing in true.
154     * @type Mixed
155     * @property waitMsgTarget
156     */
157
158    // private
159    initEl : function(el){
160        this.el = Ext.get(el);
161        this.id = this.el.id || Ext.id();
162        if(!this.standardSubmit){
163            this.el.on('submit', this.onSubmit, this);
164        }
165        this.el.addClass('x-form');
166    },
167
168    /**
169     * Get the HTML form Element
170     * @return Ext.Element
171     */
172    getEl: function(){
173        return this.el;
174    },
175
176    // private
177    onSubmit : function(e){
178        e.stopEvent();
179    },
180
181    // private
182    destroy: function() {
183        this.items.each(function(f){
184            Ext.destroy(f);
185        });
186        if(this.el){
187            this.el.removeAllListeners();
188            this.el.remove();
189        }
190        this.purgeListeners();
191    },
192
193    /**
194     * Returns true if client-side validation on the form is successful.
195     * @return Boolean
196     */
197    isValid : function(){
198        var valid = true;
199        this.items.each(function(f){
200           if(!f.validate()){
201               valid = false;
202           }
203        });
204        return valid;
205    },
206
207    /**
208     * Returns true if any fields in this form have changed since their original load.
209     * @return Boolean
210     */
211    isDirty : function(){
212        var dirty = false;
213        this.items.each(function(f){
214           if(f.isDirty()){
215               dirty = true;
216               return false;
217           }
218        });
219        return dirty;
220    },
221
222    /**
223     * Performs a predefined action ({@link Ext.form.Action.Submit} or
224     * {@link Ext.form.Action.Load}) or a custom extension of {@link Ext.form.Action}
225     * to perform application-specific processing.
226     * @param {String/Object} actionName The name of the predefined action type,
227     * or instance of {@link Ext.form.Action} to perform.
228     * @param {Object} options (optional) The options to pass to the {@link Ext.form.Action}.
229     * All of the config options listed below are supported by both the submit
230     * and load actions unless otherwise noted (custom actions could also accept
231     * other config options):<ul>
232     * <li><b>url</b> : String<p style="margin-left:1em">The url for the action (defaults
233     * to the form's url.)</p></li>
234     * <li><b>method</b> : String<p style="margin-left:1em">The form method to use (defaults
235     * to the form's method, or POST if not defined)</p></li>
236     * <li><b>params</b> : String/Object<p style="margin-left:1em">The params to pass
237     * (defaults to the form's baseParams, or none if not defined)</p></li>
238     * <li><b>headers</b> : Object<p style="margin-left:1em">Request headers to set for the action
239     * (defaults to the form's default headers)</p></li>
240     * <li><b>success</b> : Function<p style="margin-left:1em">The callback that will
241     * be invoked after a successful response.  The function is passed the following parameters:<ul>
242     * <li><code>form</code> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
243     * <li><code>action</code> : Ext.form.Action<div class="sub-desc">The {@link Ext.form.Action Action} object which performed the operation. The {@link Ext.form.Action#result result}
244     * property of this object may be examined to perform custom postprocessing.</div></li>
245     * </ul></p></li>
246     * <li><b>failure</b> : Function<p style="margin-left:1em">The callback that will
247     * be invoked after a failed transaction attempt.  The function
248     * is passed the following parameters:<ul>
249     * <li><code>form</code> : Ext.form.BasicForm<div class="sub-desc">The form that requested the action</div></li>
250     * <li><code>action</code> : Ext.form.Action<div class="sub-desc">The {@link Ext.form.Action Action} object which performed the operation. If an Ajax
251     * error ocurred, the failure type will be in {@link Ext.form.Action#failureType failureType}. The {@link Ext.form.Action#result result}
252     * property of this object may be examined to perform custom postprocessing.</div></li>
253     * </ul></p></li>
254     * <li><b>scope</b> : Object<p style="margin-left:1em">The scope in which to call the
255     * callback functions (The <tt>this</tt> reference for the callback functions).</p></li>
256     * <li><b>clientValidation</b> : Boolean<p style="margin-left:1em">Submit Action only.
257     * Determines whether a Form's fields are validated in a final call to
258     * {@link Ext.form.BasicForm#isValid isValid} prior to submission. Set to <tt>false</tt>
259     * to prevent this. If undefined, pre-submission field validation is performed.</p></li></ul>
260     * @return {BasicForm} this
261     */
262    doAction : function(action, options){
263        if(typeof action == 'string'){
264            action = new Ext.form.Action.ACTION_TYPES[action](this, options);
265        }
266        if(this.fireEvent('beforeaction', this, action) !== false){
267            this.beforeAction(action);
268            action.run.defer(100, action);
269        }
270        return this;
271    },
272
273    /**
274     * Shortcut to do a submit action.
275     * @param {Object} options The options to pass to the action (see {@link #doAction} for details).<br>
276     * <p><b>Note:</b> this is ignored when using the {@link #standardSubmit} option.</p>
277     * <p>The following code:</p><pre><code>
278myFormPanel.getForm().submit({
279    clientValidation: true,
280    url: 'updateConsignment.php',
281    params: {
282        newStatus: 'delivered'
283    },
284    success: function(form, action) {
285       Ext.Msg.alert("Success", action.result.msg);
286    },
287    failure: function(form, action) {
288        switch (action.failureType) {
289            case Ext.form.Action.CLIENT_INVALID:
290                Ext.Msg.alert("Failure", "Form fields may not be submitted with invalid values");
291                break;
292            case Ext.form.Action.CONNECT_FAILURE:
293                Ext.Msg.alert("Failure", "Ajax communication failed");
294                break;
295            case Ext.form.Action.SERVER_INVALID:
296               Ext.Msg.alert("Failure", action.result.msg);
297       }
298    }
299});
300</code></pre>
301     * would process the following server response for a successful submission:<pre><code>
302{
303    success: true,
304    msg: 'Consignment updated'
305}
306</code></pre>
307     * and the following server response for a failed submission:<pre><code>
308{
309    success: false,
310    msg: 'You do not have permission to perform this operation'
311}
312</code></pre>
313     * @return {BasicForm} this
314     */
315    submit : function(options){
316        if(this.standardSubmit){
317            var v = this.isValid();
318            if(v){
319                this.el.dom.submit();
320            }
321            return v;
322        }
323        this.doAction('submit', options);
324        return this;
325    },
326
327    /**
328     * Shortcut to do a load action.
329     * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
330     * @return {BasicForm} this
331     */
332    load : function(options){
333        this.doAction('load', options);
334        return this;
335    },
336
337    /**
338     * Persists the values in this form into the passed Ext.data.Record object in a beginEdit/endEdit block.
339     * @param {Record} record The record to edit
340     * @return {BasicForm} this
341     */
342    updateRecord : function(record){
343        record.beginEdit();
344        var fs = record.fields;
345        fs.each(function(f){
346            var field = this.findField(f.name);
347            if(field){
348                record.set(f.name, field.getValue());
349            }
350        }, this);
351        record.endEdit();
352        return this;
353    },
354
355    /**
356     * Loads an Ext.data.Record into this form.
357     * @param {Record} record The record to load
358     * @return {BasicForm} this
359     */
360    loadRecord : function(record){
361        this.setValues(record.data);
362        return this;
363    },
364
365    // private
366    beforeAction : function(action){
367        var o = action.options;
368        if(o.waitMsg){
369            if(this.waitMsgTarget === true){
370                this.el.mask(o.waitMsg, 'x-mask-loading');
371            }else if(this.waitMsgTarget){
372                this.waitMsgTarget = Ext.get(this.waitMsgTarget);
373                this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
374            }else{
375                Ext.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
376            }
377        }
378    },
379
380    // private
381    afterAction : function(action, success){
382        this.activeAction = null;
383        var o = action.options;
384        if(o.waitMsg){
385            if(this.waitMsgTarget === true){
386                this.el.unmask();
387            }else if(this.waitMsgTarget){
388                this.waitMsgTarget.unmask();
389            }else{
390                Ext.MessageBox.updateProgress(1);
391                Ext.MessageBox.hide();
392            }
393        }
394        if(success){
395            if(o.reset){
396                this.reset();
397            }
398            Ext.callback(o.success, o.scope, [this, action]);
399            this.fireEvent('actioncomplete', this, action);
400        }else{
401            Ext.callback(o.failure, o.scope, [this, action]);
402            this.fireEvent('actionfailed', this, action);
403        }
404    },
405
406    /**
407     * Find a Ext.form.Field in this form by id, dataIndex, name or hiddenName.
408     * @param {String} id The value to search for
409     * @return Field
410     */
411    findField : function(id){
412        var field = this.items.get(id);
413        if(!field){
414            this.items.each(function(f){
415                if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
416                    field = f;
417                    return false;
418                }
419            });
420        }
421        return field || null;
422    },
423
424
425    /**
426     * Mark fields in this form invalid in bulk.
427     * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
428     * @return {BasicForm} this
429     */
430    markInvalid : function(errors){
431        if(Ext.isArray(errors)){
432            for(var i = 0, len = errors.length; i < len; i++){
433                var fieldError = errors[i];
434                var f = this.findField(fieldError.id);
435                if(f){
436                    f.markInvalid(fieldError.msg);
437                }
438            }
439        }else{
440            var field, id;
441            for(id in errors){
442                if(typeof errors[id] != 'function' && (field = this.findField(id))){
443                    field.markInvalid(errors[id]);
444                }
445            }
446        }
447        return this;
448    },
449
450    /**
451     * Set values for fields in this form in bulk.
452     * @param {Array/Object} values Either an array in the form:<br><br><code><pre>
453[{id:'clientName', value:'Fred. Olsen Lines'},
454 {id:'portOfLoading', value:'FXT'},
455 {id:'portOfDischarge', value:'OSL'} ]</pre></code><br><br>
456     * or an object hash of the form:<br><br><code><pre>
457{
458    clientName: 'Fred. Olsen Lines',
459    portOfLoading: 'FXT',
460    portOfDischarge: 'OSL'
461}</pre></code><br>
462     * @return {BasicForm} this
463     */
464    setValues : function(values){
465        if(Ext.isArray(values)){ // array of objects
466            for(var i = 0, len = values.length; i < len; i++){
467                var v = values[i];
468                var f = this.findField(v.id);
469                if(f){
470                    f.setValue(v.value);
471                    if(this.trackResetOnLoad){
472                        f.originalValue = f.getValue();
473                    }
474                }
475            }
476        }else{ // object hash
477            var field, id;
478            for(id in values){
479                if(typeof values[id] != 'function' && (field = this.findField(id))){
480                    field.setValue(values[id]);
481                    if(this.trackResetOnLoad){
482                        field.originalValue = field.getValue();
483                    }
484                }
485            }
486        }
487        return this;
488    },
489
490    /**
491     * <p>Returns the fields in this form as an object with key/value pairs as they would be submitted using a standard form submit.
492     * If multiple fields exist with the same name they are returned as an array.</p>
493     *
494     * <p><b>Note:</b> The values are collected from all enabled HTML input elements within the form, <u>not</u> from
495     * the Ext Field objects. This means that all returned values are Strings (or Arrays of Strings) and that the the
496     * value can potentionally be the emptyText of a field.</p>
497     * @param {Boolean} asString (optional) false to return the values as an object (defaults to returning as a string)
498     * @return {String/Object}
499     */
500    getValues : function(asString){
501        var fs = Ext.lib.Ajax.serializeForm(this.el.dom);
502        if(asString === true){
503            return fs;
504        }
505        return Ext.urlDecode(fs);
506    },
507
508    /**
509     * Clears all invalid messages in this form.
510     * @return {BasicForm} this
511     */
512    clearInvalid : function(){
513        this.items.each(function(f){
514           f.clearInvalid();
515        });
516        return this;
517    },
518
519    /**
520     * Resets this form.
521     * @return {BasicForm} this
522     */
523    reset : function(){
524        this.items.each(function(f){
525            f.reset();
526        });
527        return this;
528    },
529
530    /**
531     * Add Ext.form Components to this form's Collection. This does not result in rendering of
532     * the passed Component, it just enables the form to validate Fields, and distribute values to
533     * Fields.
534     * <p><b>You will not usually call this function. In order to be rendered, a Field must be added
535     * to a {@link Ext.Container Container}, usually an {@link Ext.form.FormPanel FormPanel}.
536     * The FormPanel to which the field is added takes care of adding the Field to the BasicForm's
537     * collection.</b></p>
538     * @param {Field} field1
539     * @param {Field} field2 (optional)
540     * @param {Field} etc (optional)
541     * @return {BasicForm} this
542     */
543    add : function(){
544        this.items.addAll(Array.prototype.slice.call(arguments, 0));
545        return this;
546    },
547
548
549    /**
550     * Removes a field from the items collection (does NOT remove its markup).
551     * @param {Field} field
552     * @return {BasicForm} this
553     */
554    remove : function(field){
555        this.items.remove(field);
556        return this;
557    },
558
559    /**
560     * Iterates through the {@link Ext.form.Field Field}s which have been {@link #add add}ed to this BasicForm,
561     * checks them for an id attribute, and calls {@link Ext.form.Field#applyToMarkup} on the existing dom element with that id.
562     * @return {BasicForm} this
563     */
564    render : function(){
565        this.items.each(function(f){
566            if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
567                f.applyToMarkup(f.id);
568            }
569        });
570        return this;
571    },
572
573    /**
574     * Calls {@link Ext#apply} for all fields in this form with the passed object.
575     * @param {Object} values
576     * @return {BasicForm} this
577     */
578    applyToFields : function(o){
579        this.items.each(function(f){
580           Ext.apply(f, o);
581        });
582        return this;
583    },
584
585    /**
586     * Calls {@link Ext#applyIf} for all field in this form with the passed object.
587     * @param {Object} values
588     * @return {BasicForm} this
589     */
590    applyIfToFields : function(o){
591        this.items.each(function(f){
592           Ext.applyIf(f, o);
593        });
594        return this;
595    }
596});
597
598// back compat
599Ext.BasicForm = Ext.form.BasicForm;
Note: See TracBrowser for help on using the repository browser.