/**
* @docauthor Jason Johnston <[email protected]>
*
* カスタムのスタイルを備えたファイルアップロードフィールドであり、ボタンのテキストや、{@link Ext.form.field.Text#emptyText 空白のテキスト}といったその他の{@link Ext.form.field.Text テキストフィールド}の機能をコントロールできます。非表示のファイル入力要素をバックグラウンドで使用することにより、ユーザーによるファイル選択を実現し、{@link Ext.form.Basic#submit フォーム送信}時における実際のアップロード処理を実行します。
*
* クロスブラウザにおいて、ファイル入力による値をプログラム的にセットする安全な方法はないため、フィールドの標準的な`setValue`メソッドは実装されません。`{@link #getValue}`メソッドではブラウザに依存する値を返します。ファイル名のみを持つものもあれば、完全パスを持つものや偽のパスを持つものもあります。
*
* **重要:**ファイルアップロードは、通常の'Ajax' 技術では実行されません。詳細は、{@link Ext.form.Basic#hasUpload}の説明を参照してください。
*
* #使用例
*
* @example
* Ext.create('Ext.form.Panel', {
* title: 'Upload a Photo',
* width: 400,
* bodyPadding: 10,
* frame: true,
* renderTo: Ext.getBody(),
* items: [{
* xtype: 'filefield',
* name: 'photo',
* fieldLabel: 'Photo',
* labelWidth: 50,
* msgTarget: 'side',
* allowBlank: false,
* anchor: '100%',
* buttonText: 'Select Photo...'
* }],
*
* buttons: [{
* text: 'Upload',
* handler: function() {
* var form = this.up('form').getForm();
* if(form.isValid()){
* form.submit({
* url: 'photo-upload.php',
* waitMsg: 'Uploading your photo...',
* success: function(fp, o) {
* Ext.Msg.alert('Success', 'Your photo "' + o.result.file + '" has been uploaded.');
* }
* });
* }
* }
* }]
* });
*/
Ext.define('Ext.form.field.File', {
extend: 'Ext.form.field.Text',
alias: ['widget.filefield', 'widget.fileuploadfield'],
alternateClassName: ['Ext.form.FileUploadField', 'Ext.ux.form.FileUploadField', 'Ext.form.File'],
requires: [
'Ext.form.field.FileButton',
'Ext.form.trigger.Component'
],
needArrowKeys: false,
triggers: {
filebutton: {
type: 'component',
hideOnReadOnly : false
}
},
//<locale>
/**
* @cfg {String} buttonText
* アップロードボタンに表示するテキストです。{@link #buttonConfig}の値を設定した場合は、利用可能であればbuttonConfig.textがこの値の代わりに使われることに注意してください。
*/
buttonText: 'Browse...',
//</locale>
/**
* @cfg {Boolean} buttonOnly
* true にするとテキストフィールドなしで、ボタンだけを表示します。trueに設定すると継承されたテキストフィールドのメンバー全てが利用可能です。
*/
buttonOnly: false,
/**
* @cfg {Number} buttonMargin
* ボタンとテキストフィールドの間に設けられているスペースのピクセル数です。これが適用されるのは{@link #buttonOnly} = falseの場合のみであることに注意してください。
*/
buttonMargin: 3,
/**
* @cfg {Boolean} clearOnSubmit
* このフィールドがあるフォームがサーバーに送信されたときに選択したファイルの値をクリアする場合はtrueを設定します。
*/
clearOnSubmit: true,
/**
* @cfg {Object} buttonConfig
* アップロードボタン用のオプションのカスタムボタン{@link Ext.button.Button}コンフィグ(iconCls、textなど)を指定します。
*/
/**
* @event change
* ユーザーがシステム上のファイル選択ダイアログで新しいファイルを選択したことにより、ファイルのインプットフィールドの値が変化した場合に発火します。
* @param {Ext.ux.form.FileUploadField} this
* @param {String} value 元となるファイル入力フィールドから返されたファイルの値です。
*/
/**
* @property {Ext.dom.Element} fileInputEl
* このフィールドのために生成された不可視のファイルインプットフィールドへの参照です。このコンポーネントがレンダリングされた後だけ存在します。
*/
/**
* @property {Ext.button.Button} button
* このアップロードフィールドのために生成されたトリガーボタンコンポーネントへの参照です。このコンポーネントがレンダリングされた後だけ存在します。
*/
// private
extraFieldBodyCls: Ext.baseCSSPrefix + 'form-file-wrap',
// private
inputCls: Ext.baseCSSPrefix + 'form-text-file',
/**
* @cfg {Boolean} [readOnly=true]
* 他のフォームフィールドとは異なり、readOnlyコンフィグはFileではデフォルトでtrueとなっています。
*/
readOnly: true,
/**
* @cfg {Boolean} editable
* @inheritdoc
*/
editable: false,
submitValue: false,
/**
* ボタンをクリックするときにファイル選択のダイアログが表示されてから、ハンドポインタを表示させません。
* @private
*/
triggerNoEditCls: '',
// @private
// Extract the file element, button outer element, and button active element.
childEls: ['browseButtonWrap'],
// @private
applyTriggers: function(triggers) {
var me = this,
triggerCfg = (triggers || {}).filebutton;
if (triggerCfg) {
triggerCfg.component = Ext.apply({
xtype: 'filebutton',
ownerCt: me,
id: me.id + '-button',
ui: me.ui,
disabled: me.disabled,
text: me.buttonText,
style: me.buttonOnly ? '' : me.getButtonMarginProp() + me.buttonMargin + 'px',
inputName: me.getName(),
listeners: {
scope: me,
change: me.onFileChange
}
}, me.buttonConfig);
return me.callParent([triggers]);
}
// <debug>
else {
Ext.Error.raise(me.$className + ' requires a valid trigger config containing "filebutton" specification');
}
// </debug>
},
getSubTplData: function(fieldData) {
var data = this.callParent([fieldData]);
// Input field itself should not be focusable since it's always decorative;
// however the input element is naturally focusable (and tabbable) so we have to
// deactivate it by setting its tabIndex to -1.
data.tabIdx = -1;
return data;
},
// @private
onRender: function() {
var me = this,
inputEl, button, buttonEl, trigger;
me.callParent(arguments);
inputEl = me.inputEl;
inputEl.dom.name = ''; //name goes on the fileInput, not the text input
trigger = me.getTrigger('filebutton');
button = me.button = trigger.component;
me.fileInputEl = button.fileInputEl;
buttonEl = button.el;
if (me.buttonOnly) {
me.inputWrap.setDisplayed(false);
me.shrinkWrap = 3;
}
// Ensure the trigger element is sized correctly upon render
trigger.el.setWidth(buttonEl.getWidth() + buttonEl.getMargin('lr'));
if (Ext.isIE) {
me.button.getEl().repaint();
}
},
/**
* subTplMarkup内へ挿入されたマークアップを取得します。
*/
getTriggerMarkup: function() {
return '<td id="' + this.id + '-browseButtonWrap" data-ref="browseButtonWrap" role="presentation"></td>';
},
/**
* @private ユーザーがファイルを選択した場合に発火するイベントハンドラです
*/
onFileChange: function(button, e, value) {
this.duringFileSelect = true;
Ext.form.field.File.superclass.setValue.call(this, value);
delete this.duringFileSelect;
},
didValueChange: function(){
// In the case of the file field, the change event will only ever fire
// if the value actually changes, so we always want to fire the change event
// This affects Chrome specifically, because hitting the cancel button will
// reset the file upload.
return !!this.duringFileSelect;
},
/**
* オーバーライドしません。
* @method
*/
setValue: Ext.emptyFn,
reset : function(){
var me = this,
clear = me.clearOnSubmit;
if (me.rendered) {
me.button.reset(clear);
me.fileInputEl = me.button.fileInputEl;
if (clear) {
me.inputEl.dom.value = '';
// Reset the underlying value if we're clearing it
Ext.form.field.File.superclass.setValue.call(this, null);
}
}
me.callParent();
},
onShow: function(){
this.callParent();
// If we started out hidden, the button may have a messed up layout
// since we don't act like a container
this.button.updateLayout();
},
onDisable: function(){
this.callParent();
this.button.disable();
},
onEnable: function(){
this.callParent();
this.button.enable();
},
/**
* @method
* @inheritdoc
*/
isFileUpload: Ext.returnTrue,
var me = this,
fileInput;
if (me.rendered) {
fileInput = me.button.fileInputEl.dom;
me.reset();
} else {
// Create a fake empty field here so it will still be submitted.
// All other unrendered fields provide a value.
fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.className = Ext.baseCSSPrefix + 'hidden-display';
fileInput.name = me.getName();
}
return fileInput;
},
restoreInput: function(el) {
// If we're not rendered we don't need to do anything, it will be created
// when we get flushed to the DOM.
if (this.rendered) {
var button = this.button;
button.restoreInput(el);
this.fileInputEl = button.fileInputEl;
}
},
onDestroy: function(){
Ext.destroyMembers(this, 'button');
delete this.fileInputEl;
this.callParent();
},
getButtonMarginProp: function() {
return 'margin-left:';
},
privates: {
getFocusEl: function() {
return this.button;
}
}
});