]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/form/field/File.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / form / field / File.js
CommitLineData
6527f429
DM
1/**\r
2 * A file upload field which has custom styling and allows control over the button text and other\r
3 * features of {@link Ext.form.field.Text text fields} like {@link Ext.form.field.Text#emptyText empty text}.\r
4 * It uses a hidden file input element behind the scenes to allow user selection of a file and to\r
5 * perform the actual upload during {@link Ext.form.Basic#submit form submit}.\r
6 *\r
7 * Because there is no secure cross-browser way to programmatically set the value of a file input,\r
8 * the standard Field `setValue` method is not implemented. The `{@link #getValue}` method will return\r
9 * a value that is browser-dependent; some have just the file name, some have a full path, some use\r
10 * a fake path.\r
11 *\r
12 * **IMPORTANT:** File uploads are not performed using normal 'Ajax' techniques; see the description for\r
13 * {@link Ext.form.Basic#hasUpload} for details.\r
14 *\r
15 * # Example Usage\r
16 *\r
17 * @example\r
18 * Ext.create('Ext.form.Panel', {\r
19 * title: 'Upload a Photo',\r
20 * width: 400,\r
21 * bodyPadding: 10,\r
22 * frame: true,\r
23 * renderTo: Ext.getBody(),\r
24 * items: [{\r
25 * xtype: 'filefield',\r
26 * name: 'photo',\r
27 * fieldLabel: 'Photo',\r
28 * labelWidth: 50,\r
29 * msgTarget: 'side',\r
30 * allowBlank: false,\r
31 * anchor: '100%',\r
32 * buttonText: 'Select Photo...'\r
33 * }],\r
34 *\r
35 * buttons: [{\r
36 * text: 'Upload',\r
37 * handler: function() {\r
38 * var form = this.up('form').getForm();\r
39 * if(form.isValid()){\r
40 * form.submit({\r
41 * url: 'photo-upload.php',\r
42 * waitMsg: 'Uploading your photo...',\r
43 * success: function(fp, o) {\r
44 * Ext.Msg.alert('Success', 'Your photo "' + o.result.file + '" has been uploaded.');\r
45 * }\r
46 * });\r
47 * }\r
48 * }\r
49 * }]\r
50 * });\r
51 */\r
52Ext.define('Ext.form.field.File', {\r
53 extend: 'Ext.form.field.Text',\r
54 alias: ['widget.filefield', 'widget.fileuploadfield'],\r
55 alternateClassName: ['Ext.form.FileUploadField', 'Ext.ux.form.FileUploadField', 'Ext.form.File'],\r
56 requires: [\r
57 'Ext.form.field.FileButton',\r
58 'Ext.form.trigger.Component'\r
59 ],\r
60\r
61 /**\r
62 * @cfg {String} emptyText\r
63 * Overridden to undefined as {@link #emptyText} is not supported with {@link #inputType inputType}:'file' and should be avoided.\r
64 * The default text to place into an empty field.\r
65 */ \r
66 emptyText: undefined,\r
67 \r
68 needArrowKeys: false,\r
69\r
70 triggers: {\r
71 filebutton: {\r
72 type: 'component',\r
73 hideOnReadOnly: false,\r
74 // Most form fields prevent the default browser action on mousedown of the trigger.\r
75 // This is intended to prevent the field's input element from losing focus when\r
76 // the trigger is clicked. File fields disable this behavior because:\r
77 // 1. The input element does not receive focus when the field is focused. The button does.\r
78 // 2. Preventing the default action of touchstart (translated from mousedown\r
79 // on mobile browsers) prevents the browser's file dialog from opening.\r
80 preventMouseDown: false\r
81 }\r
82 },\r
83\r
84 //<locale>\r
85 /**\r
86 * @cfg {String} buttonText\r
87 * The button text to display on the upload button. Note that if you supply a value for\r
88 * {@link #buttonConfig}, the buttonConfig.text value will be used instead if available.\r
89 */\r
90 buttonText: 'Browse...',\r
91 //</locale>\r
92\r
93 /**\r
94 * @cfg {Boolean} buttonOnly\r
95 * True to display the file upload field as a button with no visible text field. If true, all\r
96 * inherited Text members will still be available.\r
97 */\r
98 buttonOnly: false,\r
99\r
100 /**\r
101 * @cfg {Number} buttonMargin\r
102 * The number of pixels of space reserved between the button and the text field. Note that this only\r
103 * applies if {@link #buttonOnly} = false.\r
104 */\r
105 buttonMargin: 3,\r
106 \r
107 /**\r
108 * @cfg {Boolean} clearOnSubmit\r
109 * True to clear the selected file value when the form this field belongs to\r
110 * is submitted to the server.\r
111 */\r
112 clearOnSubmit: true,\r
113\r
114 /**\r
115 * @cfg {Object} buttonConfig\r
116 * Specify optional custom button {@link Ext.button.Button} config (eg. iconCls, text) for the upload button\r
117 */\r
118\r
119 /**\r
120 * @event change\r
121 * Fires when the underlying file input field's value has changed from the user selecting a new file from the system\r
122 * file selection dialog.\r
123 * @param {Ext.ux.form.FileUploadField} this\r
124 * @param {String} value The file value returned by the underlying file input field\r
125 */\r
126\r
127 /**\r
128 * @property {Ext.dom.Element} fileInputEl\r
129 * A reference to the invisible file input element created for this upload field. Only populated after this\r
130 * component is rendered.\r
131 */\r
132\r
133 /**\r
134 * @property {Ext.button.Button} button\r
135 * A reference to the trigger Button component created for this upload field. Only populated after this component is\r
136 * rendered.\r
137 */\r
138\r
139\r
140 /**\r
141 * @private\r
142 */\r
143 extraFieldBodyCls: Ext.baseCSSPrefix + 'form-file-wrap',\r
144\r
145 /**\r
146 * @private\r
147 */\r
148 inputCls: Ext.baseCSSPrefix + 'form-text-file',\r
149\r
150 /**\r
151 * @cfg {Boolean} [readOnly=true]\r
152 * Unlike with other form fields, the readOnly config defaults to true in File field.\r
153 */\r
154 readOnly: true,\r
155\r
156 /**\r
157 * @cfg {Boolean} editable\r
158 * @inheritdoc\r
159 */\r
160 editable: false,\r
161\r
162 submitValue: false,\r
163\r
164 /**\r
165 * Do not show hand pointer over text field since file choose dialog is only shown when clicking in the button\r
166 * @private\r
167 */\r
168 triggerNoEditCls: '',\r
169\r
170 /**\r
171 * @private\r
172 * Extract the file element, button outer element, and button active element.\r
173 */\r
174 childEls: ['browseButtonWrap'],\r
175\r
176 /**\r
177 * @private\r
178 */\r
179 applyTriggers: function(triggers) {\r
180 var me = this,\r
181 triggerCfg = (triggers || {}).filebutton;\r
182\r
183 if (triggerCfg) {\r
184 triggerCfg.component = Ext.apply({\r
185 xtype: 'filebutton',\r
186 ownerCt: me,\r
187 id: me.id + '-button',\r
188 ui: me.ui,\r
189 disabled: me.disabled,\r
190 text: me.buttonText,\r
191 style: me.buttonOnly ? '' : me.getButtonMarginProp() + me.buttonMargin + 'px',\r
192 inputName: me.getName(),\r
193 listeners: {\r
194 scope: me,\r
195 change: me.onFileChange\r
196 }\r
197 }, me.buttonConfig);\r
198\r
199 return me.callParent([triggers]);\r
200 }\r
201 // <debug>\r
202 else {\r
203 Ext.raise(me.$className + ' requires a valid trigger config containing "filebutton" specification');\r
204 }\r
205 // </debug>\r
206 },\r
207 \r
208 getSubTplData: function(fieldData) {\r
209 var data = this.callParent([fieldData]);\r
210 \r
211 // Input field itself should not be focusable since it's always decorative;\r
212 // however the input element is naturally focusable (and tabbable) so we have to\r
213 // deactivate it by setting its tabIndex to -1.\r
214 data.tabIdx = -1;\r
215 \r
216 return data;\r
217 },\r
218\r
219 /**\r
220 * @private\r
221 */\r
222 onRender: function() {\r
223 var me = this,\r
224 inputEl, button, buttonEl, trigger;\r
225\r
226 me.callParent(arguments);\r
227\r
228 inputEl = me.inputEl;\r
229 //name goes on the fileInput, not the text input\r
230 inputEl.dom.name = ''; \r
231 \r
232 // Some browsers will show a blinking cursor in the field, even if it's readonly. If we do happen\r
233 // to receive focus, forward it on to our focusEl. Also note that in IE, the file input is treated as\r
234 // 2 elements for tabbing purposes (the text, then the button). So as you tab through, it will take 2\r
235 // tabs to get to the next field. As far as I know there's no way around this in any kind of reasonable way.\r
236 inputEl.on('focus', me.onInputFocus, me);\r
237 inputEl.on('mousedown', me.onInputMouseDown, me);\r
238\r
239 trigger = me.getTrigger('filebutton');\r
240 button = me.button = trigger.component;\r
241 me.fileInputEl = button.fileInputEl;\r
242 buttonEl = button.el;\r
243\r
244 if (me.buttonOnly) {\r
245 me.inputWrap.setDisplayed(false);\r
246 me.shrinkWrap = 3;\r
247 }\r
248\r
249 // Ensure the trigger element is sized correctly upon render\r
250 trigger.el.setWidth(buttonEl.getWidth() + buttonEl.getMargin('lr'));\r
251 if (Ext.isIE) {\r
252 me.button.getEl().repaint();\r
253 }\r
254 },\r
255\r
256 /**\r
257 * Gets the markup to be inserted into the subTplMarkup.\r
258 */\r
259 getTriggerMarkup: function() {\r
260 return '<td id="' + this.id + '-browseButtonWrap" data-ref="browseButtonWrap" role="presentation"></td>';\r
261 },\r
262\r
263 /**\r
264 * @private\r
265 * Event handler fired when the user selects a file.\r
266 */\r
267 onFileChange: function(button, e, value) {\r
268 this.duringFileSelect = true;\r
269 Ext.form.field.File.superclass.setValue.call(this, value);\r
270 delete this.duringFileSelect;\r
271 },\r
272 \r
273 didValueChange: function(){\r
274 // In the case of the file field, the change event will only ever fire \r
275 // if the value actually changes, so we always want to fire the change event\r
276 // This affects Chrome specifically, because hitting the cancel button will\r
277 // reset the file upload.\r
278 return !!this.duringFileSelect;\r
279 },\r
280\r
281 /**\r
282 * @method setEmptyText\r
283 * @hide\r
284 */\r
285 setEmptyText: Ext.emptyFn,\r
286\r
287 /**\r
288 * @method setValue\r
289 * @hide\r
290 */\r
291 setValue: Ext.emptyFn,\r
292\r
293 reset : function(){\r
294 var me = this,\r
295 clear = me.clearOnSubmit;\r
296 if (me.rendered) {\r
297 me.button.reset(clear);\r
298 me.fileInputEl = me.button.fileInputEl;\r
299 if (clear) {\r
300 me.inputEl.dom.value = '';\r
301 // Reset the underlying value if we're clearing it\r
302 Ext.form.field.File.superclass.setValue.call(this, null);\r
303 }\r
304 }\r
305 me.callParent();\r
306 },\r
307 \r
308 onShow: function(){\r
309 this.callParent();\r
310 // If we started out hidden, the button may have a messed up layout\r
311 // since we don't act like a container\r
312 this.button.updateLayout(); \r
313 },\r
314\r
315 onDisable: function(){\r
316 this.callParent();\r
317 this.button.disable();\r
318 },\r
319\r
320 onEnable: function(){\r
321 this.callParent();\r
322 this.button.enable();\r
323 },\r
324\r
325 /**\r
326 * @method\r
327 * @inheritdoc\r
328 */\r
329 isFileUpload: Ext.returnTrue,\r
330\r
331 extractFileInput: function() {\r
332 var me = this,\r
333 fileInput;\r
334 \r
335 if (me.rendered) {\r
336 fileInput = me.button.fileInputEl.dom;\r
337 me.reset();\r
338 } else {\r
339 // Create a fake empty field here so it will still be submitted.\r
340 // All other unrendered fields provide a value.\r
341 fileInput = document.createElement('input');\r
342 fileInput.type = 'file';\r
343 fileInput.className = Ext.baseCSSPrefix + 'hidden-display';\r
344 fileInput.name = me.getName();\r
345 }\r
346 return fileInput;\r
347 },\r
348 \r
349 restoreInput: function(el) {\r
350 // If we're not rendered we don't need to do anything, it will be created\r
351 // when we get flushed to the DOM.\r
352 if (this.rendered) {\r
353 var button = this.button;\r
354 button.restoreInput(el);\r
355 this.fileInputEl = button.fileInputEl;\r
356 }\r
357 },\r
358\r
359 onDestroy: function(){\r
360 this.fileInputEl = this.button = null;\r
361 this.callParent();\r
362 },\r
363\r
364 getButtonMarginProp: function() {\r
365 return 'margin-left:';\r
366 },\r
367 \r
368 onInputFocus: function(e) {\r
369 this.focus();\r
370 \r
371 // Switching focus from read only input element to file input\r
372 // results in incorrect positioning of the file input.\r
373 // Adding and removing position: relative helps to fix that.\r
374 // See https://sencha.jira.com/browse/EXTJS-18933\r
375 if (Ext.isIE9m) {\r
376 this.fileInputEl.addCls(Ext.baseCSSPrefix + 'position-relative');\r
377 this.fileInputEl.removeCls(Ext.baseCSSPrefix + 'position-relative');\r
378 }\r
379 },\r
380 \r
381 onInputMouseDown: function(e) {\r
382 // Some browsers will show the cursor even if the input is read only,\r
383 // which will be visible in the short instant between inputEl focusing\r
384 // and subsequent focus jump to the FileButton. Preventing inputEl from\r
385 // focusing eliminates that flicker.\r
386 e.preventDefault();\r
387 \r
388 this.focus();\r
389 },\r
390 \r
391 privates: {\r
392 getFocusEl: function() {\r
393 return this.button;\r
394 },\r
395 \r
396 getFocusClsEl: Ext.privateFn\r
397 }\r
398});\r