source: Dev/trunk/src/client/dojox/form/Uploader.js @ 529

Last change on this file since 529 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 12.0 KB
Line 
1define([
2        "dojo/_base/kernel",
3        "dojo/_base/declare",
4        "dojo/_base/lang",
5        "dojo/_base/array",
6        "dojo/_base/connect",
7        "dojo/_base/window",
8        "dojo/dom-style",
9        "dojo/dom-class",
10        "dojo/dom-geometry",
11        "dojo/dom-attr",
12        "dojo/dom-construct",
13        "dojo/dom-form",
14        "dijit",
15        "dijit/form/Button",
16        "./uploader/_Base",
17        "./uploader/_HTML5",
18        "./uploader/_IFrame",
19        "./uploader/_Flash",
20        "dojo/i18n!./nls/Uploader",
21        "dojo/text!./resources/Uploader.html"
22],function(kernel, declare, lang, array, connect, win, domStyle, domClass, domGeometry, domAttr, domConstruct,
23                   domForm, dijit, Button, Base, HTML5, IFrame, Flash, res, template){
24
25        // TODO:
26        //              i18n
27        //              label via innerHTML
28        //              Doc and or test what can be extended.
29        //              Doc custom file events
30        //              Use new FileReader() for thumbnails
31        //              flashFieldName should default to Flash
32        //              get('value'); and set warning
33        //              Make it so URL can change (current set to Flash on build)
34        //
35
36        return declare("dojox.form.Uploader", [Base, Button, HTML5, IFrame, Flash], {
37                // summary:
38                //              A widget that creates a stylable file-input button, with optional multi-file selection,
39                //              using only HTML elements. Non-HTML5 browsers have fallback options of Flash or an iframe.
40                //
41                // description:
42                //              A bare-bones, stylable file-input button, with optional multi-file selection. The list
43                //              of files is not displayed, that is for you to handle by connecting to the onChange
44                //              event, or use the dojox.form.uploader.FileList.
45                //
46                //              Uploader without plugins does not have any ability to upload - it is for use in forms
47                //              where you handle the upload either by a standard POST or with Ajax using an iFrame. This
48                //              class is for convenience of multiple files only. No progress events are available.
49                //
50                //              If the browser supports a file-input with the "multiple" attribute, that will be used.
51                //              If the browser does not support "multiple" (ergo, IE) multiple inputs are used,
52                //              one for each selection.
53                //
54                //              Version: 1.6
55       
56       
57                // uploadOnSelect: Boolean
58                //              If true, uploads immediately after a file has been selected. If false,
59                //              waits for upload() to be called.
60                uploadOnSelect:false,
61       
62                // tabIndex: Number|String
63                //              The tab order in the DOM.
64                tabIndex:0,
65       
66                // multiple: Boolean
67                //              If true and flash mode, multiple files may be selected from the dialog.
68                multiple:false,
69       
70                // label: String
71                //              The text used in the button that when clicked, opens a system Browse Dialog.
72                label:res.label,
73       
74                // url: String
75                //              The url targeted for upload. An absolute URL is preferred. Relative URLs are
76                //              changed to absolute.
77                url:"",
78       
79                // name: String
80                //              The name attribute needs to end with square brackets: [] as this is the standard way
81                //              of handling an attribute "array". This requires a slightly different technique on the
82                //              server.
83                name:"uploadedfile",
84       
85                // flashFieldName: String
86                //              If set, this will be the name of the field of the flash uploaded files that the server
87                //              is expecting. If not set, "Flash" is appended to the "name" property.
88                flashFieldName:"",
89               
90                //      force: String
91                //              options: form, html5, iframe, flash
92                //              Empty string defaults to html5 if available, and iframe if not.
93                //              Use "flash" to always use Flash (and hopefully force the user to download the plugin
94                //              if they don't have it).
95                //              Use "iframe" to always use an iframe, and never flash nor html5. Sometimes preferred
96                //              for consistent results.
97                //              Use "form" to not use ajax and post to a page.
98                force:"",
99       
100                // uploadType: String [readonly]
101                //              The type of uploader being used. As an alternative to determining the upload type on the
102                //              server based on the fieldName, this property could be sent to the server to help
103                //              determine what type of parsing should be used.
104                //              This is set based on the force property and what features are available in the browser.
105                uploadType:"",
106       
107                // showInput: String [const]
108                //              Position to show an input which shows selected filename(s). Possible
109                //              values are "before", "after", which specifies where the input should
110                //              be placed with reference to the containerNode which contains the
111                //              label). By default, this is empty string (no such input will be
112                //              shown). Specify showInput="before" to mimic the look&feel of a
113                //              native file input element.
114                showInput: "",
115               
116                //      focusedClass: String
117                //              The class applied to the button when it is focused (via TAB key)
118                focusedClass:"dijitButtonHover",
119       
120                _nameIndex:0,
121       
122                templateString: template,
123       
124                baseClass: 'dijitUploader '+Button.prototype.baseClass,
125       
126                postMixInProperties: function(){
127                        this._inputs = [];
128                        this._cons = [];
129                        this.force = this.force.toLowerCase();
130                        if(this.supports("multiple")){
131                                this.uploadType = this.force === 'form' ? 'form' : 'html5';
132                        }else{
133                                this.uploadType = this.force === 'flash' ? 'flash' : 'iframe';
134                        }
135                       
136                        this.inherited(arguments);
137                },
138                buildRendering: function(){
139                        this.inherited(arguments);
140                        domStyle.set(this.domNode, {
141                                overflow:"hidden",
142                                position:"relative"
143                        });
144                        this._buildDisplay();
145                        //change the button node not occupy tabIndex: the real file input
146                        //will have tabIndex set
147                        domAttr.set(this.titleNode, 'tabIndex', -1);
148                },
149                _buildDisplay: function(){
150                        if(this.showInput){
151                                this.displayInput = domConstruct.create('input', {
152                                          'class':'dijitUploadDisplayInput',
153                                          'tabIndex':-1, 'autocomplete':'off',
154                                          'role':'presentation'},
155                                        this.containerNode, this.showInput);
156                                //schedule the attachpoint to be cleaned up on destroy
157                                this._attachPoints.push('displayInput');
158                                this.connect(this,'onChange', function(files){
159                                        var i=0,l=files.length, f, r=[];
160                                        while((f=files[i++])){
161                                                if(f && f.name){
162                                                        r.push(f.name);
163                                                }
164                                        }
165                                        this.displayInput.value = r.join(', ');
166                                });
167                                this.connect(this,'reset', function(){
168                                        this.displayInput.value = '';
169                                });
170                        }
171                },
172       
173                startup: function(){
174                        if(this._buildInitialized){
175                                return;
176                        }
177                        this._buildInitialized = true;
178                        this._getButtonStyle(this.domNode);
179                        this._setButtonStyle();
180                        this.inherited(arguments);
181                },
182       
183                /*************************
184                 *         Public Events         *
185                 *************************/
186       
187                onChange: function(/*Array*/ fileArray){
188                        // summary:
189                        //              stub to connect
190                        //              Fires when files are selected
191                        //              Event is an array of last files selected
192                },
193       
194                onBegin: function(/*Array*/ dataArray){
195                        // summary:
196                        //              Fires when upload begins
197                },
198       
199                onProgress: function(/*Object*/ customEvent){
200                        // summary:
201                        //              Stub to connect
202                        //              Fires on upload progress. Event is a normalized object of common properties
203                        //              from HTML5 uploaders and the Flash uploader. Will not fire for IFrame.
204                        // customEvent:
205                        //              - bytesLoaded: Number:
206                        //                      Amount of bytes uploaded so far of entire payload (all files)
207                        //              - bytesTotal: Number:
208                        //                      Amount of bytes of entire payload (all files)
209                        //              - type: String:
210                        //                      Type of event (progress or load)
211                        //              - timeStamp: Number:
212                        //                      Timestamp of when event occurred
213                },
214       
215                onComplete: function(/*Object*/ customEvent){
216                        // summary:
217                        //              stub to connect
218                        //              Fires when all files have uploaded
219                        //              Event is an array of all files
220                        this.reset();
221                },
222       
223                onCancel: function(){
224                        // summary:
225                        //              Stub to connect
226                        //              Fires when dialog box has been closed
227                        //              without a file selection
228                },
229       
230                onAbort: function(){
231                        // summary:
232                        //              Stub to connect
233                        //              Fires when upload in progress was canceled
234                },
235       
236                onError: function(/*Object or String*/ evtObject){
237                        // summary:
238                        //              Fires on errors
239       
240                        // FIXME: Unsure of a standard form of error events
241                },
242       
243                /*************************
244                 *         Public Methods        *
245                 *************************/
246       
247                upload: function(/*Object?*/ formData){                         
248                        // summary:
249                        //              When called, begins file upload. Only supported with plugins.
250                        formData = formData || {};
251                        formData.uploadType = this.uploadType;
252                        this.inherited(arguments);
253                },
254       
255                submit: function(/*form Node?*/ form){
256                        // summary:
257                        //              If Uploader is in a form, and other data should be sent along with the files, use
258                        //              this instead of form submit.
259                        form = !!form ? form.tagName ? form : this.getForm() : this.getForm();
260                        var data = domForm.toObject(form);
261                        data.uploadType = this.uploadType;
262                        this.upload(data);
263                },
264       
265                reset: function(){
266                        // summary:
267                        //              Resets entire input, clearing all files.
268                        //              NOTE:
269                        //              Removing individual files is not yet supported, because the HTML5 uploaders can't
270                        //              be edited.
271                        //              TODO:
272                        //              Add this ability by effectively, not uploading them
273                        //
274                        delete this._files;
275                        this._disconnectButton();
276                        array.forEach(this._inputs, domConstruct.destroy, dojo);
277                        this._inputs = [];
278                        this._nameIndex = 0;
279                        this._createInput();
280                },
281       
282                getFileList: function(){
283                        // summary:
284                        //              Returns a list of selected files.
285       
286                        var fileArray = [];
287                        if(this.supports("multiple")){
288                                array.forEach(this._files, function(f, i){
289                                        fileArray.push({
290                                                index:i,
291                                                name:f.name,
292                                                size:f.size,
293                                                type:f.type
294                                        });
295                                }, this);
296                        }else{
297                                array.forEach(this._inputs, function(n, i){
298                                        if(n.value){
299                                                fileArray.push({
300                                                        index:i,
301                                                        name:n.value.substring(n.value.lastIndexOf("\\")+1),
302                                                        size:0,
303                                                        type:n.value.substring(n.value.lastIndexOf(".")+1)
304                                                });
305                                        }
306                                }, this);
307       
308                        }
309                        return fileArray; // Array
310                },
311       
312                /*********************************************
313                 *         Private Property. Get off my lawn.    *
314                 *********************************************/
315       
316                _getValueAttr: function(){
317                        // summary:
318                        //              Internal. To get disabled use: uploader.get("disabled");
319                        return this.getFileList();
320                },
321       
322                _setValueAttr: function(disabled){
323                        console.error("Uploader value is read only");
324                },
325       
326                _setDisabledAttr: function(disabled){
327                        // summary:
328                        //              Internal. To set disabled use: uploader.set("disabled", true);
329                        if(this.disabled == disabled || !this.inputNode){ return; }
330                        this.inherited(arguments);
331                        domStyle.set(this.inputNode, "display", disabled ? "none" : "");
332                },
333       
334                _getButtonStyle: function(node){
335                        this.btnSize = {w:domStyle.get(node,'width'), h:domStyle.get(node,'height')};
336                },
337       
338                _setButtonStyle: function(){
339                        this.inputNodeFontSize = Math.max(2, Math.max(Math.ceil(this.btnSize.w / 60), Math.ceil(this.btnSize.h / 15)));
340                        this._createInput();
341                },
342       
343                _getFileFieldName: function(){
344                        var name;
345                        if(this.supports("multiple") && this.multiple){
346                                name = this.name+"s[]";
347                        }else{
348                                // <=IE8
349                                name = this.name + (this.multiple ? this._nameIndex : "");
350                        }
351                        return name;
352                },
353       
354                _createInput: function(){
355                        if(this._inputs.length){
356                                domStyle.set(this.inputNode, {
357                                        top:"500px"
358                                });
359                                this._disconnectButton();
360                                this._nameIndex++;
361                        }
362                        var name = this._getFileFieldName();
363                        // reset focusNode to the inputNode, so when the button is clicked,
364                        // the focus is properly moved to the input element
365                        this.focusNode = this.inputNode = domConstruct.create("input", {type:"file", name:name, "aria-labelledby":this.id+"_label"}, this.domNode, "first");
366                        if(this.supports("multiple") && this.multiple){
367                                domAttr.set(this.inputNode, "multiple", true);
368                        }
369                        this._inputs.push(this.inputNode);
370       
371                        domStyle.set(this.inputNode, {
372                                position:"absolute",
373                                fontSize:this.inputNodeFontSize+"em",
374                                top:"-3px",
375                                right:"-3px",
376                                opacity:0
377                        });
378                        this._connectButton();
379                },
380       
381                _connectButton: function(){
382                        this._cons.push(connect.connect(this.inputNode, "change", this, function(evt){
383                                this._files = this.inputNode.files;
384                                this.onChange(this.getFileList(evt));
385                                if(!this.supports("multiple") && this.multiple) this._createInput();
386                        }));
387       
388                        if(this.tabIndex > -1){
389                                this.inputNode.tabIndex = this.tabIndex;
390       
391                                this._cons.push(connect.connect(this.inputNode, "focus", this, function(){
392                                        domClass.add(this.domNode, this.focusedClass);
393                                }));
394                                this._cons.push(connect.connect(this.inputNode, "blur", this, function(){
395                                        domClass.remove(this.domNode, this.focusedClass);
396                                }));
397                        }
398                },
399       
400                _disconnectButton: function(){
401                        array.forEach(this._cons, connect.disconnect);
402                        this._cons.splice(0,this._cons.length);
403                }
404        });
405
406});
Note: See TracBrowser for help on using the repository browser.