source: Dev/trunk/src/client/dojox/form/FileUploader.js @ 532

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

Added Dojo 1.9.3 release.

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