]> git.proxmox.com Git - extjs.git/blame - extjs/modern/modern/src/form/Panel.js
add extjs 6.0.1 sources
[extjs.git] / extjs / modern / modern / src / form / Panel.js
CommitLineData
6527f429
DM
1/**\r
2 * The Form panel presents a set of form fields and provides convenient ways to load and save data. Usually a form\r
3 * panel just contains the set of fields you want to display, ordered inside the items configuration like this:\r
4 *\r
5 * @example\r
6 * var form = Ext.create('Ext.form.Panel', {\r
7 * fullscreen: true,\r
8 * items: [\r
9 * {\r
10 * xtype: 'textfield',\r
11 * name: 'name',\r
12 * label: 'Name'\r
13 * },\r
14 * {\r
15 * xtype: 'emailfield',\r
16 * name: 'email',\r
17 * label: 'Email'\r
18 * },\r
19 * {\r
20 * xtype: 'passwordfield',\r
21 * name: 'password',\r
22 * label: 'Password'\r
23 * }\r
24 * ]\r
25 * });\r
26 *\r
27 * Here we just created a simple form panel which could be used as a registration form to sign up to your service. We\r
28 * added a plain {@link Ext.field.Text text field} for the user's Name, an {@link Ext.field.Email email field} and\r
29 * finally a {@link Ext.field.Password password field}. In each case we provided a {@link Ext.field.Field#name name}\r
30 * config on the field so that we can identify it later on when we load and save data on the form.\r
31 *\r
32 * ##Loading data\r
33 *\r
34 * Using the form we created above, we can load data into it in a few different ways, the easiest is to use\r
35 * {@link #setValues}:\r
36 *\r
37 * form.setValues({\r
38 * name: 'Peter',\r
39 * email: 'peter.venkman@gb.com',\r
40 * password: 'secret'\r
41 * });\r
42 *\r
43 * It's also easy to load {@link Ext.data.Model Model} instances into a form - let's say we have a User model and want\r
44 * to load a particular instance into our form:\r
45 *\r
46 * Ext.define('MyApp.model.User', {\r
47 * extend: 'Ext.data.Model',\r
48 * config: {\r
49 * fields: ['name', 'email', 'password']\r
50 * }\r
51 * });\r
52 *\r
53 * var ed = Ext.create('MyApp.model.User', {\r
54 * name: 'Peter',\r
55 * email: 'peter.venkman@gb.com',\r
56 * password: 'secret'\r
57 * });\r
58 *\r
59 * form.setRecord(ed);\r
60 *\r
61 * ##Retrieving form data\r
62 *\r
63 * Getting data out of the form panel is simple and is usually achieve via the {@link #getValues} method:\r
64 *\r
65 * var values = form.getValues();\r
66 *\r
67 * //values now looks like this:\r
68 * {\r
69 * name: 'Peter',\r
70 * email: 'peter.venkman@gb.com',\r
71 * password: 'secret'\r
72 * }\r
73 *\r
74 * It's also possible to listen to the change events on individual fields to get more timely notification of changes\r
75 * that the user is making. Here we expand on the example above with the User model, updating the model as soon as\r
76 * any of the fields are changed:\r
77 *\r
78 * var form = Ext.create('Ext.form.Panel', {\r
79 * listeners: {\r
80 * '> field': {\r
81 * change: function(field, newValue, oldValue) {\r
82 * ed.set(field.getName(), newValue);\r
83 * }\r
84 * }\r
85 * },\r
86 * items: [\r
87 * {\r
88 * xtype: 'textfield',\r
89 * name: 'name',\r
90 * label: 'Name'\r
91 * },\r
92 * {\r
93 * xtype: 'emailfield',\r
94 * name: 'email',\r
95 * label: 'Email'\r
96 * },\r
97 * {\r
98 * xtype: 'passwordfield',\r
99 * name: 'password',\r
100 * label: 'Password'\r
101 * }\r
102 * ]\r
103 * });\r
104 *\r
105 * The above attached a listener to the {@link Ext.field.Text#change change} event of each form\r
106 * field that is a direct child of the form panel. Our listener gets the name of the field that fired the change event,\r
107 * and updates our {@link Ext.data.Model Model} instance with the new value. For example, changing the email field\r
108 * in the form will update the Model's email field.\r
109 *\r
110 * ##Submitting forms\r
111 *\r
112 * There are a few ways to submit form data. In our example above we have a Model instance that we have updated, giving\r
113 * us the option to use the Model's {@link Ext.data.Model#save save} method to persist the changes back to our server,\r
114 * without using a traditional form submission. Alternatively, we can send a normal browser form submit using the\r
115 * {@link #method} method:\r
116 *\r
117 * form.submit({\r
118 * url: 'url/to/submit/to',\r
119 * method: 'POST',\r
120 * success: function() {\r
121 * alert('form submitted successfully!');\r
122 * }\r
123 * });\r
124 *\r
125 * In this case we provided the `url` to submit the form to inside the submit call - alternatively you can just set the\r
126 * {@link #url} configuration when you create the form. We can specify other parameters (see {@link #method} for a\r
127 * full list), including callback functions for success and failure, which are called depending on whether or not the\r
128 * form submission was successful. These functions are usually used to take some action in your app after your data\r
129 * has been saved to the server side.\r
130 */\r
131Ext.define('Ext.form.Panel', {\r
132 alternateClassName: 'Ext.form.FormPanel',\r
133 extend : 'Ext.Panel',\r
134 xtype : 'formpanel',\r
135 requires: ['Ext.XTemplate', 'Ext.field.Checkbox', 'Ext.Ajax'],\r
136\r
137 /**\r
138 * @event submit\r
139 * @preventable\r
140 * Fires upon successful (Ajax-based) form submission.\r
141 * @param {Ext.form.Panel} this This FormPanel.\r
142 * @param {Object} result The result object as returned by the server.\r
143 * @param {Ext.event.Event} e The event object.\r
144 */\r
145\r
146 /**\r
147 * @event beforesubmit\r
148 * @preventable\r
149 * Fires immediately preceding any Form submit action.\r
150 * Implementations may adjust submitted form values or options prior to execution.\r
151 * A return value of `false` from this listener will abort the submission\r
152 * attempt (regardless of `standardSubmit` configuration).\r
153 * @param {Ext.form.Panel} this This FormPanel.\r
154 * @param {Object} values A hash collection of the qualified form values about to be submitted.\r
155 * @param {Object} options Submission options hash (only available when `standardSubmit` is `false`).\r
156 * @param {Ext.event.Event} e The event object if the form was submitted via a HTML5 form submit event.\r
157 */\r
158\r
159 /**\r
160 * @event exception\r
161 * Fires when either the Ajax HTTP request reports a failure OR the server returns a `success:false`\r
162 * response in the result payload.\r
163 * @param {Ext.form.Panel} this This FormPanel.\r
164 * @param {Object} result Either a failed Ext.data.Connection request object or a failed (logical) server.\r
165 * response payload.\r
166 */\r
167\r
168 config: {\r
169 /**\r
170 * @cfg {String} baseCls\r
171 * @inheritdoc\r
172 */\r
173 baseCls: Ext.baseCSSPrefix + 'form',\r
174\r
175 /**\r
176 * @cfg {Boolean} standardSubmit\r
177 * Whether or not we want to perform a standard form submit.\r
178 * @accessor\r
179 */\r
180 standardSubmit: false,\r
181\r
182 /**\r
183 * @cfg {String} url\r
184 * The default url for submit actions.\r
185 * @accessor\r
186 */\r
187 url: null,\r
188\r
189 /**\r
190 * @cfg {String} enctype\r
191 * The enctype attribute for the form, specifies how the form should be encoded when submitting\r
192 */\r
193 enctype: null,\r
194\r
195 /**\r
196 * @cfg {Object} baseParams\r
197 * Optional hash of params to be sent (when `standardSubmit` configuration is `false`) on every submit.\r
198 * @accessor\r
199 */\r
200 baseParams: null,\r
201\r
202 /**\r
203 * @cfg {Object} submitOnAction\r
204 * When this is set to `true`, the form will automatically submit itself whenever the `action`\r
205 * event fires on a field in this form. The action event usually fires whenever you press\r
206 * go or enter inside a textfield.\r
207 * @accessor\r
208 */\r
209 submitOnAction: false,\r
210\r
211 /**\r
212 * @cfg {Ext.data.Model} record The model instance of this form. Can by dynamically set at any time.\r
213 * @accessor\r
214 */\r
215 record: null,\r
216\r
217 /**\r
218 * @cfg {String} method\r
219 * The method which this form will be submitted. `post` or `get`.\r
220 */\r
221 method: 'post',\r
222\r
223 /**\r
224 * @inheritdoc\r
225 */\r
226 scrollable: true,\r
227\r
228 /**\r
229 * @cfg {Boolean} trackResetOnLoad\r
230 * If set to true, {@link #reset}() resets to the last loaded or {@link #setValues}() data instead of\r
231 * when the form was first created.\r
232 */\r
233 trackResetOnLoad:false,\r
234\r
235 /**\r
236 * @cfg {Object} api\r
237 * If specified, load and submit actions will be loaded and submitted via Ext Direct. Methods which have been imported by\r
238 * {@link Ext.direct.Manager} can be specified here to load and submit forms. API methods may also be\r
239 * specified as strings and will be parsed into the actual functions when the first submit or load has occurred. Such as the following:\r
240 *\r
241 * api: {\r
242 * load: App.ss.MyProfile.load,\r
243 * submit: App.ss.MyProfile.submit\r
244 * }\r
245 *\r
246 * api: {\r
247 * load: 'App.ss.MyProfile.load',\r
248 * submit: 'App.ss.MyProfile.submit'\r
249 * }\r
250 *\r
251 * Load actions can use {@link #paramOrder} or {@link #paramsAsHash} to customize how the load method\r
252 * is invoked. Submit actions will always use a standard form submit. The `formHandler` configuration\r
253 * (see Ext.direct.RemotingProvider#action) must be set on the associated server-side method which has\r
254 * been imported by {@link Ext.direct.Manager}.\r
255 */\r
256 api: null,\r
257\r
258 /**\r
259 * @cfg {String/String[]} paramOrder\r
260 * A list of params to be executed server side. Only used for the {@link #api} `load`\r
261 * configuration.\r
262 *\r
263 * Specify the params in the order in which they must be executed on the\r
264 * server-side as either (1) an Array of String values, or (2) a String of params\r
265 * delimited by either whitespace, comma, or pipe. For example,\r
266 * any of the following would be acceptable:\r
267 *\r
268 * paramOrder: ['param1','param2','param3']\r
269 * paramOrder: 'param1 param2 param3'\r
270 * paramOrder: 'param1,param2,param3'\r
271 * paramOrder: 'param1|param2|param'\r
272 */\r
273 paramOrder: null,\r
274\r
275 /**\r
276 * @cfg {Boolean} paramsAsHash\r
277 * Only used for the {@link #api} `load` configuration. If true, parameters will be sent as a\r
278 * single hash collection of named arguments. Providing a {@link #paramOrder} nullifies this\r
279 * configuration.\r
280 */\r
281 paramsAsHash: null,\r
282\r
283 /**\r
284 * @cfg {Number} timeout\r
285 * Timeout for form actions in seconds.\r
286 */\r
287 timeout: 30,\r
288\r
289 /**\r
290 * @cfg {Boolean} multipartDetection\r
291 * If this is enabled the form will automatically detect the need to use 'multipart/form-data' during submission.\r
292 */\r
293 multipartDetection: true,\r
294\r
295 /**\r
296 * @cfg {Boolean} enableSubmissionForm\r
297 * The submission form is generated but never added to the dom. It is a submittable version of your form panel, allowing for fields\r
298 * that are not simple textfields to be properly submitted to servers. It will also send values that are easier to parse\r
299 * with server side code.\r
300 *\r
301 * If this is false we will attempt to subject the raw form inside the form panel.\r
302 */\r
303 enableSubmissionForm: true\r
304 },\r
305\r
306 getElementConfig: function() {\r
307 var config = this.callParent();\r
308 config.tag = "form";\r
309 // Added a submit input for standard form submission. This cannot have "display: none;" or it will not work\r
310 config.children.push({\r
311 tag: 'input',\r
312 type: 'submit',\r
313 style: 'visibility: hidden; width: 0; height: 0; position: absolute; right: 0; bottom: 0;'\r
314 });\r
315\r
316 return config;\r
317 },\r
318\r
319 /**\r
320 * @private\r
321 */\r
322 initialize: function() {\r
323 var me = this;\r
324 me.callParent();\r
325\r
326 me.element.on({\r
327 submit: 'onSubmit',\r
328 scope : me\r
329 });\r
330 },\r
331\r
332 applyEnctype: function(newValue) {\r
333 var form = this.element.dom || null;\r
334 if(form) {\r
335 if (newValue) {\r
336 form.setAttribute("enctype", newValue);\r
337 } else {\r
338 form.setAttribute("enctype");\r
339 }\r
340 }\r
341 },\r
342\r
343 updateRecord: function(newRecord) {\r
344 var fields, values, name;\r
345\r
346 if (newRecord) {\r
347 values = this.getValues();\r
348 for (name in values) {\r
349 if (values.hasOwnProperty(name) && newRecord.getField(name)) {\r
350 newRecord.set(name, values[name]);\r
351 }\r
352 }\r
353 }\r
354 return this;\r
355 },\r
356\r
357 /**\r
358 * Loads matching fields from a model instance into this form.\r
359 * @param {Ext.data.Model} record The model instance.\r
360 * @return {Ext.form.Panel} This form.\r
361 */\r
362 setRecord: function(record) {\r
363 var me = this;\r
364\r
365 if (record && record.data) {\r
366 me.setValues(record.data);\r
367 }\r
368\r
369 me._record = record;\r
370\r
371 return this;\r
372 },\r
373\r
374 /**\r
375 * @private\r
376 */\r
377 onSubmit: function(e) {\r
378 var me = this;\r
379 if (e && !me.getStandardSubmit()) {\r
380 e.stopEvent();\r
381 } else {\r
382 this.submit(null, e);\r
383 }\r
384 },\r
385\r
386 updateSubmitOnAction: function(newSubmitOnAction) {\r
387 if (newSubmitOnAction) {\r
388 this.on({\r
389 action: 'onFieldAction',\r
390 scope: this\r
391 });\r
392 } else {\r
393 this.un({\r
394 action: 'onFieldAction',\r
395 scope: this\r
396 });\r
397 }\r
398 },\r
399\r
400 /**\r
401 * @private\r
402 */\r
403 onFieldAction: function(field) {\r
404 if (this.getSubmitOnAction()) {\r
405 field.blur();\r
406 this.submit();\r
407 }\r
408 },\r
409\r
410 /**\r
411 * Performs a Ajax-based submission of form values (if {@link #standardSubmit} is false) or otherwise\r
412 * executes a standard HTML Form submit action.\r
413 *\r
414 * **Notes**\r
415 *\r
416 * 1. Only the first parameter is implemented. Put all other parameters inside the first\r
417 * parameter:\r
418 *\r
419 * submit({params: "" ,headers: "" etc.})\r
420 *\r
421 * 2. Submit example:\r
422 *\r
423 * myForm.submit({\r
424 * url: 'PostMyData/To',\r
425 * method: 'Post',\r
426 * success: function() { Ext.Msg.alert("success"); },\r
427 * failure: function() { Ext.Msg.alert("error"); }\r
428 * });\r
429 *\r
430 * 3. Parameters and values only submit for a POST and not for a GET.\r
431 *\r
432 * @param {Object} options\r
433 * The configuration when submitting this form.\r
434 *\r
435 * The following are the configurations when submitting via Ajax only:\r
436 *\r
437 * @param {String} options.url\r
438 * The url for the action (defaults to the form's {@link #url}).\r
439 *\r
440 * @param {String} options.method\r
441 * The form method to use (defaults to the form's {@link #method}, or POST if not defined).\r
442 *\r
443 * @param {Object} options.headers\r
444 * Request headers to set for the action.\r
445 *\r
446 * @param {Boolean} [options.autoAbort=false]\r
447 * `true` to abort any pending Ajax request prior to submission.\r
448 * __Note:__ Has no effect when `{@link #standardSubmit}` is enabled.\r
449 *\r
450 * @param {Number} options.timeout\r
451 * The number is seconds the loading will timeout in.\r
452 *\r
453 * The following are the configurations when loading via Ajax or Direct:\r
454 *\r
455 * @param {String/Object} options.params\r
456 * The params to pass when submitting this form (defaults to this forms {@link #baseParams}).\r
457 * Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.\r
458 *\r
459 * @param {Boolean} [options.submitDisabled=false]\r
460 * `true` to submit all fields regardless of disabled state.\r
461 * __Note:__ Has no effect when `{@link #standardSubmit}` is enabled.\r
462 *\r
463 * @param {String/Object} [options.waitMsg]\r
464 * If specified, the value which is passed to the loading {@link #masked mask}. See {@link #masked} for\r
465 * more information.\r
466 *\r
467 * @param {Function} options.success\r
468 * The callback that will be invoked after a successful response. A response is successful if\r
469 * a response is received from the server and is a JSON object where the `success` property is set\r
470 * to `true`, `{"success": true}`.\r
471 *\r
472 * The function is passed the following parameters and can be used for submitting via Ajax or Direct:\r
473 *\r
474 * @param {Ext.form.Panel} options.success.form\r
475 * The {@link Ext.form.Panel} that requested the action.\r
476 *\r
477 * @param {Object/Ext.direct.Event} options.success.result\r
478 * The result object returned by the server as a result of the submit request. If the submit is sent using Ext Direct,\r
479 * this will return the {@link Ext.direct.Event} instance, otherwise will return an Object.\r
480 *\r
481 * @param {Object} options.success.data\r
482 * The parsed data returned by the server.\r
483 *\r
484 * @param {Function} options.failure\r
485 * The callback that will be invoked after a failed transaction attempt.\r
486 *\r
487 * The function is passed the following parameters and can be used for submitting via Ajax or Direct:\r
488 *\r
489 * @param {Ext.form.Panel} options.failure.form\r
490 * The {@link Ext.form.Panel} that requested the submit.\r
491 *\r
492 * @param {Ext.form.Panel} options.failure.result\r
493 * The failed response or result object returned by the server which performed the operation.\r
494 *\r
495 * @param {Object} options.success.data\r
496 * The parsed data returned by the server.\r
497 *\r
498 * @param {Object} options.scope\r
499 * The scope in which to call the callback functions (The `this` reference for the callback functions).\r
500 *\r
501 * @return {Ext.data.Connection} The request object if the {@link #standardSubmit} config is false.\r
502 * If the standardSubmit config is true, then the return value is undefined.\r
503 */\r
504 submit: function(options, e) {\r
505 options = options || {};\r
506\r
507 var me = this,\r
508 formValues = me.getValues(me.getStandardSubmit() || !options.submitDisabled),\r
509 form = me.element.dom || {};\r
510\r
511 if(this.getEnableSubmissionForm()) {\r
512 form = this.createSubmissionForm(form, formValues);\r
513 }\r
514\r
515 options = Ext.apply({\r
516 url : me.getUrl() || form.action,\r
517 submit: false,\r
518 form: form,\r
519 method : me.getMethod() || form.method || 'post',\r
520 autoAbort : false,\r
521 params : null,\r
522 waitMsg : null,\r
523 headers : null,\r
524 success : null,\r
525 failure : null\r
526 }, options || {});\r
527\r
528 return me.fireAction('beforesubmit', [me, formValues, options, e], 'doBeforeSubmit', null, null, 'after');\r
529 },\r
530\r
531 createSubmissionForm: function(form, values) {\r
532 var fields = this.getFields(),\r
533 name, input, field, fileinputElement, inputComponent;\r
534\r
535 if(form.nodeType === 1) {\r
536 form = form.cloneNode(false);\r
537\r
538 for (name in values) {\r
539 input = document.createElement("input");\r
540 input.setAttribute("type", "text");\r
541 input.setAttribute("name", name);\r
542 input.setAttribute("value", values[name]);\r
543 form.appendChild(input);\r
544 }\r
545 }\r
546\r
547 for (name in fields) {\r
548 if (fields.hasOwnProperty(name)) {\r
549 field = fields[name];\r
550 if(field.isFile) {\r
551 if(!form.$fileswap) form.$fileswap = [];\r
552\r
553 inputComponent = field.getComponent().input;\r
554 fileinputElement = inputComponent.dom;\r
555 input = fileinputElement.cloneNode(true);\r
556 fileinputElement.parentNode.insertBefore(input, fileinputElement.nextSibling);\r
557 form.appendChild(fileinputElement);\r
558 form.$fileswap.push({original: fileinputElement, placeholder: input});\r
559 } else if(field.isPassword) {\r
560 if(field.getComponent().getType !== "password") {\r
561 field.setRevealed(false);\r
562 }\r
563 }\r
564 }\r
565 }\r
566\r
567 return form;\r
568 },\r
569\r
570 doBeforeSubmit: function(me, formValues, options) {\r
571 var form = options.form || {},\r
572 multipartDetected = false;\r
573\r
574 if(this.getMultipartDetection() === true) {\r
575 this.getFieldsAsArray().forEach(function(field) {\r
576 if(field.isFile === true) {\r
577 multipartDetected = true;\r
578 return false;\r
579 }\r
580 });\r
581\r
582 if(multipartDetected) {\r
583 form.setAttribute("enctype", "multipart/form-data");\r
584 }\r
585 }\r
586\r
587 if(options.enctype) {\r
588 form.setAttribute("enctype", options.enctype);\r
589 }\r
590\r
591 if (me.getStandardSubmit()) {\r
592 if (options.url && Ext.isEmpty(form.action)) {\r
593 form.action = options.url;\r
594 }\r
595\r
596 // Spinner fields must have their components enabled *before* submitting or else the value\r
597 // will not be posted.\r
598 var fields = this.query('spinnerfield'),\r
599 ln = fields.length,\r
600 i, field;\r
601\r
602 for (i = 0; i < ln; i++) {\r
603 field = fields[i];\r
604 if (!field.getDisabled()) {\r
605 field.getComponent().setDisabled(false);\r
606 }\r
607 }\r
608\r
609 form.method = (options.method || form.method).toLowerCase();\r
610 form.submit();\r
611 } else {\r
612 var api = me.getApi(),\r
613 url = options.url || me.getUrl(),\r
614 scope = options.scope || me,\r
615 waitMsg = options.waitMsg,\r
616 failureFn = function(response, responseText) {\r
617 if (Ext.isFunction(options.failure)) {\r
618 options.failure.call(scope, me, response, responseText);\r
619 }\r
620\r
621 me.fireEvent('exception', me, response);\r
622 },\r
623 successFn = function(response, responseText) {\r
624 if (Ext.isFunction(options.success)) {\r
625 options.success.call(options.scope || me, me, response, responseText);\r
626 }\r
627\r
628 me.fireEvent('submit', me, response);\r
629 },\r
630 submit;\r
631\r
632 if (options.waitMsg) {\r
633 if (typeof waitMsg === 'string') {\r
634 waitMsg = {\r
635 xtype : 'loadmask',\r
636 message : waitMsg\r
637 };\r
638 }\r
639\r
640 me.setMasked(waitMsg);\r
641 }\r
642\r
643 if (api) {\r
644 submit = api.submit;\r
645\r
646 if (typeof submit === 'string') {\r
647 submit = Ext.direct.Manager.parseMethod(submit);\r
648\r
649 if (submit) {\r
650 api.submit = submit;\r
651 }\r
652 }\r
653\r
654 if (submit) {\r
655 return submit(this.element, function(data, response, success) {\r
656 me.setMasked(false);\r
657\r
658 if (success) {\r
659 if (data.success) {\r
660 successFn(response, data);\r
661 } else {\r
662 failureFn(response, data);\r
663 }\r
664 } else {\r
665 failureFn(response, data);\r
666 }\r
667 }, this);\r
668 }\r
669 } else {\r
670 var request = Ext.merge({},\r
671 {\r
672 url: url,\r
673 timeout: this.getTimeout() * 1000,\r
674 form: form,\r
675 scope: me\r
676 },\r
677 options\r
678 );\r
679 delete request.success;\r
680 delete request.failure;\r
681\r
682 request.params = Ext.merge(me.getBaseParams() || {}, options.params);\r
683 request.header = Ext.apply(\r
684 {\r
685 'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8'\r
686 },\r
687 options.headers || {}\r
688 );\r
689 request.callback = function(callbackOptions, success, response) {\r
690 var me = this,\r
691 responseText = response.responseText,\r
692 responseXML = response.responseXML,\r
693 statusResult = Ext.data.request.Ajax.parseStatus(response.status, response);\r
694\r
695 if(form.$fileswap) {\r
696 var original, placeholder;\r
697 Ext.each(form.$fileswap, function(item) {\r
698 original = item.original;\r
699 placeholder = item.placeholder;\r
700\r
701 placeholder.parentNode.insertBefore(original, placeholder.nextSibling);\r
702 placeholder.parentNode.removeChild(placeholder);\r
703 });\r
704 form.$fileswap = null;\r
705 delete form.$fileswap;\r
706 }\r
707\r
708 me.setMasked(false);\r
709\r
710 if(response.success === false) success = false;\r
711 if (success) {\r
712 if (statusResult && responseText && responseText.length === 0) {\r
713 success = true;\r
714 } else {\r
715 if(!Ext.isEmpty(response.responseBytes)) {\r
716 success = statusResult.success;\r
717 }else {\r
718 if(Ext.isString(responseText) && response.request.options.responseType === "text") {\r
719 response.success = true;\r
720 } else if(Ext.isString(responseText)) {\r
721 try {\r
722 response = Ext.decode(responseText);\r
723 }catch (e){\r
724 response.success = false;\r
725 response.error = e;\r
726 response.message = e.message;\r
727 }\r
728 } else if(Ext.isSimpleObject(responseText)) {\r
729 response = responseText;\r
730 Ext.applyIf(response, {success:true});\r
731 }\r
732\r
733 if(!Ext.isEmpty(responseXML)){\r
734 response.success = true;\r
735 }\r
736 success = !!response.success;\r
737 }\r
738 }\r
739 if (success) {\r
740 successFn(response, responseText);\r
741 } else {\r
742 failureFn(response, responseText);\r
743 }\r
744 }\r
745 else {\r
746 failureFn(response, responseText);\r
747 }\r
748 };\r
749\r
750 if(Ext.feature.has.XHR2 && request.xhr2) {\r
751 delete request.form;\r
752 var formData = new FormData(form);\r
753 if (request.params) {\r
754 Ext.iterate(request.params, function(name, value) {\r
755 if (Ext.isArray(value)) {\r
756 Ext.each(value, function(v) {\r
757 formData.append(name, v);\r
758 });\r
759 } else {\r
760 formData.append(name, value);\r
761 }\r
762 });\r
763 delete request.params;\r
764 }\r
765 request.data = formData;\r
766 }\r
767\r
768 return Ext.Ajax.request(request);\r
769 }\r
770 }\r
771 },\r
772\r
773 /**\r
774 * Performs an Ajax or Ext Direct call to load values for this form.\r
775 *\r
776 * @param {Object} options\r
777 * The configuration when loading this form.\r
778 *\r
779 * The following are the configurations when loading via Ajax only:\r
780 *\r
781 * @param {String} options.url\r
782 * The url for the action (defaults to the form's {@link #url}).\r
783 *\r
784 * @param {String} options.method\r
785 * The form method to use (defaults to the form's {@link #method}, or GET if not defined).\r
786 *\r
787 * @param {Object} options.headers\r
788 * Request headers to set for the action.\r
789 *\r
790 * @param {Number} options.timeout\r
791 * The number is seconds the loading will timeout in.\r
792 *\r
793 * The following are the configurations when loading via Ajax or Direct:\r
794 *\r
795 * @param {Boolean} [options.autoAbort=false]\r
796 * `true` to abort any pending Ajax request prior to loading.\r
797 *\r
798 * @param {String/Object} options.params\r
799 * The params to pass when submitting this form (defaults to this forms {@link #baseParams}).\r
800 * Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.\r
801 *\r
802 * @param {String/Object} [options.waitMsg]\r
803 * If specified, the value which is passed to the loading {@link #masked mask}. See {@link #masked} for\r
804 * more information.\r
805 *\r
806 * @param {Function} options.success\r
807 * The callback that will be invoked after a successful response. A response is successful if\r
808 * a response is received from the server and is a JSON object where the `success` property is set\r
809 * to `true`, `{"success": true}`.\r
810 *\r
811 * The function is passed the following parameters and can be used for loading via Ajax or Direct:\r
812 *\r
813 * @param {Ext.form.Panel} options.success.form\r
814 * The {@link Ext.form.Panel} that requested the load.\r
815 *\r
816 * @param {Object/Ext.direct.Event} options.success.result\r
817 * The result object returned by the server as a result of the load request. If the loading was done via Ext Direct,\r
818 * will return the {@link Ext.direct.Event} instance, otherwise will return an Object.\r
819 *\r
820 * @param {Object} options.success.data\r
821 * The parsed data returned by the server.\r
822 *\r
823 * @param {Function} options.failure\r
824 * The callback that will be invoked after a failed transaction attempt.\r
825 *\r
826 * The function is passed the following parameters and can be used for loading via Ajax or Direct:\r
827 *\r
828 * @param {Ext.form.Panel} options.failure.form\r
829 * The {@link Ext.form.Panel} that requested the load.\r
830 *\r
831 * @param {Ext.form.Panel} options.failure.result\r
832 * The failed response or result object returned by the server which performed the operation.\r
833 *\r
834 * @param {Object} options.success.data\r
835 * The parsed data returned by the server.\r
836 *\r
837 * @param {Object} options.scope\r
838 * The scope in which to call the callback functions (The `this` reference for the callback functions).\r
839 *\r
840 * @return {Ext.data.Connection} The request object.\r
841 */\r
842 load : function(options) {\r
843 options = options || {};\r
844\r
845 var me = this,\r
846 api = me.getApi(),\r
847 url = me.getUrl() || options.url,\r
848 waitMsg = options.waitMsg,\r
849 successFn = function(response, data) {\r
850 me.setValues(data.data);\r
851\r
852 if (Ext.isFunction(options.success)) {\r
853 options.success.call(options.scope || me, me, response, data);\r
854 }\r
855\r
856 me.fireEvent('load', me, response);\r
857 },\r
858 failureFn = function(response, data) {\r
859 if (Ext.isFunction(options.failure)) {\r
860 options.failure.call(scope, me, response, data);\r
861 }\r
862\r
863 me.fireEvent('exception', me, response);\r
864 },\r
865 load, method, args;\r
866\r
867 if (options.waitMsg) {\r
868 if (typeof waitMsg === 'string') {\r
869 waitMsg = {\r
870 xtype : 'loadmask',\r
871 message : waitMsg\r
872 };\r
873 }\r
874\r
875 me.setMasked(waitMsg);\r
876 }\r
877\r
878 if (api) {\r
879 load = api.load;\r
880\r
881 if (typeof load === 'string') {\r
882 load = Ext.direct.Manager.parseMethod(load);\r
883\r
884 if (load) {\r
885 api.load = load;\r
886 }\r
887 }\r
888\r
889 if (load) {\r
890 method = load.directCfg.method;\r
891 args = method.getArgs(me.getParams(options.params), me.getParamOrder(), me.getParamsAsHash());\r
892\r
893 args.push(function(data, response, success) {\r
894 me.setMasked(false);\r
895\r
896 if (success) {\r
897 successFn(response, data);\r
898 } else {\r
899 failureFn(response, data);\r
900 }\r
901 }, me);\r
902\r
903 return load.apply(window, args);\r
904 }\r
905 } else if (url) {\r
906 return Ext.Ajax.request({\r
907 url: url,\r
908 timeout: (options.timeout || this.getTimeout()) * 1000,\r
909 method: options.method || 'GET',\r
910 autoAbort: options.autoAbort,\r
911 headers: Ext.apply(\r
912 {\r
913 'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8'\r
914 },\r
915 options.headers || {}\r
916 ),\r
917 callback: function(callbackOptions, success, response) {\r
918 var responseText = response.responseText,\r
919 statusResult = Ext.data.request.Ajax.parseStatus(response.status, response);\r
920\r
921 me.setMasked(false);\r
922\r
923 if (success) {\r
924 if (statusResult && responseText.length === 0) {\r
925 success = true;\r
926 } else {\r
927 response = Ext.decode(responseText);\r
928 success = !!response.success;\r
929 }\r
930 if (success) {\r
931 successFn(response, responseText);\r
932 } else {\r
933 failureFn(response, responseText);\r
934 }\r
935 }\r
936 else {\r
937 failureFn(response, responseText);\r
938 }\r
939 }\r
940 });\r
941 }\r
942 },\r
943\r
944 /**\r
945 * @private\r
946 */\r
947 getParams : function(params) {\r
948 return Ext.apply({}, params, this.getBaseParams());\r
949 },\r
950\r
951 /**\r
952 * Sets the values of form fields in bulk. Example usage:\r
953 *\r
954 * myForm.setValues({\r
955 * name: 'Ed',\r
956 * crazy: true,\r
957 * username: 'edspencer'\r
958 * });\r
959 *\r
960 * If there groups of checkbox fields with the same name, pass their values in an array. For example:\r
961 *\r
962 * myForm.setValues({\r
963 * name: 'Jacky',\r
964 * crazy: false,\r
965 * hobbies: [\r
966 * 'reading',\r
967 * 'cooking',\r
968 * 'gaming'\r
969 * ]\r
970 * });\r
971 *\r
972 * @param {Object} values field name => value mapping object.\r
973 * @return {Ext.form.Panel} This form.\r
974 */\r
975 setValues: function(values) {\r
976 var fields = this.getFields(),\r
977 me = this,\r
978 name, field, value, ln, i, f;\r
979\r
980 values = values || {};\r
981\r
982 for (name in values) {\r
983 if (values.hasOwnProperty(name)) {\r
984 field = fields[name];\r
985 value = values[name];\r
986\r
987 if (field) {\r
988 // If there are multiple fields with the same name. Checkboxes, radio fields and maybe event just normal fields..\r
989 if (Ext.isArray(field)) {\r
990 ln = field.length;\r
991\r
992 // Loop through each of the fields\r
993 for (i = 0; i < ln; i++) {\r
994 f = field[i];\r
995\r
996 if (f.isRadio) {\r
997 // If it is a radio field just use setGroupValue which will handle all of the radio fields\r
998 f.setGroupValue(value);\r
999 break;\r
1000 } else if (f.isCheckbox) {\r
1001 if (Ext.isArray(value)) {\r
1002 f.setChecked((value.indexOf(f._value) != -1));\r
1003 } else {\r
1004 f.setChecked((value == f._value));\r
1005 }\r
1006 } else {\r
1007 // If it is a bunch of fields with the same name, check if the value is also an array, so we can map it\r
1008 // to each field\r
1009 if (Ext.isArray(value)) {\r
1010 f.setValue(value[i]);\r
1011 }\r
1012 }\r
1013 }\r
1014 } else {\r
1015 if (field.isRadio || field.isCheckbox) {\r
1016 // If the field is a radio or a checkbox\r
1017 field.setChecked(value);\r
1018 } else {\r
1019 // If just a normal field\r
1020 field.setValue(value);\r
1021 }\r
1022 }\r
1023\r
1024 if (me.getTrackResetOnLoad()) {\r
1025 field.resetOriginalValue();\r
1026 }\r
1027 }\r
1028 }\r
1029 }\r
1030\r
1031 return this;\r
1032 },\r
1033\r
1034 /**\r
1035 * Returns an object containing the value of each field in the form, keyed to the field's name.\r
1036 * For groups of checkbox fields with the same name, it will be arrays of values. For example:\r
1037 *\r
1038 * {\r
1039 * name: "Jacky Nguyen", // From a TextField\r
1040 * favorites: [\r
1041 * 'pizza',\r
1042 * 'noodle',\r
1043 * 'cake'\r
1044 * ]\r
1045 * }\r
1046 *\r
1047 * @param {Boolean} [enabled] `true` to return only enabled fields.\r
1048 * @param {Boolean} [all] `true` to return all fields even if they don't have a\r
1049 * {@link Ext.field.Field#name name} configured.\r
1050 * @return {Object} Object mapping field name to its value.\r
1051 */\r
1052 getValues: function(enabled, all) {\r
1053 var fields = this.getFields(),\r
1054 values = {},\r
1055 isArray = Ext.isArray,\r
1056 field, value, addValue, bucket, name, ln, i;\r
1057\r
1058 // Function which you give a field and a name, and it will add it into the values\r
1059 // object accordingly\r
1060 addValue = function(field, name) {\r
1061 if (!all && (!name || name === 'null') || field.isFile) {\r
1062 return;\r
1063 }\r
1064\r
1065 if (field.isCheckbox) {\r
1066 value = field.getSubmitValue();\r
1067 } else {\r
1068 value = field.getValue();\r
1069 }\r
1070\r
1071\r
1072 if (!(enabled && field.getDisabled())) {\r
1073 // RadioField is a special case where the value returned is the fields valUE\r
1074 // ONLY if it is checked\r
1075 if (field.isRadio) {\r
1076 if (field.isChecked()) {\r
1077 values[name] = value;\r
1078 }\r
1079 } else {\r
1080 // Check if the value already exists\r
1081 bucket = values[name];\r
1082 if (!Ext.isEmpty(bucket)) {\r
1083 // if it does and it isn't an array, we need to make it into an array\r
1084 // so we can push more\r
1085 if (!isArray(bucket)) {\r
1086 bucket = values[name] = [bucket];\r
1087 }\r
1088\r
1089 // Check if it is an array\r
1090 if (isArray(value)) {\r
1091 // Concat it into the other values\r
1092 bucket = values[name] = bucket.concat(value);\r
1093 } else {\r
1094 // If it isn't an array, just pushed more values\r
1095 bucket.push(value);\r
1096 }\r
1097 } else {\r
1098 values[name] = value;\r
1099 }\r
1100 }\r
1101 }\r
1102 };\r
1103\r
1104 // Loop through each of the fields, and add the values for those fields.\r
1105 for (name in fields) {\r
1106 if (fields.hasOwnProperty(name)) {\r
1107 field = fields[name];\r
1108\r
1109 if (isArray(field)) {\r
1110 ln = field.length;\r
1111 for (i = 0; i < ln; i++) {\r
1112 addValue(field[i], name);\r
1113 }\r
1114 } else {\r
1115 addValue(field, name);\r
1116 }\r
1117 }\r
1118 }\r
1119\r
1120 return values;\r
1121 },\r
1122\r
1123 /**\r
1124 * Resets all fields in the form back to their original values.\r
1125 * @return {Ext.form.Panel} This form.\r
1126 */\r
1127 reset: function() {\r
1128 this.getFieldsAsArray().forEach(function(field) {\r
1129 field.reset();\r
1130 });\r
1131\r
1132 return this;\r
1133 },\r
1134\r
1135 /**\r
1136 * A convenient method to disable all fields in this form.\r
1137 * @return {Ext.form.Panel} This form.\r
1138 */\r
1139 updateDisabled: function(newDisabled) {\r
1140 this.getFieldsAsArray().forEach(function(field) {\r
1141 field.setDisabled(newDisabled);\r
1142 });\r
1143\r
1144 return this;\r
1145 },\r
1146\r
1147 /**\r
1148 * @private\r
1149 */\r
1150 getFieldsAsArray: function() {\r
1151 var fields = [],\r
1152 getFieldsFrom = function(item) {\r
1153 if (item.isField) {\r
1154 fields.push(item);\r
1155 }\r
1156\r
1157 if (item.isContainer) {\r
1158 item.getItems().each(getFieldsFrom);\r
1159 }\r
1160 };\r
1161\r
1162 this.getItems().each(getFieldsFrom);\r
1163\r
1164 return fields;\r
1165 },\r
1166\r
1167 /**\r
1168 * Returns all {@link Ext.field.Field field} instances inside this form.\r
1169 * @param {Boolean} byName return only fields that match the given name, otherwise return all fields.\r
1170 * @return {Object/Array} All field instances, mapped by field name; or an array if `byName` is passed.\r
1171 */\r
1172 getFields: function(byName) {\r
1173 var fields = {},\r
1174 itemName;\r
1175\r
1176 var getFieldsFrom = function(item) {\r
1177 if (item.isField) {\r
1178 itemName = item.getName();\r
1179\r
1180 if ((byName && itemName == byName) || typeof byName == 'undefined') {\r
1181 if (fields.hasOwnProperty(itemName)) {\r
1182 if (!Ext.isArray(fields[itemName])) {\r
1183 fields[itemName] = [fields[itemName]];\r
1184 }\r
1185\r
1186 fields[itemName].push(item);\r
1187 } else {\r
1188 fields[itemName] = item;\r
1189 }\r
1190 }\r
1191\r
1192 }\r
1193\r
1194 if (item.isContainer) {\r
1195 item.items.each(getFieldsFrom);\r
1196 }\r
1197 };\r
1198\r
1199 this.getItems().each(getFieldsFrom);\r
1200\r
1201 return (byName) ? (fields[byName] || []) : fields;\r
1202 },\r
1203\r
1204 /**\r
1205 * Returns an array of fields in this formpanel.\r
1206 * @return {Ext.field.Field[]} An array of fields in this form panel.\r
1207 * @private\r
1208 */\r
1209 getFieldsArray: function() {\r
1210 var fields = [];\r
1211\r
1212 var getFieldsFrom = function(item) {\r
1213 if (item.isField) {\r
1214 fields.push(item);\r
1215 }\r
1216\r
1217 if (item.isContainer) {\r
1218 item.items.each(getFieldsFrom);\r
1219 }\r
1220 };\r
1221\r
1222 this.items.each(getFieldsFrom);\r
1223\r
1224 return fields;\r
1225 },\r
1226\r
1227 getFieldsFromItem: Ext.emptyFn,\r
1228\r
1229 /**\r
1230 * Shows a generic/custom mask over a designated Element.\r
1231 * @param {String/Object} cfg Either a string message or a configuration object supporting\r
1232 * the following options:\r
1233 *\r
1234 * {\r
1235 * message : 'Please Wait',\r
1236 * cls : 'form-mask'\r
1237 * }\r
1238 *\r
1239 * @param {Object} target\r
1240 * @return {Ext.form.Panel} This form\r
1241 * @deprecated 2.0.0 Please use {@link #setMasked} instead.\r
1242 */\r
1243 showMask: function(cfg, target) {\r
1244 //<debug>\r
1245 Ext.Logger.warn('showMask is now deprecated. Please use Ext.form.Panel#setMasked instead');\r
1246 //</debug>\r
1247\r
1248 cfg = Ext.isObject(cfg) ? cfg.message : cfg;\r
1249\r
1250 if (cfg) {\r
1251 this.setMasked({\r
1252 xtype: 'loadmask',\r
1253 message: cfg\r
1254 });\r
1255 } else {\r
1256 this.setMasked(true);\r
1257 }\r
1258\r
1259 return this;\r
1260 },\r
1261\r
1262 /**\r
1263 * Hides a previously shown wait mask (See {@link #showMask}).\r
1264 * @return {Ext.form.Panel} this\r
1265 * @deprecated 2.0.0 Please use {@link #unmask} or {@link #setMasked} instead.\r
1266 */\r
1267 hideMask: function() {\r
1268 this.setMasked(false);\r
1269 return this;\r
1270 },\r
1271\r
1272 /**\r
1273 * Returns the currently focused field\r
1274 * @return {Ext.field.Field} The currently focused field, if one is focused or `null`.\r
1275 * @private\r
1276 */\r
1277 getFocusedField: function() {\r
1278 var fields = this.getFieldsArray(),\r
1279 ln = fields.length,\r
1280 field, i;\r
1281\r
1282 for (i = 0; i < ln; i++) {\r
1283 field = fields[i];\r
1284 if (field.isFocused) {\r
1285 return field;\r
1286 }\r
1287 }\r
1288\r
1289 return null;\r
1290 },\r
1291\r
1292 /**\r
1293 * @return {Boolean/Ext.field.Field} The next field if one exists, or `false`.\r
1294 * @private\r
1295 */\r
1296 getNextField: function() {\r
1297 var fields = this.getFieldsArray(),\r
1298 focusedField = this.getFocusedField(),\r
1299 index;\r
1300\r
1301 if (focusedField) {\r
1302 index = fields.indexOf(focusedField);\r
1303\r
1304 if (index !== fields.length - 1) {\r
1305 index++;\r
1306 return fields[index];\r
1307 }\r
1308 }\r
1309\r
1310 return false;\r
1311 },\r
1312\r
1313 /**\r
1314 * Tries to focus the next field in the form, if there is currently a focused field.\r
1315 * @return {Boolean/Ext.field.Field} The next field that was focused, or `false`.\r
1316 * @private\r
1317 */\r
1318 focusNextField: function() {\r
1319 var field = this.getNextField();\r
1320 if (field) {\r
1321 field.focus();\r
1322 return field;\r
1323 }\r
1324\r
1325 return false;\r
1326 },\r
1327\r
1328 /**\r
1329 * @private\r
1330 * @return {Boolean/Ext.field.Field} The next field if one exists, or `false`.\r
1331 */\r
1332 getPreviousField: function() {\r
1333 var fields = this.getFieldsArray(),\r
1334 focusedField = this.getFocusedField(),\r
1335 index;\r
1336\r
1337 if (focusedField) {\r
1338 index = fields.indexOf(focusedField);\r
1339\r
1340 if (index !== 0) {\r
1341 index--;\r
1342 return fields[index];\r
1343 }\r
1344 }\r
1345\r
1346 return false;\r
1347 },\r
1348\r
1349 /**\r
1350 * Tries to focus the previous field in the form, if there is currently a focused field.\r
1351 * @return {Boolean/Ext.field.Field} The previous field that was focused, or `false`.\r
1352 * @private\r
1353 */\r
1354 focusPreviousField: function() {\r
1355 var field = this.getPreviousField();\r
1356 if (field) {\r
1357 field.focus();\r
1358 return field;\r
1359 }\r
1360\r
1361 return false;\r
1362 }\r
1363});\r