PDA

View Full Version : Ext.ux.form.BrowseButton



loeppky
10 Mar 2008, 3:19 PM
Note: this extension is no longer supported (by me) because the Ext team have provided their own File Upload field (see http://extjs.com/deploy/dev/examples/form/file-upload.html). As a result, you'll either need to read the most recent posts where people have posted updated code to support newer browsers and Ext versions or switch to using the Ext provided control.

I have put work into fixing Ext 3.x's Ext.ux.form.FileUploadField. See http://www.extjs.com/forum/showthread.php?p=402903. It also contains updated code.


I needed the ability for a user to click an Ext button and for a file browse window to appear. jsakalos has done this in his Ext.ux.UploadForm (http://extjs.com/forum/showthread.php?t=9305) and MaximGB has done it in his Ext.ux.UploadDialog extension (http://extjs.com/forum/showthread.php?t=21558). I preferred MaximGB's implementation because of the slick BrowseButton class he had created to encapsulate the logic. It also maintained all of the original Ext.Button look and feel (e.g. tooltips, hovering). I have taken his original design and improved upon it in the following ways:
Doesn't interfere with adjacent buttons.
Added a debug mode to easily see how well the overlain input file "Browse" button is being placed.
Commented it.

Updates:
3/11/08 - Fully compatible with both IE6 and IE 7. MaximGB came up with the idea of having a floating div that would follow the mouse cursor, effectively always keeping the input file "Browse" button under the cursor. I have worked from his initial implementation, and the output is great. Try the sample application below with "debug : true" to see how well it's working.
3/12/08 - Removed the destroy method because it had a bug and was unnecessary. The parent class (Ext.Button) destroy method is sufficient to remove the added elements and listeners.
3/12/08 - Updated browser compatibility as I was able to test it on a Mac. There are no issues with Safari 3, and the Firefox 2 cursor issue is same as on Windows.
3/13/08 - Updated browser compatibility as I was able to test on Linux. Also, tested things on Opera. Started keeping a list of known issues at the bottom of the post. Also stated the license (public domain).
3/14/08 - Added tab support for selecting the BrowseButton.
6/5/08 - Fixed a few outstanding issues including the leaking of <em> elements (post #16), clicking browse button in IE not working sometimes (post #24), and transparent mask can be very small (post #30)
6/23/08 - Fixed issues where the browse button looses clicks in IE 6 and 7 as reported in post 57 and 58.
8/24/09 - Added notice that this extension is no longer supported (see beginning of post).




Ext.namespace('Ext.ux.form');

/**
* @class Ext.ux.form.BrowseButton
* @extends Ext.Button
* Ext.Button that provides a customizable file browse button.
* Clicking this button, pops up a file dialog box for a user to select the file to upload.
* This is accomplished by having a transparent <input type="file"> box above the Ext.Button.
* When a user thinks he or she is clicking the Ext.Button, they're actually clicking the hidden input "Browse..." box.
* Note: this class can be instantiated explicitly or with xtypes anywhere a regular Ext.Button can be except in 2 scenarios:
* - Panel.addButton method both as an instantiated object or as an xtype config object.
* - Panel.buttons config object as an xtype config object.
* These scenarios fail because Ext explicitly creates an Ext.Button in these cases.
* Browser compatibility:
* Internet Explorer 6:
* - no issues
* Internet Explorer 7:
* - no issues
* Firefox 2 - Windows:
* - pointer cursor doesn't display when hovering over the button.
* Safari 3 - Windows:
* - no issues.
* @author loeppky - based on the work done by MaximGB in Ext.ux.UploadDialog (http://extjs.com/forum/showthread.php?t=21558)
* The follow the curosr float div idea also came from MaximGB.
* @see http://extjs.com/forum/showthread.php?t=29032
* @constructor
* Create a new BrowseButton.
* @param {Object} config Configuration options
*/
Ext.ux.form.BrowseButton = Ext.extend(Ext.Button, {
/*
* Config options:
*/
/**
* @cfg {String} inputFileName
* Name to use for the hidden input file DOM element. Deaults to "file".
*/
inputFileName: 'file',
/**
* @cfg {Boolean} debug
* Toggle for turning on debug mode.
* Debug mode doesn't make clipEl transparent so that one can see how effectively it covers the Ext.Button.
* In addition, clipEl is given a green background and floatEl a red background to see how well they are positioned.
*/
debug: false,


/*
* Private constants:
*/
/**
* @property FLOAT_EL_WIDTH
* @type Number
* The width (in pixels) of floatEl.
* It should be less than the width of the IE "Browse" button's width (65 pixels), since IE doesn't let you resize it.
* We define this width so we can quickly center floatEl at the mouse cursor without having to make any function calls.
* @private
*/
FLOAT_EL_WIDTH: 60,

/**
* @property FLOAT_EL_HEIGHT
* @type Number
* The heigh (in pixels) of floatEl.
* It should be less than the height of the "Browse" button's height.
* We define this height so we can quickly center floatEl at the mouse cursor without having to make any function calls.
* @private
*/
FLOAT_EL_HEIGHT: 18,


/*
* Private properties:
*/
/**
* @property buttonCt
* @type Ext.Element
* Element that contains the actual Button DOM element.
* We store a reference to it, so we can easily grab its size for sizing the clipEl.
* @private
*/
buttonCt: null,
/**
* @property clipEl
* @type Ext.Element
* Element that contains the floatEl.
* This element is positioned to fill the area of Ext.Button and has overflow turned off.
* This keeps floadEl tight to the Ext.Button, and prevents it from masking surrounding elements.
* @private
*/
clipEl: null,
/**
* @property floatEl
* @type Ext.Element
* Element that contains the inputFileEl.
* This element is size to be less than or equal to the size of the input file "Browse" button.
* It is then positioned wherever the user moves the cursor, so that their click always clicks the input file "Browse" button.
* Overflow is turned off to preven inputFileEl from masking surrounding elements.
* @private
*/
floatEl: null,
/**
* @property inputFileEl
* @type Ext.Element
* Element for the hiden file input.
* @private
*/
inputFileEl: null,
/**
* @property originalHandler
* @type Function
* The handler originally defined for the Ext.Button during construction using the "handler" config option.
* We need to null out the "handler" property so that it is only called when a file is selected.
* @private
*/
originalHandler: null,
/**
* @property originalScope
* @type Object
* The scope originally defined for the Ext.Button during construction using the "scope" config option.
* While the "scope" property doesn't need to be nulled, to be consistent with originalHandler, we do.
* @private
*/
originalScope: null,


/*
* Protected Ext.Button overrides
*/
/**
* @see Ext.Button.initComponent
*/
initComponent: function(){
Ext.ux.form.BrowseButton.superclass.initComponent.call(this);
// Store references to the original handler and scope before nulling them.
// This is done so that this class can control when the handler is called.
// There are some cases where the hidden file input browse button doesn't completely cover the Ext.Button.
// The handler shouldn't be called in these cases. It should only be called if a new file is selected on the file system.
this.originalHandler = this.handler;
this.originalScope = this.scope;
this.handler = null;
this.scope = null;
},

/**
* @see Ext.Button.onRender
*/
onRender: function(ct, position){
Ext.ux.form.BrowseButton.superclass.onRender.call(this, ct, position); // render the Ext.Button
this.buttonCt = this.el.child('.x-btn-center em');
this.buttonCt.position('relative'); // this is important!
var styleCfg = {
position: 'absolute',
overflow: 'hidden',
top: '0px', // default
left: '0px' // default
};
// browser specifics for better overlay tightness
if (Ext.isIE) {
Ext.apply(styleCfg, {
left: '-3px',
top: '-3px'
});
} else if (Ext.isGecko) {
Ext.apply(styleCfg, {
left: '-3px',
top: '-3px'
});
} else if (Ext.isSafari) {
Ext.apply(styleCfg, {
left: '-4px',
top: '-2px'
});
}
this.clipEl = this.buttonCt.createChild({
tag: 'div',
style: styleCfg
});
this.setClipSize();
this.clipEl.on({
'mousemove': this.onButtonMouseMove,
'mouseover': this.onButtonMouseMove,
scope: this
});

this.floatEl = this.clipEl.createChild({
tag: 'div',
style: {
position: 'absolute',
width: this.FLOAT_EL_WIDTH + 'px',
height: this.FLOAT_EL_HEIGHT + 'px',
overflow: 'hidden'
}
});


if (this.debug) {
this.clipEl.applyStyles({
'background-color': 'green'
});
this.floatEl.applyStyles({
'background-color': 'red'
});
} else {
// We don't set the clipEl to be transparent, because IE 6/7 occassionaly looses mouse events for transparent elements.
// We have listeners on the clipEl that can't be lost as they're needed for realligning the input file element.
this.floatEl.setOpacity(0.0);
}

// Cover cases where someone tabs to the button:
// Listen to focus of the button so we can translate the focus to the input file el.
var buttonEl = this.el.child(this.buttonSelector);
buttonEl.on('focus', this.onButtonFocus, this);
// In IE, it's possible to tab to the text portion of the input file el.
// We want to listen to keyevents so that if a space is pressed, we "click" the input file el.
if (Ext.isIE) {
this.el.on('keydown', this.onButtonKeyDown, this);
}

this.createInputFile();
},


/*
* Private helper methods:
*/
/**
* Sets the size of clipEl so that is covering as much of the button as possible.
* @private
*/
setClipSize: function(){
if (this.clipEl) {
var width = this.buttonCt.getWidth();
var height = this.buttonCt.getHeight();
// The button container can have a width and height of zero when it's rendered in a hidden panel.
// This is most noticable when using a card layout, as the items are all rendered but hidden,
// (unless deferredRender is set to true).
// In this case, the clip size can't be determined, so we attempt to set it later.
// This check repeats until the button container has a size.
if (width === 0 || height === 0) {
this.setClipSize.defer(100, this);
} else {
if (Ext.isIE) {
width = width + 5;
height = height + 5;
} else if (Ext.isGecko) {
width = width + 6;
height = height + 6;
} else if (Ext.isSafari) {
width = width + 6;
height = height + 6;
}
this.clipEl.setSize(width, height);
}
}
},

/**
* Creates the input file element and adds it to inputFileCt.
* The created input file elementis sized, positioned, and styled appropriately.
* Event handlers for the element are set up, and a tooltip is applied if defined in the original config.
* @private
*/
createInputFile: function(){
// When an input file gets detached and set as the child of a different DOM element,
// straggling <em> elements get left behind.
// I don't know why this happens but we delete any <em> elements we can find under the floatEl to prevent a memory leak.
this.floatEl.select('em').each(function(el){
el.remove();
});
this.inputFileEl = this.floatEl.createChild({
tag: 'input',
type: 'file',
size: 1, // must be > 0. It's value doesn't really matter due to our masking div (inputFileCt).
name: this.inputFileName || Ext.id(this.el),
tabindex: this.tabIndex,
// Use the same pointer as an Ext.Button would use. This doesn't work in Firefox.
// This positioning right-aligns the input file to ensure that the "Browse" button is visible.
style: {
position: 'absolute',
cursor: 'pointer',
right: '0px',
top: '0px'
}
});
this.inputFileEl = this.inputFileEl.child('input') || this.inputFileEl;

// setup events
this.inputFileEl.on({
'click': this.onInputFileClick,
'change': this.onInputFileChange,
'focus': this.onInputFileFocus,
'select': this.onInputFileFocus,
'blur': this.onInputFileBlur,
scope: this
});

// add a tooltip
if (this.tooltip) {
if (typeof this.tooltip == 'object') {
Ext.QuickTips.register(Ext.apply({
target: this.inputFileEl
}, this.tooltip));
} else {
this.inputFileEl.dom[this.tooltipType] = this.tooltip;
}
}
},

/**
* Redirecting focus to the input file element so the user can press space and select files.
* @param {Event} e focus event.
* @private
*/
onButtonFocus: function(e){
if (this.inputFileEl) {
this.inputFileEl.focus();
e.stopEvent();
}
},

/**
* Handler for the IE case where once can tab to the text box of an input file el.
* If the key is a space, we simply "click" the inputFileEl.
* @param {Event} e key event.
* @private
*/
onButtonKeyDown: function(e){
if (this.inputFileEl && e.getKey() == Ext.EventObject.SPACE) {
this.inputFileEl.dom.click();
e.stopEvent();
}
},

/**
* Handler when the cursor moves over the clipEl.
* The floatEl gets centered to the cursor location.
* @param {Event} e mouse event.
* @private
*/
onButtonMouseMove: function(e){
var xy = e.getXY();
xy[0] -= this.FLOAT_EL_WIDTH / 2;
xy[1] -= this.FLOAT_EL_HEIGHT / 2;
this.floatEl.setXY(xy);
},

/**
* Add the visual enhancement to the button when the input file recieves focus.
* This is the tip for the user that now he/she can press space to select the file.
* @private
*/
onInputFileFocus: function(e){
if (!this.isDisabled) {
this.el.addClass("x-btn-over");
}
},

/**
* Removes the visual enhancement from the button.
* @private
*/
onInputFileBlur: function(e){
this.el.removeClass("x-btn-over");
},

/**
* Handler when inputFileEl's "Browse..." button is clicked.
* @param {Event} e click event.
* @private
*/
onInputFileClick: function(e){
e.stopPropagation();
},

/**
* Handler when inputFileEl changes value (i.e. a new file is selected).
* @private
*/
onInputFileChange: function(){
if (this.originalHandler) {
this.originalHandler.call(this.originalScope, this);
}
},


/*
* Public methods:
*/
/**
* Detaches the input file associated with this BrowseButton so that it can be used for other purposed (e.g. uplaoding).
* The returned input file has all listeners and tooltips applied to it by this class removed.
* @param {Boolean} whether to create a new input file element for this BrowseButton after detaching.
* True will prevent creation. Defaults to false.
* @return {Ext.Element} the detached input file element.
*/
detachInputFile: function(noCreate){
var result = this.inputFileEl;

if (typeof this.tooltip == 'object') {
Ext.QuickTips.unregister(this.inputFileEl);
} else {
this.inputFileEl.dom[this.tooltipType] = null;
}
this.inputFileEl.removeAllListeners();
this.inputFileEl = null;

if (!noCreate) {
this.createInputFile();
}
return result;
},

/**
* @return {Ext.Element} the input file element attached to this BrowseButton.
*/
getInputFile: function(){
return this.inputFileEl;
},

/**
* @see Ext.Button.disable
*/
disable: function(){
Ext.ux.form.BrowseButton.superclass.disable.call(this);
this.inputFileEl.dom.disabled = true;
},

/**
* @see Ext.Button.enable
*/
enable: function(){
Ext.ux.form.BrowseButton.superclass.enable.call(this);
this.inputFileEl.dom.disabled = false;
}
});

Ext.reg('browsebutton', Ext.ux.form.BrowseButton);


License: public domain (i.e. use it however you like without any restrictions).

Ext compatibility:
This class can be instantiated explicitly or with xtypes anywhere a regular Ext.Button can be except in 2 scenarios:
Panel.addButton method both as an instantiated object or as an xtype config object.
Panel.buttons config object as an xtype config object.These scenarios fail because Ext explicitly creates an Ext.Button in these cases. It works fine in a panel's tbar with instantiation or xtype, panel's items instantiation or xtype, or panel's buttons with instantiation.

Browser compatibility:
Internet Explorer 6:
No issuesInternet Explorer 7
No issues
Firefox 2 (Mac/Windows/Linux):
Pointer cursor doesn't display when hovering over the button.
Firefox 3 (Mac/Windows/Linux):
Pointer cursor doesn't display when hovering over the button.
Safari 3 - (Mac/Windows)
No issuesOpera - (Mac/Windows/Linux)
Only works with explicitly instantiated buttons for a panel. Doesn't work in the tbar or items.

Known issues:

Reverse-tabbing (shift+tab) doesn't work once one has tabbed to the BrowseButon. One has to tab twice to get past the BrowseButton.

loeppky
10 Mar 2008, 3:20 PM
Here’s a sample of Ext.ux.form.BrowseButton in action: http://loeppky.com/steven/code/samples/BrowseButton/

ScreenshotPanel class that uses Ext.ux.form.BrowseButton for selecting a screenshot, and then creates a form to upload it. This class highlights the different ways Ext.ux.form.BrowseButton can be used.

Updates:
3/12/08 - Added a link to a sample of this code online. ScreenshotsPanel now also lists the files that were selected for upload (although the upload itself fails due to having nothing implemented on the back end).
3/13/08 - Corrected the titles of the buttons for the demo.
6/6/08 - Made a few formatting changes. Updated to use Ext 2.1.



/**
* @class Ext.ux.ScreenshotPanel
* @extends Ext.Panel
* Renders a browse button for demonstration purposes.
* @constructor
* @param {Object} config The config object
*/
Ext.ux.ScreenshotsPanel = Ext.extend(Ext.Panel, {
/*
* Config options
*/
/**
* For whether to draw the panel (and it's BrowseButtons) in debug mode.
* @type Boolean
*/
debug: false,


/*
* Private properties
*/
/**
* Panel for displaying the files that have selected for upload.
* @type Ext.Panel
*/
filePanel: null,
/**
* Form for uploading screenshot images.
* Is only instantiated if a screenshot is selected to upload.
* @type Ext.form.BasicForm
*/
addScreenshotForm: null,


/*
* Protected methods
*/
/**
* @see Ext.Panel.initConfiguration
*/
initComponent: function(){
var browseButtonBaseConfig = {
xtype: 'browsebutton',
handler: this.onAddScreenshot,
scope: this,
tooltip: 'Click to upload a new screenshot.',
inputFileName: 'screenshot',
debug: this.debug // set to true to see the "Browse" overlay
};
this.filePanel = new Ext.Panel({
frame: true,
title: 'selected files',
buttonAlign: 'center',
buttons: [{
text: 'Upload (does not work and will fail)',
handler: this.uploadScreenshots,
scope: this
}]
});
Ext.apply(this, {
tbar: [
new Ext.ux.form.BrowseButton(Ext.apply({
text: 'tbar - explicit'
}, browseButtonBaseConfig)),
Ext.apply({
text: 'tbar - xtype'
}, browseButtonBaseConfig)
],
items: [
new Ext.ux.form.BrowseButton(Ext.apply({
text: 'items - explicit'
}, browseButtonBaseConfig)),
Ext.apply({
text: 'items - xtype'
}, browseButtonBaseConfig),
this.filePanel
],
buttons: [
new Ext.ux.form.BrowseButton(Ext.apply({
text: 'buttons - explicit'
}, browseButtonBaseConfig)),
Ext.apply({
text: 'buttons - xtype'
}, browseButtonBaseConfig) // doesn't work correctly as Ext uses the config object to instaniate an Ext.Button
]
});
Ext.ux.ScreenshotsPanel.superclass.initComponent.call(this);
},


/*
* Private methods
*/
/**
* Handler for the add screenshot button.
* @param {Ext.ux.form.BrowseButton} browseButton The browse button where "Add Screenshot" was clicked.
* @private
*/
onAddScreenshot: function(browseButton){
if (!this.addScreenshotForm) { // if the form hasn't been created
var addScreenshotFormEl = this.filePanel.body.createChild({
tag: 'form',
style: 'display:none'
});
this.addScreenshotForm = new Ext.form.BasicForm(addScreenshotFormEl, {
url: '/AddScreenshot',
fileUpload: true
});
}
var inputFileEl = browseButton.detachInputFile();
inputFileEl.appendTo(this.addScreenshotForm.getEl());
this.filePanel.body.createChild({
tag: 'div',
html: inputFileEl.dom.value
});
},

/**
* Handler for the upload screenshots button
*/
uploadScreenshots: function(){
this.addScreenshotForm.submit({
params: {
extraParam1: 'value1'
},
success: this.onAddScreenshotSuccess,
failure: this.onAddScreenshotFailure,
scope: this,
waitTitle: 'Uploading Screenshot',
waitMsg: 'Your screenshot is being uploaded...'
});
},

/**
* Callback for when a screenshot has been successfully added.
* @param {Ext.form.BasicForm} form the form for which the uploading occurred.
* @param {Ext.form.Action} action the action of the form submit
* @private
*/
onAddScreenshotSuccess: function(form, action){
// remove the file input element so that it doesn't get uploaded again with the next screenshot
var inputFileEl = this.addScreenshotForm.getEl().child('input');
inputFileEl.remove();
Ext.Msg.show({
title: 'Screenshot Upload Success',
msg: 'Your screenshot was successfully uploaded.',
buttons: Ext.Msg.OK,
minWidth: 300
});
},

/**
* Callback for when a screenshot has not been successfully added.
* @param {Ext.form.BasicForm} form the form for which the uploading occurred.
* @param {Ext.form.Action} action the action of the form submit
* @private
*/
onAddScreenshotFailure: function(form, action){
// remove the file input element so that it doesn't get uploaded again with the next screenshot
var inputFileEl = this.addScreenshotForm.getEl().child('input');
inputFileEl.remove();
var errorMessageTemplate = new Ext.XTemplate('<p>Your screenshot was not uploaded. The server reported the following errors:</p>', '<tpl for="errors">', '<p>- {.}</p>', '</tpl>');
Ext.Msg.show({
title: 'Screenshot Upload Failed',
msg: errorMessageTemplate.applyTemplate(action.result),
buttons: Ext.Msg.OK,
minWidth: 300
});
}
});


Index.html file for setting everything up. Note: you’ll have to change the file paths for your setup.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<link rel="stylesheet" type="text/css" href="../../external/ext-2.1/resources/css/ext-all.css"/>
<!-- Ext dependencies -->
<script type="text/javascript" src="../../external/ext-2.1/adapter/ext/ext-base.js">
</script>
<script type="text/javascript" src="../../external/ext-2.1/ext-all-debug.js">
</script>
<!-- Ext extension dependencies -->
<script type="text/javascript" src="../../external/ext-extensions/source/widgets/form/BrowseButton.js">
</script>
<!-- Application dependencies -->
<script type="text/javascript" src="ScreenshotsPanel.js">
</script>
<!-- Bootstrap application -->
<script type="text/javascript">
Ext.onReady(function(){
Ext.QuickTips.init();
var viewport = new Ext.Viewport({
items: [{
xtype: 'panel',
items: [{
tag : 'div',
html : 'Form post: <a href="http://extjs.com/forum/showthread.php?t=29032">http://extjs.com/forum/showthread.php?t=29032</a>'
}, new Ext.ux.ScreenshotsPanel({
title: 'Screenshot Adder - Debug = false, meaning Browse butons are hidden.',
frame: true,
debug: false
}), new Ext.ux.ScreenshotsPanel({
title: 'Screenshot Adder - Debug = true, meaning Browse butons are visible.',
frame: true,
debug: true
})]
}]
});
});
</script>
<title>Ext.ux.form.BrowseButton sample</title>
</head>
<body>
</body>
</html>

shane.fox
11 Mar 2008, 6:51 PM
Good job.

I got an error in the destroy method. 'inputFileCt is not defined'

Perhaps it needs to be changed to 'this.inputFileCt.remove(); this.inputFileCt = null'

JorisA
12 Mar 2008, 1:12 AM
Thnx a gazillion, I was just going to try do something like this for myself :)

loeppky
12 Mar 2008, 8:39 AM
shane.fox: I forgot to update the destroy method yesterday when I improved the code. In looking at it again and testing, I have found that the destroy method is unnecessary to override. I have removed it from the Ext.ux.form.BrowseButton class above. Let me know how that works for you.

loeppky
12 Mar 2008, 9:10 AM
JorisA: you're very welcome although the idea credit definitely goes to MaximGB :)

loeppky
12 Mar 2008, 4:44 PM
I updated post #2 (http://extjs.com/forum/showthread.php?p=136247#post136247) with a link to a sample of BrowseButton online: http://loeppky.com/steven/code/samples/BrowseButton/

t34
13 Mar 2008, 4:51 AM
Hi, i use your BrowseButton very successful in a ASP.NET - project. Thank you for the good job.
If someone is interested - see my helper files in the appendix.

Now for some Problems:

If one step with the TAB-key through the page he reaches the hidden file selector control. I check this with FF2 and IE6.
Konqueror shows the hidden field - so it is nearly impossible to use BrowseButton here.


Greetings from the baltic,
thomas

loeppky
13 Mar 2008, 10:33 AM
t34: Glad to hear it's working for you, and thanks for reporting these issues. Here's my thoughts:

I have verified the tab key issue and made note of it. We'll see how important/worth while it is to fix. Feel free to take this on if you'd like :)
Since Ext doesn't official support Konqueror, I don't think it's worth investing any effort there.Let me know if you run into any other issues. Thanks as well for the Baltic greeting. I spent 2 months in Bosnia a couple of years ago...

dawesi
13 Mar 2008, 10:48 PM
see pic below... both render the same

loeppky
14 Mar 2008, 8:43 AM
dawesi: I don't see what the problem is? What do you mean by "they both render the same"? The debug version below shows the input file "Browse" button not being hidden.

loeppky
14 Mar 2008, 3:29 PM
t34: Once again, MaximGB comes up huge. He had some ideas on a great starting point for making tabbing work with the BrowseButton. I have incorporated, moddified (slightly), and tested his suggestions. Go ahead and test if out on the demo page: http://loeppky.com/steven/code/samples/BrowseButton/.

Notes: you may have to clear your cache to pick up the newest version of BrowseButton.js. Also, as stated in the original post, reverse-tabbing (shift+tab) doesn't work once one has tabbed to the BrowseButon. One has to tab twice to get past the BrowseButton.

jsakalos
14 Mar 2008, 9:19 PM
Hi loeppky,

is there any reason why inputs are wrapped in <em></em>? I mean detachInputFile() returns input element wrapped in em. It just complicates my code as I need to cleanup those stray ems.

jsakalos
14 Mar 2008, 9:22 PM
Better to say detachInputFile() returns input element but there are still stray unneeded ems.

loeppky
15 Mar 2008, 2:59 PM
jsakalos: I don't know what the root issue is, but I assume it's a browser or Ext thing. When I create the input file element in the createInputFile method, you'll notice that the line right after is this.inputFileEl = this.inputFileEl.child('input') || this.inputFileEl. This is done because the create method sometimes returns <em><input type="file/></em> verses <input type="file/>. Again, I don't know why this is, but the detachInputFile method should just return the input file, not the input file wrapped in em tags. Is that what you're seeing? Is your concern then just the left over em tags?

jsakalos
15 Mar 2008, 3:06 PM
Yeah, detachInputFile always returns <input></input>. The root of the issue is that I want to get rid of those unnecessary <em></em> that would lead to memory leak in the long run. (Tiny leak, but why not to cleanup if we know about it).

loeppky
15 Mar 2008, 3:16 PM
I have family coming over soon and won't be able to look into this until Monday. If you want to take care of it, and send me the fix, I'll get it posted Monday when I head into the office.

jsakalos
15 Mar 2008, 3:32 PM
Well, it is not that urgent as it doesn't prevent functionality of the browser button. Anyway, I'm not going into it - author's debugging is always most effective.

Take it easy, not urgent problem.

Ramsay
4 Apr 2008, 6:33 AM
I'd like to be able to hit the browse button, choose a file, and as soon as I hit ok have that file uploaded by XHR... any pointers on how I could do this?

t34
4 Apr 2008, 6:43 AM
Please have a look at post #8.

loeppky
4 Apr 2008, 8:37 AM
Ramsay: in addition to the example t34 posted in post #8, the sample I posted in post #2 could be easily modified to do this.

dotchris
4 Apr 2008, 2:26 PM
Anyone else having the issue where it takes 2 clicks to get it to bring up the dialog to pick a file?

mankz
8 Apr 2008, 7:00 AM
Anyone else having the issue where it takes 2 clicks to get it to bring up the dialog to pick a file?
Sometimes I can click up to 10 times before the browsebutton reacts, seen this in IE6/IE7. Will dig into it soon, let you know if I find something... I think the error is in the function below, not sure what it's for:

onButtonMouseMove: function(e){
var xy = e.getXY();
xy[0] -= this.FLOAT_EL_WIDTH / 2;
xy[1] -= this.FLOAT_EL_HEIGHT / 2;
this.floatEl.setXY(xy);
}

Whenever my browsebutton stops working, I can always get it to work by moving the mouse from the button and then try again...

4him
10 Apr 2008, 5:05 AM
Originally Posted by dotchris View Post
Anyone else having the issue where it takes 2 clicks to get it to bring up the dialog to pick a file?
Sometimes I can click up to 10 times before the browsebutton reacts, seen this in IE6/IE7. Will dig into it soon, let you know if I find something... I think the error is in the function below, not sure what it's for:

onButtonMouseMove: function(e){
var xy = e.getXY();
xy[0] -= this.FLOAT_EL_WIDTH / 2;
xy[1] -= this.FLOAT_EL_HEIGHT / 2;
this.floatEl.setXY(xy);
}

Whenever my browsebutton stops working, I can always get it to work by moving the mouse from the button and then try again...

It does it in IE for me, but not when in debug mode. I have found a workaround by applying the background styles to the clip element also when not in debug mode:


this.clipEl.applyStyles({
'background-color': 'green'
});
this.floatEl.applyStyles({
'background-color': 'red'
});

if (!this.debug) {
this.clipEl.setOpacity(0.0);
}


instead of:


if (this.debug) {
this.clipEl.applyStyles({
'background-color': 'green'
});
this.floatEl.applyStyles({
'background-color': 'red'
});
} else {
this.clipEl.setOpacity(0.0);
}


Maybe loeppky will want to add that to his code...

mankz
10 Apr 2008, 5:36 AM
Very nice discovery, cannot reproduce the error now. Though it truly puzzles me why setting a background solves this problem... huhuh

dddu88
10 Apr 2008, 6:49 AM
Hi, all,
I am trying to get the BrowseButton to work, I got an error: "path has no properties", I debugged into the code, and found it happened due to this.buttonSelector is undefined when the execution comes to


// Cover cases where someone tabs to the button:
// Listen to focus of the button so we can translate the focus to the input file el.
var buttonEl = this.el.child(this.buttonSelector);

Here is my code:


var filefield = new Ext.form.TextField({
id: 'filefield',
region:'center',
width:150,
height:100,
allowBlank:true
});
var browseButtonBaseConfig = {
xtype: 'browsebutton',
region:'east',
handler: function(){},
scope: this,
tooltip: 'Select a file.',
inputFileName: 'screenshot',
debug: false // set to true to see the "Browse" overlay
};
var bbutton = new Ext.ux.form.BrowseButton(Ext.apply({
text: 'Browse'
}, browseButtonBaseConfig));

var browsepanel = new Ext.Panel({
id:'browsepanelid',
layout:'border',
width:320,
height:100,
plain: true,
items: [filefield, bbutton]
});
var mywin = new Ext.Window({
labelWidth: 150, // label settings here cascade unless overridden
frame:true,
title: 'Select a file:',
bodyStyle:'padding:5px 5px 0',
buttonAlign: 'center',
closable: true,
draggable: true,
width:320,
height:240,
modal:false,
closeAction: 'close',
plain:true,
resizable: true,
items: [{
xtype: 'form',
labelWidth: 75,
labelAlign: 'right',
bodyStyle: 'background-color:#DFE8F6; padding-top:10px',
border: false,
items: [{
xtype:'ux-radiogroup',
fieldLabel:'group1:',
name:'group1',
horizontal:true,
radios:[{
value:1,
boxLabel:'box 1',
listeners:{
'check':function(r,c){
alert(r.boxLabel+": "+(c?"checked":"unchecked"));
}
}
}, {
value:2,
boxLabel:'box 2',
checked:true
}]
},browsepanel
],
buttons: [{
text: 'Ok',
handler: function(){
var values=mywin.getComponent(0).getForm().getValues();
alert("values:"+Ext.encode(values));
}
},{
text: 'Cancel',
tooltip: 'Cancel this operation',
handler: function(){
}
}]

}]
});
mywin.show(this);

Thanks very much for your help

Dave

loeppky
10 Apr 2008, 8:04 AM
4him and mankz: thanks for discovering this issue and for providing a work around. I'm in the middle of a project at the moment, but when it is done in a couple of weeks, I'll look into this more and publish your fix.

loeppky
10 Apr 2008, 8:08 AM
dddu88: that is very odd, as buttonSelector is defined within the parent class (Ext.Button). Anyways, I won't be able to look into this for a couple of weeks, because I'm in the middle of a project that's got to ship soon. I'll be sure to look into it more afterwards, although feel free to poke around on your own. Maybe someone else on the forum will be able to help as well...

dddu88
10 Apr 2008, 9:26 AM
Finally, I got it working by extending BrowseButton from BoxButton instead of Ext.Button.

Thanks very much for your nice work.

Dave

lagos.tout
12 Apr 2008, 11:46 PM
Hi,

When using browsebutton, I've found that I have to manually set the width and height of the button with

Ext.fly(Ext.query('#artistImage_selectFile_id div')[0]).applyStyles('height: 18px; width: 42px;');

(or whatever dimensions work best) if the button resides in a FormPanel that was rendered with config setting 'hidden: true'. If I don't do this, the hidden button is set to a size that is too small to hit (only a couple of pixels in width and height).

I'm curious to know if anyone else has encountered this. btw I'm using FF 2.0.0.13 on xp.

Thanks.

LT

loeppky
14 Apr 2008, 10:46 AM
lagos.tout: I have heard of people having similar issues when using a card layout. I believe theroot issue is the same, and that is that when the Ext.ux.form.BrowseButton is rendered, the underlying button element has no size, the transparent mask is very small. I will make a note of this, and work on it in a week or two when I get my current project done.

loeppky
7 May 2008, 3:23 PM
FYI: I will be out of town until 5/17, and thus won't be able to answer any questions until that point.

JorisA
20 May 2008, 2:47 AM
hm any fix for the clip size bug when used in a tabbed container or so?
at the moment I do

var rezTaskHack = new Ext.util.DelayedTask(function() { this.setClipSize() }, btn);
rezTaskHack.delay(500);

but that's kinda lame...

wm003
20 May 2008, 4:11 AM
Good work! Very useful. Thank you for sharing.;)

loeppky
20 May 2008, 11:07 AM
JorisA: I agree that the work around is lame. I don't have an example built up that demonstrates this issue. Can you try adding this to the BrowseButton class to see if it fixes the issue for you?



autoWidth : function(){
Ext.ux.form.BrowseButton.superclass.autoWidth.call(this);
this.setClipSize();
}


The idea here is that when the button's width is set, we'll also set the clip size. Let me know if that works for you.

Thanks!

SamuraiJack1
22 May 2008, 1:54 PM
Got the same issue as in post #24 - button work with not every click.
I tried with IE6 and FF2.

Think it is related with buttons container ("Panel" in my case, coz the same button perfectly work on every click in Saki's filetree example: http://filetree.extjs.eu/, where its placed in "Window".

loeppky
22 May 2008, 5:39 PM
SamuraiJack1: I don't think the issue is whether it's in a windor or a panel. My example in post #2 is in a panel and works fine. I think the issue has more to do with what type of layout is being employed. What layout are you using?

Either way, can you employ the fix I suggested to JorrisA in post #35?

Thanks,
loeppky

SamuraiJack1
23 May 2008, 11:53 AM
Well, the button was created this way:


var u = new Ext.Panel({
applyTo: 'ext_uploader',
title: 'uploader',
height: 100,
tbar: [{
xtype:'browsebutton'
,text:'Add...'
,iconCls:'icon-plus'
,scope:this
}]
});
As I remember, the same effect appears when I tried to add button directly to panel, like:



var u = new Ext.Panel({
applyTo: 'ext_uploader',
title: 'uploader',
height: 100,
items: [{
xtype:'browsebutton'
,text:'Add...'
,iconCls:'icon-plus'
,scope:this
}]
});
Also - FF was affected much often then IE (roughly 1/10 click works in FF vs 9/10 in IE)

Cant try that patch right now, (totally breaked my application with refactoring) - will try it for sure in couple of days - that standart browser buttons are too ugly to use..

jay@moduscreate.com
25 May 2008, 2:05 PM
This happens on OS X and Fx 2.x

http://tdg-i.com/img/screencasts/2008-05-25_1804.swf

JorisA
26 May 2008, 2:45 AM
SamuraiJack1: I don't think the issue is whether it's in a windor or a panel. My example in post #2 is in a panel and works fine. I think the issue has more to do with what type of layout is being employed. What layout are you using?

Either way, can you employ the fix I suggested to JorrisA in post #35?

Thanks,
loeppky

Hi loeppky. Sorry for the late response. Your suggested fix didn't work, I'll post a demo with the problem this afternoon.

JamesC
26 May 2008, 2:47 AM
Can you guys try with my browsebutton @ http://extjs.com/forum/showthread.php?t=28489
I've had no problems with it working more than once in firefox/ie6. If it does work maybe loeppky can integrate the fix into his.

loeppky
28 May 2008, 8:17 AM
This happens on OS X and Fx 2.x

http://tdg-i.com/img/screencasts/2008-05-25_1804.swf

jgarcia@tdg-i.com: your demo reveals exactly what's supposed to happen. I don't see what problem you're highlighting?

loeppky
28 May 2008, 8:18 AM
Hi loeppky. Sorry for the late response. Your suggested fix didn't work, I'll post a demo with the problem this afternoon.

JorisA: let me know once you get the demo up. That will help with debugging this. Thanks!

jay@moduscreate.com
28 May 2008, 8:20 AM
jgarcia@tdg-i.com: your demo reveals exactly what's supposed to happen. I don't see what problem you're highlighting?

i think i get it. ;) my apologies.

chalu
30 May 2008, 1:43 AM
Last night, I started creating an extension using this browsebutton utility and based on the screenshotpanel example usage. I am trying to create a screenshotuploadpanel extension that lets a user upload images (like passports) and when the upload finishes, a view of the just uploaded image file is displayed. I have however had some issues with it.

After the file is selected, a progress bar shows up but I get this firebug error :
XML tag name mismatch (expected img)
http://localhost/lib/extjs/ext_2_1/ext-all-debug.js
Line 6679

I don't see any XHR object in the console, I tried setting breakpoints to inspect what was happening and surprisingly the responseText member of the submit response object contained some HTML markup in my extension code.

I also need a way to set regex for the input file form field so that input files can be restricted (by configuration)to a set image file types.

chalu
30 May 2008, 10:53 AM
Any suggestions with my previous post yet ????
This is quite urgent.
Thanks yet again.

loeppky
30 May 2008, 11:16 AM
Charles: I don't have the bandwidth to fix application issues. The problem here isn't with the BrowseButton itself but how you're doing file uploads. There are loads of form posts about doing file uploads with Ext.

One thing to note is that Ext doesn't perform file uploads with XHR objects as this isn't possible. Instead it uses a hidden iframe. You can look through the Ext source code to understand it better. Anyways, you can look at the "Net" tab of Firebug to see the traffic or use the LiveHttpHeaders firefox extension to see what is sent to the server.

Good luck!
Steve

chalu
30 May 2008, 2:14 PM
You were quite right afterall, the url for the upload form was not properly set with my extensions url config parameter. I can now proceed with the extension, will let you know when I am done.

SamuraiJack1
5 Jun 2008, 11:07 AM
Well, turning back, - this time, I've added button on FormPanel and it works perfectly, with every click.
Think its a recommended way of usage.

loeppky
6 Jun 2008, 11:47 AM
SamuraiJack1: I really don't understand the first part of your post. Are you saying that everything is working for you?

SamuraiJack1
6 Jun 2008, 3:27 PM
Yes, for now, I'm adding BrowserButton on FormPanel (instead of on just Panel, like previous time). And it work with every click. Seems you was right and it is really container layout issue.

loeppky
6 Jun 2008, 3:51 PM
I fixed a few outstanding issues today including:

the leaking of <em> elements (post #16)
clicking browse button in IE not working sometimes (post #24)
and transparent mask can be very small (post #30)


The updated source code has been added to post #1. The sample in post #2 is using this updated version along with Ext 2.1 now.

Yoris
8 Jun 2008, 1:56 PM
can anybody please post an example of usage of this button? i can get it to render, but how in heaven do i get it to work with my already instantiated form with already created items under ext 2.1??????? please i really need this... i will continue to read some examples, but will wait for some help.......
thanx in advance..

loeppky
9 Jun 2008, 7:40 AM
Yoris: post #2 is a full example of how to use this. Have you looked at it yet?

ZooKeeper
16 Jun 2008, 4:40 AM
In the latest SVN there's a bug.

I'm using your example code:

if (!this.addPhotoForm) { // if the form hasn't been created
var addPhotoFormEl = this.body.createChild({
tag: 'form',
style: 'display:none'
});
this.addPhotoForm = new Ext.form.BasicForm(addPhotoFormEl, {
url: '/users/upload_photo/',
fileUpload: true
});
}
var inputFileEl = browseButton.detachInputFile();
inputFileEl.appendTo(this.addPhotoForm.getEl());
this.body.createChild({
tag: 'div',
html: inputFileEl.dom.value
});
this.addPhotoForm.submit({
success: function(){alert('sdsd')},
failure: function(){alert('sdsd')},
scope: this,
waitTitle: 'Uploading Screenshot',
waitMsg: 'Your screenshot is being uploaded...'
});

When inputFileEl.appendTo(this.addPhotoForm.getEl()); is called it becomes visible.
and i get


form.submit is not a function
form.submit();

It was not the case in the previous builds. I cann't spot the revision that introdused this bug. I'm not sure if it's your bug or Ext's.
Can you please investigate it?

Thanks for the great plugin!

loeppky
16 Jun 2008, 8:11 AM
ZooKeeper: it looks like something probably changed with Ext.form.BasicForm. I do not have SVN access, so I will not be able to look into this, unless you set up an example website.

smartlit
16 Jun 2008, 10:53 AM
Hello,

I still have problems in using it under IE... sometimes it "looses" a few click. This happen only when debug mode = false, because if debugmode=true every click is trapped. It also happens in your on-line sample. I'm testing it under IE6 and IE7.

Thanks,

Paul

Dumbledore
16 Jun 2008, 11:04 AM
This happen only when debug mode = false, because if debugmode=true every click is trapped. It also happens in your on-line sample. I'm testing it under IE6 and IE7.

i can confirm this. Same problems here...

loeppky
16 Jun 2008, 11:17 AM
Hmm, this is definitely weird. I can confirm this as well. I will hopefully be able to look into it further on Friday. Thanks guys!

juljupy
17 Jun 2008, 8:23 PM
hi i'm using your extention and it works fine but recently i upgrade my firefox from 2.0.0.14 to firefox 3 and i'm trying to test it and it doesn't works because in firefox 2.0.0.14 i used to upload a file with this route for example: c:\folder\another_one\file.cvs and it takes all the route but in firefox 3 it only takes the file.cvs not the rest of the route.

loeppky
18 Jun 2008, 8:55 AM
juljupy: my use of the BrowseButton works fine with Firefox 3. It probably has something to do with your code for detaching the inputFileEl and submitting a form.

loeppky
23 Jun 2008, 8:29 AM
I have fixed the issue where the BrowseButton "looses" clicks when debug mode = false. This has to do with IE dropping mouse events for transparent items. Post #1 has updated/corrected code, but basically in the initCompoenent method, instead of setting the clipEl to be transparent, I set the floatEl. The code is now:



if (this.debug) {
this.clipEl.applyStyles({
'background-color': 'green'
});
this.floatEl.applyStyles({
'background-color': 'red'
});
} else {
// We don't set the clipEl to be transparent, because IE 6/7 occassionaly looses mouse events for transparent elements.
// We have listeners on the clipEl that can't be lost as they're needed for realligning the input file element.
this.floatEl.setOpacity(0.0);
}



Hello,

I still have problems in using it under IE... sometimes it "looses" a few click. This happen only when debug mode = false, because if debugmode=true every click is trapped. It also happens in your on-line sample. I'm testing it under IE6 and IE7.

Thanks,

Paul

smartlit
23 Jun 2008, 12:50 PM
I have fixed the issue where the BrowseButton "looses" clicks when debug mode = false. This has to do with IE dropping mouse events for transparent items. Post #1 has updated/corrected code, but basically in the initCompoenent method, instead of setting the clipEl to be transparent, I set the floatEl. The code is now:



if (this.debug) {
this.clipEl.applyStyles({
'background-color': 'green'
});
this.floatEl.applyStyles({
'background-color': 'red'
});
} else {
// We don't set the clipEl to be transparent, because IE 6/7 occassionaly looses mouse events for transparent elements.
// We have listeners on the clipEl that can't be lost as they're needed for realligning the input file element.
this.floatEl.setOpacity(0.0);
}


Thanks, I'm checking it! :)

Paul

smartlit
23 Jun 2008, 12:55 PM
Thanks, I'm checking it! :)

Paul

One question: has your on-line demo already been updated?
Thanks,

Paul

loeppky
23 Jun 2008, 12:57 PM
Yes, the on-line demo has been updated and tested with IE 6/7. Maybe obvious, but make sure you refresh your browser's cache.


One question: has your on-line demo already been updated

smartlit
24 Jun 2008, 1:49 PM
Yes, the on-line demo has been updated and tested with IE 6/7. Maybe obvious, but make sure you refresh your browser's cache.

I have made a few tests on your demo, and sometimes it still "looses" some clicks. I've tested it under IE6. Can you confirm?
Thanks,

Paul

Note: I noted it looses clicks especially when I'm exactly on the borders, when the mouse is a hand

t34
24 Jun 2008, 11:24 PM
I noted it looses clicks especially when I'm exactly on the borders, when the mouse is a hand

Same situation here (also with IE6).
The fact is, that the clickable area does not move with the mouse pointer any more. It sits in the middle of the button all the time.
You can show this by double clicking beside that area.

Greetings t34

loeppky
25 Jun 2008, 7:59 AM
smartlit & t34: I'm aware of the clicking on the edges issue. That's what debug mode at http://loeppky.com/steven/code/samples/BrowseButton/index.html shows. The area that is green is where mouseover events are detected, which in turn causes the input file element to be moved. If someone doesn't click in the "green" area, the input file element will not be clicked. As Ext buttons have some more complicated layout than just being a button (they're actually a table with a button in the center), it's not possible to mask the whole area easily in a cross-browser supported way, hence the approximation. Hardly anyone clicks a button on the very edge, so this qualifies as "good enough". Does that make more sense now?

Please feel free to modify the code to make masking tighter. The code is well documented and pretty self explanatory. Remember though to test any changes with all button types/browsers as my example does.

schube
30 Jun 2008, 8:01 AM
Hello!

Thank you for your great plug-in!

I am able to upload a file and the webserver is answering with a JSON like


{
"result": {
"success": true
}
}
However, the "onError" event is always fired instead of the "onSuccess" event. What am I doing wrong?

PS: I also tried HTML, Plain Text and XML instead of a JSON result, but then I get other errors like when I am returning

<html><body>success</body></html>I get "success not defined".

Thank you,
Bernhard

loeppky
30 Jun 2008, 9:56 AM
schube: your issue with using Ext forms to do file uploads falls outside of the scope of this BrowseButton thread as it doesn't concern BrowseButton directly. It's been awhile since I've done file uploads with Ext, so I'm a little rusty. I'd search the forms though as there are lots of posts on the subject.

schube
30 Jun 2008, 10:34 AM
Hi!

Okay, thank you, I will search the forum in more details.

(I did search it, but it seems I was on the wrong track...)

Sorry for bothering you,
thank you,
Bernhard

anjelika
1 Jul 2008, 11:02 PM
Hello,
nice extension!
Can someone please provide a simple demo (just a regular FormPanel with a browsebutton attached which submits the uploaded file)?
I've got the post #2 working but it is not very practical for my needs, it just demonstrates the extension's role.
I need something like this:

Ext.onReady(function(){
var update = new Ext.FormPanel({
renderTo : Ext.getBody(),
fileUpload : true,
enctype : 'multipart/form-data',
frame : true,
url : 'submit.php',
items : [
new Ext.ux.BrowseButton({
text: "Browser",
tooltip: "Browse for new image",
scope: this,
handler: function() {
alert('it works');
}})
]
})
});
Thanks

galdaka
2 Jul 2008, 11:19 AM
I

loeppky
3 Jul 2008, 9:38 AM
The "ScreenshotPanel" in post #2, has all of this. There is a BrowseButton (multiple actually), a hidden form, and then a submit button to submit (although submit is broken because I don't have a backend implemented to handle the file upload). Take a look at it again. I realize something could be made that's more concise and clear, but I don't have the bandwidth to do this.



I've got the post #2 working but it is not very practical for my needs, it just demonstrates the extension's role.

loeppky
3 Jul 2008, 9:40 AM
galdaka: I haven't tried this myself, but I don't think it's possible. There's some definite "hacks" that go on to supprot the BrowseButton functionality, and I didn't create them to support creation from markup. You'd be welcome to try and implement it though. The source code is well structured and commented.

[QUOTE=galdaka;189879]I

Animal
5 Jul 2008, 2:31 AM
The standard Ext.Button class does not support applyTo. It renders itself from scratch in its onRender method which does not call the superclass onRender.

anjelika
7 Jul 2008, 7:01 AM
Hey,
I still can't make it work within a form :(
Here is the sample code :



Ext.onReady(function(){
Ext.QuickTips.init();
var update = new Ext.FormPanel({
renderTo : Ext.getBody(),
fileUpload : true,
enctype : 'multipart/form-data',
frame : true,
width : 500,
selectOnFocus : true,
msgTarget : 'side',
url : 'submit.php',
items : [
new Ext.ux.form.BrowseButton({
text: "Browser",
id : 'browsebtn',
tooltip: "Browse for new image",
handler: function() {
var inputFileEl = Ext.getCmp('browsebtn').detachInputFile();
inputFileEl.appendTo(addScreenshotForm.getEl());
filePanel.body.createChild({
tag: 'div',
html: inputFileEl.dom.value
});
addScreenshotForm.submit({
params: {
extraParam1: 'value1'
},
success: this.onAddScreenshotSuccess,
failure: this.onAddScreenshotFailure,
scope: this,
waitTitle: 'Uploading Screenshot',
waitMsg: 'Your screenshot is being uploaded...'
});
}
}),
filePanel = new Ext.Panel({
frame: true,
title: 'selected files'
})

]
});

var addScreenshotFormEl = filePanel.body.createChild({
tag: 'form',
style: 'display:none'
});
var addScreenshotForm = new Ext.form.BasicForm(addScreenshotFormEl, {
url: 'submit.php',
fileUpload: true
});

});
</script>
</body>
</html>


The error I get is :

addScreenshotForm is null or not an object
Anyone solved this?
Thanks

loeppky
7 Jul 2008, 9:39 AM
anjelika: I'm not sure what you're doing, but this is basic debugging. You're referencing addScreenshotForm before it's been defined.

anjelika
7 Jul 2008, 11:09 AM
Thanks, I got it working with the following code:

Ext.onReady(function(){
var update = new Ext.FormPanel({
renderTo : Ext.getBody(),
frame : true,
autoWidth : true,
url : 'submit.php',
items : [
browseButton = new Ext.ux.form.BrowseButton({
text: "Browser",
id : 'browsebtn',
tooltip: "Browse for new image",
handler: function() {
if (!this.addPhotoForm) { // if the form hasn't been created
var addPhotoFormEl = update.body.createChild({tag: 'form',style: 'display:none'
});
this.addPhotoForm = new Ext.form.BasicForm(addPhotoFormEl, {url: 'submit.php',fileUpload: true
});
}

this.addPhotoForm.submit({
success: function(){alert('ok')},
failure: function(){alert('error')},
scope: this,
waitTitle: 'Uploading Screenshot',
waitMsg: 'Your screenshot is being uploaded...'
});
}
}),
filePanel = new Ext.Panel({
frame: true,
title: 'selected files'
})

]
});



});

anjelika
8 Jul 2008, 12:19 PM
Still, the uploaded file content is not send to the server, I think I am missing assigning the input field into the submited form...not sure how to do that tho :(

loeppky
8 Jul 2008, 1:41 PM
anjelika: your post does not concern Ext.ux.form.BrowseButton. You must have your Ext form configured improperly. Search for form file upload within the Forums. There are all kinds of posts that cover this topic.

ZooKeeper
10 Jul 2008, 4:09 AM
Still, the uploaded file content is not send to the server, I think I am missing assigning the input field into the submited form...not sure how to do that tho :(

You forgot this part

var inputFileEl = browseButton.detachInputFile();
inputFileEl.appendTo(this.addPhotoForm.getEl());

before form submit

anjelika
10 Jul 2008, 4:24 AM
Thanks, I skipped that part.

ZooKeeper
10 Jul 2008, 5:32 AM
Another thing is the example will be broken if you're using Ext from SVN.

To fix it, replace


var addPhotoFormEl = this.body.createChild({
tag: 'form',
style: 'display:none'
});With this:


var addPhotoFormEl = this.getEl().createChild({
tag: 'form',
style: 'display:none'
});Not sure why it's not working, I think it's up to loeppky to figure.

lorezyra
10 Jul 2008, 12:06 PM
Is it possible to use this as a menu item??


I've tried to add it (explicitly) as a menu item, but it errors out with "invalid utilization..." It's nice to have it as a toolbar item... but I'm developing an "application" with a file->menu (upload) option...



any constructive advice is appreciated.

lorezyra
10 Jul 2008, 12:50 PM
I'm looking for this functionallity, but as a menu item.... is this something that has been developed already?

is it possible to extend Ext.menu to call this class? I see that DateItem and ColorItem do this... Not sure at the moment how this could be implemented...

jsakalos
10 Jul 2008, 2:19 PM
See http://extjs.com/deploy/dev/docs/?class=Ext.menu.Adapter if it helps.

loeppky
14 Jul 2008, 9:13 AM
lorezyra: was jsakalos suggestion helpful? I have never used Ext.ux.form.BrowseButton as a menu item, but it would be good to know if it's possible.

jay@moduscreate.com
14 Jul 2008, 4:20 PM
i see something just like this in the 2.2 branch. is it yours?

lorezyra
14 Jul 2008, 6:09 PM
lorezyra: was jsakalos suggestion helpful? I have never used Ext.ux.form.BrowseButton as a menu item, but it would be good to know if it's possible.


I'm sure it's possible... I just haven't devoted the time to developing it... As I'm currently working on my Image Editor (http://www.LoreZyra.com/%21Admin)...
B)

pbartels
15 Jul 2008, 4:33 AM
If you place this code:


buttonEl.on({
'mousemove': this.onButtonMouseMove,
'mouseover': this.onButtonMouseMove,
scope: this
});

AFTER:

buttonEl.on('focus', this.onButtonFocus, this);

The click loose wil be fixed in IE6. :D

loeppky
15 Jul 2008, 9:22 AM
I don't have SVN access, so I don't know. I wasn't notified of it being incorporated into the Ext core, but it was on the roadmap. Game on either way.


i see something just like this in the 2.2 branch. is it yours?

loeppky
15 Jul 2008, 9:23 AM
pbartels: thanks for the suggested fix. I'll test it out when I get some time and then update the code assuming it doesn't have any adverse effects for any browsers.

jay@moduscreate.com
16 Jul 2008, 6:53 AM
Please check your PMs ;)

loeppky
16 Jul 2008, 8:42 AM
The Ext BrowseField looks great. I don't have time to play with it yet, but it's employing the same type concepts. Good stuff!


Please check your PMs ;)

JorisA
16 Jul 2008, 11:04 PM
Uhm, am I missing something? I can't seem to find the 2.2 branch on svn. And I can't find the browsefield in 2.0 or trunk.

ktilt
22 Jul 2008, 9:39 AM
has anyone got this to work in Opera? on the first post i saw this

Opera - (Mac/Windows/Linux)
Only works with explicitly instantiated buttons for a panel. Doesn't work in the tbar or items.i have created a panel and added a Ext.ux.form.BrowseButton via the panel's 'buttons' config.. but clicking the button does nothing. just curious if im wasting my time or if someone else has seen this work. :)

thanks

ZooKeeper
11 Aug 2008, 3:41 AM
Can you please post here your opinion on the native Ext implementation as soon as you'll take a look at it.
I haven't decided to make a switch and waiting for your personal view.
Example, performing the same use case would be highly appreciated

loeppky
11 Aug 2008, 8:49 AM
ZooKeeper: I'll be sure to let you know what I find, but it will be awhile, as I don't have any bandwidth at the moment to convert my existing apps that use Ext.ux.from.BrowseButton to the Ext 2.2 release version. It may be a month or so until I get to th is. The Ext button is the way of the future so I would work on using it :)

archmisha
22 Oct 2008, 2:31 PM
Hi,
I am trying to use this great BrowseButton.

When i use
var inputFileEl = browseButton.detachInputFile();
console.info(inputFileEl.dom.value);
as in the example, i am getting only the filename (as in the example the filenames are shown in the grid).

I wonder how i can get the whole path to the file.
Because i want to display a preview of it in my application. (no uploading stuff)

Thanks.

archmisha
24 Oct 2008, 1:52 PM
bump

loeppky
27 Oct 2008, 9:12 AM
archmisha: I do not know offhand how to get the full path or if that is even possible. That is certainly outside the scope of this extension. It has much more to do with what the DOM exposes. It would be great to learn what you find, so please report back your findings.

archmisha
27 Oct 2008, 9:51 AM
Well the reason i asked this is because when i tried to play with the component, i saw input tag containing the whole path hidden somewhere in the dom.

I didnt pursue this issue farther, because i was trying to build app fastest i could, so i just uploaded the image straight to the server, kept it there in a cache. and later when the user saved the whole form i attached the uploaded item before to the object the form submitted.

(when uploading the image i return id so i can later retrieve it from the cache when saving the whole object)

emredagli
13 Nov 2008, 1:16 AM
Hii,
I try to use browsebutton with AS.NET WebService like that:


....
items:[{
id: 'productImageBrowse',
fieldLabel: 'Product Image',
xtype: 'browsebutton',
text: 'Browse',
handler: productImageFileSelected
}]
....
function productImageFileSelected(btn) {
var input = btn.detachInputFile();
var form = Ext.getBody().createChild({
tag:'form'
,action:this.url
,method:'post'
,cls:'x-hidden'
,id:Ext.id()
,cn:[{
tag:'input'
,type:'hidden'
,name:'productId'
,value:productID
}]
});

form.appendChild(input);

Ext.Ajax.request({
url: WSPath + 'Products.asmx/UploadProductPicture'
,method:'post'
,isUpload:true
,scope:this
,callback:function(options,success,response) {
...
}
,form: form
});
}
In .NET part I write Web Service to take uploaded image like that:


[WebMethod(true)]
public XmlDocument UploadProductPicture()
{
HttpContext postedContext = HttpContext.Current;
HttpFileCollection Files = postedContext.Request.Files;

if (Files.Count > 0)
{
...
}
}
But Files.Count is 0....
I am pretty new on file operations.
I couldn't find the source of the problem.
What should I do?
Thanks for your advance...

I found the problem.
I see in firebug Net tab <length required> in response part.
But It works fine...
It seems that we should not trust firebug...

pflammer
15 Nov 2008, 2:50 PM
Loeppky,Thanks for making this. It's come in handy for me.
In internet explorer, the button doesn't follow the cursor sometimes. It looks like this has been a constant issue. In the first post, it says it was fixed in June but I noticed a lot of people noting the behavior was still there. I noticed this also. I took some time, and have a fix.It amounts to better centering the floating element so if it doesn't track, you still click where you should.
You just need to change two things:

Around line 162 replace top:-3px with top:0px (it just lowers the button a bit):
------------------------------------
if (Ext.isIE) {
Ext.apply(styleCfg, {
left: '-3px',
- top: '-3px'
+ top: '0px'
});
------------------------------------

Comment out the line around line 189 where it says position:absolute:
------------------------------------
this.floatEl = this.clipEl.createChild({
tag: 'div',
style: {
- position: 'absolute',
+ //position: 'absolute',
width: this.FLOAT_EL_WIDTH + 'px',
height: this.FLOAT_EL_HEIGHT + 'px',
------------------------------------

Hopefully this helps some people.

keckeroo
22 Nov 2008, 5:10 PM
I'll try this out - I too have some users who say that the upload doesn't do anything at times .. and I suspect that this may be the trick ... I managed to do it - but only once - which obviously makes it a pain to track down.

Thanks!

Kev

skaue
24 Nov 2008, 3:49 AM
btw...

I tried to add this to a form with anchored layout and this made the entire form blink for half a second, then disappear. I had to add "anchor:null" in the declaration of the browsebutton to prevent the button to use anchored layout and "kill" the form.

skaue
24 Nov 2008, 4:11 AM
Ok... Now I'm confused!! :-?

How does this even work? Is the buttons handler supposed to create a new form and submit this separately?? I was under the impression I could use this browsebutton to submit a file along with the other form fields of my form... what I'm I not getting here?
I see anjelika figured this out, but reading through the working code doesn't make sense to me. Do I have to submit the file AND the rest of the form separately?

:((

mystix
24 Nov 2008, 8:26 AM
Ok... Now I'm confused!! :-?

How does this even work? Is the buttons handler supposed to create a new form and submit this separately?? I was under the impression I could use this browsebutton to submit a file along with the other form fields of my form... what I'm I not getting here?
I see anjelika figured this out, but reading through the working code doesn't make sense to me. Do I have to submit the file AND the rest of the form separately?

:((

the ux code does nothing but create a skinnable Ext.Button which, when clicked, will bring up the standard browser file-selection dialogue.

the example code included with the ux demonstrates how to use it in a plain old html form (IIRC) to submit a selected file.

loeppky
24 Nov 2008, 9:21 AM
pflammer: have you tried your fix out on IE6 and IE7?

pflammer
25 Nov 2008, 7:32 AM
pflammer: have you tried your fix out on IE6 and IE7?

I have been using it a fair amount since I made the fix described above on IE6 and IE7 and it seems to have fixed the issue, at least in my implementation. I am using it in the uploadPanel in Saki's filetree. I also have used it on FF2/3 and it still works there as well.

Thanks,
pflammer

rainydays
29 Nov 2008, 7:08 PM
I have been trying to get this to work for countless hours now, and I can't for my life figure out what I'm doing wrong. I've tried the different methods described here, and I've tried the way Ext.ux.UploadDialog works.

The button works just fine, I can select a file and the handler gets called. But the file upload fails.
In fact, the post is completely empty. It doesn't even post the additional parameters.

I'm sorry if I shouldn't be posting this here, but I thought it was closely related to the browsebutton.

Here's the last code I've tried. Fails just like the other versions I've tried.


tbar: [{
xtype: 'browsebutton',
text: 'New file',
cls: 'x-btn-text-icon blist',
iconCls: 'icon-add',
inputFileName: 'uploadfile',
handler: function(browseButton) {
console.log('uploading file');
if (!this.uploadForm) { // if the form hasn't been created
var uploadFormEl = Ext.getBody().createChild({
tag: 'form',
style: 'display:none'
});
this.uploadForm = new Ext.form.BasicForm(uploadFormEl, {
url: 'pages.php',
fileUpload: true
});
}
var inputFileEl = browseButton.detachInputFile();
inputFileEl.appendTo(this.uploadForm.getEl());
this.uploadForm.submit({
scope: this,
params: {
extraParam1: 'value1'
},
waitTitle: 'Uploading file',
waitMsg: 'Your file is being uploaded...'
});
},
scope: this
}]

d4rk knight
3 Dec 2008, 5:05 PM
Hi everyone,

I'm new at ExtJs and I have a problem. I need to select an image from the client local file
system and show it in a BoxComponent. I've successfuly change the image in a BoxComponent, but only with the url of them, not with local images.. with this code:

var image = new Ext.BoxComponent({
anchor: '',
autoEl: {
tag: 'img',
style: (..some style..),
qtip: '',
src: 'images/image1.png',
id:'Pic'
}
});

and then in a button handler:

image.getEl().dom.src = 'images/image2.png';

Now I'm trying to use a Ext.ux.form.BrowseButton to select the local image, but when
I have the result browsebutton.getInputFile(); I don't know how to show that image in the boxcomponent.

Any ideas....

Thanks

jsakalos
3 Dec 2008, 8:31 PM
This is not possible with javascript. Javascript has no access to local files due to security reasons.

d4rk knight
4 Dec 2008, 6:39 AM
Thanks for the answer jsakalos

obbakilla
6 Dec 2008, 12:37 PM
I have been trying to get this to work for countless hours now, and I can't for my life figure out what I'm doing wrong. I've tried the different methods described here, and I've tried the way Ext.ux.UploadDialog works.

The button works just fine, I can select a file and the handler gets called. But the file upload fails.
In fact, the post is completely empty. It doesn't even post the additional parameters.


Did you find a solution? I have the same issue, and it is really hard to debug, as Firebug does not show any POST (not even in the network-tab). Even though it might not be so closely related to the browsebutton, maybe anyone can help out here ? Thanks!

MaximGB
7 Dec 2008, 5:16 AM
Uploads should be debugged using Fiddler or other local proxy software.

loeppky
8 Dec 2008, 11:40 AM
Also, as file uploads are done using a hidden IFRAME, the Network tab of Firebug can usually shed some light as to what's occurring.

ClemsonJeeper
11 Dec 2008, 9:15 PM
I am having the same issue as rainyday.

When I go to submit the form created from the new Ext.form.BasicForm() it submits and POSTs, but I do not see any data coming in from the POST tab under Network in Firebug.

This is how I'm creating:



,{
xtype: 'xbrowsebutton',
id: 'attachButton',
text: 'Browse...',
handler: function (btn) {
if (!btn.attachForm) {
var attachFormEl = Ext.getBody().createChild({
tag: 'form',
style: 'display: none'
});
btn.attachForm = new Ext.form.BasicForm(attachFormEl, {
url: '/upload.php',
fileUpload: true
});
}
var inputFieldEl = btn.detachInputFile();
inputFieldEl.appendTo(btn.attachForm.getEl());
Ext.getCmp('attachPanel').body.createChild({
tag: 'div',
html: inputFieldEl.dom.value
});
}
},{
xtype: 'panel',
title: 'Attached Files',
style: 'margin-top: 20px',
id: 'attachPanel'
}


When I submit:

Ext.getCmp('attachButton').attachForm.submit();

It shoots an error to the Console:

Permission denied to get property XULElement.accessibleType
[Break on this error] form.submit();

But still POSTs... But it doesn't post any files that are uploaded. What am I missing here?

I've stepped through everything and it looks like its building the hidden form just fine.

rainydays
18 Dec 2008, 8:29 AM
Did you find a solution? I have the same issue, and it is really hard to debug, as Firebug does not show any POST (not even in the network-tab). Even though it might not be so closely related to the browsebutton, maybe anyone can help out here ? Thanks!


Yeah, this handler seems to work for me. You can debug the response by outputting the response.responseText from the form success callback.



fileSelected:function(browseButton) {
this.addFileForm = Ext.getBody().createChild({
tag: 'form',
cls: 'x-hidden'
});

var input_file = browseButton.detachInputFile();

input_file.appendTo(this.addFileForm);

Ext.Ajax.request({url:'upload.php',
form: this.addFileForm,
isUpload: true,
headers: {'Content-type':'multipart/form-data'},
params: {additional params goes here},
success: this.uploadSuccess.createDelegate(this),
failure: this.uploadFail.createDelegate(this)
});
}

cdeguzman
4 Feb 2009, 4:07 AM
Hi, I'm trying to use this component with Java / DWR. Here is the code to my handler class:



handleBrowseButton : function(browsebutton) {
this.addFileForm = Ext.getBody().createChild({
tag : 'form',
cls : 'x-hidden'
});

var file = browsebutton.detachInputFile(false);
file.appendTo(this.addFileForm);

miscAction.extractDataFromFile(file, 1);
}


My question is, what will be the corresponding data type in Java for the "file" variable? Is there any way to directly transform this into something compatible with java.io.File?

Thanks a lot,

jsakalos
4 Feb 2009, 4:19 AM
I guess it would be better to as on a Java forum. This is client side library and not all users use Java as server language.

cdeguzman
4 Feb 2009, 4:49 AM
Thanks a lot. I guess I should've phrased my question better. What I meant was, what is the correct usage of the 'file' variable? Does it represent the actual file itself, or does it just represent the filename?

If it represents the actual file, how can I transform it into an object that is usable in server-side?

If it is just the filename, how do I recreate the file in server-side, when the file itself is in client-side and is never directly sent to server-side? (I would guess though that if this is the case, it would be handled by Java and not Extjs).

Thanks a lot,

Animal
4 Feb 2009, 5:50 AM
You cannot use DWR. Files can only by uploaded through regular HTTP form submission.

You will have to code a servlet to process a multipart MIME message.

cdeguzman
4 Feb 2009, 6:09 AM
Aah, got it. Thanks, Animal :)

archmisha
7 Feb 2009, 8:33 AM
Can i limit the browse window to allow select only certain mime types?
For example gif and jpg only?
Currently it stand on all files.

thanks

jbird526
27 Feb 2009, 11:10 AM
I am using Sakis upload panel and as some report there have been issues with the browse button in IE. Alot of issues have been resolved. I found that when you turn on debug the button the default browse that is displayed is very small due to the font size. Ext sets the fonts to 11px. In order to make fonts resizable in my Ext apps I created IE conditional sheets.


.x-tree-node,.ext-el-mask-msg div,.x-tabs-strip .x-tabs-text,.x-form-field,.x-form-grow-sizer,.x-form-item,
.x-form-invalid-msg,.x-form fieldset legend,.x-small-editor .x-form-field,.ext-safari .x-small-editor .x-form-field,
/*.x-btn,.x-btn button,*/.x-toolbar td,.x-toolbar span,.x-toolbar input,.x-toolbar div,.x-toolbar select,.x-toolbar label,
.x-grid-hd-row td, .x-grid-row td,.x-grid-topbar, .x-grid-bottombar,.x-layout-panel-hd-text,.x-dlg .x-dlg-hd,#x-msg-box .x-dlg-bd,
#x-msg-box .ext-mb-textarea,.x-dd-drag-ghost,.x-tip .x-tip-bd,.x-tip h3,.x-date-middle,.x-date-left,.x-date-right,
.x-date-inner th,.x-date-inner a,.x-menu-list-item,.x-combo-list-hd,.x-combo-list-item {
/*font:normal small verdana, arial, helvetica, sans-serif;*/
font-size: x-small;
font-style: normal;
}

By doing this it also increased the size of the browser button that is hidden under the Ext button div. I was then able to disable the mouse move because it was no longer necessary. This is the BrowseButton file that I am using.


Ext.namespace('Ext.ux.form');

/**
* @class Ext.ux.form.BrowseButton
* @extends Ext.Button
* Ext.Button that provides a customizable file browse button.
* Clicking this button, pops up a file dialog box for a user to select the file to upload.
* This is accomplished by having a transparent <input type="file"> box above the Ext.Button.
* When a user thinks he or she is clicking the Ext.Button, they're actually clicking the hidden input "Browse..." box.
* Note: this class can be instantiated explicitly or with xtypes anywhere a regular Ext.Button can be except in 2 scenarios:
* - Panel.addButton method both as an instantiated object or as an xtype config object.
* - Panel.buttons config object as an xtype config object.
* These scenarios fail because Ext explicitly creates an Ext.Button in these cases.
* Browser compatibility:
* Internet Explorer 6:
* - no issues
* Internet Explorer 7:
* - no issues
* Firefox 2 - Windows:
* - pointer cursor doesn't display when hovering over the button.
* Safari 3 - Windows:
* - no issues.
* @author loeppky - based on the work done by MaximGB in Ext.ux.UploadDialog (http://extjs.com/forum/showthread.php?t=21558)
* The follow the curosr float div idea also came from MaximGB.
* @see http://extjs.com/forum/showthread.php?t=29032
* @constructor
* Create a new BrowseButton.
* @param {Object} config Configuration options
*/
Ext.ux.form.BrowseButton = Ext.extend(Ext.Button, {
/*
* Config options:
*/
/**
* @cfg {String} inputFileName
* Name to use for the hidden input file DOM element. Deaults to "file".
*/
inputFileName: 'file',
/**
* @cfg {Boolean} debug
* Toggle for turning on debug mode.
* Debug mode doesn't make clipEl transparent so that one can see how effectively it covers the Ext.Button.
* In addition, clipEl is given a green background and floatEl a red background to see how well they are positioned.
*/
debug: false,


/*
* Private constants:
*/
/**
* @property FLOAT_EL_WIDTH
* @type Number
* The width (in pixels) of floatEl.
* It should be less than the width of the IE "Browse" button's width (65 pixels), since IE doesn't let you resize it.
* We define this width so we can quickly center floatEl at the mouse cursor without having to make any function calls.
* @private
*/
FLOAT_EL_WIDTH: 65,

/**
* @property FLOAT_EL_HEIGHT
* @type Number
* The heigh (in pixels) of floatEl.
* It should be less than the height of the "Browse" button's height.
* We define this height so we can quickly center floatEl at the mouse cursor without having to make any function calls.
* @private
*/
FLOAT_EL_HEIGHT: 18,


/*
* Private properties:
*/
/**
* @property buttonCt
* @type Ext.Element
* Element that contains the actual Button DOM element.
* We store a reference to it, so we can easily grab its size for sizing the clipEl.
* @private
*/
buttonCt: null,
/**
* @property clipEl
* @type Ext.Element
* Element that contains the floatEl.
* This element is positioned to fill the area of Ext.Button and has overflow turned off.
* This keeps floadEl tight to the Ext.Button, and prevents it from masking surrounding elements.
* @private
*/
clipEl: null,
/**
* @property floatEl
* @type Ext.Element
* Element that contains the inputFileEl.
* This element is size to be less than or equal to the size of the input file "Browse" button.
* It is then positioned wherever the user moves the cursor, so that their click always clicks the input file "Browse" button.
* Overflow is turned off to preven inputFileEl from masking surrounding elements.
* @private
*/
floatEl: null,
/**
* @property inputFileEl
* @type Ext.Element
* Element for the hiden file input.
* @private
*/
inputFileEl: null,
/**
* @property originalHandler
* @type Function
* The handler originally defined for the Ext.Button during construction using the "handler" config option.
* We need to null out the "handler" property so that it is only called when a file is selected.
* @private
*/
originalHandler: null,
/**
* @property originalScope
* @type Object
* The scope originally defined for the Ext.Button during construction using the "scope" config option.
* While the "scope" property doesn't need to be nulled, to be consistent with originalHandler, we do.
* @private
*/
originalScope: null,


/*
* Protected Ext.Button overrides
*/
/**
* @see Ext.Button.initComponent
*/
initComponent: function(){
Ext.ux.form.BrowseButton.superclass.initComponent.call(this);
// Store references to the original handler and scope before nulling them.
// This is done so that this class can control when the handler is called.
// There are some cases where the hidden file input browse button doesn't completely cover the Ext.Button.
// The handler shouldn't be called in these cases. It should only be called if a new file is selected on the file system.
this.originalHandler = this.handler;
this.originalScope = this.scope;
this.handler = null;
this.scope = null;
},

/**
* @see Ext.Button.onRender
*/
onRender: function(ct, position){
Ext.ux.form.BrowseButton.superclass.onRender.call(this, ct, position); // render the Ext.Button
this.buttonCt = this.el.child('.x-btn-center em');
this.buttonCt.position('relative'); // this is important!
var styleCfg = {
position: 'absolute',
overflow: 'hidden',
top: '0px', // default
left: '0px' // default
};
// browser specifics for better overlay tightness
if (Ext.isIE) {
Ext.apply(styleCfg, {
left: '-3px',
top: '+2px'
});
} else if (Ext.isGecko) {
Ext.apply(styleCfg, {
left: '-4px',
top: '-3px'
});
} else if (Ext.isSafari) {
Ext.apply(styleCfg, {
left: '-4px',
top: '-2px'
});
}
this.clipEl = this.buttonCt.createChild({
tag: 'div',
style: styleCfg
});
this.setClipSize();
this.clipEl.on({
//'mousemove': this.onButtonMouseMove,
//'mouseover': this.onButtonMouseMove,
scope: this
});

this.floatEl = this.clipEl.createChild({
tag: 'div',
style: {
//position: 'absolute',
width: this.FLOAT_EL_WIDTH + 'px',
height: this.FLOAT_EL_HEIGHT + 'px',
overflow: 'hidden'
}
});


if (this.debug) {
this.clipEl.applyStyles({
'background-color': 'green'
});
this.floatEl.applyStyles({
'background-color': 'red'
});
} else {
// We don't set the clipEl to be transparent, because IE 6/7 occassionaly looses mouse events for transparent elements.
// We have listeners on the clipEl that can't be lost as they're needed for realligning the input file element.
this.floatEl.setOpacity(0.0);
}

// Cover cases where someone tabs to the button:
// Listen to focus of the button so we can translate the focus to the input file el.
var buttonEl = this.el.child(this.buttonSelector);
buttonEl.on('focus', this.onButtonFocus, this);
// In IE, it's possible to tab to the text portion of the input file el.
// We want to listen to keyevents so that if a space is pressed, we "click" the input file el.
if (Ext.isIE) {
this.el.on('keydown', this.onButtonKeyDown, this);
}

this.createInputFile();
},


/*
* Private helper methods:
*/
/**
* Sets the size of clipEl so that is covering as much of the button as possible.
* @private
*/
setClipSize: function(){
if (this.clipEl) {
var width = this.buttonCt.getWidth();
var height = this.buttonCt.getHeight();
// The button container can have a width and height of zero when it's rendered in a hidden panel.
// This is most noticable when using a card layout, as the items are all rendered but hidden,
// (unless deferredRender is set to true).
// In this case, the clip size can't be determined, so we attempt to set it later.
// This check repeats until the button container has a size.
if (width === 0 || height === 0) {
this.setClipSize.defer(100, this);
} else {
if (Ext.isIE) {
width = width + 5;
height = height + 5;
} else if (Ext.isGecko) {
width = width + 6;
height = height + 6;
} else if (Ext.isSafari) {
width = width + 6;
height = height + 6;
}
this.clipEl.setSize(width, height);
}
}
},

/**
* Creates the input file element and adds it to inputFileCt.
* The created input file elementis sized, positioned, and styled appropriately.
* Event handlers for the element are set up, and a tooltip is applied if defined in the original config.
* @private
*/
createInputFile: function(){
// When an input file gets detached and set as the child of a different DOM element,
// straggling <em> elements get left behind.
// I don't know why this happens but we delete any <em> elements we can find under the floatEl to prevent a memory leak.
this.floatEl.select('em').each(function(el){
el.remove();
});
this.inputFileEl = this.floatEl.createChild({
tag: 'input',
type: 'file',
size: 1, // must be > 0. It's value doesn't really matter due to our masking div (inputFileCt).
name: this.inputFileName || Ext.id(this.el),
tabindex: this.tabIndex,
// Use the same pointer as an Ext.Button would use. This doesn't work in Firefox.
// This positioning right-aligns the input file to ensure that the "Browse" button is visible.
style: {
position: 'absolute',
cursor: 'pointer',
right: '0px',
top: '0px'
}
});
this.inputFileEl = this.inputFileEl.child('input') || this.inputFileEl;

// setup events
this.inputFileEl.on({
'click': this.onInputFileClick,
'change': this.onInputFileChange,
'focus': this.onInputFileFocus,
'select': this.onInputFileFocus,
'blur': this.onInputFileBlur,
scope: this
});

// add a tooltip
if (this.tooltip) {
if (typeof this.tooltip == 'object') {
Ext.QuickTips.register(Ext.apply({
target: this.inputFileEl
}, this.tooltip));
} else {
this.inputFileEl.dom[this.tooltipType] = this.tooltip;
}
}
},

/**
* Redirecting focus to the input file element so the user can press space and select files.
* @param {Event} e focus event.
* @private
*/
onButtonFocus: function(e){
if (this.inputFileEl) {
this.inputFileEl.focus();
e.stopEvent();
}
},

/**
* Handler for the IE case where once can tab to the text box of an input file el.
* If the key is a space, we simply "click" the inputFileEl.
* @param {Event} e key event.
* @private
*/
onButtonKeyDown: function(e){
if (this.inputFileEl && e.getKey() == Ext.EventObject.SPACE) {
this.inputFileEl.dom.click();
e.stopEvent();
}
},

/**
* Handler when the cursor moves over the clipEl.
* The floatEl gets centered to the cursor location.
* @param {Event} e mouse event.
* @private
*/
onButtonMouseMove: function(e){
var xy = e.getXY();
xy[0] -= this.FLOAT_EL_WIDTH / 2;
xy[1] -= this.FLOAT_EL_HEIGHT / 2;
this.floatEl.setXY(xy);
},

/**
* Add the visual enhancement to the button when the input file recieves focus.
* This is the tip for the user that now he/she can press space to select the file.
* @private
*/
onInputFileFocus: function(e){
if (!this.isDisabled) {
this.el.addClass("x-btn-over");
}
},

/**
* Removes the visual enhancement from the button.
* @private
*/
onInputFileBlur: function(e){
this.el.removeClass("x-btn-over");
},

/**
* Handler when inputFileEl's "Browse..." button is clicked.
* @param {Event} e click event.
* @private
*/
onInputFileClick: function(e){
e.stopPropagation();
},

/**
* Handler when inputFileEl changes value (i.e. a new file is selected).
* @private
*/
onInputFileChange: function(){
if (this.originalHandler) {
this.originalHandler.call(this.originalScope, this);
}
},


/*
* Public methods:
*/
/**
* Detaches the input file associated with this BrowseButton so that it can be used for other purposed (e.g. uplaoding).
* The returned input file has all listeners and tooltips applied to it by this class removed.
* @param {Boolean} whether to create a new input file element for this BrowseButton after detaching.
* True will prevent creation. Defaults to false.
* @return {Ext.Element} the detached input file element.
*/
detachInputFile: function(noCreate){
var result = this.inputFileEl;

if (typeof this.tooltip == 'object') {
Ext.QuickTips.unregister(this.inputFileEl);
} else {
this.inputFileEl.dom[this.tooltipType] = null;
}
this.inputFileEl.removeAllListeners();
this.inputFileEl = null;

if (!noCreate) {
this.createInputFile();
}
return result;
},

/**
* @return {Ext.Element} the input file element attached to this BrowseButton.
*/
getInputFile: function(){
return this.inputFileEl;
},

/**
* @see Ext.Button.disable
*/
disable: function(){
Ext.ux.form.BrowseButton.superclass.disable.call(this);
this.inputFileEl.dom.disabled = true;
},

/**
* @see Ext.Button.enable
*/
enable: function(){
Ext.ux.form.BrowseButton.superclass.enable.call(this);
this.inputFileEl.dom.disabled = false;
}
});

Ext.reg('browsebutton', Ext.ux.form.BrowseButton);

pampy
12 Mar 2009, 3:52 AM
You cannot use DWR. Files can only by uploaded through regular HTTP form submission.

You will have to code a servlet to process a multipart MIME message.


DWR 3 will support file uploads, by the way.
It's already working with RC1.

At the moment I'm trying to get it to work with ux.form.BrowseButton but I still got some irregular errors in FF3. But it works fine on IE.

Take a look at that post, I just found:
http://extjs.com/forum/showthread.php?t=28489

alexb
25 Mar 2009, 9:26 PM
Hi loeppky,

Are you still interested in improving this extension?

Can you consider using floating input technique http://www.michaelmitchell.co.nz/one-click-upload/ to avoid IE issues with file dialog not shown? Is it better or worse?

GraemeBryce
27 Apr 2009, 3:42 AM
It appears that the demo page at http://loeppky.com/steven/code/samples/BrowseButton/ does not operate correctly in IE8 - has anyone else seen this and are there any comments before I dig into why?

It does work in IE8 with compaibility mode on (so IE7 really)
I wonder if this is a browser detection issue and perhaps I am missing something in EXT in general rather than specifically with the BrowseButton?

loeppky
27 Apr 2009, 10:57 AM
Hello,

Is there any reason to keep supporting this extension given that Ext has provided this support. See http://extjs.com/deploy/dev/examples/form/file-upload.html. If there is, please let me know, but my hunch is that there isn't any reason to.

Steve

mschwartz
27 Apr 2009, 11:22 AM
Both your extension and the one in the examples have an issue if you put the button in a form panel in a region of a border layout that is not super wide.

For example, border layout, form panel in the west with width of 150 or so. The button renders all the way to the left, regardless of frame: true or style: 'padding: 5px;' on the form. And it renders inside a div that has a horizontal scroll bar. The scrollbar is underneath the button and only scrolls the button to the left - the rest of the form is fine.

GraemeBryce
28 Apr 2009, 12:38 AM
I agree that support of this is no longer required as EXT have added a similar control.

I am however confused as to the way they have added it by placing the main JS cxode in the example rather than as a form widget.

It does not therefore appear in the main product documentation nor is it apparent by browsing SVN or the source folder.

Why is this?

My thanks to you and to the others that worked on the various parts of the solution(s) now available as they have enhanced both understanding and user experiences.

emredagli
29 Apr 2009, 3:29 AM
Hii,
I have a hidden browsebutton object.
But I need to call its on-click function to select file.
Is there a way to do this?

Thanks alot.

emredagli
30 Apr 2009, 12:13 PM
After I spend hours, I realize that firefox doesn't allow externally clicking (by creating mouse events) "input files" button objects.
But IE allows it.

bokgo2000
4 May 2009, 6:53 PM
Hi.
i have the same problem as graemebryce`s when i visit the browse button sample page.
Browse button is visible when debug is false.
browse button sample: http://loeppky.com/steven/code/samples/BrowseButton/

My question is that browse button in saki`s uploadpanel is working correctly with ie8.
saki`s filetree: http://filetree.extjs.eu/

What is the difference?

Thanks...

emredagli
7 May 2009, 9:57 PM
Have any body try browsebutton in Ext 3.0?
I have test it, but it doesn't work:
The error in "onRender" function:


this.buttonCt.position('relative'); // this is important!


buttonCt is null...

dario
10 May 2009, 8:57 AM
Have any body try browsebutton in Ext 3.0?
I have test it, but it doesn't work:
The error in "onRender" function:


this.buttonCt.position('relative'); // this is important!


buttonCt is null...

I had the same problem, in "onRender" function I replaced


this.buttonCt = this.el.child('.x-btn-center em');

with


this.buttonCt = this.el.child('.x-btn-mc em');

and it seems to work fine now.

Zord
13 May 2009, 5:03 AM
Does anybody could make BrowseButton work in Opera as toolbar button?

emredagli
14 May 2009, 2:14 AM
Hi,
Is this the way of getting selected file name?

var inputFileEl = browseButton.detachInputFile();
console.info(inputFileEl.dom.value);

This code doesn't work in my localhost (firefox).

By the way thanks Dario...

4him
8 Jul 2009, 5:00 AM
As bokgo2000 mentioned: there is a problem with IE 8. The transparency on the upload button doesn't work (http://loeppky.com/steven/code/samples/BrowseButton/). But how come Saki's filetree's browse button (http://filetree.extjs.eu/) works allright ??

Anybody, any clue ?

4him
8 Jul 2009, 7:02 AM
To make it work under IE 8, I had to apply the opacity directly on the "file input" element, otherwise it would not work. My guess is that IE 8 doesn't propagate the opacity to children elements anymore...

4him
9 Jul 2009, 1:31 AM
Under IE8 the height never gets set (or gets set to 0) and therefore the following test in setClipSize always fails:
if (width === 0 || height === 0)To make it work, I used an ugly hack:
if (width === 0 || (height === 0 && !Ext.isIE8)) and then I hardcoded the height of the button.

Below is the BrowseButton code as patched for IE8 and ExtJs 2 and 3. I have tested it under ExtJs 3.0.0, ExtJs 2.1 and with IE6, IE7, IE8, Firefox 3.0.11 and Firefox 3.5. As I've not tested them, I've left the Opera and Safari comments 'as is', so it should still work with ExtJs 2.x but there might be some problems with ExtJs 3 (I'm thinking about the 'overlay tightness' thing).


Ext.namespace('Ext.ux.form');

// @author 4Him
if(typeof Ext.isIE8 != 'boolean') { // Ext 2.x (at least 2.1) doesn't know IE8. Let's tell him about it...
Ext.isIE8 = Ext.isIE && navigator.userAgent.toLowerCase().indexOf("msie 8")>-1;
}

/**
* @class Ext.ux.form.BrowseButton
* @extends Ext.Button
* Ext.Button that provides a customizable file browse button.
* Clicking this button, pops up a file dialog box for a user to select the file to upload.
* This is accomplished by having a transparent <input type="file"> box above the Ext.Button.
* When a user thinks he or she is clicking the Ext.Button, they're actually clicking the hidden input "Browse..." box.
* Note: this class can be instantiated explicitly or with xtypes anywhere a regular Ext.Button can be except in 2 scenarios:
* - Panel.addButton method both as an instantiated object or as an xtype config object.
* - Panel.buttons config object as an xtype config object.
* These scenarios fail because Ext explicitly creates an Ext.Button in these cases.
* Browser compatibility:
* Internet Explorer 6:
* - no issues
* Internet Explorer 7:
* - no issues
* Internet Explorer 8:
* - no issues
* Firefox 3 - Windows:
* - pointer cursor doesn't display when hovering over the button.
* @author loeppky - based on the work done by MaximGB in Ext.ux.UploadDialog (http://extjs.com/forum/showthread.php?t=21558)
* The follow the curosr float div idea also came from MaximGB. With patches for IE8 by 4Him.
* @see http://extjs.com/forum/showthread.php?t=29032
* @constructor
* Create a new BrowseButton.
* @param {Object} config Configuration options
*/
Ext.ux.form.BrowseButton = Ext.extend(Ext.Button, {
/*
* Config options:
*/
/**
* @cfg {String} inputFileName
* Name to use for the hidden input file DOM element. Deaults to "file".
*/
inputFileName: 'file',
/**
* @cfg {Boolean} debug
* Toggle for turning on debug mode.
* Debug mode doesn't make clipEl transparent so that one can see how effectively it covers the Ext.Button.
* In addition, clipEl is given a green background and floatEl a red background to see how well they are positioned.
*/
debug: false,


/*
* Private constants:
*/
/**
* @property FLOAT_EL_WIDTH
* @type Number
* The width (in pixels) of floatEl.
* It should be less than the width of the IE "Browse" button's width (65 pixels), since IE doesn't let you resize it.
* We define this width so we can quickly center floatEl at the mouse cursor without having to make any function calls.
* @private
*/
FLOAT_EL_WIDTH: 60,

/**
* @property FLOAT_EL_HEIGHT
* @type Number
* The heigh (in pixels) of floatEl.
* It should be less than the height of the "Browse" button's height.
* We define this height so we can quickly center floatEl at the mouse cursor without having to make any function calls.
* @private
*/
FLOAT_EL_HEIGHT: 18,


/*
* Private properties:
*/
/**
* @property buttonCt
* @type Ext.Element
* Element that contains the actual Button DOM element.
* We store a reference to it, so we can easily grab its size for sizing the clipEl.
* @private
*/
buttonCt: null,
/**
* @property clipEl
* @type Ext.Element
* Element that contains the floatEl.
* This element is positioned to fill the area of Ext.Button and has overflow turned off.
* This keeps floadEl tight to the Ext.Button, and prevents it from masking surrounding elements.
* @private
*/
clipEl: null,
/**
* @property floatEl
* @type Ext.Element
* Element that contains the inputFileEl.
* This element is size to be less than or equal to the size of the input file "Browse" button.
* It is then positioned wherever the user moves the cursor, so that their click always clicks the input file "Browse" button.
* Overflow is turned off to preven inputFileEl from masking surrounding elements.
* @private
*/
floatEl: null,
/**
* @property inputFileEl
* @type Ext.Element
* Element for the hiden file input.
* @private
*/
inputFileEl: null,
/**
* @property originalHandler
* @type Function
* The handler originally defined for the Ext.Button during construction using the "handler" config option.
* We need to null out the "handler" property so that it is only called when a file is selected.
* @private
*/
originalHandler: null,
/**
* @property originalScope
* @type Object
* The scope originally defined for the Ext.Button during construction using the "scope" config option.
* While the "scope" property doesn't need to be nulled, to be consistent with originalHandler, we do.
* @private
*/
originalScope: null,
/**
* @property BROWSERS_OFFSET
* @type Object
* The browsers specific offsets used to position the clipping element for better overlay tightness. For
* Ext 3, Ext 2 offsets are used unless there is an Ext 3 entry.
* @private
* @author 4Him
*/
BROWSERS_OFFSETS: {
Ext2: {
IE8: {left: -8, top: -16, width: 16, height: 22},
IE: {left: -8, top: -3, width: 16, height: 6},
Opera: {left: -8, top: -3, width: -18, height: -1},
Gecko: {left: -8, top: -6, width: 16, height: 10},
Safari:{left: -4, top: -2, width: 6, height: 6}
},
Ext3: {
IE8: {left: -7, width: 10},
IE: {left: -3, width: 6},
Gecko: { width: 11}
}
},
/**
* @property isExt2x
* @type boolean
* Whether we are currently using Ext 2.x
* @private
* @author 4Him
*/
isExt2x: Ext.version.match(/^2\./),


/*
* Protected Ext.Button overrides
*/
/**
* @see Ext.Button.initComponent
*/
initComponent: function(){
Ext.ux.form.BrowseButton.superclass.initComponent.call(this);
// Store references to the original handler and scope before nulling them.
// This is done so that this class can control when the handler is called.
// There are some cases where the hidden file input browse button doesn't completely cover the Ext.Button.
// The handler shouldn't be called in these cases. It should only be called if a new file is selected on the file system.
this.originalHandler = this.handler;
this.originalScope = this.scope;
this.handler = null;
this.scope = null;
},

/**
* @see Ext.Button.onRender
*/
onRender: function(ct, position){
Ext.ux.form.BrowseButton.superclass.onRender.call(this, ct, position); // render the Ext.Button
// Patch for compatibility with 3.x (@author 4Him, based on dario's 05-10-2009 post)
if(this.isExt2x) {
this.buttonCt = this.el.child('.x-btn-center em');
} else {
this.buttonCt = this.el.child('.x-btn-mc em');
}
this.buttonCt.position('relative'); // this is important!
var styleCfg = {
position: 'absolute',
overflow: 'hidden',
top: '0px', // default
left: '0px' // default
};
// browser specifics for better overlay tightness - modified by 4Him
for(var browser in this.BROWSERS_OFFSETS.Ext2) {
if(Ext['is'+browser]) {
Ext.apply(styleCfg, {
left: this.getBrowserOffset(browser, 'left')+'px',
top: this.getBrowserOffset(browser, 'top')+'px'
});
break;
}
}
this.clipEl = this.buttonCt.createChild({
tag: 'div',
style: styleCfg
});
this.setClipSize();
this.clipEl.on({
'mousemove': this.onButtonMouseMove,
'mouseover': this.onButtonMouseMove,
scope: this
});

this.floatEl = this.clipEl.createChild({
tag: 'div',
style: {
position: 'absolute',
width: this.FLOAT_EL_WIDTH + 'px',
height: this.FLOAT_EL_HEIGHT + 'px',
overflow: 'hidden'
}
});


if (this.debug) {
this.clipEl.applyStyles({
'background-color': 'green'
});
this.floatEl.applyStyles({
'background-color': 'red'
});
} else {
// We don't set the clipEl to be transparent, because IE 6/7 occassionaly looses mouse events for transparent elements.
// We have listeners on the clipEl that can't be lost as they're needed for realligning the input file element.
this.floatEl.setOpacity(0.0);
}

// Cover cases where someone tabs to the button:
// Listen to focus of the button so we can translate the focus to the input file el.
var buttonEl = this.el.child(this.buttonSelector);
buttonEl.on('focus', this.onButtonFocus, this);
// In IE, it's possible to tab to the text portion of the input file el.
// We want to listen to keyevents so that if a space is pressed, we "click" the input file el.
if (Ext.isIE) {
this.el.on('keydown', this.onButtonKeyDown, this);
}

this.createInputFile();
},


/*
* Private helper methods:
*/
/**
* Returns an offset based on this.BROWSERS_OFFSET
* If currently using Ext 3.x, tries to find a value for 3.x and if there is none for 3.x, it
* returns a value for 2.x
* @param {string} the desired offset. Can be one of the following: 'left', 'top', 'width', 'height'
* @param {string} browser the browser for which to return the offset
* @return {int} the desired offset
* @author 4Him
*/
getBrowserOffset: function(browser, which) {
if(!this.isExt2x && this.BROWSERS_OFFSETS.Ext3[browser] && this.BROWSERS_OFFSETS.Ext3[browser][which]) {
return this.BROWSERS_OFFSETS.Ext3[browser][which];
} else {
return this.BROWSERS_OFFSETS.Ext2[browser][which];
}
},

/**
* Sets the size of clipEl so that is covering as much of the button as possible.
* @private
*/
setClipSize: function(){
if (this.clipEl) {
var width = this.buttonCt.getWidth();
var height = this.buttonCt.getHeight();
// The button container can have a width and height of zero when it's rendered in a hidden panel.
// This is most noticable when using a card layout, as the items are all rendered but hidden,
// (unless deferredRender is set to true).
// In this case, the clip size can't be determined, so we attempt to set it later.
// This check repeats until the button container has a size.
if (width === 0 || (height === 0 && !Ext.isIE8)) { // ugly hack (Ext.isIE8) by 4Him
this.setClipSize.defer(100, this);
} else {
// Loop by 4Him
for(var browser in this.BROWSERS_OFFSETS.Ext2) {
if(Ext['is'+browser]) {
width = width + this.getBrowserOffset(browser, 'width');
height = height + this.getBrowserOffset(browser, 'height');
break;
}
}
this.clipEl.setSize(width, height);
}
}
},

/**
* Creates the input file element and adds it to inputFileCt.
* The created input file elementis sized, positioned, and styled appropriately.
* Event handlers for the element are set up, and a tooltip is applied if defined in the original config.
* @private
*/
createInputFile: function(){
// When an input file gets detached and set as the child of a different DOM element,
// straggling <em> elements get left behind.
// I don't know why this happens but we delete any <em> elements we can find under the floatEl to prevent a memory leak.
this.floatEl.select('em').each(function(el){
el.remove();
});
this.inputFileEl = this.floatEl.createChild({
tag: 'input',
type: 'file',
size: 1, // must be > 0. It's value doesn't really matter due to our masking div (inputFileCt).
name: this.inputFileName || Ext.id(this.el),
tabindex: this.tabIndex,
// Use the same pointer as an Ext.Button would use. This doesn't work in Firefox.
// This positioning right-aligns the input file to ensure that the "Browse" button is visible.
style: {
position: 'absolute',
cursor: 'pointer',
right: '0px',
top: '0px'
}
});
this.inputFileEl = this.inputFileEl.child('input') || this.inputFileEl;
// IE8 needs opacity on the 'file input' element - @author 4Him
if(Ext.isIE8) {
this.inputFileEl.setOpacity(0.0);
}

// setup events
this.inputFileEl.on({
'click': this.onInputFileClick,
'change': this.onInputFileChange,
'focus': this.onInputFileFocus,
'select': this.onInputFileFocus,
'blur': this.onInputFileBlur,
scope: this
});

// add a tooltip
if (this.tooltip) {
if (typeof this.tooltip == 'object') {
Ext.QuickTips.register(Ext.apply({
target: this.inputFileEl
}, this.tooltip));
} else {
this.inputFileEl.dom[this.tooltipType] = this.tooltip;
}
}
},

/**
* Redirecting focus to the input file element so the user can press space and select files.
* @param {Event} e focus event.
* @private
*/
onButtonFocus: function(e){
if (this.inputFileEl) {
this.inputFileEl.focus();
e.stopEvent();
}
},

/**
* Handler for the IE case where once can tab to the text box of an input file el.
* If the key is a space, we simply "click" the inputFileEl.
* @param {Event} e key event.
* @private
*/
onButtonKeyDown: function(e){
if (this.inputFileEl && e.getKey() == Ext.EventObject.SPACE) {
this.inputFileEl.dom.click();
e.stopEvent();
}
},

/**
* Handler when the cursor moves over the clipEl.
* The floatEl gets centered to the cursor location.
* @param {Event} e mouse event.
* @private
*/
onButtonMouseMove: function(e){
var xy = e.getXY();
xy[0] -= this.FLOAT_EL_WIDTH / 2;
xy[1] -= this.FLOAT_EL_HEIGHT / 2;
this.floatEl.setXY(xy);
},

/**
* Add the visual enhancement to the button when the input file recieves focus.
* This is the tip for the user that now he/she can press space to select the file.
* @private
*/
onInputFileFocus: function(e){
if (!this.isDisabled) {
this.el.addClass("x-btn-over");
}
},

/**
* Removes the visual enhancement from the button.
* @private
*/
onInputFileBlur: function(e){
this.el.removeClass("x-btn-over");
},

/**
* Handler when inputFileEl's "Browse..." button is clicked.
* @param {Event} e click event.
* @private
*/
onInputFileClick: function(e){
e.stopPropagation();
},

/**
* Handler when inputFileEl changes value (i.e. a new file is selected).
* @private
*/
onInputFileChange: function(){
if (this.originalHandler) {
this.originalHandler.call(this.originalScope, this);
}
},


/*
* Public methods:
*/
/**
* Detaches the input file associated with this BrowseButton so that it can be used for other purposed (e.g. uplaoding).
* The returned input file has all listeners and tooltips applied to it by this class removed.
* @param {Boolean} whether to create a new input file element for this BrowseButton after detaching.
* True will prevent creation. Defaults to false.
* @return {Ext.Element} the detached input file element.
*/
detachInputFile: function(noCreate){
var result = this.inputFileEl;

if (typeof this.tooltip == 'object') {
Ext.QuickTips.unregister(this.inputFileEl);
} else {
this.inputFileEl.dom[this.tooltipType] = null;
}
this.inputFileEl.removeAllListeners();
this.inputFileEl = null;

if (!noCreate) {
this.createInputFile();
}
return result;
},

/**
* @return {Ext.Element} the input file element attached to this BrowseButton.
*/
getInputFile: function(){
return this.inputFileEl;
},

/**
* @see Ext.Button.disable
*/
disable: function(){
Ext.ux.form.BrowseButton.superclass.disable.call(this);
this.inputFileEl.dom.disabled = true;
},

/**
* @see Ext.Button.enable
*/
enable: function(){
Ext.ux.form.BrowseButton.superclass.enable.call(this);
this.inputFileEl.dom.disabled = false;
}
});

Ext.reg('browsebutton', Ext.ux.form.BrowseButton);

juststrings
9 Jul 2009, 5:34 AM
Hi,

This extension is great but is there a way to get the absolute path of the file selected rather than just the filename?

lorezyra
9 Jul 2009, 3:28 PM
Hi,

This extension is great but is there a way to get the absolute path of the file selected rather than just the filename?


The filename and path are provided by the broswer. EXTjs has no control over what the broswer returns to it. If you must have the full pathway of the user's local filesystem, then you will have to build an applet that the user must approve before visiting your site.



Why do you really need the full pathway anyway???

cleannoise
18 Jul 2009, 4:04 AM
I have rectified an Opera bug

My code:



Ext.namespace('Ext.ux.form');

Ext.ux.form.BrowseButton = Ext.extend(Ext.Button, {
inputFileName: 'file',
debug: false,
FLOAT_EL_WIDTH: 60,
FLOAT_EL_HEIGHT: 18,
buttonCt: null,
clipEl: null,
floatEl: null,
inputFileEl: null,
originalHandler: null,
originalScope: null,


initComponent: function(){
Ext.ux.form.BrowseButton.superclass.initComponent.call(this);
this.originalHandler = this.handler;
this.originalScope = this.scope;
this.handler = null;
this.scope = null;
},

onRender: function(ct, position){
Ext.ux.form.BrowseButton.superclass.onRender.call(this, ct, position); // render the Ext.Button
this.buttonCt = this.el.child('.x-btn-small em');
this.buttonCt.position('relative'); // this is important!
var styleCfg = {
position: 'absolute',
overflow: 'hidden',
top: '0px',
left: '0px'
};
// browser specifics for better overlay tightness
if (Ext.isIE) {
Ext.apply(styleCfg, {
left: '-3px',
top: '-3px'
});
} else if (Ext.isGecko) {
Ext.apply(styleCfg, {
left: '-3px',
top: '-3px'
});
} else if (Ext.isSafari) {
Ext.apply(styleCfg, {
left: '-4px',
top: '-2px'
});
}
this.clipEl = this.buttonCt.createChild({
tag: 'div',
style: styleCfg
});
this.setClipSize();
this.clipEl.on({
'mousemove': this.onButtonMouseMove,
'mouseover': this.onButtonMouseMove,
scope: this
});
this.floatEl = this.clipEl.createChild({
tag: 'div',
style: {
position: 'absolute',
width: this.FLOAT_EL_WIDTH + 'px',
height: this.FLOAT_EL_HEIGHT + 'px',
overflow: 'hidden'
}
});
if (this.debug) {
this.clipEl.applyStyles({
'background-color': 'green'
});
this.floatEl.applyStyles({
'background-color': 'red'
});
} else {
this.clipEl.setOpacity(0.0);
}
var buttonEl = this.el.child(this.buttonSelector);
buttonEl.on('focus', this.onButtonFocus, this);
if (Ext.isIE) {
this.el.on('keydown', this.onButtonKeyDown, this);
}
this.createInputFile();
},

setClipSize: function(){
if (this.clipEl) {
var width = this.buttonCt.getWidth();
var height = this.buttonCt.getHeight();
if (width === 0 || height === 0) {
this.setClipSize.defer(100, this);
} else {
if (Ext.isIE) {
width = width + 5;
height = height + 5;
} else if (Ext.isGecko) {
width = width + 6;
height = height + 6;
} else if (Ext.isSafari) {
width = width + 6;
height = height + 6;
}
this.clipEl.setSize(width, height);
}
}
},

createInputFile: function(){
var button_container = this.el.child('.x-btn-small');
button_container.position('relative');

this.floatEl.select('em').each(function(el){
el.remove();
});
this.inputFileEl = Ext.DomHelper.append(
button_container, {
tag: 'input',
type: 'file',
size: 1,
name: this.inputFileName || Ext.id(this.el),
tabindex: this.tabIndex,
style:'position:absolute; cursor:pointer; right:0px; top:0px;'
},true);
this.inputFileEl = this.inputFileEl.child('input') || this.inputFileEl;
this.inputFileEl.on({
'click': this.onInputFileClick,
'change': this.onInputFileChange,
'focus': this.onInputFileFocus,
'select': this.onInputFileFocus,
'blur': this.onInputFileBlur,
scope: this
});
this.inputFileEl.setOpacity(0);
this.adjustInputFileBox();

if (this.tooltip) {
if (typeof this.tooltip == 'object') {
Ext.QuickTips.register(Ext.apply({
target: this.inputFileEl
}, this.tooltip));
} else {
this.inputFileEl.dom[this.tooltipType] = this.tooltip;
}
}
},

adjustInputFileBox : function()
{
var btn_cont, btn_box, inp_box, adj;
if (this.el && this.inputFileEl) {
btn_cont = this.el.child('.x-btn-small');
btn_box = btn_cont.getBox();
this.inputFileEl.setStyle('font-size', (btn_box.width * 0.5) + 'px');
inp_box = this.inputFileEl.getBox();
adj = {x: 3, y: 3}
if (Ext.isIE) {
adj = {x: -3, y: 3}
}
this.inputFileEl.setLeft(btn_box.width - inp_box.width + adj.x + 'px');
this.inputFileEl.setTop(btn_box.height - inp_box.height + adj.y + 'px');
}
},

//----------------old function
/*createInputFile: function(){
this.floatEl.select('em').each(function(el){
el.remove();
});
this.inputFileEl = this.floatEl.createChild({
tag: 'input',
type: 'file',
size: 1,
name: this.inputFileName || Ext.id(this.el),
tabindex: this.tabIndex,
style:'position:absolute; cursor:pointer; right:0px; top:0px;'
});
this.inputFileEl = this.inputFileEl.child('input') || this.inputFileEl;
this.inputFileEl.on({
'click': this.onInputFileClick,
'change': this.onInputFileChange,
'focus': this.onInputFileFocus,
'select': this.onInputFileFocus,
'blur': this.onInputFileBlur,
scope: this
});

if (this.tooltip) {
if (typeof this.tooltip == 'object') {
Ext.QuickTips.register(Ext.apply({
target: this.inputFileEl
}, this.tooltip));
} else {
this.inputFileEl.dom[this.tooltipType] = this.tooltip;
}
}
},*/


onButtonFocus: function(e){
if (this.inputFileEl) {
this.inputFileEl.focus();
e.stopEvent();
}
},

onButtonKeyDown: function(e){

if (this.inputFileEl && e.getKey() == Ext.EventObject.SPACE) {
this.inputFileEl.dom.click();
e.stopEvent();
}
},

onButtonMouseMove: function(e){
var xy = e.getXY();
xy[0] -= this.FLOAT_EL_WIDTH / 2;
xy[1] -= this.FLOAT_EL_HEIGHT / 2;
this.floatEl.setXY(xy);
},

onInputFileFocus: function(e){
if (!this.isDisabled) {
this.el.addClass("x-btn-over");
}
},

onInputFileBlur: function(e){
this.el.removeClass("x-btn-over");
},

onInputFileClick: function(e){
e.stopPropagation();
},

onInputFileChange: function(){
if (this.originalHandler) {
this.originalHandler.call(this.originalScope, this);
}
},

detachInputFile: function(noCreate){
var result = this.inputFileEl;

if (typeof this.tooltip == 'object') {
Ext.QuickTips.unregister(this.inputFileEl);
} else {
this.inputFileEl.dom[this.tooltipType] = null;
}
this.inputFileEl.removeAllListeners();
this.inputFileEl = null;

if (!noCreate) {
this.createInputFile();
}
return result;
},

getInputFile: function(){
return this.inputFileEl;
},

disable: function(){
Ext.ux.form.BrowseButton.superclass.disable.call(this);
this.inputFileEl.dom.disabled = true;
},

enable: function(){

Ext.ux.form.BrowseButton.superclass.enable.call(this);
this.inputFileEl.dom.disabled = false;
}
});

Ext.reg('browsebutton', Ext.ux.form.BrowseButton);

mystix
18 Jul 2009, 7:19 AM
@cleannoise - you might want to highlight your changes to the original BrowseButton code in red. noone can tell from looking at your code what you changed.

cleannoise
18 Jul 2009, 9:50 AM
excuse me... Changes above;)

lorezyra
18 Jul 2009, 5:44 PM
@cleannoise - you might want to highlight your changes to the original BrowseButton code in red. noone can tell from looking at your code what you changed.

Or perhaps... more accurately, no one wants to figure out what changes you made...

It's too much trouble to run diff... ;)

patrosmania
31 Jul 2009, 8:28 AM
Hi everyone

I want to use this component inside an application i'm working on.

I can not show this button because it appears an error:

this.inputFileEl is null
chrome://firebug/content/blank.gifthis.inputFileEl.dom.disabled = true;\r\n

My code is:


xtype: 'browsebutton',
text: 'Subir'


Thanks in advance!

Olivier Smeesters
24 Aug 2009, 2:02 AM
Hi,

I'm using your extension and found the following issue. If you call disable() before the button has been rendered, the method will try to disable the inputFileEl before it exists.

To solve this, I've modified enable() and disable() to test if this.rendered and this.inputFileEl before applying to the inputFileEl and added the test to the onRender() method (if this.isDisabled, disable the dom element).

BTW, I'm using your extension instead of the extension provided by ExtJS 3 because the ExtJS extension has no detachFileInput. I can put your button in a toolbar and move the file input element to a hidden form for the upload. I don't see how this can be done with the ExtJS FileUploadField.

My 2 cents,


Olivier

loeppky
24 Aug 2009, 10:39 AM
Hi guys,

I'm sorry for the delayed response. I haven't been doing much Ext work with my team lately, thus am a bit out of the Ext loop.

That said, I don't want to put anytime into supporting this extension given that there is an official Ext extension instead. Olivier Smeesters brings up a concern about functionality that this extension provides over the Ext one. That said, I would rather energy be put into making the Ext one meet people's use cases.

If others want to keep this extension alive rather than working on improving the Ext one, go for it. It would probably be best to start a new thread so that the maintainers can modify the original post with the most up to date code. Alternatively, someone could create a project on GitHub for this.

I will update the original post saying this extension is no longer supported (by me) and that people will need to either read the most recent posts for the latest updates or switch to using the Ext provided control.

Sound good?
Steve

cblin
15 Oct 2009, 3:58 AM
For people that are still using this extension, note that we had to make this modification inside the code :
//this.clipEl.on({
// 'mousemove': this.onButtonMouseMove,
// 'mouseover': this.onButtonMouseMove,
// scope: this
//});

this.buttonCt.on({
'mousemove': this.onButtonMouseMove,
'mouseover': this.onButtonMouseMove,
scope: this
});

because this was sometimes causing problem with IE 6 & 7 (the mouseover and mousemove events were not triggered)

scottw
19 Oct 2009, 1:54 PM
The previously mentioned IE8 problem may have something to do with the DOCTYPE. When using XHTML strict, the problem appears. When we switched to using XHTML transitional the problem went away. I saw the problem on IE7 as well.

loeppky
27 Oct 2009, 2:53 PM
I know that I have said that I am no longer supporting this extension and that people should use Ext's Ext.ux.form.FileUploadField. I started using it myself last week and found problems with it. I have made the necessary fixes and documented them here: http://www.extjs.com/forum/showthread.php?p=402903. I don't expect to ever update this thread again.

freakmod
7 Nov 2009, 5:04 PM
I have to say this version is much better than the current official ExtJS one. Hopefully they take ideas from this one such as the moving browse button and non interference with adjacent buttons and add these features to the one in 3.x.x!

loeppky
10 Nov 2009, 1:16 PM
I have to say this version is much better than the current official ExtJS one. Hopefully they take ideas from this one such as the moving browse button and non interference with adjacent buttons and add these features to the one in 3.x.x!

Can you please add a post to http://www.extjs.com/forum/showthread.php?p=407357 suggesting that the Ext team incorporate these changes.

sanjaybh
8 Jan 2010, 4:01 AM
Hi there,

I need one <input type="file"> in my application where browse will put whole file path to the text box using extjs. I am new to extjs, need some help ?

regards
Sanjay

lorezyra
8 Jan 2010, 7:09 PM
Hi there,

I need one <input type="file"> in my application where browse will put whole file path to the text box using extjs. I am new to extjs, need some help ?

regards
Sanjay

Sanjay,
Only certain browsers will return the full pathway to a selected file. Most do NOT. If you want to get the full pathway, you will have to implement an independent applet or flash component that retrieves the file...

prasadk14
12 Jul 2010, 4:54 AM
Ext.ux.form.BrowseButton (http://www.sencha.com/forum/showthread.php?29032-Ext.ux.form.BrowseButton) is not working with Ext 3.2.1.

Is there an updated code available?

Thanks,
Prasad K