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