Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / form / field / File.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @docauthor Jason Johnston <jason@sencha.com>
17  *
18  * A file upload field which has custom styling and allows control over the button text and other
19  * features of {@link Ext.form.field.Text text fields} like {@link Ext.form.field.Text#emptyText empty text}.
20  * It uses a hidden file input element behind the scenes to allow user selection of a file and to
21  * perform the actual upload during {@link Ext.form.Basic#submit form submit}.
22  *
23  * Because there is no secure cross-browser way to programmatically set the value of a file input,
24  * the standard Field `setValue` method is not implemented. The `{@link #getValue}` method will return
25  * a value that is browser-dependent; some have just the file name, some have a full path, some use
26  * a fake path.
27  *
28  * **IMPORTANT:** File uploads are not performed using normal 'Ajax' techniques; see the description for
29  * {@link Ext.form.Basic#hasUpload} for details.
30  *
31  * # Example Usage
32  *
33  *     @example
34  *     Ext.create('Ext.form.Panel', {
35  *         title: 'Upload a Photo',
36  *         width: 400,
37  *         bodyPadding: 10,
38  *         frame: true,
39  *         renderTo: Ext.getBody(),
40  *         items: [{
41  *             xtype: 'filefield',
42  *             name: 'photo',
43  *             fieldLabel: 'Photo',
44  *             labelWidth: 50,
45  *             msgTarget: 'side',
46  *             allowBlank: false,
47  *             anchor: '100%',
48  *             buttonText: 'Select Photo...'
49  *         }],
50  *
51  *         buttons: [{
52  *             text: 'Upload',
53  *             handler: function() {
54  *                 var form = this.up('form').getForm();
55  *                 if(form.isValid()){
56  *                     form.submit({
57  *                         url: 'photo-upload.php',
58  *                         waitMsg: 'Uploading your photo...',
59  *                         success: function(fp, o) {
60  *                             Ext.Msg.alert('Success', 'Your photo "' + o.result.file + '" has been uploaded.');
61  *                         }
62  *                     });
63  *                 }
64  *             }
65  *         }]
66  *     });
67  */
68 Ext.define("Ext.form.field.File", {
69     extend: 'Ext.form.field.Text',
70     alias: ['widget.filefield', 'widget.fileuploadfield'],
71     alternateClassName: ['Ext.form.FileUploadField', 'Ext.ux.form.FileUploadField', 'Ext.form.File'],
72     uses: ['Ext.button.Button', 'Ext.layout.component.field.File'],
73
74     /**
75      * @cfg {String} buttonText
76      * The button text to display on the upload button. Note that if you supply a value for
77      * {@link #buttonConfig}, the buttonConfig.text value will be used instead if available.
78      */
79     buttonText: 'Browse...',
80
81     /**
82      * @cfg {Boolean} buttonOnly
83      * True to display the file upload field as a button with no visible text field. If true, all
84      * inherited Text members will still be available.
85      */
86     buttonOnly: false,
87
88     /**
89      * @cfg {Number} buttonMargin
90      * The number of pixels of space reserved between the button and the text field. Note that this only
91      * applies if {@link #buttonOnly} = false.
92      */
93     buttonMargin: 3,
94
95     /**
96      * @cfg {Object} buttonConfig
97      * A standard {@link Ext.button.Button} config object.
98      */
99
100     /**
101      * @event change
102      * Fires when the underlying file input field's value has changed from the user selecting a new file from the system
103      * file selection dialog.
104      * @param {Ext.ux.form.FileUploadField} this
105      * @param {String} value The file value returned by the underlying file input field
106      */
107
108     /**
109      * @property {Ext.Element} fileInputEl
110      * A reference to the invisible file input element created for this upload field. Only populated after this
111      * component is rendered.
112      */
113
114     /**
115      * @property {Ext.button.Button} button
116      * A reference to the trigger Button component created for this upload field. Only populated after this component is
117      * rendered.
118      */
119
120     /**
121      * @cfg {String} [fieldBodyCls='x-form-file-wrap']
122      * An extra CSS class to be applied to the body content element in addition to {@link #fieldBodyCls}.
123      */
124     fieldBodyCls: Ext.baseCSSPrefix + 'form-file-wrap',
125
126     /**
127      * @cfg {Boolean} readOnly
128      * Unlike with other form fields, the readOnly config defaults to true in File field.
129      */
130     readOnly: true,
131
132     // private
133     componentLayout: 'filefield',
134
135     // private
136     onRender: function() {
137         var me = this,
138             inputEl;
139
140         me.callParent(arguments);
141
142         me.createButton();
143         me.createFileInput();
144         
145         // we don't create the file/button til after onRender, the initial disable() is
146         // called in the onRender of the component.
147         if (me.disabled) {
148             me.disableItems();
149         }
150
151         inputEl = me.inputEl;
152         inputEl.dom.removeAttribute('name'); //name goes on the fileInput, not the text input
153         if (me.buttonOnly) {
154             inputEl.setDisplayed(false);
155         }
156     },
157
158     /**
159      * @private
160      * Creates the custom trigger Button component. The fileInput will be inserted into this.
161      */
162     createButton: function() {
163         var me = this;
164         me.button = Ext.widget('button', Ext.apply({
165             ui: me.ui,
166             renderTo: me.bodyEl,
167             text: me.buttonText,
168             cls: Ext.baseCSSPrefix + 'form-file-btn',
169             preventDefault: false,
170             style: me.buttonOnly ? '' : 'margin-left:' + me.buttonMargin + 'px'
171         }, me.buttonConfig));
172     },
173
174     /**
175      * @private
176      * Creates the file input element. It is inserted into the trigger button component, made
177      * invisible, and floated on top of the button's other content so that it will receive the
178      * button's clicks.
179      */
180     createFileInput : function() {
181         var me = this;
182         me.fileInputEl = me.button.el.createChild({
183             name: me.getName(),
184             cls: Ext.baseCSSPrefix + 'form-file-input',
185             tag: 'input',
186             type: 'file',
187             size: 1
188         }).on('change', me.onFileChange, me);
189     },
190
191     /**
192      * @private Event handler fired when the user selects a file.
193      */
194     onFileChange: function() {
195         this.lastValue = null; // force change event to get fired even if the user selects a file with the same name
196         Ext.form.field.File.superclass.setValue.call(this, this.fileInputEl.dom.value);
197     },
198
199     /**
200      * Overridden to do nothing
201      * @hide
202      */
203     setValue: Ext.emptyFn,
204
205     reset : function(){
206         var me = this;
207         if (me.rendered) {
208             me.fileInputEl.remove();
209             me.createFileInput();
210             me.inputEl.dom.value = '';
211         }
212         me.callParent();
213     },
214
215     onDisable: function(){
216         this.callParent();
217         this.disableItems();
218     },
219     
220     disableItems: function(){
221         var file = this.fileInputEl,
222             button = this.button;
223              
224         if (file) {
225             file.dom.disabled = true;
226         }
227         if (button) {
228             button.disable();
229         }    
230     },
231
232     onEnable: function(){
233         var me = this;
234         me.callParent();
235         me.fileInputEl.dom.disabled = false;
236         me.button.enable();
237     },
238
239     isFileUpload: function() {
240         return true;
241     },
242
243     extractFileInput: function() {
244         var fileInput = this.fileInputEl.dom;
245         this.reset();
246         return fileInput;
247     },
248
249     onDestroy: function(){
250         Ext.destroyMembers(this, 'fileInputEl', 'button');
251         this.callParent();
252     }
253
254
255 });
256