source: Dev/branches/rest-dojo-ui/client/dojox/form/FileUploader.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: 43.5 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/_base/sniff",
9        "dojo/query",
10        "dojo/dom-style",
11        "dojo/dom-geometry",
12        "dojo/dom-attr",
13        "dojo/dom-class",
14        "dojo/dom-construct",
15        "dojo/dom-form",
16        "dojo/_base/config",
17        "dijit/_base/manager",
18        "dojo/io/iframe",
19        "dojo/_base/Color",
20        "dojo/_base/unload",
21        "dijit/_Widget",
22        "dijit/_TemplatedMixin",
23        "dijit/_Contained",
24        "dojox/embed/Flash",
25        "dojox/embed/flashVars",
26        "dojox/html/styles"
27],function(kernel, declare, lang, array, connect, win, has, query, domStyle, domGeometry, domAttr, domClass, domConstruct, domForm, config, manager, ioIframe, Color, unloadUtils, Widget, TemplatedMixin, Contained, embedFlash, embedFlashVars, htmlStyles){
28
29kernel.deprecated("dojox.form.FileUploader", "Use dojox.form.Uploader", "2.0");
30
31        //      Usage Notes:
32        //              To center text vertically, use vertical-align:middle;
33        //                      which emulates a boxModel button. Using line-height to center text
34        //                      can cause height problems in IE6
35
36
37        /*=====
38                Widget = dijit._Widget;
39                TemplatedMixin = dijit._TemplatedMixin;
40                Contained = dijit._Contained;
41        =====*/
42declare("dojox.form.FileUploader", [Widget, TemplatedMixin, Contained], {
43        // version:
44        //              1.5 (deprecated)
45        // summary:
46        //              Handles File Uploading to a server (PHP script included for testing)
47        //
48        //              FileUploader is now a WIDGET. You do not have to pass a button
49        //              in. Passing a button is still supported until version 1.5 to maintain
50        //              backwards compatibility, but it is not reccomended. Just create your
51        //              uploader like any other widget.
52        //
53        // description:
54        //              If the correct version of Flash Player is available (> 9.0) , a SWF
55        //              is used. If Flash Player is not installed or is outdated, a typical
56        //              html fileInput is used. This process can be overridden with
57        //              force:"flash" or force:"html".
58        //
59        //              FileUploader works with Flash 10.
60        //
61        //              The button styles are now recreated in Flash, so there is no longer
62        //              using an invisible Flash movie with wmode=transparent. This way the Flash button
63        //              is actually placed inline with the DOM, not floating above it and constantly
64        //              resetting its position. The "Windows Firefox clickable bug" should be fixed (and
65        //              hopefully some Linux problems).
66        //
67        //              The HTML button is created in a new way and it is now inline as is the
68        //              FLash button. Styling is much easier and more versatile.
69        //
70        //      Dependencies:
71        //              FileUploader no longer uses FileInput.css. It now uses FileUploader.css
72        //              See requires for JavaScript dependencies.
73        //
74        //      NEW FEATURES -
75        //              There are a ton of features and fixes in this version.
76        //                      Disabled: Can be toggled with widget.attr("disable", true|false)
77        //                      Submit: A convenience method has been added for if the uploader is in a form.
78        //                                      Instead of submitting the form, call uploader.submit(theForm), and the
79        //                                      Uploader will handle all of the form values and post the data.
80        //                      Selected List: If passing the ID of a container, the Uploaders will populate it
81        //                                      with the selected files.
82        //                      Deleting Files: You can now delete pending files.
83        //                      Progress Built in: showProgress:true will change the button to a progress
84        //                                      bar on upload.
85        //                      Progress Attach: Passing progressWidgetId will tell the Uploader of a progress
86        //                                      widget. If the Progress widget is initially hidden, it will change to
87        //                                      visible and then restored after upload.
88        //                      A11Y: The Flash button can be accessed with the TAB key. (The HTML cannot due
89        //                                      to browser limtations)
90        //                      Deferred Uploading: (Flash only) throttles the upload to one file at a time
91        //
92        //
93        //      CDN USERS -
94        //              FileUpload now works with the CDN but with limitations. The SWF must
95        //              be from the same domain as the HTML page. 'swfPath' has been exposed
96        //              so that you may link to that file (could of course be the same SWF in
97        //              dojox resource folder). The SWF will *NOT* work from the
98        //              CDN server. This would require a special XML file that would allow
99        //              access to your server, and the logistics to that is impossible.
100        //
101        //      LIMITATIONS
102        //              - This is not designed to be a part of a form, it contains its own. (See submit())
103        //              - Currently does not in a Dialog box or a Tab where it is not initially visible,
104        //              - The default style inherits font sizes - but a parent container should have a font size
105        //                      set somewhere of the results could be inconsistent.
106        //
107        //      OPERA USERS -
108        //              It works better than the 1.3 version. fileInputs apperantly can't have opacity
109        //              set to zero. The Flash uploader works but files are auto-uploaded. Must be a
110        //              flashVar problem.
111        //
112        //      Safari Bug note:
113        //      The bug is in the way Safari handles the connection:
114        //              https://bugs.webkit.org/show_bug.cgi?id=5760
115        //              I added this to the virtual host in the Apache conf file, and now it
116        //              works like a charm:
117        //              BrowserMatch Safari nokeepalive
118        //
119        swfPath: config.uploaderPath || require.toUrl("dojox/form/resources/fileuploader.swf"),
120
121
122        templateString:'<div><div dojoAttachPoint="progNode"><div dojoAttachPoint="progTextNode"></div></div><div dojoAttachPoint="insideNode" class="uploaderInsideNode"></div></div>',
123
124        // uploadUrl: String
125        //              The url targeted for upload. An absolute URL is preferred. Relative URLs are
126        //              changed to absolute.
127        uploadUrl: "",
128        //
129        //      isDebug: Boolean
130        //              If true, outputs traces from the SWF to console. What exactly gets passed
131        //              is very relative, and depends upon what traces have been left in the DEFT SWF.
132        isDebug:false,
133        //
134        //      devMode: Boolean.
135        //              Re-implemented. devMode increases the logging, adding style tracing from the SWF.
136        devMode:false,
137        //
138        //      id: String
139        //              The object id, just like any other widget in Dojo. However, this id
140        //              is also used as a reference for the SWF
141        // id: "",
142        //
143        //      baseClass: String
144        //              The name of the class that will style the button in a "normal" state.
145        //              If baseClass is not defined, 'class' will be used.
146        //              NOTE: By default the uploader will be styled like a dijit buttons and
147        //              adhere to the the themes. Tundra, Soria, and Nihilo are supported.
148        //              You can cascade the existing style by using 'class' or 'style'. If you
149        //              overwrite baseClass, you should overwrite the remaing state classes
150        //              that follow) as well.
151        baseClass:"dojoxUploaderNorm",
152        //
153        //      hoverClass: String
154        //              The name of the class that will style the button in a "hover" state. A specific
155        //              class should be made to do this. Do not rely on a target like button:hover{...}
156        hoverClass:"dojoxUploaderHover",
157        //
158        //      activeClass: String
159        //              The name of the class that will style the button in a "press" state. A specific
160        //              class should be made to do this. Do not rely on a target like button:active{...}
161        activeClass:"dojoxUploaderActive",
162        //
163        //      disabledClass: String
164        //              The name of the class that will style the button when its disabled.
165        disabledClass:"dojoxUploaderDisabled",
166        //
167        //      force: String
168        //              Use "flash" to always use Flash (and hopefully force the user to download the plugin
169        //              if they don't have it). Use "html" to always use the HTML uploader. An empty string
170        //              (default) will check for the right version of Flash and use HTML if not available.
171        force:"",
172        //
173        //      uploaderType: [readonly] String
174        //              Internal. What type of uploader is being used: "flash" or "html"
175        uploaderType:"",
176        //
177        //      flashObject: [readonly] dojox.embed.Flash
178        //              The object that creates the SWF embed object. Mostly Internal.
179        flashObject: null,
180        //
181        //      flashMovie: [readonly] Function
182        //              The SWF. Mostly Internal.
183        flashMovie: null,
184        //
185        //      insideNode: [readonly] HTMLNode
186        //              The div that holds the SWF and form/fileInput
187        insideNode: null,
188        //
189        //      deferredUploading: Number (1 - X)
190        //              (Flash only) throttles the upload to a certain amount of files at a time.
191        //              By default, Flash uploads file one at a time to the server, but in parallel.
192        //              Firefox will try to queue all files at once, leading to problems. Set this
193        //              to the amount to upload in parallel at a time.
194        //              Generally, 1 should work fine, but you can experiment with queuing more than
195        //              one at a time.
196        //              This is of course ignored if selectMultipleFiles equals false.
197        deferredUploading:1,
198        //
199        //      fileListId: String
200        //              The id of a dom node to be used as a container for the pending file list.
201        fileListId:"",
202        //
203        //      uploadOnChange: Boolean
204        //              If true, uploads imediately after a file has been selected. If false,
205        //              waits for upload() to be called.
206        uploadOnChange: false,
207        //
208        //      selectMultipleFiles: Boolean
209        //              If true and flash mode, multiple files may be selected from the dialog.
210        //              If html mode, files are not uploaded until upload() is called. The references
211        //              to each file is incremented:uploadedfile0, uploadedfile1, uploadedfile2... etc.
212        selectMultipleFiles: true,
213        //
214        //      htmlFieldName: String
215        //              The name of the field of the fileInput that the server is expecting
216        htmlFieldName:"uploadedfile",
217        //
218        //      flashFieldName: String
219        //              The name of the field of the flash uploaded files that the server is expecting
220        flashFieldName:"flashUploadFiles",
221        //
222        // fileMask:  Array[ Array[Description, FileTypes], Array[...]...]
223        //              (an array, or an array of arrays)
224        //              Restrict file selection to certain file types
225        //              Empty array defaults to "All Files"
226        // example:
227        //      fileMask = ["Images", "*.jpg;*.jpeg;*.gif;*.png"]
228        //      or
229        //      fileMask = [
230        //              ["Jpeg File",   "*.jpg;*.jpeg"],
231        //              ["GIF File",    "*.gif"],
232        //              ["PNG File",    "*.png"],
233        //              ["All Images",  "*.jpg;*.jpeg;*.gif;*.png"],
234        //      ]
235        //      NOTE: MacType is not supported, as it does not work very well.
236        //                      fileMask will work on a Mac, but differently than
237        //                      Windows.
238        fileMask: null,
239        //
240        //      minFlashVersion: Number
241        //              The minimum of version of Flash player to target. 0 would always install Flash, 100
242        //              would never install it. The Flash Player has supported multiple uploads since
243        //              version 8, so it could go as low as that safely.
244        minFlashVersion:9,
245        //
246        //      tabIndex: Number|String
247        //              The tab order in the DOM. Only supported by Flash. HTML Uploaders have security
248        //              protection to prevent you from tabbing to the uploader. Stupid.
249        tabIndex:-1,
250        //
251        //      showProgress: Boolean
252        //              If true, the button changes to a progress bar during upload.
253        showProgress:false,
254        //
255        //      progressMessage: String
256        //              The message shown while the button is changed to a progress bar
257        progressMessage:"Loading",
258        //
259        //      progressBackgroundUrl: String|Uri
260        //              The background image to use for the button-progress
261        progressBackgroundUrl:require.toUrl("dijit/themes/tundra/images/buttonActive.png"),
262        //
263        //      progressBackgroundColor: String|Number
264        //              The background color to use for the button-progress
265        progressBackgroundColor:"#ededed",
266        //
267        //      progressWidgetId:String
268        //              The widget id of a Dijit Progress bar. The Uploader will bind to it and update it
269        //              automatically.
270        progressWidgetId:"",
271        //
272        // skipServerCheck: Boolean
273        //              If true, will not verify that the server was sent the correct format.
274        //              This can be safely set to true. The purpose of the server side check
275        //              is mainly to show the dev if they've implemented the different returns
276        //              correctly.
277        skipServerCheck:false,
278        //
279        // serverTimeout:Number (milliseconds)
280        //              The amount of time given to the uploaded file
281        //              to wait for a server response. After this amount
282        //              of time, the onComplete is fired but with a 'server timeout'
283        //              error in the returned item.
284        serverTimeout: 5000,
285
286
287        log: function(){
288                //      summary:
289                //              Due to the excessive logging necessary to make this code happen,
290                //              It's easier to turn it on and off here in one place.
291                //              Also helpful if there are multiple uploaders on one page.
292                if(this.isDebug){
293                        console["log"](Array.prototype.slice.call(arguments).join(" "));
294                }
295        },
296
297        constructor: function(){
298                this._subs = [];
299        },
300
301        postMixInProperties: function(){
302                // internal stuff:
303                this.fileList = [];
304                this._cons = [];
305                this.fileMask = this.fileMask || [];
306                this.fileInputs = [];
307                this.fileCount = 0;
308                this.flashReady = false;
309                this._disabled = false;
310                this.force = this.force.toLowerCase(); // Pete FTW.
311                this.uploaderType = ((embedFlash.available >= this.minFlashVersion || this.force=="flash") && this.force != "html") ? "flash" : "html";
312                this.deferredUploading = this.deferredUploading===true ? 1 : this.deferredUploading;
313
314                this._refNode = this.srcNodeRef;
315
316                this.getButtonStyle();
317        },
318
319        startup: function(){
320        },
321
322        postCreate: function(){
323                this.inherited(arguments);
324
325                // internal stuff:
326                this.setButtonStyle();
327                var createMethod;
328                if(this.uploaderType == "flash"){
329                        createMethod = "createFlashUploader";
330                }else{
331                        this.uploaderType = "html";
332                        createMethod = "createHtmlUploader";
333
334                }
335
336                this[createMethod]();
337
338                if(this.fileListId){
339                        this.connect(dom.byId(this.fileListId), "click", function(evt){
340                                var p = evt.target.parentNode.parentNode.parentNode; // in a table
341                                if(p.id && p.id.indexOf("file_")>-1){
342                                        this.removeFile(p.id.split("file_")[1]);
343                                }
344                        });
345                }
346
347                // cleaning up solves memory leak issues in the HTML version
348                unloadUtils.addOnUnload(this, this.destroy);
349        },
350
351        getHiddenNode: function(/*DomNode*/ node){
352                // summary:
353                //              Internal.
354                //              If a parent node is styled as display:none,
355                //              returns that node. This node will be temporarilly
356                //              changed to display:block. Note if the node is in
357                //              a widget that has an onShow event, this is
358                //              overridden.
359                //
360                if(!node){ return null; }
361                var hidden = null;
362                var p = node.parentNode;
363                while(p && p.tagName.toLowerCase() != "body"){
364                        var d = domStyle.get(p, "display");
365                        if(d == "none"){
366                                hidden = p;
367                                break;
368                        }
369                        p = p.parentNode;
370                }
371                return hidden;
372        },
373
374        getButtonStyle: function(){
375                // summary:
376                //              Internal.
377                //              Get necessary style information from srcRefNode and
378                //              assigned styles
379                //
380
381
382                // TODO:
383                //              To call this from postCreate....
384                //              could do the style stuff initially, but if hidden they will be bad sizes
385                //              could then redo the sizes
386                //              alt is to create a genuine button and copy THAT instead of how doing now
387
388                var refNode = this.srcNodeRef;
389                this._hiddenNode = this.getHiddenNode(refNode);
390                if(this._hiddenNode){
391                        domStyle.set(this._hiddenNode, "display", "block");
392                }
393
394                if(!refNode && this.button && this.button.domNode){
395                        // backwards compat for a Dijit button
396                        var isDijitButton = true;
397                        var cls = this.button.domNode.className + " dijitButtonNode";
398                        var txt = this.getText(query(".dijitButtonText", this.button.domNode)[0]);
399                        var domTxt = '<button id="'+this.button.id+'" class="'+cls+'">'+txt+'</button>';
400                        refNode = domConstruct.place(domTxt, this.button.domNode, "after");      /// Pete doesn't like this?
401                        this.srcNodeRef = refNode;
402                        this.button.destroy();
403
404                        this.baseClass = "dijitButton";
405                        this.hoverClass = "dijitButtonHover";
406                        this.pressClass = "dijitButtonActive";
407                        this.disabledClass = "dijitButtonDisabled";
408
409                }else if(!this.srcNodeRef && this.button){
410                        refNode = this.button;
411                }
412
413                if(domAttr.get(refNode, "class")){
414                        this.baseClass += " " + domAttr.get(refNode, "class");
415                }
416                domAttr.set(refNode, "class", this.baseClass);
417
418
419                this.norm = this.getStyle(refNode);
420                this.width = this.norm.w;
421                this.height = this.norm.h;
422
423                if(this.uploaderType == "flash"){
424
425                        this.over = this.getTempNodeStyle(refNode, this.baseClass+" "+this.hoverClass, isDijitButton);
426                        this.down = this.getTempNodeStyle(refNode, this.baseClass+" "+this.activeClass, isDijitButton);
427                        this.dsbl = this.getTempNodeStyle(refNode, this.baseClass+" "+this.disabledClass, isDijitButton);
428
429                        this.fhtml = {
430                                cn:this.getText(refNode),
431                                nr:this.norm,
432                                ov:this.over,
433                                dn:this.down,
434                                ds:this.dsbl
435                        };
436                }else{
437                        this.fhtml = {
438                                cn:this.getText(refNode),
439                                nr:this.norm
440                        }
441                        if(this.norm.va == "middle"){
442                                this.norm.lh = this.norm.h;
443                        }
444                }
445
446                if(this.devMode){
447                        this.log("classes - base:", this.baseClass, " hover:", this.hoverClass, "active:", this.activeClass);
448                        this.log("fhtml:", this.fhtml)
449                        this.log("norm:", this.norm)
450                        this.log("over:", this.over)
451                        this.log("down:", this.down)
452
453                }
454        },
455
456        setButtonStyle: function(){
457                // summary:
458                //              Internal.
459                //              Set up internal dom nodes for button construction.
460                //
461                domStyle.set(this.domNode, {
462                        width:this.fhtml.nr.w+"px",
463                        height:(this.fhtml.nr.h)+"px",
464                        padding:"0px",
465                        lineHeight: "normal",
466                        position:"relative"
467                });
468                if(this.uploaderType == "html" && this.norm.va == "middle"){
469                        domStyle.set(this.domNode, "lineHeight", this.norm.lh + "px");
470                }
471                if(this.showProgress){
472                        this.progTextNode.innerHTML = this.progressMessage;
473                        domStyle.set(this.progTextNode, {
474                                width:this.fhtml.nr.w+"px",
475                                height:(this.fhtml.nr.h+0)+"px",
476                                padding:"0px",
477                                margin:"0px",
478                                left:"0px",
479                                lineHeight:(this.fhtml.nr.h+0)+"px",
480                                position:"absolute"
481                        });
482                        domStyle.set(this.progNode, {
483                                width:this.fhtml.nr.w+"px",
484                                height:(this.fhtml.nr.h+0)+"px",
485                                padding:"0px",
486                                margin:"0px",
487                                left:"0px",
488                                position:"absolute",
489                                display:"none",
490                                backgroundImage:"url("+this.progressBackgroundUrl+")",
491                                backgroundPosition:"bottom",
492                                backgroundRepeat:"repeat-x",
493                                backgroundColor:this.progressBackgroundColor
494                        });
495                }else{
496                        domConstruct.destroy(this.progNode);
497                }
498                domStyle.set(this.insideNode,{
499                        position:"absolute",
500                        top:"0px",
501                        left:"0px",
502                        display:""
503                });
504                domClass.add(this.domNode, this.srcNodeRef.className);
505                if(this.fhtml.nr.d.indexOf("inline")>-1){
506                        domClass.add(this.domNode, "dijitInline");
507                }
508
509                try{
510                        this.insideNode.innerHTML = this.fhtml.cn;
511                }catch(e){
512                        // You have got to be kidding me. IE does us he favor of checking that
513                        //      we aren't inserting the improper type of content with innerHTML into
514                        //      an inline element. Alert us with an "Unknown Runtime Error". You can't
515                        //      MAKE this stuff up.
516                        //
517                        if(this.uploaderType == "flash"){
518                        this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
519                                win.body().appendChild(this.insideNode);
520                                this.insideNode.innerHTML = this.fhtml.cn;
521                                var c = connect.connect(this, "onReady", this, function(){ connect.disconnect(c);
522                                        this.insideNode = this.insideNode.parentNode.removeChild(this.insideNode);
523                                        this.domNode.appendChild(this.insideNode);
524                                });
525                        }else{
526                                this.insideNode.appendChild(document.createTextNode(this.fhtml.cn));
527                        }
528                }
529                if(this._hiddenNode){
530                        domStyle.set(this._hiddenNode, "display", "none");
531                }
532        },
533
534
535        /*************************
536         *         Public Events         *
537         *************************/
538
539        // The following events are inherited from _Widget and still may be connected:
540        // onClick
541        // onMouseUp
542        // onMouseDown
543        // onMouseOver
544        // onMouseOut
545
546        onChange: function(dataArray){
547                //      summary:
548                //              stub to connect
549                //              Fires when files are selected
550                //              Event is an array of last files selected
551        },
552
553        onProgress: function(dataArray){
554                // summary:
555                //              Stub to connect
556                //              Fires as progress returns from SWF
557                //              Event is an array of all files uploading
558                //              Can be connected to for HTML uploader,
559                //              but will not return anything.
560        },
561
562        onComplete: function(dataArray){
563                // summary:
564                //              stub to connect
565                //              Fires when all files have uploaded
566                //              Event is an array of all files
567        },
568
569        onCancel: function(){
570                // summary:
571                //              Stub to connect
572                //              Fires when dialog box has been closed
573                //              without a file selection
574        },
575
576        onError: function(/* Object or String */evtObject){
577                // summary:
578                //              Fires on errors
579                //
580                //FIXME: Unsure of a standard form for receiving errors
581        },
582
583        onReady: function(/* dojox.form.FileUploader */ uploader){
584                // summary:
585                //              Stub - Fired when embedFlash has created the
586                //              Flash object, but it has not necessarilly finished
587                //              downloading, and is ready to be communicated with.
588        },
589
590        onLoad: function(/* dojox.form.FileUploader */ uploader){
591                // summary:
592                //              Stub - SWF has been downloaded 100%.
593        },
594
595        /*************************
596         *         Public Methods        *
597         *************************/
598        submit: function(/* form node ? */form){
599                // summary:
600                //              If FileUploader is in a form, and other data should be sent
601                //              along with the files, use this instead of form submit.
602                //
603                var data = form ? domForm.toObject(form) : null;
604                this.upload(data);
605                return false; // Boolean
606        },
607        upload: function(/*Object ? */data){
608                // summary:
609                //              When called, begins file upload
610                //      data: Object
611                //              postData to be sent to server
612                //
613                if(!this.fileList.length){
614                        return false;
615                }
616                if(!this.uploadUrl){
617                        console.warn("uploadUrl not provided. Aborting.");
618                        return false;
619                }
620                if(!this.showProgress){
621                        this.set("disabled", true);
622                }
623
624                if(this.progressWidgetId){
625
626                        var node = manager.byId(this.progressWidgetId).domNode;
627                        if(domStyle.get(node, "display") == "none"){
628                                this.restoreProgDisplay = "none";
629                                domStyle.set(node, "display", "block");
630                        }
631                        if(domStyle.get(node, "visibility") == "hidden"){
632                                this.restoreProgDisplay = "hidden";
633                                domStyle.set(node, "visibility", "visible");
634                        }
635                }
636
637                if(data && !data.target){
638                        this.postData = data;
639                }
640                this.log("upload type:", this.uploaderType, " - postData:", this.postData);
641
642                for(var i = 0; i < this.fileList.length; i++){
643                        var f = this.fileList[i];
644                        f.bytesLoaded = 0;
645                        f.bytesTotal = f.size || 100000;
646                        f.percent = 0;
647                }
648                if(this.uploaderType == "flash"){
649                        this.uploadFlash();
650                }else{
651                        this.uploadHTML();
652                }
653                // prevent form submit
654                return false;
655        },
656        removeFile: function(/*String*/name, /*Boolean*/noListEdit){
657                // summary:
658                //              Removes a file from the pending file list.
659                //              Removes pending data from the Flash movie
660                //              and fileInputes from the HTML uploader.
661                //              If a file container node is bound, the file
662                //              will also be removed.
663                // name:String
664                //              The name of the file to be removed. Typically the file name,
665                //              such as: picture01.png
666                // noListEdit:Boolean
667                //              Internal. If true don't remove files from list.
668                //
669                var i;
670                for(i = 0; i < this.fileList.length; i++){
671                        if(this.fileList[i].name == name){
672                                if(!noListEdit){ // if onComplete, don't do this
673                                        this.fileList.splice(i,1);
674                                }
675                                break;
676                        }
677                }
678                if(this.uploaderType == "flash"){
679                        this.flashMovie.removeFile(name);
680                }else if(!noListEdit){
681                        domConstruct.destroy(this.fileInputs[i]);
682                        this.fileInputs.splice(i,1);
683                        this._renumberInputs();
684                }
685                if(this.fileListId){
686                        domConstruct.destroy("file_"+name);
687                }
688        },
689
690        destroy: function(){
691                //      summary:
692                //              Destroys uploader button
693                if(this.uploaderType == "flash" && !this.flashMovie){
694                        this._cons.push(connect.connect(this, "onLoad", this, "destroy"));
695                        return;
696                }
697                array.forEach(this._subs, connect.unsubscribe, dojo);
698                array.forEach(this._cons, connect.disconnect, dojo);
699                if(this.scrollConnect){
700                        connect.disconnect(this.scrollConnect);
701                }
702                if(this.uploaderType == "flash"){
703                        this.flashObject.destroy();
704                        delete this.flashObject;
705                }else{
706                        domConstruct.destroy(this._fileInput);
707                        domConstruct.destroy(this._formNode);
708                }
709                this.inherited(arguments);
710        },
711
712        /*************************
713         *         Private Events        *
714         *************************/
715        _displayProgress: function(/*Boolean or Number */display){
716                // summary:
717                //              Shows and updates the built-in progress bar.
718                //
719                if(display === true){
720                        if(this.uploaderType == "flash"){
721                                domStyle.set(this.insideNode,"top", "-2500px");
722                        }else{
723                                domStyle.set(this.insideNode,"display", "none");
724                        }
725                        domStyle.set(this.progNode,"display","");
726                }else if(display === false){
727                        domStyle.set(this.insideNode,{
728                                display: "",
729                                top: "0"
730                        });
731                        domStyle.set(this.progNode,"display","none");
732                }else{
733                        var w = display * this.fhtml.nr.w;
734                        domStyle.set(this.progNode, "width", w + "px");
735                }
736        },
737        _animateProgress: function(){
738                // summary:
739                //              Internal. Animated the built-in progress bar
740                this._displayProgress(true);
741                var _uploadDone = false;
742                var c = connect.connect(this, "_complete", function(){
743                        connect.disconnect(c);
744                        _uploadDone = true;
745                });
746                var w = 0;
747                var interval = setInterval(lang.hitch(this, function(){
748                        w+=5;
749                        if(w>this.fhtml.nr.w){
750                                w = 0;
751                                _uploadDone = true;
752                        }
753                        this._displayProgress(w/this.fhtml.nr.w);
754
755                        if(_uploadDone){
756                                clearInterval(interval);
757                                setTimeout(lang.hitch(this, function(){
758                                        this._displayProgress(false);
759                                }), 500);
760                        }
761
762                }),50);
763        },
764
765        _error: function(evt){
766                //var type = evtObject.type ? evtObject.type.toUpperCase() : "ERROR";
767                //var msg = evtObject.msg ? evtObject.msg : evtObject;
768                if(typeof(evt)=="string"){
769                        evt = new Error(evt);
770                }
771                this.onError(evt);
772        },
773
774        _addToFileList: function(){
775                // summary:
776                //              Internal only. If there is a file list, adds a file to it.
777                //              If you need to use a function such as this, connect to
778                //              onChange and update outside of this widget.
779                //
780                if(this.fileListId){
781                        var str = '';
782                        array.forEach(this.fileList, function(d){
783                                // have to use tables because of IE. Grumble.
784                                str += '<table id="file_'+d.name+'" class="fileToUpload"><tr><td class="fileToUploadClose"></td><td class="fileToUploadName">'+d.name+'</td><td class="fileToUploadSize">'+(d.size ? Math.ceil(d.size*.001) +"kb" : "")+'</td></tr></table>'
785                        }, this);
786                        dom.byId(this.fileListId).innerHTML = str;
787                }
788        },
789
790        _change: function(dataArray){
791                // summary:
792                //              Internal. Updates uploader selection
793                if(has("ie")){
794                        //IE6 uses the entire path in the name, which isn't terrible, but much different
795                        // than everything else
796                        array.forEach(dataArray, function(f){
797                                f.name = f.name.split("\\")[f.name.split("\\").length-1];
798                        });
799                }
800                if(this.selectMultipleFiles){
801                        this.fileList = this.fileList.concat(dataArray);
802                }else{
803                        if(this.fileList[0]){
804                                this.removeFile(this.fileList[0].name, true);
805                        }
806                        this.fileList = dataArray;
807                }
808                this._addToFileList();
809                this.onChange(dataArray);
810                if(this.uploadOnChange){
811                        if(this.uploaderType == "html"){
812                                this._buildFileInput();
813                        }
814                        this.upload();
815                }else if(this.uploaderType == "html" && this.selectMultipleFiles){
816                        this._buildFileInput();
817                        this._connectInput();
818                }
819        },
820
821        _complete: function(dataArray){
822                // summary:
823                //              Internal. Handles tasks after files have finished uploading
824                //
825                dataArray = lang.isArray(dataArray) ? dataArray : [dataArray];
826
827                // Yes. Yes I do have to do three loops here. ugh.
828                //
829                // Check if one of the files had an error
830                array.forEach(dataArray, function(f){
831                        if(f.ERROR){ this._error(f.ERROR); }
832                }, this);
833
834                // Have to be set them all too 100%, because
835                // onProgress does not always fire
836                array.forEach(this.fileList, function(f){
837                        f.bytesLoaded = 1;
838                        f.bytesTotal = 1;
839                        f.percent = 100;
840                        this._progress(f);
841                }, this);
842                // we're done. remove files.
843                array.forEach(this.fileList, function(f){
844                        this.removeFile(f.name, true);
845                }, this);
846
847                this.onComplete(dataArray);
848
849                this.fileList = [];
850                this._resetHTML();
851                this.set("disabled", false);
852
853
854                if(this.restoreProgDisplay){
855                        // using timeout so prog shows on screen for at least a short time
856                        setTimeout(lang.hitch(this, function(){
857                                domStyle.set(manager.byId(this.progressWidgetId).domNode,
858                                        this.restoreProgDisplay == "none" ? "display" : "visibility",
859                                        this.restoreProgDisplay
860                                );
861                        }), 500);
862                }
863
864        },
865
866        _progress: function(dataObject){
867                // summary:
868                //              Internal. Calculate progress
869                var total = 0;
870                var loaded = 0;
871                for(var i = 0; i < this.fileList.length; i++){
872                        var f = this.fileList[i];
873                        if(f.name == dataObject.name){
874                                f.bytesLoaded = dataObject.bytesLoaded;
875                                f.bytesTotal = dataObject.bytesTotal;
876                                f.percent = Math.ceil(f.bytesLoaded / f.bytesTotal * 100);
877                                this.log(f.name, "percent:", f.percent)
878                        }
879                        loaded += Math.ceil(.001 * f.bytesLoaded);
880                        total += Math.ceil(.001 * f.bytesTotal);
881                }
882                var percent = Math.ceil(loaded / total * 100);
883                if(this.progressWidgetId){
884                        manager.byId(this.progressWidgetId).update({progress:percent+"%"});
885                }
886                if(this.showProgress){
887                        this._displayProgress(percent * .01);
888                }
889                this.onProgress(this.fileList);
890
891        },
892        _getDisabledAttr: function(){
893                // summary:
894                //              Internal. To get disabled use: widget.get("disabled");
895                return this._disabled;
896        },
897
898        _setDisabledAttr: function(disabled){
899                // summary:
900                //              Internal. To set disabled use: widget.set("disabled", true | false);
901                if(this._disabled == disabled){ return; }
902
903                if(this.uploaderType == "flash"){
904                        if(!this.flashReady){
905                                var _fc = connect.connect(this, "onLoad", this, function(){
906                                        connect.disconnect(_fc);
907                                        this._setDisabledAttr(disabled);
908                                });
909                                return;
910                        }
911                        this._disabled = disabled;
912                        this.flashMovie.doDisable(disabled);
913                }else{
914                        this._disabled = disabled;
915                        domStyle.set(this._fileInput, "display", this._disabled ? "none" : "");
916                }
917                domClass.toggle(this.domNode, this.disabledClass, disabled);
918        },
919
920        _onFlashBlur: function(){
921                // summary:
922                //              Internal. Detects when Flash movies reliquishes focus.
923                //              We have to find all the tabIndexes in the doc and figure
924                //              out whom to give focus to next.
925                this.flashMovie.blur();
926                if(!this.nextFocusObject && this.tabIndex){
927                        var nodes = query("[tabIndex]");
928                        for(var i = 0; i<nodes.length; i++){
929                                if(nodes[i].tabIndex >= Number(this.tabIndex)+1){
930                                        this.nextFocusObject = nodes[i];
931                                        break;
932                                }
933                        }
934                }
935                this.nextFocusObject.focus();
936        },
937        _disconnect: function(){
938                // summary:
939                //              Internal. Disconnects fileInput in favor of new one.
940                array.forEach(this._cons, connect.disconnect, dojo);
941        },
942
943        /*************************
944         *                      HTML             *
945         *************************/
946        uploadHTML: function(){
947                // summary:
948                //              Internal. You could use this, but you should use upload() or submit();
949                //              which can also handle the post data.
950                //
951                // NOTE on deferredUploading:
952                //              This is not enabled for HTML. Workaround would be to force
953                //              singleFile uploads.
954                //      TODO:
955                //              Investigate removing fileInputs and resending form
956                //              multiple times adding each fileInput
957                //
958                if(this.selectMultipleFiles){
959                        domConstruct.destroy(this._fileInput);
960                }
961                this._setHtmlPostData();
962                if(this.showProgress){
963                        this._animateProgress();
964                }
965                var dfd = ioIframe.send({
966                        url: this.uploadUrl.toString(),
967                        form: this._formNode,
968                        handleAs: "json",
969                        error: lang.hitch(this, function(err){
970                                this._error("HTML Upload Error:" + err.message);
971                        }),
972                        load: lang.hitch(this, function(data, ioArgs, widgetRef){
973                                this._complete(data);
974                        })
975                });
976        },
977
978        createHtmlUploader: function(){
979                // summary:
980                //              Internal. Fires of methods to build HTML Uploader.
981                this._buildForm();
982                this._setFormStyle();
983                this._buildFileInput();
984                this._connectInput();
985                this._styleContent();
986                domStyle.set(this.insideNode, "visibility", "visible");
987                this.onReady();
988        },
989
990        _connectInput: function(){
991                // summary:
992                //              Internal. HTML Uploader connections. These get disconnected
993                //              after upload or if multi upload.
994                this._disconnect();
995                this._cons.push(connect.connect(this._fileInput, "mouseover", this, function(evt){
996                        domClass.add(this.domNode, this.hoverClass);
997                        this.onMouseOver(evt);
998                }));
999                this._cons.push(connect.connect(this._fileInput, "mouseout", this, function(evt){
1000                        setTimeout(lang.hitch(this, function(){
1001                                domClass.remove(this.domNode, this.activeClass);
1002                                domClass.remove(this.domNode, this.hoverClass);
1003                                this.onMouseOut(evt);
1004                                this._checkHtmlCancel("off");
1005                        }), 0);
1006                }));
1007                this._cons.push(connect.connect(this._fileInput, "mousedown", this, function(evt){
1008                        domClass.add(this.domNode, this.activeClass);
1009                        domClass.remove(this.domNode, this.hoverClass);
1010                        this.onMouseDown(evt);
1011                }));
1012                this._cons.push(connect.connect(this._fileInput, "mouseup", this, function(evt){
1013                        domClass.remove(this.domNode, this.activeClass);
1014                        this.onMouseUp(evt);
1015                        this.onClick(evt);
1016                        this._checkHtmlCancel("up");
1017                }));
1018                this._cons.push(connect.connect(this._fileInput, "change", this, function(){
1019                        this._checkHtmlCancel("change");
1020                        this._change([{
1021                                name: this._fileInput.value,
1022                                type: "",
1023                                size: 0
1024                        }]);
1025                }));
1026                if(this.tabIndex>=0){
1027                        domAttr.set(this.domNode, "tabIndex", this.tabIndex);
1028                }
1029        },
1030
1031        _checkHtmlCancel: function(mouseType){
1032                // summary:
1033                //              Internal. Check if the dialog was opened and canceled without file selection.
1034                if(mouseType == "change"){
1035                        this.dialogIsOpen = false;
1036                }
1037                if(mouseType == "up"){
1038                        this.dialogIsOpen = true;
1039                }
1040                if(mouseType == "off"){
1041                        if(this.dialogIsOpen){
1042                                this.onCancel();
1043                        }
1044                        this.dialogIsOpen = false;
1045                }
1046        },
1047
1048        _styleContent: function(){
1049                // summary:
1050                //              Internal.Apply style to node
1051                var o = this.fhtml.nr;
1052
1053                domStyle.set(this.insideNode, {
1054                        width:o.w+"px",
1055                        height:o.va == "middle"?o.h+"px":"auto",
1056                        textAlign:o.ta,
1057                        paddingTop:o.p[0]+"px",
1058                        paddingRight:o.p[1]+"px",
1059                        paddingBottom:o.p[2]+"px",
1060                        paddingLeft:o.p[3]+"px"
1061                });
1062
1063                try{
1064                        domStyle.set(this.insideNode, "lineHeight", "inherit");
1065                }catch(e){
1066                        // There are certain cases where IE refuses to set lineHeight.
1067                        // For the life of me I cannot figure out the combination of
1068                        // styles that IE doesn't like. Steaming... Pile...
1069                }
1070
1071        },
1072        _resetHTML: function(){
1073                // summary:
1074                //              Internal. After upload, this is called to clear the form and build a new
1075                //              fileInput.
1076                if(this.uploaderType == "html" && this._formNode){
1077                        this.fileInputs = [];
1078                        query("*", this._formNode).forEach(function(n){
1079                                domConstruct.destroy(n);
1080                        });
1081                        this.fileCount = 0;
1082                        this._buildFileInput();
1083                        this._connectInput();
1084                }
1085        },
1086        _buildForm: function(){
1087                // summary:
1088                //              Build the form that holds the fileInput
1089                //
1090
1091                if(this._formNode){ return; }
1092
1093                if(has("ie") < 9 || (has("ie") && has("quirks"))){
1094                        this._formNode = document.createElement('<form enctype="multipart/form-data" method="post">');
1095                        this._formNode.encoding = "multipart/form-data";
1096                        this._formNode.id = manager.getUniqueId("FileUploaderForm"); // needed for dynamic style
1097                        this.domNode.appendChild(this._formNode);
1098                }else{
1099                        this._formNode = domConstruct.create('form', {
1100                                enctype:"multipart/form-data",
1101                                method:"post",
1102                                id:manager.getUniqueId("FileUploaderForm")
1103                        }, this.domNode);
1104                }
1105        },
1106
1107        _buildFileInput: function(){
1108                // summary:
1109                //              Build the fileInput field
1110                //
1111                if(this._fileInput){
1112                        this._disconnect();
1113                        // FIXME:
1114                        //      Just hiding it which works, but we lose
1115                        //      reference to it and can't remove it from
1116                        //      the upload list.
1117                        this._fileInput.id = this._fileInput.id + this.fileCount;
1118                        domStyle.set(this._fileInput, "display", "none");
1119                }
1120                this._fileInput = document.createElement('input');
1121                this.fileInputs.push(this._fileInput);
1122                // server will need to know this variable:
1123                var nm = this.htmlFieldName;
1124                var _id = this.id;
1125                if(this.selectMultipleFiles){
1126                        nm += this.fileCount;
1127                        _id += this.fileCount;
1128                        this.fileCount++;
1129                }
1130
1131                domAttr.set(this._fileInput, {
1132                        id:this.id,
1133                        name:nm,
1134                        type:"file"
1135                });
1136
1137                domClass.add(this._fileInput, "dijitFileInputReal");
1138                this._formNode.appendChild(this._fileInput);
1139                var real = domGeometry.getMarginBox(this._fileInput);
1140                domStyle.set(this._fileInput, {
1141                        position:"relative",
1142                        left:(this.fhtml.nr.w - real.w) + "px",
1143                        opacity:0
1144                });
1145        },
1146
1147        _renumberInputs: function(){
1148                if(!this.selectMultipleFiles){ return; }
1149                var nm;
1150                this.fileCount = 0;
1151                array.forEach(this.fileInputs, function(inp){
1152                        nm = this.htmlFieldName + this.fileCount;
1153                        this.fileCount++;
1154                        domAttr.set(inp, "name", nm);
1155                }, this);
1156        },
1157
1158        _setFormStyle: function(){
1159                // summary:
1160                //              Apply a dynamic style to the form and input
1161                var size = Math.max(2, Math.max(Math.ceil(this.fhtml.nr.w / 60), Math.ceil(this.fhtml.nr.h / 15)));
1162                // Now create a style associated with the form ID
1163                htmlStyles.insertCssRule("#" + this._formNode.id + " input", "font-size:" + size + "em");
1164                domStyle.set(this.domNode, {
1165                        overflow:"hidden",
1166                        position:"relative"
1167                });
1168                domStyle.set(this.insideNode, "position", "absolute");
1169        },
1170
1171        _setHtmlPostData: function(){
1172                // summary:
1173                //              Internal.Apply postData to hidden fields in form
1174                if(this.postData){
1175                        for(var nm in this.postData){
1176                                domConstruct.create("input", {
1177                                        type: "hidden",
1178                                        name: nm,
1179                                        value: this.postData[nm]
1180                                }, this._formNode);
1181                        }
1182                }
1183        },
1184
1185        /*************************
1186         *                      FLASH            *
1187         *************************/
1188        uploadFlash: function(){
1189                // summary:
1190                //              Internal. You should use upload() or submit();
1191                try{
1192                        if(this.showProgress){
1193                                this._displayProgress(true);
1194                                var c = connect.connect(this, "_complete", this, function(){
1195                                        connect.disconnect(c);
1196                                        this._displayProgress(false);
1197                                });
1198                        }
1199
1200                        var o = {};
1201                        for(var nm in this.postData){
1202                                o[nm] = this.postData[nm];
1203                        }
1204                        this.flashMovie.doUpload(o);
1205
1206                }catch(err){
1207                        this._error("FileUploader - Sorry, the SWF failed to initialize." + err);
1208                }
1209        },
1210
1211        createFlashUploader: function(){
1212                // summary:
1213                //              Internal. Creates Flash Uploader
1214                this.uploadUrl = this.uploadUrl.toString();
1215                if(this.uploadUrl){
1216                        if(this.uploadUrl.toLowerCase().indexOf("http")<0 && this.uploadUrl.indexOf("/")!=0){
1217                                // Appears to be a relative path. Attempt to
1218                                //      convert it to absolute, so it will better
1219                                //target the SWF.
1220                                //
1221                                var loc = window.location.href.split("/");
1222                                loc.pop();
1223                                loc = loc.join("/")+"/";
1224                                this.uploadUrl = loc+this.uploadUrl;
1225                                this.log("SWF Fixed - Relative loc:", loc, " abs loc:", this.uploadUrl);
1226                        }else{
1227                                this.log("SWF URL unmodified:", this.uploadUrl)
1228                        }
1229                }else{
1230                        console.warn("Warning: no uploadUrl provided.");
1231                }
1232
1233                var w = this.fhtml.nr.w;
1234                var h = this.fhtml.nr.h;
1235
1236                var args = {
1237                        expressInstall:true,
1238                        path: this.swfPath.uri || this.swfPath,
1239                        width: w,
1240                        height: h,
1241                        allowScriptAccess:"always",
1242                        allowNetworking:"all",
1243                        vars: {
1244                                uploadDataFieldName: this.flashFieldName,
1245                                uploadUrl: this.uploadUrl,
1246                                uploadOnSelect: this.uploadOnChange,
1247                                deferredUploading:this.deferredUploading || 0,
1248                                selectMultipleFiles: this.selectMultipleFiles,
1249                                id: this.id,
1250                                isDebug: this.isDebug,
1251                                devMode:this.devMode,
1252                                flashButton:embedFlashVars.serialize("fh", this.fhtml),
1253                                fileMask:embedFlashVars.serialize("fm", this.fileMask),
1254                                noReturnCheck: this.skipServerCheck,
1255                                serverTimeout:this.serverTimeout
1256                        },
1257                        params: {
1258                                scale:"noscale",
1259                                wmode:"opaque",
1260                                allowScriptAccess:"always",
1261                                allowNetworking:"all"
1262                        }
1263
1264                };
1265
1266                this.flashObject = new embedFlash(args, this.insideNode);
1267                this.flashObject.onError = lang.hitch(function(msg){
1268                        this._error("Flash Error: " + msg);
1269                });
1270                this.flashObject.onReady = lang.hitch(this, function(){
1271                        domStyle.set(this.insideNode, "visibility", "visible");
1272                        this.log("FileUploader flash object ready");
1273                        this.onReady(this);
1274                });
1275                this.flashObject.onLoad = lang.hitch(this, function(mov){
1276                        this.flashMovie = mov;
1277                        this.flashReady = true;
1278
1279                        this.onLoad(this);
1280                });
1281                this._connectFlash();
1282
1283        },
1284
1285        _connectFlash: function(){
1286                //      summary:
1287                //              Subscribing to published topics coming from the
1288                //              Flash uploader.
1289                //      description:
1290                //              Sacrificing some readbilty for compactness. this.id
1291                //              will be on the beginning of the topic, so more than
1292                //              one uploader can be on a page and can have unique calls.
1293                //
1294                this._doSub("/filesSelected", "_change");
1295                this._doSub("/filesUploaded", "_complete");
1296                this._doSub("/filesProgress", "_progress");
1297                this._doSub("/filesError", "_error");
1298                this._doSub("/filesCanceled", "onCancel");
1299                this._doSub("/stageBlur", "_onFlashBlur");
1300                this._doSub("/up", "onMouseUp");
1301                this._doSub("/down", "onMouseDown");
1302                this._doSub("/over", "onMouseOver");
1303                this._doSub("/out", "onMouseOut");
1304
1305                this.connect(this.domNode, "focus", function(){
1306                        // TODO: some kind of indicator that the Flash button
1307                        //      is in focus
1308                        this.flashMovie.focus();
1309                        this.flashMovie.doFocus();
1310                });
1311                if(this.tabIndex>=0){
1312                        domAttr.set(this.domNode, "tabIndex", this.tabIndex);
1313                }
1314        },
1315
1316        _doSub: function(subStr, funcStr){
1317                // summary:
1318                //              Internal. Shortcut for subscribes to Flash movie
1319                this._subs.push(connect.subscribe(this.id + subStr, this, funcStr));
1320        },
1321
1322        /*************************************
1323         *              DOM INSPECTION METHODS           *
1324         *************************************/
1325
1326        urlencode: function(url){
1327                // Using symbols in place of URL chars that will break in Flash serialization.
1328                if(!url || url == "none"){
1329                        return false;
1330                }
1331                return url.replace(/:/g,"||").replace(/\./g,"^^").replace("url(", "").replace(")","").replace(/'/g,"").replace(/"/g,"");
1332        },
1333
1334        isButton: function(node){
1335                // testing if button for styling purposes
1336                var tn = node.tagName.toLowerCase();
1337                return tn == "button" || tn == "input";
1338        },
1339
1340        getTextStyle: function(node){
1341                // getting font info
1342                var o = {};
1343                o.ff = domStyle.get(node, "fontFamily");
1344                if(o.ff){
1345                        o.ff = o.ff.replace(", ", ","); // remove spaces. IE in Flash no likee
1346                        o.ff = o.ff.replace(/\"|\'/g, "");
1347                        o.ff = o.ff == "sans-serif" ? "Arial" : o.ff; // Flash doesn't know what sans-serif is
1348                        o.fw = domStyle.get(node, "fontWeight");
1349                        o.fi = domStyle.get(node, "fontStyle");
1350                        o.fs = parseInt(domStyle.get(node, "fontSize"), 10);
1351                        if(domStyle.get(node, "fontSize").indexOf("%") > -1){
1352                                // IE doesn't convert % to px. For god sakes.
1353                                var n = node;
1354                                while(n.tagName){
1355                                        if(domStyle.get(n, "fontSize").indexOf("%") == -1){
1356                                                o.fs = parseInt(domStyle.get(n, "fontSize"), 10);
1357                                                break;
1358                                        }
1359                                        if(n.tagName.toLowerCase()=="body"){
1360                                                // if everyting is %, the the font size is 16px * the %
1361                                                o.fs = 16 * .01 * parseInt(domStyle.get(n, "fontSize"), 10);
1362                                        }
1363                                        n = n.parentNode;
1364                                }
1365                        }
1366                        o.fc = new Color(domStyle.get(node, "color")).toHex();
1367                        o.fc = parseInt(o.fc.substring(1,Infinity),16);
1368                }
1369                o.lh = domStyle.get(node, "lineHeight");
1370                o.ta = domStyle.get(node, "textAlign");
1371                o.ta = o.ta == "start" || !o.ta ? "left" : o.ta;
1372                o.va = this.isButton(node) ? "middle" : o.lh == o.h ? "middle" : domStyle.get(node, "verticalAlign");
1373                return o;
1374        },
1375
1376        getText: function(node){
1377                // Get the text of the button. It's possible to use HTML in the Flash Button,
1378                //      but the results are not spectacular.
1379                var cn = lang.trim(node.innerHTML);
1380                if(cn.indexOf("<") >- 1){
1381                        cn = escape(cn);
1382                }
1383                return cn;
1384        },
1385
1386        getStyle: function(node){
1387                // getting the style of a node. Using very abbreviated characters which the
1388                //      Flash movie understands.
1389                var o = {};
1390                var dim = domGeometry.getContentBox(node);
1391                var pad = domGeometry.getPadExtents(node);
1392                o.p = [pad.t, pad.w-pad.l, pad.h-pad.t, pad.l];
1393                o.w = dim.w + pad.w;
1394                o.h = dim.h + pad.h;
1395                o.d = domStyle.get(node, "display");
1396                var clr = new Color(domStyle.get(node, "backgroundColor"));
1397                // if no color, Safari sets #000000 and alpha=0 since we don't support alpha,
1398                // it makes black - make it white
1399                o.bc = clr.a == 0 ? "#ffffff" : clr.toHex();
1400                o.bc = parseInt(o.bc.substring(1,Infinity),16);
1401                var url = this.urlencode(domStyle.get(node, "backgroundImage"));
1402                if(url){
1403                        o.bi = {
1404                                url:url,
1405                                rp:domStyle.get(node, "backgroundRepeat"),
1406                                pos: escape(domStyle.get(node, "backgroundPosition"))
1407                        };
1408                        if(!o.bi.pos){
1409                                // IE does Xpx and Ypx, not "X% Y%"
1410                                var rx = domStyle.get(node, "backgroundPositionX");
1411                                var ry = domStyle.get(node, "backgroundPositionY");
1412                                rx = (rx == "left") ? "0%" : (rx == "right") ? "100%" : rx;
1413                                ry = (ry == "top") ? "0%" : (ry == "bottom") ? "100%" : ry;
1414                                o.bi.pos = escape(rx+" "+ry);
1415                        }
1416                }
1417                return lang.mixin(o, this.getTextStyle(node));
1418        },
1419
1420        getTempNodeStyle: function(node, _class, isDijitButton){
1421                // This sets up a temp node to get the style of the hover, active, and disabled states
1422                var temp, style;
1423                if(isDijitButton){
1424                        // backwards compat until dojo 1.5
1425                        temp = domConstruct.place("<"+node.tagName+"><span>"+node.innerHTML+"</span></"+node.tagName+">", node.parentNode); //+" "+_class+"
1426                        var first = temp.firstChild;
1427                        domClass.add(first, node.className);
1428                        domClass.add(temp, _class);
1429                        style = this.getStyle(first);
1430                }else{
1431                        temp = domConstruct.place("<"+node.tagName+">"+node.innerHTML+"</"+node.tagName+">", node.parentNode);
1432                        domClass.add(temp, node.className);
1433                        domClass.add(temp, _class);
1434                        temp.id = node.id;
1435                        style = this.getStyle(temp);
1436                }
1437                // dev note: comment out this line to see what the
1438                // button states look like to the FileUploader
1439                domConstruct.destroy(temp);
1440                return style;
1441        }
1442});
1443return dojox.form.FileUploader;
1444});
Note: See TracBrowser for help on using the repository browser.