Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 349302
Collapse All | Expand All

(-)js/org/eclipse/rwt/widgets/FileUpload.js (-283 / +288 lines)
Lines 1-283 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2011 EclipseSource and others.
2
 * Copyright (c) 2011 EclipseSource and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
7
 *
8
 * Contributors:
8
 * Contributors:
9
 *    EclipseSource - initial API and implementation
9
 *    EclipseSource - initial API and implementation
10
 ******************************************************************************/
10
 ******************************************************************************/
11
11
12
qx.Class.define( "org.eclipse.rwt.widgets.FileUpload", {
12
qx.Class.define( "org.eclipse.rwt.widgets.FileUpload", {
13
13
14
  extend : org.eclipse.rwt.widgets.Button,
14
  extend : org.eclipse.rwt.widgets.Button,
15
15
16
  construct : function() {
16
  construct : function() {
17
    this.base( arguments, "push" );
17
    this.base( arguments, "push" );
18
    this.addState( "rwt_PUSH" ); // temporary
18
    this.addState( "rwt_PUSH" ); // temporary
19
    this.addEventListener( "insertDom", this._layoutInputElement, this );
19
    this.addEventListener( "insertDom", this._layoutInputElement, this );
20
    this.addEventListener( "elementOver", this._onMouseOverElement, this );
20
    this.addEventListener( "elementOver", this._onMouseOverElement, this );
21
    this._formElement = null;
21
    this._formElement = null;
22
    this._inputElement = null;
22
    this._inputElement = null;
23
    this._iframe = null;
23
    this._iframe = null;
24
    this._cursor = "";
24
    this._cursor = "";
25
    this.__onValueChange = qx.lang.Function.bind( this._onValueChange, this );    
25
    this.__onValueChange = qx.lang.Function.bind( this._onValueChange, this );    
26
    this.setEnableElementFocus( false );
26
    this.setEnableElementFocus( false );
27
    this._createIframeWidget();
27
    this._createIframeWidget();
28
  },
28
  },
29
  
29
  
30
  destruct : function() {
30
  destruct : function() {
31
    this._formElement = null;
31
    this._formElement = null;
32
    this._inputElement = null;    
32
    this._inputElement = null;    
33
  },
33
  },
34
  
34
  
35
  members : {
35
  members : {
36
36
37
    submit : function( url ) {
37
    submit : function( url ) {
38
      if( typeof url !== "string" ) {
38
      if( typeof url !== "string" ) {
39
        throw new Error( "No url given!" );
39
        throw new Error( "No url given!" );
40
      } 
40
      } 
41
      if( this._getFileName() === "" ) {
41
      if( this._getFileName() === "" ) {
42
        throw new Error( "No file selected!" );
42
        throw new Error( "No file selected!" );
43
      }
43
      }
44
      if( this._formElement === null ) {
44
      if( this._formElement === null ) {
45
        throw new Error( "Form element not created!" );
45
        throw new Error( "Form element not created!" );
46
      }
46
      }
47
      this._formElement.setAttribute( "action", url );
47
      this._formElement.setAttribute( "action", url );
48
      this._formElement.submit();
48
      this._formElement.submit();
49
    },
49
    },
50
50
51
    destroy : function() {
51
    destroy : function() {
52
      this.base( arguments );
52
      this.base( arguments );
53
      this._iframe.destroy();
53
      this._iframe.destroy();
54
    },
54
    },
55
55
56
    ////////////
56
    ////////////
57
    // Internals
57
    // Internals
58
58
59
    _createSubelements : function() {
59
    _createSubelements : function() {
60
      this.base( arguments );
60
      this.base( arguments );
61
      // NOTE: MultiCellWidget uses innerHTML, therefore this must be done here:
61
      // NOTE: MultiCellWidget uses innerHTML, therefore this must be done here:
62
      if( this._formElement === null ) {
62
      if( this._formElement === null ) {
63
        this._createFormElement();
63
        this._createFormElement();
64
        this._createInputElement();
64
        this._createInputElement();
65
      } else {
65
      } else {
66
        this._getTargetNode().appendChild( this._formElement );
66
        this._getTargetNode().appendChild( this._formElement );
67
        var isMshtml = qx.core.Variant.isSet( "qx.client", "mshtml" );
67
        var isMshtml = qx.core.Variant.isSet( "qx.client", "mshtml" );
68
        if( isMshtml ) {
68
        if( isMshtml ) {
69
          this._formElement.appendChild( this._inputElement );
69
          this._formElement.appendChild( this._inputElement );
70
        }
70
        }
71
      }
71
      }
72
    },
72
    },
73
73
74
    _createFormElement : function() {
74
    _createFormElement : function() {
75
      this._formElement = document.createElement( "form" );
75
      this._formElement = document.createElement( "form" );
76
      this._formElement.setAttribute( "target", this._getFrameName() );
76
      this._formElement.setAttribute( "target", this._getFrameName() );
77
      this._formElement.setAttribute( "method", "POST" );
77
      this._formElement.setAttribute( "method", "POST" );
78
      var isMshtml = qx.core.Variant.isSet( "qx.client", "mshtml" );
78
      var isMshtml = qx.core.Variant.isSet( "qx.client", "mshtml" );
79
      if( isMshtml ) {
79
      if( isMshtml ) {
80
        this._formElement.setAttribute( "encoding", "multipart/form-data" );        
80
        this._formElement.setAttribute( "encoding", "multipart/form-data" );        
81
      } else {
81
      } else {
82
        this._formElement.setAttribute( "enctype", "multipart/form-data" );
82
        this._formElement.setAttribute( "enctype", "multipart/form-data" );
83
      }
83
      }
84
      this._getTargetNode().appendChild( this._formElement );
84
      this._getTargetNode().appendChild( this._formElement );
85
    },
85
    },
86
86
87
    _createInputElement : function() {
87
    _createInputElement : function() {
88
      this._inputElement = document.createElement( "input" );
88
      this._inputElement = document.createElement( "input" );
89
      this._inputElement.style.position = "absolute";
89
      this._inputElement.style.position = "absolute";
90
      this._inputElement.setAttribute( "type", "file" );
90
      this._inputElement.setAttribute( "type", "file" );
91
      this._inputElement.setAttribute( "name", "file" );
91
      this._inputElement.setAttribute( "name", "file" );
92
      this._inputElement.setAttribute( "size", "1" );
92
      this._inputElement.setAttribute( "size", "1" );
93
      this._inputElement.style.cursor = this._cursor;
93
      this._inputElement.style.cursor = this._cursor;
94
      this._inputElement.onchange = this.__onValueChange;
94
      this._inputElement.onchange = this.__onValueChange;
95
      org.eclipse.rwt.HtmlUtil.setOpacity( this._inputElement, 0 );
95
      org.eclipse.rwt.HtmlUtil.setOpacity( this._inputElement, 0 );
96
      this._formElement.appendChild( this._inputElement );
96
      this._formElement.appendChild( this._inputElement );
97
    },
97
    },
98
98
99
    _createIframeWidget : function() {
99
    _createIframeWidget : function() {
100
      this._iframe = new qx.ui.embed.Iframe();
100
      this._iframe = new qx.ui.embed.Iframe();
101
      // NOTE: The frame-content should only be changed by the form:
101
      // NOTE: The frame-content should only be changed by the form:
102
      this._iframe.setSource( "about:blank" );
102
      this._iframe.setSource( "about:blank" );
103
      this._iframe.setVisibility( false );
103
      this._iframe.setVisibility( false );
104
      this._iframe.setWidth( 0 );
104
      this._iframe.setWidth( 0 );
105
      this._iframe.setHeight( 0 );
105
      this._iframe.setHeight( 0 );
106
      this._iframe.setFrameName( this._getFrameName() );
106
      this._iframe.setFrameName( this._getFrameName() );
107
      this._iframe.addToDocument();
107
      this._iframe.addToDocument();
108
    },
108
    },
109
109
110
    _onValueChange : function( event ) {
110
    _onValueChange : function( event ) {
111
      // TODO [tb] : implement setHasValueChangedListener?
111
      // TODO [tb] : implement setHasValueChangedListener?
112
      var fileName = this._formatFileName( this._getFileName() );
112
      var fileName = this._formatFileName( this._getFileName() );
113
      if( !org.eclipse.swt.EventUtil.getSuspended() ) {
113
      if( !org.eclipse.swt.EventUtil.getSuspended() ) {
114
        var req = org.eclipse.swt.Request.getInstance();
114
        var req = org.eclipse.swt.Request.getInstance();
115
        var widgetManager = org.eclipse.swt.WidgetManager.getInstance();
115
        var widgetManager = org.eclipse.swt.WidgetManager.getInstance();
116
        var id = widgetManager.findIdByWidget( this );
116
        var id = widgetManager.findIdByWidget( this );
117
        req.addParameter( id + ".fileName", fileName );
117
        req.addParameter( id + ".fileName", fileName );
118
        req.send();
118
        req.send();
119
      }
119
      }
120
    },
120
    },
121
121
122
    _getFileName : function() {
122
    _getFileName : function() {
123
      return this._inputElement.value;
123
      return this._inputElement.value;
124
    },
124
    },
125
125
126
    ////////////
126
    ////////////
127
    // Layouting
127
    // Layouting
128
128
129
    _layoutPost : function( changes ) {
129
    _layoutPost : function( changes ) {
130
      this.base( arguments, changes );
130
      this.base( arguments, changes );
131
      if( changes.width || changes.height ) {
131
      if( changes.width || changes.height ) {
132
        this._layoutInputElement();
132
        this._layoutInputElement();
133
      }
133
      }
134
    },
134
    },
135
135
136
    _layoutInputElement : function() {
136
    _layoutInputElement : function() {
137
      if( this.getEnabled() && this.isInDom() ) {
137
      if( this.getEnabled() && this.isSeeable() ) {
138
        //Assumed maximal padding between input button and input outer dimensions:
138
        //Assumed maximal padding between input button and input outer dimensions:
139
        var padding = 10; 
139
        var padding = 10; 
140
        this._layoutInputElementHorizontal( padding );
140
        this._layoutInputElementHorizontal( padding );
141
        this._layoutInputElementVertical( padding );
141
        this._layoutInputElementVertical( padding );
142
      }
142
      }
143
    },
143
    },
144
144
145
    _layoutInputElementHorizontal : function( padding ) {
145
    _layoutInputElementHorizontal : function( padding ) {
146
      // Respect assumed maximal relative width of the input textfield:
146
      // Respect assumed maximal relative width of the input textfield:
147
      var inputButtonPercentage = 0.6;
147
      var inputButtonPercentage = 0.6;
148
      // NOTE : This is how inputWidth is calculated:
148
      // NOTE : This is how inputWidth is calculated:
149
      // widgetWidth + padding * 2 = 0.6 * inputWidth
149
      // widgetWidth + padding * 2 = 0.6 * inputWidth
150
      // inputWidth = ( widthWidth + padding * 2 ) / 0.6
150
      // inputWidth = ( widthWidth + padding * 2 ) / 0.6
151
      var widgetWidth = this.getBoxWidth();
151
      var widgetWidth = this.getBoxWidth();
152
      var inputTargetWidth =   ( widgetWidth + padding * 2 ) 
152
      var inputTargetWidth =   ( widgetWidth + padding * 2 ) 
153
                             / ( inputButtonPercentage );
153
                             / ( inputButtonPercentage );
154
      var fontSize = inputTargetWidth / 10; 
154
      var fontSize = inputTargetWidth / 10; 
155
      this._inputElement.style.fontSize = fontSize;
155
      this._inputElement.style.fontSize = fontSize;
156
      var iterations = 0;
156
      var iterations = 0;
157
      while( this._inputElement.offsetWidth <= inputTargetWidth ) {
157
      while( this._inputElement.offsetWidth <= inputTargetWidth ) {
158
        fontSize += 10;
158
        fontSize += 10;
159
        this._inputElement.style.fontSize = fontSize;
159
        this._inputElement.style.fontSize = fontSize;
160
        iterations++;
160
        iterations++;
161
        if( iterations > 100 ) {
161
        if( iterations > 100 ) {
162
          // crash the rap-application instead of freezing the browser.
162
          // crash the rap-application instead of freezing the browser.
163
          var msg = "Failed to force input-element width.";
163
          var msg = "Failed to force input-element width.";
164
          throw new Error( msg );
164
          throw new Error( msg );
165
        }
165
        }
166
      }
166
      }
167
      var actualInputWidth = this._inputElement.offsetWidth;
167
      var actualInputWidth = this._inputElement.offsetWidth;
168
      var inputLeft = widgetWidth - actualInputWidth + padding;
168
      var inputLeft = widgetWidth - actualInputWidth + padding;
169
      this._inputElement.style.left = inputLeft + "px";
169
      this._inputElement.style.left = inputLeft + "px";
170
    },
170
    },
171
171
172
    _layoutInputElementVertical : function( padding ) {
172
    _layoutInputElementVertical : function( padding ) {
173
      var widgetHeight = this.getBoxHeight();
173
      var widgetHeight = this.getBoxHeight();
174
      this._inputElement.style.height = ( widgetHeight + padding * 2 ) + "px";
174
      this._inputElement.style.height = ( widgetHeight + padding * 2 ) + "px";
175
      this._inputElement.style.top = ( padding * -1 ) + "px";
175
      this._inputElement.style.top = ( padding * -1 ) + "px";
176
    },
176
    },
177
177
178
    setStyleProperty : function( propName, value ) {
178
    setStyleProperty : function( propName, value ) {
179
      if( propName === "cursor" ) {
179
      if( propName === "cursor" ) {
180
        this._cursor = value;
180
        this._cursor = value;
181
        if( this._inputElement != null ) {
181
        if( this._inputElement != null ) {
182
          // NOTE : This will have no effect in firefox.
182
          // NOTE : This will have no effect in firefox.
183
          this._inputElement.style.cursor = value;
183
          this._inputElement.style.cursor = value;
184
        }
184
        }
185
      } else {
185
      } else {
186
        this.base( arguments, propName, value );
186
        this.base( arguments, propName, value );
187
      }
187
      }
188
    },
188
    },
189
189
190
    _applyEnabled : function( value, oldValue ) {
190
    _applyEnabled : function( value, oldValue ) {
191
      this.base( arguments, value, oldValue );
191
      this.base( arguments, value, oldValue );
192
      if( this._inputElement ) {
192
      if( this._inputElement ) {
193
        this._inputElement.style.display = value ? "" : "none";
193
        this._inputElement.style.display = value ? "" : "none";
194
        this._layoutInputElement();
194
        this._layoutInputElement();
195
      }
195
      }
196
    },
196
    },
197
197
198
    ////////////////
198
    _afterAppear : function() {
199
    // Mouse-control
199
      this.base( arguments );
200
    
200
      this._layoutInputElement();
201
    // NOTE : In contrast to other widgets, the border does not trigger the
201
    },
202
    //        expected function, adapt state-behavior accordingly:
202
203
    
203
    ////////////////
204
    _onMouseOver : function( event ) {
204
    // Mouse-control
205
      if( event.getDomTarget() === this._inputElement ) {
205
    
206
        this.base( arguments, event );
206
    // NOTE : In contrast to other widgets, the border does not trigger the
207
      }
207
    //        expected function, adapt state-behavior accordingly:
208
    },
208
    
209
209
    _onMouseOver : function( event ) {
210
    _onMouseOverElement : function( event ) {
210
      if( event.getDomTarget() === this._inputElement ) {
211
      if( event.getDomTarget() === this._inputElement ) {
211
        this.base( arguments, event );
212
        this._onMouseOver( event );
212
      }
213
      }
213
    },
214
    },
214
215
215
    _onMouseOverElement : function( event ) {
216
    _onMouseDown : function( event ) {
216
      if( event.getDomTarget() === this._inputElement ) {
217
      if( event.getDomTarget() === this._inputElement ) {
217
        this._onMouseOver( event );
218
        this.base( arguments, event );
218
      }
219
      }
219
    },
220
      if( org.eclipse.rwt.Client.getBrowser() === "chrome") {
220
221
        // Chrome looses keyboard control on mouse-focus, see _ontabfocus.
221
    _onMouseDown : function( event ) {
222
        this._onBlur();
222
      if( event.getDomTarget() === this._inputElement ) {
223
      }      
223
        this.base( arguments, event );
224
    },
224
      }
225
225
      if( org.eclipse.rwt.Client.getBrowser() === "chrome") {
226
    _onMouseUp : function( event ) {
226
        // Chrome looses keyboard control on mouse-focus, see _ontabfocus.
227
      if( event.getDomTarget() === this._inputElement || this.hasState( "abandoned" ) ) {
227
        this._onBlur();
228
        this.base( arguments, event );
228
      }      
229
      }
229
    },
230
    },
230
231
231
    _onMouseUp : function( event ) {
232
    /////////////////////////
232
      if( event.getDomTarget() === this._inputElement || this.hasState( "abandoned" ) ) {
233
    // Focus/keyboard-control
233
        this.base( arguments, event );
234
234
      }
235
    // NOTE : Since the browse-button can't be triggered programatically,
235
    },
236
    //        supporting native keyboard-control is necessary, which is a bit
236
237
    //        problematic.
237
    /////////////////////////
238
    _onFocus : function( event ) {
238
    // Focus/keyboard-control
239
      this.base( arguments, event );
239
240
      this._inputElement.focus();
240
    // NOTE : Since the browse-button can't be triggered programatically,
241
    },
241
    //        supporting native keyboard-control is necessary, which is a bit
242
242
    //        problematic.
243
    // NOTE : key-handling interferes with native keyboard control. This 
243
    _onFocus : function( event ) {
244
    //        disables the "pressed" state, but is still the lesser evil.
244
      this.base( arguments, event );
245
    _onKeyDown : qx.lang.Function.returnTrue,
245
      this._inputElement.focus();
246
    _onKeyUp : qx.lang.Function.returnTrue,
246
    },
247
247
248
    // NOTE : In chrome (windows?), the input-element needs to be focused using
248
    // NOTE : key-handling interferes with native keyboard control. This 
249
    //        tabulator for keyboard control to work. To minimize confusion,
249
    //        disables the "pressed" state, but is still the lesser evil.
250
    //        do not display focus frame in other cases.
250
    _onKeyDown : qx.lang.Function.returnTrue,
251
    _ontabfocus : function() {
251
    _onKeyUp : qx.lang.Function.returnTrue,
252
      if( org.eclipse.rwt.Client.getBrowser() === "chrome" ) {
252
253
        this._showFocusIndicator( true );
253
    // NOTE : In chrome (windows?), the input-element needs to be focused using
254
      }
254
    //        tabulator for keyboard control to work. To minimize confusion,
255
    },
255
    //        do not display focus frame in other cases.
256
256
    _ontabfocus : function() {
257
    _showFocusIndicator : function( allow ) {
257
      if( org.eclipse.rwt.Client.getBrowser() === "chrome" ) {
258
      var isChrome = org.eclipse.rwt.Client.getBrowser() === "chrome";
258
        this._showFocusIndicator( true );
259
      if( !isChrome || allow ) {
259
      }
260
        this.base( arguments );
260
    },
261
      }
261
262
    },
262
    _showFocusIndicator : function( allow ) {
263
263
      var isChrome = org.eclipse.rwt.Client.getBrowser() === "chrome";
264
    /////////
264
      if( !isChrome || allow ) {
265
    // Helper
265
        this.base( arguments );
266
266
      }
267
    _formatFileName : function( fileName ) {
267
    },
268
      var result = fileName;
268
269
      if( result.indexOf( "\\" ) != -1 ) {
269
    /////////
270
        result = result.substr( result.lastIndexOf( "\\" ) + 1 );
270
    // Helper
271
      } else if( result.indexOf( "/" ) != -1 ) {
271
272
        result = result.substr( result.lastIndexOf( "/" ) + 1 );
272
    _formatFileName : function( fileName ) {
273
      }
273
      var result = fileName;
274
      return result;
274
      if( result.indexOf( "\\" ) != -1 ) {
275
    },
275
        result = result.substr( result.lastIndexOf( "\\" ) + 1 );
276
276
      } else if( result.indexOf( "/" ) != -1 ) {
277
    _getFrameName : function() {
277
        result = result.substr( result.lastIndexOf( "/" ) + 1 );
278
      return "FileUpload_" + this.toHashCode();
278
      }
279
    }
279
      return result;
280
280
    },
281
  }
281
282
282
    _getFrameName : function() {
283
} );
283
      return "FileUpload_" + this.toHashCode();
284
    }
285
286
  }
287
288
} );
(-)js/org/eclipse/rwt/test/tests/FileUploadTest.js (+25 lines)
Lines 153-158 Link Here
153
      this._checkInputLayout( upload );
153
      this._checkInputLayout( upload );
154
      upload.destroy();       
154
      upload.destroy();       
155
    },
155
    },
156
    
157
    testInputLayoutNotVisible : function() {
158
      var testUtil = org.eclipse.rwt.test.fixture.TestUtil;
159
      var upload = this._createFileUpload( true );
160
      upload.setVisibility( false );
161
      testUtil.flush();
162
      upload.setVisibility( true );
163
      testUtil.flush();
164
      this._checkInputLayout( upload );
165
      upload.destroy();       
166
    },
167
168
    testInputLayoutParentNotVisible : function() {
169
      var testUtil = org.eclipse.rwt.test.fixture.TestUtil;
170
      var upload = this._createFileUpload( true );
171
      var parent = new org.eclipse.swt.widgets.Composite();
172
      parent.addToDocument();
173
      upload.setParent( parent );
174
      upload.getParent().setVisibility( false );
175
      testUtil.flush();
176
      upload.getParent().setVisibility( true );
177
      testUtil.flush();
178
      this._checkInputLayout( upload );
179
      upload.destroy();       
180
    },
156
181
157
    testCursor : function() {
182
    testCursor : function() {
158
      var testUtil = org.eclipse.rwt.test.fixture.TestUtil;
183
      var testUtil = org.eclipse.rwt.test.fixture.TestUtil;

Return to bug 349302