source: Dev/branches/rest-dojo-ui/client/dojox/form/Uploader.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

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